From d34b4a786b63e39d9705ae35f736da119ad0b03d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 21 Jun 2015 02:01:48 -0400 Subject: [PATCH] Implement duration ramp and war cry with new AA system Rampage also correctly does a full attack round for classes other than monk and ranger --- zone/aa.cpp | 43 ++++++++++++++------- zone/attack.cpp | 36 ++++++++++++++++++ zone/client.h | 3 +- zone/client_process.cpp | 84 +++-------------------------------------- zone/effects.cpp | 7 +++- zone/mob.h | 1 + zone/spell_effects.cpp | 53 ++++++++------------------ zone/spells.cpp | 16 ++++---- 8 files changed, 102 insertions(+), 141 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index 0c31034c7..41be9a0cf 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -828,7 +828,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) { if(!CanUseAlternateAdvancementRank(rank)) { return; } - + int size = sizeof(AARankInfo_Struct) + (sizeof(AARankEffect_Struct) * rank->effects.size()) + (sizeof(AARankPrereq_Struct) * rank->prereqs.size()); EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, size); AARankInfo_Struct *aai = (AARankInfo_Struct*)outapp->pBuffer; @@ -996,7 +996,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { if(!CanPurchaseAlternateAdvancementRank(rank, true)) { return; } - + if(rank->base_ability->charges > 0) { uint32 charges = 0; GetAA(rank_id, &charges); @@ -1004,7 +1004,7 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { if(charges > 0) { return; } - + SetAA(rank_id, rank->current_value, rank->base_ability->charges); } else { SetAA(rank_id, rank->current_value, 0); @@ -1022,10 +1022,10 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { SendAlternateAdvancementStats(); if(rank->prev) { - Message_StringID(15, AA_IMPROVE, - std::to_string(rank->title_sid).c_str(), - std::to_string(rank->prev->current_value).c_str(), - std::to_string(rank->cost).c_str(), + Message_StringID(15, AA_IMPROVE, + std::to_string(rank->title_sid).c_str(), + std::to_string(rank->prev->current_value).c_str(), + std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); /* QS: Player_Log_AA_Purchases */ @@ -1034,9 +1034,9 @@ void Client::PurchaseAlternateAdvancementRank(int rank_id) { QServ->PlayerLogEvent(Player_Log_AA_Purchases, CharacterID(), event_desc); } } else { - Message_StringID(15, AA_GAIN_ABILITY, - std::to_string(rank->title_sid).c_str(), - std::to_string(rank->cost).c_str(), + Message_StringID(15, AA_GAIN_ABILITY, + std::to_string(rank->title_sid).c_str(), + std::to_string(rank->cost).c_str(), std::to_string(AA_POINTS).c_str()); /* QS: Player_Log_AA_Purchases */ if (RuleB(QueryServ, PlayerLogAAPurchases)){ @@ -1125,7 +1125,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if(!IsValidSpell(rank->spell)) { return; } - + if(!CanUseAlternateAdvancementRank(rank)) { return; } @@ -1448,7 +1448,7 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price) if(!ability) return false; - + if(!CanUseAlternateAdvancementRank(rank)) { return false; } @@ -1474,7 +1474,7 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price) } //if expendable only let us purchase if we have no charges already - //not quite sure on how this functions client side atm + //not quite sure on how this functions client side atm //I intend to look into it later to make sure the behavior is right if(ability->charges > 0 && current_charges > 0) { return false; @@ -1565,7 +1565,7 @@ void Zone::LoadAlternateAdvancement() { } bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map> &abilities, - std::unordered_map> &ranks) + std::unordered_map> &ranks) { Log.Out(Logs::General, Logs::Status, "Loading Alternate Advancement Abilities..."); abilities.clear(); @@ -1727,3 +1727,18 @@ void Mob::GrantAlternateAdvancementAbility(int aa_id, int points) { c->CalcBonuses(); } } + +bool Mob::CheckAATimer(int timer) +{ + if (timer >= aaTimerMax) + return false; + if (aa_timers[timer].Enabled()) { + if (aa_timers[timer].Check(false)) { + aa_timers[timer].Disable(); + return false; + } else { + return true; + } + } + return false; +} diff --git a/zone/attack.cpp b/zone/attack.cpp index 6a8125507..202ad77b6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5056,3 +5056,39 @@ void NPC::SetAttackTimer() TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true); } } + +void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) +{ + if (!target) + return; + + Attack(target, hand, false, false, IsFromSpell); + + if (CanThisClassDoubleAttack()) { + CheckIncreaseSkill(SkillDoubleAttack, target, -10); + if (CheckDoubleAttack()) + Attack(target, hand, false, false, IsFromSpell); + if (hand == MainPrimary && GetLevel() >= 60 && + (GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) && + CheckDoubleAttack(true)) + Attack(target, hand, false, false, IsFromSpell); + } + if (hand == MainPrimary) { + auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + if (flurrychance && zone->random.Roll(flurrychance)) { + Message_StringID(MT_NPCFlurry, YOU_FLURRY); + Attack(target, hand, false, false, IsFromSpell); + Attack(target, hand, false, false, IsFromSpell); + } + + auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance; + if (extraattackchance) { + auto wpn = GetInv().GetItem(MainPrimary); + if (wpn && (wpn->GetItem()->ItemType == ItemType2HBlunt || + wpn->GetItem()->ItemType == ItemType2HSlash || + wpn->GetItem()->ItemType == ItemType2HPiercing)) + if (zone->random.Roll(extraattackchance)) + Attack(target, hand, false, false, IsFromSpell); + } + } +} diff --git a/zone/client.h b/zone/client.h index 07d42a313..44e86f971 100644 --- a/zone/client.h +++ b/zone/client.h @@ -226,6 +226,7 @@ public: virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); virtual void SetAttackTimer(); float GetQuiverHaste(); + void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false); void AI_Init(); void AI_Start(uint32 iMoveDelay = 0); @@ -774,7 +775,7 @@ public: void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } int GetAAPoints() { return m_pp.aapoints; } int GetSpentAA() { return m_pp.aapoints_spent; } - + //old AA methods that we still use void ResetAA(); void RefundAA(); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 603636a82..42c244e6b 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -391,74 +391,12 @@ bool Client::Process() { } else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP { - //old aa - //if(CheckAAEffect(aaEffectRampage)) - //{ - // entity_list.AEAttack(this, 30); - //} else { - Attack(auto_attack_target, MainPrimary); // Kaiyodo - added attacking hand to arguments - //} ItemInst *wpn = GetInv().GetItem(MainPrimary); TryWeaponProc(wpn, auto_attack_target, MainPrimary); - bool tripleAttackSuccess = false; - if( auto_attack_target && CanThisClassDoubleAttack() ) { - - CheckIncreaseSkill(SkillDoubleAttack, auto_attack_target, -10); - if(CheckDoubleAttack()) { - //should we allow rampage on double attack? - //if(CheckAAEffect(aaEffectRampage)) { - // entity_list.AEAttack(this, 30); - //} else { - Attack(auto_attack_target, MainPrimary, false); - //} - } - - //triple attack: rangers, monks, warriors, berserkers over level 60 - if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) - && GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE)) - && CheckDoubleAttack(true)) - { - tripleAttackSuccess = true; - Attack(auto_attack_target, MainPrimary, false); - } - - //quad attack, does this belong here?? - if(GetSpecialAbility(SPECATK_QUAD) && CheckDoubleAttack(true)) - { - Attack(auto_attack_target, MainPrimary, false); - } - } - - //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; - - if (auto_attack_target && flurrychance) - { - if(zone->random.Int(0, 99) < flurrychance) - { - Message_StringID(MT_NPCFlurry, YOU_FLURRY); - Attack(auto_attack_target, MainPrimary, false); - Attack(auto_attack_target, MainPrimary, false); - } - } - - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; - - if (auto_attack_target && ExtraAttackChanceBonus) { - ItemInst *wpn = GetInv().GetItem(MainPrimary); - if(wpn){ - if(wpn->GetItem()->ItemType == ItemType2HSlash || - wpn->GetItem()->ItemType == ItemType2HBlunt || - wpn->GetItem()->ItemType == ItemType2HPiercing ) - { - if(zone->random.Int(0, 99) < ExtraAttackChanceBonus) - { - Attack(auto_attack_target, MainPrimary, false); - } - } - } - } + DoAttackRounds(auto_attack_target, MainPrimary); + if (CheckAATimer(aaTimerRampage)) + entity_list.AEAttack(this, 30); } } @@ -499,23 +437,11 @@ bool Client::Process() { float random = zone->random.Real(0, 1); CheckIncreaseSkill(SkillDualWield, auto_attack_target, -10); - if (random < DualWieldProbability){ // Max 78% of DW - //if(CheckAAEffect(aaEffectRampage)) { - // entity_list.AEAttack(this, 30, MainSecondary); - //} else { - Attack(auto_attack_target, MainSecondary); // Single attack with offhand - //} + if (random < DualWieldProbability) { // Max 78% of DW ItemInst *wpn = GetInv().GetItem(MainSecondary); TryWeaponProc(wpn, auto_attack_target, MainSecondary); - if( CanThisClassDoubleAttack() && CheckDoubleAttack()) { - //if(CheckAAEffect(aaEffectRampage)) { - // entity_list.AEAttack(this, 30, MainSecondary); - //} else { - // if(auto_attack_target && auto_attack_target->GetHP() > -10) - Attack(auto_attack_target, MainSecondary); // Single attack with offhand - //} - } + DoAttackRounds(auto_attack_target, MainSecondary); } } } diff --git a/zone/effects.cpp b/zone/effects.cpp index 55e56cdac..68e68e78d 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -878,7 +878,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff caster->CastToClient()->CheckSongSkillIncrease(spell_id); } -//Dook- Rampage and stuff for clients. +// Rampage and stuff for clients. Normal and Duration rampages //NPCs handle it differently in Mob::Rampage void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { //Dook- Will need tweaking, currently no pets or players or horses @@ -896,7 +896,10 @@ void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool I && curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ && (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2) ) { - attacker->Attack(curmob, Hand, false, false, IsFromSpell); + if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) + attacker->Attack(curmob, Hand, false, false, IsFromSpell); + else + attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell); hit++; if (count != 0 && hit >= count) return; diff --git a/zone/mob.h b/zone/mob.h index 8352d764e..c9ba4471d 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -971,6 +971,7 @@ public: void CalcAABonuses(StatBonuses* newbon); void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon); void GrantAlternateAdvancementAbility(int aa_id, int points); + bool CheckAATimer(int timer); protected: void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 3c0c8fad4..5c30185aa 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -638,42 +638,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Group Fear Immunity"); #endif //Added client messages to give some indication this effect is active. - uint32 group_id_caster = 0; - uint32 time = spell.base[i]*10; - if(caster->IsClient()) - { - if(caster->IsGrouped()) - { - group_id_caster = GetGroup()->GetID(); - } - else if(caster->IsRaidGrouped()) - { - group_id_caster = (GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(CastToClient()) + 1); + // Is there a message generated? Too disgusted by raids. + uint32 time = spell.base[i] * 10 * 1000; + if (caster->IsClient()) { + if (caster->IsGrouped()) { + auto group = caster->GetGroup(); + for (int i = 0; i < 6; ++i) + if (group->members[i]) + group->members[i]->aa_timers[aaTimerWarcry].Start(time); + } else if (caster->IsRaidGrouped()) { + auto raid = caster->GetRaid(); + uint32 gid = raid->GetGroup(caster->CastToClient()); + if (gid < 12) + for (int i = 0; i < MAX_RAID_MEMBERS; ++i) + if (raid->members[i].member && raid->members[i].GroupNumber == gid) + raid->members[i].member->aa_timers[aaTimerWarcry].Start(time); } } - //old aa - //if(group_id_caster){ - // Group *g = entity_list.GetGroupByID(group_id_caster); - // uint32 time = spell.base[i]*10; - // if(g){ - // for(int gi=0; gi < 6; gi++){ - // if(g->members[gi] && g->members[gi]->IsClient()) - // { - // g->members[gi]->CastToClient()->EnableAAEffect(aaEffectWarcry , time); - // if (g->members[gi]->GetID() != caster->GetID()) - // g->members[gi]->Message(13, "You hear the war cry."); - // else - // Message(13, "You let loose a fierce war cry."); - // } - // } - // } - //} - // - //else{ - // CastToClient()->EnableAAEffect(aaEffectWarcry , time); - // Message(13, "You let loose a fierce war cry."); - //} - break; } @@ -2238,9 +2219,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Duration Rampage"); #endif - //if (caster && caster->IsClient()) { // will tidy this up later so that NPCs can duration ramp from spells too - // CastToClient()->DurationRampage(effect_value*12); - //} + aa_timers[aaTimerRampage].Start(effect_value * 10 * 1000); // Live bug, was suppose to be 1 second per value break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 4f492c343..c3e777b5c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2280,7 +2280,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if(rank && rank->base_ability) { ExpendAlternateAdvancementCharge(rank->base_ability->id); } - } + } else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { //aa new todo: aa expendable charges here @@ -4124,13 +4124,13 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) } return true; } - //else if (IsClient() && CastToClient()->CheckAAEffect(aaEffectWarcry)) //old aa - //{ - // Message(13, "Your are immune to fear."); - // Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!"); - // caster->Message_StringID(MT_Shout, IMMUNE_FEAR); - // return true; - //} + else if (CheckAATimer(aaTimerWarcry)) + { + Message(13, "Your are immune to fear."); + Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!"); + caster->Message_StringID(MT_Shout, IMMUNE_FEAR); + return true; + } } if(IsCharmSpell(spell_id))