diff options
-rwxr-xr-x | src/server/game/Entities/Creature/Creature.cpp | 6 | ||||
-rwxr-xr-x | src/server/game/Entities/Creature/Creature.h | 6 | ||||
-rwxr-xr-x | src/server/game/Entities/Unit/Unit.cpp | 35 | ||||
-rwxr-xr-x | src/server/game/Entities/Unit/Unit.h | 7 | ||||
-rwxr-xr-x | src/server/game/Miscellaneous/SharedDefines.h | 10 | ||||
-rwxr-xr-x | src/server/game/Spells/Spell.cpp | 259 | ||||
-rwxr-xr-x | src/server/game/Spells/Spell.h | 20 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 121 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 3 | ||||
-rwxr-xr-x | src/server/game/Spells/SpellMgr.cpp | 22 | ||||
-rwxr-xr-x | src/server/game/Spells/SpellMgr.h | 4 |
11 files changed, 202 insertions, 291 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 0896a6d603a..b099fb5faa7 100755 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -996,12 +996,12 @@ void Creature::SetLootRecipient(Unit *unit) } // return true if this creature is tapped by the player or by a member of his group. -bool Creature::isTappedBy(Player* player) const +bool Creature::isTappedBy(Player const* player) const { if (player->GetGUID() == m_lootRecipient) return true; - Group* playerGroup = player->GetGroup(); + Group const* playerGroup = player->GetGroup(); if (!playerGroup || playerGroup != GetLootRecipientGroup()) // if we dont have a group we arent the recipient return false; // if creature doesnt have group bound it means it was solo killed by someone else @@ -1947,7 +1947,7 @@ bool Creature::_IsTargetAcceptable(const Unit* target) const // if the target cannot be attacked, the target is not acceptable if (IsFriendlyTo(target) - || !target->isAttackableByAOE() + || !target->isTargetableForAttack(false) || (m_vehicle && (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target)))) return false; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index b1d805a3a6e..bf61e98bb41 100755 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -555,10 +555,10 @@ class Creature : public Unit, public GridObject<Creature> Loot loot; bool lootForPickPocketed; bool lootForBody; - Player *GetLootRecipient() const; - Group *GetLootRecipientGroup() const; + Player* GetLootRecipient() const; + Group* GetLootRecipientGroup() const; bool hasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; } - bool isTappedBy(Player* player) const; // return true if the creature is tapped by the player or a member of his party. + bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party. void SetLootRecipient (Unit* unit); void AllLootRemovedFromCorpse(); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 09e3d154cae..5008f4a347e 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -12255,10 +12255,16 @@ void Unit::ClearInCombat() RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); } -// TODO: remove this function -bool Unit::isTargetableForAttack() const +bool Unit::isTargetableForAttack(bool checkFakeDeath) const { - return isAttackableByAOE() && !HasUnitState(UNIT_STAT_DIED); + if (HasFlag(UNIT_FIELD_FLAGS, + UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return false; + + if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->isGameMaster()) + return false; + + return !HasUnitState(UNIT_STAT_UNATTACKABLE) && (!checkFakeDeath || !HasUnitState(UNIT_STAT_DIED)); } bool Unit::canAttack(Unit const* target, bool force) const @@ -12285,7 +12291,7 @@ bool Unit::canAttack(Unit const* target, bool force) const else if (!IsHostileTo(target)) return false; - if (!target->isAttackableByAOE()) + if (!target->isTargetableForAttack(false)) return false; if (target->HasUnitState(UNIT_STAT_DIED)) @@ -12308,27 +12314,6 @@ bool Unit::canAttack(Unit const* target, bool force) const return true; } -bool Unit::isAttackableByAOE(SpellInfo const* spellProto) const -{ - bool targetMustBeDead = spellProto ? bool(spellProto->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) : false; - bool targetCanBeDead = spellProto ? bool(spellProto->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_DEAD) : false; - - if (targetMustBeDead && isAlive()) - return false; - - if (!targetMustBeDead && !targetCanBeDead && !isAlive()) - return false; - - if (HasFlag(UNIT_FIELD_FLAGS, - UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE)) - return false; - - if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->isGameMaster()) - return false; - - return !HasUnitState(UNIT_STAT_UNATTACKABLE); -} - int32 Unit::ModifyHealth(int32 dVal) { int32 gain = 0; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 8b7d3606b44..0e6cccd0cd5 100755 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -395,10 +395,7 @@ enum TriggerCastFlags TRIGGERED_IGNORE_SET_FACING = 0x00000200, //! Will not adjust facing to target (if any) TRIGGERED_IGNORE_SHAPESHIFT = 0x00000400, //! Will ignore shapeshift checks TRIGGERED_IGNORE_CASTER_AURASTATE = 0x00000800, //! Will ignore caster aura states including combat requirements and death state - TRIGGERED_IGNORE_TARGET_AURASTATE = 0x00001000, //! Will ignore target aura states TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE = 0x00002000, //! Will ignore mounted/on vehicle restrictions - TRIGGERED_IGNORE_TARGET_DETECTABILITY = 0x00004000, //! Will ignore canSeeOrDetect - TRIGGERED_IGNORE_LOS = 0x00008000, //! Will ignore LOS to target TRIGGERED_IGNORE_CASTER_AURAS = 0x00010000, //! Will ignore caster aura restrictions or requirements TRIGGERED_DISALLOW_PROC_EVENTS = 0x00020000, //! Disallows proc events from triggered spell (default) TRIGGERED_DONT_REPORT_CAST_ERROR = 0x00040000, //! Will return SPELL_FAILED_DONT_REPORT in CheckCast functions @@ -471,7 +468,7 @@ enum DeathState enum UnitState { - UNIT_STAT_DIED = 0x00000001, + UNIT_STAT_DIED = 0x00000001, // player has fake death aura UNIT_STAT_MELEE_ATTACKING = 0x00000002, // player is melee attacking someone //UNIT_STAT_MELEE_ATTACK_BY = 0x00000004, // player is melee attack by someone UNIT_STAT_STUNNED = 0x00000008, @@ -1547,7 +1544,7 @@ class Unit : public WorldObject bool isFrozen() const; - bool isTargetableForAttack() const; + bool isTargetableForAttack(bool checkFakeDeath = true) const; bool isAttackableByAOE(SpellInfo const* spellProto = NULL) const; bool canAttack(Unit const* target, bool force = true) const; virtual bool IsInWater() const; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0ffae2ac9dc..b9c9c39028d 100755 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -337,7 +337,7 @@ enum SpellAttr2 { SPELL_ATTR2_CAN_TARGET_DEAD = 0x00000001, // 0 can target dead unit or corpse SPELL_ATTR2_UNK1 = 0x00000002, // 1 ? many triggered spells have this flag - SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS = 0x00000004, // 2 26368 4.0.1 dbc change (NYI) + SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS = 0x00000004, // 2 26368 4.0.1 dbc change SPELL_ATTR2_UNK3 = 0x00000008, // 3 SPELL_ATTR2_DISPLAY_IN_STANCE_BAR = 0x00000010, // 4 client displays icon in stance bar when learned, even if not shapeshift SPELL_ATTR2_AUTOREPEAT_FLAG = 0x00000020, // 5 @@ -383,7 +383,7 @@ enum SpellAttr3 SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2 = 0x00000200, // 9 triggered from effect? SPELL_ATTR3_MAIN_HAND = 0x00000400, // 10 Main hand weapon required SPELL_ATTR3_BATTLEGROUND = 0x00000800, // 11 Can casted only on battleground - SPELL_ATTR3_ONLY_TARGET_GHOSTS = 0x00001000, // 12 + SPELL_ATTR3_ONLY_TARGET_GHOSTS = 0x00001000, // 12 SPELL_ATTR3_UNK13 = 0x00002000, // 13 SPELL_ATTR3_UNK14 = 0x00004000, // 14 "Honorless Target" only this spells have this flag SPELL_ATTR3_UNK15 = 0x00008000, // 15 Auto Shoot, Shoot, Throw, - this is autoshot flag @@ -482,7 +482,7 @@ enum SpellAttr6 SPELL_ATTR6_DONT_DISPLAY_COOLDOWN = 0x00000001, // 0 client doesn't display cooldown in tooltip for these spells SPELL_ATTR6_ONLY_IN_ARENA = 0x00000002, // 1 only usable in arena SPELL_ATTR6_IGNORE_CASTER_AURAS = 0x00000004, // 2 - SPELL_ATTR6_CAN_TARGET_INVISIBLE = 0x00000008, // 3 (NYI) ignore visibility requirement for spell target (phases, invisibility, etc.) + SPELL_ATTR6_UNK3 = 0x00000008, // 3 SPELL_ATTR6_UNK4 = 0x00000010, // 4 SPELL_ATTR6_UNK5 = 0x00000020, // 5 SPELL_ATTR6_UNK6 = 0x00000040, // 6 @@ -492,7 +492,7 @@ enum SpellAttr6 SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS = 0x00000400, // 10 NYI! SPELL_ATTR6_NOT_IN_RAID_INSTANCE = 0x00000800, // 11 not usable in raid instance SPELL_ATTR6_CASTABLE_WHILE_ON_VEHICLE = 0x00001000, // 12 castable while caster is on vehicle - SPELL_ATTR6_UNK13 = 0x00002000, // 13 + SPELL_ATTR6_CAN_TARGET_INVISIBLE = 0x00002000, // 13 ignore visibility requirement for spell target (phases, invisibility, etc.) SPELL_ATTR6_UNK14 = 0x00004000, // 14 SPELL_ATTR6_UNK15 = 0x00008000, // 15 not set in 3.0.3 SPELL_ATTR6_UNK16 = 0x00010000, // 16 @@ -503,7 +503,7 @@ enum SpellAttr6 SPELL_ATTR6_CLIENT_UI_TARGET_EFFECTS = 0x00200000, // 21 it's only client-side attribute SPELL_ATTR6_UNK22 = 0x00400000, // 22 SPELL_ATTR6_UNK23 = 0x00800000, // 23 - SPELL_ATTR6_CAN_TARGET_UNTARGETABLE = 0x01000000, // 24 NYI! + SPELL_ATTR6_CAN_TARGET_UNTARGETABLE = 0x01000000, // 24 SPELL_ATTR6_UNK25 = 0x02000000, // 25 SPELL_ATTR6_UNK26 = 0x04000000, // 26 SPELL_ATTR6_UNK27 = 0x08000000, // 27 diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index e86b3851b11..24d1c7f20a6 100755 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -631,7 +631,7 @@ void Spell::SelectSpellTargets() if (effectTargetType != SPELL_REQUIRE_UNIT) { if (effectTargetType == SPELL_REQUIRE_CASTER) - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); else if (effectTargetType == SPELL_REQUIRE_ITEM) if (m_targets.GetItemTarget()) AddItemTarget(m_targets.GetItemTarget(), i); @@ -642,7 +642,7 @@ void Spell::SelectSpellTargets() { if (!m_spellInfo->GetMaxRange(true)) { - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); continue; } @@ -692,9 +692,9 @@ void Spell::SelectSpellTargets() } default: if (m_targets.GetUnitTarget()) - AddUnitTarget(m_targets.GetUnitTarget(), i); + AddUnitTarget(m_targets.GetUnitTarget(), i, false); else - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); break; } break; @@ -711,10 +711,10 @@ void Spell::SelectSpellTargets() case SPELL_EFFECT_LEARN_SPELL: case SPELL_EFFECT_SEND_TAXI: if (m_targets.GetUnitTarget()) - AddUnitTarget(m_targets.GetUnitTarget(), i); + AddUnitTarget(m_targets.GetUnitTarget(), i, false); // Triggered spells have additional spell targets - cast them even if no explicit unit target is given (required for spell 50516 for example) else if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_TRIGGER_SPELL) - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); break; case SPELL_EFFECT_SUMMON_RAF_FRIEND: case SPELL_EFFECT_SUMMON_PLAYER: @@ -722,12 +722,12 @@ void Spell::SelectSpellTargets() { Player* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetSelection()); if (target) - AddUnitTarget(target, i); + AddUnitTarget(target, i, false); } break; case SPELL_EFFECT_RESURRECT_NEW: if (m_targets.GetUnitTarget()) - AddUnitTarget(m_targets.GetUnitTarget(), i); + AddUnitTarget(m_targets.GetUnitTarget(), i, false); if (m_targets.GetCorpseTargetGUID()) { Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster, m_targets.GetCorpseTargetGUID()); @@ -735,7 +735,7 @@ void Spell::SelectSpellTargets() { Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID()); if (owner) - AddUnitTarget(owner, i); + AddUnitTarget(owner, i, false); } } break; @@ -746,7 +746,7 @@ void Spell::SelectSpellTargets() case SPELL_EFFECT_FEED_PET: case SPELL_EFFECT_DESTROY_ALL_TOTEMS: case SPELL_EFFECT_KILL_CREDIT2: // only one spell: 42793 - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); break; case SPELL_EFFECT_LEARN_PET_SPELL: if (Guardian* pet = m_caster->GetGuardianPet()) @@ -757,17 +757,17 @@ void Spell::SelectSpellTargets() { case SPELL_AURA_ADD_FLAT_MODIFIER: // some spell mods auras have 0 target modes instead expected TARGET_UNIT_CASTER(1) (and present for other ranks for same spell for example) case SPELL_AURA_ADD_PCT_MODIFIER: - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); break; default: // apply to target in other case if (m_targets.GetUnitTarget()) - AddUnitTarget(m_targets.GetUnitTarget(), i); + AddUnitTarget(m_targets.GetUnitTarget(), i, false); break; } break; case SPELL_EFFECT_SKIN_PLAYER_CORPSE: if (m_targets.GetUnitTarget()) - AddUnitTarget(m_targets.GetUnitTarget(), i); + AddUnitTarget(m_targets.GetUnitTarget(), i), false; else if (m_targets.GetCorpseTargetGUID()) { Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster, m_targets.GetCorpseTargetGUID()); @@ -775,12 +775,12 @@ void Spell::SelectSpellTargets() { Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID()); if (owner) - AddUnitTarget(owner, i); + AddUnitTarget(owner, i, false); } } break; default: - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); break; } } @@ -926,12 +926,15 @@ void Spell::CleanupTargetList() m_delayMoment = 0; } -void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) +void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex, bool checkIfValid /*=true*/) { if (!m_spellInfo->Effects[effIndex].IsEffect()) return; - if (!CheckTarget(pVictim, effIndex)) + if (checkIfValid) + if (m_spellInfo->CheckTarget(m_caster, pVictim, true) != SPELL_CAST_OK) + return; + if (!CheckEffectTarget(pVictim, effIndex)) return; // Check for effect immune skip if immuned @@ -1022,12 +1025,6 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex) m_UniqueTargetInfo.push_back(target); } -void Spell::AddUnitTarget(uint64 unitGUID, uint32 effIndex) -{ - if (Unit* unit = m_caster->GetGUID() == unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID)) - AddUnitTarget(unit, effIndex); -} - void Spell::AddGOTarget(GameObject* go, uint32 effIndex) { if (!m_spellInfo->Effects[effIndex].IsEffect()) @@ -1851,15 +1848,9 @@ void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, SpellNo 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); - TagUnitMap.remove_if(Trinity::ObjectTypeIdCheck(TYPEID_PLAYER, false)); // above line will select also pets and totems, remove them - } else m_caster->GetMap()->VisitAll(pos->m_positionX, pos->m_positionY, radius, notifier); - - if (m_spellInfo->AttributesCu & SPELL_ATTR0_CU_EXCLUDE_SELF) - TagUnitMap.remove(m_caster); } void Spell::SearchGOAreaTarget(std::list<GameObject*> &TagGOMap, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry) @@ -2000,7 +1991,7 @@ void Spell::SelectEffectTargets(uint32 i, SpellImplicitTargetInfo const& cur) switch(cur.GetTarget()) { case TARGET_UNIT_CASTER: - AddUnitTarget(m_caster, i); + AddUnitTarget(m_caster, i, false); break; case TARGET_DEST_CASTER_FISHING: { @@ -2079,17 +2070,17 @@ void Spell::SelectEffectTargets(uint32 i, SpellImplicitTargetInfo const& cur) pushType = PUSH_CHAIN; break; case TARGET_UNIT_TARGET_ALLY: - AddUnitTarget(target, i); + AddUnitTarget(target, i, false); break; case TARGET_UNIT_TARGET_RAID: case TARGET_UNIT_TARGET_PARTY: case TARGET_UNIT_TARGET_MINIPET: if (IsValidSingleTargetSpell(target)) - AddUnitTarget(target, i); + AddUnitTarget(target, i, false); break; case TARGET_UNIT_TARGET_PASSENGER: if (target->IsOnVehicle(m_caster)) - AddUnitTarget(target, i); + AddUnitTarget(target, i, false); break; case TARGET_UNIT_LASTTARGET_AREA_PARTY: case TARGET_UNIT_TARGET_AREA_RAID_CLASS: @@ -2443,10 +2434,10 @@ void Spell::SelectEffectTargets(uint32 i, SpellImplicitTargetInfo const& cur) CallScriptAfterUnitTargetSelectHandlers(unitList, SpellEffIndex(i)); for (std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr) - AddUnitTarget(*itr, i); + AddUnitTarget(*itr, i, false); } else - AddUnitTarget(target, i); + AddUnitTarget(target, i, false); } else if (pushType) { @@ -2803,7 +2794,7 @@ void Spell::SelectEffectTargets(uint32 i, SpellImplicitTargetInfo const& cur) CallScriptAfterUnitTargetSelectHandlers(unitList, SpellEffIndex(i)); for (std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr) - AddUnitTarget(*itr, i); + AddUnitTarget(*itr, i, false); } if (!gobjectList.empty()) @@ -4758,69 +4749,27 @@ SpellCastResult Spell::CheckCast(bool strict) if (target) { - // target state requirements (not allowed state), apply to self also - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_TARGET_AURASTATE) && m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraStateType(m_spellInfo->TargetAuraStateNot), m_spellInfo, m_caster)) - return SPELL_FAILED_TARGET_AURASTATE; - - if (m_spellInfo->TargetAuraSpell && !target->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->TargetAuraSpell, m_caster))) - return SPELL_FAILED_TARGET_AURASTATE; - - if (m_spellInfo->ExcludeTargetAuraSpell && target->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->ExcludeTargetAuraSpell, m_caster))) - return SPELL_FAILED_TARGET_AURASTATE; - - if (!IsTriggered() && target == m_caster && m_spellInfo->AttributesEx & SPELL_ATTR1_CANT_TARGET_SELF) - return SPELL_FAILED_BAD_TARGETS; - - bool non_caster_target = target != m_caster && m_spellInfo->IsRequiringSelectedTarget(); - - if (non_caster_target) + if (target != m_caster && m_spellInfo->IsRequiringSelectedTarget()) { - // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_TARGET_AURASTATE) && m_spellInfo->TargetAuraState && !target->HasAuraState(AuraStateType(m_spellInfo->TargetAuraState), m_spellInfo, m_caster)) - return SPELL_FAILED_TARGET_AURASTATE; - - // Not allow casting on flying player or on vehicle player (if caster isnt vehicle) - if (target->HasUnitState(UNIT_STAT_IN_FLIGHT) /*|| (target->HasUnitState(UNIT_STAT_ONVEHICLE) && target->GetVehicleBase() != m_caster)*/) - return SPELL_FAILED_BAD_TARGETS; - - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_TARGET_DETECTABILITY) && !m_caster->canSeeOrDetect(target)) - return SPELL_FAILED_BAD_TARGETS; - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - // Do not allow these spells to target creatures not tapped by us (Banish, Polymorph, many quest spells) - if (m_spellInfo->AttributesEx2 & SPELL_ATTR2_CANT_TARGET_TAPPED) - if (Creature *targetCreature = target->ToCreature()) - if (targetCreature->hasLootRecipient() && !targetCreature->isTappedBy(m_caster->ToPlayer())) - return SPELL_FAILED_CANT_CAST_ON_TAPPED; + SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, false); + if (castResult != SPELL_CAST_OK) + return castResult; + } - if (m_spellInfo->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; - } + if (target != m_caster) + { + // Must be behind the target + if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET) && target->HasInArc(static_cast<float>(M_PI), m_caster)) + return SPELL_FAILED_NOT_BEHIND; - // Not allow disarm unarmed player - if (m_spellInfo->Mechanic == MECHANIC_DISARM) - { - if (target->GetTypeId() == TYPEID_PLAYER) - { - Player* 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; - } - } + // Target must be facing you + if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER) && !target->HasInArc(static_cast<float>(M_PI), m_caster)) + return SPELL_FAILED_NOT_INFRONT; - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) + if (!(m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) return SPELL_FAILED_LINE_OF_SIGHT; - } - else if (m_caster == target) + else { if (m_caster->GetTypeId() == TYPEID_PLAYER) // Target - is player caster { @@ -4843,7 +4792,7 @@ SpellCastResult Spell::CheckCast(bool strict) } } - // check pet presents + // check pet presence for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (m_spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET) @@ -4859,36 +4808,6 @@ SpellCastResult Spell::CheckCast(bool strict) break; } } - - // check creature type - // ignore self casts (including area casts when caster selected as target) - if (non_caster_target) - { - if (!CheckTargetCreatureType(target)) - { - if (target->GetTypeId() == TYPEID_PLAYER) - return SPELL_FAILED_TARGET_IS_PLAYER; - else - return SPELL_FAILED_BAD_TARGETS; - } - - // Must be behind the target - if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET) && target->HasInArc(static_cast<float>(M_PI), m_caster)) - return SPELL_FAILED_NOT_BEHIND; - - // Target must be facing you - if ((m_spellInfo->AttributesCu & SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER) && !target->HasInArc(static_cast<float>(M_PI), m_caster)) - return SPELL_FAILED_NOT_INFRONT; - - // Target must not be in combat - if (m_spellInfo->AttributesEx & SPELL_ATTR1_CANT_TARGET_IN_COMBAT && target->isInCombat()) - return SPELL_FAILED_TARGET_AFFECTING_COMBAT; - } - - if (target) - if (m_spellInfo->IsPositive()) - if (target->IsImmunedToSpell(m_spellInfo)) - return SPELL_FAILED_TARGET_AURASTATE; } // Spell casted only on battleground @@ -6474,37 +6393,6 @@ void Spell::UpdatePointers() m_targets.Update(m_caster); } -bool Spell::CheckTargetCreatureType(Unit* target) const -{ - uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType; - - // Curse of Doom & Exorcism: not find another way to fix spell target check :/ - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 1179) - { - // not allow cast at player - if (target->GetTypeId() == TYPEID_PLAYER) - return false; - - spellCreatureTargetMask = 0x7FF; - } - - // Dismiss Pet and Taming Lesson skipped - if (m_spellInfo->Id == 2641 || m_spellInfo->Id == 23356) - spellCreatureTargetMask = 0; - - // Polymorph and Grounding Totem - if (target->GetEntry() == 5925 && m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (m_spellInfo->SpellFamilyFlags[0] & 0x1000000) && m_spellInfo->Effects[0].ApplyAuraName == SPELL_AURA_MOD_CONFUSE) - return true; - - if (spellCreatureTargetMask) - { - uint32 TargetCreatureType = target->GetCreatureTypeMask(); - - return !TargetCreatureType || (spellCreatureTargetMask & TargetCreatureType); - } - return true; -} - CurrentSpellTypes Spell::GetCurrentContainer() { if (IsNextMeleeSwingSpell()) @@ -6517,52 +6405,10 @@ CurrentSpellTypes Spell::GetCurrentContainer() return(CURRENT_GENERIC_SPELL); } -bool Spell::CheckTarget(Unit* target, uint32 eff) +bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const { - // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets) - if (m_spellInfo->Effects[eff].TargetA.GetTarget() != TARGET_UNIT_CASTER) - { - if (!CheckTargetCreatureType(target)) - return false; - } - - // Check Aura spell req (need for AoE spells) - if (m_spellInfo->TargetAuraSpell && !target->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->TargetAuraSpell, m_caster))) - return false; - if (m_spellInfo->ExcludeTargetAuraSpell && target->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->ExcludeTargetAuraSpell, m_caster))) - return false; - - // Check targets for not_selectable unit flag and remove - // A player can cast spells on his pet (or other controlled unit) though in any state - if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID()) - { - // any unattackable target skipped - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return false; - - // unselectable targets skipped in all cases except TARGET_UNIT_NEARBY_ENTRY targeting - // in case TARGET_UNIT_NEARBY_ENTRY target selected by server always and can't be cheated - /*if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) && - m_spellInfo->EffectImplicitTargetA[eff] != TARGET_UNIT_NEARBY_ENTRY && - m_spellInfo->EffectImplicitTargetB[eff] != TARGET_UNIT_NEARBY_ENTRY) - return false;*/ - } - - //Check player targets and remove if in GM mode or GM invisibility (for not self casting case) - if (target != m_caster && target->GetTypeId() == TYPEID_PLAYER) - { - if (!target->ToPlayer()->IsVisible()) - return false; - - if (target->ToPlayer()->isGameMaster() && !m_spellInfo->IsPositive()) - return false; - } - switch(m_spellInfo->Effects[eff].ApplyAuraName) { - case SPELL_AURA_NONE: - default: - break; case SPELL_AURA_MOD_POSSESS: case SPELL_AURA_MOD_CHARM: case SPELL_AURA_MOD_POSSESS_PET: @@ -6573,26 +6419,21 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) return false; if (target->GetCharmerGUID()) return false; - if (int32 damage = CalculateDamage(eff, target)) + if (int32 damage = m_spellInfo->Effects[eff].CalcValue()) if ((int32)target->getLevel() > damage) return false; break; + default: + break; } - //Do not do further checks for triggered spells - if (IsTriggered()) + if (m_spellInfo->AttributesEx2 & SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) return true; + // todo: shit below shouldn't be here, but it's temporary //Check targets for LOS visibility (except spells without range limitations) switch(m_spellInfo->Effects[eff].Effect) { - case SPELL_EFFECT_SUMMON_RAF_FRIEND: - case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere - break; - case SPELL_EFFECT_DUMMY: - if (m_spellInfo->Id != 20577) // Cannibalize - break; - //fall through case SPELL_EFFECT_RESURRECT_NEW: // player far away, maybe his corpse near? if (target != m_caster && !target->IsWithinLOSInMap(m_caster)) @@ -6620,8 +6461,6 @@ bool Spell::CheckTarget(Unit* target, uint32 eff) caster = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); if (!caster) caster = m_caster; - if (target->GetEntry() == 5925) - return true; if (target != m_caster && !target->IsWithinLOSInMap(caster)) return false; break; diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 47d22463119..50c3a093a13 100755 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -429,7 +429,7 @@ class Spell template<typename T> WorldObject* FindCorpseUsing(); - bool CheckTarget(Unit* target, uint32 eff); + bool CheckEffectTarget(Unit const* target, uint32 eff) const; bool CanAutoCast(Unit* target); void CheckSrc() { if (!m_targets.HasSrc()) m_targets.SetSrc(*m_caster); } void CheckDst() { if (!m_targets.HasDst()) m_targets.SetDst(*m_caster); } @@ -498,8 +498,6 @@ class Spell void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc) - bool CheckTargetCreatureType(Unit* target) const; - void CleanupTargetList(); void SetSpellValue(SpellValueMod mod, int32 value); @@ -614,8 +612,7 @@ class Spell }; std::list<ItemTargetInfo> m_UniqueItemInfo; - void AddUnitTarget(Unit* target, uint32 effIndex); - void AddUnitTarget(uint64 unitGUID, uint32 effIndex); + void AddUnitTarget(Unit* target, uint32 effIndex, bool checkIfValid = true); void AddGOTarget(GameObject* target, uint32 effIndex); void AddGOTarget(uint64 goGUID, uint32 effIndex); void AddItemTarget(Item* target, uint32 effIndex); @@ -706,7 +703,6 @@ namespace Trinity const Unit* const i_source; uint32 i_entry; const Position * const i_pos; - bool i_requireDeadTarget; SpellInfo const* i_spellProto; SpellNotifierCreatureAndPlayer(Unit *source, std::list<Unit*> &data, float radius, SpellNotifyPushType type, @@ -719,13 +715,11 @@ namespace Trinity template<class T> inline void Visit(GridRefManager<T>& m) { - i_requireDeadTarget = i_spellProto ? bool(i_spellProto->AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS) : false; - for (typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr) { Unit* target = (Unit*)itr->getSource(); - if (!i_source->canSeeOrDetect(target, true)) + if (i_spellProto->CheckTarget(i_source, target, true) != SPELL_CAST_OK) continue; switch (i_TargetType) @@ -733,8 +727,6 @@ namespace Trinity case SPELL_TARGETS_ENEMY: if (target->isTotem()) continue; - if (!target->isAttackableByAOE(i_spellProto)) - continue; if (i_source->IsControlledByPlayer()) { if (i_source->IsFriendlyTo(target)) @@ -751,12 +743,6 @@ namespace Trinity continue; if (!i_source->IsFriendlyTo(target)) continue; - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - continue; - if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->isGameMaster()) - continue; - if (target->isAlive() == i_requireDeadTarget) - continue; break; case SPELL_TARGETS_ENTRY: if (target->GetEntry()!= i_entry) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 069864dfd74..11b7eea052a 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1486,6 +1486,127 @@ 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 +{ + 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; + + if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_PLAYERS && !target->ToPlayer()) + return SPELL_FAILED_TARGET_NOT_PLAYER; + + if (!(AttributesEx2 & SPELL_ATTR2_CAN_TARGET_DEAD) && !target->isAlive()) + return SPELL_FAILED_TARGETS_DEAD; + + if (AttributesEx3 & SPELL_ATTR3_ONLY_TARGET_GHOSTS && !(target->ToPlayer() && !target->isAlive())) + return SPELL_FAILED_TARGET_NOT_GHOST; + + if (AttributesEx6 & SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED && !target->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; + + 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 (target->GetTypeId() == TYPEID_PLAYER) + return SPELL_FAILED_TARGET_IS_PLAYER; + else + return SPELL_FAILED_BAD_TARGETS; + } + + // check UNIT_FLAG_NON_ATTACKABLE flag - a player can cast spells on his pet (or other controlled unit) though in any state + if (target != caster && target->GetCharmerOrOwnerGUID() != caster->GetGUID()) + { + // any unattackable target skipped + if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE)) + return SPELL_FAILED_BAD_TARGETS; + } + + // check GM mode and GM invisibility - only for player casts (npc casts are controlled by AI) + if (target != caster && caster->IsControlledByPlayer() && target->GetTypeId() == TYPEID_PLAYER) + { + if (!target->ToPlayer()->IsVisible()) + return SPELL_FAILED_BAD_TARGETS; + + if (target->ToPlayer()->isGameMaster()) + return SPELL_FAILED_BAD_TARGETS; + } + + // not allow casting on flying player + if (target->HasUnitState(UNIT_STAT_IN_FLIGHT)) + return SPELL_FAILED_BAD_TARGETS; + + if (TargetAuraState && !target->HasAuraState(AuraStateType(TargetAuraState), this, caster)) + return SPELL_FAILED_TARGET_AURASTATE; + + if (TargetAuraStateNot && target->HasAuraState(AuraStateType(TargetAuraStateNot), this, caster)) + return SPELL_FAILED_TARGET_AURASTATE; + + if (TargetAuraSpell && !target->HasAura(sSpellMgr->GetSpellIdForDifficulty(TargetAuraSpell, caster))) + return SPELL_FAILED_TARGET_AURASTATE; + + if (ExcludeTargetAuraSpell && target->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; + } + } + } + return SPELL_CAST_OK; +} + +bool SpellInfo::CheckTargetCreatureType(Unit const* target) const +{ + // Curse of Doom & Exorcism: not find another way to fix spell target check :/ + if (SpellFamilyName == SPELLFAMILY_WARLOCK && Category == 1179) + { + // not allow cast at player + if (target->GetTypeId() == TYPEID_PLAYER) + return false; + else + return true; + } + uint32 creatureType = target->GetCreatureTypeMask(); + return !TargetCreatureType || !creatureType || (creatureType & TargetCreatureType); +} + SpellSchoolMask SpellInfo::GetSchoolMask() const { return SpellSchoolMask(SchoolMask); diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 5abf3851279..9efc4701f19 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -159,7 +159,6 @@ enum SpellCustomAttributes SPELL_ATTR0_CU_DIRECT_DAMAGE = 0x00000100, SPELL_ATTR0_CU_CHARGE = 0x00000200, SPELL_ATTR0_CU_PICKPOCKET = 0x00000400, - SPELL_ATTR0_CU_EXCLUDE_SELF = 0x00000800, SPELL_ATTR0_CU_NEGATIVE_EFF0 = 0x00001000, SPELL_ATTR0_CU_NEGATIVE_EFF1 = 0x00002000, SPELL_ATTR0_CU_NEGATIVE_EFF2 = 0x00004000, @@ -403,6 +402,8 @@ 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; + bool CheckTargetCreatureType(Unit const* target) const; SpellSchoolMask GetSchoolMask() const; uint32 GetAllEffectsMechanicMask() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 8b86f002fa2..94c066c8c39 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -430,7 +430,7 @@ void SpellMgr::SetSpellDifficultyId(uint32 spellId, uint32 id) mSpellDifficultySearcherMap[spellId] = id; } -uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit* caster) const +uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) const { if (!GetSpellInfo(spellId)) return spellId; @@ -472,7 +472,7 @@ uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit* caster) const return uint32(difficultyEntry->SpellID[mode]); } -SpellInfo const* SpellMgr::GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit* caster) const +SpellInfo const* SpellMgr::GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit const* caster) const { uint32 newSpellId = GetSpellIdForDifficulty(spell->Id, caster); SpellInfo const* newSpell = GetSpellInfo(newSpellId); @@ -2774,21 +2774,6 @@ void SpellMgr::LoadSpellCustomAttr() // ONLY SPELLS WITH SPELLFAMILY_GENERIC and EFFECT_SCHOOL_DAMAGE spellInfo->AttributesCu |= SPELL_ATTR0_CU_SHARE_DAMAGE; break; - case 27820: // Mana Detonation - case 69782: // Ooze Flood - case 69796: // Ooze Flood - case 69798: // Ooze Flood - case 69801: // Ooze Flood - case 69538: // Ooze Combine - case 69553: // Ooze Combine - case 69610: // Ooze Combine - case 71447: // Bloodbolt Splash - case 71481: // Bloodbolt Splash - case 71482: // Bloodbolt Splash - case 71483: // Bloodbolt Splash - case 71390: // Pact of the Darkfallen - spellInfo->AttributesCu |= SPELL_ATTR0_CU_EXCLUDE_SELF; - break; case 18500: // Wing Buffet case 33086: // Wild Bite case 49749: // Piercing Blow @@ -3105,9 +3090,6 @@ void SpellMgr::LoadDbcDataCorrections() case 27937: // Anchor to Skulls spellInfo->rangeIndex = 13; break; - case 48743: // Death Pact - spellInfo->AttributesEx &= ~SPELL_ATTR1_CANT_TARGET_SELF; - break; // target allys instead of enemies, target A is src_caster, spells with effect like that have ally target // this is the only known exception, probably just wrong data case 29214: // Wrath of the Plaguebringer diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index f6f8fb4d080..78a8b02c044 100755 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -550,8 +550,8 @@ class SpellMgr // Spell difficulty uint32 GetSpellDifficultyId(uint32 spellId) const; void SetSpellDifficultyId(uint32 spellId, uint32 id); - uint32 GetSpellIdForDifficulty(uint32 spellId, Unit* caster) const; - SpellInfo const* GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit* caster) const; + uint32 GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) const; + SpellInfo const* GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit const* caster) const; // Spell Ranks table SpellChainNode const* GetSpellChainNode(uint32 spell_id) const; |