diff options
author | QAston <qaston@gmail.com> | 2011-07-10 13:13:02 +0200 |
---|---|---|
committer | QAston <qaston@gmail.com> | 2011-07-10 13:13:02 +0200 |
commit | 72ea9c9fa86fa88a5cbdd971d817c6903abf725a (patch) | |
tree | da26b1a3acda8a57925293302435745e6fe0a46e | |
parent | 22135f4d91001f24cabea843c8f883558a6a2365 (diff) |
Core/Spells: Add a base for brand new proc system, this is a WIP and doesn't affect currently working code.
-rwxr-xr-x | src/server/game/Entities/Player/Player.cpp | 4 | ||||
-rwxr-xr-x | src/server/game/Entities/Unit/Unit.cpp | 122 | ||||
-rwxr-xr-x | src/server/game/Entities/Unit/Unit.h | 102 | ||||
-rwxr-xr-x | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 200 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.h | 9 | ||||
-rwxr-xr-x | src/server/game/Spells/Auras/SpellAuras.cpp | 143 | ||||
-rwxr-xr-x | src/server/game/Spells/Auras/SpellAuras.h | 14 | ||||
-rwxr-xr-x | src/server/game/Spells/SpellMgr.cpp | 72 | ||||
-rwxr-xr-x | src/server/game/Spells/SpellMgr.h | 13 | ||||
-rw-r--r-- | src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp | 1 |
10 files changed, 621 insertions, 59 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9eac54ee3f8..eea6159dbeb 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -19631,7 +19631,7 @@ void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura) SpellModifier *mod = *itr; // spellmods without aura set cannot be charged - if (!mod->ownerAura || !mod->ownerAura->GetCharges()) + if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) continue; // Restore only specific owner aura mods @@ -19689,7 +19689,7 @@ void Player::RemoveSpellMods(Spell* spell) ++itr; // spellmods without aura set cannot be charged - if (!mod->ownerAura || !mod->ownerAura->GetCharges()) + if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) continue; // check if mod affected this spell diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e3146a354b2..c8e50740bdc 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -92,6 +92,52 @@ static bool isAlwaysTriggeredAura[TOTAL_AURAS]; // Prepare lists static bool procPrepared = InitTriggerAuraData(); +DamageInfo::DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellEntry const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType) +: m_attacker(_attacker), m_victim(_victim), m_damage(_damage), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask), +m_damageType(_damageType), m_attackType(BASE_ATTACK) +{ + m_absorb = 0; + m_resist = 0; + m_block = 0; +} +DamageInfo::DamageInfo(CalcDamageInfo& dmgInfo) +: m_attacker(dmgInfo.attacker), m_victim(dmgInfo.target), m_damage(dmgInfo.damage), m_spellInfo(NULL), m_schoolMask(SpellSchoolMask(dmgInfo.damageSchoolMask)), +m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType) +{ + m_absorb = 0; + m_resist = 0; + m_block = 0; +} +void DamageInfo::ModifyDamage(int32 amount) +{ + amount = std::min(amount, int32(GetDamage())); + m_damage += amount; +} +void DamageInfo::AbsorbDamage(uint32 amount) +{ + amount = std::min(amount, GetDamage()); + m_absorb += amount; + m_damage -= amount; +} +void DamageInfo::ResistDamage(uint32 amount) +{ + amount = std::min(amount, GetDamage()); + m_resist += amount; + m_damage -= amount; +} +void DamageInfo::BlockDamage(uint32 amount) +{ + amount = std::min(amount, GetDamage()); + m_block += amount; + m_damage -= amount; +} + +ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +:_actor(actor), _actionTarget(actionTarget), _procTarget(procTarget), _typeMask(typeMask), _spellTypeMask(spellTypeMask), _spellPhaseMask(spellPhaseMask), +_hitMask(hitMask), _spell(spell), _damageInfo(damageInfo), _healInfo(healInfo) +{ +} + // we can disable this warning for this since it only // causes undefined behavior when passed to the base class constructor #ifdef _MSC_VER @@ -1931,7 +1977,9 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex DealDamageMods(pVictim, damageInfo.damage, &damageInfo.absorb); SendAttackStateUpdate(&damageInfo); + //TriggerAurasProcOnEvent(damageInfo); ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType); + DealMeleeDamage(&damageInfo, true); if (GetTypeId() == TYPEID_PLAYER) @@ -14294,7 +14342,11 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* pTarget, uint32 procFlag, if (i->aura->IsRemoved()) continue; - bool useCharges = i->aura->GetCharges() > 0; + bool useCharges = i->aura->IsUsingCharges(); + // no more charges to use, prevent proc + if (useCharges && !i->aura->GetCharges()) + continue; + bool takeCharges = false; SpellEntry const* spellInfo = i->aura->GetSpellProto(); uint32 Id = i->aura->GetId(); @@ -14495,6 +14547,70 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* pTarget, uint32 procFlag, SetCantProc(false); } +void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo) +{ + // use provided list of auras which can proc + if (procAuras) + { + for (std::list<AuraApplication*>::iterator itr = procAuras->begin(); itr!= procAuras->end(); ++itr) + { + ASSERT((*itr)->GetTarget() == this); + if (!(*itr)->GetRemoveMode()) + if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo)) + { + (*itr)->GetBase()->PrepareProcToTrigger(); + aurasTriggeringProc.push_back(*itr); + } + } + } + // or generate one on our own + else + { + for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) + { + if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo)) + { + itr->second->GetBase()->PrepareProcToTrigger(); + aurasTriggeringProc.push_back(itr->second); + } + } + } +} + +void Unit::TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo) +{ + DamageInfo dmgInfo = DamageInfo(damageInfo); + TriggerAurasProcOnEvent(NULL, NULL, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, 0, 0, damageInfo.procEx, NULL, &dmgInfo, NULL); +} + +void Unit::TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std::list<AuraApplication*>* targetProcAuras, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +{ + // prepare data for self trigger + ProcEventInfo myProcEventInfo = ProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + std::list<AuraApplication*> myAurasTriggeringProc; + GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, myProcAuras, myProcEventInfo); + + // prepare data for target trigger + ProcEventInfo targetProcEventInfo = ProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + std::list<AuraApplication*> targetAurasTriggeringProc; + if (typeMaskActionTarget) + GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, targetProcAuras, targetProcEventInfo); + + TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); + + if (typeMaskActionTarget) + TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); +} + +void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& aurasTriggeringProc) +{ + for (std::list<AuraApplication*>::iterator itr = aurasTriggeringProc.begin(); itr != aurasTriggeringProc.end(); ++itr) + { + if (!(*itr)->GetRemoveMode()) + (*itr)->GetBase()->TriggerProcOnEvent(*itr, eventInfo); + } +} + SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const { return SPELL_SCHOOL_MASK_NORMAL; @@ -15059,6 +15175,10 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co { SpellEntry const* spellProto = aura->GetSpellProto(); + // let the aura be handled by new proc system if it has new entry + if (sSpellMgr->GetSpellProcEntry(spellProto->Id)) + return false; + // Get proc Event Entry spellProcEvent = sSpellMgr->GetSpellProcEvent(spellProto->Id); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index e119438583c..6f5ca9c3aa3 100755 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -801,6 +801,8 @@ struct CleanDamage MeleeHitOutcome hitOutCome; }; +struct CalcDamageInfo; + class DamageInfo { private: @@ -810,51 +812,84 @@ private: SpellEntry const* const m_spellInfo; SpellSchoolMask const m_schoolMask; DamageEffectType const m_damageType; + WeaponAttackType m_attackType; uint32 m_absorb; uint32 m_resist; uint32 m_block; public: - explicit DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellEntry const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType) - : m_attacker(_attacker), m_victim(_victim), m_damage(_damage), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask), m_damageType(_damageType) - { - m_absorb = 0; - m_resist = 0; - m_block = 0; - } - void ModifyDamage(int32 amount) - { - amount = std::min(amount, int32(GetDamage())); - m_damage += amount; - } - void AbsorbDamage(uint32 amount) - { - amount = std::min(amount, GetDamage()); - m_absorb += amount; - m_damage -= amount; - } - void ResistDamage(uint32 amount) - { - amount = std::min(amount, GetDamage()); - m_resist += amount; - m_damage -= amount; - } - void BlockDamage(uint32 amount) - { - amount = std::min(amount, GetDamage()); - m_block += amount; - m_damage -= amount; - } + explicit DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellEntry const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType); + explicit DamageInfo(CalcDamageInfo& dmgInfo); + + void ModifyDamage(int32 amount); + void AbsorbDamage(uint32 amount); + void ResistDamage(uint32 amount); + void BlockDamage(uint32 amount); + Unit* GetAttacker() const { return m_attacker; }; Unit* GetVictim() const { return m_victim; }; - DamageEffectType GetDamageType() const { return m_damageType; }; SpellEntry const* GetSpellInfo() const { return m_spellInfo; }; SpellSchoolMask GetSchoolMask() const { return m_schoolMask; }; + DamageEffectType GetDamageType() const { return m_damageType; }; + WeaponAttackType GetAttackType() const { return m_attackType; }; uint32 GetDamage() const { return m_damage; }; uint32 GetAbsorb() const { return m_absorb; }; uint32 GetResist() const { return m_resist; }; uint32 GetBlock() const { return m_block; }; }; +class HealInfo +{ +private: + Unit* const m_healer; + Unit* const m_target; + uint32 m_heal; + uint32 m_absorb; + SpellEntry const* const m_spellInfo; + SpellSchoolMask const m_schoolMask; +public: + explicit HealInfo(Unit* _healer, Unit* _target, uint32 _heal, SpellEntry const* _spellInfo, SpellSchoolMask _schoolMask) + : m_healer(_healer), m_target(_target), m_heal(_heal), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask) + { + m_absorb = 0; + } + void AbsorbHeal(uint32 amount) + { + amount = std::min(amount, GetHeal()); + m_absorb += amount; + m_heal -= amount; + } + + uint32 GetHeal() const { return m_heal; }; +}; + +class ProcEventInfo +{ +private: + Unit* const _actor; + Unit* const _actionTarget; + Unit* const _procTarget; + uint32 _typeMask; + uint32 _spellTypeMask; + uint32 _spellPhaseMask; + uint32 _hitMask; + Spell* _spell; + DamageInfo* _damageInfo; + HealInfo* _healInfo; +public: + explicit ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); + Unit* GetActor() { return _actor; }; + Unit* GetActionTarget() const { return _actionTarget; } + Unit* GetProcTarget() const { return _procTarget; } + uint32 GetTypeMask() const { return _typeMask; } + uint32 GetSpellTypeMask() const { return _spellTypeMask; } + uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } + uint32 GetHitMask() const { return _hitMask; } + SpellEntry const* GetSpellInfo() const { return NULL; } + SpellSchoolMask GetSchoolMask() const { return SPELL_SCHOOL_MASK_NONE; } + DamageInfo* GetDamageInfo() const { return _damageInfo; } + HealInfo* GetHealInfo() const { return _healInfo; } +}; + // Struct for use in Unit::CalculateMeleeDamage // Need create structure like in SMSG_ATTACKERSTATEUPDATE opcode struct CalcDamageInfo @@ -1362,6 +1397,11 @@ class Unit : public WorldObject void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellEntry const *procSpell = NULL, SpellEntry const* procAura = NULL); void ProcDamageAndSpellFor(bool isVictim, Unit* pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const* procSpell, uint32 damage , SpellEntry const* procAura = NULL); + void GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo); + void TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo); + void TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std::list<AuraApplication*>* targetProcAuras, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); + void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& procAuras); + void HandleEmoteCommand(uint32 anim_id); void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool extra = false); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e2cdad9f266..cb89a04bca6 100755 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1039,7 +1039,7 @@ void AuraEffect::ApplySpellMod(Unit* target, bool apply) target->ToPlayer()->AddSpellMod(m_spellmod, apply); // Auras with charges do not mod amount of passive auras - if (GetBase()->GetCharges()) + if (GetBase()->IsUsingCharges()) return; // reapply some passive spells after add/remove related spellmods // Warning: it is a dead loop if 2 auras each other amount-shouldn't happen @@ -1263,6 +1263,20 @@ bool AuraEffect::IsPeriodicTickCrit(Unit* target, Unit const* caster) const return false; } +bool AuraEffect::IsAffectedOnSpell(SpellEntry const* spell) const +{ + if (!spell) + return false; + // Check family name + if (spell->SpellFamilyName != m_spellProto->SpellFamilyName) + return false; + + // Check EffectClassMask + if (m_spellProto->EffectSpellClassMask[m_effIndex] & spell->SpellFamilyFlags) + return true; + return false; +} + void AuraEffect::SendTickImmune(Unit* target, Unit *caster) const { if (caster) @@ -1324,18 +1338,29 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const } } -bool AuraEffect::IsAffectedOnSpell(SpellEntry const* spell) const +void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { - if (!spell) - return false; - // Check family name - if (spell->SpellFamilyName != m_spellProto->SpellFamilyName) - return false; - - // Check EffectClassMask - if (m_spellProto->EffectSpellClassMask[m_effIndex] & spell->SpellFamilyFlags) - return true; - return false; + // TODO: effect script handlers here + switch(GetAuraType()) + { + case SPELL_AURA_PROC_TRIGGER_SPELL: + HandleProcTriggerSpellAuraProc(aurApp, eventInfo); + break; + case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: + HandleProcTriggerSpellWithValueAuraProc(aurApp, eventInfo); + break; + case SPELL_AURA_PROC_TRIGGER_DAMAGE: + HandleProcTriggerDamageAuraProc(aurApp, eventInfo); + break; + case SPELL_AURA_RAID_PROC_FROM_CHARGE: + HandleRaidProcFromChargeAuraProc(aurApp, eventInfo); + break; + case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: + HandleRaidProcFromChargeWithValueAuraProc(aurApp, eventInfo); + break; + default: + break; + } } void AuraEffect::CleanupTriggeredSpells(Unit* target) @@ -5885,13 +5910,13 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) return; } // Remote Toy - case 37027: triggerSpellId = 37029; break; + case 37027: + triggerSpellId = 37029; + break; // Eye of Grillok case 38495: - { - target->CastSpell(target, 38530, true, NULL, this); - return; - } + triggerSpellId = 38530; + break; // Absorb Eye of Grillok (Zezzak's Shard) case 38554: { @@ -6634,3 +6659,144 @@ void AuraEffect::HandlePeriodicPowerBurnManaAuraTick(Unit* target, Unit* caster) caster->DealSpellDamage(&damageInfo, true); } + +void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) +{ + Unit* triggerCaster = aurApp->GetTarget(); + Unit* triggerTarget = eventInfo.GetProcTarget(); + + uint32 triggerSpellId = GetSpellProto()->EffectTriggerSpell[GetEffIndex()]; + if (SpellEntry const* triggeredSpellInfo = sSpellStore.LookupEntry(triggerSpellId)) + { + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleProcTriggerSpellAuraProc: Triggering spell %u from aura %u proc", triggeredSpellInfo->Id, GetId()); + triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo, true, NULL, this); + } + else + sLog->outDebug(LOG_FILTER_SPELLS_AURAS,"AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); +} + +void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) +{ + Unit* triggerCaster = aurApp->GetTarget(); + Unit* triggerTarget = eventInfo.GetProcTarget(); + + uint32 triggerSpellId = GetSpellProto()->EffectTriggerSpell[m_effIndex]; + if (SpellEntry const *triggeredSpellInfo = sSpellStore.LookupEntry(triggerSpellId)) + { + int32 basepoints0 = GetAmount(); + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Triggering spell %u with value %d from aura %u proc", triggeredSpellInfo->Id, basepoints0, GetId()); + triggerCaster->CastCustomSpell(triggerTarget, triggerSpellId, &basepoints0, NULL, NULL, true, NULL, this); + } + else + sLog->outDebug(LOG_FILTER_SPELLS_AURAS,"AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); +} + +void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) +{ + Unit* target = aurApp->GetTarget(); + Unit* triggerTarget = eventInfo.GetProcTarget(); + SpellNonMeleeDamage damageInfo(target, triggerTarget, GetId(), GetSpellProto()->SchoolMask); + uint32 damage = target->SpellDamageBonus(triggerTarget, GetSpellProto(), GetAmount(), SPELL_DIRECT_DAMAGE); + target->CalculateSpellDamageTaken(&damageInfo, damage, GetSpellProto()); + target->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); + target->SendSpellNonMeleeDamageLog(&damageInfo); + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleProcTriggerDamageAuraProc: Triggering %u spell damage from aura %u proc", damage, GetId()); + target->DealSpellDamage(&damageInfo, true); +} + +void AuraEffect::HandleRaidProcFromChargeAuraProc(AuraApplication* aurApp, ProcEventInfo& /*eventInfo*/) +{ + Unit* target = aurApp->GetTarget(); + + uint32 triggerSpellId; + switch (GetId()) + { + case 57949: // Shiver + triggerSpellId = 57952; + //animationSpellId = 57951; dummy effects for jump spell have unknown use (see also 41637) + break; + case 59978: // Shiver + triggerSpellId = 59979; + break; + case 43593: // Cold Stare + triggerSpellId = 43594; + break; + default: + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleRaidProcFromChargeAuraProc: received not handled spell: %u", GetId()); + return; + } + + int32 jumps = GetBase()->GetCharges(); + + // current aura expire on proc finish + GetBase()->SetCharges(0); + GetBase()->SetUsingCharges(true); + + // next target selection + if (jumps > 0) + { + Unit* caster = GetCaster(); + float radius = (float)GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[GetEffIndex()])); + + if (caster) + { + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, radius, NULL); + + if (Unit* triggerTarget = target->GetNextRandomRaidMemberOrPet(radius)) + { + target->CastSpell(triggerTarget, GetSpellProto(), true, NULL, this, GetCasterGUID()); + if (Aura* aura = triggerTarget->GetAura(GetId(), GetCasterGUID())) + aura->SetCharges(jumps); + } + } + } + + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleRaidProcFromChargeAuraProc: Triggering spell %u from aura %u proc", triggerSpellId, GetId()); + target->CastSpell(target, triggerSpellId, true, NULL, this, GetCasterGUID()); +} + + +void AuraEffect::HandleRaidProcFromChargeWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& /*eventInfo*/) +{ + Unit* target = aurApp->GetTarget(); + + // Currently only Prayer of Mending + if (!(GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST && GetSpellProto()->SpellFamilyFlags[1] & 0x20)) + { + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleRaidProcFromChargeWithValueAuraProc: received not handled spell: %u", GetId()); + return; + } + uint32 triggerSpellId = 33110; + + int32 value = GetAmount(); + + int32 jumps = GetBase()->GetCharges(); + + // current aura expire on proc finish + GetBase()->SetCharges(0); + GetBase()->SetUsingCharges(true); + + // next target selection + if (jumps > 0) + { + Unit* caster = GetCaster(); + float radius = (float)GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[GetEffIndex()])); + + if (caster) + { + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, radius, NULL); + + if (Unit* triggerTarget = target->GetNextRandomRaidMemberOrPet(radius)) + { + target->CastCustomSpell(triggerTarget, GetId(), &value, NULL, NULL, true, NULL, this, GetCasterGUID()); + if (Aura* aura = triggerTarget->GetAura(GetId(), GetCasterGUID())) + aura->SetCharges(jumps); + } + } + } + + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraEffect::HandleRaidProcFromChargeWithValueAuraProc: Triggering spell %u from aura %u proc", triggerSpellId, GetId()); + target->CastCustomSpell(target, triggerSpellId, &value, NULL, NULL, true, NULL, this, GetCasterGUID()); +} diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 8aaef85c394..dea2e980c12 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -84,6 +84,8 @@ class AuraEffect void SendTickImmune(Unit* target, Unit *caster) const; void PeriodicTick(AuraApplication * aurApp, Unit* caster) const; + void HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void CleanupTriggeredSpells(Unit* target); // add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras @@ -288,6 +290,13 @@ class AuraEffect void HandleObsModPowerAuraTick(Unit* target, Unit* caster) const; void HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) const; void HandlePeriodicPowerBurnManaAuraTick(Unit* target, Unit* caster) const; + + // aura effect proc handlers + void HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void HandleRaidProcFromChargeAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); + void HandleRaidProcFromChargeWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); }; namespace Trinity diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 0f21cee9868..0c457697805 100755 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -193,7 +193,8 @@ void AuraApplication::BuildUpdatePacket(ByteBuffer& data, bool remove) const flags |= AFLAG_DURATION; data << uint8(flags); data << uint8(aura->GetCasterLevel()); - data << uint8(aura->GetStackAmount() > 1 ? aura->GetStackAmount() : (aura->GetCharges()) ? aura->GetCharges() : 1); + // stack amount has priority over charges (checked on retail with spell 50262) + data << uint8(aura->GetStackAmount() > 1 ? aura->GetStackAmount() : (aura->IsUsingCharges()) ? aura->GetCharges() : 0); if (!(flags & AFLAG_CASTER)) data.appendPackGUID(aura->GetCasterGUID()); @@ -332,7 +333,7 @@ m_spellProto(spellproto), m_casterGuid(casterGUID ? casterGUID : caster->GetGUID m_castItemGuid(castItem ? castItem->GetGUID() : 0), m_applyTime(time(NULL)), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellProto->spellLevel), m_procCharges(0), m_stackAmount(1), -m_isRemoved(false), m_isSingleTarget(false) +m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false) { if (m_spellProto->manaPerSecond || m_spellProto->manaPerSecondPerLevel) m_timeCla = 1 * IN_MILLISECONDS; @@ -340,6 +341,8 @@ m_isRemoved(false), m_isSingleTarget(false) m_maxDuration = CalcMaxDuration(caster); m_duration = m_maxDuration; m_procCharges = CalcMaxCharges(caster); + m_isUsingCharges = m_procCharges != 0; + // m_casterLevel = cast item level/caster level, caster level should be saved to db, confirmed with sniffs } void Aura::_InitEffects(uint8 effMask, Unit* caster, int32 *baseAmount) @@ -741,12 +744,15 @@ void Aura::SetCharges(uint8 charges) if (m_procCharges == charges) return; m_procCharges = charges; + m_isUsingCharges = m_procCharges != 0; SetNeedClientUpdateForTargets(); } uint8 Aura::CalcMaxCharges(Unit* caster) const { uint8 maxProcCharges = m_spellProto->procCharges; + if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId())) + maxProcCharges = procEntry->charges; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) @@ -756,7 +762,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const bool Aura::ModCharges(int32 num, AuraRemoveMode removeMode) { - if (m_procCharges) + if (IsUsingCharges()) { int32 charges = m_procCharges + num; int32 maxCharges = CalcMaxCharges(); @@ -881,6 +887,10 @@ bool Aura::CanBeSaved() const if (GetId() == 44413) return false; + // don't save auras removed by proc system + if (IsUsingCharges() && !GetCharges()) + return false; + return true; } @@ -906,6 +916,7 @@ void Aura::SetLoadedState(int32 maxduration, int32 duration, int32 charges, uint m_maxDuration = maxduration; m_duration = duration; m_procCharges = charges; + m_isUsingCharges = m_procCharges != 0; m_stackAmount = stackamount; Unit* caster = GetCaster(); for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -1734,6 +1745,132 @@ bool Aura::CheckAreaTarget(Unit* target) return CallScriptCheckAreaTargetHandlers(target); } +bool Aura::IsProcOnCooldown() const +{ + /*if (m_procCooldown) + { + if (m_procCooldown > time(NULL)) + return true; + }*/ + return false; +} + +void Aura::AddProcCooldown(uint32 msec) +{ + //m_procCooldown = time(NULL) + msec; +} + +void Aura::PrepareProcToTrigger() +{ + // TODO: allow scripts to prevent charge drop/cooldown + // take one charge, aura expiration will be handled in Aura::TriggerProcOnEvent (if needed) + if (IsUsingCharges()) + { + --m_procCharges; + SetNeedClientUpdateForTargets(); + } + + SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); + + ASSERT(procEntry); + + // cooldowns should be added to the whole aura (see 51698 area aura) + AddProcCooldown(procEntry->cooldown); +} + +bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const +{ + SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); + // only auras with spell proc entry can trigger proc + if (!procEntry) + return false; + + // check if we have charges to proc with + if (IsUsingCharges() && !GetCharges()) + return false; + + // check proc cooldown + if (IsProcOnCooldown()) + return false; + + // TODO: + // something about triggered spells triggering, and add extra attack effect + + // do checks against db data + if (!sSpellMgr->CanSpellTriggerProcOnEvent(*procEntry, eventInfo)) + return false; + + // Check if current equipment meets aura requirements + // do that only for passive spells + // TODO: this needs to be unified for all kinds of auras + Unit* target = aurApp->GetTarget(); + if (IsPassive() && target->GetTypeId() == TYPEID_PLAYER) + { + if (GetSpellProto()->EquippedItemClass == ITEM_CLASS_WEAPON) + { + if (target->ToPlayer()->IsInFeralForm()) + return false; + + if (eventInfo.GetDamageInfo()) + { + WeaponAttackType attType = eventInfo.GetDamageInfo()->GetAttackType(); + Item *item = NULL; + if (attType == BASE_ATTACK) + item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + else if (attType == OFF_ATTACK) + item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + else + item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & GetSpellProto()->EquippedItemSubClassMask)) + return false; + } + } + else if (GetSpellProto()->EquippedItemClass == ITEM_CLASS_ARMOR) + { + // Check if player is wearing shield + Item *item = target->ToPlayer()->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & GetSpellProto()->EquippedItemSubClassMask)) + return false; + } + } + + return roll_chance_f(CalcProcChance(*procEntry, eventInfo)); +} + +float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const +{ + float chance = procEntry.chance; + // calculate chances depending on unit with caster's data + // so talents modifying chances and judgements will have properly calculated proc chance + if (Unit * caster = GetCaster()) + { + // calculate ppm chance if present and we're using weapon + if (eventInfo.GetDamageInfo() && procEntry.ratePerMinute != 0) + { + uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType()); + chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ratePerMinute, GetSpellProto()); + } + // apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell) + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetId(), SPELLMOD_CHANCE_OF_SUCCESS, chance); + } + return chance; +} + +void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) +{ + // TODO: script hooks here (allowing prevention of selected effects) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (aurApp->HasEffect(i)) + GetEffect(i)->HandleProc(aurApp, eventInfo); + // TODO: script hooks here + + // Remove aura if we've used last charge to proc + if (IsUsingCharges() && !GetCharges()) + Remove(); +} + void Aura::_DeleteRemovedApplications() { while (!m_removedApplications.empty()) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index c51ae74b958..6ba2f7fe749 100755 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -25,12 +25,14 @@ class Unit; struct SpellEntry; struct SpellModifier; struct ProcTriggerSpell; +struct SpellProcEntry; // forward decl class AuraEffect; class Aura; class DynamicObject; class AuraScript; +class ProcInfo; // update aura target map every 500 ms instead of every update - reduce amount of grid searcher calls #define UPDATE_TARGET_MAP_INTERVAL 500 @@ -179,6 +181,17 @@ class Aura bool CanBeAppliedOn(Unit* target); bool CheckAreaTarget(Unit* target); + // Proc system + bool IsProcOnCooldown() const; + void AddProcCooldown(uint32 msec); + bool IsUsingCharges() const { return m_isUsingCharges; } + void SetUsingCharges(bool val) { m_isUsingCharges = val; } + void PrepareProcToTrigger(); + bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; + float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; + void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); + + // AuraScript void LoadScripts(); bool CallScriptCheckAreaTargetHandlers(Unit* target); @@ -219,6 +232,7 @@ class Aura bool m_isRemoved:1; bool m_isSingleTarget:1; // true if it's a single target spell and registered at caster - can change at spell steal for example + bool m_isUsingCharges:1; private: Unit::AuraApplicationList m_removedApplications; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index e1bd2450641..f615c48c16b 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1528,7 +1528,8 @@ void SpellMgr::LoadSpellProcs() baseProcEntry.attributesMask = fields[10].GetUInt32(); baseProcEntry.ratePerMinute = fields[11].GetFloat(); baseProcEntry.chance = fields[12].GetFloat(); - baseProcEntry.cooldown = fields[13].GetFloat(); + float cooldown = fields[13].GetFloat(); + baseProcEntry.cooldown = uint32(cooldown); baseProcEntry.charges = fields[14].GetUInt32(); while(true) @@ -1563,7 +1564,7 @@ void SpellMgr::LoadSpellProcs() sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `ratePerMinute` field", spellId); procEntry.ratePerMinute = 0; } - if (procEntry.cooldown < 0) + if (cooldown < 0) { sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `cooldown` field", spellId); procEntry.cooldown = 0; @@ -1579,7 +1580,7 @@ void SpellMgr::LoadSpellProcs() sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `typeMask` value defined, proc will not be triggered", spellId); if (procEntry.spellTypeMask & ~PROC_SPELL_PHASE_MASK_ALL) sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellId, procEntry.spellTypeMask); - if (procEntry.spellTypeMask && !(procEntry.typeMask & SPELL_PROC_FLAG_MASK)) + if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it won't be used for defined `typeMask` value", spellId); if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK) sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `spellPhaseMask` value defined, but it's required for defined `typeMask` value, proc will not be triggered", spellId); @@ -1609,6 +1610,71 @@ void SpellMgr::LoadSpellProcs() sLog->outString(); } +bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) +{ + // proc type doesn't match + if (!(eventInfo.GetTypeMask() & procEntry.typeMask)) + return false; + + // check XP or honor target requirement + if (procEntry.attributesMask & PROC_ATTR_REQ_EXP_OR_HONOR) + if (Player* actor = eventInfo.GetActor()->ToPlayer()) + if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget())) + return false; + + // always trigger for these types + if (eventInfo.GetTypeMask() & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) + return true; + + // check school mask (if set) for other trigger types + if (procEntry.schoolMask && !(eventInfo.GetSchoolMask() & procEntry.schoolMask)) + return false; + + // check spell family name/flags (if set) for spells + if (eventInfo.GetTypeMask() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION)) + { + if (procEntry.spellFamilyName && (procEntry.spellFamilyName != eventInfo.GetSpellInfo()->SpellFamilyName)) + return false; + + if (procEntry.spellFamilyMask && !(procEntry.spellFamilyMask & eventInfo.GetSpellInfo()->SpellFamilyFlags)) + return false; + } + + // check spell type mask (if set) + if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)) + { + if (procEntry.spellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.spellTypeMask)) + return false; + } + + // check spell phase mask + if (eventInfo.GetTypeMask() & REQ_SPELL_PHASE_PROC_FLAG_MASK) + { + if (!(eventInfo.GetSpellPhaseMask() & procEntry.spellPhaseMask)) + return false; + } + + // check hit mask (on taken hit or on done hit, but not on spell cast phase) + if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) || ((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) && !(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST))) + { + uint32 hitMask = procEntry.hitMask; + // get default values if hit mask not set + if (!hitMask) + { + // for taken procs allow normal + critical hits by default + if (eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) + hitMask |= PROC_HIT_NORMAL | PROC_HIT_CRITICAL; + // for done procs allow normal + critical + absorbs by default + else + hitMask |= PROC_HIT_NORMAL | PROC_HIT_CRITICAL | PROC_HIT_ABSORB; + } + if (!(eventInfo.GetHitMask() & hitMask)) + return false; + } + + return true; +} + void SpellMgr::LoadSpellBonusess() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 56ae5259dbf..a18592232cb 100755 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -777,7 +777,7 @@ struct SpellProcEntry uint32 attributesMask; // bitmask, see ProcAttributes float ratePerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60 float chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if perMinuteRate set - float cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura + uint32 cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura uint32 charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite }; @@ -1156,6 +1156,17 @@ class SpellMgr return itr->second; } + // spell proc table + SpellProcEntry const* GetSpellProcEntry(uint32 spellId) const + { + SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId); + if (itr != mSpellProcMap.end()) + return &itr->second; + return NULL; + } + + bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo); + // Spell proc events SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const { diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index 3183f87c5a5..c3b6b3b5770 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -605,7 +605,6 @@ public: DoCast(me, SPELL_BODY_REGEN, true); DoCast(Head, SPELL_FLYING_HEAD, true); DoCast(me, SPELL_CONFUSE, false); //test - done_by->ProcDamageAndSpell(me, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); whirlwind = urand(4000, 8000); regen = 0; } |