aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Player/Player.cpp2
-rw-r--r--src/server/game/Maps/Map.cpp14
-rw-r--r--src/server/game/Maps/Map.h6
-rw-r--r--src/server/game/Spells/Spell.cpp1032
-rw-r--r--src/server/game/Spells/Spell.h94
-rw-r--r--src/server/game/Spells/SpellEffects.cpp16
-rw-r--r--src/server/game/Spells/SpellInfo.cpp17
-rw-r--r--src/server/game/Spells/SpellInfo.h2
-rw-r--r--src/server/game/Spells/SpellMgr.cpp11
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp30
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);
}
}