diff options
| author | Spp <spp@jorge.gr> | 2012-02-23 13:06:35 +0100 |
|---|---|---|
| committer | Spp <spp@jorge.gr> | 2012-02-23 13:06:35 +0100 |
| commit | 5cff9e071640bc47688b71bb264edd8267ba77c3 (patch) | |
| tree | c599d12dfb7870d7c34a07490a930b3102c3d8ac /src/server/game/Spells | |
| parent | f0ca875a216eaab3d213feae8ba75c07795c9304 (diff) | |
| parent | 9219625243bc9f63e5a152e6cda1043cfaade201 (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-x | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 8 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.h | 7 | ||||
| -rwxr-xr-x | src/server/game/Spells/Auras/SpellAuras.cpp | 76 | ||||
| -rwxr-xr-x | src/server/game/Spells/Auras/SpellAuras.h | 45 | ||||
| -rwxr-xr-x | src/server/game/Spells/Spell.cpp | 2649 | ||||
| -rwxr-xr-x | src/server/game/Spells/Spell.h | 193 | ||||
| -rwxr-xr-x | src/server/game/Spells/SpellEffects.cpp | 813 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 430 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.h | 36 | ||||
| -rwxr-xr-x | src/server/game/Spells/SpellMgr.cpp | 15 | ||||
| -rwxr-xr-x | src/server/game/Spells/SpellMgr.h | 12 | ||||
| -rwxr-xr-x | src/server/game/Spells/SpellScript.cpp | 23 | ||||
| -rwxr-xr-x | src/server/game/Spells/SpellScript.h | 27 |
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); |
