From 57d0a5d16d8ca6162d7e141f94ee6d49cba935df Mon Sep 17 00:00:00 2001 From: Aokromes Date: Fri, 21 Oct 2016 07:27:04 +0200 Subject: [PATCH] Revert "Core/Spells: Implementation of QAston proc system" This reverts commit a70e4e26b30dd694a82563131d2c2e8ff3dbdc8e. --- src/server/game/Accounts/RBAC.h | 2 +- src/server/game/Combat/ThreatManager.cpp | 2 +- src/server/game/Entities/Player/Player.cpp | 169 +- src/server/game/Entities/Player/Player.h | 124 +- src/server/game/Entities/Unit/Unit.cpp | 3129 +++++++++++++++-- src/server/game/Entities/Unit/Unit.h | 297 +- src/server/game/Miscellaneous/SharedDefines.h | 2 +- .../game/Spells/Auras/SpellAuraEffects.cpp | 156 +- .../game/Spells/Auras/SpellAuraEffects.h | 7 +- src/server/game/Spells/Auras/SpellAuras.cpp | 127 +- src/server/game/Spells/Auras/SpellAuras.h | 9 +- src/server/game/Spells/Spell.cpp | 251 +- src/server/game/Spells/Spell.h | 6 +- src/server/game/Spells/SpellEffects.cpp | 39 +- src/server/game/Spells/SpellHistory.cpp | 4 +- src/server/game/Spells/SpellInfo.cpp | 12 +- src/server/game/Spells/SpellMgr.cpp | 622 ++-- src/server/game/Spells/SpellMgr.h | 62 +- src/server/game/Spells/SpellScript.cpp | 16 - src/server/game/Spells/SpellScript.h | 17 - src/server/game/World/World.cpp | 3 + src/server/scripts/Commands/cs_reload.cpp | 10 + 22 files changed, 3878 insertions(+), 1188 deletions(-) diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index be766706fb2..300d57530ef 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -602,7 +602,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE = 695, RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL = 696, RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS = 697, - // 698 - reuse + RBAC_PERM_COMMAND_RELOAD_SPELL_PROC_EVENT = 698, RBAC_PERM_COMMAND_RELOAD_SPELL_PROC = 699, RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS = 700, RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION = 701, diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 8bb5a2e7eb6..d2c97458b56 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -44,7 +44,7 @@ float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float return threat; if (Player* modOwner = hatedUnit->GetSpellModOwner()) - modOwner->ApplySpellMod(threatSpell->Id, threat); + modOwner->ApplySpellMod(threatSpell->Id, SPELLMOD_THREAT, threat); } return hatedUnit->ApplyTotalThreatModifier(threat, schoolMask); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 75802497481..bd62dce416e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8184,10 +8184,8 @@ void Player::UpdateEquipSpellsAtFormChange() } } } - -void Player::CastItemCombatSpell(DamageInfo const& damageInfo) +void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx) { - Unit* target = damageInfo.GetVictim(); if (!target || !target->IsAlive() || target == this) return; @@ -8195,9 +8193,7 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo) { // If usable, try to cast item spell if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) - { - if (!item->IsBroken() && CanUseAttackType(damageInfo.GetAttackType())) - { + if (!item->IsBroken() && CanUseAttackType(attType)) if (ItemTemplate const* proto = item->GetTemplate()) { // Additional check for weapons @@ -8205,42 +8201,31 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo) { // offhand item cannot proc from main hand hit etc EquipmentSlots slot; - switch (damageInfo.GetAttackType()) + switch (attType) { - case BASE_ATTACK: - slot = EQUIPMENT_SLOT_MAINHAND; - break; - case OFF_ATTACK: - slot = EQUIPMENT_SLOT_OFFHAND; - break; - case RANGED_ATTACK: - slot = EQUIPMENT_SLOT_RANGED; - break; - default: - slot = EQUIPMENT_SLOT_END; - break; + case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break; + case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break; + case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break; + default: slot = EQUIPMENT_SLOT_END; break; } if (slot != i) continue; // Check if item is useable (forms or disarm) - if (damageInfo.GetAttackType() == BASE_ATTACK) + if (attType == BASE_ATTACK) if (!IsUseEquipedWeapon(true) && !IsInFeralForm()) continue; } - - CastItemCombatSpell(damageInfo, item, proto); + CastItemCombatSpell(target, attType, procVictim, procEx, item, proto); } - } - } } } -void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto) +void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto) { // Can do effect if any damage done to target - // for done procs allow normal + critical + absorbs by default - bool canTrigger = (damageInfo.GetHitMask() & (PROC_HIT_NORMAL | PROC_HIT_CRITICAL | PROC_HIT_ABSORB)) != 0; - if (canTrigger) { + if (procVictim & PROC_FLAG_TAKEN_DAMAGE) + //if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE) + { for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { _Spell const& spellData = proto->Spells[i]; @@ -8269,14 +8254,14 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT if (proto->SpellPPMRate) { - uint32 WeaponSpeed = GetAttackTime(damageInfo.GetAttackType()); + uint32 WeaponSpeed = GetAttackTime(attType); chance = GetPPMProcChance(WeaponSpeed, proto->SpellPPMRate, spellInfo); } else if (chance > 100.0f) chance = GetWeaponProcChance(); if (roll_chance_f(chance)) - CastSpell(damageInfo.GetVictim(), spellInfo->Id, true, item); + CastSpell(target, spellInfo->Id, true, item); } } @@ -8297,17 +8282,18 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT continue; SpellEnchantProcEntry const* entry = sSpellMgr->GetSpellEnchantProcEvent(enchant_id); + if (entry && entry->procEx) { // Check hit/crit/dodge/parry requirement - if ((entry->procEx & damageInfo.GetHitMask()) == 0) + if ((entry->procEx & procEx) == 0) continue; } else { // Can do effect if any damage done to target - // for done procs allow normal + critical + absorbs by default - if (!canTrigger) + if (!(procVictim & PROC_FLAG_TAKEN_DAMAGE)) + //if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)) continue; } @@ -8320,6 +8306,7 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT } float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance(); + if (entry) { if (entry->PPMChance) @@ -8329,7 +8316,7 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT } // Apply spell mods - ApplySpellMod(pEnchant->spellid[s], chance); + ApplySpellMod(pEnchant->spellid[s], SPELLMOD_CHANCE_OF_SUCCESS, chance); // Shiv has 100% chance to apply the poison if (FindCurrentSpellBySpellId(5938) && e_slot == TEMP_ENCHANTMENT_SLOT) @@ -8340,7 +8327,7 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT if (spellInfo->IsPositive()) CastSpell(this, spellInfo, true, item); else - CastSpell(damageInfo.GetVictim(), spellInfo, true, item); + CastSpell(target, spellInfo, true, item); } } } @@ -21201,8 +21188,8 @@ bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod if (!mod || !spellInfo) return false; - // First time this aura applies a mod to us and is out of charges - if (spell && mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges() && !spell->m_appliedMods.count(mod->ownerAura)) + // Mod out of charges + if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end()) return false; // +duration to infinite duration spells making them limited @@ -21225,18 +21212,18 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) size_t writePos = data.wpos(); data << uint32(modTypeCount); data << uint8(mod->op); - for (int32 eff = 0; eff < 96; ++eff) + for (int eff = 0; eff < 96; ++eff) { if (eff != 0 && (eff % 32) == 0) _mask[i++] = 0; _mask[i] = uint32(1) << (eff - (32 * i)); - if ((mod->mask & _mask)) + if (mod->mask & _mask) { int32 val = 0; - for (SpellModifier* spellMod : m_spellMods[mod->op]) - if (spellMod->type == mod->type && (spellMod->mask & _mask)) - val += spellMod->value; + for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr) + if ((*itr)->type == mod->type && (*itr)->mask & _mask) + val += (*itr)->value; val += apply ? mod->value : -(mod->value); data << uint8(eff); @@ -21247,31 +21234,36 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) data.put(writePos, modTypeCount); SendDirectMessage(&data); if (apply) - m_spellMods[mod->op].insert(mod); + m_spellMods[mod->op].push_back(mod); else - m_spellMods[mod->op].erase(mod); + { + m_spellMods[mod->op].remove(mod); + // mods bound to aura will be removed in AuraEffect::~AuraEffect + if (!mod->ownerAura) + delete mod; + } } // Restore spellmods in case of failed cast -void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/) +void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura) { if (!spell || spell->m_appliedMods.empty()) return; std::list aurasQueue; - for (uint8 i = 0; i < MAX_SPELLMOD; ++i) + for (uint8 i=0; iownerAura->IsUsingCharges()) + // Spellmods without aura set cannot be charged + if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges()) continue; // Restore only specific owner aura mods - if (ownerAuraId && mod->spellId != ownerAuraId) + if (ownerAuraId && (ownerAuraId != mod->ownerAura->GetSpellInfo()->Id)) continue; if (aura && mod->ownerAura != aura) @@ -21292,33 +21284,84 @@ void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId /*= 0*/, Aura* au // only see the first of its modifier restored) aurasQueue.push_back(mod->ownerAura); - // add charges back to aura - mod->ownerAura->ModCharges(1); + // add mod charges back to mod + if (mod->charges == -1) + mod->charges = 1; + else + mod->charges++; + + // Do not set more spellmods than available + if (mod->ownerAura->GetCharges() < mod->charges) + mod->charges = mod->ownerAura->GetCharges(); + + // Skip this check for now - aura charges may change due to various reason + /// @todo track these changes correctly + //ASSERT (mod->ownerAura->GetCharges() <= mod->charges); } } - for (Aura* aura : aurasQueue) - spell->m_appliedMods.erase(aura); + for (std::list::iterator itr = aurasQueue.begin(); itr != aurasQueue.end(); ++itr) + { + Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(*itr); + if (iterMod != spell->m_appliedMods.end()) + spell->m_appliedMods.erase(iterMod); + } } -void Player::RestoreAllSpellMods(uint32 ownerAuraId /*= 0*/, Aura* aura /*= nullptr*/) +void Player::RestoreAllSpellMods(uint32 ownerAuraId, Aura* aura) { for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) - if (Spell* spell = m_currentSpells[i]) - RestoreSpellMods(spell, ownerAuraId, aura); + if (m_currentSpells[i]) + RestoreSpellMods(m_currentSpells[i], ownerAuraId, aura); } -void Player::ApplyModToSpell(SpellModifier* mod, Spell* spell) +void Player::RemoveSpellMods(Spell* spell) { if (!spell) return; - // don't do anything with no charges - if (mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges()) + if (spell->m_appliedMods.empty()) return; - // register inside spell, proc system uses this to drop charges - spell->m_appliedMods.insert(mod->ownerAura); + for (uint8 i=0; iownerAura || !mod->ownerAura->IsUsingCharges()) + continue; + + // check if mod affected this spell + Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura); + if (iterMod == spell->m_appliedMods.end()) + continue; + + // remove from list + spell->m_appliedMods.erase(iterMod); + + if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE)) + itr = m_spellMods[i].begin(); + } + } +} + +void Player::DropModCharge(SpellModifier* mod, Spell* spell) +{ + // don't handle spells with proc_event entry defined + // this is a temporary workaround, because all spellmods should be handled like that + if (sSpellMgr->GetSpellProcEvent(mod->spellId)) + return; + + if (spell && mod->ownerAura && mod->charges > 0) + { + if (--mod->charges == 0) + mod->charges = -1; + + spell->m_appliedMods.insert(mod->ownerAura); + } } void Player::SetSpellModTakingSpell(Spell* spell, bool apply) @@ -21329,7 +21372,7 @@ void Player::SetSpellModTakingSpell(Spell* spell, bool apply) if (apply && spell->getState() == SPELL_STATE_FINISHED) return; - m_spellModTakingSpell = apply ? spell : nullptr; + m_spellModTakingSpell = apply ? spell : NULL; } // send Proficiency diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 4fcbc941a8f..545e1ff48b9 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -67,10 +67,10 @@ typedef std::deque PlayerMails; #define PLAYER_EXPLORED_ZONES_SIZE 156 // Note: SPELLMOD_* values is aura types in fact -enum SpellModType : uint8 +enum SpellModType { - SPELLMOD_FLAT = SPELL_AURA_ADD_FLAT_MODIFIER, - SPELLMOD_PCT = SPELL_AURA_ADD_PCT_MODIFIER + SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER + SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER }; // 2^n values, Player::m_isunderwater is a bitmask. These are Trinity internal values, they are never send to any client @@ -155,11 +155,10 @@ enum TalentTree // talent tabs // Spell modifier (used for modify other spells) struct SpellModifier { - SpellModifier(Aura* _ownerAura) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), value(0), mask(), spellId(0), ownerAura(_ownerAura) { } - - SpellModOp op; - SpellModType type; - + SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), charges(0), value(0), mask(), spellId(0), ownerAura(_ownerAura) { } + SpellModOp op : 8; + SpellModType type : 8; + int16 charges : 16; int32 value; flag96 mask; uint32 spellId; @@ -183,7 +182,7 @@ struct PlayerCurrency typedef std::unordered_map PlayerTalentMap; typedef std::unordered_map PlayerSpellMap; -typedef std::unordered_set SpellModContainer; +typedef std::list SpellModList; typedef std::unordered_map PlayerCurrenciesMap; /// Maximum number of CompactUnitFrames profiles @@ -1833,12 +1832,13 @@ class TC_GAME_API Player : public Unit, public GridObject PlayerSpellMap & GetSpellMap() { return m_spells; } void AddSpellMod(SpellModifier* mod, bool apply); - static bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr); - template - void ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell = nullptr) const; + bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr); + template + void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr); + void RemoveSpellMods(Spell* spell); void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr); void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr); - static void ApplyModToSpell(SpellModifier* mod, Spell* spell); + void DropModCharge(SpellModifier* mod, Spell* spell); void SetSpellModTakingSpell(Spell* spell, bool apply); void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false); @@ -2186,9 +2186,9 @@ class TC_GAME_API Player : public Unit, public GridObject void ApplyItemEquipSpell(Item* item, bool apply, bool form_change = false); void ApplyEquipSpell(SpellInfo const* spellInfo, Item* item, bool apply, bool form_change = false); void UpdateEquipSpellsAtFormChange(); - void CastItemCombatSpell(DamageInfo const& damageInfo); - void CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto); + void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx); void CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 cast_count, uint32 glyphIndex); + void CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item* item, ItemTemplate const* proto); void SendEquipmentSetList(); void SetEquipmentSet(uint32 index, EquipmentSet eqset); @@ -2708,7 +2708,7 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 m_baseHealthRegen; int32 m_spellPenetrationItemMod; - SpellModContainer m_spellMods[MAX_SPELLMOD]; + SpellModList m_spellMods[MAX_SPELLMOD]; EnchantDurationList m_enchantDuration; ItemDurationList m_itemDuration; @@ -2879,8 +2879,8 @@ TC_GAME_API void AddItemsSetItem(Player* player, Item* item); TC_GAME_API void RemoveItemsSetItem(Player* player, ItemTemplate const* proto); // "the bodies of template functions must be made available in a header file" -template -void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullptr*/) const +template +void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) @@ -2892,89 +2892,33 @@ void Player::ApplySpellMod(uint32 spellId, T& basevalue, Spell* spell /*= nullpt if (m_spellModTakingSpell) spell = m_spellModTakingSpell; - switch (op) + for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr) { - // special case, if a mod makes spell instant, only consume that mod - case SPELLMOD_CASTING_TIME: - { - SpellModifier* modInstantSpell = nullptr; - for (SpellModifier* mod : m_spellMods[SPELLMOD_CASTING_TIME]) - { - if (!IsAffectedBySpellmod(spellInfo, mod, spell)) - continue; + SpellModifier* mod = *itr; - if (mod->type == SPELLMOD_PCT && basevalue < T(10000) && mod->value <= -100) - { - modInstantSpell = mod; - break; - } - } + // Charges can be set only for mods with auras + if (!mod->ownerAura) + ASSERT(mod->charges == 0); - if (modInstantSpell) - { - Player::ApplyModToSpell(modInstantSpell, spell); - basevalue = T(0); - return; - } - break; - } - // special case if two mods apply 100% critical chance, only consume one - case SPELLMOD_CRITICAL_CHANCE: - { - SpellModifier* modCritical = nullptr; - for (SpellModifier* mod : m_spellMods[SPELLMOD_CRITICAL_CHANCE]) - { - if (!IsAffectedBySpellmod(spellInfo, mod, spell)) - continue; - - if (mod->type == SPELLMOD_FLAT && mod->value >= 100) - { - modCritical = mod; - break; - } - } - - if (modCritical) - { - Player::ApplyModToSpell(modCritical, spell); - basevalue = T(100); - return; - } - break; - } - default: - break; - } - - for (SpellModifier* mod : m_spellMods[op]) - { if (!IsAffectedBySpellmod(spellInfo, mod, spell)) continue; - switch (mod->type) + if (mod->type == SPELLMOD_FLAT) + totalflat += mod->value; + else if (mod->type == SPELLMOD_PCT) { - case SPELLMOD_FLAT: - totalflat += mod->value; - break; - case SPELLMOD_PCT: - { - // skip percent mods for null basevalue (most important for spell mods with charges) - if (basevalue == T(0)) - continue; + // skip percent mods for null basevalue (most important for spell mods with charges) + if (basevalue == T(0)) + continue; - // special case (skip > 10sec spell casts for instant cast setting) - if (op == SPELLMOD_CASTING_TIME) - { - if (mod->value <= -100 && basevalue >= T(10000)) - continue; - } + // special case (skip > 10sec spell casts for instant cast setting) + if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100) + continue; - totalmul += CalculatePct(1.0f, mod->value); - break; - } + totalmul += CalculatePct(1.0f, mod->value); } - Player::ApplyModToSpell(mod, spell); + DropModCharge(mod, spell); } basevalue = T(float(basevalue + totalflat) * totalmul); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f3752147f39..9f6144b03ba 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -92,6 +92,17 @@ float playerBaseMoveSpeed[MAX_MOVE_TYPE] = 3.14f // MOVE_PITCH_RATE }; +// Used for prepare can/can`t triggr aura +static bool InitTriggerAuraData(); +// Define can trigger auras +static bool isTriggerAura[TOTAL_AURAS]; +// Define can't trigger auras (need for disable second trigger) +static bool isNonTriggerAura[TOTAL_AURAS]; +// Triggered always, even from triggered spells +static bool isAlwaysTriggeredAura[TOTAL_AURAS]; +// Prepare lists +static bool procPrepared = InitTriggerAuraData(); + DamageInfo::DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType) : m_attacker(attacker), m_victim(victim), m_damage(damage), m_spellInfo(spellInfo), m_schoolMask(schoolMask), m_damageType(damageType), m_attackType(attackType), m_absorb(0), m_resist(0), m_block(0), m_hitMask(0) @@ -135,23 +146,15 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo) case MELEE_HIT_EVADE: m_hitMask |= PROC_HIT_EVADE; break; - case MELEE_HIT_CRUSHING: - case MELEE_HIT_GLANCING: - case MELEE_HIT_NORMAL: - m_hitMask |= PROC_HIT_NORMAL; - break; - case MELEE_HIT_CRIT: - m_hitMask |= PROC_HIT_CRITICAL; - break; default: break; } } -DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask) +DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType) : m_attacker(spellNonMeleeDamage.attacker), m_victim(spellNonMeleeDamage.target), m_damage(spellNonMeleeDamage.damage), m_spellInfo(sSpellMgr->GetSpellInfo(spellNonMeleeDamage.SpellID)), m_schoolMask(SpellSchoolMask(spellNonMeleeDamage.schoolMask)), m_damageType(damageType), - m_attackType(attackType), m_absorb(spellNonMeleeDamage.absorb), m_resist(spellNonMeleeDamage.resist), m_block(spellNonMeleeDamage.blocked), m_hitMask(hitMask) + m_attackType(attackType), m_absorb(spellNonMeleeDamage.absorb), m_resist(spellNonMeleeDamage.resist), m_block(spellNonMeleeDamage.blocked), m_hitMask(0) { if (spellNonMeleeDamage.blocked) m_hitMask |= PROC_HIT_BLOCK; @@ -198,7 +201,7 @@ uint32 DamageInfo::GetHitMask() const } HealInfo::HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask) - : _healer(healer), _target(target), _heal(heal), _effectiveHeal(0), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask), _hitMask(0) + : _healer(healer), _target(target), _heal(heal), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask), _hitMask(0) { } @@ -207,8 +210,6 @@ void HealInfo::AbsorbHeal(uint32 amount) amount = std::min(amount, GetHeal()); _absorb += amount; _heal -= amount; - amount = std::min(amount, GetEffectiveHeal()); - _effectiveHeal -= amount; _hitMask |= PROC_HIT_ABSORB; } @@ -1096,7 +1097,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama uint32 crit_bonus = damage; // Apply crit_damage bonus for melee spells if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, crit_bonus); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); damage += crit_bonus; // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE @@ -1205,6 +1206,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo = 0; damageInfo->procAttacker = PROC_FLAG_NONE; damageInfo->procVictim = PROC_FLAG_NONE; + damageInfo->procEx = PROC_EX_NONE; damageInfo->hitOutCome = MELEE_HIT_EVADE; if (!victim) @@ -1235,6 +1237,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->HitInfo |= HITINFO_NORMALSWING; damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE; + damageInfo->procEx |= PROC_EX_IMMUNE; damageInfo->damage = 0; damageInfo->cleanDamage = 0; return; @@ -1264,23 +1267,27 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam case MELEE_HIT_EVADE: damageInfo->HitInfo |= HITINFO_MISS | HITINFO_SWINGNOHITSOUND; damageInfo->TargetState = VICTIMSTATE_EVADES; + damageInfo->procEx |= PROC_EX_EVADE; damageInfo->damage = 0; damageInfo->cleanDamage = 0; return; case MELEE_HIT_MISS: damageInfo->HitInfo |= HITINFO_MISS; damageInfo->TargetState = VICTIMSTATE_INTACT; + damageInfo->procEx |= PROC_EX_MISS; damageInfo->damage = 0; damageInfo->cleanDamage = 0; break; case MELEE_HIT_NORMAL: damageInfo->TargetState = VICTIMSTATE_HIT; + damageInfo->procEx |= PROC_EX_NORMAL_HIT; break; case MELEE_HIT_CRIT: { damageInfo->HitInfo |= HITINFO_CRITICALHIT; damageInfo->TargetState = VICTIMSTATE_HIT; + damageInfo->procEx |= PROC_EX_CRITICAL_HIT; // Crit bonus calc damageInfo->damage += damageInfo->damage; float mod = 0.0f; @@ -1299,17 +1306,20 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam } case MELEE_HIT_PARRY: damageInfo->TargetState = VICTIMSTATE_PARRY; + damageInfo->procEx |= PROC_EX_PARRY; damageInfo->cleanDamage += damageInfo->damage; damageInfo->damage = 0; break; case MELEE_HIT_DODGE: damageInfo->TargetState = VICTIMSTATE_DODGE; + damageInfo->procEx |= PROC_EX_DODGE; damageInfo->cleanDamage += damageInfo->damage; damageInfo->damage = 0; break; case MELEE_HIT_BLOCK: damageInfo->TargetState = VICTIMSTATE_HIT; damageInfo->HitInfo |= HITINFO_BLOCK; + damageInfo->procEx |= PROC_EX_BLOCK | PROC_EX_NORMAL_HIT; // 30% damage blocked, double blocked amount if block is critical damageInfo->blocked_amount = CalculatePct(damageInfo->damage, damageInfo->target->isBlockCritical() ? damageInfo->target->GetBlockPercent() * 2 : damageInfo->target->GetBlockPercent()); damageInfo->damage -= damageInfo->blocked_amount; @@ -1319,6 +1329,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam { damageInfo->HitInfo |= HITINFO_GLANCING; damageInfo->TargetState = VICTIMSTATE_HIT; + damageInfo->procEx |= PROC_EX_NORMAL_HIT; int32 leveldif = int32(victim->getLevel()) - int32(getLevel()); if (leveldif > 3) leveldif = 3; @@ -1330,6 +1341,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam case MELEE_HIT_CRUSHING: damageInfo->HitInfo |= HITINFO_CRUSHING; damageInfo->TargetState = VICTIMSTATE_HIT; + damageInfo->procEx |= PROC_EX_NORMAL_HIT; // 150% normal damage damageInfo->damage += (damageInfo->damage / 2); break; @@ -1355,7 +1367,10 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam CalcAbsorbResist(damageInfo->target, SpellSchoolMask(damageInfo->damageSchoolMask), DIRECT_DAMAGE, damageInfo->damage, &damageInfo->absorb, &damageInfo->resist); if (damageInfo->absorb) + { damageInfo->HitInfo |= (damageInfo->damage - damageInfo->absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB); + damageInfo->procEx |= PROC_EX_ABSORB; + } if (damageInfo->resist) damageInfo->HitInfo |= (damageInfo->damage - damageInfo->resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST); @@ -1445,10 +1460,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) } if (GetTypeId() == TYPEID_PLAYER) - { - DamageInfo dmgInfo(*damageInfo); - ToPlayer()->CastItemCombatSpell(dmgInfo); - } + ToPlayer()->CastItemCombatSpell(victim, damageInfo->attackType, damageInfo->procVictim, damageInfo->procEx); // Do effect if any damage done to target if (damageInfo->damage) @@ -1549,7 +1561,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo if (spellInfo) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, armor); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor); AuraEffectList const& resIgnoreAuras = GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); for (AuraEffectList::const_iterator j = resIgnoreAuras.begin(); j != resIgnoreAuras.end(); ++j) @@ -1858,10 +1870,8 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL); DealDamage(caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellInfo(), false); - // break 'Fear' and similar auras - DamageInfo damageInfo(caster, this, splitDamage, (*itr)->GetSpellInfo(), schoolMask, DIRECT_DAMAGE, BASE_ATTACK); - ProcSkillsAndAuras(caster, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, PROC_HIT_NONE, nullptr, &damageInfo, nullptr); + caster->ProcDamageAndSpellFor(true, this, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_NORMAL_HIT, BASE_ATTACK, (*itr)->GetSpellInfo(), splitDamage); } } @@ -1869,22 +1879,21 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe *absorb = dmgInfo.GetAbsorb(); } -void Unit::CalcHealAbsorb(HealInfo& healInfo) +void Unit::CalcHealAbsorb(Unit* victim, SpellInfo const* healSpell, uint32 &healAmount, uint32 &absorb) { - if (!healInfo.GetHeal()) + if (!healAmount) return; - int32 const healing = static_cast(healInfo.GetHeal()); - int32 absorbAmount = 0; + int32 RemainingHeal = healAmount; // Need remove expired auras after bool existExpired = false; // absorb without mana cost - AuraEffectList const& vHealAbsorb = healInfo.GetTarget()->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); - for (AuraEffectList::const_iterator i = vHealAbsorb.begin(); i != vHealAbsorb.end() && absorbAmount <= healing; ++i) + AuraEffectList const& vHealAbsorb = victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB); + for (AuraEffectList::const_iterator i = vHealAbsorb.begin(); i != vHealAbsorb.end() && RemainingHeal > 0; ++i) { - if (!((*i)->GetMiscValue() & healInfo.GetSpellInfo()->SchoolMask)) + if (!((*i)->GetMiscValue() & healSpell->SchoolMask)) continue; // Max Amount can be absorbed by this aura @@ -1899,10 +1908,10 @@ void Unit::CalcHealAbsorb(HealInfo& healInfo) // currentAbsorb - damage can be absorbed by shield // If need absorb less damage - if (healing < currentAbsorb + absorbAmount) - currentAbsorb = healing - absorbAmount; + if (RemainingHeal < currentAbsorb) + currentAbsorb = RemainingHeal; - absorbAmount += currentAbsorb; + RemainingHeal -= currentAbsorb; // Reduce shield amount (*i)->SetAmount((*i)->GetAmount() - currentAbsorb); @@ -1920,19 +1929,19 @@ void Unit::CalcHealAbsorb(HealInfo& healInfo) ++i; if (auraEff->GetAmount() <= 0) { - uint32 removedAuras = healInfo.GetTarget()->m_removedAurasCount; + uint32 removedAuras = victim->m_removedAurasCount; auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL); - if (removedAuras + 1 < healInfo.GetTarget()->m_removedAurasCount) + if (removedAuras+1 < victim->m_removedAurasCount) i = vHealAbsorb.begin(); } } } - if (absorbAmount > 0) - healInfo.AbsorbHeal(absorbAmount); + absorb = RemainingHeal > 0 ? (healAmount - RemainingHeal) : healAmount; + healAmount = RemainingHeal; } -void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra) +void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool extra) { if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) return; @@ -1966,7 +1975,8 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb); SendAttackStateUpdate(&damageInfo); - TriggerAurasProcOnEvent(damageInfo); + //TriggerAurasProcOnEvent(damageInfo); + ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType); DealMeleeDamage(&damageInfo, true); @@ -2492,7 +2502,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Spellmod from SPELLMOD_RESIST_MISS_CHANCE if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, modHitChance); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) @@ -2572,9 +2582,12 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo for (Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) if ((*i)->GetMiscValue() & spellInfo->GetSchoolMask()) reflectchance += (*i)->GetAmount(); - if (reflectchance > 0 && roll_chance_i(reflectchance)) + { + // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) + ProcDamageAndSpell(victim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_REFLECT, 1, BASE_ATTACK, spellInfo); return SPELL_MISS_REFLECT; + } } switch (spellInfo->DmgClass) @@ -4985,16 +4998,15 @@ void Unit::SendSpellNonMeleeDamageLog(Unit* target, uint32 SpellID, uint32 Damag SendSpellNonMeleeDamageLog(&log); } -void Unit::ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +void Unit::ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellInfo const* procSpell, SpellInfo const* procAura) { - WeaponAttackType attType = damageInfo ? damageInfo->GetAttackType() : BASE_ATTACK; - if (typeMaskActor && CanProc()) - ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); - - if (typeMaskActionTarget && actionTarget && actionTarget->CanProc()) - actionTarget->ProcSkillsAndReactives(true, this, typeMaskActionTarget, hitMask, attType); - - TriggerAurasProcOnEvent(actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + // Not much to do if no flags are set. + if (procAttacker) + ProcDamageAndSpellFor(false, victim, procAttacker, procExtra, attType, procSpell, amount, procAura); + // Now go on with a victim's events'n'auras + // Not much to do if no flags are set or there is no victim + if (victim && victim->IsAlive() && procVictim) + victim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpell, amount, procAura); } void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* pInfo) @@ -5286,6 +5298,2168 @@ bool Unit::HandleAuraProcOnPowerAmount(Unit* victim, uint32 /*damage*/, AuraEffe return true; } +//victim may be NULL +bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) +{ + SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); + uint32 effIndex = triggeredByAura->GetEffIndex(); + int32 triggerAmount = triggeredByAura->GetAmount(); + + Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER + ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; + + uint32 triggered_spell_id = 0; + uint32 cooldown_spell_id = 0; // for random trigger, will be one of the triggered spell to avoid repeatable triggers + // otherwise, it's the triggered_spell_id by default + Unit* target = victim; + int32 basepoints0 = 0; + ObjectGuid originalCaster; + + switch (dummySpell->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + { + switch (dummySpell->Id) + { + // Unstable Power + case 24658: + { + if (!procSpell || procSpell->Id == 24659) + return false; + // Need remove one 24659 aura + RemoveAuraFromStack(24659); + return true; + } + // Restless Strength + case 24661: + { + // Need remove one 24662 aura + RemoveAuraFromStack(24662); + return true; + } + // Mark of Malice + case 33493: + { + // Cast finish spell at last charge + if (triggeredByAura->GetBase()->GetCharges() > 1) + return false; + + target = this; + triggered_spell_id = 33494; + break; + } + // Twisted Reflection (boss spell) + case 21063: + triggered_spell_id = 21064; + break; + // Vampiric Aura (boss spell) + case 38196: + { + basepoints0 = 3 * damage; // 300% + if (basepoints0 < 0) + return false; + + triggered_spell_id = 31285; + target = this; + break; + } + // Aura of Madness (Darkmoon Card: Madness trinket) + //===================================================== + // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior) + // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid) + // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid) + // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin) + // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes) + // 41005 Manic: +35 haste (spell, melee and ranged) (All classes) + // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter) + // 41011 Martyr Complex: +35 stamina (All classes) + // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) + // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin) + case 39446: + { + if (GetTypeId() != TYPEID_PLAYER || !IsAlive()) + return false; + + // Select class defined buff + switch (getClass()) + { + case CLASS_PALADIN: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 + case CLASS_DRUID: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 + triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409); + cooldown_spell_id = 39511; + break; + case CLASS_ROGUE: // 39511, 40997, 40998, 41002, 41005, 41011 + case CLASS_WARRIOR: // 39511, 40997, 40998, 41002, 41005, 41011 + case CLASS_DEATH_KNIGHT: + triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011); + cooldown_spell_id = 39511; + break; + case CLASS_PRIEST: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 + case CLASS_SHAMAN: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 + case CLASS_MAGE: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 + case CLASS_WARLOCK: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 + triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409); + cooldown_spell_id = 40999; + break; + case CLASS_HUNTER: // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409 + triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409); + cooldown_spell_id = 40997; + break; + default: + return false; + } + + target = this; + if (roll_chance_i(10)) + ToPlayer()->Say("This is Madness!", LANG_UNIVERSAL); /// @todo It should be moved to database, shouldn't it? + break; + } + // Sunwell Exalted Caster Neck (??? neck) + // cast ??? Light's Wrath if Exalted by Aldor + // cast ??? Arcane Bolt if Exalted by Scryers + case 46569: + return false; // old unused version + // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck) + // cast 45479 Light's Wrath if Exalted by Aldor + // cast 45429 Arcane Bolt if Exalted by Scryers + case 45481: + { + Player* player = ToPlayer(); + if (!player) + return false; + + // Get Aldor reputation rank + if (player->GetReputationRank(932) == REP_EXALTED) + { + target = this; + triggered_spell_id = 45479; + break; + } + // Get Scryers reputation rank + if (player->GetReputationRank(934) == REP_EXALTED) + { + // triggered at positive/self casts also, current attack target used then + if (target && IsFriendlyTo(target)) + { + target = GetVictim(); + if (!target) + { + target = player->GetSelectedUnit(); + if (!target) + return false; + } + if (IsFriendlyTo(target)) + return false; + } + + triggered_spell_id = 45429; + break; + } + return false; + } + // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck) + // cast 45480 Light's Strength if Exalted by Aldor + // cast 45428 Arcane Strike if Exalted by Scryers + case 45482: + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + + // Get Aldor reputation rank + if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) + { + target = this; + triggered_spell_id = 45480; + break; + } + // Get Scryers reputation rank + if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) + { + triggered_spell_id = 45428; + break; + } + return false; + } + // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck) + // cast 45431 Arcane Insight if Exalted by Aldor + // cast 45432 Light's Ward if Exalted by Scryers + case 45483: + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + + // Get Aldor reputation rank + if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) + { + target = this; + triggered_spell_id = 45432; + break; + } + // Get Scryers reputation rank + if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) + { + target = this; + triggered_spell_id = 45431; + break; + } + return false; + } + // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck) + // cast 45478 Light's Salvation if Exalted by Aldor + // cast 45430 Arcane Surge if Exalted by Scryers + case 45484: + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + + // Get Aldor reputation rank + if (ToPlayer()->GetReputationRank(932) == REP_EXALTED) + { + target = this; + triggered_spell_id = 45478; + break; + } + // Get Scryers reputation rank + if (ToPlayer()->GetReputationRank(934) == REP_EXALTED) + { + triggered_spell_id = 45430; + break; + } + return false; + } + // Kill command + case 58914: + { + // Remove aura stack from pet + RemoveAuraFromStack(58914); + Unit* owner = GetOwner(); + if (!owner) + return true; + // reduce the owner's aura stack + owner->RemoveAuraFromStack(34027); + return true; + } + // Vampiric Touch (generic, used by some boss) + case 52723: + case 60501: + { + triggered_spell_id = 52724; + basepoints0 = damage / 2; + target = this; + break; + } + // Shadowfiend Death (Gain mana if pet dies with Glyph of Shadowfiend) + case 57989: + { + Unit* owner = GetOwner(); + if (!owner || owner->GetTypeId() != TYPEID_PLAYER) + return false; + // Glyph of Shadowfiend (need cast as self cast for owner, no hidden cooldown) + owner->CastSpell(owner, 58227, true, castItem, triggeredByAura); + return true; + } + // Divine purpose + case 31871: + case 31872: + { + // Roll chane + if (!victim || !victim->IsAlive() || !roll_chance_i(triggerAmount)) + return false; + + // Remove any stun effect on target + victim->RemoveAurasWithMechanic(1< RandomSpells; + switch (getClass()) + { + case CLASS_WARRIOR: + case CLASS_PALADIN: + case CLASS_DEATH_KNIGHT: + RandomSpells.push_back(71484); + RandomSpells.push_back(71491); + RandomSpells.push_back(71492); + break; + case CLASS_SHAMAN: + case CLASS_ROGUE: + RandomSpells.push_back(71486); + RandomSpells.push_back(71485); + RandomSpells.push_back(71492); + break; + case CLASS_DRUID: + RandomSpells.push_back(71484); + RandomSpells.push_back(71485); + RandomSpells.push_back(71492); + break; + case CLASS_HUNTER: + RandomSpells.push_back(71486); + RandomSpells.push_back(71491); + RandomSpells.push_back(71485); + break; + default: + return false; + } + if (RandomSpells.empty()) // shouldn't happen + return false; + + uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); + CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); + for (std::vector::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) + { + if (!GetSpellHistory()->HasCooldown(*itr)) + GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); + } + break; + } + case 71562: // Deathbringer's Will Heroic + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + + std::vector RandomSpells; + switch (getClass()) + { + case CLASS_WARRIOR: + case CLASS_PALADIN: + case CLASS_DEATH_KNIGHT: + RandomSpells.push_back(71561); + RandomSpells.push_back(71559); + RandomSpells.push_back(71560); + break; + case CLASS_SHAMAN: + case CLASS_ROGUE: + RandomSpells.push_back(71558); + RandomSpells.push_back(71556); + RandomSpells.push_back(71560); + break; + case CLASS_DRUID: + RandomSpells.push_back(71561); + RandomSpells.push_back(71556); + RandomSpells.push_back(71560); + break; + case CLASS_HUNTER: + RandomSpells.push_back(71558); + RandomSpells.push_back(71559); + RandomSpells.push_back(71556); + break; + default: + return false; + } + if (RandomSpells.empty()) // shouldn't happen + return false; + + uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); + CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); + for (std::vector::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) + { + if (!GetSpellHistory()->HasCooldown(*itr)) + GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); + } + break; + } + case 65032: // Boom aura (321 Boombot) + { + if (victim->GetEntry() != 33343) // Scrapbot + return false; + + InstanceScript* instance = GetInstanceScript(); + if (!instance) + return false; + + instance->DoCastSpellOnPlayers(65037); // Achievement criteria marker + break; + } + case 47020: // Enter vehicle XT-002 (Scrapbot) + { + if (GetTypeId() != TYPEID_UNIT) + return false; + + Unit* vehicleBase = GetVehicleBase(); + if (!vehicleBase) + return false; + + // Todo: Check if this amount is blizzlike + vehicleBase->ModifyHealth(int32(vehicleBase->CountPctFromMaxHealth(1))); + break; + } + } + break; + } + case SPELLFAMILY_MAGE: + { + // Hot Streak & Improved Hot Streak + if (dummySpell->SpellIconID == 2999) + { + if (effIndex != 0) + return false; + AuraEffect* counter = triggeredByAura->GetBase()->GetEffect(EFFECT_1); + if (!counter) + return true; + + // Count spell criticals in a row in second aura + if (procEx & PROC_EX_CRITICAL_HIT) + { + counter->SetAmount(counter->GetAmount() * 2); + if (counter->GetAmount() < 100 && dummySpell->Id != 44445) // not enough or Hot Streak spell + return true; + // Crititcal counted -> roll chance + if (roll_chance_i(triggerAmount)) + CastSpell(this, 48108, true, castItem, triggeredByAura); + } + counter->SetAmount(25); + return true; + } + // Incanter's Regalia set (add trigger chance to Mana Shield) + if (dummySpell->SpellFamilyFlags[0] & 0x8000) + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + + target = this; + triggered_spell_id = 37436; + break; + } + } + case SPELLFAMILY_WARLOCK: + { + // Seed of Corruption + if (dummySpell->SpellFamilyFlags[1] & 0x00000010) + { + if (procSpell && procSpell->SpellFamilyFlags[1] & 0x8000) + return false; + // if damage is more than need or target die from damage deal finish spell + if (triggeredByAura->GetAmount() <= int32(damage) || GetHealth() <= damage) + { + // remember caster before aura delete + Unit* caster = triggeredByAura->GetCaster(); + + // Remove aura (before cast for prevent infinite loop handlers) + RemoveAurasDueToSpell(triggeredByAura->GetId()); + + uint32 spell = sSpellMgr->GetSpellWithRank(27285, dummySpell->GetRank()); + + // Cast finish spell (triggeredByAura already not exist!) + if (caster) + caster->CastSpell(this, spell, true, castItem); + return true; // no hidden cooldown + } + + // Damage counting + triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); + return true; + } + // Seed of Corruption (Mobs cast) - no die req + if (dummySpell->SpellFamilyFlags.IsEqual(0, 0, 0) && dummySpell->SpellIconID == 1932) + { + // if damage is more than need deal finish spell + if (triggeredByAura->GetAmount() <= int32(damage)) + { + // remember caster before aura delete + Unit* caster = triggeredByAura->GetCaster(); + + // Remove aura (before cast for prevent infinite loop handlers) + RemoveAurasDueToSpell(triggeredByAura->GetId()); + + // Cast finish spell (triggeredByAura already not exist!) + if (caster) + caster->CastSpell(this, 32865, true, castItem); + return true; // no hidden cooldown + } + // Damage counting + triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); + return true; + } + switch (dummySpell->Id) + { + // Shadowflame (Voidheart Raiment set bonus) + case 37377: + { + triggered_spell_id = 37379; + break; + } + // Pet Healing (Corruptor Raiment or Rift Stalker Armor) + case 37381: + { + target = GetGuardianPet(); + if (!target) + return false; + + // heal amount + basepoints0 = CalculatePct(int32(damage), triggerAmount); + triggered_spell_id = 37382; + break; + } + // Shadowflame Hellfire (Voidheart Raiment set bonus) + case 39437: + { + triggered_spell_id = 37378; + break; + } + } + break; + } + case SPELLFAMILY_PRIEST: + { + switch (dummySpell->Id) + { + // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen) + case 40438: + { + // Shadow Word: Pain + if (!procSpell) + return false; + else if (procSpell->SpellFamilyFlags[0] & 0x8000) + triggered_spell_id = 40441; + // Renew + else if (procSpell->SpellFamilyFlags[0] & 0x40) + triggered_spell_id = 40440; + else + return false; + + target = this; + break; + } + // Oracle Healing Bonus ("Garments of the Oracle" set) + case 26169: + { + // heal amount + basepoints0 = int32(CalculatePct(damage, 10)); + target = this; + triggered_spell_id = 26170; + break; + } + // Frozen Shadoweave (Shadow's Embrace set) warning! its not only priest set + case 39372: + { + if (!procSpell || (procSpell->GetSchoolMask() & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW)) == 0) + return false; + + // heal amount + basepoints0 = CalculatePct(int32(damage), triggerAmount); + target = this; + triggered_spell_id = 39373; + break; + } + // Greater Heal (Vestments of Faith (Priest Tier 3) - 4 pieces bonus) + case 28809: + { + triggered_spell_id = 28810; + break; + } + // Priest T10 Healer 2P Bonus + case 70770: + // Flash Heal + if (procSpell && procSpell->SpellFamilyFlags[0] & 0x800) + { + triggered_spell_id = 70772; + SpellInfo const* blessHealing = sSpellMgr->GetSpellInfo(triggered_spell_id); + if (!blessHealing) + return false; + basepoints0 = int32(CalculatePct(damage, triggerAmount) / (blessHealing->GetMaxDuration() / blessHealing->Effects[0].Amplitude)); + } + break; + } + break; + } + case SPELLFAMILY_DRUID: + { + switch (dummySpell->Id) + { + // Glyph of Bloodletting + case 54815: + { + if (!target) + return false; + + // try to find spell Rip on the target + if (AuraEffect const* AurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00800000, 0x0, 0x0, GetGUID())) + { + // Rip's max duration, note: spells which modifies Rip's duration also counted + uint32 CountMin = AurEff->GetBase()->GetMaxDuration(); + + // just Rip's max duration without other spells + uint32 CountMax = AurEff->GetSpellInfo()->GetMaxDuration(); + + // add possible auras' and Glyph of Shred's max duration + CountMax += 3 * triggerAmount * IN_MILLISECONDS; // Glyph of Bloodletting -> +6 seconds + CountMax += HasAura(60141) ? 4 * IN_MILLISECONDS : 0; // Rip Duration/Lacerate Damage -> +4 seconds + + // if min < max -> that means caster didn't cast 3 shred yet + // so set Rip's duration and max duration + if (CountMin < CountMax) + { + AurEff->GetBase()->SetDuration(AurEff->GetBase()->GetDuration() + triggerAmount * IN_MILLISECONDS); + AurEff->GetBase()->SetMaxDuration(CountMin + triggerAmount * IN_MILLISECONDS); + return true; + } + } + // if not found Rip + return false; + } + // Leader of the Pack + case 24932: + { + if (triggerAmount <= 0) + return false; + basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); + target = this; + triggered_spell_id = 34299; + if (triggeredByAura->GetCasterGUID() != GetGUID()) + break; + int32 basepoints1 = CalculatePct(GetMaxPower(Powers(POWER_MANA)), triggerAmount * 2); + // Improved Leader of the Pack + // Check cooldown of heal spell cooldown + if (!GetSpellHistory()->HasCooldown(34299)) + CastCustomSpell(this, 68285, &basepoints1, 0, 0, true, 0, triggeredByAura); + break; + } + // Healing Touch (Dreamwalker Raiment set) + case 28719: + { + if (procSpell) + { + // mana back + basepoints0 = int32(CalculatePct(procSpell->ManaCost, 30)); + target = this; + triggered_spell_id = 28742; + } + break; + } + // Healing Touch Refund (Idol of Longevity trinket) + case 28847: + { + target = this; + triggered_spell_id = 28848; + break; + } + // Mana Restore (Malorne Raiment set / Malorne Regalia set) + case 37288: + case 37295: + { + target = this; + triggered_spell_id = 37238; + break; + } + // Druid Tier 6 Trinket + case 40442: + { + float chance; + + if (!procSpell) + return false; + // Starfire + else if (procSpell->SpellFamilyFlags[0] & 0x4) + { + triggered_spell_id = 40445; + chance = 25.0f; + } + // Rejuvenation + else if (procSpell->SpellFamilyFlags[0] & 0x10) + { + triggered_spell_id = 40446; + chance = 25.0f; + } + // Mangle (Bear) and Mangle (Cat) + else if (procSpell->SpellFamilyFlags[1] & 0x00000440) + { + triggered_spell_id = 40452; + chance = 40.0f; + } + else + return false; + + if (!roll_chance_f(chance)) + return false; + + target = this; + break; + } + // Item - Druid T10 Balance 4P Bonus + case 70723: + { + // Wrath & Starfire + if (procSpell && (procSpell->SpellFamilyFlags[0] & 0x5) && (procEx & PROC_EX_CRITICAL_HIT)) + { + triggered_spell_id = 71023; + SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); + if (!triggeredSpell) + return false; + basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); + // Add remaining ticks to damage done + basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); + } + break; + } + // Item - Druid T10 Restoration 4P Bonus (Rejuvenation) + case 70664: + { + // Proc only from normal Rejuvenation + if (!procSpell || procSpell->SpellVisual[0] != 32) + return false; + + Player* caster = ToPlayer(); + if (!caster) + return false; + if (!caster->GetGroup() && victim == this) + return false; + + CastCustomSpell(70691, SPELLVALUE_BASE_POINT0, damage, victim, true); + return true; + } + } + break; + } + case SPELLFAMILY_PALADIN: + { + // Judgements of the Wise + if (dummySpell->SpellIconID == 3017) + { + target = this; + triggered_spell_id = 31930; + break; + } + switch (dummySpell->Id) + { + // Sacred Shield + case 53601: + { + if (procFlag & PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS) + return false; + + if (damage > 0) + triggered_spell_id = 58597; + + // Item - Paladin T8 Holy 4P Bonus + if (Unit* caster = triggeredByAura->GetCaster()) + if (AuraEffect const* aurEff = caster->GetAuraEffect(64895, 0)) + cooldown = aurEff->GetAmount(); + + target = this; + break; + } + // Holy Power (Redemption Armor set) + case 28789: + { + if (!victim) + return false; + + // Set class defined buff + switch (victim->getClass()) + { + case CLASS_PALADIN: + case CLASS_PRIEST: + case CLASS_SHAMAN: + case CLASS_DRUID: + triggered_spell_id = 28795; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d. + break; + case CLASS_MAGE: + case CLASS_WARLOCK: + triggered_spell_id = 28793; // Increases the friendly target's spell damage and healing by up to $s1 for $d. + break; + case CLASS_HUNTER: + case CLASS_ROGUE: + triggered_spell_id = 28791; // Increases the friendly target's attack power by $s1 for $d. + break; + case CLASS_WARRIOR: + triggered_spell_id = 28790; // Increases the friendly target's armor + break; + default: + return false; + } + break; + } + case 31801: + { + if (effIndex != 0) // effect 2 used by seal unleashing code + return false; + + // At melee attack or Hammer of the Righteous spell damage considered as melee attack + bool stacker = !procSpell || procSpell->Id == 53595; + // spells with SPELL_DAMAGE_CLASS_MELEE excluding Judgements + bool damager = procSpell && (procSpell->EquippedItemClass != -1 || (procSpell->SpellIconID == 243 && procSpell->SpellVisual[0] == 39)); + + if (!stacker && !damager) + return false; + + triggered_spell_id = 31803; + + // On target with 5 stacks of Censure direct damage is done + if (Aura* aur = victim->GetAura(triggered_spell_id, GetGUID())) + { + if (aur->GetStackAmount() == 5) + { + if (stacker) + aur->RefreshDuration(); + CastSpell(victim, 42463, true); + return true; + } + } + + if (!stacker) + return false; + break; + } + // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal) + case 40470: + { + if (!procSpell) + return false; + + float chance; + + // Flash of light/Holy light + if (procSpell->SpellFamilyFlags[0] & 0xC0000000) + { + triggered_spell_id = 40471; + chance = 15.0f; + } + // Judgement (any) + else if (procSpell->GetSpellSpecific() == SPELL_SPECIFIC_JUDGEMENT) + { + triggered_spell_id = 40472; + chance = 50.0f; + } + else + return false; + + if (!roll_chance_f(chance)) + return false; + + break; + } + // Item - Paladin T8 Holy 2P Bonus + case 64890: + { + triggered_spell_id = 64891; + basepoints0 = triggerAmount * damage / 300; + break; + } + case 71406: // Tiny Abomination in a Jar + case 71545: // Tiny Abomination in a Jar (Heroic) + { + if (!victim || !victim->IsAlive()) + return false; + + CastSpell(this, 71432, true, NULL, triggeredByAura); + + Aura const* dummy = GetAura(71432); + if (!dummy || dummy->GetStackAmount() < (dummySpell->Id == 71406 ? 8 : 7)) + return false; + + RemoveAurasDueToSpell(71432); + triggered_spell_id = 71433; // default main hand attack + // roll if offhand + if (Player const* player = ToPlayer()) + if (player->GetWeaponForAttack(OFF_ATTACK, true) && urand(0, 1)) + triggered_spell_id = 71434; + target = victim; + break; + } + // Item - Icecrown 25 Normal Dagger Proc + case 71880: + { + switch (getPowerType()) + { + case POWER_MANA: + triggered_spell_id = 71881; + break; + case POWER_RAGE: + triggered_spell_id = 71883; + break; + case POWER_ENERGY: + triggered_spell_id = 71882; + break; + case POWER_RUNIC_POWER: + triggered_spell_id = 71884; + break; + default: + return false; + } + break; + } + // Item - Icecrown 25 Heroic Dagger Proc + case 71892: + { + switch (getPowerType()) + { + case POWER_MANA: + triggered_spell_id = 71888; + break; + case POWER_RAGE: + triggered_spell_id = 71886; + break; + case POWER_ENERGY: + triggered_spell_id = 71887; + break; + case POWER_RUNIC_POWER: + triggered_spell_id = 71885; + break; + default: + return false; + } + break; + } + } + break; + } + case SPELLFAMILY_SHAMAN: + { + switch (dummySpell->Id) + { + // Totemic Power (The Earthshatterer set) + case 28823: + { + if (!victim) + return false; + + // Set class defined buff + switch (victim->getClass()) + { + case CLASS_PALADIN: + case CLASS_PRIEST: + case CLASS_SHAMAN: + case CLASS_DRUID: + triggered_spell_id = 28824; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d. + break; + case CLASS_MAGE: + case CLASS_WARLOCK: + triggered_spell_id = 28825; // Increases the friendly target's spell damage and healing by up to $s1 for $d. + break; + case CLASS_HUNTER: + case CLASS_ROGUE: + triggered_spell_id = 28826; // Increases the friendly target's attack power by $s1 for $d. + break; + case CLASS_WARRIOR: + triggered_spell_id = 28827; // Increases the friendly target's armor + break; + default: + return false; + } + break; + } + // Lesser Healing Wave (Totem of Flowing Water Relic) + case 28849: + { + target = this; + triggered_spell_id = 28850; + break; + } + // Windfury Weapon (Passive) 1-8 Ranks + case 33757: + { + Player* player = ToPlayer(); + if (!player || !castItem || !castItem->IsEquipped() || !victim || !victim->IsAlive()) + return false; + + // custom cooldown processing case + if (cooldown && GetSpellHistory()->HasCooldown(dummySpell->Id)) + return false; + + if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID()) + return false; + + WeaponAttackType attType = WeaponAttackType(player->GetAttackBySlot(castItem->GetSlot())); + if ((attType != BASE_ATTACK && attType != OFF_ATTACK) + || (attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) + || (attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)) + return false; + + // Now compute real proc chance... + uint32 chance = 20; + player->ApplySpellMod(dummySpell->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); + + Item* addWeapon = player->GetWeaponForAttack(attType == BASE_ATTACK ? OFF_ATTACK : BASE_ATTACK, true); + uint32 enchant_id_add = addWeapon ? addWeapon->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)) : 0; + SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id_add); + if (pEnchant && pEnchant->spellid[0] == dummySpell->Id) + chance += 14; + + if (!roll_chance_i(chance)) + return false; + + // Now amount of extra power stored in 1 effect of Enchant spell + uint32 spellId = 8232; + SpellInfo const* windfurySpellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!windfurySpellInfo) + { + TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: non-existing spell id: %u (Windfury)", spellId); + return false; + } + + int32 extra_attack_power = CalculateSpellDamage(victim, windfurySpellInfo, 1); + + // Value gained from additional AP + basepoints0 = int32(extra_attack_power / 14.0f * GetAttackTime(attType) / 1000); + + if (procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK) + triggered_spell_id = 25504; + + if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) + triggered_spell_id = 33750; + + // apply cooldown before cast to prevent processing itself + if (cooldown) + player->GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown)); + + // Attack Twice + for (uint32 i = 0; i < 2; ++i) + CastCustomSpell(victim, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); + + return true; + } + // Shaman Tier 6 Trinket + case 40463: + { + if (!procSpell) + return false; + + float chance; + if (procSpell->SpellFamilyFlags[0] & 0x1) + { + triggered_spell_id = 40465; // Lightning Bolt + chance = 15.0f; + } + else if (procSpell->SpellFamilyFlags[0] & 0x80) + { + triggered_spell_id = 40465; // Lesser Healing Wave + chance = 10.0f; + } + else if (procSpell->SpellFamilyFlags[1] & 0x00000010) + { + triggered_spell_id = 40466; // Stormstrike + chance = 50.0f; + } + else + return false; + + if (!roll_chance_f(chance)) + return false; + + target = this; + break; + } + // Spirit Hunt + case 58877: + { + // Cast on owner + target = GetOwner(); + if (!target) + return false; + basepoints0 = CalculatePct(int32(damage), triggerAmount); + triggered_spell_id = 58879; + // Cast on spirit wolf + CastCustomSpell(this, triggered_spell_id, &basepoints0, NULL, NULL, true, NULL, triggeredByAura); + break; + } + // Shaman T8 Elemental 4P Bonus + case 64928: + { + basepoints0 = CalculatePct(int32(damage), triggerAmount); + triggered_spell_id = 64930; // Electrified + break; + } + // Shaman T9 Elemental 4P Bonus + case 67228: + { + // Lava Burst + if (procSpell && procSpell->SpellFamilyFlags[1] & 0x1000) + { + triggered_spell_id = 71824; + SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); + if (!triggeredSpell) + return false; + basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); + } + break; + } + // Item - Shaman T10 Restoration 4P Bonus + case 70808: + { + // Chain Heal + if (procSpell && (procSpell->SpellFamilyFlags[0] & 0x100) && (procEx & PROC_EX_CRITICAL_HIT)) + { + triggered_spell_id = 70809; + SpellInfo const* triggeredSpell = sSpellMgr->GetSpellInfo(triggered_spell_id); + if (!triggeredSpell) + return false; + basepoints0 = CalculatePct(int32(damage), triggerAmount) / (triggeredSpell->GetMaxDuration() / triggeredSpell->Effects[0].Amplitude); + // Add remaining ticks to healing done + basepoints0 += GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL); + } + break; + } + // Item - Shaman T10 Elemental 4P Bonus + case 70817: + { + if (!target) + return false; + // try to find spell Flame Shock on the target + if (AuraEffect const* aurEff = target->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0x0, 0x0, GetGUID())) + { + Aura* flameShock = aurEff->GetBase(); + int32 maxDuration = flameShock->GetMaxDuration(); + int32 newDuration = flameShock->GetDuration() + 2 * aurEff->GetAmplitude(); + + flameShock->SetDuration(newDuration); + // is it blizzlike to change max duration for FS? + if (newDuration > maxDuration) + flameShock->SetMaxDuration(newDuration); + + return true; + } + // if not found Flame Shock + return false; + } + break; + } + // Frozen Power + if (dummySpell->SpellIconID == 3780) + { + if (!target) + return false; + if (GetDistance(target) < 15.0f) + return false; + float chance = (float)triggerAmount; + if (!roll_chance_f(chance)) + return false; + + triggered_spell_id = 63685; + break; + } + // Flametongue Weapon (Passive) + if (dummySpell->SpellFamilyFlags[0] & 0x200000) + { + if (GetTypeId() != TYPEID_PLAYER || !victim || !victim->IsAlive() || !castItem || !castItem->IsEquipped()) + return false; + + WeaponAttackType attType = WeaponAttackType(Player::GetAttackBySlot(castItem->GetSlot())); + if ((attType != BASE_ATTACK && attType != OFF_ATTACK) + || (attType == BASE_ATTACK && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) + || (attType == OFF_ATTACK && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK)) + return false; + + float fire_onhit = float(CalculatePct(dummySpell->Effects[EFFECT_0]. CalcValue(), 1.0f)); + + float add_spellpower = (float)(SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_FIRE) + + victim->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_FIRE)); + + // 1.3speed = 5%, 2.6speed = 10%, 4.0 speed = 15%, so, 1.0speed = 3.84% + ApplyPct(add_spellpower, 3.84f); + + // Enchant on Off-Hand and ready? + if (castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) + { + float BaseWeaponSpeed = GetAttackTime(OFF_ATTACK) / 1000.0f; + + // Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed + basepoints0 = int32((fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed)); + triggered_spell_id = 10444; + } + + // Enchant on Main-Hand and ready? + else if (castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && procFlag & PROC_FLAG_DONE_MAINHAND_ATTACK) + { + float BaseWeaponSpeed = GetAttackTime(BASE_ATTACK) / 1000.0f; + + // Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed + basepoints0 = int32((fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed)); + triggered_spell_id = 10444; + } + + // If not ready, we should return, shouldn't we?! + else + return false; + + CastCustomSpell(victim, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); + return true; + } + // Static Shock + if (dummySpell->SpellIconID == 3059) + { + // Lightning Shield + if (GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0x400, 0, 0)) + { + uint32 spell = 26364; + + // custom cooldown processing case + if (GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(spell)) + GetSpellHistory()->ResetCooldown(spell); + + CastSpell(target, spell, true, castItem, triggeredByAura); + return true; + } + return false; + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Blood-Caked Blade + if (dummySpell->SpellIconID == 138) + { + if (!target || !target->IsAlive()) + return false; + + triggered_spell_id = dummySpell->Effects[effIndex].TriggerSpell; + break; + } + // Dancing Rune Weapon + if (dummySpell->Id == 49028) + { + // 1 dummy aura for dismiss rune blade + if (effIndex != 1) + return false; + + Unit* pPet = NULL; + for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) // Find Rune Weapon + if ((*itr)->GetEntry() == 27893) + { + pPet = *itr; + break; + } + + if (pPet && pPet->GetVictim() && damage && procSpell) + { + uint32 procDmg = damage / 2; + pPet->SendSpellNonMeleeDamageLog(pPet->GetVictim(), procSpell->Id, procDmg, procSpell->GetSchoolMask(), 0, 0, false, 0, false); + pPet->DealDamage(pPet->GetVictim(), procDmg, NULL, SPELL_DIRECT_DAMAGE, procSpell->GetSchoolMask(), procSpell, true); + break; + } + else + return false; + } + // Unholy Blight + if (dummySpell->Id == 49194) + { + triggered_spell_id = 50536; + SpellInfo const* unholyBlight = sSpellMgr->GetSpellInfo(triggered_spell_id); + if (!unholyBlight) + return false; + + basepoints0 = CalculatePct(int32(damage), triggerAmount); + + //Glyph of Unholy Blight + if (AuraEffect* glyph=GetAuraEffect(63332, 0)) + AddPct(basepoints0, glyph->GetAmount()); + + basepoints0 = basepoints0 / (unholyBlight->GetMaxDuration() / unholyBlight->Effects[0].Amplitude); + basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); + break; + } + // Threat of Thassarian + if (dummySpell->SpellIconID == 2023) + { + // Must Dual Wield + if (!procSpell || !haveOffhandWeapon()) + return false; + // Chance as basepoints for dummy aura + if (!roll_chance_i(triggerAmount)) + return false; + + switch (procSpell->Id) + { + case 49020: triggered_spell_id = 66198; break; // Obliterate + case 49143: triggered_spell_id = 66196; break; // Frost Strike + case 45462: triggered_spell_id = 66216; break; // Plague Strike + case 49998: triggered_spell_id = 66188; break; // Death Strike + case 56815: triggered_spell_id = 66217; break; // Rune Strike + case 45902: triggered_spell_id = 66215; break; // Blood Strike + default: + return false; + } + break; + } + // Runic Power Back on Snare/Root + if (dummySpell->Id == 61257) + { + // only for spells and hit/crit (trigger start always) and not start from self cast spells + if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) + return false; + // Need snare or root mechanic + if (procSpell && !(procSpell->GetAllEffectsMechanicMask() & ((1<SpellIconID) + { + // Guard Dog + case 201: + { + if (!victim || !procSpell) + return false; + + triggered_spell_id = 54445; + target = this; + float addThreat = float(CalculatePct(procSpell->Effects[0].CalcValue(this), triggerAmount)); + victim->AddThreat(this, addThreat); + break; + } + // Silverback + case 1582: + triggered_spell_id = dummySpell->Id == 62765 ? 62801 : 62800; + target = this; + break; + } + break; + } + default: + break; + } + + // if not handled by custom case, get triggered spell from dummySpell proto + if (!triggered_spell_id) + triggered_spell_id = dummySpell->Effects[triggeredByAura->GetEffIndex()].TriggerSpell; + + // processed charge only counting case + if (!triggered_spell_id) + return true; + + SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); + if (!triggerEntry) + { + TC_LOG_ERROR("entities.unit", "Unit::HandleDummyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id); + return false; + } + + if (cooldown_spell_id == 0) + cooldown_spell_id = triggered_spell_id; + + if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(cooldown_spell_id)) + return false; + + if (basepoints0) + CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura, originalCaster); + else + CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster); + + if (cooldown && GetTypeId() == TYPEID_PLAYER) + GetSpellHistory()->AddCooldown(cooldown_spell_id, 0, std::chrono::seconds(cooldown)); + + return true; +} + + /* + */ + + +// Used in case when access to whole aura is needed +// All procs should be handled like this... +bool Unit::HandleAuraProc(Unit* victim, uint32 /*damage*/, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown, bool * handled) +{ + SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); + + switch (dummySpell->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + switch (dummySpell->Id) + { + // Nevermelting Ice Crystal + case 71564: + RemoveAuraFromStack(71564); + *handled = true; + break; + // Gaseous Bloat + case 70672: + case 72455: + case 72832: + case 72833: + { + *handled = true; + uint32 stack = triggeredByAura->GetStackAmount(); + int32 const mod = (GetMap()->GetSpawnMode() & 1) ? 1500 : 1250; + int32 dmg = 0; + for (uint8 i = 1; i <= stack; ++i) + dmg += mod * i; + if (Unit* caster = triggeredByAura->GetCaster()) + caster->CastCustomSpell(70701, SPELLVALUE_BASE_POINT0, dmg); + break; + } + // Ball of Flames Proc + case 71756: + case 72782: + case 72783: + case 72784: + RemoveAuraFromStack(dummySpell->Id); + *handled = true; + break; + // Discerning Eye of the Beast + case 59915: + { + CastSpell(this, 59914, true); // 59914 already has correct basepoints in DBC, no need for custom bp + *handled = true; + break; + } + // Swift Hand of Justice + case 59906: + { + int32 bp0 = CalculatePct(GetMaxHealth(), dummySpell->Effects[EFFECT_0]. CalcValue()); + CastCustomSpell(this, 59913, &bp0, NULL, NULL, true); + *handled = true; + break; + } + } + + break; + case SPELLFAMILY_PALADIN: + { + // Judgements of the Just + if (dummySpell->SpellIconID == 3015) + { + *handled = true; + CastSpell(victim, 68055, true); + return true; + } + // Glyph of Divinity + else if (dummySpell->Id == 54939) + { + if (!procSpell) + return false; + *handled = true; + // Check if we are the target and prevent mana gain + if (victim && triggeredByAura->GetCasterGUID() == victim->GetGUID()) + return false; + // Lookup base amount mana restore + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) + { + if (procSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE) + { + // value multiplied by 2 because you should get twice amount + int32 mana = procSpell->Effects[i].CalcValue() * 2; + CastCustomSpell(this, 54986, 0, &mana, NULL, true); + } + } + return true; + } + break; + } + case SPELLFAMILY_MAGE: + { + switch (dummySpell->Id) + { + // Empowered Fire + case 31656: + case 31657: + case 31658: + { + *handled = true; + + SpellInfo const* spInfo = sSpellMgr->GetSpellInfo(67545); + if (!spInfo) + return false; + + int32 bp0 = int32(CalculatePct(GetCreateMana(), spInfo->Effects[0].CalcValue())); + CastCustomSpell(this, 67545, &bp0, NULL, NULL, true, NULL, triggeredByAura->GetEffect(EFFECT_0), GetGUID()); + return true; + } + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Blood of the North + // Reaping + // Death Rune Mastery + /// @todo move those to spell scripts + if (dummySpell->SpellIconID == 3041 || (dummySpell->SpellIconID == 22 && dummySpell->Id != 62459) || dummySpell->SpellIconID == 2622) + { + *handled = true; + // Convert recently used Blood Rune to Death Rune + if (Player* player = ToPlayer()) + { + if (player->getClass() != CLASS_DEATH_KNIGHT) + return false; + + RuneType rune = ToPlayer()->GetLastUsedRune(); + // can't proc from death rune use + if (rune == RUNE_DEATH) + return false; + AuraEffect* aurEff = triggeredByAura->GetEffect(EFFECT_0); + if (!aurEff) + return false; + + // Reset amplitude - set death rune remove timer to 30s + aurEff->ResetPeriodic(true); + uint32 runesLeft; + + if (dummySpell->SpellIconID == 2622) + runesLeft = 2; + else + runesLeft = 1; + + for (uint8 i = 0; i < MAX_RUNES && runesLeft; ++i) + { + if (dummySpell->SpellIconID == 2622) + { + if (player->GetCurrentRune(i) == RUNE_DEATH || + player->GetBaseRune(i) == RUNE_BLOOD) + continue; + } + else + { + if (player->GetCurrentRune(i) == RUNE_DEATH || + player->GetBaseRune(i) != RUNE_BLOOD) + continue; + } + if (player->GetRuneCooldown(i) != (player->GetRuneBaseCooldown(i) - player->GetLastRuneGraceTimer(i))) + continue; + + --runesLeft; + // Mark aura as used + player->AddRuneByAuraEffect(i, RUNE_DEATH, aurEff); + } + return true; + } + return false; + } + + switch (dummySpell->Id) + { + // Bone Shield cooldown + case 49222: + { + *handled = true; + if (cooldown && GetTypeId() == TYPEID_PLAYER) + { + if (GetSpellHistory()->HasCooldown(100000)) + return false; + GetSpellHistory()->AddCooldown(100000, 0, std::chrono::seconds(cooldown)); + } + return true; + } + } + break; + } + case SPELLFAMILY_WARRIOR: + { + switch (dummySpell->Id) + { + // Item - Warrior T10 Protection 4P Bonus + case 70844: + { + int32 basepoints0 = CalculatePct(GetMaxHealth(), dummySpell->Effects[EFFECT_1]. CalcValue()); + CastCustomSpell(this, 70845, &basepoints0, NULL, NULL, true); + break; + } + // Recklessness + case 1719: + { + //! Possible hack alert + //! Don't drop charges on proc, they will be dropped on SpellMod removal + //! Before this change, it was dropping two charges per attack, one in ProcDamageAndSpellFor, and one in RemoveSpellMods. + //! The reason of this behaviour is Recklessness having three auras, 2 of them can not proc (isTriggeredAura array) but the other one can, making the whole spell proc. + *handled = true; + break; + } + default: + break; + } + break; + } + } + return false; +} + +bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown) +{ + // Get triggered aura spell info + SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo(); + + // Basepoints of trigger aura + int32 triggerAmount = triggeredByAura->GetAmount(); + + // Set trigger spell id, target, custom basepoints + uint32 trigger_spell_id = auraSpellInfo->Effects[triggeredByAura->GetEffIndex()].TriggerSpell; + + Unit* target = NULL; + int32 basepoints0 = 0; + + if (triggeredByAura->GetAuraType() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE) + basepoints0 = triggerAmount; + + Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER + ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; + + // Try handle unknown trigger spells + // triggered spells exists only in serverside spell_dbc + /// @todo: reverify and move these spells to spellscripts + { + switch (auraSpellInfo->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + switch (auraSpellInfo->Id) + { + case 43820: // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket) + // Pct value stored in dummy + basepoints0 = victim->GetCreateHealth() * auraSpellInfo->Effects[1].CalcValue() / 100; + target = victim; + break; + case 57345: // Darkmoon Card: Greatness + { + float stat = 0.0f; + // strength + if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 60229;stat = GetStat(STAT_STRENGTH); } + // agility + if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 60233;stat = GetStat(STAT_AGILITY); } + // intellect + if (GetStat(STAT_INTELLECT)> stat) { trigger_spell_id = 60234;stat = GetStat(STAT_INTELLECT);} + // spirit + if (GetStat(STAT_SPIRIT) > stat) { trigger_spell_id = 60235; } + break; + } + case 64568: // Blood Reserve + { + if (HealthBelowPctDamaged(35, damage)) + { + CastCustomSpell(this, 64569, &triggerAmount, NULL, NULL, true); + RemoveAura(64568); + } + return false; + } + case 67702: // Death's Choice, Item - Coliseum 25 Normal Melee Trinket + { + float stat = 0.0f; + // strength + if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67708;stat = GetStat(STAT_STRENGTH); } + // agility + if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67703; } + break; + } + case 67771: // Death's Choice (heroic), Item - Coliseum 25 Heroic Melee Trinket + { + float stat = 0.0f; + // strength + if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67773;stat = GetStat(STAT_STRENGTH); } + // agility + if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67772; } + break; + } + // Mana Drain Trigger + case 27522: + case 40336: + { + // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target. + if (IsAlive()) + CastSpell(this, 29471, true, castItem, triggeredByAura); + if (victim && victim->IsAlive()) + CastSpell(victim, 27526, true, castItem, triggeredByAura); + return true; + } + // Evasive Maneuvers + case 50240: + { + // Remove a Evasive Charge + Aura* charge = GetAura(50241); + if (charge && charge->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL)) + RemoveAurasDueToSpell(50240); + break; + } + // Battle Experience + // already handled in gunship battle script + case 71201: + return false; + } + break; + case SPELLFAMILY_DRUID: + { + switch (auraSpellInfo->Id) + { + // Druid Forms Trinket + case 37336: + { + switch (GetShapeshiftForm()) + { + case FORM_NONE: trigger_spell_id = 37344; break; + case FORM_CAT: trigger_spell_id = 37341; break; + case FORM_BEAR: trigger_spell_id = 37340; break; + case FORM_TREE: trigger_spell_id = 37342; break; + case FORM_MOONKIN: trigger_spell_id = 37343; break; + default: + return false; + } + break; + } + // Druid T9 Feral Relic (Lacerate, Swipe, Mangle, and Shred) + case 67353: + { + switch (GetShapeshiftForm()) + { + case FORM_CAT: trigger_spell_id = 67355; break; + case FORM_BEAR: trigger_spell_id = 67354; break; + default: + return false; + } + break; + } + default: + break; + } + break; + } + case SPELLFAMILY_HUNTER: + { + if (auraSpellInfo->SpellIconID == 3247) // Piercing Shots + { + switch (auraSpellInfo->Id) + { + case 53234: // Rank 1 + case 53237: // Rank 2 + case 53238: // Rank 3 + trigger_spell_id = 63468; + break; + default: + TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Piercing Shots", auraSpellInfo->Id); + return false; + } + SpellInfo const* TriggerPS = sSpellMgr->GetSpellInfo(trigger_spell_id); + if (!TriggerPS) + return false; + + basepoints0 = CalculatePct(int32(damage), triggerAmount) / (TriggerPS->GetMaxDuration() / TriggerPS->Effects[0].Amplitude); + basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_DAMAGE); + break; + } + // Item - Hunter T9 4P Bonus + if (auraSpellInfo->Id == 67151) + { + trigger_spell_id = 68130; + target = this; + break; + } + break; + } + case SPELLFAMILY_PALADIN: + { + switch (auraSpellInfo->Id) + { + // Soul Preserver + case 60510: + { + switch (getClass()) + { + case CLASS_DRUID: + trigger_spell_id = 60512; + break; + case CLASS_PALADIN: + trigger_spell_id = 60513; + break; + case CLASS_PRIEST: + trigger_spell_id = 60514; + break; + case CLASS_SHAMAN: + trigger_spell_id = 60515; + break; + } + + target = this; + break; + } + case 37657: // Lightning Capacitor + case 54841: // Thunder Capacitor + case 67712: // Item - Coliseum 25 Normal Caster Trinket + case 67758: // Item - Coliseum 25 Heroic Caster Trinket + { + if (!victim || !victim->IsAlive() || GetTypeId() != TYPEID_PLAYER) + return false; + + uint32 stack_spell_id = 0; + switch (auraSpellInfo->Id) + { + case 37657: + stack_spell_id = 37658; + trigger_spell_id = 37661; + break; + case 54841: + stack_spell_id = 54842; + trigger_spell_id = 54843; + break; + case 67712: + stack_spell_id = 67713; + trigger_spell_id = 67714; + break; + case 67758: + stack_spell_id = 67759; + trigger_spell_id = 67760; + break; + } + + CastSpell(this, stack_spell_id, true, NULL, triggeredByAura); + + Aura* dummy = GetAura(stack_spell_id); + if (!dummy || dummy->GetStackAmount() < triggerAmount) + return false; + + RemoveAurasDueToSpell(stack_spell_id); + target = victim; + break; + } + default: + break; + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Item - Death Knight T10 Melee 4P Bonus + if (auraSpellInfo->Id == 70656) + { + if (GetTypeId() != TYPEID_PLAYER || getClass() != CLASS_DEATH_KNIGHT) + return false; + + for (uint8 i = 0; i < MAX_RUNES; ++i) + if (ToPlayer()->GetRuneCooldown(i) == 0) + return false; + } + break; + } + case SPELLFAMILY_ROGUE: + { + switch (auraSpellInfo->Id) + { + // Rogue T10 2P bonus, should only proc on caster + case 70805: + { + if (victim != this) + return false; + break; + } + } + break; + } + default: + break; + } + } + + // All ok. Check current trigger spell + SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(trigger_spell_id); + if (triggerEntry == NULL) + { + // Don't cast unknown spell + TC_LOG_ERROR("entities.unit.handleproctriggerspell", "Unit::HandleProcTriggerSpell: Spell %u (effIndex: %u) has unknown TriggerSpell %u. Unhandled custom case?", auraSpellInfo->Id, triggeredByAura->GetEffIndex(), trigger_spell_id); + return false; + } + + // not allow proc extra attack spell at extra attack + if (m_extraAttacks && triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) + return false; + + // Custom requirements (not listed in procEx) Warning! damage dealing after this + // Custom triggered spells + switch (auraSpellInfo->Id) + { + // Deep Wounds + case 12834: + case 12849: + case 12867: + { + if (GetTypeId() != TYPEID_PLAYER) + return false; + + float averageDmg = 0; + // now compute approximate weapon damage by formula from wowwiki.com + if (procFlags & PROC_FLAG_DONE_OFFHAND_ATTACK) + averageDmg = (GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f; + else + averageDmg = (GetFloatValue(UNIT_FIELD_MINDAMAGE) + GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f; + + basepoints0 = int32(averageDmg); + break; + } + // Persistent Shield (Scarab Brooch trinket) + // This spell originally trigger 13567 - Dummy Trigger (vs dummy efect) + case 26467: + { + basepoints0 = int32(CalculatePct(damage, 15)); + target = victim; + trigger_spell_id = 26470; + break; + } + // Unyielding Knights (item exploit 29108\29109) + case 38164: + { + if (!victim || victim->GetEntry() != 19457) // Proc only if your target is Grillok + return false; + break; + } + // Deflection + case 52420: + { + if (!HealthBelowPctDamaged(35, damage)) + return false; + break; + } + + // Cheat Death + case 28845: + { + // When your health drops below 20% + if (HealthBelowPctDamaged(20, damage) || HealthBelowPct(20)) + return false; + break; + } + // Greater Heal Refund (Avatar Raiment set) + case 37594: + { + if (!victim || !victim->IsAlive()) + return false; + + // Doesn't proc if target already has full health + if (victim->IsFullHealth()) + return false; + // If your Greater Heal brings the target to full health, you gain $37595s1 mana. + if (victim->GetHealth() + damage < victim->GetMaxHealth()) + return false; + break; + } + // Bonus Healing (Crystal Spire of Karabor mace) + case 40971: + { + // If your target is below $s1% health + if (!victim || !victim->IsAlive() || victim->HealthAbovePct(triggerAmount)) + return false; + break; + } + // Decimation + case 63156: + case 63158: + // Can proc only if target has hp below 25% + if (!victim || !victim->HealthBelowPct(auraSpellInfo->Effects[EFFECT_1].CalcValue())) + return false; + break; + // Deathbringer Saurfang - Blood Beast's Blood Link + case 72176: + basepoints0 = 3; + break; + ASSERT(procSpell); + // Professor Putricide - Ooze Spell Tank Protection + case 71770: + if (victim) + victim->CastSpell(victim, trigger_spell_id, true); // EffectImplicitTarget is self + return true; + case 45057: // Evasive Maneuvers (Commendation of Kael`thas trinket) + case 71634: // Item - Icecrown 25 Normal Tank Trinket 1 + case 71640: // Item - Icecrown 25 Heroic Tank Trinket 1 + case 75475: // Item - Chamber of Aspects 25 Normal Tank Trinket + case 75481: // Item - Chamber of Aspects 25 Heroic Tank Trinket + { + // Procs only if damage takes health below $s1% + if (!HealthBelowPctDamaged(triggerAmount, damage)) + return false; + break; + } + default: + break; + } + + // Custom basepoints/target for exist spell + // dummy basepoints or other customs + switch (trigger_spell_id) + { + // Auras which should proc on area aura source (caster in this case): + // Cast positive spell on enemy target + case 7099: // Curse of Mending + case 39703: // Curse of Mending + case 29494: // Temptation + case 20233: // Improved Lay on Hands (cast on target) + { + target = victim; + break; + } + // Finish moves that add combo + case 14189: // Seal Fate (Netherblade set) + case 14157: // Ruthlessness + { + if (!victim || victim == this) + return false; + // Need add combopoint AFTER finish movie (or they dropped in finish phase) + break; + } + // Item - Druid T10 Balance 2P Bonus + case 16870: + { + if (HasAura(70718)) + CastSpell(this, 70721, true); + break; + } + // Enlightenment (trigger only from mana cost spells) + case 35095: + { + if (!procSpell || procSpell->PowerType != POWER_MANA || (procSpell->ManaCost == 0 && procSpell->ManaCostPercentage == 0 && procSpell->ManaCostPerlevel == 0)) + return false; + break; + } + case 46916: // Slam! (Bloodsurge proc) + case 52437: // Sudden Death + { + // Item - Warrior T10 Melee 4P Bonus + if (AuraEffect const* aurEff = GetAuraEffect(70847, 0)) + { + if (!roll_chance_i(aurEff->GetAmount())) + break; + CastSpell(this, 70849, true, castItem, triggeredByAura); // Extra Charge! + CastSpell(this, 71072, true, castItem, triggeredByAura); // Slam GCD Reduced + CastSpell(this, 71069, true, castItem, triggeredByAura); // Execute GCD Reduced + } + break; + } + // Maelstrom Weapon + case 53817: + { + // has rank dependant proc chance, ignore too often cases + // PPM = 2.5 * (rank of talent), + uint32 rank = auraSpellInfo->GetRank(); + // 5 rank -> 100% 4 rank -> 80% and etc from full rate + if (!roll_chance_i(20*rank)) + return false; + // Item - Shaman T10 Enhancement 4P Bonus + if (AuraEffect const* aurEff = GetAuraEffect(70832, 0)) + if (Aura const* maelstrom = GetAura(53817)) + if ((maelstrom->GetStackAmount() == maelstrom->GetSpellInfo()->StackAmount) && roll_chance_i(aurEff->GetAmount())) + CastSpell(this, 70831, true, castItem, triggeredByAura); + break; + } + // Glyph of Death's Embrace + case 58679: + { + // Proc only from healing part of Death Coil. Check is essential as all Death Coil spells have 0x2000 mask in SpellFamilyFlags + if (!procSpell || !(procSpell->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && procSpell->SpellFamilyFlags[0] == 0x80002000)) + return false; + break; + } + // Savage Defense + case 62606: + { + basepoints0 = CalculatePct(triggerAmount, GetTotalAttackPowerValue(BASE_ATTACK)); + break; + } + // Body and Soul + case 64128: + case 65081: + { + // Proc only from PW:S cast + if (!(procSpell->SpellFamilyFlags[0] & 0x00000001)) + return false; + break; + } + // Culling the Herd + case 70893: + { + // check if we're doing a critical hit + if (!(procSpell->SpellFamilyFlags[1] & 0x10000000) && (procEx != PROC_EX_CRITICAL_HIT)) + return false; + // check if we're procced by Claw, Bite or Smack (need to use the spell icon ID to detect it) + if (!(procSpell->SpellIconID == 262 || procSpell->SpellIconID == 1680 || procSpell->SpellIconID == 473)) + return false; + break; + } + } + + if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(trigger_spell_id)) + return false; + + // extra attack should hit same target + if (triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) + target = victim; + + // try detect target manually if not set + if (target == NULL) + target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; + + if (basepoints0) + CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); + else + CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); + + if (cooldown && GetTypeId() == TYPEID_PLAYER) + GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown)); + + return true; +} + +bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 cooldown) +{ + int32 scriptId = triggeredByAura->GetMiscValue(); + + if (!victim || !victim->IsAlive()) + return false; + + Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER + ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; + + uint32 triggered_spell_id = 0; + + switch (scriptId) + { + case 4533: // Dreamwalker Raiment 2 pieces bonus + { + // Chance 50% + if (!roll_chance_i(50)) + return false; + + switch (victim->getPowerType()) + { + case POWER_MANA: triggered_spell_id = 28722; break; + case POWER_RAGE: triggered_spell_id = 28723; break; + case POWER_ENERGY: triggered_spell_id = 28724; break; + default: + return false; + } + break; + } + case 4537: // Dreamwalker Raiment 6 pieces bonus + triggered_spell_id = 28750; // Blessing of the Claw + break; + default: + break; + } + + // not processed + if (!triggered_spell_id) + return false; + + // standard non-dummy case + SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); + + if (!triggerEntry) + { + TC_LOG_ERROR("entities.unit", "Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u", triggered_spell_id, scriptId); + return false; + } + + if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(triggered_spell_id)) + return false; + + CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura); + + if (cooldown && GetTypeId() == TYPEID_PLAYER) + GetSpellHistory()->AddCooldown(triggered_spell_id, 0, std::chrono::seconds(cooldown)); + + return true; +} + void Unit::setPowerType(Powers new_powertype) { if (getPowerType() == new_powertype) @@ -5329,10 +7503,6 @@ void Unit::setPowerType(Powers new_powertype) case POWER_ENERGY: SetMaxPower(POWER_ENERGY, uint32(std::ceil(GetCreatePowers(POWER_ENERGY) * powerMultiplier))); break; - case POWER_HAPPINESS: - SetMaxPower(POWER_HAPPINESS, uint32(std::ceil(GetCreatePowers(POWER_HAPPINESS) * powerMultiplier))); - SetPower(POWER_HAPPINESS, uint32(std::ceil(GetCreatePowers(POWER_HAPPINESS) * powerMultiplier))); - break; } } @@ -5379,6 +7549,7 @@ ReputationRank Unit::GetReactionTo(Unit const* target) const return *repRank; } + if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) @@ -5800,9 +7971,9 @@ uint32 Unit::BuildAuraStateUpdateForTarget(Unit* target) const { uint32 auraStates = GetUInt32Value(UNIT_FIELD_AURASTATE) &~(PER_CASTER_AURA_STATE_MASK); for (AuraStateAurasMap::const_iterator itr = m_auraStateAuras.begin(); itr != m_auraStateAuras.end(); ++itr) - if ((1 << (itr->first - 1)) & PER_CASTER_AURA_STATE_MASK) + if ((1<<(itr->first-1)) & PER_CASTER_AURA_STATE_MASK) if (itr->second->GetBase()->GetCasterGUID() == target->GetGUID()) - auraStates |= (1 << (itr->first - 1)); + auraStates |= (1<<(itr->first-1)); return auraStates; } @@ -5814,14 +7985,13 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co if (spellProto) { AuraEffectList const& stateAuras = Caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); - for (AuraEffect const* aurEff : stateAuras) - if (aurEff->IsAffectedOnSpell(spellProto)) + for (AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j) + if ((*j)->IsAffectingSpell(spellProto)) return true; } - // Check per caster aura state // If aura with aurastate by caster not found return false - if ((1 << (flag - 1)) & PER_CASTER_AURA_STATE_MASK) + if ((1<<(flag-1)) & PER_CASTER_AURA_STATE_MASK) { AuraStateAurasMapBounds range = m_auraStateAuras.equal_range(flag); for (AuraStateAurasMap::const_iterator itr = range.first; itr != range.second; ++itr) @@ -5831,7 +8001,7 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co } } - return HasFlag(UNIT_FIELD_AURASTATE, 1 << (flag - 1)); + return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); } void Unit::SetOwnerGUID(ObjectGuid owner) @@ -6199,11 +8369,9 @@ void Unit::SetCharm(Unit* charm, bool apply) } } -void Unit::DealHeal(HealInfo& healInfo) +int32 Unit::DealHeal(Unit* victim, uint32 addhealth) { int32 gain = 0; - Unit* victim = healInfo.GetTarget(); - uint32 addhealth = healInfo.GetHeal(); if (victim->IsAIEnabled) victim->GetAI()->HealReceived(this, addhealth); @@ -6240,8 +8408,7 @@ void Unit::DealHeal(HealInfo& healInfo) player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED, addhealth); } - if (gain) - healInfo.SetEffectiveHeal(gain > 0 ? static_cast(gain) : 0UL); + return gain; } bool Unit::IsMagnet() const @@ -6454,29 +8621,30 @@ void Unit::UnsummonAllTotems() } } -void Unit::SendHealSpellLog(HealInfo& healInfo, bool critical /*= false*/) +void Unit::SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical) { // we guess size WorldPacket data(SMSG_SPELLHEALLOG, 8 + 8 + 4 + 4 + 4 + 4 + 1 + 1); - data << healInfo.GetTarget()->GetPackGUID(); + data << victim->GetPackGUID(); data << GetPackGUID(); - data << uint32(healInfo.GetSpellInfo()->Id); - data << uint32(healInfo.GetHeal()); - data << uint32(healInfo.GetHeal() - healInfo.GetEffectiveHeal()); - data << uint32(healInfo.GetAbsorb()); // Absorb amount + data << uint32(SpellID); + data << uint32(Damage); + data << uint32(OverHeal); + data << uint32(Absorb); // Absorb amount data << uint8(critical ? 1 : 0); data << uint8(0); // unused SendMessageToSet(&data, true); } -int32 Unit::HealBySpell(HealInfo& healInfo, bool critical /*= false*/) +int32 Unit::HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical) { + uint32 absorb = 0; // calculate heal absorb and reduce healing - CalcHealAbsorb(healInfo); + CalcHealAbsorb(victim, spellInfo, addHealth, absorb); - DealHeal(healInfo); - SendHealSpellLog(healInfo, critical); - return healInfo.GetEffectiveHeal(); + int32 gain = DealHeal(victim, addHealth); + SendHealSpellLog(victim, spellInfo->Id, addHealth, uint32(addHealth - gain), absorb, critical); + return gain; } void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, Powers powerType) @@ -6616,7 +8784,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, coeff); + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); coeff /= 100.0f; } DoneTotal += int32(DoneAdvertisedBenefit * coeff * factorMod); @@ -6626,12 +8794,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin float tmpDamage = (int32(pdamage) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellDamagePctDone(victim, spellProto, damagetype)); // apply spellmod to Done damage (flat and pct) if (Player* modOwner = GetSpellModOwner()) - { - if (damagetype == DOT) - modOwner->ApplySpellMod(spellProto->Id, tmpDamage); - else - modOwner->ApplySpellMod(spellProto->Id, tmpDamage); - } + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); return uint32(std::max(tmpDamage, 0.0f)); } @@ -6894,7 +9057,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, coeff); + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); coeff /= 100.0f; } TakenTotal += int32(TakenAdvertisedBenefit * coeff * factorMod); @@ -7038,24 +9201,21 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto if (!((*i)->IsAffectingSpell(spellProto))) continue; - float modChance = 0.f; switch ((*i)->GetMiscValue()) { -/* need help with this block */ // Shatter case 911: if (victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) AddPct(crit_chance, (*i)->GetAmount()*20); break; -/* end need help with this block */ case 7917: // Glyph of Shadowburn if (victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) - crit_chance += (*i)->GetAmount(); + crit_chance+=(*i)->GetAmount(); break; case 7997: // Renewed Hope case 7998: if (victim->HasAura(6788)) - crit_chance += (*i)->GetAmount(); + crit_chance+=(*i)->GetAmount(); break; default: break; @@ -7160,7 +9320,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto // percent done // only players use intelligence for critical chance computations if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, crit_chance); + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); // for this types the bonus was already added in GetUnitCriticalChance, do not add twice if (spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellProto->DmgClass != SPELL_DAMAGE_CLASS_RANGED) @@ -7212,7 +9372,7 @@ uint32 Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage { // adds additional damage to critBonus (from talents) if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, crit_bonus); + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); } crit_bonus += damage; @@ -7331,7 +9491,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui if (Player* modOwner = GetSpellModOwner()) { coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, coeff); + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff); coeff /= 100.0f; } @@ -7356,12 +9516,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui float heal = float(int32(healamount) + DoneTotal) * (damagetype == DOT ? 1.0f : SpellHealingPctDone(victim, spellProto)); // apply spellmod to Done amount if (Player* modOwner = GetSpellModOwner()) - { - if (damagetype == DOT) - modOwner->ApplySpellMod(spellProto->Id, heal); - else - modOwner->ApplySpellMod(spellProto->Id, heal); - } + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); return uint32(std::max(heal, 0.0f)); } @@ -9250,17 +11405,17 @@ float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index { if (Player* modOwner = GetSpellModOwner()) { - modOwner->ApplySpellMod(spellProto->Id, value); + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value); switch (effect_index) { - case EFFECT_0: - modOwner->ApplySpellMod(spellProto->Id, value); + case 0: + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT1, value); break; - case EFFECT_1: - modOwner->ApplySpellMod(spellProto->Id, value); + case 1: + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT2, value); break; - case EFFECT_2: - modOwner->ApplySpellMod(spellProto->Id, value); + case 2: + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT3, value); break; } } @@ -9389,7 +11544,7 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, castTime, spell); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) @@ -9410,7 +11565,7 @@ void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32 & duration, Sp // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, duration, spell); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, duration, spell); if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) @@ -10491,83 +12646,142 @@ bool Unit::isFrozen() const return HasAuraState(AURA_STATE_FROZEN); } -uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) +struct ProcTriggeredData { - uint32 hitMask = PROC_HIT_NONE; + ProcTriggeredData(Aura* _aura) + : aura(_aura) + { + effMask = 0; + spellProcEvent = NULL; + } + SpellProcEventEntry const* spellProcEvent; + Aura* aura; + uint32 effMask; +}; + +typedef std::list< ProcTriggeredData > ProcTriggeredList; + +// List of auras that CAN be trigger but may not exist in spell_proc_event +// in most case need for drop charges +// in some types of aura need do additional check +// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic +bool InitTriggerAuraData() +{ + for (uint16 i = 0; i < TOTAL_AURAS; ++i) + { + isTriggerAura[i] = false; + isNonTriggerAura[i] = false; + isAlwaysTriggeredAura[i] = false; + } + isTriggerAura[SPELL_AURA_PROC_ON_POWER_AMOUNT] = true; + isTriggerAura[SPELL_AURA_DUMMY] = true; + isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; + isTriggerAura[SPELL_AURA_MOD_THREAT] = true; + isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger + isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; + isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; + isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; + isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger + isTriggerAura[SPELL_AURA_MOD_ROOT] = true; + isTriggerAura[SPELL_AURA_TRANSFORM] = true; + isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; + isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; + isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; + isTriggerAura[SPELL_AURA_SCHOOL_ABSORB] = true; // Savage Defense untested + isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; + isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; + isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; + isTriggerAura[SPELL_AURA_MECHANIC_IMMUNITY] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; + isTriggerAura[SPELL_AURA_SPELL_MAGNET] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true; + isTriggerAura[SPELL_AURA_MOD_POWER_REGEN_PERCENT] = true; + isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true; + isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; + isTriggerAura[SPELL_AURA_MOD_MECHANIC_RESISTANCE] = true; + isTriggerAura[SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS] = true; + isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true; + isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE_3] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; + isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true; + isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; + + isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN] = true; + isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK] = true; + + isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; + isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; + isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true; + isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; + + return true; +} + +uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition) +{ + uint32 procEx = PROC_EX_NONE; // Check victim state if (missCondition != SPELL_MISS_NONE) - { switch (missCondition) { - case SPELL_MISS_MISS: - hitMask |= PROC_HIT_MISS; - break; - case SPELL_MISS_DODGE: - hitMask |= PROC_HIT_DODGE; - break; - case SPELL_MISS_PARRY: - hitMask |= PROC_HIT_PARRY; - break; - case SPELL_MISS_BLOCK: - hitMask |= PROC_HIT_BLOCK; - break; - case SPELL_MISS_EVADE: - hitMask |= PROC_HIT_EVADE; - break; - case SPELL_MISS_IMMUNE: - hitMask |= PROC_HIT_IMMUNE; - break; - case SPELL_MISS_IMMUNE2: - hitMask |= PROC_HIT_IMMUNE; - break; - case SPELL_MISS_DEFLECT: - hitMask |= PROC_HIT_DEFLECT; - break; - case SPELL_MISS_ABSORB: - hitMask |= PROC_HIT_ABSORB; - break; - case SPELL_MISS_REFLECT: - hitMask |= PROC_HIT_REFLECT; - break; + case SPELL_MISS_MISS: procEx|=PROC_EX_MISS; break; + case SPELL_MISS_RESIST: procEx|=PROC_EX_RESIST; break; + case SPELL_MISS_DODGE: procEx|=PROC_EX_DODGE; break; + case SPELL_MISS_PARRY: procEx|=PROC_EX_PARRY; break; + case SPELL_MISS_BLOCK: procEx|=PROC_EX_BLOCK; break; + case SPELL_MISS_EVADE: procEx|=PROC_EX_EVADE; break; + case SPELL_MISS_IMMUNE: procEx|=PROC_EX_IMMUNE; break; + case SPELL_MISS_IMMUNE2: procEx|=PROC_EX_IMMUNE; break; + case SPELL_MISS_DEFLECT: procEx|=PROC_EX_DEFLECT;break; + case SPELL_MISS_ABSORB: procEx|=PROC_EX_ABSORB; break; + case SPELL_MISS_REFLECT: procEx|=PROC_EX_REFLECT;break; default: break; } - } else { // On block if (damageInfo->blocked) - hitMask |=PROC_HIT_BLOCK; + procEx|=PROC_EX_BLOCK; // On absorb if (damageInfo->absorb) - hitMask |=PROC_HIT_ABSORB; + procEx|=PROC_EX_ABSORB; // On crit if (damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT) - hitMask |=PROC_HIT_CRITICAL; + procEx|=PROC_EX_CRITICAL_HIT; else - hitMask |=PROC_EX_NORMAL_HIT; + procEx|=PROC_EX_NORMAL_HIT; } - - return hitMask; + return procEx; } -void Unit::ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType) +void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, uint32 damage, SpellInfo const* procAura) { // Player is loaded now - do not allow passive spell casts to proc if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->GetSession()->PlayerLoading()) return; - // For melee/ranged based attack need update skills and set some Aura states if victim present - if (typeMask & MELEE_BASED_TRIGGER_MASK && procTarget) + if (procFlag & MELEE_BASED_TRIGGER_MASK && target) { // If exist crit/parry/dodge/block need update aura state (for victim and attacker) - if (hitMask & (PROC_HIT_CRITICAL | PROC_HIT_PARRY|PROC_HIT_DODGE | PROC_HIT_BLOCK)) + if (procExtra & (PROC_EX_CRITICAL_HIT|PROC_EX_PARRY|PROC_EX_DODGE|PROC_EX_BLOCK)) { // for victim if (isVictim) { // if victim and dodge attack - if (hitMask & PROC_HIT_DODGE) + if (procExtra & PROC_EX_DODGE) { // Update AURA_STATE on dodge if (getClass() != CLASS_ROGUE) // skip Rogue Riposte @@ -10577,7 +12791,7 @@ void Unit::ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMa } } // if victim and parry attack - if (hitMask & PROC_HIT_PARRY) + if (procExtra & PROC_EX_PARRY) { // For Hunters only Counterattack (skip Mongoose bite) if (getClass() == CLASS_HUNTER) @@ -10592,7 +12806,7 @@ void Unit::ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMa } } // if and victim block attack - if (hitMask & PROC_HIT_BLOCK) + if (procExtra & PROC_EX_BLOCK) { ModifyAuraState(AURA_STATE_DEFENSE, true); StartReactiveTimer(REACTIVE_DEFENSE); @@ -10601,40 +12815,357 @@ void Unit::ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMa else // For attacker { // Overpower on victim dodge - if (hitMask & PROC_HIT_DODGE && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) + if (procExtra & PROC_EX_DODGE && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR) { - ToPlayer()->AddComboPoints(procTarget, 1); + ToPlayer()->AddComboPoints(target, 1); StartReactiveTimer(REACTIVE_OVERPOWER); } } } } + + Unit* actor = isVictim ? target : this; + Unit* actionTarget = !isVictim ? target : this; + + DamageInfo damageInfo = DamageInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE, attType); + HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); + ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo); + + ProcTriggeredList procTriggered; + // Fill procTriggered list + for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) + { + // Do not allow auras to proc from effect triggered by itself + if (procAura && procAura->Id == itr->first) + continue; + ProcTriggeredData triggerData(itr->second->GetBase()); + // Defensive procs are active on absorbs (so absorption effects are not a hindrance) + bool active = damage || (procExtra & PROC_EX_BLOCK && isVictim); + if (isVictim) + procExtra &= ~PROC_EX_INTERNAL_REQ_FAMILY; + + SpellInfo const* spellProto = itr->second->GetBase()->GetSpellInfo(); + + // only auras that has triggered spell should proc from fully absorbed damage + if (procExtra & PROC_EX_ABSORB && isVictim) + if (damage || spellProto->Effects[EFFECT_0].TriggerSpell || spellProto->Effects[EFFECT_1].TriggerSpell || spellProto->Effects[EFFECT_2].TriggerSpell) + active = true; + + if (!IsTriggeredAtSpellProcEvent(target, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent)) + continue; + + // do checks using conditions table + if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, spellProto->Id, eventInfo.GetActor(), eventInfo.GetActionTarget())) + continue; + + // AuraScript Hook + if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) + continue; + + bool procSuccess = RollProcResult(target, triggerData.aura, attType, isVictim, triggerData.spellProcEvent); + if (!procSuccess) + continue; + + bool triggeredCanProcAura = true; + // Additional checks for triggered spells (ignore trap casts) + if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) + { + if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) + triggeredCanProcAura = false; + } + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (itr->second->HasEffect(i)) + { + AuraEffect* aurEff = itr->second->GetBase()->GetEffect(i); + // Skip this auras + if (isNonTriggerAura[aurEff->GetAuraType()]) + continue; + // If not trigger by default and spellProcEvent == NULL - skip + if (!isTriggerAura[aurEff->GetAuraType()] && triggerData.spellProcEvent == NULL) + continue; + // Some spells must always trigger + if (triggeredCanProcAura || isAlwaysTriggeredAura[aurEff->GetAuraType()]) + triggerData.effMask |= 1<aura->IsRemoved()) + continue; + + bool useCharges = i->aura->IsUsingCharges(); + // no more charges to use, prevent proc + if (useCharges && !i->aura->GetCharges()) + continue; + + bool takeCharges = false; + SpellInfo const* spellInfo = i->aura->GetSpellInfo(); + uint32 Id = i->aura->GetId(); + + AuraApplication* aurApp = i->aura->GetApplicationOfTarget(GetGUID()); + + bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo); + + //Milliseconds cooldown = Milliseconds::zero(); // temp revert. dig for the 3.3.5a commit changing this. + uint32 cooldown = 0; + if (prepare && i->spellProcEvent && i->spellProcEvent->cooldown) + cooldown = i->spellProcEvent->cooldown; + + // Note: must SetCantProc(false) before return + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) + SetCantProc(true); + + bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); + + // "handled" is needed as long as proc can be handled in multiple places + if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), Id); + takeCharges = true; + } + + if (!handled) + { + for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) + { + if (!(i->effMask & (1<aura->GetEffect(effIndex); + ASSERT(triggeredByAura); + + bool prevented = i->aura->CallScriptEffectProcHandlers(triggeredByAura, aurApp, eventInfo); + if (prevented) + { + takeCharges = true; + continue; + } + + switch (triggeredByAura->GetAuraType()) + { + case SPELL_AURA_PROC_TRIGGER_SPELL: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + // Don`t drop charge or add cooldown for not started trigger + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + takeCharges = true; + break; + } + case SPELL_AURA_PROC_TRIGGER_DAMAGE: + { + // target has to be valid + if (!eventInfo.GetProcTarget()) + break; + + triggeredByAura->HandleProcTriggerDamageAuraProc(aurApp, eventInfo); // this function is part of the new proc system + takeCharges = true; + break; + } + case SPELL_AURA_MANA_SHIELD: + case SPELL_AURA_DUMMY: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + takeCharges = true; + break; + } + case SPELL_AURA_PROC_ON_POWER_AMOUNT: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (HandleAuraProcOnPowerAmount(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + takeCharges = true; + break; + } + case SPELL_AURA_OBS_MOD_POWER: + case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: + case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: + case SPELL_AURA_MOD_POWER_REGEN_PERCENT: + case SPELL_AURA_MOD_MELEE_HASTE: + case SPELL_AURA_MOD_MELEE_HASTE_3: + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); + takeCharges = true; + break; + case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell, cooldown)) + takeCharges = true; + break; + } + case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", + (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + + HandleAuraRaidProcFromChargeWithValue(triggeredByAura); + takeCharges = true; + break; + } + case SPELL_AURA_RAID_PROC_FROM_CHARGE: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", + (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + + HandleAuraRaidProcFromCharge(triggeredByAura); + takeCharges = true; + break; + } + case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: + { + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + takeCharges = true; + break; + } + case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: + // Skip melee hits or instant cast spells + if (procSpell && procSpell->CalcCastTime(getLevel()) > 0) + takeCharges = true; + break; + case SPELL_AURA_REFLECT_SPELLS_SCHOOL: + // Skip Melee hits and spells ws wrong school + if (procSpell && (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check + takeCharges = true; + break; + case SPELL_AURA_SPELL_MAGNET: + // Skip Melee hits and targets with magnet aura + if (procSpell && (triggeredByAura->GetBase()->GetUnitOwner()->ToUnit() == ToUnit())) // Magnet + takeCharges = true; + break; + case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: + case SPELL_AURA_MOD_POWER_COST_SCHOOL: + // Skip melee hits and spells ws wrong school or zero cost + if (procSpell && + (procSpell->ManaCost != 0 || procSpell->ManaCostPercentage != 0) && // Cost check + (triggeredByAura->GetMiscValue() & procSpell->SchoolMask)) // School check + takeCharges = true; + break; + case SPELL_AURA_MECHANIC_IMMUNITY: + // Compare mechanic + if (procSpell && procSpell->Mechanic == uint32(triggeredByAura->GetMiscValue())) + takeCharges = true; + break; + case SPELL_AURA_MOD_MECHANIC_RESISTANCE: + // Compare mechanic + if (procSpell && procSpell->Mechanic == uint32(triggeredByAura->GetMiscValue())) + takeCharges = true; + break; + case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: + // Compare casters + if (triggeredByAura->GetCasterGUID() == target->GetGUID()) + takeCharges = true; + break; + // CC Auras which use their amount amount to drop + // Are there any more auras which need this? + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + case SPELL_AURA_MOD_ROOT: + case SPELL_AURA_TRANSFORM: + { + // chargeable mods are breaking on hit + if (useCharges) + takeCharges = true; + else + { + // Spell own direct damage at apply wont break the CC + if (procSpell && (procSpell->Id == triggeredByAura->GetId())) + { + Aura* aura = triggeredByAura->GetBase(); + // called from spellcast, should not have ticked yet + if (aura->GetDuration() == aura->GetMaxDuration()) + break; + } + int32 damageLeft = triggeredByAura->GetAmount(); + // No damage left + if (damageLeft < int32(damage)) + i->aura->Remove(); + else + triggeredByAura->SetAmount(damageLeft - damage); + } + break; + } + //case SPELL_AURA_ADD_FLAT_MODIFIER: + //case SPELL_AURA_ADD_PCT_MODIFIER: + // HandleSpellModAuraProc + //break; + default: + // nothing do, just charges counter + takeCharges = true; + break; + } // switch (triggeredByAura->GetAuraType()) + i->aura->CallScriptAfterEffectProcHandlers(triggeredByAura, aurApp, eventInfo); + } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) + } // if (!handled) + + // Remove charge (aura can be removed by triggers) + if (prepare && useCharges && takeCharges) + { + // Set charge drop delay (only for missiles) + if ((procExtra & PROC_EX_REFLECT) && target && procSpell && procSpell->Speed > 0.0f) + { + // Set up missile speed based delay (from Spell.cpp: Spell::AddUnitTarget()::L2237) + uint32 delay = uint32(std::floor(std::max(target->GetDistance(this), 5.0f) / procSpell->Speed * 1000.0f)); + // Schedule charge drop + i->aura->DropChargeDelayed(delay); + } + else + i->aura->DropCharge(); + } + + i->aura->CallScriptAfterProcHandlers(aurApp, eventInfo); + + if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) + SetCantProc(false); + } + + // Cleanup proc requirements + if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC)) + SetCantProc(false); } -void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo& eventInfo) +void Unit::GetProcAurasTriggeredOnEvent(std::list& aurasTriggeringProc, std::list* procAuras, ProcEventInfo eventInfo) { // use provided list of auras which can proc if (procAuras) { - for (AuraApplication* aurApp : *procAuras) + for (std::list::iterator itr = procAuras->begin(); itr!= procAuras->end(); ++itr) { - ASSERT(aurApp->GetTarget() == this); - if (uint8 procEffectMask = aurApp->GetBase()->IsProcTriggeredOnEvent(aurApp, eventInfo, now)) - { - aurApp->GetBase()->PrepareProcToTrigger(aurApp, eventInfo, now); - aurasTriggeringProc.emplace_back(std::make_pair(procEffectMask, aurApp)); - } + ASSERT((*itr)->GetTarget() == this); + if (!(*itr)->GetRemoveMode()) + if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo)) + { + (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo); + aurasTriggeringProc.push_back(*itr); + } } } // or generate one on our own else { - for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr != GetAppliedAuras().end(); ++itr) + for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) { - if (uint8 procEffectMask = itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) + if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo)) { itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo); - aurasTriggeringProc.emplace_back(std::make_pair(procEffectMask, itr->second)); + aurasTriggeringProc.push_back(itr->second); } } } @@ -10643,49 +13174,34 @@ void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTrigg void Unit::TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo) { DamageInfo dmgInfo = DamageInfo(damageInfo); - TriggerAurasProcOnEvent(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr); + TriggerAurasProcOnEvent(NULL, NULL, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, 0, 0, damageInfo.procEx, NULL, &dmgInfo, NULL); } -void Unit::TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +void Unit::TriggerAurasProcOnEvent(std::list* myProcAuras, std::list* 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(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); - if (typeMaskActor && CanProc()) - { - AuraApplicationProcContainer myAurasTriggeringProc; - GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, nullptr, myProcEventInfo); - TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); - } + ProcEventInfo myProcEventInfo = ProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + std::list myAurasTriggeringProc; + GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, myProcAuras, myProcEventInfo); // prepare data for target trigger - ProcEventInfo targetProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); - if (typeMaskActionTarget && actionTarget && actionTarget->CanProc()) - { - AuraApplicationProcContainer targetAurasTriggeringProc; - actionTarget->GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, nullptr, targetProcEventInfo); - actionTarget->TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); - } + ProcEventInfo targetProcEventInfo = ProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + std::list targetAurasTriggeringProc; + if (typeMaskActionTarget) + GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, targetProcAuras, targetProcEventInfo); + + TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc); + + if (typeMaskActionTarget) + TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc); } -void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& aurasTriggeringProc) +void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list& aurasTriggeringProc) { - for (auto const& aurAppProc : aurasTriggeringProc) + for (std::list::iterator itr = aurasTriggeringProc.begin(); itr != aurasTriggeringProc.end(); ++itr) { - AuraApplication* aurApp; - uint8 procEffectMask; - std::tie(procEffectMask, aurApp) = aurAppProc; - - if (aurApp->GetRemoveMode()) - continue; - - SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); - if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) - SetCantProc(true); - - aurApp->GetBase()->TriggerProcOnEvent(procEffectMask, aurApp, eventInfo); - - if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) - SetCantProc(false); + if (!(*itr)->GetRemoveMode()) + (*itr)->GetBase()->TriggerProcOnEvent(*itr, eventInfo); } } @@ -11282,6 +13798,202 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } +bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent) +{ + SpellInfo const* spellInfo = aura->GetSpellInfo(); + + // let the aura be handled by new proc system if it has new entry + if (sSpellMgr->GetSpellProcEntry(spellInfo->Id)) + return false; + + // Get proc Event Entry + spellProcEvent = sSpellMgr->GetSpellProcEvent(spellInfo->Id); + + // Get EventProcFlag + uint32 EventProcFlag; + if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags + EventProcFlag = spellProcEvent->procFlags; + else + EventProcFlag = spellInfo->ProcFlags; // else get from spell proto + // Continue if no trigger exist + if (!EventProcFlag) + return false; + + // Check spellProcEvent data requirements + if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellInfo, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) + return false; + // In most cases req get honor or XP from kill + if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) + { + bool allow = false; + + if (victim) + allow = ToPlayer()->isHonorOrXPTarget(victim); + + // Shadow Word: Death - can trigger from every kill + if (aura->GetId() == 32409) + allow = true; + if (!allow) + return false; + } + // Aura added by spell can`t trigger from self (prevent drop charges/do triggers) + // But except periodic and kill triggers (can triggered from self) + if (procSpell && procSpell->Id == spellInfo->Id + && !(spellInfo->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) + return false; + + // Check if current equipment allows aura to proc + if (!isVictim && GetTypeId() == TYPEID_PLAYER) + { + Player* player = ToPlayer(); + if (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON) + { + Item* item = NULL; + if (attType == BASE_ATTACK) + item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + else if (attType == OFF_ATTACK) + item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + else + item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + + if (player->IsInFeralForm()) + return false; + + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) + return false; + } + else if (spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR) + { + // Check if player is wearing shield + Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) + return false; + } + } + + return true; +} + +bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent) +{ + SpellInfo const* spellInfo = aura->GetSpellInfo(); + // Get chance from spell + float chance = float(spellInfo->ProcChance); + // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance; + if (spellProcEvent && spellProcEvent->customChance) + chance = spellProcEvent->customChance; + // If PPM exist calculate chance from PPM + if (spellProcEvent && spellProcEvent->ppmRate != 0) + { + if (!isVictim) + { + uint32 weaponSpeed = GetAttackTime(attType); + chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); + } + else if (victim) + { + uint32 weaponSpeed = victim->GetAttackTime(attType); + chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); + } + } + // Apply chance modifer aura + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); + return roll_chance_f(chance); +} + +bool Unit::HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura) +{ + // aura can be deleted at casts + SpellInfo const* spellProto = triggeredByAura->GetSpellInfo(); + int32 heal = triggeredByAura->GetAmount(); + ObjectGuid caster_guid = triggeredByAura->GetCasterGUID(); + + // Currently only Prayer of Mending + if (!(spellProto->SpellFamilyName == SPELLFAMILY_PRIEST && spellProto->SpellFamilyFlags[1] & 0x20)) + { + TC_LOG_DEBUG("spells", "Unit::HandleAuraRaidProcFromChargeWithValue, received not handled spell: %u", spellProto->Id); + return false; + } + + // jumps + int32 jumps = triggeredByAura->GetBase()->GetCharges()-1; + + // current aura expire + triggeredByAura->GetBase()->SetCharges(1); // will removed at next charges decrease + + // next target selection + if (jumps > 0) + { + if (Unit* caster = triggeredByAura->GetCaster()) + { + float radius = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcRadius(caster); + + if (Unit* target = GetNextRandomRaidMemberOrPet(radius)) + { + CastCustomSpell(target, spellProto->Id, &heal, NULL, NULL, true, NULL, triggeredByAura, caster_guid); + if (Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID())) + aura->SetCharges(jumps); + } + } + } + + // heal + CastCustomSpell(this, 33110, &heal, NULL, NULL, true, NULL, NULL, caster_guid); + return true; + +} +bool Unit::HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura) +{ + // aura can be deleted at casts + SpellInfo const* spellProto = triggeredByAura->GetSpellInfo(); + + uint32 damageSpellId; + switch (spellProto->Id) + { + case 57949: // shiver + damageSpellId = 57952; + //animationSpellId = 57951; dummy effects for jump spell have unknown use (see also 41637) + break; + case 59978: // shiver + damageSpellId = 59979; + break; + case 43593: // Cold Stare + damageSpellId = 43594; + break; + default: + TC_LOG_ERROR("entities.unit", "Unit::HandleAuraRaidProcFromCharge, received unhandled spell: %u", spellProto->Id); + return false; + } + + ObjectGuid caster_guid = triggeredByAura->GetCasterGUID(); + + // jumps + int32 jumps = triggeredByAura->GetBase()->GetCharges()-1; + + // current aura expire + triggeredByAura->GetBase()->SetCharges(1); // will removed at next charges decrease + + // next target selection + if (jumps > 0) + { + if (Unit* caster = triggeredByAura->GetCaster()) + { + float radius = triggeredByAura->GetSpellInfo()->Effects[triggeredByAura->GetEffIndex()].CalcRadius(caster); + if (Unit* target= GetNextRandomRaidMemberOrPet(radius)) + { + CastSpell(target, spellProto, true, NULL, triggeredByAura, caster_guid); + if (Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID())) + aura->SetCharges(jumps); + } + } + } + + CastSpell(this, damageSpellId, true, NULL, triggeredByAura, caster_guid); + + return true; +} + void Unit::SendDurabilityLoss(Player* receiver, uint32 percent) { WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 4); @@ -11391,17 +14103,14 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) // Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim if (IsPet() || IsTotem()) - { - // proc only once for victim if (Unit* owner = GetOwner()) - owner->ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); - } + owner->ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_EX_NONE, 0); if (!victim->IsCritter()) - ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); // Proc auras on death - must be before aura/combat remove - victim->ProcSkillsAndAuras(victim, PROC_FLAG_NONE, PROC_FLAG_DEATH, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + victim->ProcDamageAndSpell(NULL, PROC_FLAG_DEATH, PROC_FLAG_NONE, PROC_EX_NONE, 0, BASE_ATTACK, 0); // update get killing blow achievements, must be done before setDeathState to be able to require auras on target // and before Spirit of Redemption as it also removes auras @@ -12361,7 +15070,7 @@ float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, u if (spellId) { if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, hitChance); + modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance); } missChance += hitChance - 100.0f; @@ -14459,4 +17168,8 @@ SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo) const { if (uint32(auraEffect->GetMiscValue()) == spellInfo->Id || auraEffect->IsAffectingSpell(spellInfo)) if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(auraEffect->GetAmount())) - \ No newline at end of file + return newInfo; + } + + return spellInfo; +} diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 07028c48f89..686dd62c1e7 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -82,43 +82,43 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE) }; -enum SpellModOp : uint8 +enum SpellModOp { - SPELLMOD_DAMAGE = 0, - SPELLMOD_DURATION = 1, - SPELLMOD_THREAT = 2, - SPELLMOD_EFFECT1 = 3, - SPELLMOD_CHARGES = 4, - SPELLMOD_RANGE = 5, - SPELLMOD_RADIUS = 6, - SPELLMOD_CRITICAL_CHANCE = 7, - SPELLMOD_ALL_EFFECTS = 8, - SPELLMOD_NOT_LOSE_CASTING_TIME = 9, - SPELLMOD_CASTING_TIME = 10, - SPELLMOD_COOLDOWN = 11, - SPELLMOD_EFFECT2 = 12, - SPELLMOD_IGNORE_ARMOR = 13, - SPELLMOD_COST = 14, - SPELLMOD_CRIT_DAMAGE_BONUS = 15, - SPELLMOD_RESIST_MISS_CHANCE = 16, - SPELLMOD_JUMP_TARGETS = 17, - SPELLMOD_CHANCE_OF_SUCCESS = 18, - SPELLMOD_ACTIVATION_TIME = 19, - SPELLMOD_DAMAGE_MULTIPLIER = 20, - SPELLMOD_GLOBAL_COOLDOWN = 21, - SPELLMOD_DOT = 22, - SPELLMOD_EFFECT3 = 23, - SPELLMOD_BONUS_MULTIPLIER = 24, + SPELLMOD_DAMAGE = 0, + SPELLMOD_DURATION = 1, + SPELLMOD_THREAT = 2, + SPELLMOD_EFFECT1 = 3, + SPELLMOD_CHARGES = 4, + SPELLMOD_RANGE = 5, + SPELLMOD_RADIUS = 6, + SPELLMOD_CRITICAL_CHANCE = 7, + SPELLMOD_ALL_EFFECTS = 8, + SPELLMOD_NOT_LOSE_CASTING_TIME = 9, + SPELLMOD_CASTING_TIME = 10, + SPELLMOD_COOLDOWN = 11, + SPELLMOD_EFFECT2 = 12, + SPELLMOD_IGNORE_ARMOR = 13, + SPELLMOD_COST = 14, + SPELLMOD_CRIT_DAMAGE_BONUS = 15, + SPELLMOD_RESIST_MISS_CHANCE = 16, + SPELLMOD_JUMP_TARGETS = 17, + SPELLMOD_CHANCE_OF_SUCCESS = 18, + SPELLMOD_ACTIVATION_TIME = 19, + SPELLMOD_DAMAGE_MULTIPLIER = 20, + SPELLMOD_GLOBAL_COOLDOWN = 21, + SPELLMOD_DOT = 22, + SPELLMOD_EFFECT3 = 23, + SPELLMOD_BONUS_MULTIPLIER = 24, // spellmod 25 - SPELLMOD_PROC_PER_MINUTE = 26, - SPELLMOD_VALUE_MULTIPLIER = 27, - SPELLMOD_RESIST_DISPEL_CHANCE = 28, - SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell - SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30, - - MAX_SPELLMOD + SPELLMOD_PROC_PER_MINUTE = 26, + SPELLMOD_VALUE_MULTIPLIER = 27, + SPELLMOD_RESIST_DISPEL_CHANCE = 28, + SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell + SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30 }; +#define MAX_SPELLMOD 32 + enum SpellValueMod { SPELLVALUE_BASE_POINT0, @@ -432,7 +432,7 @@ enum TriggerCastFlags TRIGGERED_IGNORE_CASTER_AURASTATE = 0x00000800, //! Will ignore caster aura states including combat requirements and death state TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE = 0x00002000, //! Will ignore mounted/on vehicle restrictions TRIGGERED_IGNORE_CASTER_AURAS = 0x00010000, //! Will ignore caster aura restrictions or requirements - // reuse 0x00020000 + TRIGGERED_DISALLOW_PROC_EVENTS = 0x00020000, //! Disallows proc events from triggered spell (default) TRIGGERED_DONT_REPORT_CAST_ERROR = 0x00040000, //! Will return SPELL_FAILED_DONT_REPORT in CheckCast functions TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT = 0x00080000, //! Will ignore equipped item requirements TRIGGERED_IGNORE_TARGET_CHECK = 0x00100000, //! Will ignore most target checks (mostly DBC target checks) @@ -823,13 +823,13 @@ struct DiminishingReturn : DRGroup(group), stack(0), hitTime(t), hitCount(count) { } - DiminishingGroup DRGroup; - uint16 stack; + DiminishingGroup DRGroup:16; + uint16 stack:16; uint32 hitTime; uint32 hitCount; }; -enum MeleeHitOutcome : uint8 +enum MeleeHitOutcome { MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY, MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL @@ -837,18 +837,21 @@ enum MeleeHitOutcome : uint8 class DispelInfo { - public: - explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : - _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } +public: + explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : + _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } - Unit* GetDispeller() const { return _dispellerUnit; } - uint32 GetDispellerSpellId() const { return _dispellerSpell; } - uint8 GetRemovedCharges() const { return _chargesRemoved; } - void SetRemovedCharges(uint8 amount) { _chargesRemoved = amount; } - private: - Unit* _dispellerUnit; - uint32 _dispellerSpell; - uint8 _chargesRemoved; + Unit* GetDispeller() const { return _dispellerUnit; } + uint32 GetDispellerSpellId() const { return _dispellerSpell; } + uint8 GetRemovedCharges() const { return _chargesRemoved; } + void SetRemovedCharges(uint8 amount) + { + _chargesRemoved = amount; + } +private: + Unit* _dispellerUnit; + uint32 _dispellerSpell; + uint8 _chargesRemoved; }; struct CleanDamage @@ -863,110 +866,105 @@ struct CleanDamage MeleeHitOutcome hitOutCome; }; -struct SpellNonMeleeDamage; +struct CalcDamageInfo; class TC_GAME_API DamageInfo { - private: - Unit* const m_attacker; - Unit* const m_victim; - uint32 m_damage; - SpellInfo const* const m_spellInfo; - SpellSchoolMask const m_schoolMask; - DamageEffectType const m_damageType; - WeaponAttackType m_attackType; - uint32 m_absorb; - uint32 m_resist; - uint32 m_block; - uint32 m_hitMask; - public: - DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType); - explicit DamageInfo(CalcDamageInfo const& dmgInfo); - DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask); +private: + Unit* const m_attacker; + Unit* const m_victim; + uint32 m_damage; + SpellInfo const* const m_spellInfo; + SpellSchoolMask const m_schoolMask; + DamageEffectType const m_damageType; + WeaponAttackType m_attackType; + uint32 m_absorb; + uint32 m_resist; + uint32 m_block; + uint32 m_hitMask; +public: + DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType); + explicit DamageInfo(CalcDamageInfo const& dmgInfo); + DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType); - void ModifyDamage(int32 amount); - void AbsorbDamage(uint32 amount); - void ResistDamage(uint32 amount); - void BlockDamage(uint32 amount); + 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; } - SpellInfo 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; } + Unit* GetAttacker() const { return m_attacker; } + Unit* GetVictim() const { return m_victim; } + SpellInfo 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; } - uint32 GetHitMask() const; + uint32 GetHitMask() const; }; class TC_GAME_API HealInfo { - private: - Unit* const _healer; - Unit* const _target; - uint32 _heal; - uint32 _effectiveHeal; - uint32 _absorb; - SpellInfo const* const _spellInfo; - SpellSchoolMask const _schoolMask; - uint32 _hitMask; +private: + Unit* const _healer; + Unit* const _target; + uint32 _heal; + uint32 _absorb; + SpellInfo const* const _spellInfo; + SpellSchoolMask const _schoolMask; + uint32 _hitMask; - public: - HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask); +public: + explicit HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask); - void AbsorbHeal(uint32 amount); - void SetEffectiveHeal(uint32 amount) { _effectiveHeal = amount; } + void AbsorbHeal(uint32 amount); - Unit* GetHealer() const { return _healer; } - Unit* GetTarget() const { return _target; } - uint32 GetHeal() const { return _heal; } - uint32 GetEffectiveHeal() const { return _effectiveHeal; } - uint32 GetAbsorb() const { return _absorb; } - SpellInfo const* GetSpellInfo() const { return _spellInfo; }; - SpellSchoolMask GetSchoolMask() const { return _schoolMask; }; + Unit* GetHealer() const { return _healer; } + Unit* GetTarget() const { return _target; } + uint32 GetHeal() const { return _heal; } + uint32 GetAbsorb() const { return _absorb; } + SpellInfo const* GetSpellInfo() const { return _spellInfo; }; + SpellSchoolMask GetSchoolMask() const { return _schoolMask; }; - uint32 GetHitMask() const; + uint32 GetHitMask() const; }; class TC_GAME_API ProcEventInfo { - public: - ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, - uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, - Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); +public: + 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; } + 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; } + uint32 GetTypeMask() const { return _typeMask; } + uint32 GetSpellTypeMask() const { return _spellTypeMask; } + uint32 GetSpellPhaseMask() const { return _spellPhaseMask; } + uint32 GetHitMask() const { return _hitMask; } - SpellInfo const* GetSpellInfo() const; - SpellSchoolMask GetSchoolMask() const; + SpellInfo const* GetSpellInfo() const; + SpellSchoolMask GetSchoolMask() const; - DamageInfo* GetDamageInfo() const { return _damageInfo; } - HealInfo* GetHealInfo() const { return _healInfo; } + DamageInfo* GetDamageInfo() const { return _damageInfo; } + HealInfo* GetHealInfo() const { return _healInfo; } - Spell const* GetProcSpell() const { return _spell; } - - private: - Unit* const _actor; - Unit* const _actionTarget; - Unit* const _procTarget; - uint32 _typeMask; - uint32 _spellTypeMask; - uint32 _spellPhaseMask; - uint32 _hitMask; - Spell* _spell; - DamageInfo* _damageInfo; - HealInfo* _healInfo; +private: + Unit* const _actor; + Unit* const _actionTarget; + Unit* const _procTarget; + uint32 _typeMask; + uint32 _spellTypeMask; + uint32 _spellPhaseMask; + uint32 _hitMask; + Spell* _spell; + DamageInfo* _damageInfo; + HealInfo* _healInfo; }; // Struct for use in Unit::CalculateMeleeDamage @@ -986,6 +984,7 @@ struct CalcDamageInfo WeaponAttackType attackType; // uint32 procAttacker; uint32 procVictim; + uint32 procEx; uint32 cleanDamage; // Used only for rage calculation MeleeHitOutcome hitOutCome; /// @todo remove this field (need use TargetState) }; @@ -1028,7 +1027,7 @@ struct SpellPeriodicAuraLogInfo bool critical; }; -uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); +uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); struct RedirectThreatInfo { @@ -1253,6 +1252,8 @@ enum PlayerTotemType #define ATTACK_DISPLAY_DELAY 200 #define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player +struct SpellProcEventEntry; // used only privately + class TC_GAME_API Unit : public WorldObject { public: @@ -1275,8 +1276,6 @@ class TC_GAME_API Unit : public WorldObject typedef std::list AuraApplicationList; typedef std::list Diminishing; - typedef std::deque> AuraApplicationProcContainer; - typedef std::map VisibleAuraMap; virtual ~Unit(); @@ -1456,18 +1455,18 @@ class TC_GAME_API Unit : public WorldObject uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = NULL, bool durabilityLoss = true); void Kill(Unit* victim, bool durabilityLoss = true); void KillSelf(bool durabilityLoss = true) { Kill(this, durabilityLoss); } - void DealHeal(HealInfo& healInfo); + int32 DealHeal(Unit* victim, uint32 addhealth); - void ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, - uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, - DamageInfo* damageInfo, HealInfo* healInfo); + void ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpell = NULL, SpellInfo const* procAura = NULL); + void ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, uint32 damage, SpellInfo const* procAura = NULL); - void GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo& eventInfo); + void GetProcAurasTriggeredOnEvent(AuraApplicationList& aurasTriggeringProc, AuraApplicationList* procAuras, ProcEventInfo eventInfo); void TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo); - void TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, + void TriggerAurasProcOnEvent(AuraApplicationList* myProcAuras, AuraApplicationList* targetProcAuras, + Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); - void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& procAuras); + void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationList& procAuras); void HandleEmoteCommand(uint32 anim_id); void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false); @@ -1561,8 +1560,8 @@ class TC_GAME_API Unit : public WorldObject virtual void UpdateUnderwaterState(Map* m, float x, float y, float z); bool isInAccessiblePlaceFor(Creature const* c) const; - void SendHealSpellLog(HealInfo& healInfo, bool critical = false); - int32 HealBySpell(HealInfo& healInfo, bool critical = false); + void SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical = false); + int32 HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical = false); void SendEnergizeSpellLog(Unit* victim, uint32 spellID, int32 damage, Powers powertype); void EnergizeBySpell(Unit* victim, uint32 SpellID, int32 Damage, Powers powertype); uint32 SpellNonMeleeDamageLog(Unit* victim, uint32 spellID, uint32 damage); @@ -2035,7 +2034,7 @@ class TC_GAME_API Unit : public WorldObject uint32 CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK); uint32 CalcSpellResistance(Unit* victim, SpellSchoolMask schoolMask, SpellInfo const* spellInfo) const; void CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32* absorb, uint32* resist, SpellInfo const* spellInfo = NULL); - void CalcHealAbsorb(HealInfo& healInfo); + void CalcHealAbsorb(Unit* victim, SpellInfo const* spellInfo, uint32& healAmount, uint32& absorb); void UpdateSpeed(UnitMoveType mtype); float GetSpeed(UnitMoveType mtype) const; @@ -2098,7 +2097,7 @@ class TC_GAME_API Unit : public WorldObject void UpdateAuraForGroup(uint8 slot); // proc trigger system - bool CanProc() const { return !m_procDeep; } + bool CanProc() const {return !m_procDeep;} void SetCantProc(bool apply); // pet auras @@ -2269,8 +2268,16 @@ class TC_GAME_API Unit : public WorldObject bool IsAlwaysDetectableFor(WorldObject const* seer) const override; void DisableSpline(); - private: + bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent); + bool RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent); + bool HandleAuraProcOnPowerAmount(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled); + bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); + bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown); + bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); + bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); void UpdateSplineMovement(uint32 t_diff); void UpdateSplinePosition(); @@ -2279,8 +2286,6 @@ class TC_GAME_API Unit : public WorldObject float GetCombatRatingReduction(CombatRating cr) const; uint32 GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const; - void ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType); - protected: void SetFeared(bool apply); void SetConfused(bool apply); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0179e330590..d7b4675514f 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -3599,7 +3599,7 @@ enum DiminishingReturnsType }; // Diminishing Return Groups -enum DiminishingGroup : uint16 +enum DiminishingGroup { DIMINISHING_NONE = 0, DIMINISHING_BANISH = 1, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index b1c3922cbd1..e11022e18cd 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -100,8 +100,8 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY &AuraEffect::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY &AuraEffect::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY - &AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in AuraEffect::HandleProc - &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in AuraEffect::HandleProc + &AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell + &AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor &AuraEffect::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES &AuraEffect::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES &AuraEffect::HandleNULL, // 46 SPELL_AURA_46 (used in test spells 54054 and 54058, and spell 48050) (3.0.8a) @@ -320,7 +320,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleUnused, //259 unused (4.3.4) old SPELL_AURA_MOD_HOT_PCT &AuraEffect::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code &AuraEffect::HandlePhase, //261 SPELL_AURA_PHASE - &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in Spell::CheckCast + &AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in spell::cancast &AuraEffect::HandleAuraAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask &AuraEffect::HandleUnused, //264 unused (3.2.0) &AuraEffect::HandleUnused, //265 unused (4.3.4) @@ -634,7 +634,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool resetPeriodicTimer /*= tru { // Apply periodic time mod if (modOwner) - modOwner->ApplySpellMod(GetId(), m_amplitude); + modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_amplitude); if (caster) { @@ -683,6 +683,7 @@ void AuraEffect::CalculateSpellMod() m_spellmod->type = SpellModType(uint32(GetAuraType())); // SpellModType value == spell aura types m_spellmod->spellId = GetId(); m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask; + m_spellmod->charges = GetBase()->GetCharges(); } m_spellmod->value = GetAmount(); break; @@ -1059,50 +1060,6 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const } } -bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const -{ - bool result = GetBase()->CallScriptCheckEffectProcHandlers(this, aurApp, eventInfo); - if (!result) - return false; - - SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); - switch (GetAuraType()) - { - case SPELL_AURA_MECHANIC_IMMUNITY: - case SPELL_AURA_MOD_MECHANIC_RESISTANCE: - // compare mechanic - if (!spellInfo || static_cast(spellInfo->Mechanic) != GetMiscValue()) - result = false; - break; - case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: - // skip melee hits and instant cast spells - if (!spellInfo || !spellInfo->CalcCastTime()) - result = false; - break; - case SPELL_AURA_MOD_DAMAGE_FROM_CASTER: - // Compare casters - if (GetCasterGUID() != eventInfo.GetActor()->GetGUID()) - result = false; - break; - case SPELL_AURA_MOD_POWER_COST_SCHOOL: - case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT: - // Skip melee hits and spells with wrong school or zero cost - if (!spellInfo || (!spellInfo->ManaCost && !spellInfo->ManaCostPercentage) || // Cost Check - !(spellInfo->GetSchoolMask() & GetMiscValue())) // School Check - result = false; - break; - case SPELL_AURA_REFLECT_SPELLS_SCHOOL: - // Skip melee hits and spells with wrong school - if (!spellInfo || !(spellInfo->GetSchoolMask() & GetMiscValue())) - result = false; - break; - default: - break; - } - - return result; -} - void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { bool prevented = GetBase()->CallScriptEffectProcHandlers(this, aurApp, eventInfo); @@ -1111,16 +1068,6 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) switch (GetAuraType()) { - // CC Auras which use their amount to drop - // Are there any more auras which need this? - case SPELL_AURA_MOD_CONFUSE: - case SPELL_AURA_MOD_FEAR: - case SPELL_AURA_MOD_STUN: - case SPELL_AURA_MOD_ROOT: - case SPELL_AURA_TRANSFORM: - HandleBreakableCCAuraProc(aurApp, eventInfo); - break; - case SPELL_AURA_DUMMY: case SPELL_AURA_PROC_TRIGGER_SPELL: HandleProcTriggerSpellAuraProc(aurApp, eventInfo); break; @@ -4776,6 +4723,10 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool if (caster) target->GetMotionMaster()->MoveFall(); break; + case 71563: + if (Aura* newAura = target->AddAura(71564, target)) + newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount); + break; } } // AT REMOVE @@ -5563,10 +5514,8 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) if (caster) { int32 heal = caster->CountPctFromMaxHealth(10); - HealInfo healInfo(caster, target, heal, auraSpellInfo, auraSpellInfo->GetSchoolMask()); - caster->HealBySpell(healInfo); + caster->HealBySpell(target, auraSpellInfo, heal); - /// @todo: should proc other auras? if (int32 mana = caster->GetMaxPower(POWER_MANA)) { mana /= 10; @@ -5877,7 +5826,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = std::max(int32(damage * GetDonePct()), 0); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetSpellInfo()->Id, damage); + modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); @@ -5926,14 +5875,12 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = uint32(target->CountPctFromMaxHealth(damage)); if (!m_spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) - { if (m_spellInfo->Effects[m_effIndex].IsTargetingArea() || isAreaAura) { damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); if (caster->GetTypeId() != TYPEID_PLAYER) damage = int32(float(damage) * target->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask)); } - } bool crit = false; @@ -5959,7 +5906,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; + uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT; damage = (damage <= absorb+resist) ? 0 : (damage-absorb-resist); if (damage) procVictim |= PROC_FLAG_TAKEN_DAMAGE; @@ -5971,8 +5918,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const SpellPeriodicAuraLogInfo pInfo(this, damage, overkill, absorb, resist, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); - caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &damageInfo, nullptr); + caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); caster->DealDamage(target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true); } @@ -6049,17 +5995,12 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; + uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT; damage = (damage <= absorb+resist) ? 0 : (damage-absorb-resist); if (damage) procVictim |= PROC_FLAG_TAKEN_DAMAGE; - if (caster->IsAlive()) - { - DamageInfo damageInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK); - caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &damageInfo, nullptr); - } - + caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); int32 new_damage = caster->DealDamage(target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), false); if (caster->IsAlive()) { @@ -6068,11 +6009,8 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c uint32 heal = uint32(caster->SpellHealingBonusDone(caster, GetSpellInfo(), uint32(new_damage * gainMultiplier), DOT, GetBase()->GetStackAmount())); heal = uint32(caster->SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DOT, GetBase()->GetStackAmount())); - HealInfo healInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); - caster->HealBySpell(healInfo); - - caster->getHostileRefManager().threatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo()); - caster->ProcSkillsAndAuras(caster, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); + int32 gain = caster->HealBySpell(caster, GetSpellInfo(), heal); + caster->getHostileRefManager().threatAssist(caster, gain * 0.5f, GetSpellInfo()); } } @@ -6101,9 +6039,7 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster) damage = int32(damage * gainMultiplier); - HealInfo healInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); - caster->HealBySpell(healInfo); - caster->ProcSkillsAndAuras(target, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, PROC_HIT_NORMAL, nullptr, nullptr, &healInfo); + caster->HealBySpell(target, GetSpellInfo(), damage); } void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const @@ -6176,7 +6112,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const damage = std::max(int32(damage * GetDonePct()), 0); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetSpellInfo()->Id, damage); + modOwner->ApplySpellMod(GetSpellInfo()->Id, SPELLMOD_DOT, damage); damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount()); } @@ -6192,16 +6128,15 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s heal of %s for %u health inflicted by %u", GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId()); + uint32 absorb = 0; uint32 heal = damage; + caster->CalcHealAbsorb(target, GetSpellInfo(), heal, absorb); + int32 gain = caster->DealHeal(target, heal); - HealInfo healInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); - caster->CalcHealAbsorb(healInfo); - caster->DealHeal(healInfo); - - SpellPeriodicAuraLogInfo pInfo(this, heal, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit); + SpellPeriodicAuraLogInfo pInfo(this, heal, heal - gain, absorb, 0, 0.0f, crit); target->SendPeriodicAuraLog(&pInfo); - target->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo()); + target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo()); bool haveCastItem = !GetBase()->GetCastItemGUID().IsEmpty(); @@ -6211,8 +6146,8 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const { uint32 funnelDamage = GetSpellInfo()->ManaPerSecond; // damage is not affected by spell power - if (funnelDamage > healInfo.GetEffectiveHeal() && healInfo.GetEffectiveHeal()) - funnelDamage = healInfo.GetEffectiveHeal(); + if ((int32)funnelDamage > gain && gain > 0) + funnelDamage = gain; uint32 funnelAbsorb = 0; caster->DealDamageMods(caster, funnelDamage, &funnelAbsorb); @@ -6224,10 +6159,10 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL; + uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_HOT; // ignore item heals if (!haveCastItem) - caster->ProcSkillsAndAuras(target, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); + caster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo()); } void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) const @@ -6398,41 +6333,15 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; - uint32 hitMask = createProcHitMask(&damageInfo, SPELL_MISS_NONE); - uint32 spellTypeMask = PROC_SPELL_TYPE_NO_DMG_HEAL; + uint32 procEx = createProcExtendMask(&damageInfo, SPELL_MISS_NONE) | PROC_EX_INTERNAL_DOT; if (damageInfo.damage) - { procVictim |= PROC_FLAG_TAKEN_DAMAGE; - spellTypeMask |= PROC_SPELL_TYPE_DAMAGE; - } - DamageInfo dotDamageInfo(damageInfo, DOT, BASE_ATTACK, hitMask); - caster->ProcSkillsAndAuras(target, procAttacker, procVictim, spellTypeMask, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &dotDamageInfo, nullptr); + caster->ProcDamageAndSpell(damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, BASE_ATTACK, spellProto); caster->DealSpellDamage(&damageInfo, true); } -void AuraEffect::HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) -{ - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo) - return; - - // aura own damage at apply won't break CC - if (eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST) - { - if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) - if (spellInfo == GetSpellInfo()) - return; - } - - int32 damageLeft = GetAmount(); - if (damageLeft < int32(damageInfo->GetDamage())) - aurApp->GetTarget()->RemoveAura(aurApp); - else - SetAmount(damageLeft - damageInfo->GetDamage()); -} - void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { Unit* triggerCaster = aurApp->GetTarget(); @@ -6445,8 +6354,7 @@ void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEve triggerCaster->CastSpell(triggerTarget, triggeredSpellInfo, true, NULL, this); } else - TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); - + TC_LOG_DEBUG("spells","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) @@ -6462,7 +6370,7 @@ void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp triggerCaster->CastCustomSpell(triggerTarget, triggerSpellId, &basepoints0, NULL, NULL, true, NULL, this); } else - TC_LOG_ERROR("spells","AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell %u from aura %u proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId()); + TC_LOG_DEBUG("spells","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) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 84ec8596637..6ea6a1d011c 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -41,6 +41,7 @@ class TC_GAME_API AuraEffect Aura* GetBase() const { return m_base; } void GetTargetList(std::list & targetList) const; void GetApplicationList(std::list & applicationList) const; + SpellModifier* GetSpellModifier() const { return m_spellmod; } SpellInfo const* GetSpellInfo() const { return m_spellInfo; } uint32 GetId() const { return m_spellInfo->Id; } @@ -89,9 +90,8 @@ class TC_GAME_API AuraEffect bool HasSpellClassMask() const { return m_spellInfo->Effects[m_effIndex].SpellClassMask; } void SendTickImmune(Unit* target, Unit* caster) const; - void PeriodicTick(AuraApplication* aurApp, Unit* caster) const; - - bool CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; + void PeriodicTick(AuraApplication * aurApp, Unit* caster) const; + void HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void CleanupTriggeredSpells(Unit* target); @@ -309,7 +309,6 @@ class TC_GAME_API AuraEffect void HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) const; // aura effect proc handlers - void HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); void HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 821b17dd1f1..39a6eff8553 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -738,7 +738,7 @@ int32 Aura::CalcMaxDuration(Unit* caster) const // IsPermanent() checks max duration (which we are supposed to calculate here) if (maxDuration != -1 && modOwner) - modOwner->ApplySpellMod(GetId(), maxDuration); + modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, maxDuration); return maxDuration; } @@ -748,7 +748,7 @@ void Aura::SetDuration(int32 duration, bool withMods) if (withMods) if (Unit* caster = GetCaster()) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), duration); + modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, duration); m_duration = duration; SetNeedClientUpdateForTargets(); @@ -819,7 +819,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const if (caster) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), maxProcCharges); + modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, maxProcCharges); return uint8(maxProcCharges); } @@ -925,6 +925,12 @@ bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode) // reset charges SetCharges(CalcMaxCharges()); + // FIXME: not a best way to synchronize charges, but works + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (AuraEffect* aurEff = GetEffect(i)) + if (aurEff->GetAuraType() == SPELL_AURA_ADD_FLAT_MODIFIER || aurEff->GetAuraType() == SPELL_AURA_ADD_PCT_MODIFIER) + if (SpellModifier* mod = aurEff->GetSpellModifier()) + mod->charges = GetCharges(); } SetNeedClientUpdateForTargets(); @@ -1059,7 +1065,7 @@ int32 Aura::CalcDispelChance(Unit* auraTarget, bool offensive) const // Apply dispel mod from aura caster if (Unit* caster = GetCaster()) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), resistChance); + modOwner->ApplySpellMod(GetId(), SPELLMOD_RESIST_DISPEL_CHANCE, resistChance); // Dispel resistance from target SPELL_AURA_MOD_DISPEL_RESIST // Only affects offensive dispels @@ -1116,7 +1122,7 @@ void Aura::HandleAllEffects(AuraApplication * aurApp, uint8 mode, bool apply) m_effects[i]->HandleEffect(aurApp, mode, apply); } -void Aura::GetApplicationList(Unit::AuraApplicationList& applicationList) const +void Aura::GetApplicationList(std::list & applicationList) const { for (Aura::ApplicationMap::const_iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter) { @@ -1713,76 +1719,49 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf } SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); + ASSERT(procEntry); // cooldowns should be added to the whole aura (see 51698 area aura) AddProcCooldown(procEntry->Cooldown); } -uint8 Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const +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 0; + return false; // check if we have charges to proc with - if (IsUsingCharges()) - { - if (!GetCharges()) - return 0; - - if (procEntry->AttributesMask & PROC_ATTR_REQ_SPELLMOD) - if (Spell const* spell = eventInfo.GetProcSpell()) - if (!spell->m_appliedMods.count(const_cast(this))) - return 0; - } + if (IsUsingCharges() && !GetCharges()) + return false; // check proc cooldown if (IsProcOnCooldown()) - return 0; + return false; + + /// @todo + // something about triggered spells triggering, and add extra attack effect // do checks against db data if (!sSpellMgr->CanSpellTriggerProcOnEvent(*procEntry, eventInfo)) - return 0; - - // check if aura can proc when spell is triggered - if (!(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC)) - if (Spell const* spell = eventInfo.GetProcSpell()) - if (spell->IsTriggered()) - if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) - return 0; + return false; // do checks using conditions table if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SPELL_PROC, GetId(), eventInfo.GetActor(), eventInfo.GetActionTarget())) - return 0; + return false; // AuraScript Hook bool check = const_cast(this)->CallScriptCheckProcHandlers(aurApp, eventInfo); if (!check) - return 0; - - // At least one effect has to pass checks to proc aura - uint8 procEffectMask = 0; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (aurApp->HasEffect(i)) - if (GetEffect(i)->CheckEffectProc(aurApp, eventInfo)) - procEffectMask |= (1 << i); - - if (!procEffectMask) - return 0; + return false; /// @todo // do allow additional requirements for procs // this is needed because this is the last moment in which you can prevent aura charge drop on proc // and possibly a way to prevent default checks (if there're going to be any) - // Aura added by spell can't trigger from self (prevent drop charges/do triggers) - // But except periodic and kill triggers (can triggered from self) - if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) - if (spellInfo->Id == GetId() && !(eventInfo.GetTypeMask() & (PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) - return 0; - // Check if current equipment meets aura requirements // do that only for passive spells /// @todo this needs to be unified for all kinds of auras @@ -1792,12 +1771,12 @@ uint8 Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& event if (GetSpellInfo()->EquippedItemClass == ITEM_CLASS_WEAPON) { if (target->ToPlayer()->IsInFeralForm()) - return 0; + return false; - if (DamageInfo* damageInfo = eventInfo.GetDamageInfo()) + if (eventInfo.GetDamageInfo()) { - WeaponAttackType attType = damageInfo->GetAttackType(); - Item* item = nullptr; + 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) @@ -1805,23 +1784,20 @@ uint8 Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& event 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) & GetSpellInfo()->EquippedItemSubClassMask)) - return 0; + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) + return false; } } else if (GetSpellInfo()->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) & GetSpellInfo()->EquippedItemSubClassMask)) - return 0; + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<GetTemplate()->SubClass) & GetSpellInfo()->EquippedItemSubClassMask)) + return false; } } - if (roll_chance_f(CalcProcChance(*procEntry, eventInfo))) - return procEffectMask; - - return 0; + return roll_chance_f(CalcProcChance(*procEntry, eventInfo)); } float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const @@ -1839,29 +1815,21 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event } // apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(GetId(), chance); + modOwner->ApplySpellMod(GetId(), SPELLMOD_CHANCE_OF_SUCCESS, chance); } return chance; } -void Aura::TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo) +void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) { - bool prevented = CallScriptProcHandlers(aurApp, eventInfo); - if (!prevented) - { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (!(procEffectMask & (1 << i))) - continue; - + CallScriptProcHandlers(aurApp, eventInfo); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (aurApp->HasEffect(i)) // OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc() - if (aurApp->HasEffect(i)) - GetEffect(i)->HandleProc(aurApp, eventInfo); - } + GetEffect(i)->HandleProc(aurApp, eventInfo); - CallScriptAfterProcHandlers(aurApp, eventInfo); - } + CallScriptAfterProcHandlers(aurApp, eventInfo); // Remove aura if we've used last charge to proc if (IsUsingCharges() && !GetCharges()) @@ -2219,23 +2187,6 @@ void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventI } } -bool Aura::CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) -{ - bool result = true; - for (std::list::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) - { - (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, aurApp); - std::list::iterator hookItrEnd = (*scritr)->DoCheckEffectProc.end(), hookItr = (*scritr)->DoCheckEffectProc.begin(); - for (; hookItr != hookItrEnd; ++hookItr) - if (hookItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - result &= hookItr->Call(*scritr, aurEff, eventInfo); - - (*scritr)->_FinishScriptCall(); - } - - return result; -} - bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool preventDefault = false; diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index e83d46ee276..f792581c86d 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -199,14 +199,18 @@ class TC_GAME_API Aura bool CheckAreaTarget(Unit* target); bool CanStackWith(Aura const* existingAura) const; + // Proc system + // this subsystem is not yet in use - the core of it is functional, but still some research has to be done + // and some dependant problems fixed before it can replace old proc system (for example cooldown handling) + // currently proc system functionality is implemented in Unit::ProcDamageAndSpell bool IsProcOnCooldown() const; void AddProcCooldown(Milliseconds msec); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo); - uint8 IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; + bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; - void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo); + void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); // AuraScript void LoadScripts(); @@ -229,7 +233,6 @@ class TC_GAME_API Aura void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount); // Spell Proc Hooks bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); - bool CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 82b200b3b4f..f485c1e7782 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -594,7 +594,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_healing = 0; m_procAttacker = 0; m_procVictim = 0; - m_hitMask = 0; + m_procEx = 0; focusObject = NULL; m_cast_count = 0; m_glyphIndex = 0; @@ -1474,7 +1474,7 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg { uint32 maxTargets = m_spellInfo->Effects[effIndex].ChainTarget; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, maxTargets, this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this); if (maxTargets > 1) { @@ -1943,11 +1943,12 @@ GameObject* Spell::SearchSpellFocus() return focus; } -void Spell::prepareDataForTriggerSystem() +void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) { //========================================================================================== // Now fill data for trigger system, need know: - // Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_hitMask) + // can spell trigger another or not (m_canTrigger) + // Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_procEx) //========================================================================================== m_procVictim = m_procAttacker = 0; @@ -1977,7 +1978,7 @@ void Spell::prepareDataForTriggerSystem() break; default: if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && - m_spellInfo->EquippedItemSubClassMask & (1 << ITEM_SUBCLASS_WEAPON_WAND) + m_spellInfo->EquippedItemSubClassMask & (1<HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) // Wands auto attack { m_procAttacker = PROC_FLAG_DONE_RANGED_AUTO_ATTACK; @@ -1986,7 +1987,7 @@ void Spell::prepareDataForTriggerSystem() // For other spells trigger procflags are set in Spell::DoAllEffectOnTarget // Because spell positivity is dependant on target } - m_hitMask = PROC_HIT_NONE; + m_procEx = PROC_EX_NONE; // Hunter trap spells - activation proc for Lock and Load, Entrapment and Misdirection if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && @@ -1997,12 +1998,29 @@ void Spell::prepareDataForTriggerSystem() m_procAttacker |= PROC_FLAG_DONE_TRAP_ACTIVATION; } + /* Effects which are result of aura proc from triggered spell cannot proc + to prevent chain proc of these spells */ + // Hellfire Effect - trigger as DOT if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags[0] & 0x00000040) { m_procAttacker = PROC_FLAG_DONE_PERIODIC; m_procVictim = PROC_FLAG_TAKEN_PERIODIC; } + + // Ranged autorepeat attack is set as triggered spell - ignore it + if (!(m_procAttacker & PROC_FLAG_DONE_RANGED_AUTO_ATTACK)) + { + if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS && + (m_spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC) || + m_spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2))) + m_procEx |= PROC_EX_INTERNAL_CANT_PROC; + else if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS) + m_procEx |= PROC_EX_INTERNAL_TRIGGERED; + } + // Totem casts require spellfamilymask defined in spell_proc_event to proc + if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsTotem() && m_caster->IsControlledByPlayer()) + m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY; } void Spell::CleanupTargetList() @@ -2013,32 +2031,6 @@ void Spell::CleanupTargetList() m_delayMoment = 0; } -class ProcReflectDelayed : public BasicEvent -{ - public: - ProcReflectDelayed(Unit* owner, ObjectGuid casterGuid) : _victim(owner), _casterGuid(casterGuid) { } - - bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override - { - Unit* caster = ObjectAccessor::GetUnit(*_victim, _casterGuid); - if (!caster) - return true; - - uint32 const typeMaskActor = PROC_FLAG_NONE; - uint32 const typeMaskActionTarget = PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG; - uint32 const spellTypeMask = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL; - uint32 const spellPhaseMask = PROC_SPELL_PHASE_NONE; - uint32 const hitMask = PROC_HIT_REFLECT; - - caster->ProcSkillsAndAuras(_victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, nullptr, nullptr, nullptr); - return true; - } - - private: - Unit* _victim; - ObjectGuid _casterGuid; -}; - void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= true*/, bool implicit /*= true*/, Position const* losPosition /*= nullptr*/) { for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) @@ -2122,20 +2114,20 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= targetInfo.timeDelay = uint64(m_spellInfo->Speed * 1000.0f); // Calculate minimum incoming time - if (!m_delayMoment || m_delayMoment > targetInfo.timeDelay) + if (m_delayMoment == 0 || m_delayMoment > targetInfo.timeDelay) m_delayMoment = targetInfo.timeDelay; } else - targetInfo.timeDelay = 0ULL; + targetInfo.timeDelay = 0LL; // If target reflect spell back to caster if (targetInfo.missCondition == SPELL_MISS_REFLECT) { // Calculate reflected spell result on caster - targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, false); // can't reflect twice + targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect); - // Proc spell reflect aura when missile hits the original target - target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(targetInfo.timeDelay)); + if (targetInfo.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell + targetInfo.reflectResult = SPELL_MISS_PARRY; // Increase time interval for reflected spells by 1.5 targetInfo.timeDelay += targetInfo.timeDelay >> 1; @@ -2157,14 +2149,14 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) { switch (m_spellInfo->Effects[effIndex].Effect) { - case SPELL_EFFECT_GAMEOBJECT_DAMAGE: - case SPELL_EFFECT_GAMEOBJECT_REPAIR: - case SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE: - if (go->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - effectMask &= ~(1 << effIndex); - break; - default: - break; + case SPELL_EFFECT_GAMEOBJECT_DAMAGE: + case SPELL_EFFECT_GAMEOBJECT_REPAIR: + case SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE: + if (go->GetGoType() != GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) + effectMask &= ~(1 << effIndex); + break; + default: + break; } } } @@ -2204,7 +2196,7 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) else target.timeDelay = uint64(m_spellInfo->Speed * 1000.0f); - if (!m_delayMoment || m_delayMoment > target.timeDelay) + if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) m_delayMoment = target.timeDelay; } else @@ -2312,13 +2304,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Fill base trigger info uint32 procAttacker = m_procAttacker; uint32 procVictim = m_procVictim; - uint32 hitMask = m_hitMask; + uint32 procEx = m_procEx; - m_spellAura = nullptr; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied + m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied - // Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); - Unit* spellHitTarget = nullptr; + //Spells with this flag cannot trigger if effect is cast on self + bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); + Unit* spellHitTarget = NULL; if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target spellHitTarget = unit; @@ -2346,15 +2338,23 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (missInfo2 != SPELL_MISS_MISS) m_caster->SendSpellMiss(unit, m_spellInfo->Id, missInfo2); m_damage = 0; - spellHitTarget = nullptr; + spellHitTarget = NULL; } } // Do not take combo points on dodge and miss - if (missInfo != SPELL_MISS_NONE && m_needComboPoints && m_targets.GetUnitTargetGUID() == target->targetGUID) + if (missInfo != SPELL_MISS_NONE && m_needComboPoints && + m_targets.GetUnitTargetGUID() == target->targetGUID) + { m_needComboPoints = false; + // Restore spell mods for a miss/dodge/parry Cold Blood + /// @todo check how broad this rule should be + if (m_caster->GetTypeId() == TYPEID_PLAYER && (missInfo == SPELL_MISS_MISS || + missInfo == SPELL_MISS_DODGE || missInfo == SPELL_MISS_PARRY)) + m_caster->ToPlayer()->RestoreSpellMods(this, 14177); + } - // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now + // Trigger info was not filled in spell::preparedatafortriggersystem - we do it now if (canEffectTrigger && !procAttacker && !procVictim) { bool positive = true; @@ -2401,20 +2401,19 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) uint32 addhealth = m_healing; if (crit) { - hitMask |= PROC_HIT_CRITICAL; - addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, nullptr); + procEx |= PROC_EX_CRITICAL_HIT; + addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, NULL); } else - hitMask |= PROC_HIT_NORMAL; + procEx |= PROC_EX_NORMAL_HIT; - HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask()); - caster->HealBySpell(healInfo, crit); - unitTarget->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, m_spellInfo); - m_healing = healInfo.GetEffectiveHeal(); + int32 gain = caster->HealBySpell(unitTarget, m_spellInfo, addhealth, crit); + unitTarget->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo); + m_healing = gain; - // Do triggers for unit - if (canEffectTrigger) - caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, nullptr, &healInfo); + // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) + if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) + caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell); } // Do damage and triggers else if (m_damage > 0) @@ -2429,18 +2428,16 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Send log damage message to client caster->SendSpellNonMeleeDamageLog(&damageInfo); - hitMask |= createProcHitMask(&damageInfo, missInfo); + procEx |= createProcExtendMask(&damageInfo, missInfo); procVictim |= PROC_FLAG_TAKEN_DAMAGE; - // Do triggers for unit - if (canEffectTrigger) + // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) + if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) { - DamageInfo spellDamageInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, hitMask); - caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, hitMask, this, &spellDamageInfo, nullptr); - - if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && - (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(spellDamageInfo); + caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell); + if (caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) == 0 && + (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); } m_damage = damageInfo.damage; @@ -2452,17 +2449,10 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) { // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - hitMask |= createProcHitMask(&damageInfo, missInfo); - // Do triggers for unit - if (canEffectTrigger) - { - DamageInfo spellNoDamageInfo(damageInfo, NODAMAGE, m_attackType, hitMask); - caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_NO_DMG_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, &spellNoDamageInfo, nullptr); - - if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && - (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(spellNoDamageInfo); - } + procEx |= createProcExtendMask(&damageInfo, missInfo); + // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) + if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT) + caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell); // Failed Pickpocket, reveal rogue if (missInfo == SPELL_MISS_RESIST && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && unitTarget->GetTypeId() == TYPEID_UNIT) @@ -2829,7 +2819,7 @@ bool Spell::UpdateChanneledTargetList() { range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive()); if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, range, this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); } for (std::list::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) @@ -2971,7 +2961,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered } // Prepare data for triggers - prepareDataForTriggerSystem(); + prepareDataForTriggerSystem(triggeredByAura); if (Player* player = m_caster->ToPlayer()) { @@ -3083,6 +3073,10 @@ void Spell::cancel() SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); + // spell is canceled-take mods and clear list + if (m_caster->GetTypeId() == TYPEID_PLAYER) + m_caster->ToPlayer()->RemoveSpellMods(this); + m_appliedMods.clear(); break; @@ -3316,25 +3310,7 @@ void Spell::cast(bool skipCheck) SetExecutedCurrently(false); if (Creature* creatureCaster = m_caster->ToCreature()) - - if (!m_originalCaster) - return; - - // Handle procs on cast - uint32 procAttacker = m_procAttacker; - if (!procAttacker) - { - if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) - procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; - else - procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; - } - - uint32 hitMask = m_hitMask; - if (!(hitMask & PROC_HIT_CRITICAL)) - hitMask |= PROC_HIT_NORMAL; - - m_originalCaster->ProcSkillsAndAuras(nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr); + creatureCaster->ReleaseFocus(this); } void Spell::handle_immediate() @@ -3348,7 +3324,7 @@ void Spell::handle_immediate() // First mod_duration then haste - see Missile Barrage // Apply duration mod if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, duration); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); // Apply haste mods m_caster->ModSpellDurationTime(m_spellInfo, duration, this); @@ -3491,8 +3467,22 @@ void Spell::_handle_immediate_phase() } // process items - for (std::list::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) + for (std::list::iterator ihit= m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) DoAllEffectOnTarget(&(*ihit)); + + if (!m_originalCaster) + return; + // Handle procs on cast + /// @todo finish new proc system:P + if (m_UniqueTargetInfo.empty() && m_targets.HasDst()) + { + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS; + + // Proc the spells that have DEST target + m_originalCaster->ProcDamageAndSpell(NULL, procAttacker, 0, m_procEx | PROC_EX_NORMAL_HIT, 0, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell); + } } void Spell::_handle_finish_phase() @@ -3519,24 +3509,7 @@ void Spell::_handle_finish_phase() m_caster->m_extraAttacks = 0; } - // Handle procs on finish - if (!m_originalCaster) - return; - - uint32 procAttacker = m_procAttacker; - if (!procAttacker) - { - if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) - procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; - else - procAttacker = m_spellInfo->IsPositive() ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; - } - - uint32 hitMask = m_hitMask; - if (!(hitMask & PROC_HIT_CRITICAL)) - hitMask |= PROC_HIT_NORMAL; - - m_originalCaster->ProcSkillsAndAuras(nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_FINISH, hitMask, this, nullptr, nullptr); + /// @todo trigger proc phase finish here } void Spell::SendSpellCooldown() @@ -3702,6 +3675,18 @@ void Spell::finish(bool ok) m_caster->ToPlayer()->UpdatePotionCooldown(this); } + if (Player* modOwner = m_caster->GetSpellModOwner()) + { + // triggered spell pointer can be not set in some cases + // this is needed for proper apply of triggered spell mods + modOwner->SetSpellModTakingSpell(this, true); + + // Take mods after trigger spell (needed for 14177 to affect 48664) + // mods are taken only on succesfull cast and independantly from targets of the spell + modOwner->RemoveSpellMods(this); + modOwner->SetSpellModTakingSpell(this, false); + } + // Stop Attack for some spells if (m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET)) m_caster->AttackStop(); @@ -4440,7 +4425,7 @@ void Spell::TakePower() hit = false; //lower spell cost on fail (by talent aura) if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, m_powerCost); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); } break; } @@ -4529,7 +4514,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) { runeCost[i] = src->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, runeCost[i], this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); } runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later @@ -4569,7 +4554,7 @@ void Spell::TakeRunePower(bool didHit) { runeCost[i] = runeCostData->RuneCost[i]; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, runeCost[i], this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this); } // Let's say we use a skill that requires a Frost rune. This is the order: @@ -6015,7 +6000,7 @@ SpellCastResult Spell::CheckRange(bool strict) maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, maxRange, this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, this); maxRange += rangeMod; @@ -6545,7 +6530,7 @@ void Spell::Delayed() // only called in DealDamage() //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, delayReduce, this); + m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6583,7 +6568,7 @@ void Spell::DelayedChannel() int32 delaytime = CalculatePct(duration, 25); // channeling delay is normally 25% of its time per hit int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, delayReduce, this); + m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -7506,8 +7491,8 @@ void Spell::TriggerGlobalCooldown() if (m_spellInfo->StartRecoveryTime >= MIN_GCD && m_spellInfo->StartRecoveryTime <= MAX_GCD) { // gcd modifier auras are applied only to own spells and only players have such mods - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, gcd, this); + if (m_caster->GetTypeId() == TYPEID_PLAYER) + m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this); bool isMeleeOrRangedSpell = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED || diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 4798d962fbd..6291ccf6995 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -359,7 +359,7 @@ class TC_GAME_API Spell void EffectUpdatePlayerPhase(SpellEffIndex effIndex); void EffectUpdateZoneAurasAndPhases(SpellEffIndex effIndex); - typedef std::unordered_set UsedSpellMods; + typedef std::set UsedSpellMods; Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = ObjectGuid::Empty, bool skipCheck = false); ~Spell(); @@ -584,8 +584,8 @@ class TC_GAME_API Spell // ****************************************** uint32 m_procAttacker; // Attacker trigger flags uint32 m_procVictim; // Victim trigger flags - uint32 m_hitMask; - void prepareDataForTriggerSystem(); + uint32 m_procEx; + void prepareDataForTriggerSystem(AuraEffect const* triggeredByAura); // ***************************************** // Spell target subsystem diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 18368c518e5..c1c0952a3f3 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1343,8 +1343,7 @@ void Spell::EffectHealthLeech(SpellEffIndex effIndex) healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL); healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); - HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellSchoolMask); - m_caster->HealBySpell(healInfo); + m_caster->HealBySpell(m_caster, m_spellInfo, uint32(healthGain)); } } @@ -2033,7 +2032,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, duration); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); TempSummon* summon = NULL; @@ -5181,8 +5180,18 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) m_runesState = m_caster->ToPlayer()->GetRunesState(); uint32 count = damage; - if (count == 0) - count = 1; + if (count == 0) count = 1; + for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) + { + if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) + { + if (m_spellInfo->Id == 45529) + if (player->GetBaseRune(j) != RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) + continue; + player->SetRuneCooldown(j, 0); + --count; + } + } // Blood Tap if (m_spellInfo->Id == 45529 && count > 0) @@ -5190,12 +5199,11 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l) { // Check if both runes are on cd as that is the only time when this needs to come into effect - if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RUNE_BLOOD) && (player->GetRuneCooldown(l + 1) && player->GetBaseRune(l + 1) == RUNE_BLOOD)) + if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetBaseRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) { // Should always update the rune with the lowest cd - if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l + 1)) - ++l; - + if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1)) + l++; player->SetRuneCooldown(l, 0); --count; // is needed to push through to the client that the rune is active @@ -5206,15 +5214,6 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) } } - for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j) - { - if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue)) - { - player->SetRuneCooldown(j, 0); - --count; - } - } - // Empower rune weapon if (m_spellInfo->Id == 47568) { @@ -5224,7 +5223,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 i = 0; i < MAX_RUNES; ++i) { - if (player->GetRuneCooldown(i) && (player->GetBaseRune(i) == RUNE_FROST)) + if (player->GetRuneCooldown(i) && (player->GetCurrentRune(i) == RUNE_FROST || player->GetCurrentRune(i) == RUNE_DEATH)) player->SetRuneCooldown(i, 0); } } @@ -5358,7 +5357,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* int32 duration = m_spellInfo->GetDuration(); if (Player* modOwner = m_originalCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, duration); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); //TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; Map* map = caster->GetMap(); diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 7d23336ab76..38574fdeff8 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -305,10 +305,10 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel if (Player* modOwner = _owner->GetSpellModOwner()) { if (cooldown > 0) - modOwner->ApplySpellMod(spellInfo->Id, cooldown, spell); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell); if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) - modOwner->ApplySpellMod(spellInfo->Id, categoryCooldown, spell); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell); } if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE, spellInfo)) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 5acb2c26a34..6a674b3080c 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -592,7 +592,7 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const { float multiplier = ValueMultiplier; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, multiplier, spell); + modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell); return multiplier; } @@ -600,7 +600,7 @@ float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const { float multiplierPercent = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, multiplierPercent, spell); + modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell); return multiplierPercent / 100.0f; } @@ -629,7 +629,7 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const radius += entry->RadiusPerLevel * caster->getLevel(); radius = std::min(radius, entry->RadiusMax); if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(_spellInfo->Id, radius, spell); + modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell); } return radius; @@ -2295,7 +2295,7 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const range = RangeEntry->maxRangeHostile; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, range, spell); + modOwner->ApplySpellMod(Id, SPELLMOD_RANGE, range, spell); return range; } @@ -2461,7 +2461,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) c // Apply cost mod by spell if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, powerCost); + modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost); if (!caster->IsControlledByPlayer()) { @@ -2643,7 +2643,7 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const } break; case SPELLFAMILY_MAGE: - // Impact + // Ignite if (SpellIconID == 45) return true; break; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 9150daee23a..7fdd067aed9 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -18,7 +18,6 @@ #include "SpellMgr.h" #include "SpellInfo.h" -#include "Spell.h" #include "ObjectMgr.h" #include "SpellAuraDefines.h" #include "SharedDefines.h" @@ -783,6 +782,171 @@ SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const return SPELL_GROUP_STACK_RULE_DEFAULT; } +SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const +{ + SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId); + if (itr != mSpellProcEventMap.end()) + return &itr->second; + return NULL; +} + +bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const +{ + // No extra req need + uint32 procEvent_procEx = PROC_EX_NONE; + + // check prockFlags for condition + if ((procFlags & EventProcFlag) == 0) + return false; + + bool hasFamilyMask = false; + + /** + + * @brief Check auras procced by periodics + + *Only damaging Dots can proc auras with PROC_FLAG_TAKEN_DAMAGE + + *Only Dots can proc if ONLY has PROC_FLAG_DONE_PERIODIC or PROC_FLAG_TAKEN_PERIODIC. + + *Hots can proc if ONLY has PROC_FLAG_DONE_PERIODIC and spellfamily != 0 + + *Only Dots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG + + *Only Hots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS + + *Only Dots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG + + *Only Hots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS + + * @param procSpell the spell proccing the aura + * @param procFlags proc_flags of spellProc + * @param procExtra proc_EX of procSpell + * @param EventProcFlag proc_flags of aura to be procced + * @param spellProto SpellInfo of aura to be procced + + */ + + /// Quick Check - If PROC_FLAG_TAKEN_DAMAGE is set for aura and procSpell dealt damage, proc no matter what kind of spell that deals the damage. + if (procFlags & PROC_FLAG_TAKEN_DAMAGE && EventProcFlag & PROC_FLAG_TAKEN_DAMAGE) + return true; + + if (procFlags & PROC_FLAG_DONE_PERIODIC && EventProcFlag & PROC_FLAG_DONE_PERIODIC) + { + if (procExtra & PROC_EX_INTERNAL_HOT) + { + if (EventProcFlag == PROC_FLAG_DONE_PERIODIC) + { + /// no aura with only PROC_FLAG_DONE_PERIODIC and spellFamilyName == 0 can proc from a HOT. + if (!spellProto->SpellFamilyName) + return false; + } + /// Aura must have positive procflags for a HOT to proc + else if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS))) + return false; + } + /// Aura must have negative or neutral(PROC_FLAG_DONE_PERIODIC only) procflags for a DOT to proc + else if (EventProcFlag != PROC_FLAG_DONE_PERIODIC) + if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG))) + return false; + } + + if (procFlags & PROC_FLAG_TAKEN_PERIODIC && EventProcFlag & PROC_FLAG_TAKEN_PERIODIC) + { + if (procExtra & PROC_EX_INTERNAL_HOT) + { + /// No aura that only has PROC_FLAG_TAKEN_PERIODIC can proc from a HOT. + if (EventProcFlag == PROC_FLAG_TAKEN_PERIODIC) + return false; + /// Aura must have positive procflags for a HOT to proc + if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS))) + return false; + } + /// Aura must have negative or neutral(PROC_FLAG_TAKEN_PERIODIC only) procflags for a DOT to proc + else if (EventProcFlag != PROC_FLAG_TAKEN_PERIODIC) + if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG))) + return false; + } + // Trap casts are active by default + if (procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION) + active = true; + + // Always trigger for this + if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) + return true; + + if (spellProcEvent) // Exist event data + { + // Store extra req + procEvent_procEx = spellProcEvent->procEx; + + // For melee triggers + if (procSpell == NULL) + { + // Check (if set) for school (melee attack has Normal school) + if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0) + return false; + } + else // For spells need check school/spell family/family mask + { + // Check (if set) for school + if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0) + return false; + + // Check (if set) for spellFamilyName + if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) + return false; + + // spellFamilyName is Ok need check for spellFamilyMask if present + if (spellProcEvent->spellFamilyMask) + { + if (!(spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags)) + return false; + hasFamilyMask = true; + // Some spells are not considered as active even with spellfamilyflags set + if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)) + active = true; + } + } + } + + if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY)) + { + if (!hasFamilyMask) + return false; + } + + // Check for extra req (if none) and hit/crit + if (procEvent_procEx == PROC_EX_NONE) + { + // No extra req, so can trigger only for hit/crit - spell has to be active + if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active) + return true; + } + else // Passive spells hits here only if resist/reflect/immune/evade + { + if (procExtra & AURA_SPELL_PROC_EX_MASK) + { + // if spell marked as procing only from not active spells + if (active && procEvent_procEx & PROC_EX_NOT_ACTIVE_SPELL) + return false; + // if spell marked as procing only from active spells + if (!active && procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL) + return false; + // Exist req for PROC_EX_EX_TRIGGER_ALWAYS + if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS) + return true; + // PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before + if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && ((procEvent_procEx & (AURA_SPELL_PROC_EX_MASK)) == 0)) + return true; + } + // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other) + if (procEvent_procEx & procExtra) + return true; + } + return false; +} + SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const { SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId); @@ -803,31 +967,10 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget())) return false; - // check mana requirement - if (procEntry.AttributesMask & PROC_ATTR_REQ_MANA_COST) - if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo()) - if (!eventSpellInfo->ManaCost && !eventSpellInfo->ManaCostPercentage) - return false; - // always trigger for these types if (eventInfo.GetTypeMask() & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) return true; - // do triggered cast checks - if (!(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC)) - { - if (Spell const* spell = eventInfo.GetProcSpell()) - { - if (spell->IsTriggered()) - { - SpellInfo const* spellInfo = spell->GetSpellInfo(); - if (!spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2) && - !spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC)) - return false; - } - } - } - // check school mask (if set) for other trigger types if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask)) return false; @@ -1762,72 +1905,94 @@ void SpellMgr::LoadSpellGroupStackRules() TC_LOG_INFO("server.loading", ">> Loaded %u spell group stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -// Used for prepare can/can't triggr aura -static bool InitTriggerAuraData(); -// Define can trigger auras -static bool isTriggerAura[TOTAL_AURAS]; -// Triggered always, even from triggered spells -static bool isAlwaysTriggeredAura[TOTAL_AURAS]; -// Prepare lists -static bool procPrepared = InitTriggerAuraData(); - -// List of auras that CAN be trigger but may not exist in spell_proc_event -// in most case need for drop charges -// in some types of aura need do additional check -// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic -bool InitTriggerAuraData() +void SpellMgr::LoadSpellProcEvents() { - for (uint16 i = 0; i < TOTAL_AURAS; ++i) + uint32 oldMSTime = getMSTime(); + + mSpellProcEventMap.clear(); // need for reload case + + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); + if (!result) { - isTriggerAura[i] = false; - isAlwaysTriggeredAura[i] = false; + TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty."); + return; } - isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting. Remove? - isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; // "Any direct damaging attack will revive targets" - isTriggerAura[SPELL_AURA_MOD_THREAT] = true; // Only one spell: 28762 part of Mage T3 8p bonus - isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger - isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true; - isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true; - isTriggerAura[SPELL_AURA_MOD_STEALTH] = true; - isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger - isTriggerAura[SPELL_AURA_MOD_ROOT] = true; - isTriggerAura[SPELL_AURA_TRANSFORM] = true; - isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true; - isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true; - isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true; - isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true; - isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true; - isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true; - isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true; - isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true; - isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true; - isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; - isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true; - isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true; - isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true; - isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true; - isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; - isTriggerAura[SPELL_AURA_ADD_FLAT_MODIFIER] = true; - isTriggerAura[SPELL_AURA_ADD_PCT_MODIFIER] = true; - isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; - isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; - isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; - isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true; - isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true; - isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; + uint32 count = 0; - return true; + do + { + Field* fields = result->Fetch(); + + int32 spellId = fields[0].GetInt32(); + + bool allRanks = false; + if (spellId < 0) + { + allRanks = true; + spellId = -spellId; + } + + SpellInfo const* spellInfo = GetSpellInfo(spellId); + if (!spellInfo) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` does not exist.", spellId); + continue; + } + + if (allRanks) + { + if (!spellInfo->IsRanked()) + TC_LOG_ERROR("sql.sql", "The spell %u is listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId); + + if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is not first rank of spell.", spellId); + continue; + } + } + + SpellProcEventEntry spellProcEvent; + + spellProcEvent.schoolMask = fields[1].GetInt8(); + spellProcEvent.spellFamilyName = fields[2].GetUInt16(); + spellProcEvent.spellFamilyMask[0] = fields[3].GetUInt32(); + spellProcEvent.spellFamilyMask[1] = fields[4].GetUInt32(); + spellProcEvent.spellFamilyMask[2] = fields[5].GetUInt32(); + spellProcEvent.procFlags = fields[6].GetUInt32(); + spellProcEvent.procEx = fields[7].GetUInt32(); + spellProcEvent.ppmRate = fields[8].GetFloat(); + spellProcEvent.customChance = fields[9].GetFloat(); + spellProcEvent.cooldown = fields[10].GetUInt32(); + + while (spellInfo) + { + if (mSpellProcEventMap.find(spellInfo->Id) != mSpellProcEventMap.end()) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id); + break; + } + + if (!spellInfo->ProcFlags && !spellProcEvent.procFlags) + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is probably not a triggered spell.", spellInfo->Id); + + mSpellProcEventMap[spellInfo->Id] = spellProcEvent; + + if (allRanks) + spellInfo = spellInfo->GetNextRankSpell(); + else + break; + } + + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -+void SpellMgr::LoadSpellProcs() +void SpellMgr::LoadSpellProcs() { uint32 oldMSTime = getMSTime(); @@ -1838,199 +2003,126 @@ bool InitTriggerAuraData() // 6 7 8 9 10 11 12 13 14 "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); - uint32 count = 0; - if (result) + if (!result) { - do - { - Field* fields = result->Fetch(); - - int32 spellId = fields[0].GetInt32(); - - bool allRanks = false; - if (spellId < 0) - { - allRanks = true; - spellId = -spellId; - } - - SpellInfo const* spellInfo = GetSpellInfo(spellId); - if (!spellInfo) - { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); - continue; - } - - if (allRanks) - { - if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); - - if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) - { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); - continue; - } - } - - SpellProcEntry baseProcEntry; - - baseProcEntry.SchoolMask = fields[1].GetInt8(); - baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); - baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); - baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); - baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); - baseProcEntry.ProcFlags = fields[6].GetUInt32(); - baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); - baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); - baseProcEntry.HitMask = fields[9].GetUInt32(); - baseProcEntry.AttributesMask = fields[10].GetUInt32(); - baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); - baseProcEntry.Chance = fields[12].GetFloat(); - baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); - baseProcEntry.Charges = fields[14].GetUInt8(); - - while (spellInfo) - { - if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) - { - TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); - break; - } - SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); - - // take defaults from dbcs - if (!procEntry.ProcFlags) - procEntry.ProcFlags = spellInfo->ProcFlags; - if (!procEntry.Charges) - procEntry.Charges = spellInfo->ProcCharges; - if (!procEntry.Chance && !procEntry.ProcsPerMinute) - procEntry.Chance = float(spellInfo->ProcChance); - - // validate data - if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); - if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < SPELLFAMILY_MAGE || procEntry.SpellFamilyName > SPELLFAMILY_PET || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); - if (procEntry.Chance < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); - procEntry.Chance = 0; - } - if (procEntry.ProcsPerMinute < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); - procEntry.ProcsPerMinute = 0; - } - if (!procEntry.ProcFlags) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); - if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); - if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); - if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); - if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); - if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); - if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); - if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); - - mSpellProcMap[spellInfo->Id] = procEntry; - - if (allRanks) - spellInfo = spellInfo->GetNextRankSpell(); - else - break; - } - ++count; - } while (result->NextRow()); - } - else TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); + return; + } - TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - - // This generates default procs to retain compatibility with previous proc system - TC_LOG_INFO("server.loading", "Generating spell proc data from SpellMap..."); - count = 0; - oldMSTime = getMSTime(); - - for (SpellInfo const* spellInfo : mSpellInfoMap) + uint32 count = 0; + do { - if (!spellInfo) - continue; + Field* fields = result->Fetch(); - if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) - continue; + int32 spellId = fields[0].GetInt32(); - bool found = false, addTriggerFlag = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + bool allRanks = false; + if (spellId < 0) { - if (!spellInfo->Effects[i].IsEffect()) - continue; - - uint32 auraName = spellInfo->Effects[i].ApplyAuraName; - if (!auraName) - continue; - - if (!isTriggerAura[auraName]) - continue; - - found = true; - - if (!addTriggerFlag && isAlwaysTriggeredAura[auraName]) - addTriggerFlag = true; - break; + allRanks = true; + spellId = -spellId; } - if (!found) - continue; - - if (!spellInfo->ProcFlags) - continue; - - SpellProcEntry procEntry; - procEntry.SchoolMask = 0; - procEntry.SpellFamilyName = spellInfo->SpellFamilyName; - procEntry.ProcFlags = spellInfo->ProcFlags; - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (spellInfo->Effects[i].IsEffect() && isTriggerAura[spellInfo->Effects[i].ApplyAuraName]) - procEntry.SpellFamilyMask |= spellInfo->Effects[i].SpellClassMask; - - procEntry.SpellTypeMask = PROC_SPELL_TYPE_MASK_ALL; - procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT; - procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent - - // Reflect auras should only proc off reflects - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) + SpellInfo const* spellInfo = GetSpellInfo(spellId); + if (!spellInfo) { - if (spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS) || spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS_SCHOOL)) + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); + continue; + } + + if (allRanks) + { + if (!spellInfo->IsRanked()) + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); + + if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) { - procEntry.HitMask = PROC_HIT_REFLECT; + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); + continue; + } + } + + SpellProcEntry baseProcEntry; + + baseProcEntry.SchoolMask = fields[1].GetInt8(); + baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); + baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); + baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); + baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); + baseProcEntry.ProcFlags = fields[6].GetUInt32(); + baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); + baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); + baseProcEntry.HitMask = fields[9].GetUInt32(); + baseProcEntry.AttributesMask = fields[10].GetUInt32(); + baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); + baseProcEntry.Chance = fields[12].GetFloat(); + baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); + baseProcEntry.Charges = fields[14].GetUInt8(); + + while (spellInfo) + { + if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) + { + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); break; } + + SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); + + // take defaults from dbcs + if (!procEntry.ProcFlags) + procEntry.ProcFlags = spellInfo->ProcFlags; + if (!procEntry.Charges) + procEntry.Charges = spellInfo->ProcCharges; + if (!procEntry.Chance && !procEntry.ProcsPerMinute) + procEntry.Chance = float(spellInfo->ProcChance); + + // validate data + if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); + if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); + if (procEntry.Chance < 0) + { + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); + procEntry.Chance = 0; + } + if (procEntry.ProcsPerMinute < 0) + { + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); + procEntry.ProcsPerMinute = 0; + } + if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id); + if (!procEntry.ProcFlags) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); + if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); + if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); + if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); + + mSpellProcMap[spellInfo->Id] = procEntry; + + if (allRanks) + spellInfo = spellInfo->GetNextRankSpell(); + else + break; } - - procEntry.AttributesMask = 0; - if (spellInfo->ProcFlags & PROC_FLAG_KILL) - procEntry.AttributesMask |= PROC_ATTR_REQ_EXP_OR_HONOR; - if (addTriggerFlag) - procEntry.AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC; - - procEntry.ProcsPerMinute = 0; - procEntry.Chance = spellInfo->ProcChance; - procEntry.Cooldown = Milliseconds::zero(); - procEntry.Charges = spellInfo->ProcCharges; - - mSpellProcMap[spellInfo->Id] = procEntry; ++count; } + while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Generated spell proc data for %u spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } void SpellMgr::LoadSpellBonusess() @@ -3007,7 +3099,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 59725: // Improved Spell Reflection - aoe aura // Target entry seems to be wrong for this spell :/ spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER_AREA_PARTY); - spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_20_YARDS); + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_10_YARDS_2); break; case 43265: // Death and Decay // We need access to the targets of the dynaura to correctly apply the damage spell @@ -3100,12 +3192,23 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->MaxAffectedTargets = 1; spellInfo->Effects[EFFECT_0].TriggerSpell = 33760; break; + case 17941: // Shadow Trance + case 22008: // Netherwind Focus + case 34477: // Misdirection + case 34936: // Backlash + case 48108: // Hot Streak + case 51124: // Killing Machine + case 54741: // Firestarter + case 57761: // Fireball! + case 64823: // Item - Druid T8 Balance 4P Bonus case 88819: // Daybreak + case 18820: // Insight spellInfo->ProcCharges = 1; break; case 44544: // Fingers of Frost spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(685904631, 1151048, 0); break; + case 74396: // Fingers of Frost visual buff case 53257: // Cobra Strikes spellInfo->ProcCharges = 2; spellInfo->StackAmount = 0; @@ -3115,6 +3218,9 @@ void SpellMgr::LoadSpellInfoCorrections() case 56612: spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(0x02000000, 0, 0); break; + case 28200: // Ascendance (Talisman of Ascendance trinket) + spellInfo->ProcCharges = 6; + break; case 47201: // Everlasting Affliction (1) case 47202: // Everlasting Affliction (2) case 47203: // Everlasting Affliction (3) diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index d518926c555..3808a20bdc7 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -192,6 +192,43 @@ enum ProcFlags PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | \ PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS) +enum ProcFlagsExLegacy +{ + PROC_EX_NONE = 0x0000000, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag) + PROC_EX_NORMAL_HIT = 0x0000001, // If set only from normal hit (only damage spells) + PROC_EX_CRITICAL_HIT = 0x0000002, + PROC_EX_MISS = 0x0000004, + PROC_EX_RESIST = 0x0000008, + PROC_EX_DODGE = 0x0000010, + PROC_EX_PARRY = 0x0000020, + PROC_EX_BLOCK = 0x0000040, + PROC_EX_EVADE = 0x0000080, + PROC_EX_IMMUNE = 0x0000100, + PROC_EX_DEFLECT = 0x0000200, + PROC_EX_ABSORB = 0x0000400, + PROC_EX_REFLECT = 0x0000800, + PROC_EX_INTERRUPT = 0x0001000, // Melee hit result can be Interrupt (not used) + PROC_EX_FULL_BLOCK = 0x0002000, // block al attack damage + PROC_EX_RESERVED2 = 0x0004000, + PROC_EX_NOT_ACTIVE_SPELL = 0x0008000, // Spell mustn't do damage/heal to proc + PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always no matter of hit result + PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not implemented yet) + PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc + + // Flags for internal use - do not use these in db! + PROC_EX_INTERNAL_CANT_PROC = 0x0800000, + PROC_EX_INTERNAL_DOT = 0x1000000, + PROC_EX_INTERNAL_HOT = 0x2000000, + PROC_EX_INTERNAL_TRIGGERED = 0x4000000, + PROC_EX_INTERNAL_REQ_FAMILY = 0x8000000 +}; + +#define AURA_SPELL_PROC_EX_MASK \ + (PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT | PROC_EX_MISS | \ + PROC_EX_RESIST | PROC_EX_DODGE | PROC_EX_PARRY | PROC_EX_BLOCK | \ + PROC_EX_EVADE | PROC_EX_IMMUNE | PROC_EX_DEFLECT | \ + PROC_EX_ABSORB | PROC_EX_REFLECT | PROC_EX_INTERRUPT) + enum ProcFlagsSpellType { PROC_SPELL_TYPE_NONE = 0x0000000, @@ -232,12 +269,23 @@ enum ProcFlagsHit enum ProcAttributes { - PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000001, // requires proc target to give exp or honor for aura proc - PROC_ATTR_TRIGGERED_CAN_PROC = 0x0000002, // aura can proc even with triggered spells - PROC_ATTR_REQ_MANA_COST = 0x0000004, // requires triggering spell to have a mana cost for aura proc - PROC_ATTR_REQ_SPELLMOD = 0x0000008 // requires triggering spell to be affected by proccing aura to drop charges + PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000010 }; +struct SpellProcEventEntry +{ + uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2 + uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value + flag96 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) + uint32 procFlags; // bitmask for matching proc event + uint32 procEx; // proc Extend info (see ProcFlagsEx) + float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc + float customChance; // Owerride chance (in most cases for debug only) + uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_ +}; + +typedef std::unordered_map SpellProcEventMap; + struct SpellProcEntry { uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school @@ -610,6 +658,10 @@ class TC_GAME_API SpellMgr SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const; SpellGroupStackRule GetSpellGroupStackRule(SpellGroup groupid) const; + // Spell proc event table + SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const; + bool IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const; + // Spell proc table SpellProcEntry const* GetSpellProcEntry(uint32 spellId) const; bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; @@ -668,6 +720,7 @@ class TC_GAME_API SpellMgr void LoadSpellTargetPositions(); void LoadSpellGroups(); void LoadSpellGroupStackRules(); + void LoadSpellProcEvents(); void LoadSpellProcs(); void LoadSpellBonusess(); void LoadSpellThreats(); @@ -696,6 +749,7 @@ class TC_GAME_API SpellMgr SpellSpellGroupMap mSpellSpellGroup; SpellGroupSpellMap mSpellGroupSpell; SpellGroupStackMap mSpellGroupStack; + SpellProcEventMap mSpellProcEventMap; SpellProcMap mSpellProcMap; SpellBonusMap mSpellBonusMap; SpellThreatMap mSpellThreatMap; diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index 67aaa582776..e2598386466 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -720,10 +720,6 @@ bool AuraScript::_Validate(SpellInfo const* entry) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); - for (std::list::iterator itr = DoCheckEffectProc.begin(); itr != DoCheckEffectProc.end(); ++itr) - if (!itr->GetAffectedEffectsMask(entry)) - TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoCheckEffectProc` of AuraScript won't be executed", entry->Id, itr->ToString().c_str(), m_scriptName->c_str()); - for (std::list::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) TC_LOG_ERROR("scripts", "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); @@ -891,17 +887,6 @@ bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& e return (auraScript->*_HandlerScript)(eventInfo); } -AuraScript::CheckEffectProcHandler::CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) - : AuraScript::EffectBase(effIndex, effName) -{ - _HandlerScript = handlerScript; -} - -bool AuraScript::CheckEffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo) -{ - return (auraScript->*_HandlerScript)(aurEff, eventInfo); -} - AuraScript::AuraProcHandler::AuraProcHandler(AuraProcFnType handlerScript) { _HandlerScript = handlerScript; @@ -1163,7 +1148,6 @@ Unit* AuraScript::GetTarget() const case AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: case AURA_SCRIPT_HOOK_CHECK_PROC: - case AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC: case AURA_SCRIPT_HOOK_PREPARE_PROC: case AURA_SCRIPT_HOOK_PROC: case AURA_SCRIPT_HOOK_AFTER_PROC: diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 1ec10f03820..539bc54cc94 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -468,7 +468,6 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_AFTER_DISPEL, // Spell Proc Hooks AURA_SCRIPT_HOOK_CHECK_PROC, - AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, AURA_SCRIPT_HOOK_PREPARE_PROC, AURA_SCRIPT_HOOK_PROC, AURA_SCRIPT_HOOK_EFFECT_PROC, @@ -500,7 +499,6 @@ class TC_GAME_API AuraScript : public _SpellScript typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \ - typedef bool(CLASSNAME::*AuraCheckEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \ typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ @@ -610,14 +608,6 @@ class TC_GAME_API AuraScript : public _SpellScript private: AuraCheckProcFnType _HandlerScript; }; - class TC_GAME_API CheckEffectProcHandler : public EffectBase - { - public: - CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName); - bool Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo); - private: - AuraCheckEffectProcFnType _HandlerScript; - }; class TC_GAME_API AuraProcHandler { public: @@ -648,7 +638,6 @@ class TC_GAME_API AuraScript : public _SpellScript class EffectManaShieldFunction : public AuraScript::EffectManaShieldHandler { public: EffectManaShieldFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectManaShieldHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) { } }; \ class EffectSplitFunction : public AuraScript::EffectSplitHandler { public: EffectSplitFunction(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectSplitHandler((AuraScript::AuraEffectSplitFnType)_pEffectHandlerScript, _effIndex) { } }; \ class CheckProcHandlerFunction : public AuraScript::CheckProcHandler { public: CheckProcHandlerFunction(AuraCheckProcFnType handlerScript) : AuraScript::CheckProcHandler((AuraScript::AuraCheckProcFnType)handlerScript) { } }; \ - class CheckEffectProcHandlerFunction : public AuraScript::CheckEffectProcHandler { public: CheckEffectProcHandlerFunction(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) : AuraScript::CheckEffectProcHandler((AuraScript::AuraCheckEffectProcFnType)handlerScript, effIndex, effName) { } }; \ class AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) { } }; \ class EffectProcHandlerFunction : public AuraScript::EffectProcHandler { public: EffectProcHandlerFunction(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) : AuraScript::EffectProcHandler((AuraScript::AuraEffectProcFnType)effectHandlerScript, effIndex, effName) { } } @@ -787,12 +776,6 @@ class TC_GAME_API AuraScript : public _SpellScript HookList DoCheckProc; #define AuraCheckProcFn(F) CheckProcHandlerFunction(&F) - // executed when aura effect checks if it can proc the aura - // example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier); - // where function is bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo); - HookList DoCheckEffectProc; - #define AuraCheckEffectProcFn(F, I, N) CheckEffectProcHandlerFunction(&F, I, N) - // executed before aura procs (possibility to prevent charge drop/cooldown) // example: DoPrepareProc += AuraProcFn(class::function); // where function is: void function (ProcEventInfo& eventInfo); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c97eba9f3be..27c9435ba8d 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1560,6 +1560,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Spell Learn Spells..."); sSpellMgr->LoadSpellLearnSpells(); + TC_LOG_INFO("server.loading", "Loading Spell Proc Event conditions..."); + sSpellMgr->LoadSpellProcEvents(); + TC_LOG_INFO("server.loading", "Loading Spell Proc conditions and data..."); sSpellMgr->LoadSpellProcs(); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index ab59559af8f..b7bc2ee262b 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -142,6 +142,7 @@ public: { "spell_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesSpellCommand, "" }, { "spell_linked_spell", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL, true, &HandleReloadSpellLinkedSpellCommand, "" }, { "spell_pet_auras", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS, true, &HandleReloadSpellPetAurasCommand, "" }, + { "spell_proc_event", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC_EVENT, true, &HandleReloadSpellProcEventCommand, "" }, { "spell_proc", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC, true, &HandleReloadSpellProcsCommand, "" }, { "spell_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS, true, &HandleReloadSpellScriptsCommand, "" }, { "spell_target_position", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, true, &HandleReloadSpellTargetPositionCommand, "" }, @@ -273,6 +274,7 @@ public: HandleReloadSpellGroupsCommand(handler, "a"); HandleReloadSpellLearnSpellCommand(handler, "a"); HandleReloadSpellLinkedSpellCommand(handler, "a"); + HandleReloadSpellProcEventCommand(handler, "a"); HandleReloadSpellProcsCommand(handler, "a"); HandleReloadSpellBonusesCommand(handler, "a"); HandleReloadSpellTargetPositionCommand(handler, "a"); @@ -807,6 +809,14 @@ public: return true; } + static bool HandleReloadSpellProcEventCommand(ChatHandler* handler, const char* /*args*/) + { + TC_LOG_INFO("misc", "Re-Loading Spell Proc Event conditions..."); + sSpellMgr->LoadSpellProcEvents(); + handler->SendGlobalGMSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded."); + return true; + } + static bool HandleReloadSpellProcsCommand(ChatHandler* handler, const char* /*args*/) { TC_LOG_INFO("misc", "Re-Loading Spell Proc conditions and data...");