diff options
| -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);                  }              } | 
