diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Maps/Map.cpp | 14 | ||||
-rw-r--r-- | src/server/game/Maps/Map.h | 6 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 1032 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 94 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 16 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 17 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/SpellMgr.cpp | 11 | ||||
-rw-r--r-- | src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp | 30 |
10 files changed, 604 insertions, 620 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 52c635ca059..2dcf47bb590 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -22366,7 +22366,7 @@ Unit* Player::GetSelectedUnit() const Player* Player::GetSelectedPlayer() const { if (ObjectGuid selectionGUID = GetTarget()) - return ObjectAccessor::GetPlayer(*this, selectionGUID); + return ObjectAccessor::FindConnectedPlayer(selectionGUID); return nullptr; } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 347882d34c6..609426e1b11 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3395,8 +3395,22 @@ bool Map::IsSpawnGroupActive(uint32 groupId) const return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN); } +void Map::AddFarSpellCallback(FarSpellCallback&& callback) +{ + _farSpellCallbacks.Enqueue(new FarSpellCallback(std::move(callback))); +} + void Map::DelayedUpdate(uint32 t_diff) { + { + FarSpellCallback* callback; + while (_farSpellCallbacks.Dequeue(callback)) + { + (*callback)(this); + delete callback; + } + } + for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) { Transport* transport = *_transportsUpdateIter; diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 94feabcc7af..adccf8c00c5 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -26,6 +26,7 @@ #include "GridDefines.h" #include "GridRefManager.h" #include "MapRefManager.h" +#include "MPSCQueue.h" #include "ObjectGuid.h" #include "Optional.h" #include "SharedDefines.h" @@ -853,6 +854,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType> // This will not affect any already-present creatures in the group void SetSpawnGroupInactive(uint32 groupId) { SetSpawnGroupActive(groupId, false); } + typedef std::function<void(Map*)> FarSpellCallback; + void AddFarSpellCallback(FarSpellCallback&& callback); + private: // Type specific code for add/remove to/from grid template<class T> @@ -916,6 +920,8 @@ class TC_GAME_API Map : public GridRefManager<NGridType> std::unordered_set<Corpse*> _corpseBones; std::unordered_set<Object*> _updateObjects; + + MPSCQueue<FarSpellCallback> _farSpellCallbacks; }; enum InstanceResetMethod diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 799818fd81f..145c385dcd0 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -806,9 +806,9 @@ void Spell::SelectSpellTargets() } uint8 mask = (1 << i); - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { - if (ihit->effectMask & mask) + if (ihit->EffectMask & mask) { m_channelTargetEffectMask |= mask; break; @@ -818,20 +818,19 @@ void Spell::SelectSpellTargets() else if (m_auraScaleMask) { bool checkLvl = !m_UniqueTargetInfo.empty(); - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end();) + m_UniqueTargetInfo.erase(std::remove_if(std::begin(m_UniqueTargetInfo), std::end(m_UniqueTargetInfo), [&](TargetInfo const& targetInfo) -> bool { // remove targets which did not pass min level check - if (m_auraScaleMask && ihit->effectMask == m_auraScaleMask) + if (m_auraScaleMask && targetInfo.EffectMask == m_auraScaleMask) { // Do not check for selfcast - if (!ihit->scaleAura && ihit->targetGUID != m_caster->GetGUID()) - { - m_UniqueTargetInfo.erase(ihit++); - continue; - } + if (!targetInfo.ScaleAura && targetInfo.TargetGUID != m_caster->GetGUID()) + return true; } - ++ihit; - } + + return false; + }), std::end(m_UniqueTargetInfo)); + if (checkLvl && m_UniqueTargetInfo.empty()) { SendCastResult(SPELL_FAILED_LOWLEVEL); @@ -1232,11 +1231,11 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge case TARGET_REFERENCE_TYPE_LAST: { // find last added target for this effect - for (std::list<TargetInfo>::reverse_iterator ihit = m_UniqueTargetInfo.rbegin(); ihit != m_UniqueTargetInfo.rend(); ++ihit) + for (auto ihit = m_UniqueTargetInfo.rbegin(); ihit != m_UniqueTargetInfo.rend(); ++ihit) { - if (ihit->effectMask & (1 << effIndex)) + if (ihit->EffectMask & (1 << effIndex)) { - referer = ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + referer = ObjectAccessor::GetUnit(*m_caster, ihit->TargetGUID); break; } } @@ -1687,8 +1686,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) { - // special case for SPELL_EFFECT_SUMMON_RAF_FRIEND and SPELL_EFFECT_SUMMON_PLAYER - /// @todo this is a workaround - target shouldn't be stored in target map for those spells + // special case for SPELL_EFFECT_SUMMON_RAF_FRIEND and SPELL_EFFECT_SUMMON_PLAYER, queue them on map for later execution switch (m_spellInfo->Effects[effIndex].Effect) { case SPELL_EFFECT_SUMMON_RAF_FRIEND: @@ -1696,11 +1694,31 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->GetTarget()) { WorldObject* target = ObjectAccessor::FindPlayer(m_caster->GetTarget()); - CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo()); - if (target && target->ToPlayer()) - AddUnitTarget(target->ToUnit(), 1 << effIndex, false); + // scripts may modify the target - recheck + if (target && target->GetTypeId() == TYPEID_PLAYER) + { + // target is not stored in target map for those spells + // since we're completely skipping AddUnitTarget logic, we need to check immunity manually + // eg. aura 21546 makes target immune to summons + Player* player = target->ToPlayer(); + if (player->IsImmunedToSpellEffect(m_spellInfo, effIndex, nullptr)) + return; + + target->GetMap()->AddFarSpellCallback(std::bind([](Map* map, Spell* spell, uint8 effIndex, ObjectGuid const& targetGuid) + { + Player* player = ObjectAccessor::GetPlayer(map, targetGuid); + if (!player) + return; + + // check immunity again in case it changed during update + if (player->IsImmunedToSpellEffect(spell->GetSpellInfo(), effIndex, nullptr)) + return; + + spell->HandleEffects(player, nullptr, nullptr, effIndex, SPELL_EFFECT_HANDLE_HIT_TARGET); + }, std::placeholders::_1, this, effIndex, target->GetGUID())); + } } return; default: @@ -1994,7 +2012,7 @@ void Spell::prepareDataForTriggerSystem() m_procAttacker = PROC_FLAG_DONE_RANGED_AUTO_ATTACK; m_procVictim = PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK; } - // For other spells trigger procflags are set in Spell::DoAllEffectOnTarget + // For other spells trigger procflags are set in Spell::TargetInfo::DoDamageAndTriggers // Because spell positivity is dependant on target } @@ -2006,7 +2024,7 @@ void Spell::prepareDataForTriggerSystem() { m_procAttacker |= PROC_FLAG_DONE_TRAP_ACTIVATION; - // also fill up other flags (DoAllEffectOnTarget only fills up flag if both are not set) + // also fill up other flags (TargetInfo::DoDamageAndTriggers only fills up flag if both are not set) m_procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; m_procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG; } @@ -2075,45 +2093,44 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= ObjectGuid targetGUID = target->GetGUID(); // Lookup target in already in list - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + auto ihit = std::find_if(std::begin(m_UniqueTargetInfo), std::end(m_UniqueTargetInfo), [targetGUID](TargetInfo const& target) { return target.TargetGUID == targetGUID; }); + if (ihit != std::end(m_UniqueTargetInfo)) // Found in list { - if (targetGUID == ihit->targetGUID) // Found in list + // Immune effects removed from mask + ihit->EffectMask |= effectMask; + ihit->ScaleAura = false; + if (m_auraScaleMask && ihit->EffectMask == m_auraScaleMask && m_caster != target) { - ihit->effectMask |= effectMask; // Immune effects removed from mask - ihit->scaleAura = false; - if (m_auraScaleMask && ihit->effectMask == m_auraScaleMask && m_caster != target) - { - SpellInfo const* auraSpell = m_spellInfo->GetFirstRankSpell(); - if (uint32(target->getLevel() + 10) >= auraSpell->SpellLevel) - ihit->scaleAura = true; - } - return; + SpellInfo const* auraSpell = m_spellInfo->GetFirstRankSpell(); + if (uint32(target->getLevel() + 10) >= auraSpell->SpellLevel) + ihit->ScaleAura = true; } + return; } // This is new target calculate data for him // Get spell hit result on target TargetInfo targetInfo; - targetInfo.targetGUID = targetGUID; // Store target GUID - targetInfo.effectMask = effectMask; // Store all effects not immune - targetInfo.processed = false; // Effects not apply on target - targetInfo.alive = target->IsAlive(); - targetInfo.damage = 0; - targetInfo.crit = false; - targetInfo.scaleAura = false; - if (m_auraScaleMask && targetInfo.effectMask == m_auraScaleMask && m_caster != target) + targetInfo.TargetGUID = targetGUID; // Store target GUID + targetInfo.EffectMask = effectMask; // Store all effects not immune + targetInfo.IsAlive = target->IsAlive(); + targetInfo.Damage = 0; + targetInfo.Healing = 0; + targetInfo.IsCrit = false; + targetInfo.ScaleAura = false; + if (m_auraScaleMask && targetInfo.EffectMask == m_auraScaleMask && m_caster != target) { SpellInfo const* auraSpell = m_spellInfo->GetFirstRankSpell(); if (uint32(target->getLevel() + 10) >= auraSpell->SpellLevel) - targetInfo.scaleAura = true; + targetInfo.ScaleAura = true; } // Calculate hit result if (m_originalCaster) - targetInfo.missCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); + targetInfo.MissCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); else - targetInfo.missCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE; + targetInfo.MissCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE; // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. @@ -2125,32 +2142,32 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= if (dist < 5.0f) dist = 5.0f; - targetInfo.timeDelay = (uint64)std::floor(dist / m_spellInfo->Speed * 1000.0f); + targetInfo.TimeDelay = uint64(std::floor(dist / m_spellInfo->Speed * 1000.0f)); // Calculate minimum incoming time - if (!m_delayMoment || m_delayMoment > targetInfo.timeDelay) - m_delayMoment = targetInfo.timeDelay; + if (!m_delayMoment || m_delayMoment > targetInfo.TimeDelay) + m_delayMoment = targetInfo.TimeDelay; } else - targetInfo.timeDelay = 0ULL; + targetInfo.TimeDelay = 0ULL; // If target reflect spell back to caster - if (targetInfo.missCondition == SPELL_MISS_REFLECT) + 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, false); // can't reflect twice // 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)); + target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(targetInfo.TimeDelay)); // Increase time interval for reflected spells by 1.5 - targetInfo.timeDelay += targetInfo.timeDelay >> 1; + targetInfo.TimeDelay += targetInfo.TimeDelay >> 1; } else - targetInfo.reflectResult = SPELL_MISS_NONE; + targetInfo.ReflectResult = SPELL_MISS_NONE; // Add target to list - m_UniqueTargetInfo.push_back(targetInfo); + m_UniqueTargetInfo.emplace_back(std::move(targetInfo)); } void Spell::AddGOTarget(GameObject* go, uint32 effectMask) @@ -2181,21 +2198,19 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) ObjectGuid targetGUID = go->GetGUID(); // Lookup target in already in list - for (std::list<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) + auto ihit = std::find_if(std::begin(m_UniqueGOTargetInfo), std::end(m_UniqueGOTargetInfo), [targetGUID](GOTargetInfo const& target) { return target.TargetGUID == targetGUID; }); + if (ihit != std::end(m_UniqueGOTargetInfo)) // Found in list { - if (targetGUID == ihit->targetGUID) // Found in list - { - ihit->effectMask |= effectMask; // Add only effect mask - return; - } + // Add only effect mask + ihit->EffectMask |= effectMask; + return; } // This is new target calculate data for him GOTargetInfo target; - target.targetGUID = targetGUID; - target.effectMask = effectMask; - target.processed = false; // Effects not apply on target + target.TargetGUID = targetGUID; + target.EffectMask = effectMask; // Spell have speed - need calculate incoming time if (m_spellInfo->Speed > 0.0f) @@ -2204,15 +2219,15 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask) float dist = m_caster->GetDistance(go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()); if (dist < 5.0f) dist = 5.0f; - target.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f)); - if (!m_delayMoment || m_delayMoment > target.timeDelay) - m_delayMoment = target.timeDelay; + target.TimeDelay = uint64(std::floor(dist / m_spellInfo->Speed * 1000.0f)); + if (!m_delayMoment || m_delayMoment > target.TimeDelay) + m_delayMoment = target.TimeDelay; } else - target.timeDelay = 0LL; + target.TimeDelay = 0ULL; // Add target to list - m_UniqueGOTargetInfo.push_back(target); + m_UniqueGOTargetInfo.emplace_back(std::move(target)); } void Spell::AddItemTarget(Item* item, uint32 effectMask) @@ -2226,22 +2241,21 @@ void Spell::AddItemTarget(Item* item, uint32 effectMask) return; // Lookup target in already in list - for (std::list<ItemTargetInfo>::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) + auto ihit = std::find_if(std::begin(m_UniqueItemInfo), std::end(m_UniqueItemInfo), [item](ItemTargetInfo const& target) { return target.TargetItem == item; }); + if (ihit != std::end(m_UniqueItemInfo)) // Found in list { - if (item == ihit->item) // Found in list - { - ihit->effectMask |= effectMask; // Add only effect mask - return; - } + // Add only effect mask + ihit->EffectMask |= effectMask; + return; } // This is new target add data ItemTargetInfo target; - target.item = item; - target.effectMask = effectMask; + target.TargetItem = item; + target.EffectMask = effectMask; - m_UniqueItemInfo.push_back(target); + m_UniqueItemInfo.emplace_back(std::move(target)); } void Spell::AddDestTarget(SpellDestination const& dest, uint32 effIndex) @@ -2249,122 +2263,124 @@ void Spell::AddDestTarget(SpellDestination const& dest, uint32 effIndex) m_destTargets[effIndex] = dest; } -void Spell::DoAllEffectOnTarget(TargetInfo* target) +void Spell::TargetInfo::PreprocessTarget(Spell* spell) { - if (!target || target->processed) + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + if (!unit) return; - target->processed = true; // Target checked in apply effects procedure + // Need init unitTarget by default unit (can changed in code on reflect) + spell->unitTarget = unit; - // Get mask of effects for target - uint8 mask = target->effectMask; + // Reset damage/healing counter + spell->m_damage = Damage; + spell->m_healing = Healing; - Unit* unit = m_caster->GetGUID() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); - if (!unit && !target->targetGUID.IsPlayer()) // only players may be targeted across maps - return; + _spellHitTarget = nullptr; + if (MissCondition == SPELL_MISS_NONE) + _spellHitTarget = unit; + else if (MissCondition == SPELL_MISS_REFLECT && ReflectResult == SPELL_MISS_NONE) + _spellHitTarget = spell->m_caster; - if (!unit) + _enablePVP = false; // need to check PvP state before spell effects, but act on it afterwards + if (_spellHitTarget) { - uint8 farMask = 0; - // create far target mask - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (m_spellInfo->Effects[i].IsFarUnitTargetEffect()) - if ((1 << i) & mask) - farMask |= (1 << i); - - if (!farMask) - return; - // find unit in world - unit = ObjectAccessor::FindPlayer(target->targetGUID); - if (!unit) - return; + // if target is flagged for pvp also flag caster if a player + if (unit->IsPvP() && spell->m_caster->GetTypeId() == TYPEID_PLAYER) + _enablePVP = true; // Decide on PvP flagging now, but act on it later. - // do far effects on the unit - // can't use default call because of threading, do stuff as fast as possible - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (farMask & (1 << i)) - HandleEffects(unit, nullptr, nullptr, i, SPELL_EFFECT_HANDLE_HIT_TARGET); - return; + SpellMissInfo missInfo = spell->PreprocessSpellHit(_spellHitTarget, ScaleAura, *this); + if (missInfo != SPELL_MISS_NONE) + { + if (missInfo != SPELL_MISS_MISS) + spell->m_caster->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo); + spell->m_damage = 0; + _spellHitTarget = nullptr; + } } - if (unit->IsAlive() != target->alive) - return; - - if (getState() == SPELL_STATE_DELAYED && !IsPositive() && (GameTime::GetGameTimeMS() - target->timeDelay) <= unit->m_lastSanctuaryTime) - return; // No missinfo in that case + spell->CallScriptOnHitHandlers(); - // Get original caster (if exist) and calculate damage/healing from him data - Unit* caster = m_originalCaster ? m_originalCaster : m_caster; + // scripts can modify damage/healing for current target, save them + Damage = spell->m_damage; + Healing = spell->m_healing; +} - // Skip if m_originalCaster not avaiable - if (!caster) +void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) +{ + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + if (!unit) return; - SpellMissInfo missInfo = target->missCondition; - // Need init unitTarget by default unit (can changed in code on reflect) // Or on missInfo != SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem) - unitTarget = unit; - targetMissInfo = missInfo; + spell->unitTarget = unit; + spell->targetMissInfo = MissCondition; // Reset damage/healing counter - m_damage = target->damage; - m_healing = -target->damage; + spell->m_damage = Damage; + spell->m_healing = Healing; - // Fill base trigger info - uint32 procAttacker = m_procAttacker; - uint32 procVictim = m_procVictim; - uint32 hitMask = PROC_HIT_NONE; + if (unit->IsAlive() != IsAlive) + return; - // Spells with this flag cannot trigger if effect is cast on self - bool const canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); - Unit* spellHitTarget = nullptr; + if (spell->getState() == SPELL_STATE_DELAYED && !spell->IsPositive() && (GameTime::GetGameTimeMS() - TimeDelay) <= unit->m_lastSanctuaryTime) + return; // No missinfo in that case - if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target - spellHitTarget = unit; - else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit) - { - if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him - spellHitTarget = m_caster; - } + if (_spellHitTarget) + spell->DoSpellEffectHit(_spellHitTarget, effIndex, *this); - bool enablePvP = false; // need to check PvP state before spell effects, but act on it afterwards + // scripts can modify damage/healing for current target, save them + Damage = spell->m_damage; + Healing = spell->m_healing; +} - if (spellHitTarget) - { - // if target is flagged for pvp also flag caster if a player - if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER) - enablePvP = true; // Decide on PvP flagging now, but act on it later. +void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) +{ + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + if (!unit) + return; - SpellMissInfo missInfo2 = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura); - if (missInfo2 != SPELL_MISS_NONE) - { - if (missInfo2 != SPELL_MISS_MISS) - m_caster->SendSpellMiss(unit, m_spellInfo->Id, missInfo2); - m_damage = 0; - spellHitTarget = nullptr; - } - } + // other targets executed before this one changed pointer + spell->unitTarget = unit; + if (_spellHitTarget) + spell->unitTarget = _spellHitTarget; - // Do not take combo points on dodge and miss - if (missInfo != SPELL_MISS_NONE && m_needComboPoints && m_targets.GetUnitTargetGUID() == target->targetGUID) - m_needComboPoints = false; + // Reset damage/healing counter + spell->m_damage = Damage; + spell->m_healing = Healing; + + // Get original caster (if exist) and calculate damage/healing from him data + // Skip if m_originalCaster not available + Unit* caster = spell->m_originalCaster ? spell->m_originalCaster : spell->m_caster; + if (!caster) + return; + + // Fill base trigger info + uint32 procAttacker = spell->m_procAttacker; + uint32 procVictim = spell->m_procVictim; + uint32 procSpellType = PROC_SPELL_TYPE_NONE; + uint32 hitMask = PROC_HIT_NONE; + + // Spells with this flag cannot trigger if effect is cast on self + bool const canEffectTrigger = !spell->m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && spell->unitTarget->CanProc() && + (spell->CanExecuteTriggersOnHit(EffectMask) || MissCondition == SPELL_MISS_IMMUNE || MissCondition == SPELL_MISS_IMMUNE2); // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now if (canEffectTrigger && !procAttacker && !procVictim) { bool positive = true; - if (m_damage > 0) + if (spell->m_damage > 0) positive = false; - else if (!m_healing) + else if (!spell->m_healing) { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { // in case of immunity, check all effects to choose correct procFlags, as none has technically hit - if (target->effectMask && !(target->effectMask & (1 << i))) + if (EffectMask && !(EffectMask & (1 << i))) continue; - if (!m_spellInfo->IsPositiveEffect(i)) + if (!spell->m_spellInfo->IsPositiveEffect(i)) { positive = false; break; @@ -2372,164 +2388,204 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) } } - switch (m_spellInfo->DmgClass) + switch (spell->m_spellInfo->DmgClass) { case SPELL_DAMAGE_CLASS_MAGIC: if (positive) { procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS; - procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS; + procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS; } else { procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; - procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG; + procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG; } - break; + break; case SPELL_DAMAGE_CLASS_NONE: if (positive) { procAttacker |= PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS; - procVictim |= PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS; + procVictim |= PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS; } else { procAttacker |= PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; - procVictim |= PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG; + procVictim |= PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG; } - break; + break; } } - CallScriptOnHitHandlers(); // All calculated do it! - // Do healing and triggers - if (m_healing > 0) + // Do healing + std::unique_ptr<DamageInfo> spellDamageInfo; + std::unique_ptr<HealInfo> healInfo; + if (spell->m_healing > 0) { - bool crit = target->crit; - uint32 addhealth = m_healing; - if (crit) + uint32 addhealth = spell->m_healing; + if (IsCrit) { hitMask |= PROC_HIT_CRITICAL; - addhealth = Unit::SpellCriticalHealingBonus(caster, m_spellInfo, addhealth, nullptr); + addhealth = Unit::SpellCriticalHealingBonus(caster, spell->m_spellInfo, addhealth, nullptr); } else hitMask |= PROC_HIT_NORMAL; - HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask()); - caster->HealBySpell(healInfo, crit); - unitTarget->GetThreatManager().ForwardThreatForAssistingMe(caster, float(healInfo.GetEffectiveHeal())*0.5f, m_spellInfo); - m_healing = healInfo.GetEffectiveHeal(); + healInfo = std::make_unique<HealInfo>(caster, spell->unitTarget, addhealth, spell->m_spellInfo, spell->m_spellInfo->GetSchoolMask()); + caster->HealBySpell(*healInfo, IsCrit); + spell->unitTarget->GetThreatManager().ForwardThreatForAssistingMe(caster, float(healInfo->GetEffectiveHeal()) * 0.5f, spell->m_spellInfo); + spell->m_healing = healInfo->GetEffectiveHeal(); - // Do triggers for unit - if (canEffectTrigger) - Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, nullptr, &healInfo); + procSpellType |= PROC_SPELL_TYPE_HEAL; } - // Do damage and triggers - else if (m_damage > 0) + + // Do damage + if (spell->m_damage > 0) { // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - + SpellNonMeleeDamage damageInfo(caster, spell->unitTarget, spell->m_spellInfo->Id, spell->m_spellSchoolMask); // Check damage immunity - if (unitTarget->IsImmunedToDamage(m_spellInfo)) + if (spell->unitTarget->IsImmunedToDamage(spell->m_spellInfo)) { hitMask = PROC_HIT_IMMUNE; - m_damage = 0; + spell->m_damage = 0; // no packet found in sniffs } else { // Add bonuses and fill damageInfo struct - caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); + caster->CalculateSpellDamageTaken(&damageInfo, spell->m_damage, spell->m_spellInfo, spell->m_attackType, IsCrit); Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); // Send log damage message to client caster->SendSpellNonMeleeDamageLog(&damageInfo); - hitMask |= createProcHitMask(&damageInfo, missInfo); + hitMask |= createProcHitMask(&damageInfo, MissCondition); procVictim |= PROC_FLAG_TAKEN_DAMAGE; - m_damage = damageInfo.damage; + spell->m_damage = damageInfo.damage; caster->DealSpellDamage(&damageInfo, true); } // Do triggers for unit if (canEffectTrigger) { - DamageInfo spellDamageInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, hitMask); - Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, hitMask, this, &spellDamageInfo, nullptr); + spellDamageInfo = std::make_unique<DamageInfo>(damageInfo, SPELL_DIRECT_DAMAGE, spell->m_attackType, hitMask); + procSpellType |= PROC_SPELL_TYPE_DAMAGE; - if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && !m_spellInfo->HasAttribute(SPELL_ATTR4_CANT_TRIGGER_ITEM_SPELLS) && - (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(spellDamageInfo); + if (caster->GetTypeId() == TYPEID_PLAYER && !spell->m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && !spell->m_spellInfo->HasAttribute(SPELL_ATTR4_CANT_TRIGGER_ITEM_SPELLS) && + (spell->m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || spell->m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(*spellDamageInfo); } } + // Passive spell hits/misses or active spells only misses (only triggers) - else + if (spell->m_damage <= 0 && spell->m_healing <= 0) { // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - hitMask |= createProcHitMask(&damageInfo, missInfo); + SpellNonMeleeDamage damageInfo(caster, spell->unitTarget, spell->m_spellInfo->Id, spell->m_spellSchoolMask); + hitMask |= createProcHitMask(&damageInfo, MissCondition); // Do triggers for unit if (canEffectTrigger) { - DamageInfo spellNoDamageInfo(damageInfo, NODAMAGE, m_attackType, hitMask); - Unit::ProcSkillsAndAuras(caster, 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->HasAttribute(SPELL_ATTR4_CANT_TRIGGER_ITEM_SPELLS) && - (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(spellNoDamageInfo); + spellDamageInfo = std::make_unique<DamageInfo>(damageInfo, NODAMAGE, spell->m_attackType, hitMask); + procSpellType |= PROC_SPELL_TYPE_NO_DMG_HEAL; } // Failed Pickpocket, reveal rogue - if (missInfo == SPELL_MISS_RESIST && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && unitTarget->GetTypeId() == TYPEID_UNIT) + if (MissCondition == SPELL_MISS_RESIST && spell->m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && spell->unitTarget->GetTypeId() == TYPEID_UNIT) { - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); - unitTarget->ToCreature()->EngageWithTarget(m_caster); + spell->m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); + spell->unitTarget->ToCreature()->EngageWithTarget(spell->m_caster); + } + } + + // Do triggers for unit + if (canEffectTrigger) + { + Unit::ProcSkillsAndAuras(caster, spell->unitTarget, procAttacker, procVictim, procSpellType, PROC_SPELL_PHASE_HIT, hitMask, spell, spellDamageInfo.get(), healInfo.get()); + + // item spells (spell hit of non-damage spell may also activate items, for example seal of corruption hidden hit) + if (caster->GetTypeId() == TYPEID_PLAYER && (procSpellType & (PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL))) + { + if (spell->m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || spell->m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED) + if (!spell->m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET) && !spell->m_spellInfo->HasAttribute(SPELL_ATTR4_CANT_TRIGGER_ITEM_SPELLS)) + caster->ToPlayer()->CastItemCombatSpell(*spellDamageInfo); } } // set hitmask for finish procs - m_hitMask |= hitMask; + spell->m_hitMask |= hitMask; - // spellHitTarget can be null if spell is missed in DoSpellHitOnUnit - if (missInfo != SPELL_MISS_EVADE && spellHitTarget && !m_caster->IsFriendlyTo(unit) && (!IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) + // Do not take combo points on dodge and miss + if (MissCondition != SPELL_MISS_NONE && spell->m_needComboPoints && spell->m_targets.GetUnitTargetGUID() == TargetGUID) + spell->m_needComboPoints = false; + + // _spellHitTarget can be null if spell is missed in DoSpellHitOnUnit + if (MissCondition != SPELL_MISS_EVADE && _spellHitTarget && !spell->m_caster->IsFriendlyTo(unit) && (!spell->IsPositive() || spell->m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) { - m_caster->AttackedTarget(unit, m_spellInfo->HasInitialAggro()); + spell->m_caster->AttackedTarget(unit, spell->m_spellInfo->HasInitialAggro()); if (!unit->IsStandState()) unit->SetStandState(UNIT_STAND_STATE_STAND); } // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER - if (missInfo == SPELL_MISS_NONE && m_spellInfo->HasAttribute(SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER) && unit->GetTypeId() != TYPEID_PLAYER) + if (MissCondition == SPELL_MISS_NONE && spell->m_spellInfo->HasAttribute(SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER) && unit->GetTypeId() != TYPEID_PLAYER) caster->CastSpell(unit, SPELL_INTERRUPT_NONPLAYER, true); - if (spellHitTarget) + if (_spellHitTarget) { //AI functions - if (spellHitTarget->GetTypeId() == TYPEID_UNIT) - if (spellHitTarget->ToCreature()->IsAIEnabled) - spellHitTarget->ToCreature()->AI()->SpellHit(m_caster, m_spellInfo); + if (_spellHitTarget->GetTypeId() == TYPEID_UNIT) + if (_spellHitTarget->ToCreature()->IsAIEnabled) + _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster, spell->m_spellInfo); - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsAIEnabled) - m_caster->ToCreature()->AI()->SpellHitTarget(spellHitTarget, m_spellInfo); + if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled) + spell->m_caster->ToCreature()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo); // Needs to be called after dealing damage/healing to not remove breaking on damage auras - DoTriggersOnSpellHit(spellHitTarget, mask); + spell->DoTriggersOnSpellHit(_spellHitTarget, EffectMask); - if (enablePvP) - m_caster->ToPlayer()->UpdatePvP(true); - - CallScriptAfterHitHandlers(); + if (_enablePVP) + spell->m_caster->ToPlayer()->UpdatePvP(true); } + + spell->CallScriptAfterHitHandlers(); } -SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleAura) +void Spell::GOTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) { - if (!unit || !effectMask) + GameObject* go = ObjectAccessor::GetGameObject(*spell->m_caster, TargetGUID); + if (!go) + return; + + spell->CallScriptBeforeHitHandlers(); + + spell->HandleEffects(nullptr, nullptr, go, effIndex, SPELL_EFFECT_HANDLE_HIT_TARGET); + + if (go->AI()) + go->AI()->SpellHit(spell->m_caster, spell->m_spellInfo); + + spell->CallScriptOnHitHandlers(); + spell->CallScriptAfterHitHandlers(); +} + +void Spell::ItemTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) +{ + spell->CallScriptBeforeHitHandlers(); + + spell->HandleEffects(nullptr, TargetItem, nullptr, effIndex, SPELL_EFFECT_HANDLE_HIT_TARGET); + + spell->CallScriptOnHitHandlers(); + spell->CallScriptAfterHitHandlers(); +} + +SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& hitInfo) +{ + if (!unit) return SPELL_MISS_EVADE; // Target may have begun evading between launch and hit phases - re-check now @@ -2541,21 +2597,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (m_spellInfo->Speed && unit->IsImmunedToSpell(m_spellInfo, m_caster)) return SPELL_MISS_IMMUNE; - // disable effects to which unit is immune - SpellMissInfo returnVal = SPELL_MISS_IMMUNE; - for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber) - { - if (effectMask & (1 << effectNumber)) - { - if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber, m_caster)) - effectMask &= ~(1 << effectNumber); - } - } - - if (!effectMask) - return returnVal; - - PrepareScriptHitHandlers(); CallScriptBeforeHitHandlers(); if (Player* player = unit->ToPlayer()) @@ -2603,43 +2644,76 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA } } - uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, effectMask, unit); - - // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add - bool triggered = (m_triggeredByAuraSpell != nullptr); - DiminishingGroup diminishGroup = m_spellInfo->GetDiminishingReturnsGroupForSpell(triggered); - - DiminishingLevels diminishLevel = DIMINISHING_LEVEL_1; - if (diminishGroup && aura_effmask) - { - diminishLevel = unit->GetDiminishing(diminishGroup); - DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(triggered); - // Increase Diminishing on unit, current informations for actually casts will use values above - if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && unit->IsAffectedByDiminishingReturns())) - unit->IncrDiminishing(m_spellInfo, triggered); - } - - if (aura_effmask) + // check immunity due to diminishing returns + if (m_originalCaster && Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit)) { // Select rank for aura with level requirements only in specific cases // Unit has to be target only of aura effect, both caster and target have to be players, target has to be other than unit target - SpellInfo const* aurSpellInfo = m_spellInfo; - int32 basePoints[MAX_SPELL_EFFECTS] = { }; + hitInfo.AuraSpellInfo = m_spellInfo; if (scaleAura) { - aurSpellInfo = m_spellInfo->GetAuraRankForLevel(unitTarget->getLevel()); - ASSERT(aurSpellInfo); + if (SpellInfo const* actualSpellInfo = m_spellInfo->GetAuraRankForLevel(unitTarget->getLevel())) + hitInfo.AuraSpellInfo = actualSpellInfo; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + hitInfo.AuraBasePoints[i] = hitInfo.AuraSpellInfo->Effects[i].BasePoints; + if (m_spellInfo->Effects[i].Effect != hitInfo.AuraSpellInfo->Effects[i].Effect) + { + hitInfo.AuraSpellInfo = m_spellInfo; + break; + } + } + } + + // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add + bool triggered = (m_triggeredByAuraSpell != nullptr); + hitInfo.DRGroup = m_spellInfo->GetDiminishingReturnsGroupForSpell(triggered); + + DiminishingLevels diminishLevel = DIMINISHING_LEVEL_1; + if (hitInfo.DRGroup) + { + diminishLevel = unit->GetDiminishing(hitInfo.DRGroup); + DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(triggered); + // Increase Diminishing on unit, current informations for actually casts will use values above + if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && unit->IsAffectedByDiminishingReturns())) + unit->IncrDiminishing(m_spellInfo, triggered); + } + + // Now Reduce spell duration using data received at spell hit + // check whatever effects we're going to apply, diminishing returns only apply to negative aura effects + hitInfo.Positive = true; + if (m_originalCaster == unit || !m_originalCaster->IsFriendlyTo(unit)) + { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - basePoints[i] = aurSpellInfo->Effects[i].BasePoints; - if (m_spellInfo->Effects[i].Effect != aurSpellInfo->Effects[i].Effect) + // mod duration only for effects applying aura! + if (hitInfo.EffectMask & (1 << i) && + hitInfo.AuraSpellInfo->Effects[i].IsUnitOwnedAuraEffect() && + !hitInfo.AuraSpellInfo->IsPositiveEffect(i)) { - aurSpellInfo = m_spellInfo; + hitInfo.Positive = false; break; } } } + hitInfo.AuraDuration = hitInfo.AuraSpellInfo->GetMaxDuration(); + + // unit is immune to aura if it was diminished to 0 duration + if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(hitInfo.AuraSpellInfo, triggered, hitInfo.AuraDuration, m_originalCaster, diminishLevel)) + if (std::all_of(std::begin(hitInfo.AuraSpellInfo->Effects), std::end(hitInfo.AuraSpellInfo->Effects), [](SpellEffectInfo const& effInfo) { return !effInfo.IsEffect() || effInfo.Effect == SPELL_EFFECT_APPLY_AURA; })) + return SPELL_MISS_IMMUNE; + } + + return SPELL_MISS_NONE; +} + +void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) +{ + uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit); + if (aura_effmask) + { if (m_originalCaster) { bool refresh = false; @@ -2647,12 +2721,12 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (!_spellAura) { bool const resetPeriodicTimer = !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER); - uint8 const allAuraEffectMask = Aura::BuildEffectMaskForOwner(aurSpellInfo, MAX_EFFECT_MASK, unit); - int32 const* bp = basePoints; - if (aurSpellInfo == m_spellInfo) + uint8 const allAuraEffectMask = Aura::BuildEffectMaskForOwner(hitInfo.AuraSpellInfo, MAX_EFFECT_MASK, unit); + int32 const* bp = hitInfo.AuraBasePoints; + if (hitInfo.AuraSpellInfo == m_spellInfo) bp = m_spellValue->EffectBasePoints; - AuraCreateInfo createInfo(aurSpellInfo, allAuraEffectMask, unit); + AuraCreateInfo createInfo(hitInfo.AuraSpellInfo, allAuraEffectMask, unit); createInfo .SetCaster(m_originalCaster) .SetBaseAmount(bp) @@ -2678,65 +2752,27 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA _spellAura->ModStackAmount(m_spellValue->AuraStackAmount); } - // Now Reduce spell duration using data received at spell hit - // check whatever effects we're going to apply, diminishing returns only apply to negative aura effects - bool positive = true; - if (m_originalCaster == unit || !m_originalCaster->IsFriendlyTo(unit)) - { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - // mod duration only for effects applying aura! - if ((aura_effmask & (1 << i)) && !aurSpellInfo->IsPositiveEffect(i)) - { - positive = false; - break; - } - } - } - - int32 duration = _spellAura->GetMaxDuration(); + _spellAura->SetDiminishGroup(hitInfo.DRGroup); - // unit is immune to aura if it was diminished to 0 duration - if (!positive && !unit->ApplyDiminishingToDuration(aurSpellInfo, triggered, duration, m_originalCaster, diminishLevel)) - { - _spellAura->Remove(); - _spellAura = nullptr; - - bool found = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (effectMask & (1 << i) && m_spellInfo->Effects[i].Effect != SPELL_EFFECT_APPLY_AURA) - found = true; - if (!found) - return SPELL_MISS_IMMUNE; - } - else - { - _spellAura->SetDiminishGroup(diminishGroup); + hitInfo.AuraDuration = m_originalCaster->ModSpellDuration(hitInfo.AuraSpellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); - duration = m_originalCaster->ModSpellDuration(aurSpellInfo, unit, duration, positive, effectMask); + // Haste modifies duration of channeled spells + if (m_spellInfo->IsChanneled()) + m_originalCaster->ModSpellDurationTime(hitInfo.AuraSpellInfo, hitInfo.AuraDuration, this); + // and duration of auras affected by SPELL_AURA_PERIODIC_HASTE + else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, hitInfo.AuraSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) + hitInfo.AuraDuration = int32(hitInfo.AuraDuration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED)); - // Haste modifies duration of channeled spells - if (m_spellInfo->IsChanneled()) - m_originalCaster->ModSpellDurationTime(aurSpellInfo, duration, this); - // and duration of auras affected by SPELL_AURA_PERIODIC_HASTE - else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, aurSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) - duration = int32(duration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED)); - - if (duration != _spellAura->GetMaxDuration()) - { - _spellAura->SetMaxDuration(duration); - _spellAura->SetDuration(duration); - } + if (hitInfo.AuraDuration != _spellAura->GetMaxDuration()) + { + _spellAura->SetMaxDuration(hitInfo.AuraDuration); + _spellAura->SetDuration(hitInfo.AuraDuration); } } } } - for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber) - if (effectMask & (1 << effectNumber)) - HandleEffects(unit, nullptr, nullptr, effectNumber, SPELL_EFFECT_HANDLE_HIT_TARGET); - - return SPELL_MISS_NONE; + HandleEffects(unit, nullptr, nullptr, effIndex, SPELL_EFFECT_HANDLE_HIT_TARGET); } void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) @@ -2811,52 +2847,6 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) } } -void Spell::DoAllEffectOnTarget(GOTargetInfo* target) -{ - if (target->processed) // Check target - return; - target->processed = true; // Target checked in apply effects procedure - - uint32 effectMask = target->effectMask; - if (!effectMask) - return; - - GameObject* go = m_caster->GetMap()->GetGameObject(target->targetGUID); - if (!go) - return; - - PrepareScriptHitHandlers(); - CallScriptBeforeHitHandlers(); - - for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber) - if (effectMask & (1 << effectNumber)) - HandleEffects(nullptr, nullptr, go, effectNumber, SPELL_EFFECT_HANDLE_HIT_TARGET); - - if (go->AI()) - go->AI()->SpellHit(m_caster, m_spellInfo); - - CallScriptOnHitHandlers(); - CallScriptAfterHitHandlers(); -} - -void Spell::DoAllEffectOnTarget(ItemTargetInfo* target) -{ - uint32 effectMask = target->effectMask; - if (!target->item || !effectMask) - return; - - PrepareScriptHitHandlers(); - CallScriptBeforeHitHandlers(); - - for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber) - if (effectMask & (1 << effectNumber)) - HandleEffects(nullptr, target->item, nullptr, effectNumber, SPELL_EFFECT_HANDLE_HIT_TARGET); - - CallScriptOnHitHandlers(); - - CallScriptAfterHitHandlers(); -} - bool Spell::UpdateChanneledTargetList() { // Not need check return true @@ -2867,7 +2857,7 @@ bool Spell::UpdateChanneledTargetList() uint8 channelAuraMask = 0; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) - channelAuraMask |= 1<<i; + channelAuraMask |= 1 << i; channelAuraMask &= channelTargetEffectMask; @@ -2882,24 +2872,23 @@ bool Spell::UpdateChanneledTargetList() range += std::min(MAX_SPELL_RANGE_TOLERANCE, range*0.1f); // 10% but no more than MAX_SPELL_RANGE_TOLERANCE } - for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (TargetInfo& targetInfo : m_UniqueTargetInfo) { - if (ihit->missCondition == SPELL_MISS_NONE && (channelTargetEffectMask & ihit->effectMask)) + if (targetInfo.MissCondition == SPELL_MISS_NONE && (channelTargetEffectMask & targetInfo.EffectMask)) { - Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); - + Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); if (!unit) continue; if (IsValidDeadOrAliveTarget(unit)) { - if (channelAuraMask & ihit->effectMask) + if (channelAuraMask & targetInfo.EffectMask) { if (AuraApplication * aurApp = unit->GetAuraApplication(m_spellInfo->Id, m_originalCasterGUID)) { if (m_caster != unit && !m_caster->IsWithinDistInMap(unit, range)) { - ihit->effectMask &= ~aurApp->GetEffectMask(); + targetInfo.EffectMask &= ~aurApp->GetEffectMask(); unit->RemoveAura(aurApp); continue; } @@ -2908,7 +2897,7 @@ bool Spell::UpdateChanneledTargetList() continue; } - channelTargetEffectMask &= ~ihit->effectMask; // remove from need alive mask effect that have alive target + channelTargetEffectMask &= ~targetInfo.EffectMask; // remove from need alive mask effect that have alive target } } } @@ -3110,9 +3099,9 @@ void Spell::cancel() break; case SPELL_STATE_CASTING: - for (std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if ((*ihit).missCondition == SPELL_MISS_NONE) - if (Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) + for (TargetInfo const& targetInfo : m_UniqueTargetInfo) + if (targetInfo.MissCondition == SPELL_MISS_NONE) + if (Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); SendChannelUpdate(0); @@ -3339,8 +3328,6 @@ void Spell::_cast(bool skipCheck) // CAST SPELL SendSpellCooldown(); - PrepareScriptHitHandlers(); - HandleLaunchPhase(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... @@ -3415,6 +3402,21 @@ void Spell::_cast(bool skipCheck) Unit::ProcSkillsAndAuras(m_originalCaster, nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr); } +template <class Container> +void Spell::DoProcessTargetContainer(Container& targetContainer) +{ + for (TargetInfoBase& target : targetContainer) + target.PreprocessTarget(this); + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + for (TargetInfoBase& target : targetContainer) + if (target.EffectMask & (1 << i)) + target.DoTargetSpellHit(this, i); + + for (TargetInfoBase& target : targetContainer) + target.DoDamageAndTriggers(this); +} + void Spell::handle_immediate() { // start channeling if applicable @@ -3453,13 +3455,9 @@ void Spell::handle_immediate() if (m_UniqueTargetInfo.empty()) m_hitMask = PROC_HIT_NORMAL; else - { - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); - } + DoProcessTargetContainer(m_UniqueTargetInfo); - for (std::list<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); + DoProcessTargetContainer(m_UniqueGOTargetInfo); FinishTargetProcessing(); @@ -3503,30 +3501,47 @@ uint64 Spell::handle_delayed(uint64 t_offset) bool single_missile = (m_targets.HasDst()); // now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases) - for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { - if (ihit->processed == false) + std::vector<TargetInfo> delayedTargets; + auto itr = std::remove_if(std::begin(m_UniqueTargetInfo), std::end(m_UniqueTargetInfo), [&](TargetInfo& target) -> bool { - if (single_missile || ihit->timeDelay <= t_offset) + if (single_missile || target.TimeDelay <= t_offset) { - ihit->timeDelay = t_offset; - DoAllEffectOnTarget(&(*ihit)); + target.TimeDelay = t_offset; + return true; } - else if (next_time == 0 || ihit->timeDelay < next_time) - next_time = ihit->timeDelay; - } + else if (next_time == 0 || target.TimeDelay < next_time) + next_time = target.TimeDelay; + + return false; + }); + + delayedTargets.insert(delayedTargets.end(), std::make_move_iterator(itr), std::make_move_iterator(m_UniqueTargetInfo.end())); + m_UniqueTargetInfo.erase(itr, m_UniqueTargetInfo.end()); + + DoProcessTargetContainer(delayedTargets); } // now recheck gameobject targeting correctness - for (std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end(); ++ighit) { - if (ighit->processed == false) + std::vector<GOTargetInfo> delayedGOTargets; + auto itr = std::remove_if(std::begin(m_UniqueGOTargetInfo), std::end(m_UniqueGOTargetInfo), [&](GOTargetInfo& goTarget) -> bool { - if (single_missile || ighit->timeDelay <= t_offset) - DoAllEffectOnTarget(&(*ighit)); - else if (next_time == 0 || ighit->timeDelay < next_time) - next_time = ighit->timeDelay; - } + if (single_missile || goTarget.TimeDelay <= t_offset) + { + goTarget.TimeDelay = t_offset; + return true; + } + else if (next_time == 0 || goTarget.TimeDelay < next_time) + next_time = goTarget.TimeDelay; + + return false; + }); + + delayedGOTargets.insert(delayedGOTargets.end(), std::make_move_iterator(itr), std::make_move_iterator(m_UniqueGOTargetInfo.end())); + m_UniqueGOTargetInfo.erase(itr, m_UniqueGOTargetInfo.end()); + + DoProcessTargetContainer(delayedGOTargets); } FinishTargetProcessing(); @@ -3557,8 +3572,6 @@ void Spell::_handle_immediate_phase() // handle some immediate features of the spell here HandleThreatSpells(); - PrepareScriptHitHandlers(); - // handle effects with SPELL_EFFECT_HANDLE_HIT mode for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) { @@ -3571,8 +3584,7 @@ void Spell::_handle_immediate_phase() } // process items - for (std::list<ItemTargetInfo>::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); + DoProcessTargetContainer(m_UniqueItemInfo); } void Spell::_handle_finish_phase() @@ -3676,7 +3688,7 @@ void Spell::update(uint32 difftime) // Also remove applied auras for (TargetInfo const& target : m_UniqueTargetInfo) - if (Unit* unit = m_caster->GetGUID() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID)) + if (Unit* unit = m_caster->GetGUID() == target.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.TargetGUID)) unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); } @@ -4242,11 +4254,11 @@ void Spell::WriteSpellGoTargets(WorldPacket* data) { // This function also fill data for channeled spells: // m_needAliveTargetMask req for stop channelig if one target die - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (TargetInfo& targetInfo : m_UniqueTargetInfo) { - if ((*ihit).effectMask == 0) // No effect apply - all immuned add state + if (targetInfo.EffectMask == 0) // No effect apply - all immuned add state // possibly SPELL_MISS_IMMUNE2 for this?? - ihit->missCondition = SPELL_MISS_IMMUNE2; + targetInfo.MissCondition = SPELL_MISS_IMMUNE2; } // Hit and miss target counts are both uint8, that limits us to 255 targets for each @@ -4258,33 +4270,33 @@ void Spell::WriteSpellGoTargets(WorldPacket* data) uint32 hit = 0; size_t hitPos = data->wpos(); *data << (uint8)0; // placeholder - for (std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end() && hit < 255; ++ihit) + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end() && hit < 255; ++ihit) { - if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits + if (ihit->MissCondition == SPELL_MISS_NONE) // Add only hits { - *data << uint64(ihit->targetGUID); - m_channelTargetEffectMask |=ihit->effectMask; + *data << uint64(ihit->TargetGUID); + m_channelTargetEffectMask |= ihit->EffectMask; ++hit; } } - for (std::list<GOTargetInfo>::const_iterator ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end() && hit < 255; ++ighit) + for (auto ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end() && hit < 255; ++ighit) { - *data << uint64(ighit->targetGUID); // Always hits + *data << uint64(ighit->TargetGUID); // Always hits ++hit; } uint32 miss = 0; size_t missPos = data->wpos(); *data << (uint8)0; // placeholder - for (std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end() && miss < 255; ++ihit) + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end() && miss < 255; ++ihit) { - if (ihit->missCondition != SPELL_MISS_NONE) // Add only miss + if (ihit->MissCondition != SPELL_MISS_NONE) // Add only miss { - *data << uint64(ihit->targetGUID); - *data << uint8(ihit->missCondition); - if (ihit->missCondition == SPELL_MISS_REFLECT) - *data << uint8(ihit->reflectResult); + *data << uint64(ihit->TargetGUID); + *data << uint8(ihit->MissCondition); + if (ihit->MissCondition == SPELL_MISS_REFLECT) + *data << uint8(ihit->ReflectResult); ++miss; } } @@ -4434,7 +4446,7 @@ void Spell::SendChannelStart(uint32 duration) ObjectGuid channelTarget = m_targets.GetObjectTargetGUID(); if (!channelTarget && !m_spellInfo->NeedsExplicitUnitTarget()) if (m_UniqueTargetInfo.size() + m_UniqueGOTargetInfo.size() == 1) // this is for TARGET_SELECT_CATEGORY_NEARBY - channelTarget = !m_UniqueTargetInfo.empty() ? m_UniqueTargetInfo.front().targetGUID : m_UniqueGOTargetInfo.front().targetGUID; + channelTarget = !m_UniqueTargetInfo.empty() ? m_UniqueTargetInfo.front().TargetGUID : m_UniqueGOTargetInfo.front().TargetGUID; WorldPacket data(MSG_CHANNEL_START, (8+4+4)); data << m_caster->GetPackGUID(); @@ -4562,19 +4574,13 @@ void Spell::TakePower() { if (ObjectGuid targetGUID = m_targets.GetUnitTargetGUID()) { - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + auto ihit = std::find_if(std::begin(m_UniqueTargetInfo), std::end(m_UniqueTargetInfo), [&](TargetInfo const& targetInfo) { return targetInfo.TargetGUID == targetGUID && targetInfo.MissCondition != SPELL_MISS_NONE; }); + if (ihit != std::end(m_UniqueTargetInfo)) { - if (ihit->targetGUID == targetGUID) - { - if (ihit->missCondition != SPELL_MISS_NONE) - { - hit = false; - //lower spell cost on fail (by talent aura) - if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); - } - break; - } + hit = false; + //lower spell cost on fail (by talent aura) + if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); } } } @@ -4851,13 +4857,13 @@ void Spell::HandleThreatSpells() // since 2.0.1 threat from positive effects also is distributed among all targets, so the overall caused threat is at most the defined bonus threat /= m_UniqueTargetInfo.size(); - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { float threatToAdd = threat; - if (ihit->missCondition != SPELL_MISS_NONE) + if (ihit->MissCondition != SPELL_MISS_NONE) threatToAdd = 0.0f; - Unit* target = ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + Unit* target = ObjectAccessor::GetUnit(*m_caster, ihit->TargetGUID); if (!target) continue; @@ -6102,8 +6108,8 @@ bool Spell::CanAutoCast(Unit* target) return true; SelectSpellTargets(); //check if among target units, our WANTED target is as well (->only self cast spells return false) - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if (ihit->targetGUID == targetguid) + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + if (ihit->TargetGUID == targetguid) return true; } // either the cast failed or the intended target wouldn't be hit @@ -6873,9 +6879,9 @@ void Spell::DelayedChannel() TC_LOG_DEBUG("spells", "Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer); - for (std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if ((*ihit).missCondition == SPELL_MISS_NONE) - if (Unit* unit = (m_caster->GetGUID() == ihit->targetGUID) ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) + for (TargetInfo const& targetInfo : m_UniqueTargetInfo) + if (targetInfo.MissCondition == SPELL_MISS_NONE) + if (Unit* unit = (m_caster->GetGUID() == targetInfo.TargetGUID) ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) unit->DelayOwnedAuras(m_spellInfo->Id, m_originalCasterGUID, delaytime); // partially interrupt persistent area auras @@ -7098,23 +7104,6 @@ bool Spell::IsNeedSendToClient() const m_spellInfo->Speed > 0.0f || (!m_triggeredByAuraSpell && !IsTriggered()); } -bool Spell::HaveTargetsForEffect(uint8 effect) const -{ - for (std::list<TargetInfo>::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) - if (itr->effectMask & (1 << effect)) - return true; - - for (std::list<GOTargetInfo>::const_iterator itr = m_UniqueGOTargetInfo.begin(); itr != m_UniqueGOTargetInfo.end(); ++itr) - if (itr->effectMask & (1 << effect)) - return true; - - for (std::list<ItemTargetInfo>::const_iterator itr = m_UniqueItemInfo.begin(); itr != m_UniqueItemInfo.end(); ++itr) - if (itr->effectMask & (1 << effect)) - return true; - - return false; -} - SpellEvent::SpellEvent(Spell* spell) : BasicEvent() { m_Spell = spell; @@ -7258,110 +7247,114 @@ void Spell::HandleLaunchPhase() HandleEffects(nullptr, nullptr, nullptr, i, SPELL_EFFECT_HANDLE_LAUNCH); } - float multiplier[MAX_SPELL_EFFECTS]; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (m_applyMultiplierMask & (1 << i)) - multiplier[i] = m_spellInfo->Effects[i].CalcDamageMultiplier(m_originalCaster, this); - bool usesAmmo = m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DIRECT_DAMAGE); if (m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_ABILITY_CONSUME_NO_AMMO, m_spellInfo)) usesAmmo = false; + // do not consume ammo anymore for Hunter's volley spell + if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea()) + usesAmmo = false; + PrepareTargetProcessing(); - for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - TargetInfo& target = *ihit; - - uint32 mask = target.effectMask; - if (!mask) - continue; - - // do not consume ammo anymore for Hunter's volley spell - if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea()) - usesAmmo = false; + float multiplier = 1.0f; + if (m_applyMultiplierMask & (1 << i)) + multiplier = m_spellInfo->Effects[i].CalcDamageMultiplier(m_originalCaster, this); - if (usesAmmo) + bool ammoTaken = false; + for (TargetInfo& target : m_UniqueTargetInfo) { - bool ammoTaken = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) + uint32 mask = target.EffectMask; + if (!(mask & (1 << i))) + continue; + + if (usesAmmo && !ammoTaken) { - if (!(mask & 1 << i)) - continue; - switch (m_spellInfo->Effects[i].Effect) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - case SPELL_EFFECT_SCHOOL_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - ammoTaken = true; - TakeAmmo(); + if (!(mask & 1 << i)) + continue; + + switch (m_spellInfo->Effects[i].Effect) + { + case SPELL_EFFECT_SCHOOL_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + ammoTaken = true; + TakeAmmo(); + break; + default: + break; + } + + if (ammoTaken) + break; } - if (ammoTaken) - break; } - } - DoAllEffectOnLaunchTarget(target, multiplier); + DoEffectOnLaunchTarget(target, multiplier, i); + } } FinishTargetProcessing(); } -void Spell::DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier) +void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uint8 effIndex) { Unit* unit = nullptr; // In case spell hit target, do all effect on that target - if (targetInfo.missCondition == SPELL_MISS_NONE) - unit = m_caster->GetGUID() == targetInfo.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.targetGUID); + if (targetInfo.MissCondition == SPELL_MISS_NONE) + unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); // In case spell reflect from target, do all effect on caster (if hit) - else if (targetInfo.missCondition == SPELL_MISS_REFLECT && targetInfo.reflectResult == SPELL_MISS_NONE) + else if (targetInfo.MissCondition == SPELL_MISS_REFLECT && targetInfo.ReflectResult == SPELL_MISS_NONE) unit = m_caster; if (!unit) return; // This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget) - if (targetInfo.missCondition != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) + if (targetInfo.MissCondition != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) m_caster->SetInCombatWith(unit); - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (targetInfo.effectMask & (1<<i)) - { - m_damage = 0; - m_healing = 0; - - HandleEffects(unit, nullptr, nullptr, i, SPELL_EFFECT_HANDLE_LAUNCH_TARGET); + m_damage = 0; + m_healing = 0; - if (m_damage > 0) - { - if (m_spellInfo->Effects[i].IsTargetingArea() || m_spellInfo->Effects[i].IsAreaAuraEffect() || m_spellInfo->Effects[i].IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA)) - { - m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_caster->GetGUID()); + HandleEffects(unit, nullptr, nullptr, effIndex, SPELL_EFFECT_HANDLE_LAUNCH_TARGET); - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - uint32 targetAmount = m_UniqueTargetInfo.size(); - if (targetAmount > 10) - m_damage = m_damage * 10/targetAmount; - } - } - } + if (m_damage > 0) + { + if (m_spellInfo->Effects[effIndex].IsTargetingArea() || m_spellInfo->Effects[effIndex].IsAreaAuraEffect() || m_spellInfo->Effects[effIndex].IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA)) + { + m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_caster->GetGUID()); - if (m_applyMultiplierMask & (1 << i)) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { - m_damage = int32(m_damage * m_damageMultipliers[i]); - m_damageMultipliers[i] *= multiplier[i]; + // cap damage of player AOE + uint32 targetAmount = m_UniqueTargetInfo.size(); + if (targetAmount > 10) + m_damage = m_damage * 10 / targetAmount; } - targetInfo.damage += m_damage; } } + if (m_applyMultiplierMask & (1 << effIndex)) + { + m_damage = int32(m_damage * m_damageMultipliers[effIndex]); + m_healing = int32(m_healing * m_damageMultipliers[effIndex]); + + m_damageMultipliers[effIndex] *= multiplier; + } + + targetInfo.Damage += m_damage; + targetInfo.Healing += m_healing; + float critChance = m_spellValue->CriticalChance; if (!critChance) critChance = m_caster->SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType); - targetInfo.crit = roll_chance_f(unit->SpellCritChanceTaken(m_caster, m_spellInfo, m_spellSchoolMask, critChance, m_attackType)); + targetInfo.IsCrit = roll_chance_f(unit->SpellCritChanceTaken(m_caster, m_spellInfo, m_spellSchoolMask, critChance, m_attackType)); } SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) @@ -7555,18 +7548,14 @@ SpellCastResult Spell::CallScriptCheckCastHandlers() return retVal; } -void Spell::PrepareScriptHitHandlers() -{ - for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) - (*scritr)->_InitHit(); -} - bool Spell::CallScriptEffectHandlers(SpellEffIndex effIndex, SpellEffectHandleMode mode) { // execute script effect handler hooks and check if effects was prevented bool preventDefault = false; for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { + (*scritr)->_InitHit(); + HookList<SpellScript::EffectHandler>::iterator effItr, effEndItr; SpellScriptHookType hookType; switch (mode) @@ -7626,6 +7615,7 @@ void Spell::CallScriptBeforeHitHandlers() { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { + (*scritr)->_InitHit(); (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_BEFORE_HIT); auto hookItrEnd = (*scritr)->BeforeHit.end(), hookItr = (*scritr)->BeforeHit.begin(); for (; hookItr != hookItrEnd; ++hookItr) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index ecf0e289d88..9ea93357e16 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -445,7 +445,6 @@ class TC_GAME_API Spell int32 CalculateDamage(uint8 i, Unit const* target) const; - bool HaveTargetsForEffect(uint8 effect) const; void Delayed(); void DelayedChannel(); uint32 getState() const { return m_spellState; } @@ -599,7 +598,7 @@ class TC_GAME_API Spell bool m_executedCurrently; // mark as executed to prevent deleted and access by dead pointers bool m_needComboPoints; uint8 m_applyMultiplierMask; - float m_damageMultipliers[3]; + float m_damageMultipliers[MAX_SPELL_EFFECTS]; // Current targets, to be used in SpellEffects (MUST BE USED ONLY IN SPELL EFFECTS) Unit* unitTarget; @@ -632,39 +631,70 @@ class TC_GAME_API Spell // Spell target subsystem // ***************************************** // Targets store structures and data - struct TargetInfo + struct TargetInfoBase { - ObjectGuid targetGUID; - uint64 timeDelay; - int32 damage; - - SpellMissInfo missCondition; - SpellMissInfo reflectResult; - - uint8 effectMask; - bool processed; - bool alive; - bool crit; - bool scaleAura; + virtual void PreprocessTarget(Spell* /*spell*/) { } + virtual void DoTargetSpellHit(Spell* spell, uint8 effIndex) = 0; + virtual void DoDamageAndTriggers(Spell* /*spell*/) { } + + uint8 EffectMask = 0; + + protected: + TargetInfoBase() { } + virtual ~TargetInfoBase() { } + }; + + struct TargetInfo : public TargetInfoBase + { + void PreprocessTarget(Spell* spell) override; + void DoTargetSpellHit(Spell* spell, uint8 effIndex) override; + void DoDamageAndTriggers(Spell* spell) override; + + ObjectGuid TargetGUID; + uint64 TimeDelay = 0ULL; + int32 Damage = 0; + int32 Healing = 0; + + SpellMissInfo MissCondition = SPELL_MISS_NONE; + SpellMissInfo ReflectResult = SPELL_MISS_NONE; + + bool IsAlive = false; + bool IsCrit = false; + bool ScaleAura = false; + + // info set at PreprocessTarget, used by DoTargetSpellHit + DiminishingGroup DRGroup = DIMINISHING_NONE; + int32 AuraDuration = 0; + SpellInfo const* AuraSpellInfo = nullptr; + int32 AuraBasePoints[MAX_SPELL_EFFECTS] = { }; + bool Positive = true; + + private: + Unit* _spellHitTarget = nullptr; // changed for example by reflect + bool _enablePVP = false; // need to enable PVP at DoDamageAndTriggers? }; - std::list<TargetInfo> m_UniqueTargetInfo; + std::vector<TargetInfo> m_UniqueTargetInfo; uint8 m_channelTargetEffectMask; // Mask req. alive targets - struct GOTargetInfo + struct GOTargetInfo : public TargetInfoBase { - ObjectGuid targetGUID; - uint64 timeDelay; - uint8 effectMask; - bool processed; + void DoTargetSpellHit(Spell* spell, uint8 effIndex) override; + + ObjectGuid TargetGUID; + uint64 TimeDelay = 0ULL; }; - std::list<GOTargetInfo> m_UniqueGOTargetInfo; + std::vector<GOTargetInfo> m_UniqueGOTargetInfo; - struct ItemTargetInfo + struct ItemTargetInfo : public TargetInfoBase { - Item *item; - uint8 effectMask; + void DoTargetSpellHit(Spell* spell, uint8 effIndex) override; + + Item* TargetItem = nullptr; }; - std::list<ItemTargetInfo> m_UniqueItemInfo; + std::vector<ItemTargetInfo> m_UniqueItemInfo; + + template <class Container> + void DoProcessTargetContainer(Container& targetContainer); SpellDestination m_destTargets[MAX_SPELL_EFFECTS]; @@ -673,15 +703,14 @@ class TC_GAME_API Spell void AddItemTarget(Item* item, uint32 effectMask); void AddDestTarget(SpellDestination const& dest, uint32 effIndex); - void DoAllEffectOnTarget(TargetInfo* target); - SpellMissInfo DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleAura); + SpellMissInfo PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& targetInfo); + void DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& targetInfo); + void DoTriggersOnSpellHit(Unit* unit, uint8 effMask); - void DoAllEffectOnTarget(GOTargetInfo* target); - void DoAllEffectOnTarget(ItemTargetInfo* target); bool UpdateChanneledTargetList(); bool IsValidDeadOrAliveTarget(Unit const* target) const; void HandleLaunchPhase(); - void DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier); + void DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uint8 effIndex); void PrepareTargetProcessing(); void FinishTargetProcessing(); @@ -696,7 +725,6 @@ class TC_GAME_API Spell void CallScriptOnCastHandlers(); void CallScriptAfterCastHandlers(); SpellCastResult CallScriptCheckCastHandlers(); - void PrepareScriptHitHandlers(); bool CallScriptEffectHandlers(SpellEffIndex effIndex, SpellEffectHandleMode mode); void CallScriptSuccessfulDispel(SpellEffIndex effIndex); void CallScriptBeforeHitHandlers(); @@ -745,7 +773,7 @@ class TC_GAME_API Spell uint8 m_auraScaleMask; std::unique_ptr<PathGenerator> m_preGeneratedPath; - ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS]; + ByteBuffer* m_effectExecuteData[MAX_SPELL_EFFECTS]; Spell(Spell const& right) = delete; Spell& operator=(Spell const& right) = delete; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 2695c741874..7c64440eea9 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -327,8 +327,8 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_SHARE_DAMAGE)) { uint32 count = 0; - for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if (ihit->effectMask & (1<<effIndex)) + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + if (ihit->EffectMask & (1 << effIndex)) ++count; damage /= count; // divide to all targets @@ -704,8 +704,8 @@ void Spell::EffectDummy(SpellEffIndex effIndex) case 31789: // Righteous Defense (step 1) { // Clear targets for eff 1 - for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - ihit->effectMask &= ~(1<<1); + for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + ihit->EffectMask &= ~(1 << 1); // not empty (checked), copy Unit::AttackerSet attackers = unitTarget->getAttackers(); @@ -1296,7 +1296,7 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) // add log data before multiplication (need power amount, not damage) ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, newDamage, 0.0f); - newDamage = int32(newDamage* dmgMultiplier); + newDamage = int32(newDamage * dmgMultiplier); m_damage += newDamage; } @@ -1321,10 +1321,10 @@ void Spell::EffectHeal(SpellEffIndex effIndex) if (m_spellInfo->Id == 45064) { // Amount of heal - depends from stacked Holy Energy - int damageAmount = 0; + int32 damageAmount = 0; if (AuraEffect const* aurEff = m_caster->GetAuraEffect(45062, 0)) { - damageAmount+= aurEff->GetAmount(); + damageAmount += aurEff->GetAmount(); m_caster->RemoveAurasDueToSpell(45062); } @@ -1413,7 +1413,7 @@ void Spell::EffectHeal(SpellEffIndex effIndex) if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) unitTarget->RemoveAura(48920); - m_damage -= addhealth; + m_healing += addhealth; } } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index d581e1cc52e..cf8ef564413 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -390,19 +390,6 @@ bool SpellEffectInfo::IsAreaAuraEffect() const return false; } -bool SpellEffectInfo::IsFarUnitTargetEffect() const -{ - return (Effect == SPELL_EFFECT_SUMMON_PLAYER) - || (Effect == SPELL_EFFECT_SUMMON_RAF_FRIEND) - || (Effect == SPELL_EFFECT_RESURRECT) - || (Effect == SPELL_EFFECT_RESURRECT_NEW); -} - -bool SpellEffectInfo::IsFarDestTargetEffect() const -{ - return Effect == SPELL_EFFECT_TELEPORT_UNITS; -} - bool SpellEffectInfo::IsUnitOwnedAuraEffect() const { return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA; @@ -3071,14 +3058,14 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const int32 SpellInfo::GetDuration() const { if (!DurationEntry) - return 0; + return IsPassive() ? -1 : 0; return (DurationEntry->Duration[0] == -1) ? -1 : abs(DurationEntry->Duration[0]); } int32 SpellInfo::GetMaxDuration() const { if (!DurationEntry) - return 0; + return IsPassive() ? -1 : 0; return (DurationEntry->Duration[2] == -1) ? -1 : abs(DurationEntry->Duration[2]); } diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index cd86f8b9c9e..92b31f6af02 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -272,8 +272,6 @@ public: bool IsAura(AuraType aura) const; bool IsTargetingArea() const; bool IsAreaAuraEffect() const; - bool IsFarUnitTargetEffect() const; - bool IsFarDestTargetEffect() const; bool IsUnitOwnedAuraEffect() const; int32 CalcValue(Unit const* caster = nullptr, int32 const* basePoints = nullptr, Unit const* target = nullptr) const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index fd57fd5f8ba..e85989c6183 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3985,17 +3985,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->MaxAffectedTargets = 1; }); - // Boom (XT-002) - ApplySpellFix({ 62834 }, [](SpellInfo* spellInfo) - { - // This hack is here because we suspect our implementation of spell effect execution on targets - // is done in the wrong order. We suspect that EFFECT_0 needs to be applied on all targets, - // then EFFECT_1, etc - instead of applying each effect on target1, then target2, etc. - // The above situation causes the visual for this spell to be bugged, so we remove the instakill - // effect and implement a script hack for that. - spellInfo->Effects[EFFECT_1].Effect = 0; - }); - ApplySpellFix({ 64386, // Terrifying Screech (Auriaya) 64389, // Sentinel Blast (Auriaya) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp index a4b5de9dd5b..8d9f69b9096 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp @@ -629,29 +629,6 @@ class npc_pummeller : public CreatureScript * XE-321 BOOMBOT * *///---------------------------------------------------- -class BoomEvent : public BasicEvent -{ - public: - BoomEvent(Creature* me) : _me(me) - { - } - - bool Execute(uint64 /*time*/, uint32 /*diff*/) override - { - // This hack is here because we suspect our implementation of spell effect execution on targets - // is done in the wrong order. We suspect that EFFECT_0 needs to be applied on all targets, - // then EFFECT_1, etc - instead of applying each effect on target1, then target2, etc. - // The above situation causes the visual for this spell to be bugged, so we remove the instakill - // effect and implement a script hack for that. - - _me->CastSpell(_me, SPELL_BOOM, false); - return true; - } - - private: - Creature* _me; -}; - class npc_boombot : public CreatureScript { public: @@ -703,12 +680,7 @@ class npc_boombot : public CreatureScript damage = 0; - // Visual only seems to work if the instant kill event is delayed or the spell itself is delayed - // Casting done from player and caster source has the same targetinfo flags, - // so that can't be the issue - // See BoomEvent class - // Schedule 1s delayed - me->m_Events.AddEvent(new BoomEvent(me), me->m_Events.CalculateTime(1*IN_MILLISECONDS)); + DoCastAOE(SPELL_BOOM); } } |