aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells
diff options
context:
space:
mode:
authorSpp <spp@jorge.gr>2012-02-23 13:06:35 +0100
committerSpp <spp@jorge.gr>2012-02-23 13:06:35 +0100
commit5cff9e071640bc47688b71bb264edd8267ba77c3 (patch)
treec599d12dfb7870d7c34a07490a930b3102c3d8ac /src/server/game/Spells
parentf0ca875a216eaab3d213feae8ba75c07795c9304 (diff)
parent9219625243bc9f63e5a152e6cda1043cfaade201 (diff)
Merge branch 'master' into 4.x
Conflicts: sql/base/auth_database.sql src/server/authserver/Server/AuthSocket.cpp src/server/game/AI/CoreAI/CombatAI.cpp src/server/game/AuctionHouse/AuctionHouseMgr.cpp src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp src/server/game/DataStores/DBCStructure.h src/server/game/DataStores/DBCfmt.h src/server/game/Entities/Unit/Unit.cpp src/server/game/Entities/Vehicle/Vehicle.cpp src/server/game/Globals/ObjectMgr.cpp src/server/game/Globals/ObjectMgr.h src/server/game/Handlers/AuctionHouseHandler.cpp src/server/game/Miscellaneous/SharedDefines.h src/server/game/Movement/MotionMaster.cpp src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp src/server/game/Quests/QuestDef.cpp src/server/game/Server/Protocol/Opcodes.cpp src/server/game/Server/WorldSocket.cpp src/server/game/Spells/Spell.cpp src/server/game/Spells/SpellEffects.cpp src/server/game/Spells/SpellInfo.cpp src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp src/server/scripts/Spells/spell_quest.cpp src/server/shared/Logging/Log.h src/server/worldserver/worldserver.conf.dist src/tools/vmap3_extractor/model.h src/tools/vmap4_extractor/CMakeLists.txt src/tools/vmap4_extractor/dbcfile.cpp src/tools/vmap4_extractor/dbcfile.h src/tools/vmap4_extractor/loadlib/loadlib.h
Diffstat (limited to 'src/server/game/Spells')
-rwxr-xr-xsrc/server/game/Spells/Auras/SpellAuraEffects.cpp8
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.h7
-rwxr-xr-xsrc/server/game/Spells/Auras/SpellAuras.cpp76
-rwxr-xr-xsrc/server/game/Spells/Auras/SpellAuras.h45
-rwxr-xr-xsrc/server/game/Spells/Spell.cpp2649
-rwxr-xr-xsrc/server/game/Spells/Spell.h193
-rwxr-xr-xsrc/server/game/Spells/SpellEffects.cpp813
-rw-r--r--src/server/game/Spells/SpellInfo.cpp430
-rw-r--r--src/server/game/Spells/SpellInfo.h36
-rwxr-xr-xsrc/server/game/Spells/SpellMgr.cpp15
-rwxr-xr-xsrc/server/game/Spells/SpellMgr.h12
-rwxr-xr-xsrc/server/game/Spells/SpellScript.cpp23
-rwxr-xr-xsrc/server/game/Spells/SpellScript.h27
13 files changed, 1929 insertions, 2405 deletions
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index b3b4cf4a30d..273fcb4f049 100755
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -419,10 +419,10 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
};
AuraEffect::AuraEffect(Aura* base, uint8 effIndex, int32 *baseAmount, Unit* caster):
-m_base(base), m_spellInfo(base->GetSpellInfo()), m_effIndex(effIndex),
-m_baseAmount(baseAmount ? *baseAmount : m_spellInfo->Effects[m_effIndex].BasePoints),
-m_canBeRecalculated(true), m_spellmod(NULL), m_isPeriodic(false),
-m_periodicTimer(0), m_tickNumber(0)
+m_base(base), m_spellInfo(base->GetSpellInfo()),
+m_baseAmount(baseAmount ? *baseAmount : m_spellInfo->Effects[effIndex].BasePoints),
+m_spellmod(NULL), m_periodicTimer(0), m_tickNumber(0), m_effIndex(effIndex),
+m_canBeRecalculated(true), m_isPeriodic(false)
{
CalculatePeriodic(caster, true, false);
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h
index 930dfba4289..28a5beafa2c 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.h
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.h
@@ -95,18 +95,19 @@ class AuraEffect
Aura* const m_base;
SpellInfo const* const m_spellInfo;
- uint8 const m_effIndex;
int32 const m_baseAmount;
int32 m_amount;
- bool m_canBeRecalculated;
SpellModifier* m_spellmod;
- bool m_isPeriodic;
int32 m_periodicTimer;
int32 m_amplitude;
uint32 m_tickNumber;
+
+ uint8 const m_effIndex;
+ bool m_canBeRecalculated;
+ bool m_isPeriodic;
private:
bool IsPeriodicTickCrit(Unit* target, Unit const* caster) const;
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index d20411eb8a6..6015a944bdb 100755
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -37,8 +37,8 @@
#include "Vehicle.h"
AuraApplication::AuraApplication(Unit* target, Unit* caster, Aura* aura, uint8 effMask):
-m_target(target), m_base(aura), m_slot(MAX_AURAS), m_flags(AFLAG_NONE),
-m_effectsToApply(effMask), m_removeMode(AURA_REMOVE_NONE), m_needClientUpdate(false)
+_target(target), _base(aura), _removeMode(AURA_REMOVE_NONE), _slot(MAX_AURAS),
+_flags(AFLAG_NONE), _effectsToApply(effMask), _needClientUpdate(false)
{
ASSERT(GetTarget() && GetBase());
@@ -70,7 +70,7 @@ m_effectsToApply(effMask), m_removeMode(AURA_REMOVE_NONE), m_needClientUpdate(fa
// Register Visible Aura
if (slot < MAX_AURAS)
{
- m_slot = slot;
+ _slot = slot;
GetTarget()->SetVisibleAura(slot, this);
SetNeedClientUpdate();
sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Aura: %u Effect: %d put to unit visible auras slot: %u", GetBase()->GetId(), GetEffectMask(), slot);
@@ -89,7 +89,7 @@ void AuraApplication::_Remove()
if (slot >= MAX_AURAS)
return;
- if (AuraApplication * foundAura = m_target->GetAuraApplication(GetBase()->GetId(), GetBase()->GetCasterGUID(), GetBase()->GetCastItemGUID()))
+ if (AuraApplication * foundAura = _target->GetAuraApplication(GetBase()->GetId(), GetBase()->GetCasterGUID(), GetBase()->GetCastItemGUID()))
{
// Reuse visible aura slot by aura which is still applied - prevent storing dead pointers
if (slot == foundAura->GetSlot())
@@ -115,7 +115,7 @@ void AuraApplication::_Remove()
void AuraApplication::_InitFlags(Unit* caster, uint8 effMask)
{
// mark as selfcasted if needed
- m_flags |= (GetBase()->GetCasterGUID() == GetTarget()->GetGUID()) ? AFLAG_CASTER : AFLAG_NONE;
+ _flags |= (GetBase()->GetCasterGUID() == GetTarget()->GetGUID()) ? AFLAG_CASTER : AFLAG_NONE;
// aura is casted by self or an enemy
// one negative effect and we know aura is negative
@@ -130,7 +130,7 @@ void AuraApplication::_InitFlags(Unit* caster, uint8 effMask)
break;
}
}
- m_flags |= negativeFound ? AFLAG_NEGATIVE : AFLAG_POSITIVE;
+ _flags |= negativeFound ? AFLAG_NEGATIVE : AFLAG_POSITIVE;
}
// aura is casted by friend
// one positive effect and we know aura is positive
@@ -145,7 +145,7 @@ void AuraApplication::_InitFlags(Unit* caster, uint8 effMask)
break;
}
}
- m_flags |= positiveFound ? AFLAG_POSITIVE : AFLAG_NEGATIVE;
+ _flags |= positiveFound ? AFLAG_POSITIVE : AFLAG_NEGATIVE;
}
// there are more auras that require this flag, this is just the beginning
@@ -164,19 +164,19 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply)
AuraEffect* aurEff = GetBase()->GetEffect(effIndex);
ASSERT(aurEff);
ASSERT(HasEffect(effIndex) == (!apply));
- ASSERT((1<<effIndex) & m_effectsToApply);
+ ASSERT((1<<effIndex) & _effectsToApply);
sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "AuraApplication::_HandleEffect: %u, apply: %u: amount: %u", aurEff->GetAuraType(), apply, aurEff->GetAmount());
if (apply)
{
- ASSERT(!(m_flags & (1<<effIndex)));
- m_flags |= 1<<effIndex;
+ ASSERT(!(_flags & (1<<effIndex)));
+ _flags |= 1<<effIndex;
aurEff->HandleEffect(this, AURA_EFFECT_HANDLE_REAL, true);
}
else
{
- ASSERT(m_flags & (1<<effIndex));
- m_flags &= ~(1<<effIndex);
+ ASSERT(_flags & (1<<effIndex));
+ _flags &= ~(1<<effIndex);
aurEff->HandleEffect(this, AURA_EFFECT_HANDLE_REAL, false);
// Remove all triggered by aura spells vs unlimited duration
@@ -187,19 +187,19 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply)
void AuraApplication::BuildUpdatePacket(ByteBuffer& data, bool remove) const
{
- data << uint8(m_slot);
+ data << uint8(_slot);
if (remove)
{
- ASSERT(!m_target->GetVisibleAura(m_slot));
+ ASSERT(!_target->GetVisibleAura(_slot));
data << uint32(0);
return;
}
- ASSERT(m_target->GetVisibleAura(m_slot));
+ ASSERT(_target->GetVisibleAura(_slot));
Aura const* aura = GetBase();
data << uint32(aura->GetId());
- uint32 flags = m_flags;
+ uint32 flags = _flags;
if (aura->GetMaxDuration() > 0 && !(aura->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_HIDE_DURATION))
flags |= AFLAG_DURATION;
data << uint16(flags);
@@ -225,13 +225,13 @@ void AuraApplication::BuildUpdatePacket(ByteBuffer& data, bool remove) const
void AuraApplication::ClientUpdate(bool remove)
{
- m_needClientUpdate = false;
+ _needClientUpdate = false;
WorldPacket data(SMSG_AURA_UPDATE);
data.append(GetTarget()->GetPackGUID());
BuildUpdatePacket(data, remove);
- m_target->SendMessageToSet(&data, true);
+ _target->SendMessageToSet(&data, true);
}
uint8 Aura::BuildEffectMaskForOwner(SpellInfo const* spellProto, uint8 avalibleEffectMask, WorldObject* owner)
@@ -535,7 +535,13 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
bool addUnit = true;
// check target immunities
- if (itr->first->IsImmunedToSpell(GetSpellInfo())
+ for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
+ {
+ if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex))
+ itr->second &= ~(1 << effIndex);
+ }
+ if (!itr->second
+ || itr->first->IsImmunedToSpell(GetSpellInfo())
|| !CanBeAppliedOn(itr->first))
addUnit = false;
@@ -1849,6 +1855,10 @@ bool Aura::CanStackWith(Aura const* existingAura) const
if (!sameCaster)
{
+ // Channeled auras can stack if not forbidden by db or aura type
+ if (existingAura->GetSpellInfo()->IsChanneled())
+ return true;
+
if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_STACK_FOR_DIFF_CASTERS)
return true;
@@ -1870,7 +1880,7 @@ bool Aura::CanStackWith(Aura const* existingAura) const
case SPELL_AURA_OBS_MOD_HEALTH:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
// periodic auras which target areas are not allowed to stack this way (replenishment for example)
- if (m_spellInfo->Effects[i].IsArea() || existingSpellInfo->Effects[i].IsArea())
+ if (m_spellInfo->Effects[i].IsTargetingArea() || existingSpellInfo->Effects[i].IsTargetingArea())
break;
return true;
default:
@@ -2050,7 +2060,7 @@ void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo)
if (aurApp->HasEffect(i))
// TODO: OnEffectProc hook here (allowing prevention of selected effects)
GetEffect(i)->HandleProc(aurApp, eventInfo);
- // TODO: AfterEffectProc hook here
+ // TODO: AfterEffectProc hook here
// TODO: AfterProc hook here
@@ -2100,6 +2110,30 @@ bool Aura::CallScriptCheckAreaTargetHandlers(Unit* target)
return true;
}
+void Aura::CallScriptDispel(DispelInfo* dispelInfo)
+{
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end() ; ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_DISPEL);
+ std::list<AuraScript::AuraDispelHandler>::iterator hookItrEnd = (*scritr)->OnDispel.end(), hookItr = (*scritr)->OnDispel.begin();
+ for (; hookItr != hookItrEnd ; ++hookItr)
+ (*hookItr).Call(*scritr, dispelInfo);
+ (*scritr)->_FinishScriptCall();
+ }
+}
+
+void Aura::CallScriptAfterDispel(DispelInfo* dispelInfo)
+{
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end() ; ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_DISPEL);
+ std::list<AuraScript::AuraDispelHandler>::iterator hookItrEnd = (*scritr)->AfterDispel.end(), hookItr = (*scritr)->AfterDispel.begin();
+ for (; hookItr != hookItrEnd ; ++hookItr)
+ (*hookItr).Call(*scritr, dispelInfo);
+ (*scritr)->_FinishScriptCall();
+ }
+}
+
bool Aura::CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode)
{
bool preventDefault = false;
diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h
index de743eb2991..2f50d47a79b 100755
--- a/src/server/game/Spells/Auras/SpellAuras.h
+++ b/src/server/game/Spells/Auras/SpellAuras.h
@@ -21,6 +21,7 @@
#include "SpellAuraDefines.h"
#include "SpellInfo.h"
+#include "Unit.h"
class Unit;
class SpellInfo;
@@ -46,13 +47,13 @@ class AuraApplication
friend void Unit::RemoveAura(AuraApplication * aurApp, AuraRemoveMode mode);
friend AuraApplication * Unit::_CreateAuraApplication(Aura* aura, uint8 effMask);
private:
- Unit* const m_target;
- Aura* const m_base;
- uint8 m_slot; // Aura slot on unit
- uint8 m_flags; // Aura info flag
- uint8 m_effectsToApply; // Used only at spell hit to determine which effect should be applied
- AuraRemoveMode m_removeMode:8; // Store info for know remove aura reason
- bool m_needClientUpdate:1;
+ Unit* const _target;
+ Aura* const _base;
+ AuraRemoveMode _removeMode:8; // Store info for know remove aura reason
+ uint8 _slot; // Aura slot on unit
+ uint8 _flags; // Aura info flag
+ uint8 _effectsToApply; // Used only at spell hit to determine which effect should be applied
+ bool _needClientUpdate:1;
explicit AuraApplication(Unit* target, Unit* caster, Aura* base, uint8 effMask);
void _Remove();
@@ -61,22 +62,22 @@ class AuraApplication
void _HandleEffect(uint8 effIndex, bool apply);
public:
- Unit* GetTarget() const { return m_target; }
- Aura* GetBase() const { return m_base; }
+ Unit* GetTarget() const { return _target; }
+ Aura* GetBase() const { return _base; }
- uint8 GetSlot() const { return m_slot; }
- uint8 GetFlags() const { return m_flags; }
- uint8 GetEffectMask() const { return m_flags & (AFLAG_EFF_INDEX_0 | AFLAG_EFF_INDEX_1 | AFLAG_EFF_INDEX_2); }
- bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return m_flags & (1<<effect); }
- bool IsPositive() const { return m_flags & AFLAG_POSITIVE; }
- bool IsSelfcasted() const { return m_flags & AFLAG_CASTER; }
- uint8 GetEffectsToApply() const { return m_effectsToApply; }
+ uint8 GetSlot() const { return _slot; }
+ uint8 GetFlags() const { return _flags; }
+ uint8 GetEffectMask() const { return _flags & (AFLAG_EFF_INDEX_0 | AFLAG_EFF_INDEX_1 | AFLAG_EFF_INDEX_2); }
+ bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return _flags & (1<<effect); }
+ bool IsPositive() const { return _flags & AFLAG_POSITIVE; }
+ bool IsSelfcasted() const { return _flags & AFLAG_CASTER; }
+ uint8 GetEffectsToApply() const { return _effectsToApply; }
- void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; }
- AuraRemoveMode GetRemoveMode() const {return m_removeMode;}
+ void SetRemoveMode(AuraRemoveMode mode) { _removeMode = mode; }
+ AuraRemoveMode GetRemoveMode() const {return _removeMode;}
- void SetNeedClientUpdate() { m_needClientUpdate = true;}
- bool IsNeedClientUpdate() const { return m_needClientUpdate;}
+ void SetNeedClientUpdate() { _needClientUpdate = true;}
+ bool IsNeedClientUpdate() const { return _needClientUpdate;}
void BuildUpdatePacket(ByteBuffer& data, bool remove) const;
void ClientUpdate(bool remove = false);
};
@@ -186,7 +187,7 @@ class Aura
bool CanStackWith(Aura const* existingAura) const;
// Proc system
- // this subsystem is not yet in use - the core of it is functional, but still some research has to be done
+ // this subsystem is not yet in use - the core of it is functional, but still some research has to be done
// and some dependant problems fixed before it can replace old proc system (for example cooldown handling)
// currently proc system functionality is implemented in Unit::ProcDamageAndSpell
bool IsProcOnCooldown() const;
@@ -201,6 +202,8 @@ class Aura
// AuraScript
void LoadScripts();
bool CallScriptCheckAreaTargetHandlers(Unit* target);
+ void CallScriptDispel(DispelInfo* dispelInfo);
+ void CallScriptAfterDispel(DispelInfo* dispelInfo);
bool CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode);
bool CallScriptEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode);
void CallScriptAfterEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index c56969b2094..88654dcc4b1 100755
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -593,33 +593,6 @@ Spell::~Spell()
CheckEffectExecuteData();
}
-template<typename T>
-WorldObject* Spell::FindCorpseUsing()
-{
- // non-standard target selection
- float max_range = m_spellInfo->GetMaxRange(false);
-
- CellCoord p(Trinity::ComputeCellCoord(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.SetNoCreate();
-
- WorldObject* result = NULL;
-
- T u_check(m_caster, max_range);
- Trinity::WorldObjectSearcher<T> searcher(m_caster, result, u_check);
-
- TypeContainerVisitor<Trinity::WorldObjectSearcher<T>, GridTypeMapContainer > grid_searcher(searcher);
- cell.Visit(p, grid_searcher, *m_caster->GetMap(), *m_caster, max_range);
-
- if (!result)
- {
- TypeContainerVisitor<Trinity::WorldObjectSearcher<T>, WorldTypeMapContainer > world_searcher(searcher);
- cell.Visit(p, world_searcher, *m_caster->GetMap(), *m_caster, max_range);
- }
-
- return result;
-}
-
void Spell::InitExplicitTargets(SpellCastTargets const& targets)
{
m_targets = targets;
@@ -689,9 +662,41 @@ void Spell::InitExplicitTargets(SpellCastTargets const& targets)
m_targets.RemoveSrc();
}
+void Spell::SelectExplicitTargets()
+{
+ // here go all explicit target changes made to explicit targets after spell prepare phase is finished
+ if (Unit* target = m_targets.GetUnitTarget())
+ {
+ // check for explicit target redirection, for Grounding Totem for example
+ if (m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_ENEMY
+ || (m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT && !m_spellInfo->IsPositive()))
+ {
+ Unit* redirect;
+ switch (m_spellInfo->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ redirect = m_caster->GetMagicHitRedirectTarget(target, m_spellInfo);
+ break;
+ case SPELL_DAMAGE_CLASS_MELEE:
+ case SPELL_DAMAGE_CLASS_RANGED:
+ redirect = m_caster->GetMeleeHitRedirectTarget(target, m_spellInfo);
+ break;
+ default:
+ redirect = NULL;
+ break;
+ }
+ if (redirect && (redirect != target))
+ m_targets.SetUnitTarget(redirect);
+ }
+ }
+}
+
void Spell::SelectSpellTargets()
{
- uint32 processedTargets = 0;
+ // select targets for cast phase
+ SelectExplicitTargets();
+
+ uint32 processedAreaEffectsMask = 0;
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
// not call for empty effect.
@@ -699,9 +704,6 @@ void Spell::SelectSpellTargets()
if (!m_spellInfo->Effects[i].IsEffect())
continue;
- if (processedTargets & (1 << i))
- continue;
-
// set expected type of implicit targets to be sent to client
uint32 implicitTargetMask = GetTargetFlagMask(m_spellInfo->Effects[i].TargetA.GetObjectType()) | GetTargetFlagMask(m_spellInfo->Effects[i].TargetB.GetObjectType());
if (implicitTargetMask & TARGET_FLAG_UNIT)
@@ -709,13 +711,8 @@ void Spell::SelectSpellTargets()
if (implicitTargetMask & (TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_GAMEOBJECT_ITEM))
m_targets.SetTargetFlag(TARGET_FLAG_GAMEOBJECT);
- uint32 targetA = m_spellInfo->Effects[i].TargetA.GetTarget();
- uint32 targetB = m_spellInfo->Effects[i].TargetB.GetTarget();
-
- if (targetA)
- processedTargets |= SelectEffectTargets(i, m_spellInfo->Effects[i].TargetA);
- if (targetB)
- processedTargets |= SelectEffectTargets(i, m_spellInfo->Effects[i].TargetB);
+ SelectEffectImplicitTargets(SpellEffIndex(i), m_spellInfo->Effects[i].TargetA, processedAreaEffectsMask);
+ SelectEffectImplicitTargets(SpellEffIndex(i), m_spellInfo->Effects[i].TargetB, processedAreaEffectsMask);
// Select targets of effect based on effect type
// those are used when no valid target could be added for spell effect based on spell target type
@@ -777,6 +774,938 @@ void Spell::SelectSpellTargets()
}
}
+void Spell::SelectEffectImplicitTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32& processedEffectMask)
+{
+ if (!targetType.GetTarget())
+ return;
+
+ uint32 effectMask = 1 << effIndex;
+ // set the same target list for all effects
+ // some spells appear to need this, however this requires more research
+ switch (targetType.GetSelectionCategory())
+ {
+ case TARGET_SELECT_CATEGORY_NEARBY:
+ case TARGET_SELECT_CATEGORY_CONE:
+ case TARGET_SELECT_CATEGORY_AREA:
+ // targets for effect already selected
+ if (effectMask & processedEffectMask)
+ return;
+ // choose which targets we can select at once
+ for (uint32 j = effIndex + 1; j < MAX_SPELL_EFFECTS; ++j)
+ if (GetSpellInfo()->Effects[effIndex].TargetA.GetTarget() == GetSpellInfo()->Effects[j].TargetA.GetTarget() &&
+ GetSpellInfo()->Effects[effIndex].TargetB.GetTarget() == GetSpellInfo()->Effects[j].TargetB.GetTarget() &&
+ GetSpellInfo()->Effects[effIndex].ImplicitTargetConditions == GetSpellInfo()->Effects[j].ImplicitTargetConditions &&
+ GetSpellInfo()->Effects[effIndex].CalcRadius(m_caster) == GetSpellInfo()->Effects[j].CalcRadius(m_caster))
+ effectMask |= 1 << j;
+ processedEffectMask |= effectMask;
+ break;
+ default:
+ break;
+ }
+
+ switch(targetType.GetSelectionCategory())
+ {
+ case TARGET_SELECT_CATEGORY_CHANNEL:
+ SelectImplicitChannelTargets(effIndex, targetType);
+ break;
+ case TARGET_SELECT_CATEGORY_NEARBY:
+ SelectImplicitNearbyTargets(effIndex, targetType, effectMask);
+ break;
+ case TARGET_SELECT_CATEGORY_CONE:
+ SelectImplicitConeTargets(effIndex, targetType, effectMask);
+ break;
+ case TARGET_SELECT_CATEGORY_AREA:
+ SelectImplicitAreaTargets(effIndex, targetType, effectMask);
+ break;
+ case TARGET_SELECT_CATEGORY_DEFAULT:
+ switch (targetType.GetObjectType())
+ {
+ case TARGET_OBJECT_TYPE_SRC:
+ switch(targetType.GetReferenceType())
+ {
+ case TARGET_REFERENCE_TYPE_CASTER:
+ m_targets.SetSrc(*m_caster);
+ break;
+ default:
+ ASSERT("Spell::SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT_SRC");
+ break;
+ }
+ break;
+ case TARGET_OBJECT_TYPE_DEST:
+ switch(targetType.GetReferenceType())
+ {
+ case TARGET_REFERENCE_TYPE_CASTER:
+ SelectImplicitCasterDestTargets(effIndex, targetType);
+ break;
+ case TARGET_REFERENCE_TYPE_TARGET:
+ SelectImplicitTargetDestTargets(effIndex, targetType);
+ break;
+ case TARGET_REFERENCE_TYPE_DEST:
+ SelectImplicitDestDestTargets(effIndex, targetType);
+ break;
+ default:
+ ASSERT("Spell::SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT_DEST");
+ break;
+ }
+ break;
+ default:
+ switch(targetType.GetReferenceType())
+ {
+ case TARGET_REFERENCE_TYPE_CASTER:
+ SelectImplicitCasterObjectTargets(effIndex, targetType);
+ break;
+ case TARGET_REFERENCE_TYPE_TARGET:
+ SelectImplicitTargetObjectTargets(effIndex, targetType);
+ break;
+ default:
+ ASSERT("Spell::SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT");
+ break;
+ }
+ break;
+ }
+ break;
+ case TARGET_SELECT_CATEGORY_NYI:
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SPELL: target type %u, found in spellID %u, effect %u is not implemented yet!", m_spellInfo->Id, effIndex, targetType.GetTarget());
+ break;
+ default:
+ ASSERT(false && "Spell::SelectEffectImplicitTargets: received not implemented select target category");
+ break;
+ }
+}
+
+void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
+{
+ if (targetType.GetReferenceType() != TARGET_REFERENCE_TYPE_CASTER)
+ {
+ ASSERT(false && "Spell::SelectImplicitChannelTargets: received not implemented target reference type");
+ return;
+ }
+
+ Spell* channeledSpell = m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
+ if (!channeledSpell)
+ {
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell::SelectImplicitChannelTargets: cannot find channel spell for spell ID %u, effect %u", m_spellInfo->Id, effIndex);
+ return;
+ }
+ switch (targetType.GetTarget())
+ {
+ case TARGET_UNIT_CHANNEL_TARGET:
+ // unit target may be no longer avalible - teleported out of map for example
+ if (Unit* target = Unit::GetUnit(*m_caster, channeledSpell->m_targets.GetUnitTargetGUID()))
+ AddUnitTarget(target, 1 << effIndex);
+ else
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SPELL: cannot find channel spell target for spell ID %u, effect %u", m_spellInfo->Id, effIndex);
+ break;
+ case TARGET_DEST_CHANNEL_TARGET:
+ if (channeledSpell->m_targets.HasDst())
+ m_targets.SetDst(channeledSpell->m_targets);
+ else if (WorldObject* target = ObjectAccessor::GetWorldObject(*m_caster, channeledSpell->m_targets.GetObjectTargetGUID()))
+ m_targets.SetDst(*target);
+ else
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SPELL: cannot find channel spell destination for spell ID %u, effect %u", m_spellInfo->Id, effIndex);
+ break;
+ case TARGET_DEST_CHANNEL_CASTER:
+ m_targets.SetDst(*channeledSpell->GetCaster());
+ break;
+ default:
+ ASSERT(false && "Spell::SelectImplicitChannelTargets: received not implemented target type");
+ break;
+ }
+}
+
+void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask)
+{
+ if (targetType.GetReferenceType() != TARGET_REFERENCE_TYPE_CASTER)
+ {
+ ASSERT(false && "Spell::SelectImplicitNearbyTargets: received not implemented target reference type");
+ return;
+ }
+
+ float range = 0.0f;
+ switch (targetType.GetCheckType())
+ {
+ case TARGET_CHECK_ENEMY:
+ range = m_spellInfo->GetMaxRange(false, m_caster, this);
+ break;
+ case TARGET_CHECK_ALLY:
+ case TARGET_CHECK_PARTY:
+ case TARGET_CHECK_RAID:
+ case TARGET_CHECK_RAID_CLASS:
+ range = m_spellInfo->GetMaxRange(true, m_caster, this);
+ break;
+ case TARGET_CHECK_ENTRY:
+ case TARGET_CHECK_DEFAULT:
+ range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive(), m_caster, this);
+ break;
+ default:
+ ASSERT(false && "Spell::SelectImplicitNearbyTargets: received not implemented selection check type");
+ break;
+ }
+
+ ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
+
+ // handle emergency case - try to use other provided targets if no conditions provided
+ if (targetType.GetCheckType() == TARGET_CHECK_ENTRY && (!condList || condList->empty()))
+ {
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell::SelectImplicitNearbyTargets: no conditions entry for target with TARGET_CHECK_ENTRY of spell ID %u, effect %u - selecting default targets", m_spellInfo->Id, effIndex);
+ switch (targetType.GetObjectType())
+ {
+ case TARGET_OBJECT_TYPE_GOBJ:
+ if (m_spellInfo->RequiresSpellFocus)
+ {
+ if (focusObject)
+ AddGOTarget(focusObject, effMask);
+ return;
+ }
+ break;
+ case TARGET_OBJECT_TYPE_DEST:
+ if (m_spellInfo->RequiresSpellFocus)
+ {
+ if (focusObject)
+ m_targets.SetDst(*focusObject);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ WorldObject* target = SearchNearbyTarget(range, targetType.GetObjectType(), targetType.GetCheckType(), condList);
+ if (!target)
+ {
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell::SelectImplicitNearbyTargets: cannot find nearby target for spell ID %u, effect %u", m_spellInfo->Id, effIndex);
+ return;
+ }
+
+ switch (targetType.GetObjectType())
+ {
+ case TARGET_OBJECT_TYPE_UNIT:
+ if (Unit* unitTarget = target->ToUnit())
+ AddUnitTarget(unitTarget, effMask, false);
+ break;
+ case TARGET_OBJECT_TYPE_GOBJ:
+ if (GameObject* gobjTarget = target->ToGameObject())
+ AddGOTarget(gobjTarget, effMask);
+ break;
+ case TARGET_OBJECT_TYPE_DEST:
+ m_targets.SetDst(*target);
+ break;
+ default:
+ ASSERT(false && "Spell::SelectImplicitNearbyTargets: received not implemented target object type");
+ break;
+ }
+
+ SelectImplicitChainTargets(effIndex, targetType, target, effMask);
+}
+
+void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask)
+{
+ if (targetType.GetReferenceType() != TARGET_REFERENCE_TYPE_CASTER)
+ {
+ ASSERT(false && "Spell::SelectImplicitConeTargets: received not implemented target reference type");
+ return;
+ }
+ std::list<WorldObject*> targets;
+ SpellTargetObjectTypes objectType = targetType.GetObjectType();
+ SpellTargetCheckTypes selectionType = targetType.GetCheckType();
+ ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
+ float coneAngle = M_PI/2;
+ float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
+
+ if (uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList))
+ {
+ Trinity::WorldObjectSpellConeTargetCheck check(coneAngle, radius, m_caster, m_spellInfo, selectionType, condList);
+ Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> searcher(m_caster, targets, check, containerTypeMask);
+ SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> > (searcher, containerTypeMask, m_caster, m_caster, radius);
+
+ if (!targets.empty())
+ {
+ // Other special target selection goes here
+ if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
+ {
+ Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
+ for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j)
+ if ((*j)->IsAffectedOnSpell(m_spellInfo))
+ maxTargets += (*j)->GetAmount();
+
+ Trinity::RandomResizeList(targets, maxTargets);
+ }
+
+ // for compability with older code - add only unit and go targets
+ // TODO: remove this
+ std::list<Unit*> unitTargets;
+ std::list<GameObject*> gObjTargets;
+
+ for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+ {
+ if (Unit* unitTarget = (*itr)->ToUnit())
+ unitTargets.push_back(unitTarget);
+ else if (GameObject* gObjTarget = (*itr)->ToGameObject())
+ gObjTargets.push_back(gObjTarget);
+ }
+
+ CallScriptAfterUnitTargetSelectHandlers(unitTargets, effIndex);
+
+ for (std::list<Unit*>::iterator itr = unitTargets.begin(); itr != unitTargets.end(); ++itr)
+ AddUnitTarget(*itr, effMask, false);
+
+ for (std::list<GameObject*>::iterator itr = gObjTargets.begin(); itr != gObjTargets.end(); ++itr)
+ AddGOTarget(*itr, effMask);
+ }
+ }
+}
+
+void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask)
+{
+ Unit* referer = NULL;
+ switch (targetType.GetReferenceType())
+ {
+ case TARGET_REFERENCE_TYPE_SRC:
+ case TARGET_REFERENCE_TYPE_DEST:
+ case TARGET_REFERENCE_TYPE_CASTER:
+ referer = m_caster;
+ break;
+ case TARGET_REFERENCE_TYPE_TARGET:
+ referer = m_targets.GetUnitTarget();
+ break;
+ 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)
+ {
+ if (ihit->effectMask & (1<<effIndex))
+ {
+ referer = ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ ASSERT(false && "Spell::SelectImplicitAreaTargets: received not implemented target reference type");
+ return;
+ }
+ if (!referer)
+ return;
+
+ Position const* center = NULL;
+ switch (targetType.GetReferenceType())
+ {
+ case TARGET_REFERENCE_TYPE_SRC:
+ center = m_targets.GetSrc();
+ break;
+ case TARGET_REFERENCE_TYPE_DEST:
+ center = m_targets.GetDst();
+ break;
+ case TARGET_REFERENCE_TYPE_CASTER:
+ case TARGET_REFERENCE_TYPE_TARGET:
+ case TARGET_REFERENCE_TYPE_LAST:
+ center = referer;
+ break;
+ default:
+ ASSERT(false && "Spell::SelectImplicitAreaTargets: received not implemented target reference type");
+ return;
+ }
+ std::list<WorldObject*> targets;
+ float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
+ SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions);
+
+ // Custom entries
+ // TODO: remove those
+ switch (m_spellInfo->Id)
+ {
+ case 46584: // Raise Dead
+ {
+ if (Player* playerCaster = m_caster->ToPlayer())
+ {
+ for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+ {
+ switch ((*itr)->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ case TYPEID_PLAYER:
+ {
+ Unit* unitTarget = (*itr)->ToUnit();
+ if (unitTarget->isAlive() || !playerCaster->isHonorOrXPTarget(unitTarget)
+ || ((unitTarget->GetCreatureTypeMask() & (1 << (CREATURE_TYPE_HUMANOID-1))) == 0)
+ || (unitTarget->GetDisplayId() != unitTarget->GetNativeDisplayId()))
+ break;
+ AddUnitTarget(unitTarget, effMask, false);
+ // no break;
+ }
+ case TYPEID_CORPSE: // wont work until corpses are allowed in target lists, but at least will send dest in packet
+ m_targets.SetDst(*(*itr));
+ return; // nothing more to do here
+ default:
+ break;
+ }
+ }
+ }
+ return; // don't add targets to target map
+ }
+ // Corpse Explosion
+ case 49158:
+ case 51325:
+ case 51326:
+ case 51327:
+ case 51328:
+ // check if our target is not valid (spell can target ghoul or dead unit)
+ if (!(m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->GetDisplayId() == m_targets.GetUnitTarget()->GetNativeDisplayId() &&
+ ((m_targets.GetUnitTarget()->GetEntry() == 26125 && m_targets.GetUnitTarget()->GetOwnerGUID() == m_caster->GetGUID())
+ || m_targets.GetUnitTarget()->isDead())))
+ {
+ // remove existing targets
+ CleanupTargetList();
+
+ for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+ {
+ switch ((*itr)->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ case TYPEID_PLAYER:
+ if (!(*itr)->ToUnit()->isDead())
+ break;
+ AddUnitTarget((*itr)->ToUnit(), 1 << effIndex, false);
+ return;
+ default:
+ break;
+ }
+ }
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true);
+ SendCastResult(SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW);
+ finish(false);
+ }
+ return;
+ default:
+ break;
+ }
+ std::list<Unit*> unitTargets;
+ std::list<GameObject*> gObjTargets;
+ // for compability with older code - add only unit and go targets
+ // TODO: remove this
+ for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+ {
+ if (Unit* unitTarget = (*itr)->ToUnit())
+ unitTargets.push_back(unitTarget);
+ else if (GameObject* gObjTarget = (*itr)->ToGameObject())
+ gObjTargets.push_back(gObjTarget);
+ }
+
+ if (!unitTargets.empty())
+ {
+ // Special target selection for smart heals and energizes
+ uint32 maxSize = 0;
+ int32 power = -1;
+ switch (m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ switch (m_spellInfo->Id)
+ {
+ case 52759: // Ancestral Awakening
+ case 71610: // Echoes of Light (Althor's Abacus normal version)
+ case 71641: // Echoes of Light (Althor's Abacus heroic version)
+ maxSize = 1;
+ power = POWER_HEALTH;
+ break;
+ case 54968: // Glyph of Holy Light
+ maxSize = m_spellInfo->MaxAffectedTargets;
+ power = POWER_HEALTH;
+ break;
+ case 57669: // Replenishment
+ // In arenas Replenishment may only affect the caster
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->InArena())
+ {
+ unitTargets.clear();
+ unitTargets.push_back(m_caster);
+ break;
+ }
+ maxSize = 10;
+ power = POWER_MANA;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SPELLFAMILY_PRIEST:
+ if (m_spellInfo->SpellFamilyFlags[0] == 0x10000000) // Circle of Healing
+ {
+ maxSize = m_caster->HasAura(55675) ? 6 : 5; // Glyph of Circle of Healing
+ power = POWER_HEALTH;
+ }
+ else if (m_spellInfo->Id == 64844) // Divine Hymn
+ {
+ maxSize = 3;
+ power = POWER_HEALTH;
+ }
+ else if (m_spellInfo->Id == 64904) // Hymn of Hope
+ {
+ maxSize = 3;
+ power = POWER_MANA;
+ }
+ else
+ break;
+
+ // Remove targets outside caster's raid
+ for (std::list<Unit*>::iterator itr = unitTargets.begin() ; itr != unitTargets.end();)
+ {
+ if (!(*itr)->IsInRaidWith(m_caster))
+ itr = unitTargets.erase(itr);
+ else
+ ++itr;
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ if (m_spellInfo->SpellFamilyFlags[1] == 0x04000000) // Wild Growth
+ {
+ maxSize = m_caster->HasAura(62970) ? 6 : 5; // Glyph of Wild Growth
+ power = POWER_HEALTH;
+ }
+ else if (m_spellInfo->SpellFamilyFlags[2] == 0x0100) // Starfall
+ {
+ // Remove targets not in LoS or in stealth
+ for (std::list<Unit*>::iterator itr = unitTargets.begin() ; itr != unitTargets.end();)
+ {
+ if ((*itr)->HasStealthAura() || (*itr)->HasInvisibilityAura() || !(*itr)->IsWithinLOSInMap(m_caster))
+ itr = unitTargets.erase(itr);
+ else
+ ++itr;
+ }
+ break;
+ }
+ else
+ break;
+
+ // Remove targets outside caster's raid
+ for (std::list<Unit*>::iterator itr = unitTargets.begin() ; itr != unitTargets.end();)
+ if (!(*itr)->IsInRaidWith(m_caster))
+ itr = unitTargets.erase(itr);
+ else
+ ++itr;
+ break;
+ default:
+ break;
+ }
+
+ if (maxSize && power != -1)
+ {
+ if (Powers(power) == POWER_HEALTH)
+ {
+ if (unitTargets.size() > maxSize)
+ {
+ unitTargets.sort(Trinity::HealthPctOrderPred());
+ unitTargets.resize(maxSize);
+ }
+ }
+ else
+ {
+ for (std::list<Unit*>::iterator itr = unitTargets.begin() ; itr != unitTargets.end();)
+ if ((*itr)->getPowerType() != (Powers)power)
+ itr = unitTargets.erase(itr);
+ else
+ ++itr;
+
+ if (unitTargets.size() > maxSize)
+ {
+ unitTargets.sort(Trinity::PowerPctOrderPred((Powers)power));
+ unitTargets.resize(maxSize);
+ }
+ }
+ }
+
+ // Other special target selection goes here
+ if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
+ {
+ Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
+ for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j)
+ if ((*j)->IsAffectedOnSpell(m_spellInfo))
+ maxTargets += (*j)->GetAmount();
+
+ if (m_spellInfo->Id == 5246) //Intimidating Shout
+ unitTargets.remove(m_targets.GetUnitTarget());
+ Trinity::RandomResizeList(unitTargets, maxTargets);
+ }
+
+ CallScriptAfterUnitTargetSelectHandlers(unitTargets, effIndex);
+
+ for (std::list<Unit*>::iterator itr = unitTargets.begin(); itr != unitTargets.end(); ++itr)
+ AddUnitTarget(*itr, effMask, false);
+ }
+
+ if (!gObjTargets.empty())
+ {
+ if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
+ {
+ Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
+ for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j)
+ if ((*j)->IsAffectedOnSpell(m_spellInfo))
+ maxTargets += (*j)->GetAmount();
+
+ Trinity::RandomResizeList(gObjTargets, maxTargets);
+ }
+ for (std::list<GameObject*>::iterator itr = gObjTargets.begin(); itr != gObjTargets.end(); ++itr)
+ AddGOTarget(*itr, effMask);
+ }
+}
+
+void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
+{
+ switch(targetType.GetTarget())
+ {
+ case TARGET_DEST_CASTER:
+ m_targets.SetDst(*m_caster);
+ return;
+ case TARGET_DEST_HOME:
+ if (Player* playerCaster = m_caster->ToPlayer())
+ m_targets.SetDst(playerCaster->m_homebindX, playerCaster->m_homebindY, playerCaster->m_homebindZ, playerCaster->GetOrientation(), playerCaster->m_homebindMapId);
+ return;
+ case TARGET_DEST_DB:
+ if (SpellTargetPosition const* st = sSpellMgr->GetSpellTargetPosition(m_spellInfo->Id))
+ {
+ // TODO: fix this check
+ if (m_spellInfo->HasEffect(SPELL_EFFECT_TELEPORT_UNITS))
+ m_targets.SetDst(st->target_X, st->target_Y, st->target_Z, st->target_Orientation, (int32)st->target_mapId);
+ else if (st->target_mapId == m_caster->GetMapId())
+ m_targets.SetDst(st->target_X, st->target_Y, st->target_Z, st->target_Orientation);
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SPELL: unknown target coordinates for spell ID %u", m_spellInfo->Id);
+ WorldObject* target = m_targets.GetObjectTarget();
+ m_targets.SetDst(target ? *target : *m_caster);
+ }
+ return;
+ case TARGET_DEST_CASTER_FISHING:
+ {
+ float min_dis = m_spellInfo->GetMinRange(true);
+ float max_dis = m_spellInfo->GetMaxRange(true);
+ float dis = (float)rand_norm() * (max_dis - min_dis) + min_dis;
+ float x, y, z, angle;
+ angle = (float)rand_norm() * static_cast<float>(M_PI * 35.0f / 180.0f) - static_cast<float>(M_PI * 17.5f / 180.0f);
+ m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis, angle);
+ m_targets.SetDst(x, y, z, m_caster->GetOrientation());
+ return;
+ }
+ default:
+ break;
+ }
+
+ float dist;
+ float angle = targetType.CalcDirectionAngle();
+ float objSize = m_caster->GetObjectSize();
+ if (targetType.GetTarget() == TARGET_DEST_CASTER_SUMMON)
+ dist = PET_FOLLOW_DIST;
+ else
+ dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
+
+ if (dist < objSize)
+ dist = objSize;
+ else if (targetType.GetTarget() == TARGET_DEST_CASTER_RANDOM)
+ dist = objSize + (dist - objSize) * (float)rand_norm();
+
+ Position pos;
+ if (targetType.GetTarget() == TARGET_DEST_CASTER_FRONT_LEAP)
+ m_caster->GetFirstCollisionPosition(pos, dist, angle);
+ else
+ m_caster->GetNearPosition(pos, dist, angle);
+ m_targets.SetDst(*m_caster);
+ m_targets.ModDst(pos);
+}
+
+void Spell::SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
+{
+ WorldObject* target = m_targets.GetObjectTarget();
+ switch(targetType.GetTarget())
+ {
+ case TARGET_DEST_TARGET_ENEMY:
+ case TARGET_DEST_TARGET_ANY:
+ m_targets.SetDst(*target);
+ return;
+ default:
+ break;
+ }
+
+ float angle = targetType.CalcDirectionAngle();
+ float objSize = target->GetObjectSize();
+ float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
+ if (dist < objSize)
+ dist = objSize;
+ else if (targetType.GetTarget() == TARGET_DEST_TARGET_RANDOM)
+ dist = objSize + (dist - objSize) * (float)rand_norm();
+
+ Position pos;
+ target->GetNearPosition(pos, dist, angle);
+ m_targets.SetDst(*target);
+ m_targets.ModDst(pos);
+}
+
+void Spell::SelectImplicitDestDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
+{
+ // set destination to caster if no dest provided
+ // can only happen if previous destination target could not be set for some reason
+ // (not found nearby target, or channel target for example
+ // maybe we should abort the spell in such case?
+ if (!m_targets.HasDst())
+ m_targets.SetDst(*m_caster);
+
+ switch(targetType.GetTarget())
+ {
+ case TARGET_DEST_DYNOBJ_ENEMY:
+ case TARGET_DEST_DYNOBJ_ALLY:
+ case TARGET_DEST_DYNOBJ_NONE:
+ case TARGET_DEST_DEST:
+ return;
+ case TARGET_DEST_TRAJ:
+ SelectImplicitTrajTargets();
+ return;
+ default:
+ break;
+ }
+
+ float angle = targetType.CalcDirectionAngle();
+ float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
+ if (targetType.GetTarget() == TARGET_DEST_DEST_RANDOM)
+ dist *= (float)rand_norm();
+
+ Position pos = *m_targets.GetDst();
+ m_caster->MovePosition(pos, dist, angle);
+ m_targets.ModDst(pos);
+}
+
+void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
+{
+ switch(targetType.GetTarget())
+ {
+ case TARGET_UNIT_CASTER:
+ AddUnitTarget(m_caster, 1 << effIndex, false);
+ break;
+ case TARGET_UNIT_MASTER:
+ if (Unit* owner = m_caster->GetCharmerOrOwner())
+ AddUnitTarget(owner, 1 << effIndex);
+ break;
+ case TARGET_UNIT_PET:
+ if (Guardian* pet = m_caster->GetGuardianPet())
+ AddUnitTarget(pet, 1 << effIndex);
+ break;
+ case TARGET_UNIT_SUMMONER:
+ if (m_caster->isSummon())
+ if (Unit* unit = m_caster->ToTempSummon()->GetSummoner())
+ AddUnitTarget(unit, 1 << effIndex);
+ break;
+ case TARGET_UNIT_VEHICLE:
+ if (Unit *vehicle = m_caster->GetVehicleBase())
+ AddUnitTarget(vehicle, 1 << effIndex);
+ break;
+ case TARGET_UNIT_PASSENGER_0:
+ case TARGET_UNIT_PASSENGER_1:
+ case TARGET_UNIT_PASSENGER_2:
+ case TARGET_UNIT_PASSENGER_3:
+ case TARGET_UNIT_PASSENGER_4:
+ case TARGET_UNIT_PASSENGER_5:
+ case TARGET_UNIT_PASSENGER_6:
+ case TARGET_UNIT_PASSENGER_7:
+ if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsVehicle())
+ if (Unit *unit = m_caster->GetVehicleKit()->GetPassenger(targetType.GetTarget() - TARGET_UNIT_PASSENGER_0))
+ AddUnitTarget(unit, 1 << effIndex);
+ break;
+ default:
+ break;
+ }
+}
+
+void Spell::SelectImplicitTargetObjectTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
+{
+ ASSERT((m_targets.GetObjectTarget() || m_targets.GetItemTarget()) && "Spell::SelectImplicitTargetObjectTargets - no explicit object or item target available!");
+ if (Unit* unit = m_targets.GetUnitTarget())
+ AddUnitTarget(unit, 1 << effIndex);
+ else if (GameObject* gobj = m_targets.GetGOTarget())
+ AddGOTarget(gobj, 1 << effIndex);
+ else
+ AddItemTarget(m_targets.GetItemTarget(), effIndex);
+
+ if (WorldObject* target = m_targets.GetObjectTarget())
+ SelectImplicitChainTargets(effIndex, targetType, target, 1 << effIndex);
+}
+
+void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, WorldObject* target, uint32 effMask)
+{
+ uint32 maxTargets = m_spellInfo->Effects[effIndex].ChainTarget;
+ if (Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this);
+
+ if (maxTargets > 1)
+ {
+ // mark damage multipliers as used
+ for (uint32 k = effIndex; k < MAX_SPELL_EFFECTS; ++k)
+ if (effMask & (1 << k))
+ m_damageMultipliers[k] = 1.0f;
+ m_applyMultiplierMask |= effMask;
+
+ std::list<WorldObject*> targets;
+ SearchChainTargets(targets, maxTargets - 1, target, targetType.GetObjectType(), targetType.GetCheckType()
+ , m_spellInfo->Effects[effIndex].ImplicitTargetConditions, targetType.GetTarget() == TARGET_UNIT_TARGET_CHAINHEAL_ALLY);
+
+ // for backward compability
+ std::list<Unit*> unitTargets;
+ for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+ if (Unit* unitTarget = (*itr)->ToUnit())
+ unitTargets.push_back(unitTarget);
+
+ CallScriptAfterUnitTargetSelectHandlers(unitTargets, effIndex);
+
+ for (std::list<Unit*>::iterator itr = unitTargets.begin(); itr != unitTargets.end(); ++itr)
+ AddUnitTarget(*itr, effMask, false);
+ }
+}
+
+float tangent(float x)
+{
+ x = tan(x);
+ //if (x < std::numeric_limits<float>::max() && x > -std::numeric_limits<float>::max()) return x;
+ //if (x >= std::numeric_limits<float>::max()) return std::numeric_limits<float>::max();
+ //if (x <= -std::numeric_limits<float>::max()) return -std::numeric_limits<float>::max();
+ if (x < 100000.0f && x > -100000.0f) return x;
+ if (x >= 100000.0f) return 100000.0f;
+ if (x <= 100000.0f) return -100000.0f;
+ return 0.0f;
+}
+
+#define DEBUG_TRAJ(a) //a
+
+void Spell::SelectImplicitTrajTargets()
+{
+ if (!m_targets.HasTraj())
+ return;
+
+ float dist2d = m_targets.GetDist2d();
+ if (!dist2d)
+ return;
+
+ float srcToDestDelta = m_targets.GetDst()->m_positionZ - m_targets.GetSrc()->m_positionZ;
+
+ std::list<WorldObject*> targets;
+ Trinity::WorldObjectSpellTrajTargetCheck check(dist2d, m_targets.GetSrc(), m_caster, m_spellInfo);
+ Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellTrajTargetCheck> searcher(m_caster, targets, check, GRID_MAP_TYPE_MASK_ALL);
+ SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellTrajTargetCheck> > (searcher, GRID_MAP_TYPE_MASK_ALL, m_caster, m_targets.GetSrc(), dist2d);
+ if (targets.empty())
+ return;
+
+ targets.sort(Trinity::ObjectDistanceOrderPred(m_caster));
+
+ float b = tangent(m_targets.GetElevation());
+ float a = (srcToDestDelta - dist2d * b) / (dist2d * dist2d);
+ if (a > -0.0001f)
+ a = 0;
+ DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: a %f b %f", a, b);)
+
+ float bestDist = m_spellInfo->GetMaxRange(false);
+
+ std::list<WorldObject*>::const_iterator itr = targets.begin();
+ for (; itr != targets.end(); ++itr)
+ {
+ if (Unit* unitTarget = (*itr)->ToUnit())
+ if (m_caster == *itr || m_caster->IsOnVehicle(unitTarget) || (unitTarget)->GetVehicle())//(*itr)->IsOnVehicle(m_caster))
+ continue;
+
+ const float size = std::max((*itr)->GetObjectSize() * 0.7f, 1.0f); // 1/sqrt(3)
+ // TODO: all calculation should be based on src instead of m_caster
+ const float objDist2d = m_targets.GetSrc()->GetExactDist2d(*itr) * cos(m_targets.GetSrc()->GetRelativeAngle(*itr));
+ const float dz = (*itr)->GetPositionZ() - m_targets.GetSrc()->m_positionZ;
+
+ DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: check %u, dist between %f %f, height between %f %f.", (*itr)->GetEntry(), objDist2d - size, objDist2d + size, dz - size, dz + size);)
+
+ float dist = objDist2d - size;
+ float height = dist * (a * dist + b);
+ DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: dist %f, height %f.", dist, height);)
+ if (dist < bestDist && height < dz + size && height > dz - size)
+ {
+ bestDist = dist > 0 ? dist : 0;
+ break;
+ }
+
+#define CHECK_DIST {\
+ DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: dist %f, height %f.", dist, height);)\
+ if (dist > bestDist)\
+ continue;\
+ if (dist < objDist2d + size && dist > objDist2d - size)\
+ {\
+ bestDist = dist;\
+ break;\
+ }\
+ }
+
+ if (!a)
+ {
+ height = dz - size;
+ dist = height / b;
+ CHECK_DIST;
+
+ height = dz + size;
+ dist = height / b;
+ CHECK_DIST;
+
+ continue;
+ }
+
+ height = dz - size;
+ float sqrt1 = b * b + 4 * a * height;
+ if (sqrt1 > 0)
+ {
+ sqrt1 = sqrt(sqrt1);
+ dist = (sqrt1 - b) / (2 * a);
+ CHECK_DIST;
+ }
+
+ height = dz + size;
+ float sqrt2 = b * b + 4 * a * height;
+ if (sqrt2 > 0)
+ {
+ sqrt2 = sqrt(sqrt2);
+ dist = (sqrt2 - b) / (2 * a);
+ CHECK_DIST;
+
+ dist = (-sqrt2 - b) / (2 * a);
+ CHECK_DIST;
+ }
+
+ if (sqrt1 > 0)
+ {
+ dist = (-sqrt1 - b) / (2 * a);
+ CHECK_DIST;
+ }
+ }
+
+ if (m_targets.GetSrc()->GetExactDist2d(m_targets.GetDst()) > bestDist)
+ {
+ float x = m_targets.GetSrc()->m_positionX + cos(m_caster->GetOrientation()) * bestDist;
+ float y = m_targets.GetSrc()->m_positionY + sin(m_caster->GetOrientation()) * bestDist;
+ float z = m_targets.GetSrc()->m_positionZ + bestDist * (a * bestDist + b);
+
+ if (itr != targets.end())
+ {
+ float distSq = (*itr)->GetExactDistSq(x, y, z);
+ float sizeSq = (*itr)->GetObjectSize();
+ sizeSq *= sizeSq;
+ DEBUG_TRAJ(sLog->outError("Initial %f %f %f %f %f", x, y, z, distSq, sizeSq);)
+ if (distSq > sizeSq)
+ {
+ float factor = 1 - sqrt(sizeSq / distSq);
+ x += factor * ((*itr)->GetPositionX() - x);
+ y += factor * ((*itr)->GetPositionY() - y);
+ z += factor * ((*itr)->GetPositionZ() - z);
+
+ distSq = (*itr)->GetExactDistSq(x, y, z);
+ DEBUG_TRAJ(sLog->outError("Initial %f %f %f %f %f", x, y, z, distSq, sizeSq);)
+ }
+ }
+
+ Position trajDst;
+ trajDst.Relocate(x, y, z, m_caster->GetOrientation());
+ m_targets.ModDst(trajDst);
+ }
+}
+
void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex)
{
// special case for SPELL_EFFECT_SUMMON_RAF_FRIEND and SPELL_EFFECT_SUMMON_PLAYER
@@ -849,6 +1778,192 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex)
}
}
+uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* condList)
+{
+ // this function selects which containers need to be searched for spell target
+ uint32 retMask = GRID_MAP_TYPE_MASK_ALL;
+
+ // filter searchers based on searched object type
+ switch (objType)
+ {
+ case TARGET_OBJECT_TYPE_UNIT:
+ case TARGET_OBJECT_TYPE_UNIT_AND_DEST:
+ case TARGET_OBJECT_TYPE_CORPSE:
+ case TARGET_OBJECT_TYPE_CORPSE_ENEMY:
+ case TARGET_OBJECT_TYPE_CORPSE_ALLY:
+ retMask &= GRID_MAP_TYPE_MASK_PLAYER | GRID_MAP_TYPE_MASK_CORPSE | GRID_MAP_TYPE_MASK_CREATURE;
+ break;
+ case TARGET_OBJECT_TYPE_GOBJ:
+ case TARGET_OBJECT_TYPE_GOBJ_ITEM:
+ retMask &= GRID_MAP_TYPE_MASK_GAMEOBJECT;
+ break;
+ default:
+ break;
+ }
+ if (!(m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_DEAD))
+ retMask &= ~GRID_MAP_TYPE_MASK_CORPSE;
+ if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS)
+ retMask &= GRID_MAP_TYPE_MASK_CORPSE | GRID_MAP_TYPE_MASK_PLAYER;
+ if (m_spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS)
+ retMask &= GRID_MAP_TYPE_MASK_PLAYER;
+
+ if (condList)
+ retMask &= sConditionMgr->GetSearcherTypeMaskForConditionList(*condList);
+ return retMask;
+}
+
+template<class SEARCHER>
+void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius)
+{
+ if (!containerMask)
+ return;
+
+ // search world and grid for possible targets
+ bool searchInGrid = containerMask & (GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_GAMEOBJECT);
+ bool searchInWorld = containerMask & (GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER | GRID_MAP_TYPE_MASK_CORPSE);
+ if (searchInGrid || searchInWorld)
+ {
+ float x,y;
+ x = pos->GetPositionX();
+ y = pos->GetPositionY();
+
+ CellCoord p(Trinity::ComputeCellCoord(x, y));
+ Cell cell(p);
+ cell.SetNoCreate();
+
+ Map& map = *(referer->GetMap());
+
+ if (searchInWorld)
+ {
+ TypeContainerVisitor<SEARCHER, WorldTypeMapContainer> world_object_notifier(searcher);
+ cell.Visit(p, world_object_notifier, map, radius, x, y);
+ }
+ if (searchInGrid)
+ {
+ TypeContainerVisitor<SEARCHER, GridTypeMapContainer > grid_object_notifier(searcher);
+ cell.Visit(p, grid_object_notifier, map, radius, x , y);
+ }
+ }
+}
+
+WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList)
+{
+ WorldObject* target = NULL;
+ uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList);
+ if (!containerTypeMask)
+ return NULL;
+ Trinity::WorldObjectSpellNearbyTargetCheck check(range, m_caster, m_spellInfo, selectionType, condList);
+ Trinity::WorldObjectLastSearcher<Trinity::WorldObjectSpellNearbyTargetCheck> searcher(m_caster, target, check, containerTypeMask);
+ SearchTargets<Trinity::WorldObjectLastSearcher<Trinity::WorldObjectSpellNearbyTargetCheck> > (searcher, containerTypeMask, m_caster, m_caster, range);
+ return target;
+}
+
+void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList)
+{
+ uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList);
+ if (!containerTypeMask)
+ return;
+ Trinity::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList);
+ Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck> searcher(m_caster, targets, check, containerTypeMask);
+ SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck> > (searcher, containerTypeMask, m_caster, m_caster, range);
+}
+
+void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionList* condList, bool isChainHeal)
+{
+ // max dist for jump target selection
+ float jumpRadius = 0.0f;
+ switch (m_spellInfo->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_RANGED:
+ // 7.5y for multi shot
+ jumpRadius = 7.5f;
+ break;
+ case SPELL_DAMAGE_CLASS_MELEE:
+ // 5y for swipe, cleave and similar
+ jumpRadius = 5.0f;
+ break;
+ case SPELL_DAMAGE_CLASS_NONE:
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ // 12.5y for chain heal spell since 3.2 patch
+ if (isChainHeal)
+ jumpRadius = 12.5f;
+ // 10y as default for magic chain spells
+ else
+ jumpRadius = 10.0f;
+ break;
+ }
+
+ // chain lightning/heal spells and similar - allow to jump at larger distance and go out of los
+ bool isBouncingFar = (m_spellInfo->AttributesEx4 & SPELL_ATTR4_AREA_TARGET_CHAIN
+ || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE
+ || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC);
+
+ // max dist which spell can reach
+ float searchRadius = jumpRadius;
+ if (isBouncingFar)
+ searchRadius *= chainTargets;
+
+ std::list<WorldObject*> tempTargets;
+ SearchAreaTargets(tempTargets, searchRadius, target, m_caster, objectType, selectType, condList);
+ tempTargets.remove(target);
+
+ // remove targets which are always invalid for chain spells
+ // for some spells allow only chain targets in front of caster (swipe for example)
+ if (!isBouncingFar)
+ {
+ for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end();)
+ {
+ std::list<WorldObject*>::iterator checkItr = itr++;
+ if (!m_caster->HasInArc(static_cast<float>(M_PI), *checkItr))
+ tempTargets.erase(checkItr);
+ }
+ }
+
+ while (chainTargets)
+ {
+ // try to get unit for next chain jump
+ std::list<WorldObject*>::iterator foundItr = tempTargets.end();
+ // get unit with highest hp deficit in dist
+ if (isChainHeal)
+ {
+ uint32 maxHPDeficit = 0;
+ for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end(); ++itr)
+ {
+ if (Unit* unitTarget = (*itr)->ToUnit())
+ {
+ uint32 deficit = unitTarget->GetMaxHealth() - unitTarget->GetHealth();
+ if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unitTarget, jumpRadius) && target->IsWithinLOSInMap(unitTarget))
+ {
+ foundItr = itr;
+ maxHPDeficit = deficit;
+ }
+ }
+ }
+ }
+ // get closest object
+ else
+ {
+ for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end(); ++itr)
+ {
+ if (foundItr == tempTargets.end())
+ {
+ if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr))
+ foundItr = itr;
+ }
+ else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr))
+ foundItr = itr;
+ }
+ }
+ // not found any valid target - chain ends
+ if (foundItr == tempTargets.end())
+ break;
+ target = *foundItr;
+ tempTargets.erase(foundItr);
+ targets.push_back(target);
+ --chainTargets;
+ }
+}
+
void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/)
{
//==========================================================================================
@@ -1402,13 +2517,17 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
}
}
-SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, const uint32 effectMask, bool scaleAura)
+SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleAura)
{
if (!unit || !effectMask)
return SPELL_MISS_EVADE;
- // Recheck immune (only for delayed spells)
- if (m_spellInfo->Speed && (unit->IsImmunedToDamage(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo)))
+ // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case
+ // disable effects to which unit is immune
+ for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber)
+ if (effectMask & (1 << effectNumber) && unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber))
+ effectMask &= ~(1 << effectNumber);
+ if (!effectMask || (m_spellInfo->Speed && (unit->IsImmunedToDamage(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo))))
return SPELL_MISS_IMMUNE;
PrepareScriptHitHandlers();
@@ -1563,7 +2682,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, const uint32 effectMask, bool
}
for (uint32 effectNumber = 0; effectNumber < MAX_SPELL_EFFECTS; ++effectNumber)
- if (effectMask & (1 << effectNumber) && !unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber)) //Handle effect only if the target isn't immune.
+ if (effectMask & (1 << effectNumber))
HandleEffects(unit, NULL, NULL, effectNumber, SPELL_EFFECT_HANDLE_HIT_TARGET);
return SPELL_MISS_NONE;
@@ -1771,1082 +2890,6 @@ struct ChainHealingOrder : public std::binary_function<const Unit*, const Unit*,
}
};
-void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uint32 num, SpellTargets TargetType)
-{
- Unit* cur = m_targets.GetUnitTarget();
- if (!cur)
- return;
-
- //FIXME: This very like horrible hack and wrong for most spells
- if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE)
- max_range += num * CHAIN_SPELL_JUMP_RADIUS;
-
- std::list<Unit*> tempUnitMap;
- if (TargetType == SPELL_TARGETS_CHAINHEAL)
- {
- SearchAreaTarget(tempUnitMap, max_range, PUSH_CHAIN, SPELL_TARGETS_ALLY);
- tempUnitMap.sort(ChainHealingOrder(m_caster));
- }
- else
- SearchAreaTarget(tempUnitMap, max_range, PUSH_CHAIN, TargetType);
- tempUnitMap.remove(cur);
-
- while (num)
- {
- TagUnitMap.push_back(cur);
- --num;
-
- if (tempUnitMap.empty())
- break;
-
- std::list<Unit*>::iterator next;
-
- if (TargetType == SPELL_TARGETS_CHAINHEAL)
- {
- next = tempUnitMap.begin();
- while (cur->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS || !cur->IsWithinLOSInMap(*next))
- {
- ++next;
- if (next == tempUnitMap.end())
- return;
- }
- }
- else
- {
- tempUnitMap.sort(Trinity::ObjectDistanceOrderPred(cur));
- next = tempUnitMap.begin();
-
- if (cur->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS) // Don't search beyond the max jump radius
- break;
-
- // Check if (*next) is a valid chain target. If not, don't add to TagUnitMap, and repeat loop.
- // If you want to add any conditions to exclude a target from TagUnitMap, add condition in this while () loop.
- while ((m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE
- && !m_caster->isInFrontInMap(*next, max_range))
- || !m_caster->canSeeOrDetect(*next)
- || !cur->IsWithinLOSInMap(*next)
- || (*next)->GetCreatureType() == CREATURE_TYPE_CRITTER
- || ((GetSpellInfo()->AttributesEx6 & SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED) && !(*next)->CanFreeMove()))
- {
- ++next;
- if (next == tempUnitMap.end() || cur->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS) // Don't search beyond the max jump radius
- return;
- }
- }
-
- cur = *next;
- tempUnitMap.erase(next);
- }
-}
-
-void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry)
-{
- if (TargetType == SPELL_TARGETS_GO)
- return;
-
- Position const* pos;
- switch (type)
- {
- case PUSH_DST_CENTER:
- CheckDst();
- pos = m_targets.GetDst();
- break;
- case PUSH_SRC_CENTER:
- CheckSrc();
- pos = m_targets.GetSrc();
- break;
- case PUSH_CHAIN:
- {
- Unit* target = m_targets.GetUnitTarget();
- if (!target)
- {
- sLog->outError("SPELL: cannot find unit target for spell ID %u\n", m_spellInfo->Id);
- return;
- }
- pos = target;
- break;
- }
- default:
- pos = m_caster;
- break;
- }
-
- Trinity::SpellNotifierCreatureAndPlayer notifier(m_caster, TagUnitMap, radius, type, TargetType, pos, entry, m_spellInfo);
- if ((m_spellInfo->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS) || (TargetType == SPELL_TARGETS_ENTRY && !entry))
- m_caster->GetMap()->VisitWorld(pos->m_positionX, pos->m_positionY, radius, notifier);
- else
- m_caster->GetMap()->VisitAll(pos->m_positionX, pos->m_positionY, radius, notifier);
-}
-
-void Spell::SearchGOAreaTarget(std::list<GameObject*> &TagGOMap, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry)
-{
- if (TargetType != SPELL_TARGETS_GO)
- return;
-
- Position const* pos;
- switch (type)
- {
- case PUSH_DST_CENTER:
- CheckDst();
- pos = m_targets.GetDst();
- break;
- case PUSH_SRC_CENTER:
- CheckSrc();
- pos = m_targets.GetSrc();
- break;
- default:
- pos = m_caster;
- break;
- }
-
- Trinity::GameObjectInRangeCheck check(pos->m_positionX, pos->m_positionY, pos->m_positionZ, radius, entry);
- Trinity::GameObjectListSearcher<Trinity::GameObjectInRangeCheck> searcher(m_caster, TagGOMap, check);
- m_caster->GetMap()->VisitGrid(pos->m_positionX, pos->m_positionY, radius, searcher);
-}
-
-WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType, SpellEffIndex effIndex)
-{
- switch (TargetType)
- {
- case SPELL_TARGETS_ENTRY:
- {
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, m_spellInfo->Id);
- if (conditions.empty())
- {
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) (caster Entry: %u) does not have record in `conditions` for spell script target (ConditionSourceType 13)", m_spellInfo->Id, m_caster->GetEntry());
- if (m_spellInfo->IsPositive())
- return SearchNearbyTarget(range, SPELL_TARGETS_ALLY, effIndex);
- else
- return SearchNearbyTarget(range, SPELL_TARGETS_ENEMY, effIndex);
- }
-
- Creature* creatureScriptTarget = NULL;
- GameObject* goScriptTarget = NULL;
-
- for (ConditionList::const_iterator i_spellST = conditions.begin(); i_spellST != conditions.end(); ++i_spellST)
- {
- if ((*i_spellST)->mConditionType != CONDITION_SPELL_SCRIPT_TARGET)
- continue;
- if ((*i_spellST)->mConditionValue3 && !((*i_spellST)->mConditionValue3 & (1 << uint32(effIndex))))
- continue;
- switch ((*i_spellST)->mConditionValue1)
- {
- case SPELL_TARGET_TYPE_CONTROLLED:
- for (Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr)
- if ((*itr)->GetEntry() == (*i_spellST)->mConditionValue2 && (*itr)->IsWithinDistInMap(m_caster, range))
- {
- goScriptTarget = NULL;
- creatureScriptTarget = (*itr)->ToCreature();
- range = m_caster->GetDistance(creatureScriptTarget);
- }
- break;
- case SPELL_TARGET_TYPE_GAMEOBJECT:
- if ((*i_spellST)->mConditionValue2)
- {
- if (GameObject* go = m_caster->FindNearestGameObject((*i_spellST)->mConditionValue2, range))
- {
- // remember found target and range, next attempt will find more near target with another entry
- goScriptTarget = go;
- creatureScriptTarget = NULL;
- range = m_caster->GetDistance(goScriptTarget);
- }
- }
- else if (focusObject) //Focus Object
- {
- float frange = m_caster->GetDistance(focusObject);
- if (range >= frange)
- {
- creatureScriptTarget = NULL;
- goScriptTarget = focusObject;
- range = frange;
- }
- }
- break;
- case SPELL_TARGET_TYPE_CREATURE:
- if (m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->GetEntry() == (*i_spellST)->mConditionValue2)
- return m_targets.GetUnitTarget();
- case SPELL_TARGET_TYPE_DEAD:
- default:
- if (Creature* cre = m_caster->FindNearestCreature((*i_spellST)->mConditionValue2, range, (*i_spellST)->mConditionValue1 != SPELL_TARGET_TYPE_DEAD))
- {
- creatureScriptTarget = cre;
- goScriptTarget = NULL;
- range = m_caster->GetDistance(creatureScriptTarget);
- }
- break;
- }
- }
-
- if (creatureScriptTarget)
- return creatureScriptTarget;
- else
- return goScriptTarget;
- }
- default:
- case SPELL_TARGETS_ENEMY:
- {
- Unit* target = NULL;
- Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, range);
- Trinity::UnitLastSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check);
- m_caster->VisitNearbyObject(range, searcher);
- return target;
- }
- case SPELL_TARGETS_ALLY:
- {
- Unit* target = NULL;
- Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(m_caster, m_caster, range);
- Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(m_caster, target, u_check);
- m_caster->VisitNearbyObject(range, searcher);
- return target;
- }
- }
-}
-
-uint32 Spell::SelectEffectTargets(uint32 i, SpellImplicitTargetInfo const& cur)
-{
- SpellNotifyPushType pushType = PUSH_NONE;
- Player* modOwner = NULL;
- if (m_originalCaster)
- modOwner = m_originalCaster->GetSpellModOwner();
-
- uint32 effectMask = 1 << i;
- // ENTRY targets may have different selection lists, skip those for now until we can compare lists easily and quickly
- if (GetSpellInfo()->Effects[i].TargetA.GetSelectionCheckType() != TARGET_SELECT_CHECK_ENTRY &&
- GetSpellInfo()->Effects[i].TargetB.GetSelectionCheckType() != TARGET_SELECT_CHECK_ENTRY)
- for (uint32 j = i + 1; j < MAX_SPELL_EFFECTS; ++j)
- if (GetSpellInfo()->Effects[i].TargetA.GetTarget() == GetSpellInfo()->Effects[j].TargetA.GetTarget() &&
- GetSpellInfo()->Effects[i].TargetB.GetTarget() == GetSpellInfo()->Effects[j].TargetB.GetTarget() &&
- GetSpellInfo()->Effects[i].CalcRadius(m_caster) == GetSpellInfo()->Effects[j].CalcRadius(m_caster))
- effectMask |= 1 << j;
-
- switch (cur.GetType())
- {
- case TARGET_TYPE_UNIT_CASTER:
- {
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_CASTER:
- AddUnitTarget(m_caster, effectMask, false);
- break;
- case TARGET_DEST_CASTER_FISHING:
- {
- float min_dis = m_spellInfo->GetMinRange(true);
- float max_dis = m_spellInfo->GetMaxRange(true);
- float dis = (float)rand_norm() * (max_dis - min_dis) + min_dis;
- float x, y, z, angle;
- angle = (float)rand_norm() * static_cast<float>(M_PI * 35.0f / 180.0f) - static_cast<float>(M_PI * 17.5f / 180.0f);
- m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis, angle);
- m_targets.SetDst(x, y, z, m_caster->GetOrientation());
- break;
- }
- case TARGET_UNIT_MASTER:
- if (Unit* owner = m_caster->GetCharmerOrOwner())
- AddUnitTarget(owner, effectMask);
- break;
- case TARGET_UNIT_PET:
- if (Guardian* pet = m_caster->GetGuardianPet())
- AddUnitTarget(pet, effectMask);
- break;
- case TARGET_UNIT_SUMMONER:
- if (m_caster->isSummon())
- if (Unit* unit = m_caster->ToTempSummon()->GetSummoner())
- AddUnitTarget(unit, effectMask);
- break;
- case TARGET_UNIT_CASTER_AREA_PARTY:
- case TARGET_UNIT_CASTER_AREA_RAID:
- pushType = PUSH_CASTER_CENTER;
- break;
- case TARGET_UNIT_VEHICLE:
- if (Unit* vehicle = m_caster->GetVehicleBase())
- AddUnitTarget(vehicle, effectMask);
- break;
- case TARGET_UNIT_PASSENGER_0:
- case TARGET_UNIT_PASSENGER_1:
- case TARGET_UNIT_PASSENGER_2:
- case TARGET_UNIT_PASSENGER_3:
- case TARGET_UNIT_PASSENGER_4:
- case TARGET_UNIT_PASSENGER_5:
- case TARGET_UNIT_PASSENGER_6:
- case TARGET_UNIT_PASSENGER_7:
- if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsVehicle())
- if (Unit* unit = m_caster->GetVehicleKit()->GetPassenger(cur.GetTarget() - TARGET_UNIT_PASSENGER_0))
- AddUnitTarget(unit, effectMask);
- break;
- default:
- break;
- }
- break;
- }
-
- case TARGET_TYPE_UNIT_TARGET:
- {
- Unit* target = m_targets.GetUnitTarget();
- if (!target)
- {
- sLog->outError("SPELL: no unit target for spell ID %u", m_spellInfo->Id);
- break;
- }
-
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_TARGET_ENEMY:
- if (Unit* magnet = m_caster->SelectMagnetTarget(target, m_spellInfo))
- if (magnet != target)
- m_targets.SetUnitTarget(magnet);
- pushType = PUSH_CHAIN;
- break;
- case TARGET_UNIT_TARGET_ANY:
- if (!m_spellInfo->IsPositive())
- if (Unit* magnet = m_caster->SelectMagnetTarget(target, m_spellInfo))
- if (magnet != target)
- m_targets.SetUnitTarget(magnet);
- pushType = PUSH_CHAIN;
- break;
- case TARGET_UNIT_TARGET_CHAINHEAL_ALLY:
- pushType = PUSH_CHAIN;
- break;
- case TARGET_UNIT_TARGET_ALLY:
- AddUnitTarget(target, effectMask, false);
- break;
- case TARGET_UNIT_TARGET_RAID:
- case TARGET_UNIT_TARGET_PARTY:
- case TARGET_UNIT_TARGET_MINIPET:
- AddUnitTarget(target, effectMask, false);
- break;
- case TARGET_UNIT_TARGET_PASSENGER:
- AddUnitTarget(target, effectMask, false);
- break;
- case TARGET_UNIT_LASTTARGET_AREA_PARTY:
- case TARGET_UNIT_TARGET_AREA_RAID_CLASS:
- pushType = PUSH_CASTER_CENTER; // not real
- break;
- default:
- break;
- }
- break;
- }
-
- case TARGET_TYPE_UNIT_NEARBY:
- {
- WorldObject* target = NULL;
- float range;
-
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_NEARBY_ENEMY:
- range = m_spellInfo->GetMaxRange(false);
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
- target = SearchNearbyTarget(range, SPELL_TARGETS_ENEMY, SpellEffIndex(i));
- break;
- case TARGET_UNIT_NEARBY_ALLY:
- case TARGET_UNIT_NEARBY_PARTY: // TODO: fix party/raid targets
- case TARGET_UNIT_NEARBY_RAID:
- range = m_spellInfo->GetMaxRange(true);
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
- target = SearchNearbyTarget(range, SPELL_TARGETS_ALLY, SpellEffIndex(i));
- break;
- case TARGET_UNIT_NEARBY_ENTRY:
- case TARGET_GAMEOBJECT_NEARBY_ENTRY:
- range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive());
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
- target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY, SpellEffIndex(i));
- break;
- default:
- break;
- }
-
- if (!target)
- return 0;
- else if (target->GetTypeId() == TYPEID_GAMEOBJECT)
- AddGOTarget((GameObject*)target, effectMask);
- else
- {
- pushType = PUSH_CHAIN;
-
- if (m_targets.GetUnitTarget() != target)
- m_targets.SetUnitTarget((Unit*)target);
- }
-
- break;
- }
-
- case TARGET_TYPE_AREA_SRC:
- pushType = PUSH_SRC_CENTER;
- break;
-
- case TARGET_TYPE_AREA_DST:
- pushType = PUSH_DST_CENTER;
- break;
-
- case TARGET_TYPE_AREA_CONE:
- if (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_CONE_BACK)
- pushType = PUSH_IN_BACK;
- else if (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_CONE_LINE)
- pushType = PUSH_IN_LINE;
- else
- pushType = PUSH_IN_FRONT;
- break;
-
- case TARGET_TYPE_DEST_CASTER: //4+8+2
- {
- if (cur.GetTarget() == TARGET_SRC_CASTER)
- {
- m_targets.SetSrc(*m_caster);
- break;
- }
- else if (cur.GetTarget() == TARGET_DEST_CASTER)
- {
- m_targets.SetDst(*m_caster);
- break;
- }
-
- float angle, dist;
-
- float objSize = m_caster->GetObjectSize();
- if (cur.GetTarget() == TARGET_DEST_CASTER_SUMMON)
- dist = 0.0f;
- else
- dist = m_spellInfo->Effects[i].CalcRadius(m_caster);
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, dist, this);
- if (dist < objSize)
- dist = objSize;
- else if (cur.GetTarget() == TARGET_DEST_CASTER_RANDOM)
- dist = objSize + (dist - objSize) * (float)rand_norm();
-
- switch (cur.GetTarget())
- {
- case TARGET_DEST_CASTER_FRONT_LEFT: angle = static_cast<float>(-M_PI/4); break;
- case TARGET_DEST_CASTER_BACK_LEFT: angle = static_cast<float>(-3*M_PI/4); break;
- case TARGET_DEST_CASTER_BACK_RIGHT: angle = static_cast<float>(3*M_PI/4); break;
- case TARGET_DEST_CASTER_FRONT_RIGHT:angle = static_cast<float>(M_PI/4); break;
- case TARGET_DEST_CASTER_SUMMON:
- case TARGET_DEST_CASTER_FRONT_LEAP:
- case TARGET_DEST_CASTER_FRONT: angle = 0.0f; break;
- case TARGET_DEST_CASTER_BACK: angle = static_cast<float>(M_PI); break;
- case TARGET_DEST_CASTER_RIGHT: angle = static_cast<float>(-M_PI/2); break;
- case TARGET_DEST_CASTER_LEFT: angle = static_cast<float>(M_PI/2); break;
- default: angle = (float)rand_norm()*static_cast<float>(2*M_PI); break;
- }
-
- Position pos;
- if (cur.GetTarget() == TARGET_DEST_CASTER_FRONT_LEAP)
- m_caster->GetFirstCollisionPosition(pos, dist, angle);
- else
- m_caster->GetNearPosition(pos, dist, angle);
- m_targets.SetDst(*m_caster);
- m_targets.ModDst(pos);
- break;
- }
-
- case TARGET_TYPE_DEST_TARGET: //2+8+2
- {
- Unit* target = m_targets.GetUnitTarget();
- if (!target)
- {
- sLog->outError("SPELL: no unit target for spell ID %u", m_spellInfo->Id);
- break;
- }
-
- if (cur.GetTarget() == TARGET_DEST_TARGET_ENEMY || cur.GetTarget() == TARGET_DEST_TARGET_ANY)
- {
- m_targets.SetDst(*target);
- break;
- }
-
- float angle, dist;
-
- float objSize = target->GetObjectSize();
- dist = m_spellInfo->Effects[i].CalcRadius(m_caster);
- if (dist < objSize)
- dist = objSize;
- else if (cur.GetTarget() == TARGET_DEST_TARGET_RANDOM)
- dist = objSize + (dist - objSize) * (float)rand_norm();
-
- switch (cur.GetTarget())
- {
- case TARGET_DEST_TARGET_FRONT: angle = 0.0f; break;
- case TARGET_DEST_TARGET_BACK: angle = static_cast<float>(M_PI); break;
- case TARGET_DEST_TARGET_RIGHT: angle = static_cast<float>(M_PI/2); break;
- case TARGET_DEST_TARGET_LEFT: angle = static_cast<float>(-M_PI/2); break;
- case TARGET_DEST_TARGET_FRONT_LEFT: angle = static_cast<float>(-M_PI/4); break;
- case TARGET_DEST_TARGET_BACK_LEFT: angle = static_cast<float>(-3*M_PI/4); break;
- case TARGET_DEST_TARGET_BACK_RIGHT: angle = static_cast<float>(3*M_PI/4); break;
- case TARGET_DEST_TARGET_FRONT_RIGHT:angle = static_cast<float>(M_PI/4); break;
- default: angle = (float)rand_norm()*static_cast<float>(2*M_PI); break;
- }
-
- Position pos;
- target->GetNearPosition(pos, dist, angle);
- m_targets.SetDst(*target);
- m_targets.ModDst(pos);
- break;
- }
-
- case TARGET_TYPE_DEST_DEST: //5+8+1
- {
- if (!m_targets.HasDst())
- {
- sLog->outError("SPELL: no destination for spell ID %u", m_spellInfo->Id);
- break;
- }
-
- float angle;
- switch (cur.GetTarget())
- {
- case TARGET_DEST_DYNOBJ_ENEMY:
- case TARGET_DEST_DYNOBJ_ALLY:
- case TARGET_DEST_DYNOBJ_NONE:
- case TARGET_DEST_DEST:
- return effectMask;
- case TARGET_DEST_TRAJ:
- SelectTrajTargets();
- return effectMask;
- case TARGET_DEST_DEST_FRONT: angle = 0.0f; break;
- case TARGET_DEST_DEST_BACK: angle = static_cast<float>(M_PI); break;
- case TARGET_DEST_DEST_RIGHT: angle = static_cast<float>(M_PI/2); break;
- case TARGET_DEST_DEST_LEFT: angle = static_cast<float>(-M_PI/2); break;
- case TARGET_DEST_DEST_FRONT_LEFT: angle = static_cast<float>(-M_PI/4); break;
- case TARGET_DEST_DEST_BACK_LEFT: angle = static_cast<float>(-3*M_PI/4); break;
- case TARGET_DEST_DEST_BACK_RIGHT: angle = static_cast<float>(3*M_PI/4); break;
- case TARGET_DEST_DEST_FRONT_RIGHT:angle = static_cast<float>(M_PI/4); break;
- default: angle = (float)rand_norm()*static_cast<float>(2*M_PI); break;
- }
-
- float dist = m_spellInfo->Effects[i].CalcRadius(m_caster);
- if (cur.GetTarget() == TARGET_DEST_DEST_RANDOM || cur.GetTarget() == TARGET_DEST_DEST_RADIUS)
- dist *= (float)rand_norm();
-
- // must has dst, no need to set flag
- Position pos = *m_targets.GetDst();
- m_caster->MovePosition(pos, dist, angle);
- m_targets.ModDst(pos);
- break;
- }
-
- case TARGET_TYPE_DEST_SPECIAL:
- {
- switch (cur.GetTarget())
- {
- case TARGET_DEST_DB:
- if (SpellTargetPosition const* st = sSpellMgr->GetSpellTargetPosition(m_spellInfo->Id))
- {
- //TODO: fix this check
- if (m_spellInfo->Effects[0].Effect == SPELL_EFFECT_TELEPORT_UNITS || m_spellInfo->Effects[1].Effect == SPELL_EFFECT_TELEPORT_UNITS || m_spellInfo->Effects[2].Effect == SPELL_EFFECT_TELEPORT_UNITS)
- m_targets.SetDst(st->target_X, st->target_Y, st->target_Z, st->target_Orientation, (int32)st->target_mapId);
- else if (st->target_mapId == m_caster->GetMapId())
- m_targets.SetDst(st->target_X, st->target_Y, st->target_Z, st->target_Orientation);
- }
- else
- {
- sLog->outError("SPELL: unknown target coordinates for spell ID %u", m_spellInfo->Id);
- Unit* target = NULL;
- if (uint64 guid = m_caster->GetUInt64Value(UNIT_FIELD_TARGET))
- target = ObjectAccessor::GetUnit(*m_caster, guid);
- m_targets.SetDst(target ? *target : *m_caster);
- }
- break;
- case TARGET_DEST_HOME:
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- m_targets.SetDst(m_caster->ToPlayer()->m_homebindX, m_caster->ToPlayer()->m_homebindY, m_caster->ToPlayer()->m_homebindZ, m_caster->ToPlayer()->GetOrientation(), m_caster->ToPlayer()->m_homebindMapId);
- break;
- case TARGET_DEST_NEARBY_ENTRY:
- {
- float range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive());
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
-
- if (WorldObject* target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY, SpellEffIndex(i)))
- m_targets.SetDst(*target);
- break;
- }
- default:
- break;
- }
- break;
- }
-
- case TARGET_TYPE_CHANNEL:
- {
- if (!m_originalCaster || !m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
- {
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SPELL: no current channeled spell for spell ID %u - spell triggering this spell was interrupted.", m_spellInfo->Id);
- break;
- }
-
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_CHANNEL_TARGET:
- // unit target may be no longer avalible - teleported out of map for example
- if (Unit* target = Unit::GetUnit(*m_caster, m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)->m_targets.GetUnitTargetGUID()))
- AddUnitTarget(target, effectMask);
- else
- sLog->outError("SPELL: cannot find channel spell target for spell ID %u", m_spellInfo->Id);
- break;
- case TARGET_DEST_CHANNEL_TARGET:
- if (m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)->m_targets.HasDst())
- m_targets.SetDst(m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)->m_targets);
- else if (Unit* target = Unit::GetUnit(*m_caster, m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)->m_targets.GetUnitTargetGUID()))
- m_targets.SetDst(*target);
- else
- sLog->outError("SPELL: cannot find channel spell destination for spell ID %u", m_spellInfo->Id);
- break;
- case TARGET_DEST_CHANNEL_CASTER:
- m_targets.SetDst(*m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)->GetCaster());
- break;
- default:
- break;
- }
- break;
- }
-
- default:
- {
- switch (cur.GetTarget())
- {
- case TARGET_GAMEOBJECT_TARGET:
- if (m_targets.GetGOTarget())
- AddGOTarget(m_targets.GetGOTarget(), effectMask);
- break;
- case TARGET_GAMEOBJECT_ITEM_TARGET:
- if (m_targets.GetGOTargetGUID())
- AddGOTarget(m_targets.GetGOTarget(), effectMask);
- else if (m_targets.GetItemTarget())
- AddItemTarget(m_targets.GetItemTarget(), effectMask);
- break;
- default:
- sLog->outError("SPELL (caster[type: %u; guidlow: %u], spell: %u): unhandled spell target (%u)",
- m_caster->GetTypeId(), m_caster->GetGUIDLow(), m_spellInfo->Id, cur.GetTarget());
- break;
- }
- break;
- }
- }
-
- if (pushType == PUSH_CHAIN) // Chain
- {
- Unit* target = m_targets.GetUnitTarget();
- if (!target)
- {
- sLog->outError("SPELL: no chain unit target for spell ID %u", m_spellInfo->Id);
- return 0;
- }
-
- //Chain: 2, 6, 22, 25, 45, 77
- uint32 maxTargets = m_spellInfo->Effects[i].ChainTarget;
- if (modOwner)
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this);
-
- if (maxTargets > 1)
- {
- //otherwise, this multiplier is used for something else
- for (uint32 k = i; k < MAX_SPELL_EFFECTS; ++k)
- if (effectMask & (1 << k))
- m_damageMultipliers[k] = 1.0f;
- m_applyMultiplierMask |= effectMask;
-
- float range;
- std::list<Unit*> unitList;
-
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_NEARBY_ENEMY:
- case TARGET_UNIT_TARGET_ENEMY:
- case TARGET_UNIT_NEARBY_ENTRY: // fix me
- range = m_spellInfo->GetMaxRange(false);
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
- SearchChainTarget(unitList, range, maxTargets, SPELL_TARGETS_ENEMY);
- break;
- case TARGET_UNIT_TARGET_CHAINHEAL_ALLY:
- case TARGET_UNIT_NEARBY_ALLY: // fix me
- case TARGET_UNIT_NEARBY_PARTY:
- case TARGET_UNIT_NEARBY_RAID:
- range = m_spellInfo->GetMaxRange(true);
- if (modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
- SearchChainTarget(unitList, range, maxTargets, SPELL_TARGETS_CHAINHEAL);
- break;
- default:
- break;
- }
-
- CallScriptAfterUnitTargetSelectHandlers(unitList, SpellEffIndex(i));
-
- for (std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr)
- AddUnitTarget(*itr, effectMask, false);
- }
- else
- AddUnitTarget(target, effectMask, false);
- }
- else if (pushType)
- {
- float radius;
- SpellTargets targetType;
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_SRC_AREA_ENEMY:
- case TARGET_UNIT_DEST_AREA_ENEMY:
- case TARGET_UNIT_CONE_ENEMY_24:
- case TARGET_UNIT_CONE_ENEMY_54:
- case TARGET_UNIT_CONE_ENEMY_104:
- radius = m_spellInfo->Effects[i].CalcRadius();
- targetType = SPELL_TARGETS_ENEMY;
- break;
- case TARGET_UNIT_SRC_AREA_ALLY:
- case TARGET_UNIT_DEST_AREA_ALLY:
- case TARGET_UNIT_CONE_ALLY:
- radius = m_spellInfo->Effects[i].CalcRadius();
- targetType = SPELL_TARGETS_ALLY;
- break;
- case TARGET_UNIT_DEST_AREA_ENTRY:
- case TARGET_UNIT_SRC_AREA_ENTRY:
- case TARGET_UNIT_CONE_ENTRY: // fix me
- radius = m_spellInfo->Effects[i].CalcRadius();
- targetType = SPELL_TARGETS_ENTRY;
- break;
- case TARGET_GAMEOBJECT_SRC_AREA:
- case TARGET_GAMEOBJECT_DEST_AREA:
- case TARGET_GAMEOBJECT_CONE:
- radius = m_spellInfo->Effects[i].CalcRadius();
- targetType = SPELL_TARGETS_GO;
- break;
- default:
- radius = m_spellInfo->Effects[i].CalcRadius();
- targetType = SPELL_TARGETS_NONE;
- break;
- }
-
- if (modOwner)
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius, this);
- radius *= m_spellValue->RadiusMod;
-
- std::list<Unit*> unitList;
- std::list<GameObject*> gobjectList;
- switch (targetType)
- {
- case SPELL_TARGETS_ENTRY:
- {
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, m_spellInfo->Id);
- if (!conditions.empty())
- {
- for (ConditionList::const_iterator i_spellST = conditions.begin(); i_spellST != conditions.end(); ++i_spellST)
- {
- if ((*i_spellST)->mConditionType != CONDITION_SPELL_SCRIPT_TARGET)
- continue;
- if ((*i_spellST)->mConditionValue3 && !((*i_spellST)->mConditionValue3 & effectMask))
- continue;
- if ((*i_spellST)->mConditionValue1 == SPELL_TARGET_TYPE_CREATURE)
- SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, (*i_spellST)->mConditionValue2);
- else if ((*i_spellST)->mConditionValue1 == SPELL_TARGET_TYPE_CONTROLLED)
- {
- for (Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr)
- if ((*itr)->GetEntry() == (*i_spellST)->mConditionValue2 &&
- (*itr)->IsInMap(m_caster)) // For 60243 and 52173 need skip radius check or use range (no radius entry for effect)
- unitList.push_back(*itr);
- }
- }
- }
- else
- {
- // Custom entries
- // TODO: move these to sql
- switch (m_spellInfo->Id)
- {
- case 46584: // Raise Dead
- {
- if (WorldObject* result = FindCorpseUsing<Trinity::RaiseDeadObjectCheck> ())
- {
- switch (result->GetTypeId())
- {
- case TYPEID_UNIT:
- m_targets.SetDst(*result);
- break;
- default:
- break;
- }
- }
- break;
- }
- // Corpse Explosion
- case 49158:
- case 51325:
- case 51326:
- case 51327:
- case 51328:
- // Search for ghoul if our ghoul or dead body not valid unit target
- if (!(m_targets.GetUnitTarget() && ((m_targets.GetUnitTarget()->GetEntry() == 26125 && m_targets.GetUnitTarget()->GetOwnerGUID() == m_caster->GetGUID())
- || (m_targets.GetUnitTarget()->getDeathState() == CORPSE
- && m_targets.GetUnitTarget()->GetTypeId() == TYPEID_UNIT
- && !(m_targets.GetUnitTarget()->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL)
- && m_targets.GetUnitTarget()->GetDisplayId() == m_targets.GetUnitTarget()->GetNativeDisplayId()))))
- {
- CleanupTargetList();
-
- WorldObject* result = FindCorpseUsing <Trinity::ExplodeCorpseObjectCheck> ();
-
- if (result)
- {
- switch (result->GetTypeId())
- {
- case TYPEID_UNIT:
- case TYPEID_PLAYER:
- m_targets.SetUnitTarget((Unit*)result);
- break;
- default:
- break;
- }
- }
- else
- {
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true);
- SendCastResult(SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW);
- finish(false);
- }
- }
- break;
-
- default:
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) (caster Entry: %u) does not have type CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET record in `conditions` table.", m_spellInfo->Id, m_caster->GetEntry());
-
- if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_TELEPORT_UNITS)
- SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, 0);
- else if (m_spellInfo->IsPositiveEffect(i))
- SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ALLY);
- else
- SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENEMY);
- }
- }
- break;
- }
- case SPELL_TARGETS_GO:
- {
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, m_spellInfo->Id);
- if (!conditions.empty())
- {
- for (ConditionList::const_iterator i_spellST = conditions.begin(); i_spellST != conditions.end(); ++i_spellST)
- {
- if ((*i_spellST)->mConditionType != CONDITION_SPELL_SCRIPT_TARGET)
- continue;
- if ((*i_spellST)->mConditionValue3 && !((*i_spellST)->mConditionValue3 & effectMask))
- continue;
- if ((*i_spellST)->mConditionValue1 == SPELL_TARGET_TYPE_GAMEOBJECT)
- SearchGOAreaTarget(gobjectList, radius, pushType, SPELL_TARGETS_GO, (*i_spellST)->mConditionValue2);
- }
- }
- else
- {
- if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_ACTIVATE_OBJECT)
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) (caster Entry: %u) with SPELL_EFFECT_ACTIVATE_OBJECT does not have type CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET record in `conditions` table.", m_spellInfo->Id, m_caster->GetEntry());
- SearchGOAreaTarget(gobjectList, radius, pushType, SPELL_TARGETS_GO);
- }
- break;
- }
- case SPELL_TARGETS_ALLY:
- case SPELL_TARGETS_ENEMY:
- case SPELL_TARGETS_CHAINHEAL:
- case SPELL_TARGETS_ANY:
- SearchAreaTarget(unitList, radius, pushType, targetType);
- break;
- default:
- switch (cur.GetTarget())
- {
- case TARGET_UNIT_SRC_AREA_PARTY:
- case TARGET_UNIT_DEST_AREA_PARTY:
- m_caster->GetPartyMemberInDist(unitList, radius); //fix me
- break;
- case TARGET_UNIT_LASTTARGET_AREA_PARTY:
- m_targets.GetUnitTarget()->GetPartyMemberInDist(unitList, radius);
- break;
- case TARGET_UNIT_CASTER_AREA_PARTY:
- m_caster->GetPartyMemberInDist(unitList, radius);
- break;
- case TARGET_UNIT_CASTER_AREA_RAID:
- m_caster->GetRaidMember(unitList, radius);
- break;
- case TARGET_UNIT_TARGET_AREA_RAID_CLASS:
- {
- Player* targetPlayer = m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->GetTypeId() == TYPEID_PLAYER
- ? (Player*)m_targets.GetUnitTarget() : NULL;
-
- Group* group = targetPlayer ? targetPlayer->GetGroup() : NULL;
- if (group)
- {
- for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* Target = itr->getSource();
-
- // IsHostileTo check duel and controlled by enemy
- if (Target && targetPlayer->IsWithinDistInMap(Target, radius) && targetPlayer->getClass() == Target->getClass() && !m_caster->IsHostileTo(Target))
- AddUnitTarget(Target, effectMask);
- }
- }
- else if (m_targets.GetUnitTarget())
- AddUnitTarget(m_targets.GetUnitTarget(), effectMask);
- break;
- }
- default:
- break;
- }
- break;
- }
-
- if (!unitList.empty())
- {
- // Special target selection for smart heals and energizes
- uint32 maxSize = 0;
- int32 power = -1;
- switch (m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_GENERIC:
- switch (m_spellInfo->Id)
- {
- case 52759: // Ancestral Awakening
- case 71610: // Echoes of Light (Althor's Abacus normal version)
- case 71641: // Echoes of Light (Althor's Abacus heroic version)
- maxSize = 1;
- power = POWER_HEALTH;
- break;
- case 54968: // Glyph of Holy Light
- maxSize = m_spellInfo->MaxAffectedTargets;
- power = POWER_HEALTH;
- break;
- case 57669: // Replenishment
- // In arenas Replenishment may only affect the caster
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->InArena())
- {
- unitList.clear();
- unitList.push_back(m_caster);
- break;
- }
- maxSize = 10;
- power = POWER_MANA;
- break;
- default:
- break;
- }
- break;
- case SPELLFAMILY_PRIEST:
- if (m_spellInfo->SpellFamilyFlags[0] == 0x10000000) // Circle of Healing
- {
- maxSize = m_caster->HasAura(55675) ? 6 : 5; // Glyph of Circle of Healing
- power = POWER_HEALTH;
- }
- else if (m_spellInfo->Id == 64844) // Divine Hymn
- {
- maxSize = 3;
- power = POWER_HEALTH;
- }
- else if (m_spellInfo->Id == 64904) // Hymn of Hope
- {
- maxSize = 3;
- power = POWER_MANA;
- }
- else
- break;
-
- // Remove targets outside caster's raid
- for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
- {
- if (!(*itr)->IsInRaidWith(m_caster))
- itr = unitList.erase(itr);
- else
- ++itr;
- }
- break;
- case SPELLFAMILY_DRUID:
- if (m_spellInfo->SpellFamilyFlags[1] == 0x04000000) // Wild Growth
- {
- maxSize = m_caster->HasAura(62970) ? 6 : 5; // Glyph of Wild Growth
- power = POWER_HEALTH;
- }
- else if (m_spellInfo->SpellFamilyFlags[2] == 0x0100) // Starfall
- {
- // Remove targets not in LoS or in stealth
- for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
- {
- if ((*itr)->HasStealthAura() || (*itr)->HasInvisibilityAura() || !(*itr)->IsWithinLOSInMap(m_caster))
- itr = unitList.erase(itr);
- else
- ++itr;
- }
- break;
- }
- else
- break;
-
- // Remove targets outside caster's raid
- for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
- if (!(*itr)->IsInRaidWith(m_caster))
- itr = unitList.erase(itr);
- else
- ++itr;
- break;
- default:
- break;
- }
-
- if (maxSize && power != -1)
- {
- if (Powers(power) == POWER_HEALTH)
- {
- if (unitList.size() > maxSize)
- {
- unitList.sort(Trinity::HealthPctOrderPred());
- unitList.resize(maxSize);
- }
- }
- else
- {
- for (std::list<Unit*>::iterator itr = unitList.begin() ; itr != unitList.end();)
- if ((*itr)->getPowerType() != (Powers)power)
- itr = unitList.erase(itr);
- else
- ++itr;
-
- if (unitList.size() > maxSize)
- {
- unitList.sort(Trinity::PowerPctOrderPred((Powers)power));
- unitList.resize(maxSize);
- }
- }
- }
-
- // Other special target selection goes here
- if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
- {
- Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
- for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j)
- if ((*j)->IsAffectingSpell(m_spellInfo))
- maxTargets += (*j)->GetAmount();
-
- if (m_spellInfo->Id == 5246) //Intimidating Shout
- unitList.remove(m_targets.GetUnitTarget());
- Trinity::RandomResizeList(unitList, maxTargets);
- }
-
- CallScriptAfterUnitTargetSelectHandlers(unitList, SpellEffIndex(i));
-
- for (std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr)
- AddUnitTarget(*itr, effectMask, false);
- }
-
- if (!gobjectList.empty())
- {
- if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
- {
- Unit::AuraEffectList const& Auras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
- for (Unit::AuraEffectList::const_iterator j = Auras.begin(); j != Auras.end(); ++j)
- if ((*j)->IsAffectingSpell(m_spellInfo))
- maxTargets += (*j)->GetAmount();
-
- Trinity::RandomResizeList(gobjectList, maxTargets);
- }
- for (std::list<GameObject*>::iterator itr = gobjectList.begin(); itr != gobjectList.end(); ++itr)
- AddGOTarget(*itr, effectMask);
- }
- }
-
- return effectMask;
-}
-
void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura)
{
if (m_CastItem)
@@ -2856,20 +2899,6 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
InitExplicitTargets(*targets);
- if (Player* plrCaster = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself())
- {
- //check for special spell conditions
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL, m_spellInfo->Id);
- if (!conditions.empty())
- if (!sConditionMgr->IsPlayerMeetToConditions(plrCaster, conditions))
- {
- //SendCastResult(SPELL_FAILED_DONT_REPORT);
- SendCastResult(plrCaster, m_spellInfo, m_cast_count, SPELL_FAILED_DONT_REPORT);
- finish(false);
- return;
- }
- }
-
// Fill aura scaling information
if (m_caster->IsControlledByPlayer() && !m_spellInfo->IsPassive() && m_spellInfo->SpellLevel && !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_SCALING))
{
@@ -3762,6 +3791,27 @@ void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cas
case SPELL_FAILED_SILENCED:
data << uint32(0); // Unknown
break;
+ case SPELL_FAILED_REAGENTS:
+ {
+ uint32 missingItem = 0;
+ for (uint32 i = 0; i < MAX_SPELL_REAGENTS; i++)
+ {
+ if (spellInfo->Reagent[i] <= 0)
+ continue;
+
+ uint32 itemid = spellInfo->Reagent[i];
+ uint32 itemcount = spellInfo->ReagentCount[i];
+
+ if (!caster->HasItemCount(itemid, itemcount))
+ {
+ missingItem = itemid;
+ break;
+ }
+ }
+
+ data << uint32(missingItem); // first missing item
+ break;
+ }
default:
break;
}
@@ -4617,7 +4667,7 @@ SpellCastResult Spell::CheckCast(bool strict)
Unit::AuraEffectList const& blockSpells = m_caster->GetAuraEffectsByType(SPELL_AURA_BLOCK_SPELL_FAMILY);
for (Unit::AuraEffectList::const_iterator blockItr = blockSpells.begin(); blockItr != blockSpells.end(); ++blockItr)
- if ((*blockItr)->GetMiscValue() == m_spellInfo->SpellFamilyName)
+ if (uint32((*blockItr)->GetMiscValue()) == m_spellInfo->SpellFamilyName)
return SPELL_FAILED_SPELL_UNAVAILABLE;
bool reqCombat = true;
@@ -4693,6 +4743,25 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_DONT_REPORT;
}
+ // check spell cast conditions from database
+ {
+ ConditionSourceInfo condInfo = ConditionSourceInfo(m_caster);
+ condInfo.mConditionTargets[1] = m_targets.GetObjectTarget();
+ ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL, m_spellInfo->Id);
+ if (!conditions.empty() && !sConditionMgr->IsObjectMeetToConditions(condInfo, conditions))
+ {
+ // send error msg to player if condition failed and text message available
+ // TODO: using WorldSession::SendNotification is not blizzlike
+ if (Player* playerCaster = m_caster->ToPlayer())
+ {
+ if (playerCaster->GetSession() && condInfo.mLastFailedCondition
+ && condInfo.mLastFailedCondition->ErrorTextId)
+ playerCaster->GetSession()->SendNotification(condInfo.mLastFailedCondition->ErrorTextId);
+ }
+ return SPELL_FAILED_DONT_REPORT;
+ }
+ }
+
// Don't check explicit target for passive spells (workaround) (check should be skipped only for learn case)
// those spells may have incorrect target entries or not filled at all (for example 15332)
// such spells when learned are not targeting anyone using targeting system, they should apply directly to caster instead
@@ -4736,6 +4805,16 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
+ //Check for line of sight for spells with dest
+ if (m_targets.HasDst())
+ {
+ float x, y, z;
+ m_targets.GetDst()->GetPosition(x, y, z);
+
+ if (!(m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOS(x, y, z))
+ return SPELL_FAILED_LINE_OF_SIGHT;
+ }
+
// check pet presence
for (int j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
@@ -5266,7 +5345,7 @@ SpellCastResult Spell::CheckCast(bool strict)
{
float x, y, z;
m_caster->GetPosition(x, y, z);
- float ground_Z = m_caster->GetMap()->GetHeight(x, y, z);
+ float ground_Z = m_caster->GetMap()->GetHeight(m_caster->GetPhaseMask(), x, y, z);
if (fabs(ground_Z - z) < 0.1f)
return SPELL_FAILED_DONT_REPORT;
break;
@@ -5368,6 +5447,9 @@ SpellCastResult Spell::CheckCast(bool strict)
}
case SPELL_AURA_PERIODIC_MANA_LEECH:
{
+ if (m_spellInfo->Effects[i].IsTargetingArea())
+ break;
+
if (!m_targets.GetUnitTarget())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
@@ -5871,7 +5953,7 @@ SpellCastResult Spell::CheckItems()
}
}
if (!p_caster->HasItemCount(itemid, itemcount))
- return SPELL_FAILED_ITEM_NOT_READY; //0x54
+ return SPELL_FAILED_REAGENTS;
}
}
@@ -6554,7 +6636,7 @@ void Spell::HandleLaunchPhase()
continue;
// do not consume ammo anymore for Hunter's volley spell
- if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsAOE())
+ if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea())
usesAmmo = false;
if (usesAmmo)
@@ -6605,7 +6687,7 @@ void Spell::DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier)
if (m_damage > 0)
{
- if (m_spellInfo->Effects[i].IsArea())
+ if (m_spellInfo->Effects[i].IsTargetingArea())
{
m_damage = int32(float(m_damage) * unit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, m_spellInfo->SchoolMask));
if (m_caster->GetTypeId() == TYPEID_UNIT)
@@ -6719,150 +6801,6 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value)
}
}
-float tangent(float x)
-{
- x = tan(x);
- //if (x < std::numeric_limits<float>::max() && x > -std::numeric_limits<float>::max()) return x;
- //if (x >= std::numeric_limits<float>::max()) return std::numeric_limits<float>::max();
- //if (x <= -std::numeric_limits<float>::max()) return -std::numeric_limits<float>::max();
- if (x < 100000.0f && x > -100000.0f) return x;
- if (x >= 100000.0f) return 100000.0f;
- if (x <= 100000.0f) return -100000.0f;
- return 0.0f;
-}
-
-#define DEBUG_TRAJ(a) //a
-
-void Spell::SelectTrajTargets()
-{
- if (!m_targets.HasTraj())
- return;
-
- float dist2d = m_targets.GetDist2d();
- if (!dist2d)
- return;
-
- float srcToDestDelta = m_targets.GetDst()->m_positionZ - m_targets.GetSrc()->m_positionZ;
-
- UnitList unitList;
- SearchAreaTarget(unitList, dist2d, PUSH_IN_THIN_LINE, SPELL_TARGETS_ANY);
- if (unitList.empty())
- return;
-
- unitList.sort(Trinity::ObjectDistanceOrderPred(m_caster));
-
- float b = tangent(m_targets.GetElevation());
- float a = (srcToDestDelta - dist2d * b) / (dist2d * dist2d);
- if (a > -0.0001f)
- a = 0;
- DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: a %f b %f", a, b);)
-
- float bestDist = m_spellInfo->GetMaxRange(false);
-
- UnitList::const_iterator itr = unitList.begin();
- for (; itr != unitList.end(); ++itr)
- {
- if (m_caster == *itr || m_caster->IsOnVehicle(*itr) || (*itr)->GetVehicle())//(*itr)->IsOnVehicle(m_caster))
- continue;
-
- const float size = std::max((*itr)->GetObjectSize() * 0.7f, 1.0f); // 1/sqrt(3)
- // TODO: all calculation should be based on src instead of m_caster
- const float objDist2d = m_targets.GetSrc()->GetExactDist2d(*itr) * cos(m_targets.GetSrc()->GetRelativeAngle(*itr));
- const float dz = (*itr)->GetPositionZ() - m_targets.GetSrc()->m_positionZ;
-
- DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: check %u, dist between %f %f, height between %f %f.", (*itr)->GetEntry(), objDist2d - size, objDist2d + size, dz - size, dz + size);)
-
- float dist = objDist2d - size;
- float height = dist * (a * dist + b);
- DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: dist %f, height %f.", dist, height);)
- if (dist < bestDist && height < dz + size && height > dz - size)
- {
- bestDist = dist > 0 ? dist : 0;
- break;
- }
-
-#define CHECK_DIST {\
- DEBUG_TRAJ(sLog->outError("Spell::SelectTrajTargets: dist %f, height %f.", dist, height);)\
- if (dist > bestDist)\
- continue;\
- if (dist < objDist2d + size && dist > objDist2d - size)\
- {\
- bestDist = dist;\
- break;\
- }\
- }
-
- if (!a)
- {
- height = dz - size;
- dist = height / b;
- CHECK_DIST;
-
- height = dz + size;
- dist = height / b;
- CHECK_DIST;
-
- continue;
- }
-
- height = dz - size;
- float sqrt1 = b * b + 4 * a * height;
- if (sqrt1 > 0)
- {
- sqrt1 = sqrt(sqrt1);
- dist = (sqrt1 - b) / (2 * a);
- CHECK_DIST;
- }
-
- height = dz + size;
- float sqrt2 = b * b + 4 * a * height;
- if (sqrt2 > 0)
- {
- sqrt2 = sqrt(sqrt2);
- dist = (sqrt2 - b) / (2 * a);
- CHECK_DIST;
-
- dist = (-sqrt2 - b) / (2 * a);
- CHECK_DIST;
- }
-
- if (sqrt1 > 0)
- {
- dist = (-sqrt1 - b) / (2 * a);
- CHECK_DIST;
- }
- }
-
- if (m_targets.GetSrc()->GetExactDist2d(m_targets.GetDst()) > bestDist)
- {
- float x = m_targets.GetSrc()->m_positionX + cos(m_caster->GetOrientation()) * bestDist;
- float y = m_targets.GetSrc()->m_positionY + sin(m_caster->GetOrientation()) * bestDist;
- float z = m_targets.GetSrc()->m_positionZ + bestDist * (a * bestDist + b);
-
- if (itr != unitList.end())
- {
- float distSq = (*itr)->GetExactDistSq(x, y, z);
- float sizeSq = (*itr)->GetObjectSize();
- sizeSq *= sizeSq;
- DEBUG_TRAJ(sLog->outError("Initial %f %f %f %f %f", x, y, z, distSq, sizeSq);)
- if (distSq > sizeSq)
- {
- float factor = 1 - sqrt(sizeSq / distSq);
- x += factor * ((*itr)->GetPositionX() - x);
- y += factor * ((*itr)->GetPositionY() - y);
- z += factor * ((*itr)->GetPositionZ() - z);
-
- distSq = (*itr)->GetExactDistSq(x, y, z);
- DEBUG_TRAJ(sLog->outError("Initial %f %f %f %f %f", x, y, z, distSq, sizeSq);)
- }
- }
-
- Position trajDst;
- trajDst.Relocate(x, y, z, m_caster->GetOrientation());
- m_targets.ModDst(trajDst);
- }
-}
-
void Spell::PrepareTargetProcessing()
{
CheckEffectExecuteData();
@@ -7214,3 +7152,152 @@ void Spell::CancelGlobalCooldown()
else if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo);
}
+
+namespace Trinity
+{
+
+WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo,
+ SpellTargetCheckTypes selectionType, ConditionList* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo),
+ _targetSelectionType(selectionType), _condList(condList)
+{
+ if (condList)
+ _condSrcInfo = new ConditionSourceInfo(NULL, caster);
+ else
+ _condSrcInfo = NULL;
+}
+
+WorldObjectSpellTargetCheck::~WorldObjectSpellTargetCheck()
+{
+ if (_condSrcInfo)
+ delete _condSrcInfo;
+}
+
+bool WorldObjectSpellTargetCheck::operator()(WorldObject* target)
+{
+ if (_spellInfo->CheckTarget(_caster, target, true) != SPELL_CAST_OK)
+ return false;
+ Unit* unitTarget = target->ToUnit();
+ if (Corpse* corpseTarget = target->ToCorpse())
+ {
+ // use ofter for party/assistance checks
+ if (Player* owner = ObjectAccessor::FindPlayer(corpseTarget->GetOwnerGUID()))
+ unitTarget = owner;
+ else
+ return false;
+ }
+ if (unitTarget)
+ {
+ switch (_targetSelectionType)
+ {
+ case TARGET_CHECK_ENEMY:
+ if (unitTarget->isTotem())
+ return false;
+ if (!_caster->_IsValidAttackTarget(unitTarget, _spellInfo))
+ return false;
+ break;
+ case TARGET_CHECK_ALLY:
+ if (unitTarget->isTotem())
+ return false;
+ if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
+ return false;
+ break;
+ case TARGET_CHECK_PARTY:
+ if (unitTarget->isTotem())
+ return false;
+ if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
+ return false;
+ if (!_referer->IsInPartyWith(unitTarget))
+ return false;
+ break;
+ case TARGET_CHECK_RAID_CLASS:
+ if (_referer->getClass() != unitTarget->getClass())
+ return false;
+ // nobreak;
+ case TARGET_CHECK_RAID:
+ if (unitTarget->isTotem())
+ return false;
+ if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
+ return false;
+ if (!_referer->IsInRaidWith(unitTarget))
+ return false;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!_condSrcInfo)
+ return true;
+ _condSrcInfo->mConditionTargets[0] = target;
+ return sConditionMgr->IsObjectMeetToConditions(*_condSrcInfo, *_condList);
+}
+
+WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo,
+ SpellTargetCheckTypes selectionType, ConditionList* condList)
+ : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _range(range), _position(caster)
+{
+}
+
+bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target)
+{
+ float dist = target->GetDistance(*_position);
+ if (dist < _range && WorldObjectSpellTargetCheck::operator ()(target))
+ {
+ _range = dist;
+ return true;
+ }
+ return false;
+}
+
+WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
+ Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
+ : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position)
+{
+}
+
+bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target)
+{
+ if (!target->IsWithinDist3d(_position, _range))
+ return false;
+ return WorldObjectSpellTargetCheck::operator ()(target);
+}
+
+WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster,
+ SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
+ : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList), _coneAngle(coneAngle)
+{
+}
+
+bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target)
+{
+ if (_spellInfo->AttributesCu & SPELL_ATTR0_CU_CONE_BACK)
+ {
+ if (!_caster->isInBack(target, _coneAngle))
+ return false;
+ }
+ else if (_spellInfo->AttributesCu & SPELL_ATTR0_CU_CONE_LINE)
+ {
+ if (!_caster->HasInLine(target, _caster->GetObjectSize()))
+ return false;
+ }
+ else
+ {
+ if (!_caster->isInFront(target, _coneAngle))
+ return false;
+ }
+ return WorldObjectSpellAreaTargetCheck::operator ()(target);
+}
+
+WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, SpellInfo const* spellInfo)
+ : WorldObjectSpellAreaTargetCheck(range, position, caster, caster, spellInfo, TARGET_CHECK_DEFAULT, NULL)
+{
+}
+
+bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target)
+{
+ // return all targets on missile trajectory (0 - size of a missile)
+ if (!_caster->HasInLine(target, 0))
+ return false;
+ return WorldObjectSpellAreaTargetCheck::operator ()(target);
+}
+
+} //namespace Trinity
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index dc3a95030ae..dd377d66feb 100755
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -79,19 +79,6 @@ enum SpellRangeFlag
SPELL_RANGE_RANGED = 2, //hunter range and ranged weapon
};
-enum SpellNotifyPushType
-{
- PUSH_NONE = 0,
- PUSH_IN_FRONT,
- PUSH_IN_BACK,
- PUSH_IN_LINE,
- PUSH_IN_THIN_LINE,
- PUSH_SRC_CENTER,
- PUSH_DST_CENTER,
- PUSH_CASTER_CENTER, //this is never used in grid search
- PUSH_CHAIN,
-};
-
class SpellCastTargets
{
public:
@@ -210,17 +197,6 @@ enum SpellEffectHandleMode
SPELL_EFFECT_HANDLE_HIT_TARGET,
};
-enum SpellTargets
-{
- SPELL_TARGETS_NONE = 0,
- SPELL_TARGETS_ALLY,
- SPELL_TARGETS_ENEMY,
- SPELL_TARGETS_ENTRY,
- SPELL_TARGETS_CHAINHEAL,
- SPELL_TARGETS_ANY,
- SPELL_TARGETS_GO
-};
-
namespace Trinity
{
struct SpellNotifierCreatureAndPlayer;
@@ -228,7 +204,6 @@ namespace Trinity
class Spell
{
- friend struct Trinity::SpellNotifierCreatureAndPlayer;
friend void Unit::SetCurrentCastedSpell(Spell* pSpell);
friend class SpellScript;
public:
@@ -364,6 +339,32 @@ class Spell
Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, uint64 originalCasterGUID = 0, bool skipCheck = false);
~Spell();
+ void InitExplicitTargets(SpellCastTargets const& targets);
+ void SelectExplicitTargets();
+
+ void SelectSpellTargets();
+ void SelectEffectImplicitTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32& processedEffectMask);
+ void SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask);
+ void SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask);
+ void SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask);
+ void SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void SelectImplicitDestDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void SelectImplicitTargetObjectTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, WorldObject* target, uint32 effMask);
+ void SelectImplicitTrajTargets();
+
+ void SelectEffectTypeImplicitTargets(uint8 effIndex);
+
+ uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* condList);
+ template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius);
+
+ WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList = NULL);
+ void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList);
+ void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionList* condList, bool isChainHeal);
+
void prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = NULL);
void cancel();
void update(uint32 difftime);
@@ -403,14 +404,6 @@ class Spell
void DoCreateItem(uint32 i, uint32 itemtype);
void WriteSpellGoTargets(WorldPacket* data);
- void InitExplicitTargets(SpellCastTargets const& targets);
- void SelectSpellTargets();
- void SelectEffectTypeImplicitTargets(uint8 effIndex);
- uint32 SelectEffectTargets(uint32 i, SpellImplicitTargetInfo const& cur);
- void SelectTrajTargets();
-
- template<typename T> WorldObject* FindCorpseUsing();
-
bool CheckEffectTarget(Unit const* target, uint32 eff) const;
bool CanAutoCast(Unit* target);
void CheckSrc() { if (!m_targets.HasSrc()) m_targets.SetSrc(*m_caster); }
@@ -605,10 +598,6 @@ class Spell
void DoAllEffectOnTarget(GOTargetInfo* target);
void DoAllEffectOnTarget(ItemTargetInfo* target);
bool UpdateChanneledTargetList();
- void SearchAreaTarget(std::list<Unit*> &unitList, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry = 0);
- void SearchGOAreaTarget(std::list<GameObject*> &gobjectList, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry = 0);
- void SearchChainTarget(std::list<Unit*> &unitList, float radius, uint32 unMaxTargets, SpellTargets TargetType);
- WorldObject* SearchNearbyTarget(float range, SpellTargets TargetType, SpellEffIndex effIndex);
bool IsValidDeadOrAliveTarget(Unit const* target) const;
void HandleLaunchPhase();
void DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier);
@@ -642,7 +631,7 @@ class Spell
// effect helpers
void GetSummonPosition(uint32 i, Position &pos, float radius = 0.0f, uint32 count = 0);
- void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties);
+ void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numSummons);
void CalculateJumpSpeeds(uint8 i, float dist, float & speedxy, float & speedz);
SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue);
@@ -674,98 +663,52 @@ class Spell
namespace Trinity
{
- struct SpellNotifierCreatureAndPlayer
+ struct WorldObjectSpellTargetCheck
{
- std::list<Unit*> *i_data;
- SpellNotifyPushType i_push_type;
- float i_radius;
- SpellTargets i_TargetType;
- const Unit* const i_source;
- uint32 i_entry;
- const Position* const i_pos;
- SpellInfo const* i_spellProto;
-
- SpellNotifierCreatureAndPlayer(Unit* source, std::list<Unit*> &data, float radius, SpellNotifyPushType type,
- SpellTargets TargetType = SPELL_TARGETS_ENEMY, const Position* pos = NULL, uint32 entry = 0, SpellInfo const* spellProto = NULL)
- : i_data(&data), i_push_type(type), i_radius(radius), i_TargetType(TargetType),
- i_source(source), i_entry(entry), i_pos(pos), i_spellProto(spellProto)
- {
- ASSERT(i_source);
- }
+ Unit* _caster;
+ Unit* _referer;
+ SpellInfo const* _spellInfo;
+ SpellTargetCheckTypes _targetSelectionType;
+ ConditionSourceInfo* _condSrcInfo;
+ ConditionList* _condList;
+
+ WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo,
+ SpellTargetCheckTypes selectionType, ConditionList* condList);
+ ~WorldObjectSpellTargetCheck();
+ bool operator()(WorldObject* target);
+ };
- template<class T> inline void Visit(GridRefManager<T>& m)
- {
- for (typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr)
- {
- Unit* target = (Unit*)itr->getSource();
-
- if (i_spellProto->CheckTarget(i_source, target, true) != SPELL_CAST_OK)
- continue;
-
- switch (i_TargetType)
- {
- case SPELL_TARGETS_ENEMY:
- if (target->isTotem())
- continue;
- if (!i_source->_IsValidAttackTarget(target, i_spellProto))
- continue;
- break;
- case SPELL_TARGETS_ALLY:
- if (target->isTotem())
- continue;
- if (!i_source->_IsValidAssistTarget(target, i_spellProto))
- continue;
- break;
- case SPELL_TARGETS_ENTRY:
- if (target->GetEntry()!= i_entry)
- continue;
- break;
- case SPELL_TARGETS_ANY:
- default:
- break;
- }
-
- switch (i_push_type)
- {
- case PUSH_SRC_CENTER:
- case PUSH_DST_CENTER:
- case PUSH_CHAIN:
- default:
- if (target->IsWithinDist3d(i_pos, i_radius))
- i_data->push_back(target);
- break;
- case PUSH_IN_FRONT:
- if (i_source->isInFront(target, i_radius, static_cast<float>(M_PI/2)))
- i_data->push_back(target);
- break;
- case PUSH_IN_BACK:
- if (i_source->isInBack(target, i_radius, static_cast<float>(M_PI/2)))
- i_data->push_back(target);
- break;
- case PUSH_IN_LINE:
- if (i_source->HasInLine(target, i_radius, i_source->GetObjectSize()))
- i_data->push_back(target);
- break;
- case PUSH_IN_THIN_LINE: // only traj
- if (i_pos->HasInLine(target, i_radius, 0))
- i_data->push_back(target);
- break;
- }
- }
- }
+ struct WorldObjectSpellNearbyTargetCheck : public WorldObjectSpellTargetCheck
+ {
+ float _range;
+ Position const* _position;
+ WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo,
+ SpellTargetCheckTypes selectionType, ConditionList* condList);
+ bool operator()(WorldObject* target);
+ };
+
+ struct WorldObjectSpellAreaTargetCheck : public WorldObjectSpellTargetCheck
+ {
+ float _range;
+ Position const* _position;
+ WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
+ Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
+ bool operator()(WorldObject* target);
+ };
- #ifdef _WIN32
- template<> inline void Visit(CorpseMapType &) {}
- template<> inline void Visit(GameObjectMapType &) {}
- template<> inline void Visit(DynamicObjectMapType &) {}
- #endif
+ struct WorldObjectSpellConeTargetCheck : public WorldObjectSpellAreaTargetCheck
+ {
+ float _coneAngle;
+ WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster,
+ SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
+ bool operator()(WorldObject* target);
};
- #ifndef _WIN32
- template<> inline void SpellNotifierCreatureAndPlayer::Visit(CorpseMapType&) {}
- template<> inline void SpellNotifierCreatureAndPlayer::Visit(GameObjectMapType&) {}
- template<> inline void SpellNotifierCreatureAndPlayer::Visit(DynamicObjectMapType&) {}
- #endif
+ struct WorldObjectSpellTrajTargetCheck : public WorldObjectSpellAreaTargetCheck
+ {
+ WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, SpellInfo const* spellInfo);
+ bool operator()(WorldObject* target);
+ };
}
typedef void(Spell::*pEffect)(SpellEffIndex effIndex);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 6ea53fcfd8b..271d4859c4c 100755
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -764,600 +764,6 @@ void Spell::EffectDummy(SpellEffIndex effIndex)
// selection by spell family
switch (m_spellInfo->SpellFamilyName)
{
- case SPELLFAMILY_GENERIC:
- {
- switch (m_spellInfo->Id)
- {
- case 31225: // Shimmering Vessel (restore creature to life)
- {
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
- return;
- unitTarget->ToCreature()->setDeathState(JUST_ALIVED);
- return;
- }
- case 12162: // Deep wounds
- case 12850: // (now good common check for this spells)
- case 12868:
- {
- if (!unitTarget)
- return;
-
- // apply percent damage mods
- damage = m_caster->SpellDamageBonus(unitTarget, m_spellInfo, damage, SPELL_DIRECT_DAMAGE);
-
- switch (m_spellInfo->Id)
- {
- case 12162: ApplyPctN(damage, 16); break; // Rank 1
- case 12850: ApplyPctN(damage, 32); break; // Rank 2
- case 12868: ApplyPctN(damage, 48); break; // Rank 3
- default:
- sLog->outError("Spell::EffectDummy: Spell %u not handled in DW", m_spellInfo->Id);
- return;
- }
-
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(12721);
- uint32 ticks = spellInfo->GetDuration() / spellInfo->Effects[EFFECT_0].Amplitude;
-
- // Add remaining ticks to damage done
- if (AuraEffect const* aurEff = unitTarget->GetAuraEffect(12721, EFFECT_0, m_caster->GetGUID()))
- damage += aurEff->GetAmount() * (ticks - aurEff->GetTickNumber());
-
- damage = damage / ticks;
- m_caster->CastCustomSpell(unitTarget, 12721, &damage, NULL, NULL, true);
- return;
- }
- case 13567: // Dummy Trigger
- {
- // can be used for different aura triggering, so select by aura
- if (!m_triggeredByAuraSpell || !unitTarget)
- return;
-
- switch (m_triggeredByAuraSpell->Id)
- {
- case 26467: // Persistent Shield
- m_caster->CastCustomSpell(unitTarget, 26470, &damage, NULL, NULL, true);
- break;
- default:
- sLog->outError("EffectDummy: Non-handled case for spell 13567 for triggered aura %u", m_triggeredByAuraSpell->Id);
- break;
- }
- return;
- }
- case 17251: // Spirit Healer Res
- {
- if (!unitTarget || !m_originalCaster)
- return;
-
- if (m_originalCaster->GetTypeId() == TYPEID_PLAYER)
- {
- WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8);
- data << uint64(unitTarget->GetGUID());
- m_originalCaster->ToPlayer()->GetSession()->SendPacket(&data);
- }
- return;
- }
- case 23019: // Crystal Prison Dummy DND
- {
- if (!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || unitTarget->ToCreature()->isPet())
- return;
-
- Creature* creatureTarget = unitTarget->ToCreature();
-
- m_caster->SummonGameObject(179644, creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), creatureTarget->GetOrientation(), 0, 0, 0, 0, uint32(creatureTarget->GetRespawnTime()-time(NULL)));
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SummonGameObject at SpellEfects.cpp EffectDummy for Spell 23019");
-
- creatureTarget->DespawnOrUnsummon();
-
- return;
- }
- case 23448: // Transporter Arrival - Ultrasafe Transporter: Gadgetzan - backfires
- {
- int32 r = irand(0, 119);
- if (r < 20) // Transporter Malfunction - 1/6 polymorph
- m_caster->CastSpell(m_caster, 23444, true);
- else if (r < 100) // Evil Twin - 4/6 evil twin
- m_caster->CastSpell(m_caster, 23445, true);
- else // Transporter Malfunction - 1/6 miss the target
- m_caster->CastSpell(m_caster, 36902, true);
- return;
- }
- case 23453: // Gnomish Transporter - Ultrasafe Transporter: Gadgetzan
- if (roll_chance_i(50)) // Gadgetzan Transporter - success
- m_caster->CastSpell(m_caster, 23441, true);
- else // Gadgetzan Transporter Failure - failure
- m_caster->CastSpell(m_caster, 23446, true);
- return;
- case 25860: // Reindeer Transformation
- {
- if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
- return;
-
- float flyspeed = m_caster->GetSpeedRate(MOVE_FLIGHT);
- float speed = m_caster->GetSpeedRate(MOVE_RUN);
-
- m_caster->RemoveAurasByType(SPELL_AURA_MOUNTED);
-
- //5 different spells used depending on mounted speed and if mount can fly or not
- if (flyspeed >= 4.1f)
- // Flying Reindeer
- m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer
- else if (flyspeed >= 3.8f)
- // Flying Reindeer
- m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer
- else if (flyspeed >= 1.6f)
- // Flying Reindeer
- m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer
- else if (speed >= 2.0f)
- // Reindeer
- m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer
- else
- // Reindeer
- m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer
-
- return;
- }
- case 26074: // Holiday Cheer
- // implemented at client side
- return;
- // Polarity Shift
- case 28089:
- if (unitTarget)
- unitTarget->CastSpell(unitTarget, roll_chance_i(50) ? 28059 : 28084, true, NULL, NULL, m_caster->GetGUID());
- break;
- // Polarity Shift
- case 39096:
- if (unitTarget)
- unitTarget->CastSpell(unitTarget, roll_chance_i(50) ? 39088 : 39091, true, NULL, NULL, m_caster->GetGUID());
- break;
- case 29200: // Purify Helboar Meat
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- spell_id = roll_chance_i(50)
- ? 29277 // Summon Purified Helboar Meat
- : 29278; // Summon Toxic Helboar Meat
-
- m_caster->CastSpell(m_caster, spell_id, true, NULL);
- return;
- }
- case 29858: // Soulshatter
- if (unitTarget && unitTarget->CanHaveThreatList()
- && unitTarget->getThreatManager().getThreat(m_caster) > 0.0f)
- m_caster->CastSpell(unitTarget, 32835, true);
- return;
- case 30458: // Nigh Invulnerability
- if (!m_CastItem) return;
- if (roll_chance_i(86)) // Nigh-Invulnerability - success
- m_caster->CastSpell(m_caster, 30456, true, m_CastItem);
- else // Complete Vulnerability - backfire in 14% casts
- m_caster->CastSpell(m_caster, 30457, true, m_CastItem);
- return;
- case 30507: // Poultryizer
- if (!m_CastItem) return;
- if (roll_chance_i(80)) // Poultryized! - success
- m_caster->CastSpell(unitTarget, 30501, true, m_CastItem);
- else // Poultryized! - backfire 20%
- m_caster->CastSpell(unitTarget, 30504, true, m_CastItem);
- return;
- case 35745: // Socrethar's Stone
- {
- switch (m_caster->GetAreaId())
- {
- case 3900:
- spell_id = 35743;
- break; // Socrethar Portal
- case 3742:
- spell_id = 35744;
- break; // Socrethar Portal
- default:
- return;
- }
-
- m_caster->CastSpell(m_caster, spell_id, true);
- return;
- }
- case 37674: // Chaos Blast
- {
- if (!unitTarget)
- return;
-
- int32 basepoints0 = 100;
- m_caster->CastCustomSpell(unitTarget, 37675, &basepoints0, NULL, NULL, true);
- return;
- }
- // Wrath of the Astromancer
- case 42784:
- {
- uint32 count = 0;
- for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
- if (ihit->effectMask & (1<<effIndex))
- ++count;
-
- damage = 12000; // maybe wrong value
- damage /= count;
-
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(42784);
-
- // now deal the damage
- for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
- if (ihit->effectMask & (1<<effIndex))
- {
- if (Unit* casttarget = Unit::GetUnit((*unitTarget), ihit->targetGUID))
- m_caster->DealDamage(casttarget, damage, NULL, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_ARCANE, spellInfo, false);
- }
-
- return;
- }
- // Demon Broiled Surprise
- case 43723:
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)m_caster;
-
- if (player && player->GetQuestStatus(11379) == QUEST_STATUS_INCOMPLETE)
- {
- Creature* creature = player->FindNearestCreature(19973, 10, false);
- if (!creature)
- {
- SendCastResult(SPELL_FAILED_NOT_HERE);
- return;
- }
-
- player->CastSpell(player, 43753, false);
- }
- return;
- }
- case 44875: // Complete Raptor Capture
- {
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
- return;
-
- unitTarget->ToCreature()->DespawnOrUnsummon();
-
- //cast spell Raptor Capture Credit
- m_caster->CastSpell(m_caster, 42337, true, NULL);
- return;
- }
- case 47170: // Impale Leviroth
- {
- if (!unitTarget || (unitTarget->GetEntry() != 26452 && unitTarget->HealthAbovePct(95)))
- return;
-
- m_caster->DealDamage(unitTarget, unitTarget->CountPctFromMaxHealth(93));
- return;
- }
- case 49357: // Brewfest Mount Transformation
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
- if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
- return;
- m_caster->RemoveAurasByType(SPELL_AURA_MOUNTED);
- // Ram for Alliance, Kodo for Horde
- if (m_caster->ToPlayer()->GetTeam() == ALLIANCE)
- {
- if (m_caster->GetSpeedRate(MOVE_RUN) >= 2.0f)
- // 100% Ram
- m_caster->CastSpell(m_caster, 43900, true);
- else
- // 60% Ram
- m_caster->CastSpell(m_caster, 43899, true);
- }
- else
- {
- if (m_caster->ToPlayer()->GetSpeedRate(MOVE_RUN) >= 2.0f)
- // 100% Kodo
- m_caster->CastSpell(m_caster, 49379, true);
- else
- // 60% Kodo
- m_caster->CastSpell(m_caster, 49378, true);
- }
- return;
- case 52845: // Brewfest Mount Transformation (Faction Swap)
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
- if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
- return;
- m_caster->RemoveAurasByType(SPELL_AURA_MOUNTED);
- // Ram for Horde, Kodo for Alliance
- if (m_caster->ToPlayer()->GetTeam() == HORDE)
- {
- if (m_caster->GetSpeedRate(MOVE_RUN) >= 2.0f)
- // 100% Ram
- m_caster->CastSpell(m_caster, 43900, true);
- else
- // 60% Ram
- m_caster->CastSpell(m_caster, 43899, true);
- }
- else
- {
- if (m_caster->ToPlayer()->GetSpeedRate(MOVE_RUN) >= 2.0f)
- // 100% Kodo
- m_caster->CastSpell(m_caster, 49379, true);
- else
- // 60% Kodo
- m_caster->CastSpell(m_caster, 49378, true);
- }
- return;
- case 55004: // Nitro Boosts
- if (!m_CastItem)
- return;
- if (roll_chance_i(95)) // Nitro Boosts - success
- m_caster->CastSpell(m_caster, 54861, true, m_CastItem);
- else // Knocked Up - backfire 5%
- m_caster->CastSpell(m_caster, 46014, true, m_CastItem);
- return;
- case 50243: // Teach Language
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // spell has a 1/3 chance to trigger one of the below
- if (roll_chance_i(66))
- return;
- if (m_caster->ToPlayer()->GetTeam() == ALLIANCE)
- {
- // 1000001 - gnomish binary
- m_caster->CastSpell(m_caster, 50242, true);
- }
- else
- {
- // 01001000 - goblin binary
- m_caster->CastSpell(m_caster, 50246, true);
- }
-
- return;
- }
- case 51582: //Rocket Boots Engaged (Rocket Boots Xtreme and Rocket Boots Xtreme Lite)
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if (Battleground* bg = m_caster->ToPlayer()->GetBattleground())
- bg->EventPlayerDroppedFlag(m_caster->ToPlayer());
-
- m_caster->CastSpell(m_caster, 30452, true, NULL);
- return;
- }
- case 52759: // Ancestral Awakening
- if (!unitTarget)
- return;
- m_caster->CastCustomSpell(unitTarget, 52752, &damage, NULL, NULL, true);
- return;
- case 54171: // Divine Storm
- {
- if (m_UniqueTargetInfo.size())
- {
- int32 heal = damage / m_UniqueTargetInfo.size();
- m_caster->CastCustomSpell(unitTarget, 54172, &heal, NULL, NULL, true);
- }
- return;
- }
- case 58418: // Portal to Orgrimmar
- case 58420: // Portal to Stormwind
- return; // implemented in EffectScript[0]
- case 62324: // Throw Passenger
- {
- if (m_targets.HasTraj())
- {
- if (Vehicle* vehicle = m_caster->GetVehicleKit())
- if (Unit* passenger = vehicle->GetPassenger(damage - 1))
- {
- std::list<Unit*> unitList;
- // use 99 because it is 3d search
- SearchAreaTarget(unitList, 99, PUSH_DST_CENTER, SPELL_TARGETS_ENTRY, 33114);
- float minDist = 99 * 99;
- Unit* target = NULL;
- for (std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr)
- {
- if (Vehicle* seat = (*itr)->GetVehicleKit())
- if (!seat->GetPassenger(0))
- if (Unit* device = seat->GetPassenger(2))
- if (!device->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
- {
- float dist = (*itr)->GetExactDistSq(m_targets.GetDst());
- if (dist < minDist)
- {
- minDist = dist;
- target = (*itr);
- }
- }
- }
- if (target && target->IsWithinDist2d(m_targets.GetDst(), m_spellInfo->Effects[effIndex].CalcRadius() * 2)) // now we use *2 because the location of the seat is not correct
- passenger->EnterVehicle(target, 0);
- else
- {
- passenger->ExitVehicle();
- float x, y, z;
- m_targets.GetDst()->GetPosition(x, y, z);
- passenger->GetMotionMaster()->MoveJump(x, y, z, m_targets.GetSpeedXY(), m_targets.GetSpeedZ());
- }
- }
- }
- return;
- }
- case 64385: // Unusual Compass
- {
- m_caster->SetOrientation(float(urand(0, 62832)) / 10000.0f);
- WorldPacket data;
- m_caster->BuildHeartBeatMsg(&data);
- m_caster->SendMessageToSet(&data, true);
- return;
- }
- case 53808: // Pygmy Oil
- {
- Aura* pAura = m_caster->GetAura(53806);
- if (pAura)
- pAura->RefreshDuration();
- else
- {
- pAura = m_caster->GetAura(53805);
- if (!pAura || pAura->GetStackAmount() < 5 || !roll_chance_i(50))
- m_caster->CastSpell(m_caster, 53805, true);
- else
- {
- pAura->Remove();
- m_caster->CastSpell(m_caster, 53806, true);
- }
- }
- return;
- }
- case 54577: // U.D.E.D.
- {
- if (unitTarget->GetEntry() != 29402)
- return;
-
- m_caster->SummonGameObject(192693, unitTarget->GetPositionX(), unitTarget->GetPositionY(),
- unitTarget->GetPositionZ(), unitTarget->GetOrientation(), 0, 0, 0, 0, 100);
-
- for (uint8 i = 0; i < 4; ++i)
- m_caster->SummonGameObject(191567, float(unitTarget->GetPositionX() + irand(-7, 7)),
- float(unitTarget->GetPositionY() + irand(-7, 7)), unitTarget->GetPositionZ(), unitTarget->GetOrientation(),
- 0, 0, 0, 0, 100);
-
- unitTarget->Kill(unitTarget);
- return;
- }
- case 51961: // Captured Chicken Cover - Quest 12702 & 12532
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER
- || !unitTarget->HasAura(51959)
- || !(m_caster->ToPlayer()->GetQuestStatus(12702) == QUEST_STATUS_INCOMPLETE || m_caster->ToPlayer()->GetQuestStatus(12532) == QUEST_STATUS_INCOMPLETE))
- return;
-
- m_caster->CastSpell(m_caster, 51037, true);
- unitTarget->Kill(unitTarget);
- return;
- }
- }
-
- break;
- }
- case SPELLFAMILY_WARRIOR:
- // Charge
- if (m_spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_WARRIOR_CHARGE && m_spellInfo->SpellVisual[0] == 867)
- {
- int32 chargeBasePoints0 = damage;
- m_caster->CastCustomSpell(m_caster, 34846, &chargeBasePoints0, NULL, NULL, true);
-
- //Juggernaut crit bonus
- if (m_caster->HasAura(64976))
- m_caster->CastSpell(m_caster, 65156, true);
- return;
- }
- // Slam
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_WARRIOR_SLAM && m_spellInfo->SpellIconID == 559)
- {
- int32 bp0 = damage;
- m_caster->CastCustomSpell(unitTarget, 50783, &bp0, NULL, NULL, true, 0);
- return;
- }
- // Execute
- if (m_spellInfo->SpellFamilyFlags[EFFECT_0] & SPELLFAMILYFLAG_WARRIOR_EXECUTE)
- {
- if (!unitTarget)
- return;
-
- spell_id = 20647;
-
- int32 rageUsed = std::min<int32>(300 - m_powerCost, m_caster->GetPower(POWER_RAGE));
- int32 newRage = std::max<int32>(0, m_caster->GetPower(POWER_RAGE) - rageUsed);
-
- // Sudden Death rage save
- if (AuraEffect* aurEff = m_caster->GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_GENERIC, 1989, EFFECT_0))
- {
- int32 ragesave = aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue() * 10;
- newRage = std::max(newRage, ragesave);
- }
-
- m_caster->SetPower(POWER_RAGE, uint32(newRage));
-
- // Glyph of Execution bonus
- if (AuraEffect* aurEff = m_caster->GetAuraEffect(58367, EFFECT_0))
- rageUsed += aurEff->GetAmount() * 10;
-
- bp = damage + int32(rageUsed * m_spellInfo->Effects[effIndex].DamageMultiplier + m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.2f);
- break;
- }
- // Concussion Blow
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_WARRIOR_CONCUSSION_BLOW)
- {
- m_damage += CalculatePctF(damage, m_caster->GetTotalAttackPowerValue(BASE_ATTACK));
- return;
- }
- switch (m_spellInfo->Id)
- {
- // Bloodthirst
- case 23881:
- {
- m_caster->CastCustomSpell(unitTarget, 23885, &damage, NULL, NULL, true, NULL);
- return;
- }
- }
- break;
- case SPELLFAMILY_WARLOCK:
- // Life Tap
- if ((m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_WARLOCK_LIFETAP) && m_caster->ToPlayer())
- {
- float spFactor = 0.0f;
- switch (m_spellInfo->Id)
- {
- case 11689: spFactor = 0.2f; break;
- case 27222:
- case 57946: spFactor = 0.5f; break;
- }
- int32 damage = int32(m_spellInfo->Effects[EFFECT_0].CalcValue() + (6.3875 * m_spellInfo->BaseLevel));
- int32 mana = int32(damage + (m_caster->ToPlayer()->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+SPELL_SCHOOL_SHADOW) * spFactor));
-
- if (unitTarget && (int32(unitTarget->GetHealth()) > damage))
- {
- // Shouldn't Appear in Combat Log
- unitTarget->ModifyHealth(-damage);
-
- // Improved Life Tap mod
- if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_WARLOCK, 208, 0))
- AddPctN(mana, aurEff->GetAmount());
-
- m_caster->CastCustomSpell(unitTarget, 31818, &mana, NULL, NULL, false);
-
- // Mana Feed
- int32 manaFeedVal = 0;
- if (AuraEffect const* aurEff = m_caster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, 1982, 0))
- manaFeedVal = aurEff->GetAmount();
-
- if (manaFeedVal > 0)
- {
- ApplyPctN(manaFeedVal, mana);
- m_caster->CastCustomSpell(m_caster, 32553, &manaFeedVal, NULL, NULL, true, NULL);
- }
- }
- else
- SendCastResult(SPELL_FAILED_FIZZLE);
- return;
- }
- break;
- case SPELLFAMILY_DRUID:
- // Starfall
- if (m_spellInfo->SpellFamilyFlags[2] & SPELLFAMILYFLAG2_DRUID_STARFALL)
- {
- //Shapeshifting into an animal form or mounting cancels the effect.
- if (m_caster->GetCreatureType() == CREATURE_TYPE_BEAST || m_caster->IsMounted())
- {
- if (m_triggeredByAuraSpell)
- m_caster->RemoveAurasDueToSpell(m_triggeredByAuraSpell->Id);
- return;
- }
-
- //Any effect which causes you to lose control of your character will supress the starfall effect.
- if (m_caster->HasUnitState(UNIT_STATE_CONTROLLED))
- return;
-
- m_caster->CastSpell(unitTarget, damage, true);
- return;
- }
- break;
case SPELLFAMILY_PALADIN:
switch (m_spellInfo->Id)
{
@@ -1391,128 +797,40 @@ void Spell::EffectDummy(SpellEffIndex effIndex)
}
}
break;
- case SPELLFAMILY_SHAMAN:
- // Cleansing Totem Pulse
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_SHAMAN_TOTEM_EFFECTS && m_spellInfo->SpellIconID == 1673)
- {
- int32 bp1 = 1;
- // Cleansing Totem Effect
- if (unitTarget)
- m_caster->CastCustomSpell(unitTarget, 52025, NULL, &bp1, NULL, true, NULL, NULL, m_originalCasterGUID);
- return;
- }
- // Healing Stream Totem
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_SHAMAN_HEALING_STREAM)
- {
- if (!unitTarget)
- return;
- if (Unit* owner = m_caster->GetOwner())
- {
- if (m_triggeredByAuraSpell)
- damage = int32(owner->SpellHealingBonus(unitTarget, m_triggeredByAuraSpell, damage, HEAL));
-
- // Restorative Totems
- if (AuraEffect* dummy = owner->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 338, 1))
- AddPctN(damage, dummy->GetAmount());
-
- // Glyph of Healing Stream Totem
- if (AuraEffect const* aurEff = owner->GetAuraEffect(55456, EFFECT_0))
- AddPctN(damage, aurEff->GetAmount());
- }
- m_caster->CastCustomSpell(unitTarget, 52042, &damage, 0, 0, true, 0, 0, m_originalCasterGUID);
- return;
- }
- // Mana Spring Totem
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_SHAMAN_MANA_SPRING)
- {
- if (!unitTarget || unitTarget->getPowerType() != POWER_MANA)
- return;
- m_caster->CastCustomSpell(unitTarget, 52032, &damage, 0, 0, true, 0, 0, m_originalCasterGUID);
- return;
- }
- // Lava Lash
- if (m_spellInfo->SpellFamilyFlags[2] & SPELLFAMILYFLAG2_SHAMAN_LAVA_LASH)
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if (m_caster->ToPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
- {
- // Damage is increased by 25% if your off-hand weapon is enchanted with Flametongue.
- if (m_caster->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0x200000, 0, 0))
- AddPctN(m_damage, damage);
- }
- return;
- }
- break;
case SPELLFAMILY_DEATHKNIGHT:
- // Death strike
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_DK_DEATH_STRIKE)
- {
- uint32 count = unitTarget->GetDiseasesByCaster(m_caster->GetGUID());
- bp = int32(count * m_caster->CountPctFromMaxHealth(int32(m_spellInfo->Effects[EFFECT_0].DamageMultiplier)));
- // Improved Death Strike
- if (AuraEffect const* aurEff = m_caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 2751, 0))
- AddPctN(bp, m_caster->CalculateSpellDamage(m_caster, aurEff->GetSpellInfo(), 2));
- m_caster->CastCustomSpell(m_caster, 45470, &bp, NULL, NULL, false);
- return;
- }
- // Death Coil
- if (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_DK_DEATH_COIL)
- {
- if (m_caster->IsFriendlyTo(unitTarget))
- {
- bp = int32(damage * 1.5f);
- m_caster->CastCustomSpell(unitTarget, 47633, &bp, NULL, NULL, true);
- }
- else
- {
- bp = damage;
- m_caster->CastCustomSpell(unitTarget, 47632, &bp, NULL, NULL, true);
- }
- return;
- }
switch (m_spellInfo->Id)
{
- case 49560: // Death Grip
- Position pos;
- GetSummonPosition(effIndex, pos);
- if (Unit* unit = unitTarget->GetVehicleBase()) // what is this for?
- unit->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), damage, true);
- else if (!unitTarget->HasAuraType(SPELL_AURA_DEFLECT_SPELLS)) // Deterrence
- unitTarget->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), damage, true);
- return;
- case 46584: // Raise Dead
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
+ case 46584: // Raise Dead
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
- // Do we have talent Master of Ghouls?
- if (m_caster->HasAura(52143))
- // summon as pet
- bp = 52150;
- else
- // or guardian
- bp = 46585;
+ // Do we have talent Master of Ghouls?
+ if (m_caster->HasAura(52143))
+ // summon as pet
+ bp = 52150;
+ else
+ // or guardian
+ bp = 46585;
- if (m_targets.HasDst())
- targets.SetDst(*m_targets.GetDst());
- else
- {
- targets.SetDst(*m_caster);
- // Corpse not found - take reagents (only not triggered cast can take them)
- triggered = false;
- }
- // Remove cooldown - summon spellls have category
- m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true);
- spell_id = 48289;
- break;
- // Raise dead - take reagents and trigger summon spells
- case 48289:
- if (m_targets.HasDst())
- targets.SetDst(*m_targets.GetDst());
+ if (m_targets.HasDst())
+ targets.SetDst(*m_targets.GetDst());
+ else
+ {
+ targets.SetDst(*m_caster);
+ // Corpse not found - take reagents (only not triggered cast can take them)
+ triggered = false;
+ }
+ // Remove cooldown - summon spellls have category
+ m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true);
+ spell_id = 48289;
+ break;
+ // Raise dead - take reagents and trigger summon spells
+ case 48289:
+ if (m_targets.HasDst())
+ targets.SetDst(*m_targets.GetDst());
- spell_id = CalculateDamage(0, NULL);
- break;
+ spell_id = CalculateDamage(0, NULL);
+ break;
}
break;
}
@@ -3057,6 +2375,36 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
TempSummon* summon = NULL;
+ // determine how many units should be summoned
+ uint32 numSummons;
+
+ // some spells need to summon many units, for those spells number of summons is stored in effect value
+ // however so far noone found a generic check to find all of those (there's no related data in summonproperties.dbc
+ // and in spell attributes, possibly we need to add a table for those)
+ // so here's a list of MiscValueB values, which is currently most generic check
+ switch (properties->Id)
+ {
+ case 64:
+ case 61:
+ case 1101:
+ case 66:
+ case 648:
+ case 2301:
+ case 1061:
+ case 1261:
+ case 629:
+ case 181:
+ case 715:
+ case 1562:
+ case 833:
+ case 1161:
+ numSummons = (damage > 0) ? damage : 1;
+ break;
+ default:
+ numSummons = 1;
+ break;
+ }
+
switch (properties->Category)
{
case SUMMON_CATEGORY_WILD:
@@ -3064,7 +2412,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
case SUMMON_CATEGORY_UNK:
if (properties->Flags & 512)
{
- SummonGuardian(effIndex, entry, properties);
+ SummonGuardian(effIndex, entry, properties, numSummons);
break;
}
switch (properties->Type)
@@ -3073,7 +2421,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
case SUMMON_TYPE_GUARDIAN:
case SUMMON_TYPE_GUARDIAN2:
case SUMMON_TYPE_MINION:
- SummonGuardian(effIndex, entry, properties);
+ SummonGuardian(effIndex, entry, properties, numSummons);
break;
// Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE)
case SUMMON_TYPE_VEHICLE:
@@ -3106,7 +2454,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
summon->SelectLevel(summon->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp
summon->SetUInt32Value(UNIT_NPC_FLAGS, summon->GetCreatureInfo()->npcflag);
- summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE | UNIT_FLAG_PASSIVE);
+ summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
summon->AI()->EnterEvadeMode();
break;
@@ -3115,14 +2463,9 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
{
float radius = m_spellInfo->Effects[effIndex].CalcRadius();
- uint32 amount = damage > 0 ? damage : 1;
- if (m_spellInfo->Id == 18662 || // Curse of Doom
- properties->Id == 2081) // Mechanical Dragonling, Arcanite Dragonling, Mithril Dragonling TODO: Research on meaning of basepoints
- amount = 1;
-
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
- for (uint32 count = 0; count < amount; ++count)
+ for (uint32 count = 0; count < numSummons; ++count)
{
GetSummonPosition(effIndex, pos, radius, count);
@@ -3144,7 +2487,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
}//switch
break;
case SUMMON_CATEGORY_PET:
- SummonGuardian(effIndex, entry, properties);
+ SummonGuardian(effIndex, entry, properties, numSummons);
break;
case SUMMON_CATEGORY_PUPPET:
summon = m_caster->GetMap()->SummonCreature(entry, pos, properties, duration, m_originalCaster, m_spellInfo->Id);
@@ -3330,7 +2673,7 @@ void Spell::EffectDispel(SpellEffIndex effIndex)
// Send dispelled spell info
dataSuccess << uint32(itr->first->GetId()); // Spell Id
dataSuccess << uint8(0); // 0 - dispelled !=0 cleansed
- unitTarget->RemoveAurasDueToSpellByDispel(itr->first->GetId(), itr->first->GetCasterGUID(), m_caster, itr->second);
+ unitTarget->RemoveAurasDueToSpellByDispel(itr->first->GetId(), m_spellInfo->Id, itr->first->GetCasterGUID(), m_caster, itr->second);
}
m_caster->SendMessageToSet(&dataSuccess, true);
@@ -3795,7 +3138,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
{
SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(67);
if (properties)
- SummonGuardian(effIndex, petentry, properties);
+ SummonGuardian(effIndex, petentry, properties, 1);
return;
}
@@ -5601,7 +4944,7 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/)
if (target->isInFlight())
return;
- target->TeleportTo(target->GetStartPosition(), m_caster == m_caster ? TELE_TO_SPELL : 0);
+ target->TeleportTo(target->GetStartPosition(), TELE_TO_SPELL);
// homebind location is loaded always
// target->TeleportTo(target->m_homebindMapId, target->m_homebindX, target->m_homebindY, target->m_homebindZ, target->GetOrientation(), (m_caster == m_caster ? TELE_TO_SPELL : 0));
@@ -5627,7 +4970,7 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/)
return;
float x, y, z;
- m_caster->GetClosePoint(x, y, z, unitTarget->GetObjectSize());
+ m_caster->GetPosition(x, y, z);
unitTarget->ToPlayer()->SetSummonPoint(m_caster->GetMapId(), x, y, z);
@@ -6016,7 +5359,10 @@ void Spell::EffectLeap(SpellEffIndex /*effIndex*/)
if (!m_targets.HasDst())
return;
- unitTarget->NearTeleportTo(m_targets.GetDst()->GetPositionX(), m_targets.GetDst()->GetPositionY(), m_targets.GetDst()->GetPositionZ(), m_targets.GetDst()->GetOrientation(), unitTarget == m_caster);
+ Position pos;
+ m_targets.GetDst()->GetPosition(&pos);
+ unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetDistance(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() + 2.0f), 0.0f);
+ unitTarget->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), unitTarget == m_caster);
}
void Spell::EffectReputation(SpellEffIndex effIndex)
@@ -7062,7 +6408,7 @@ void Spell::EffectGameObjectSetDestructionState(SpellEffIndex effIndex)
gameObjTarget->SetDestructibleState(GameObjectDestructibleState(m_spellInfo->Effects[effIndex].MiscValue), player, true);
}
-void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties)
+void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numGuardians)
{
Unit* caster = m_originalCaster;
if (!caster)
@@ -7081,27 +6427,16 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const*
if (uint16 skill202 = caster->ToPlayer()->GetSkillValue(SKILL_ENGINEERING))
level = skill202 / 5;
- //float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
float radius = 5.0f;
- uint32 amount = damage > 0 ? damage : 1;
int32 duration = m_spellInfo->GetDuration();
- switch (m_spellInfo->Id)
- {
- case 1122: // Inferno
- amount = 1;
- break;
- case 49028: // Dancing Rune Weapon
- if (AuraEffect* aurEff = m_originalCaster->GetAuraEffect(63330, 0)) // glyph of Dancing Rune Weapon
- duration += aurEff->GetAmount();
- break;
- }
+
if (Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
//TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Map* map = caster->GetMap();
- for (uint32 count = 0; count < amount; ++count)
+ for (uint32 count = 0; count < numGuardians; ++count)
{
Position pos;
GetSummonPosition(i, pos, radius, count);
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 718efaed411..8b8f1fb5afc 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -20,6 +20,7 @@
#include "SpellMgr.h"
#include "Spell.h"
#include "DBCStores.h"
+#include "ConditionMgr.h"
uint32 GetTargetFlagMask(SpellTargetObjectTypes objType)
{
@@ -80,7 +81,7 @@ SpellTargetObjectTypes SpellImplicitTargetInfo::GetObjectType() const
return _data[_target].ObjectType;
}
-SpellTargetSelectionCheckTypes SpellImplicitTargetInfo::GetSelectionCheckType() const
+SpellTargetCheckTypes SpellImplicitTargetInfo::GetCheckType() const
{
return _data[_target].SelectionCheckType;
}
@@ -158,23 +159,25 @@ uint32 SpellImplicitTargetInfo::GetExplicitTargetMask(bool& srcSet, bool& dstSet
case TARGET_OBJECT_TYPE_UNIT_AND_DEST:
case TARGET_OBJECT_TYPE_UNIT:
case TARGET_OBJECT_TYPE_DEST:
- switch (GetSelectionCheckType())
+ switch (GetCheckType())
{
- case TARGET_SELECT_CHECK_ENEMY:
+ case TARGET_CHECK_ENEMY:
targetMask = TARGET_FLAG_UNIT_ENEMY;
break;
- case TARGET_SELECT_CHECK_ALLY:
+ case TARGET_CHECK_ALLY:
targetMask = TARGET_FLAG_UNIT_ALLY;
break;
- case TARGET_SELECT_CHECK_PARTY:
+ case TARGET_CHECK_PARTY:
targetMask = TARGET_FLAG_UNIT_PARTY;
break;
- case TARGET_SELECT_CHECK_RAID:
+ case TARGET_CHECK_RAID:
targetMask = TARGET_FLAG_UNIT_RAID;
break;
- case TARGET_SELECT_CHECK_PASSENGER:
+ case TARGET_CHECK_PASSENGER:
targetMask = TARGET_FLAG_UNIT_PASSENGER;
break;
+ case TARGET_CHECK_RAID_CLASS:
+ // nobreak;
default:
targetMask = TARGET_FLAG_UNIT;
break;
@@ -344,117 +347,117 @@ SpellSelectTargetTypes SpellImplicitTargetInfo::Type[TOTAL_SPELL_TARGETS];
SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_TARGETS] =
{
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, //
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 1 TARGET_UNIT_CASTER
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 2 TARGET_UNIT_NEARBY_ENEMY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_PARTY, TARGET_DIR_NONE}, // 3 TARGET_UNIT_NEARBY_PARTY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_NONE}, // 4 TARGET_UNIT_NEARBY_ALLY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 5 TARGET_UNIT_PET
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 6 TARGET_UNIT_TARGET_ENEMY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_ENTRY, TARGET_DIR_NONE}, // 7 TARGET_UNIT_SRC_AREA_ENTRY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_ENTRY, TARGET_DIR_NONE}, // 8 TARGET_UNIT_DEST_AREA_ENTRY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 9 TARGET_DEST_HOME
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 10
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 11 TARGET_UNIT_SRC_AREA_UNK_11
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 12
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 13
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 14
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 15 TARGET_UNIT_SRC_AREA_ENEMY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 16 TARGET_UNIT_DEST_AREA_ENEMY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 17 TARGET_DEST_DB
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 18 TARGET_DEST_CASTER
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 19
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_PARTY, TARGET_DIR_NONE}, // 20 TARGET_UNIT_CASTER_AREA_PARTY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_NONE}, // 21 TARGET_UNIT_TARGET_ALLY
- {TARGET_OBJECT_TYPE_SRC, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 22 TARGET_SRC_CASTER
- {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 23 TARGET_GAMEOBJECT_TARGET
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_FRONT}, // 24 TARGET_UNIT_CONE_ENEMY_24
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 25 TARGET_UNIT_TARGET_ANY
- {TARGET_OBJECT_TYPE_GOBJ_ITEM, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 26 TARGET_GAMEOBJECT_ITEM_TARGET
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 27 TARGET_UNIT_MASTER
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 28 TARGET_DEST_DYNOBJ_ENEMY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_NONE}, // 29 TARGET_DEST_DYNOBJ_ALLY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_NONE}, // 30 TARGET_UNIT_SRC_AREA_ALLY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_NONE}, // 31 TARGET_UNIT_DEST_AREA_ALLY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 32 TARGET_DEST_CASTER_SUMMON
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_PARTY, TARGET_DIR_NONE}, // 33 TARGET_UNIT_SRC_AREA_PARTY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_PARTY, TARGET_DIR_NONE}, // 34 TARGET_UNIT_DEST_AREA_PARTY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_PARTY, TARGET_DIR_NONE}, // 35 TARGET_UNIT_TARGET_PARTY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 36 TARGET_DEST_CASTER_UNK_36
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_LAST, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_PARTY, TARGET_DIR_NONE}, // 37 TARGET_UNIT_LASTTARGET_AREA_PARTY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_ENTRY, TARGET_DIR_NONE}, // 38 TARGET_UNIT_NEARBY_ENTRY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 39 TARGET_DEST_CASTER_FISHING
- {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_ENTRY, TARGET_DIR_NONE}, // 40 TARGET_GAMEOBJECT_NEARBY_ENTRY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_RIGHT}, // 41 TARGET_DEST_CASTER_FRONT_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK_RIGHT}, // 42 TARGET_DEST_CASTER_BACK_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK_LEFT}, // 43 TARGET_DEST_CASTER_BACK_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 44 TARGET_DEST_CASTER_FRONT_LEFT
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_NONE}, // 45 TARGET_UNIT_TARGET_CHAINHEAL_ALLY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_ENTRY, TARGET_DIR_NONE}, // 46 TARGET_DEST_NEARBY_ENTRY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 47 TARGET_DEST_CASTER_FRONT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK}, // 48 TARGET_DEST_CASTER_BACK
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RIGHT}, // 49 TARGET_DEST_CASTER_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_LEFT}, // 50 TARGET_DEST_CASTER_LEFT
- {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 51 TARGET_GAMEOBJECT_SRC_AREA
- {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 52 TARGET_GAMEOBJECT_DEST_AREA
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 53 TARGET_DEST_TARGET_ENEMY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_FRONT}, // 54 TARGET_UNIT_CONE_ENEMY_54
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 55 TARGET_DEST_CASTER_FRONT_LEAP
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_RAID, TARGET_DIR_NONE}, // 56 TARGET_UNIT_CASTER_AREA_RAID
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_RAID, TARGET_DIR_NONE}, // 57 TARGET_UNIT_TARGET_RAID
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_SELECT_CHECK_RAID, TARGET_DIR_NONE}, // 58 TARGET_UNIT_NEARBY_RAID
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_SELECT_CHECK_ALLY, TARGET_DIR_FRONT}, // 59 TARGET_UNIT_CONE_ALLY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_SELECT_CHECK_ENTRY, TARGET_DIR_FRONT}, // 60 TARGET_UNIT_CONE_ENTRY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_AREA, TARGET_SELECT_CHECK_RAID, TARGET_DIR_NONE}, // 61 TARGET_UNIT_TARGET_AREA_RAID_CLASS
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 62 TARGET_UNK_62
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 63 TARGET_DEST_TARGET_ANY
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 64 TARGET_DEST_TARGET_FRONT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK}, // 65 TARGET_DEST_TARGET_BACK
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RIGHT}, // 66 TARGET_DEST_TARGET_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_LEFT}, // 67 TARGET_DEST_TARGET_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_RIGHT}, // 68 TARGET_DEST_TARGET_FRONT_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK_RIGHT}, // 69 TARGET_DEST_TARGET_BACK_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK_LEFT}, // 70 TARGET_DEST_TARGET_BACK_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 71 TARGET_DEST_TARGET_FRONT_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 72 TARGET_DEST_CASTER_RANDOM
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 73 TARGET_DEST_CASTER_RADIUS
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 74 TARGET_DEST_TARGET_RANDOM
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 75 TARGET_DEST_TARGET_RADIUS
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 76 TARGET_DEST_CHANNEL_TARGET
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 77 TARGET_UNIT_CHANNEL_TARGET
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 78 TARGET_DEST_DEST_FRONT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK}, // 79 TARGET_DEST_DEST_BACK
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RIGHT}, // 80 TARGET_DEST_DEST_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_LEFT}, // 81 TARGET_DEST_DEST_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_RIGHT}, // 82 TARGET_DEST_DEST_FRONT_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK_RIGHT}, // 83 TARGET_DEST_DEST_BACK_RIGHT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_BACK_LEFT}, // 84 TARGET_DEST_DEST_BACK_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 85 TARGET_DEST_DEST_FRONT_LEFT
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 86 TARGET_DEST_DEST_RANDOM
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 87 TARGET_DEST_DEST
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 88 TARGET_DEST_DYNOBJ_NONE
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 89 TARGET_DEST_TRAJ
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 90 TARGET_UNIT_TARGET_MINIPET
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 91 TARGET_DEST_DEST_RADIUS
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 92 TARGET_UNIT_SUMMONER
- {TARGET_OBJECT_TYPE_CORPSE, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_NONE}, // 93 TARGET_CORPSE_SRC_AREA_ENEMY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 94 TARGET_UNIT_VEHICLE
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_PASSENGER, TARGET_DIR_NONE}, // 95 TARGET_UNIT_TARGET_PASSENGER
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 96 TARGET_UNIT_PASSENGER_0
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 97 TARGET_UNIT_PASSENGER_1
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 98 TARGET_UNIT_PASSENGER_2
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 99 TARGET_UNIT_PASSENGER_3
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 100 TARGET_UNIT_PASSENGER_4
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 101 TARGET_UNIT_PASSENGER_5
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 102 TARGET_UNIT_PASSENGER_6
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 103 TARGET_UNIT_PASSENGER_7
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_SELECT_CHECK_ENEMY, TARGET_DIR_FRONT}, // 104 TARGET_UNIT_CONE_ENEMY_104
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 105 TARGET_UNIT_UNK_105
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 106 TARGET_DEST_CHANNEL_CASTER
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 107 TARGET_UNK_DEST_AREA_UNK_107
- {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE
- {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 109
- {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_SELECT_CHECK_DEFAULT, TARGET_DIR_NONE}, // 110 TARGET_DEST_UNK_110
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, //
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 1 TARGET_UNIT_CASTER
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 2 TARGET_UNIT_NEARBY_ENEMY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 3 TARGET_UNIT_NEARBY_PARTY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 4 TARGET_UNIT_NEARBY_ALLY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 5 TARGET_UNIT_PET
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 6 TARGET_UNIT_TARGET_ENEMY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 7 TARGET_UNIT_SRC_AREA_ENTRY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 8 TARGET_UNIT_DEST_AREA_ENTRY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 9 TARGET_DEST_HOME
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 10
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 11 TARGET_UNIT_SRC_AREA_UNK_11
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 12
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 13
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 14
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 15 TARGET_UNIT_SRC_AREA_ENEMY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 16 TARGET_UNIT_DEST_AREA_ENEMY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 17 TARGET_DEST_DB
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 18 TARGET_DEST_CASTER
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 19
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 20 TARGET_UNIT_CASTER_AREA_PARTY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 21 TARGET_UNIT_TARGET_ALLY
+ {TARGET_OBJECT_TYPE_SRC, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 22 TARGET_SRC_CASTER
+ {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 23 TARGET_GAMEOBJECT_TARGET
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 24 TARGET_UNIT_CONE_ENEMY_24
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 25 TARGET_UNIT_TARGET_ANY
+ {TARGET_OBJECT_TYPE_GOBJ_ITEM, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 26 TARGET_GAMEOBJECT_ITEM_TARGET
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 27 TARGET_UNIT_MASTER
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 28 TARGET_DEST_DYNOBJ_ENEMY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 29 TARGET_DEST_DYNOBJ_ALLY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 30 TARGET_UNIT_SRC_AREA_ALLY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 31 TARGET_UNIT_DEST_AREA_ALLY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 32 TARGET_DEST_CASTER_SUMMON
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 33 TARGET_UNIT_SRC_AREA_PARTY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 34 TARGET_UNIT_DEST_AREA_PARTY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 35 TARGET_UNIT_TARGET_PARTY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 36 TARGET_DEST_CASTER_UNK_36
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_LAST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 37 TARGET_UNIT_LASTTARGET_AREA_PARTY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 38 TARGET_UNIT_NEARBY_ENTRY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 39 TARGET_DEST_CASTER_FISHING
+ {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 40 TARGET_GAMEOBJECT_NEARBY_ENTRY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_RIGHT}, // 41 TARGET_DEST_CASTER_FRONT_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK_RIGHT}, // 42 TARGET_DEST_CASTER_BACK_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK_LEFT}, // 43 TARGET_DEST_CASTER_BACK_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 44 TARGET_DEST_CASTER_FRONT_LEFT
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 45 TARGET_UNIT_TARGET_CHAINHEAL_ALLY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 46 TARGET_DEST_NEARBY_ENTRY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 47 TARGET_DEST_CASTER_FRONT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK}, // 48 TARGET_DEST_CASTER_BACK
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RIGHT}, // 49 TARGET_DEST_CASTER_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_LEFT}, // 50 TARGET_DEST_CASTER_LEFT
+ {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 51 TARGET_GAMEOBJECT_SRC_AREA
+ {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 52 TARGET_GAMEOBJECT_DEST_AREA
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 53 TARGET_DEST_TARGET_ENEMY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 54 TARGET_UNIT_CONE_ENEMY_54
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 55 TARGET_DEST_CASTER_FRONT_LEAP
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 56 TARGET_UNIT_CASTER_AREA_RAID
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 57 TARGET_UNIT_TARGET_RAID
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 58 TARGET_UNIT_NEARBY_RAID
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ALLY, TARGET_DIR_FRONT}, // 59 TARGET_UNIT_CONE_ALLY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENTRY, TARGET_DIR_FRONT}, // 60 TARGET_UNIT_CONE_ENTRY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID_CLASS,TARGET_DIR_NONE}, // 61 TARGET_UNIT_TARGET_AREA_RAID_CLASS
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 62 TARGET_UNK_62
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 63 TARGET_DEST_TARGET_ANY
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 64 TARGET_DEST_TARGET_FRONT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK}, // 65 TARGET_DEST_TARGET_BACK
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RIGHT}, // 66 TARGET_DEST_TARGET_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_LEFT}, // 67 TARGET_DEST_TARGET_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_RIGHT}, // 68 TARGET_DEST_TARGET_FRONT_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK_RIGHT}, // 69 TARGET_DEST_TARGET_BACK_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK_LEFT}, // 70 TARGET_DEST_TARGET_BACK_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 71 TARGET_DEST_TARGET_FRONT_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 72 TARGET_DEST_CASTER_RANDOM
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 73 TARGET_DEST_CASTER_RADIUS
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 74 TARGET_DEST_TARGET_RANDOM
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 75 TARGET_DEST_TARGET_RADIUS
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 76 TARGET_DEST_CHANNEL_TARGET
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 77 TARGET_UNIT_CHANNEL_TARGET
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 78 TARGET_DEST_DEST_FRONT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK}, // 79 TARGET_DEST_DEST_BACK
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RIGHT}, // 80 TARGET_DEST_DEST_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_LEFT}, // 81 TARGET_DEST_DEST_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_RIGHT}, // 82 TARGET_DEST_DEST_FRONT_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK_RIGHT}, // 83 TARGET_DEST_DEST_BACK_RIGHT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_BACK_LEFT}, // 84 TARGET_DEST_DEST_BACK_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT_LEFT}, // 85 TARGET_DEST_DEST_FRONT_LEFT
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 86 TARGET_DEST_DEST_RANDOM
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 87 TARGET_DEST_DEST
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 88 TARGET_DEST_DYNOBJ_NONE
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 89 TARGET_DEST_TRAJ
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 90 TARGET_UNIT_TARGET_MINIPET
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 91 TARGET_DEST_DEST_RADIUS
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 92 TARGET_UNIT_SUMMONER
+ {TARGET_OBJECT_TYPE_CORPSE, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 93 TARGET_CORPSE_SRC_AREA_ENEMY
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 94 TARGET_UNIT_VEHICLE
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_PASSENGER, TARGET_DIR_NONE}, // 95 TARGET_UNIT_TARGET_PASSENGER
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 96 TARGET_UNIT_PASSENGER_0
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 97 TARGET_UNIT_PASSENGER_1
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 98 TARGET_UNIT_PASSENGER_2
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 99 TARGET_UNIT_PASSENGER_3
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 100 TARGET_UNIT_PASSENGER_4
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 101 TARGET_UNIT_PASSENGER_5
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 102 TARGET_UNIT_PASSENGER_6
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 103 TARGET_UNIT_PASSENGER_7
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 104 TARGET_UNIT_CONE_ENEMY_104
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 105 TARGET_UNIT_UNK_105
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CHANNEL, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 106 TARGET_DEST_CHANNEL_CASTER
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 107 TARGET_UNK_DEST_AREA_UNK_107
+ {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE
+ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 109
+ {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 110 TARGET_DEST_UNK_110
};
SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex)
@@ -463,7 +466,6 @@ SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const*
_spellInfo = spellInfo;
_effIndex = effIndex;
-
Effect = _effect ? _effect->Effect : 0;
ApplyAuraName = _effect ? _effect->EffectApplyAuraName : 0;
Amplitude = _effect ? _effect->EffectAmplitude : 0;
@@ -484,6 +486,7 @@ SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const*
ItemType = _effect ? _effect->EffectItemType : 0;
TriggerSpell = _effect ? _effect->EffectTriggerSpell : 0;
SpellClassMask = _effect ? _effect->EffectSpellClassMask : flag96(0);
+ ImplicitTargetConditions = NULL;
}
bool SpellEffectInfo::IsEffect() const
@@ -506,7 +509,7 @@ bool SpellEffectInfo::IsAura(AuraType aura) const
return IsAura() && ApplyAuraName == aura;
}
-bool SpellEffectInfo::IsArea() const
+bool SpellEffectInfo::IsTargetingArea() const
{
return TargetA.IsArea() || TargetB.IsArea();
}
@@ -1014,6 +1017,11 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry)
ChainEntry = NULL;
}
+SpellInfo::~SpellInfo()
+{
+ _UnloadImplicitTargetConditionLists();
+}
+
bool SpellInfo::HasEffect(SpellEffects effect) const
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
@@ -1137,10 +1145,19 @@ bool SpellInfo::IsAbilityOfSkillType(uint32 skillType) const
return false;
}
-bool SpellInfo::IsAOE() const
+bool SpellInfo::IsAffectingArea() const
+{
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (Effects[i].IsEffect() && (Effects[i].IsTargetingArea() || Effects[i].IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) || Effects[i].IsAreaAuraEffect()))
+ return true;
+ return false;
+}
+
+// checks if spell targets are selected from area, doesn't include spell effects in check (like area wide auras for example)
+bool SpellInfo::IsTargetingArea() const
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- if (Effects[i].IsEffect() && Effects[i].IsArea())
+ if (Effects[i].IsEffect() && Effects[i].IsTargetingArea())
return true;
return false;
}
@@ -1630,38 +1647,99 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a
return SPELL_CAST_OK;
}
-SpellCastResult SpellInfo::CheckTarget(Unit const* caster, Unit const* target, bool implicit) const
+SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* target, bool implicit) const
{
if (AttributesEx & SPELL_ATTR1_CANT_TARGET_SELF && caster == target)
return SPELL_FAILED_BAD_TARGETS;
- if (AttributesEx & SPELL_ATTR1_CANT_TARGET_IN_COMBAT && target->isInCombat())
- return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
+ // check visibility - ignore stealth for implicit (area) targets
+ if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !caster->canSeeOrDetect(target, implicit))
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Unit const* unitTarget = target->ToUnit();
+
+ // creature/player specific target checks
+ if (unitTarget)
+ {
+ if (AttributesEx & SPELL_ATTR1_CANT_TARGET_IN_COMBAT && unitTarget->isInCombat())
+ return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
+
+ // only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts
+ if (((AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) != 0) != unitTarget->HasAuraType(SPELL_AURA_GHOST))
+ {
+ if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS)
+ return SPELL_FAILED_TARGET_NOT_GHOST;
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+
+ if (caster != unitTarget)
+ {
+ if (caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Do not allow these spells to target creatures not tapped by us (Banish, Polymorph, many quest spells)
+ if (AttributesEx2 & SPELL_ATTR2_CANT_TARGET_TAPPED)
+ if (Creature const* targetCreature = unitTarget->ToCreature())
+ if (targetCreature->hasLootRecipient() && !targetCreature->isTappedBy(caster->ToPlayer()))
+ return SPELL_FAILED_CANT_CAST_ON_TAPPED;
+
+ if (AttributesCu & SPELL_ATTR0_CU_PICKPOCKET)
+ {
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+ else if ((unitTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) == 0)
+ return SPELL_FAILED_TARGET_NO_POCKETS;
+ }
- if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS && !target->ToPlayer())
+ // Not allow disarm unarmed player
+ if (Mechanic == MECHANIC_DISARM)
+ {
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ {
+ Player const* player = unitTarget->ToPlayer();
+ if (!player->GetWeaponForAttack(BASE_ATTACK) || !player->IsUseEquipedWeapon(true))
+ return SPELL_FAILED_TARGET_NO_WEAPONS;
+ }
+ else if (!unitTarget->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID))
+ return SPELL_FAILED_TARGET_NO_WEAPONS;
+ }
+ }
+ }
+ }
+ // corpse specific target checks
+ else if (Corpse const* corpseTarget = target->ToCorpse())
+ {
+ // cannot target bare bones
+ if (corpseTarget->GetType() == CORPSE_BONES)
+ return SPELL_FAILED_BAD_TARGETS;
+ // we have to use owner for some checks (aura preventing resurrection for example)
+ if (Player* owner = ObjectAccessor::FindPlayer(corpseTarget->GetOwnerGUID()))
+ unitTarget = owner;
+ // we're not interested in corpses without owner
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ // other types of objects - always valid
+ else return SPELL_CAST_OK;
+
+ // corpseOwner and unit specific target checks
+ if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS && !unitTarget->ToPlayer())
return SPELL_FAILED_TARGET_NOT_PLAYER;
- if (!IsAllowingDeadTarget() && !target->isAlive())
+ if (!IsAllowingDeadTarget() && !unitTarget->isAlive())
return SPELL_FAILED_TARGETS_DEAD;
- if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS && !(!target->isAlive() && target->HasAuraType(SPELL_AURA_GHOST)))
- return SPELL_FAILED_TARGET_NOT_GHOST;
-
// check this flag only for implicit targets (chain and area), allow to explicitly target units for spells like Shield of Righteousness
- if (implicit && AttributesEx6 & SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED && !target->CanFreeMove())
+ if (implicit && AttributesEx6 & SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED && !unitTarget->CanFreeMove())
return SPELL_FAILED_BAD_TARGETS;
- // check visibility - ignore stealth for implicit (area) targets
- if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !caster->canSeeOrDetect(target, implicit))
- return SPELL_FAILED_BAD_TARGETS;
-
// checked in Unit::IsValidAttack/AssistTarget, shouldn't be checked for ENTRY targets
//if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
// return SPELL_FAILED_BAD_TARGETS;
//if (!(AttributesEx6 & SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS)
- if (!CheckTargetCreatureType(target))
+ if (!CheckTargetCreatureType(unitTarget))
{
if (target->GetTypeId() == TYPEID_PLAYER)
return SPELL_FAILED_TARGET_IS_PLAYER;
@@ -1670,65 +1748,32 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, Unit const* target, b
}
// check GM mode and GM invisibility - only for player casts (npc casts are controlled by AI) and negative spells
- if (target != caster && (caster->IsControlledByPlayer() || !IsPositive()) && target->GetTypeId() == TYPEID_PLAYER)
+ if (unitTarget != caster && (caster->IsControlledByPlayer() || !IsPositive()) && unitTarget->GetTypeId() == TYPEID_PLAYER)
{
- if (!target->ToPlayer()->IsVisible())
+ if (!unitTarget->ToPlayer()->IsVisible())
return SPELL_FAILED_BM_OR_INVISGOD;
- if (target->ToPlayer()->isGameMaster())
+ if (unitTarget->ToPlayer()->isGameMaster())
return SPELL_FAILED_BM_OR_INVISGOD;
}
// not allow casting on flying player
- if (target->HasUnitState(UNIT_STATE_IN_FLIGHT))
+ if (unitTarget->HasUnitState(UNIT_STATE_IN_FLIGHT))
return SPELL_FAILED_BAD_TARGETS;
- if (TargetAuraState && !target->HasAuraState(AuraStateType(TargetAuraState), this, caster))
+ if (TargetAuraState && !unitTarget->HasAuraState(AuraStateType(TargetAuraState), this, caster))
return SPELL_FAILED_TARGET_AURASTATE;
- if (TargetAuraStateNot && target->HasAuraState(AuraStateType(TargetAuraStateNot), this, caster))
+ if (TargetAuraStateNot && unitTarget->HasAuraState(AuraStateType(TargetAuraStateNot), this, caster))
return SPELL_FAILED_TARGET_AURASTATE;
- if (TargetAuraSpell && !target->HasAura(sSpellMgr->GetSpellIdForDifficulty(TargetAuraSpell, caster)))
+ if (TargetAuraSpell && !unitTarget->HasAura(sSpellMgr->GetSpellIdForDifficulty(TargetAuraSpell, caster)))
return SPELL_FAILED_TARGET_AURASTATE;
- if (ExcludeTargetAuraSpell && target->HasAura(sSpellMgr->GetSpellIdForDifficulty(ExcludeTargetAuraSpell, caster)))
+ if (ExcludeTargetAuraSpell && unitTarget->HasAura(sSpellMgr->GetSpellIdForDifficulty(ExcludeTargetAuraSpell, caster)))
return SPELL_FAILED_TARGET_AURASTATE;
- if (caster != target)
- {
- if (caster->GetTypeId() == TYPEID_PLAYER)
- {
- // Do not allow these spells to target creatures not tapped by us (Banish, Polymorph, many quest spells)
- if (AttributesEx2 & SPELL_ATTR2_CANT_TARGET_TAPPED)
- if (Creature const* targetCreature = target->ToCreature())
- if (targetCreature->hasLootRecipient() && !targetCreature->isTappedBy(caster->ToPlayer()))
- return SPELL_FAILED_CANT_CAST_ON_TAPPED;
-
- if (AttributesCu & SPELL_ATTR0_CU_PICKPOCKET)
- {
- if (target->GetTypeId() == TYPEID_PLAYER)
- return SPELL_FAILED_BAD_TARGETS;
- else if ((target->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) == 0)
- return SPELL_FAILED_TARGET_NO_POCKETS;
- }
-
- // Not allow disarm unarmed player
- if (Mechanic == MECHANIC_DISARM)
- {
- if (target->GetTypeId() == TYPEID_PLAYER)
- {
- Player const* player = target->ToPlayer();
- if (!player->GetWeaponForAttack(BASE_ATTACK) || !player->IsUseEquipedWeapon(true))
- return SPELL_FAILED_TARGET_NO_WEAPONS;
- }
- else if (!target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID))
- return SPELL_FAILED_TARGET_NO_WEAPONS;
- }
- }
- }
-
- if (target->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
+ if (unitTarget->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
if (HasEffect(SPELL_EFFECT_SELF_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT_NEW))
return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED;
@@ -2102,13 +2147,19 @@ float SpellInfo::GetMinRange(bool positive) const
return RangeEntry->minRangeHostile;
}
-float SpellInfo::GetMaxRange(bool positive) const
+float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const
{
if (!RangeEntry)
return 0.0f;
+ float range;
if (positive)
- return RangeEntry->maxRangeFriend;
- return RangeEntry->maxRangeHostile;
+ range = RangeEntry->maxRangeFriend;
+ else
+ range = RangeEntry->maxRangeHostile;
+ if (caster)
+ if (Player* modOwner = caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(Id, SPELLMOD_RANGE, range, spell);
+ return range;
}
int32 SpellInfo::GetDuration() const
@@ -2698,3 +2749,20 @@ SpellEffectEntry const* SpellEntry::GetSpellEffect(uint32 eff) const
{
return GetSpellEffectEntry(Id, eff);
}
+
+void SpellInfo::_UnloadImplicitTargetConditionLists()
+{
+ // find the same instances of ConditionList and delete them.
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ ConditionList* cur = Effects[i].ImplicitTargetConditions;
+ if (!cur)
+ continue;
+ for (uint8 j = i; j < MAX_SPELL_EFFECTS; ++j)
+ {
+ if (Effects[j].ImplicitTargetConditions == cur)
+ Effects[j].ImplicitTargetConditions = NULL;
+ }
+ delete cur;
+ }
+}
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 44fb49bb17f..baf03589794 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -36,6 +36,7 @@ struct SpellRangeEntry;
struct SpellRadiusEntry;
struct SpellEntry;
struct SpellCastTimesEntry;
+struct Condition;
enum SpellCastTargetFlags
{
@@ -105,15 +106,16 @@ enum SpellTargetObjectTypes
TARGET_OBJECT_TYPE_CORPSE_ALLY,
};
-enum SpellTargetSelectionCheckTypes
+enum SpellTargetCheckTypes
{
- TARGET_SELECT_CHECK_DEFAULT,
- TARGET_SELECT_CHECK_ENTRY,
- TARGET_SELECT_CHECK_ENEMY,
- TARGET_SELECT_CHECK_ALLY,
- TARGET_SELECT_CHECK_PARTY,
- TARGET_SELECT_CHECK_RAID,
- TARGET_SELECT_CHECK_PASSENGER,
+ TARGET_CHECK_DEFAULT,
+ TARGET_CHECK_ENTRY,
+ TARGET_CHECK_ENEMY,
+ TARGET_CHECK_ALLY,
+ TARGET_CHECK_PARTY,
+ TARGET_CHECK_RAID,
+ TARGET_CHECK_RAID_CLASS,
+ TARGET_CHECK_PASSENGER,
};
enum SpellTargetDirectionTypes
@@ -220,7 +222,7 @@ public:
SpellTargetSelectionCategories GetSelectionCategory() const;
SpellTargetReferenceTypes GetReferenceType() const;
SpellTargetObjectTypes GetObjectType() const;
- SpellTargetSelectionCheckTypes GetSelectionCheckType() const;
+ SpellTargetCheckTypes GetCheckType() const;
SpellTargetDirectionTypes GetDirectionType() const;
float CalcDirectionAngle() const;
@@ -240,7 +242,7 @@ private:
SpellTargetObjectTypes ObjectType; // type of object returned by target type
SpellTargetReferenceTypes ReferenceType; // defines which object is used as a reference when selecting target
SpellTargetSelectionCategories SelectionCategory;
- SpellTargetSelectionCheckTypes SelectionCheckType; // defines selection criteria
+ SpellTargetCheckTypes SelectionCheckType; // defines selection criteria
SpellTargetDirectionTypes DirectionType; // direction for cone and dest targets
};
static StaticData _data[TOTAL_SPELL_TARGETS];
@@ -271,6 +273,7 @@ public:
uint32 ItemType;
uint32 TriggerSpell;
flag96 SpellClassMask;
+ std::list<Condition*>* ImplicitTargetConditions;
SpellEffectInfo() {}
SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex);
@@ -279,7 +282,7 @@ public:
bool IsEffect(SpellEffects effectName) const;
bool IsAura() const;
bool IsAura(AuraType aura) const;
- bool IsArea() const;
+ bool IsTargetingArea() const;
bool IsAreaAuraEffect() const;
bool IsFarUnitTargetEffect() const;
bool IsFarDestTargetEffect() const;
@@ -431,6 +434,7 @@ public:
SpellTotemsEntry const* GetSpellTotems() const;
SpellInfo(SpellEntry const* spellEntry);
+ ~SpellInfo();
bool HasEffect(SpellEffects effect) const;
bool HasAura(AuraType aura) const;
@@ -446,7 +450,8 @@ public:
bool IsAbilityLearnedWithProfession() const;
bool IsAbilityOfSkillType(uint32 skillType) const;
- bool IsAOE() const;
+ bool IsAffectingArea() const;
+ bool IsTargetingArea() const;
bool NeedsExplicitUnitTarget() const;
bool NeedsToBeTriggeredByCaster() const;
@@ -480,7 +485,7 @@ public:
SpellCastResult CheckShapeshift(uint32 form) const;
SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL) const;
- SpellCastResult CheckTarget(Unit const* caster, Unit const* target, bool implicit = true) const;
+ SpellCastResult CheckTarget(Unit const* caster, WorldObject const* target, bool implicit = true) const;
SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = NULL) const;
bool CheckTargetCreatureType(Unit const* target) const;
@@ -498,7 +503,7 @@ public:
SpellSpecificType GetSpellSpecific() const;
float GetMinRange(bool positive = false) const;
- float GetMaxRange(bool positive = false) const;
+ float GetMaxRange(bool positive = false, Unit* caster = NULL, Spell* spell = NULL) const;
int32 GetDuration() const;
int32 GetMaxDuration() const;
@@ -524,6 +529,9 @@ public:
bool _IsPositiveEffect(uint8 effIndex, bool deep) const;
bool _IsPositiveSpell() const;
static bool _IsPositiveTarget(uint32 targetA, uint32 targetB);
+
+ // unloading helpers
+ void _UnloadImplicitTargetConditionLists();
};
#endif // _SPELLINFO_H
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index a5bea319fee..67bd7939d70 100755
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -104,9 +104,6 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto,
// Hamstring - limit duration to 10s in PvP
if (spellproto->SpellFamilyFlags[0] & 0x2)
return DIMINISHING_LIMITONLY;
- // Improved Hamstring
- else if (spellproto->AttributesEx3 & 0x80000 && spellproto->SpellIconID == 23)
- return DIMINISHING_ROOT;
// Charge Stun (own diminishing)
else if (spellproto->SpellFamilyFlags[0] & 0x01000000)
return DIMINISHING_CHARGE;
@@ -2647,11 +2644,20 @@ void SpellMgr::UnloadSpellInfoStore()
for (uint32 i = 0; i < mSpellInfoMap.size(); ++i)
{
if (mSpellInfoMap[i])
- delete mSpellInfoMap[i];
+ delete mSpellInfoMap[i];
}
mSpellInfoMap.clear();
}
+void SpellMgr::UnloadSpellInfoImplicitTargetConditionLists()
+{
+ for (uint32 i = 0; i < mSpellInfoMap.size(); ++i)
+ {
+ if (mSpellInfoMap[i])
+ mSpellInfoMap[i]->_UnloadImplicitTargetConditionLists();
+ }
+}
+
void SpellMgr::LoadSpellCustomAttr()
{
uint32 oldMSTime = getMSTime();
@@ -2893,6 +2899,7 @@ void SpellMgr::LoadSpellCustomAttr()
case 64588: // Thrust (Argent Tournament)
case 66479: // Thrust (Argent Tournament)
case 68505: // Thrust (Argent Tournament)
+ case 62709: // Counterattack! (Argent Tournament)
case 62626: // Break-Shield (Argent Tournament, Player)
case 64590: // Break-Shield (Argent Tournament, Player)
case 64342: // Break-Shield (Argent Tournament, NPC)
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index 14137b6a91b..9fffd474651 100755
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -360,17 +360,6 @@ struct SpellThreatEntry
typedef std::map<uint32, SpellThreatEntry> SpellThreatMap;
-// Spell script target related declarations (accessed using SpellMgr functions)
-enum SpellScriptTargetType
-{
- SPELL_TARGET_TYPE_GAMEOBJECT = 0,
- SPELL_TARGET_TYPE_CREATURE = 1,
- SPELL_TARGET_TYPE_DEAD = 2,
- SPELL_TARGET_TYPE_CONTROLLED = 3,
-};
-
-#define MAX_SPELL_TARGET_TYPE 4
-
// coordinates for spells (accessed using SpellMgr functions)
struct SpellTargetPosition
{
@@ -726,6 +715,7 @@ class SpellMgr
void LoadSpellAreas();
void LoadSpellInfoStore();
void UnloadSpellInfoStore();
+ void UnloadSpellInfoImplicitTargetConditionLists();
void LoadSpellCustomAttr();
void LoadDbcDataCorrections();
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 8e20fd21fcf..03fea614c0d 100755
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -468,6 +468,11 @@ void SpellScript::PreventHitAura()
m_spell->m_spellAura->Remove();
}
+void SpellScript::GetSummonPosition(uint32 i, Position &pos, float radius = 0.0f, uint32 count = 0)
+{
+ m_spell->GetSummonPosition(i, pos, radius, count);
+}
+
void SpellScript::PreventHitEffect(SpellEffIndex effIndex)
{
if (!IsInHitPhase() && !IsInEffectHook())
@@ -542,6 +547,14 @@ bool AuraScript::_Validate(SpellInfo const* entry)
if (!entry->HasAreaAuraEffect())
sLog->outError("TSCR: Spell `%u` of script `%s` does not have area aura effect - handler bound to hook `DoCheckAreaTarget` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+ for (std::list<AuraDispelHandler>::iterator itr = OnDispel.begin(); itr != OnDispel.end(); ++itr)
+ if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
+ sLog->outError("TSCR: Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `OnDispel` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+
+ for (std::list<AuraDispelHandler>::iterator itr = AfterDispel.begin(); itr != AfterDispel.end(); ++itr)
+ if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
+ sLog->outError("TSCR: Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `AfterDispel` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+
for (std::list<EffectApplyHandler>::iterator itr = OnEffectApply.begin(); itr != OnEffectApply.end(); ++itr)
if (!(*itr).GetAffectedEffectsMask(entry))
sLog->outError("TSCR: Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectApply` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str());
@@ -607,6 +620,16 @@ bool AuraScript::CheckAreaTargetHandler::Call(AuraScript* auraScript, Unit* _tar
return (auraScript->*pHandlerScript)(_target);
}
+AuraScript::AuraDispelHandler::AuraDispelHandler(AuraDispelFnType _pHandlerScript)
+{
+ pHandlerScript = _pHandlerScript;
+}
+
+void AuraScript::AuraDispelHandler::Call(AuraScript* auraScript, DispelInfo* _dispelInfo)
+{
+ (auraScript->*pHandlerScript)(_dispelInfo);
+}
+
AuraScript::EffectBase::EffectBase(uint8 _effIndex, uint16 _effName)
: _SpellScript::EffectAuraNameCheck(_effName), _SpellScript::EffectHook(_effIndex)
{
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index a1d8e6563b0..1bf8d25adef 100755
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -21,6 +21,7 @@
#include "Util.h"
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
+#include "Spell.h"
#include <stack>
class Unit;
@@ -333,7 +334,8 @@ class SpellScript : public _SpellScript
int32 GetHitHeal();
void SetHitHeal(int32 heal);
void PreventHitHeal() { SetHitHeal(0); }
-
+ Spell* GetSpell() { return m_spell; }
+ void GetSummonPosition(uint32 i, Position &pos, float radius, uint32 count);
// returns current spell hit target aura
Aura* GetHitAura();
// prevents applying aura on current spell hit target
@@ -385,6 +387,8 @@ enum AuraScriptHookType
AURA_SCRIPT_HOOK_EFFECT_MANASHIELD,
AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD,
AURA_SCRIPT_HOOK_CHECK_AREA_TARGET,
+ AURA_SCRIPT_HOOK_DISPEL,
+ AURA_SCRIPT_HOOK_AFTER_DISPEL,
/*AURA_SCRIPT_HOOK_APPLY,
AURA_SCRIPT_HOOK_REMOVE, */
};
@@ -400,6 +404,7 @@ class AuraScript : public _SpellScript
#define AURASCRIPT_FUNCTION_TYPE_DEFINES(CLASSNAME) \
typedef bool(CLASSNAME::*AuraCheckAreaTargetFnType)(Unit* target); \
+ typedef void(CLASSNAME::*AuraDispelFnType)(DispelInfo* dispelInfo); \
typedef void(CLASSNAME::*AuraEffectApplicationModeFnType)(AuraEffect const*, AuraEffectHandleModes); \
typedef void(CLASSNAME::*AuraEffectPeriodicFnType)(AuraEffect const*); \
typedef void(CLASSNAME::*AuraEffectUpdatePeriodicFnType)(AuraEffect*); \
@@ -418,6 +423,14 @@ class AuraScript : public _SpellScript
private:
AuraCheckAreaTargetFnType pHandlerScript;
};
+ class AuraDispelHandler
+ {
+ public:
+ AuraDispelHandler(AuraDispelFnType pHandlerScript);
+ void Call(AuraScript* auraScript, DispelInfo* dispelInfo);
+ private:
+ AuraDispelFnType pHandlerScript;
+ };
class EffectBase : public _SpellScript::EffectAuraNameCheck, public _SpellScript::EffectHook
{
public:
@@ -493,6 +506,7 @@ class AuraScript : public _SpellScript
#define AURASCRIPT_FUNCTION_CAST_DEFINES(CLASSNAME) \
class CheckAreaTargetFunction : public AuraScript::CheckAreaTargetHandler { public: CheckAreaTargetFunction(AuraCheckAreaTargetFnType _pHandlerScript) : AuraScript::CheckAreaTargetHandler((AuraScript::AuraCheckAreaTargetFnType)_pHandlerScript) {} }; \
+ class AuraDispelFunction : public AuraScript::AuraDispelHandler { public: AuraDispelFunction(AuraDispelFnType _pHandlerScript) : AuraScript::AuraDispelHandler((AuraScript::AuraDispelFnType)_pHandlerScript) {} }; \
class EffectPeriodicHandlerFunction : public AuraScript::EffectPeriodicHandler { public: EffectPeriodicHandlerFunction(AuraEffectPeriodicFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName) : AuraScript::EffectPeriodicHandler((AuraScript::AuraEffectPeriodicFnType)_pEffectHandlerScript, _effIndex, _effName) {} }; \
class EffectUpdatePeriodicHandlerFunction : public AuraScript::EffectUpdatePeriodicHandler { public: EffectUpdatePeriodicHandlerFunction(AuraEffectUpdatePeriodicFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName) : AuraScript::EffectUpdatePeriodicHandler((AuraScript::AuraEffectUpdatePeriodicFnType)_pEffectHandlerScript, _effIndex, _effName) {} }; \
class EffectCalcAmountHandlerFunction : public AuraScript::EffectCalcAmountHandler { public: EffectCalcAmountHandlerFunction(AuraEffectCalcAmountFnType _pEffectHandlerScript, uint8 _effIndex, uint16 _effName) : AuraScript::EffectCalcAmountHandler((AuraScript::AuraEffectCalcAmountFnType)_pEffectHandlerScript, _effIndex, _effName) {} }; \
@@ -540,6 +554,17 @@ class AuraScript : public _SpellScript
// where function is: bool function (Unit* target);
HookList<CheckAreaTargetHandler> DoCheckAreaTarget;
#define AuraCheckAreaTargetFn(F) CheckAreaTargetFunction(&F)
+
+ // executed when aura is dispelled by a unit
+ // example: OnDispel += AuraDispelFn(class::function);
+ // where function is: void function (DispelInfo* dispelInfo);
+ HookList<AuraDispelHandler> OnDispel;
+ // executed after aura is dispelled by a unit
+ // example: AfterDispel += AuraDispelFn(class::function);
+ // where function is: void function (DispelInfo* dispelInfo);
+ HookList<AuraDispelHandler> AfterDispel;
+ #define AuraDispelFn(F) AuraDispelFunction(&F)
+
// executed when aura effect is applied with specified mode to target
// should be used when when effect handler preventing/replacing is needed, do not use this hook for triggering spellcasts/removing auras etc - may be unsafe
// example: OnEffectApply += AuraEffectApplyFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier, AuraEffectHandleModes);