/* * Copyright (C) 2008-2018 TrinityCore * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "SpellInfo.h" #include "Battleground.h" #include "ConditionMgr.h" #include "Corpse.h" #include "DB2Stores.h" #include "GameTables.h" #include "InstanceScript.h" #include "Item.h" #include "ItemTemplate.h" #include "Log.h" #include "Map.h" #include "ObjectAccessor.h" #include "Pet.h" #include "Player.h" #include "Random.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "Vehicle.h" #include uint32 GetTargetFlagMask(SpellTargetObjectTypes objType) { switch (objType) { case TARGET_OBJECT_TYPE_DEST: return TARGET_FLAG_DEST_LOCATION; case TARGET_OBJECT_TYPE_UNIT_AND_DEST: return TARGET_FLAG_DEST_LOCATION | TARGET_FLAG_UNIT; case TARGET_OBJECT_TYPE_CORPSE_ALLY: return TARGET_FLAG_CORPSE_ALLY; case TARGET_OBJECT_TYPE_CORPSE_ENEMY: return TARGET_FLAG_CORPSE_ENEMY; case TARGET_OBJECT_TYPE_CORPSE: return TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY; case TARGET_OBJECT_TYPE_UNIT: return TARGET_FLAG_UNIT; case TARGET_OBJECT_TYPE_GOBJ: return TARGET_FLAG_GAMEOBJECT; case TARGET_OBJECT_TYPE_GOBJ_ITEM: return TARGET_FLAG_GAMEOBJECT_ITEM; case TARGET_OBJECT_TYPE_ITEM: return TARGET_FLAG_ITEM; case TARGET_OBJECT_TYPE_SRC: return TARGET_FLAG_SOURCE_LOCATION; default: return TARGET_FLAG_NONE; } } SpellImplicitTargetInfo::SpellImplicitTargetInfo(uint32 target) { _target = Targets(target); } bool SpellImplicitTargetInfo::IsArea() const { return GetSelectionCategory() == TARGET_SELECT_CATEGORY_AREA || GetSelectionCategory() == TARGET_SELECT_CATEGORY_CONE; } SpellTargetSelectionCategories SpellImplicitTargetInfo::GetSelectionCategory() const { return _data[_target].SelectionCategory; } SpellTargetReferenceTypes SpellImplicitTargetInfo::GetReferenceType() const { return _data[_target].ReferenceType; } SpellTargetObjectTypes SpellImplicitTargetInfo::GetObjectType() const { return _data[_target].ObjectType; } SpellTargetCheckTypes SpellImplicitTargetInfo::GetCheckType() const { return _data[_target].SelectionCheckType; } SpellTargetDirectionTypes SpellImplicitTargetInfo::GetDirectionType() const { return _data[_target].DirectionType; } float SpellImplicitTargetInfo::CalcDirectionAngle() const { switch (GetDirectionType()) { case TARGET_DIR_FRONT: return 0.0f; case TARGET_DIR_BACK: return static_cast(M_PI); case TARGET_DIR_RIGHT: return static_cast(-M_PI/2); case TARGET_DIR_LEFT: return static_cast(M_PI/2); case TARGET_DIR_FRONT_RIGHT: return static_cast(-M_PI/4); case TARGET_DIR_BACK_RIGHT: return static_cast(-3*M_PI/4); case TARGET_DIR_BACK_LEFT: return static_cast(3*M_PI/4); case TARGET_DIR_FRONT_LEFT: return static_cast(M_PI/4); case TARGET_DIR_RANDOM: return float(rand_norm())*static_cast(2*M_PI); default: return 0.0f; } } Targets SpellImplicitTargetInfo::GetTarget() const { return _target; } uint32 SpellImplicitTargetInfo::GetExplicitTargetMask(bool& srcSet, bool& dstSet) const { uint32 targetMask = 0; if (GetTarget() == TARGET_DEST_TRAJ) { if (!srcSet) targetMask = TARGET_FLAG_SOURCE_LOCATION; if (!dstSet) targetMask |= TARGET_FLAG_DEST_LOCATION; } else { switch (GetReferenceType()) { case TARGET_REFERENCE_TYPE_SRC: if (srcSet) break; targetMask = TARGET_FLAG_SOURCE_LOCATION; break; case TARGET_REFERENCE_TYPE_DEST: if (dstSet) break; targetMask = TARGET_FLAG_DEST_LOCATION; break; case TARGET_REFERENCE_TYPE_TARGET: switch (GetObjectType()) { case TARGET_OBJECT_TYPE_GOBJ: targetMask = TARGET_FLAG_GAMEOBJECT; break; case TARGET_OBJECT_TYPE_GOBJ_ITEM: targetMask = TARGET_FLAG_GAMEOBJECT_ITEM; break; case TARGET_OBJECT_TYPE_UNIT_AND_DEST: case TARGET_OBJECT_TYPE_UNIT: case TARGET_OBJECT_TYPE_DEST: switch (GetCheckType()) { case TARGET_CHECK_ENEMY: targetMask = TARGET_FLAG_UNIT_ENEMY; break; case TARGET_CHECK_ALLY: targetMask = TARGET_FLAG_UNIT_ALLY; break; case TARGET_CHECK_PARTY: targetMask = TARGET_FLAG_UNIT_PARTY; break; case TARGET_CHECK_RAID: targetMask = TARGET_FLAG_UNIT_RAID; break; case TARGET_CHECK_PASSENGER: targetMask = TARGET_FLAG_UNIT_PASSENGER; break; case TARGET_CHECK_RAID_CLASS: // nobreak; default: targetMask = TARGET_FLAG_UNIT; break; } break; default: break; } break; default: break; } } switch (GetObjectType()) { case TARGET_OBJECT_TYPE_SRC: srcSet = true; break; case TARGET_OBJECT_TYPE_DEST: case TARGET_OBJECT_TYPE_UNIT_AND_DEST: dstSet = true; break; default: break; } return targetMask; } SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_TARGETS] = { {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_AREA, 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_108 {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 109 TARGET_GAMEOBJECT_CONE_109 {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENTRY, TARGET_DIR_FRONT}, // 110 TARGET_UNIT_CONE_ENTRY_110 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 111 {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 112 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 113 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 114 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 115 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 116 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 117 {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 118 {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 119 {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 120 {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 121 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 122 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 123 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 124 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 125 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 126 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 127 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 128 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 129 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 130 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 131 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 132 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 133 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 134 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 135 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 136 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 137 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 138 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 139 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 140 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 141 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 142 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 143 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 144 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 145 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 146 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 147 {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 148 {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 149 {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 150 }; SpellEffectInfo::SpellEffectInfo(SpellInfo const* spellInfo, uint8 effIndex, SpellEffectEntry const* _effect) { _spellInfo = spellInfo; EffectIndex = _effect ? _effect->EffectIndex : effIndex; Effect = _effect ? _effect->Effect : 0; ApplyAuraName = _effect ? _effect->EffectAura : 0; ApplyAuraPeriod = _effect ? _effect->EffectAuraPeriod : 0; RealPointsPerLevel = _effect ? _effect->EffectRealPointsPerLevel : 0.0f; BasePoints = _effect ? _effect->EffectBasePoints : 0; PointsPerResource = _effect ? _effect->EffectPointsPerResource : 0.0f; Amplitude = _effect ? _effect->EffectAmplitude : 0.0f; ChainAmplitude = _effect ? _effect->EffectChainAmplitude : 0.0f; BonusCoefficient = _effect ? _effect->EffectBonusCoefficient : 0.0f; MiscValue = _effect ? _effect->EffectMiscValue[0] : 0; MiscValueB = _effect ? _effect->EffectMiscValue[1] : 0; Mechanic = Mechanics(_effect ? _effect->EffectMechanic : 0); PositionFacing = _effect ? _effect->EffectPosFacing : 0.0f; TargetA = SpellImplicitTargetInfo(_effect ? _effect->ImplicitTarget[0] : 0); TargetB = SpellImplicitTargetInfo(_effect ? _effect->ImplicitTarget[1] : 0); RadiusEntry = _effect && _effect->EffectRadiusIndex[0] ? sSpellRadiusStore.LookupEntry(_effect->EffectRadiusIndex[0]) : NULL; MaxRadiusEntry = _effect && _effect->EffectRadiusIndex[1] ? sSpellRadiusStore.LookupEntry(_effect->EffectRadiusIndex[1]) : NULL; ChainTargets = _effect ? _effect->EffectChainTargets : 0; ItemType = _effect ? _effect->EffectItemType : 0; TriggerSpell = _effect ? _effect->EffectTriggerSpell : 0; SpellClassMask = _effect ? _effect->EffectSpellClassMask : flag128(); BonusCoefficientFromAP = _effect ? _effect->BonusCoefficientFromAP : 0.0f; Scaling.Coefficient = _effect->Coefficient; Scaling.Variance = _effect->Variance; Scaling.ResourceCoefficient = _effect->ResourceCoefficient; ImplicitTargetConditions = NULL; } bool SpellEffectInfo::IsEffect() const { return Effect != 0; } bool SpellEffectInfo::IsEffect(SpellEffectName effectName) const { return Effect == uint32(effectName); } bool SpellEffectInfo::IsAura() const { return (IsUnitOwnedAuraEffect() || Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) && ApplyAuraName != 0; } bool SpellEffectInfo::IsAura(AuraType aura) const { return IsAura() && ApplyAuraName == uint32(aura); } bool SpellEffectInfo::IsTargetingArea() const { return TargetA.IsArea() || TargetB.IsArea(); } bool SpellEffectInfo::IsAreaAuraEffect() const { if (Effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || Effect == SPELL_EFFECT_APPLY_AREA_AURA_RAID || Effect == SPELL_EFFECT_APPLY_AREA_AURA_FRIEND || Effect == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || Effect == SPELL_EFFECT_APPLY_AREA_AURA_PET || Effect == SPELL_EFFECT_APPLY_AREA_AURA_OWNER) return true; return false; } bool SpellEffectInfo::IsFarUnitTargetEffect() const { return (Effect == SPELL_EFFECT_SUMMON_PLAYER) || (Effect == SPELL_EFFECT_SUMMON_RAF_FRIEND) || (Effect == SPELL_EFFECT_RESURRECT) || (Effect == SPELL_EFFECT_SKIN_PLAYER_CORPSE); } bool SpellEffectInfo::IsFarDestTargetEffect() const { return Effect == SPELL_EFFECT_TELEPORT_UNITS; } bool SpellEffectInfo::IsUnitOwnedAuraEffect() const { return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA; } int32 SpellEffectInfo::CalcValue(Unit const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/, Unit const* target /*= nullptr*/, float* variance /*= nullptr*/, int32 itemLevel /*= -1*/) const { float basePointsPerLevel = RealPointsPerLevel; // TODO: this needs to be a float, not rounded int32 basePoints = CalcBaseValue(caster, target, itemLevel); float value = bp ? *bp : basePoints; float comboDamage = PointsPerResource; if (Scaling.Variance) { float delta = fabs(Scaling.Variance * 0.5f); float valueVariance = frand(-delta, delta); value += basePoints * valueVariance; if (variance) *variance = valueVariance; } // base amount modification based on spell lvl vs caster lvl if (Scaling.Coefficient != 0.0f) { if (Scaling.ResourceCoefficient) comboDamage = Scaling.ResourceCoefficient * value; } else { if (GetScalingExpectedStat() == ExpectedStatType::None) { int32 level = caster ? int32(caster->getLevel()) : 0; if (level > int32(_spellInfo->MaxLevel) && _spellInfo->MaxLevel > 0) level = int32(_spellInfo->MaxLevel); level -= int32(_spellInfo->BaseLevel); if (level < 0) level = 0; value += level * basePointsPerLevel; } } // random damage if (caster) { // bonus amount from combo points if (caster->m_playerMovingMe && comboDamage) if (uint32 comboPoints = caster->m_playerMovingMe->GetComboPoints()) value += comboDamage * comboPoints; value = caster->ApplyEffectModifiers(_spellInfo, EffectIndex, value); } return int32(round(value)); } int32 SpellEffectInfo::CalcBaseValue(Unit const* caster, Unit const* target, int32 itemLevel) const { if (Scaling.Coefficient != 0.0f) { uint32 level = _spellInfo->SpellLevel; if (target && _spellInfo->IsPositiveEffect(EffectIndex) && (Effect == SPELL_EFFECT_APPLY_AURA)) level = target->getLevel(); else if (caster) level = caster->getLevel(); if (_spellInfo->BaseLevel && !_spellInfo->HasAttribute(SPELL_ATTR11_SCALES_WITH_ITEM_LEVEL) && _spellInfo->HasAttribute(SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING)) level = _spellInfo->BaseLevel; if (_spellInfo->Scaling.MinScalingLevel && _spellInfo->Scaling.MinScalingLevel > level) level = _spellInfo->Scaling.MinScalingLevel; if (_spellInfo->Scaling.MaxScalingLevel && _spellInfo->Scaling.MaxScalingLevel < level) level = _spellInfo->Scaling.MaxScalingLevel; float value = 0.0f; if (level > 0) { if (!_spellInfo->Scaling.Class) return 0; uint32 effectiveItemLevel = itemLevel != -1 ? uint32(itemLevel) : 1u; if (_spellInfo->Scaling.ScalesFromItemLevel || _spellInfo->HasAttribute(SPELL_ATTR11_SCALES_WITH_ITEM_LEVEL)) { if (_spellInfo->Scaling.ScalesFromItemLevel) effectiveItemLevel = _spellInfo->Scaling.ScalesFromItemLevel; if (_spellInfo->Scaling.Class == -8) { RandPropPointsEntry const* randPropPoints = sRandPropPointsStore.LookupEntry(effectiveItemLevel); if (!randPropPoints) randPropPoints = sRandPropPointsStore.AssertEntry(sRandPropPointsStore.GetNumRows() - 1); value = randPropPoints->DamageReplaceStat; } else value = GetRandomPropertyPoints(effectiveItemLevel, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); } else value = GetSpellScalingColumnForClass(sSpellScalingGameTable.GetRow(level), _spellInfo->Scaling.Class); if (_spellInfo->Scaling.Class == -7) { // todo: get inventorytype here if (GtCombatRatingsMultByILvl const* ratingMult = sCombatRatingsMultByILvlGameTable.GetRow(effectiveItemLevel)) value *= ratingMult->ArmorMultiplier; } } value *= Scaling.Coefficient; if (value != 0.0f && value < 1.0f) value = 1.0f; return int32(round(value)); } else { float value = BasePoints; ExpectedStatType stat = GetScalingExpectedStat(); if (stat != ExpectedStatType::None) { if (_spellInfo->HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) stat = ExpectedStatType::CreatureAutoAttackDps; // TODO - add expansion and content tuning id args? int32 level = caster ? int32(caster->getLevel()) : 1; value = sDB2Manager.EvaluateExpectedStat(stat, level, -2, 0, CLASS_NONE) * BasePoints / 100.0f; } return int32(round(value)); } } float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const { float multiplier = Amplitude; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell); return multiplier; } float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const { float multiplierPercent = ChainAmplitude * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell); return multiplierPercent / 100.0f; } bool SpellEffectInfo::HasRadius() const { return RadiusEntry != NULL; } bool SpellEffectInfo::HasMaxRadius() const { return MaxRadiusEntry != NULL; } float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const { const SpellRadiusEntry* entry = RadiusEntry; if (!HasRadius() && HasMaxRadius()) entry = MaxRadiusEntry; if (!entry) return 0.0f; float radius = entry->RadiusMin; // Client uses max if min is 0 if (radius == 0.0f) radius = entry->RadiusMax; if (caster) { radius += entry->RadiusPerLevel * caster->getLevel(); radius = std::min(radius, entry->RadiusMax); if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell); } return radius; } uint32 SpellEffectInfo::GetProvidedTargetMask() const { return GetTargetFlagMask(TargetA.GetObjectType()) | GetTargetFlagMask(TargetB.GetObjectType()); } uint32 SpellEffectInfo::GetMissingTargetMask(bool srcSet /*= false*/, bool dstSet /*= false*/, uint32 mask /*=0*/) const { uint32 effImplicitTargetMask = GetTargetFlagMask(GetUsedTargetObjectType()); uint32 providedTargetMask = GetProvidedTargetMask() | mask; // remove all flags covered by effect target mask if (providedTargetMask & TARGET_FLAG_UNIT_MASK) effImplicitTargetMask &= ~(TARGET_FLAG_UNIT_MASK); if (providedTargetMask & TARGET_FLAG_CORPSE_MASK) effImplicitTargetMask &= ~(TARGET_FLAG_UNIT_MASK | TARGET_FLAG_CORPSE_MASK); if (providedTargetMask & TARGET_FLAG_GAMEOBJECT_ITEM) effImplicitTargetMask &= ~(TARGET_FLAG_GAMEOBJECT_ITEM | TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_ITEM); if (providedTargetMask & TARGET_FLAG_GAMEOBJECT) effImplicitTargetMask &= ~(TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_GAMEOBJECT_ITEM); if (providedTargetMask & TARGET_FLAG_ITEM) effImplicitTargetMask &= ~(TARGET_FLAG_ITEM | TARGET_FLAG_GAMEOBJECT_ITEM); if (dstSet || providedTargetMask & TARGET_FLAG_DEST_LOCATION) effImplicitTargetMask &= ~(TARGET_FLAG_DEST_LOCATION); if (srcSet || providedTargetMask & TARGET_FLAG_SOURCE_LOCATION) effImplicitTargetMask &= ~(TARGET_FLAG_SOURCE_LOCATION); return effImplicitTargetMask; } SpellEffectImplicitTargetTypes SpellEffectInfo::GetImplicitTargetType() const { return _data[Effect].ImplicitTargetType; } SpellTargetObjectTypes SpellEffectInfo::GetUsedTargetObjectType() const { return _data[Effect].UsedTargetObjectType; } ExpectedStatType SpellEffectInfo::GetScalingExpectedStat() const { switch (Effect) { case SPELL_EFFECT_SCHOOL_DAMAGE: case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE: case SPELL_EFFECT_HEALTH_LEECH: case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: case SPELL_EFFECT_WEAPON_DAMAGE: return ExpectedStatType::CreatureSpellDamage; case SPELL_EFFECT_HEAL: case SPELL_EFFECT_HEAL_MECHANICAL: return ExpectedStatType::PlayerHealth; case SPELL_EFFECT_ENERGIZE: case SPELL_EFFECT_POWER_BURN: if (!MiscValue) return ExpectedStatType::PlayerMana; return ExpectedStatType::None; case SPELL_EFFECT_POWER_DRAIN: return ExpectedStatType::PlayerMana; case SPELL_EFFECT_APPLY_AURA: case SPELL_EFFECT_PERSISTENT_AREA_AURA: case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: case SPELL_EFFECT_APPLY_AREA_AURA_RAID: case SPELL_EFFECT_APPLY_AREA_AURA_PET: case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: case SPELL_EFFECT_APPLY_AURA_ON_PET: case SPELL_EFFECT_202: switch (ApplyAuraName) { case SPELL_AURA_PERIODIC_DAMAGE: case SPELL_AURA_MOD_DAMAGE_DONE: case SPELL_AURA_DAMAGE_SHIELD: case SPELL_AURA_PROC_TRIGGER_DAMAGE: case SPELL_AURA_PERIODIC_LEECH: case SPELL_AURA_MOD_DAMAGE_DONE_CREATURE: case SPELL_AURA_PERIODIC_HEALTH_FUNNEL: case SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS: case SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS: case SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS: return ExpectedStatType::CreatureSpellDamage; case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_MOD_DAMAGE_TAKEN: case SPELL_AURA_MOD_INCREASE_HEALTH: case SPELL_AURA_SCHOOL_ABSORB: case SPELL_AURA_MOD_REGEN: case SPELL_AURA_MANA_SHIELD: case SPELL_AURA_MOD_HEALING: case SPELL_AURA_MOD_HEALING_DONE: case SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT: case SPELL_AURA_MOD_MAX_HEALTH: case SPELL_AURA_MOD_INCREASE_HEALTH_2: case SPELL_AURA_SCHOOL_HEAL_ABSORB: return ExpectedStatType::PlayerHealth; case SPELL_AURA_PERIODIC_MANA_LEECH: return ExpectedStatType::PlayerMana; case SPELL_AURA_MOD_STAT: case SPELL_AURA_MOD_ATTACK_POWER: case SPELL_AURA_MOD_RANGED_ATTACK_POWER: return ExpectedStatType::PlayerPrimaryStat; case SPELL_AURA_MOD_RATING: return ExpectedStatType::PlayerSecondaryStat; case SPELL_AURA_MOD_RESISTANCE: case SPELL_AURA_MOD_BASE_RESISTANCE: case SPELL_AURA_MOD_TARGET_RESISTANCE: case SPELL_AURA_MOD_BONUS_ARMOR: return ExpectedStatType::ArmorConstant; case SPELL_AURA_PERIODIC_ENERGIZE: case SPELL_AURA_MOD_INCREASE_ENERGY: case SPELL_AURA_MOD_POWER_COST_SCHOOL: case SPELL_AURA_MOD_POWER_REGEN: case SPELL_AURA_POWER_BURN: case SPELL_AURA_MOD_MAX_POWER: if (!MiscValue) return ExpectedStatType::PlayerMana; return ExpectedStatType::None; default: break; } default: break; } return ExpectedStatType::None; } SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] = { // implicit target type used target object type {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 0 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 1 SPELL_EFFECT_INSTAKILL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 2 SPELL_EFFECT_SCHOOL_DAMAGE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 3 SPELL_EFFECT_DUMMY {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 4 SPELL_EFFECT_PORTAL_TELEPORT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 5 SPELL_EFFECT_TELEPORT_UNITS {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 6 SPELL_EFFECT_APPLY_AURA {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 7 SPELL_EFFECT_ENVIRONMENTAL_DAMAGE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 8 SPELL_EFFECT_POWER_DRAIN {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 9 SPELL_EFFECT_HEALTH_LEECH {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 10 SPELL_EFFECT_HEAL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 11 SPELL_EFFECT_BIND {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 12 SPELL_EFFECT_PORTAL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 13 SPELL_EFFECT_RITUAL_BASE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 16 SPELL_EFFECT_QUEST_COMPLETE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_CORPSE_ALLY}, // 18 SPELL_EFFECT_RESURRECT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 20 SPELL_EFFECT_DODGE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 21 SPELL_EFFECT_EVADE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 22 SPELL_EFFECT_PARRY {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 23 SPELL_EFFECT_BLOCK {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 24 SPELL_EFFECT_CREATE_ITEM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 25 SPELL_EFFECT_WEAPON {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 26 SPELL_EFFECT_DEFENSE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 28 SPELL_EFFECT_SUMMON {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 29 SPELL_EFFECT_LEAP {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 30 SPELL_EFFECT_ENERGIZE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 32 SPELL_EFFECT_TRIGGER_MISSILE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_GOBJ_ITEM}, // 33 SPELL_EFFECT_OPEN_LOCK {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 34 SPELL_EFFECT_SUMMON_CHANGE_ITEM {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 36 SPELL_EFFECT_LEARN_SPELL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 37 SPELL_EFFECT_SPELL_DEFENSE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 38 SPELL_EFFECT_DISPEL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 39 SPELL_EFFECT_LANGUAGE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 40 SPELL_EFFECT_DUAL_WIELD {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 41 SPELL_EFFECT_JUMP {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 42 SPELL_EFFECT_JUMP_DEST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 44 SPELL_EFFECT_SKILL_STEP {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 45 SPELL_EFFECT_ADD_HONOR {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 46 SPELL_EFFECT_SPAWN {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 47 SPELL_EFFECT_TRADE_SKILL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 48 SPELL_EFFECT_STEALTH {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 49 SPELL_EFFECT_DETECT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 50 SPELL_EFFECT_TRANS_DOOR {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 52 SPELL_EFFECT_GUARANTEE_HIT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 53 SPELL_EFFECT_ENCHANT_ITEM {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 54 SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 55 SPELL_EFFECT_TAMECREATURE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 56 SPELL_EFFECT_SUMMON_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 57 SPELL_EFFECT_LEARN_PET_SPELL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 58 SPELL_EFFECT_WEAPON_DAMAGE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 59 SPELL_EFFECT_CREATE_RANDOM_ITEM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 60 SPELL_EFFECT_PROFICIENCY {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 61 SPELL_EFFECT_SEND_EVENT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 62 SPELL_EFFECT_POWER_BURN {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 63 SPELL_EFFECT_THREAT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 64 SPELL_EFFECT_TRIGGER_SPELL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 66 SPELL_EFFECT_RECHARGE_ITEM {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 68 SPELL_EFFECT_INTERRUPT_CAST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 69 SPELL_EFFECT_DISTRACT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 70 SPELL_EFFECT_PULL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 71 SPELL_EFFECT_PICKPOCKET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 72 SPELL_EFFECT_ADD_FARSIGHT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 73 SPELL_EFFECT_UNTRAIN_TALENTS {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 74 SPELL_EFFECT_APPLY_GLYPH {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 75 SPELL_EFFECT_HEAL_MECHANICAL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 77 SPELL_EFFECT_SCRIPT_EFFECT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 78 SPELL_EFFECT_ATTACK {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 79 SPELL_EFFECT_SANCTUARY {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 80 SPELL_EFFECT_ADD_COMBO_POINTS {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 81 SPELL_EFFECT_CREATE_HOUSE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 82 SPELL_EFFECT_BIND_SIGHT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 83 SPELL_EFFECT_DUEL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 84 SPELL_EFFECT_STUCK {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 85 SPELL_EFFECT_SUMMON_PLAYER {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_GOBJ}, // 86 SPELL_EFFECT_ACTIVATE_OBJECT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_GOBJ}, // 87 SPELL_EFFECT_GAMEOBJECT_DAMAGE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_GOBJ}, // 88 SPELL_EFFECT_GAMEOBJECT_REPAIR {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_GOBJ}, // 89 SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 90 SPELL_EFFECT_KILL_CREDIT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 91 SPELL_EFFECT_THREAT_ALL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 93 SPELL_EFFECT_FORCE_DESELECT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 94 SPELL_EFFECT_SELF_RESURRECT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 95 SPELL_EFFECT_SKINNING {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 96 SPELL_EFFECT_CHARGE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 97 SPELL_EFFECT_CAST_BUTTON {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 98 SPELL_EFFECT_KNOCK_BACK {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 99 SPELL_EFFECT_DISENCHANT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 100 SPELL_EFFECT_INEBRIATE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 101 SPELL_EFFECT_FEED_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 102 SPELL_EFFECT_DISMISS_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 103 SPELL_EFFECT_REPUTATION {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 104 SPELL_EFFECT_SUMMON_OBJECT_SLOT1 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 106 SPELL_EFFECT_CHANGE_RAID_MARKER {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 107 SPELL_EFFECT_SUMMON_OBJECT_SLOT4 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 108 SPELL_EFFECT_DISPEL_MECHANIC {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 109 SPELL_EFFECT_SUMMON_DEAD_PET {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 110 SPELL_EFFECT_DESTROY_ALL_TOTEMS {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 111 SPELL_EFFECT_DURABILITY_DAMAGE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 112 SPELL_EFFECT_112 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_CORPSE_ALLY}, // 113 SPELL_EFFECT_RESURRECT_NEW {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 114 SPELL_EFFECT_ATTACK_ME {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_CORPSE_ENEMY}, // 116 SPELL_EFFECT_SKIN_PLAYER_CORPSE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 117 SPELL_EFFECT_SPIRIT_HEAL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 118 SPELL_EFFECT_SKILL {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 119 SPELL_EFFECT_APPLY_AREA_AURA_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 120 SPELL_EFFECT_TELEPORT_GRAVEYARD {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 121 SPELL_EFFECT_NORMALIZED_WEAPON_DMG {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 122 SPELL_EFFECT_122 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 123 SPELL_EFFECT_SEND_TAXI {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 124 SPELL_EFFECT_PULL_TOWARDS {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 125 SPELL_EFFECT_MODIFY_THREAT_PERCENT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 126 SPELL_EFFECT_STEAL_BENEFICIAL_BUFF {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 127 SPELL_EFFECT_PROSPECTING {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 130 SPELL_EFFECT_REDIRECT_THREAT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 131 SPELL_EFFECT_PLAY_SOUND {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 132 SPELL_EFFECT_PLAY_MUSIC {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 133 SPELL_EFFECT_UNLEARN_SPECIALIZATION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 134 SPELL_EFFECT_KILL_CREDIT2 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 135 SPELL_EFFECT_CALL_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 136 SPELL_EFFECT_HEAL_PCT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 137 SPELL_EFFECT_ENERGIZE_PCT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 138 SPELL_EFFECT_LEAP_BACK {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 139 SPELL_EFFECT_CLEAR_QUEST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 140 SPELL_EFFECT_FORCE_CAST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 141 SPELL_EFFECT_FORCE_CAST_WITH_VALUE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 144 SPELL_EFFECT_KNOCK_BACK_DEST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 145 SPELL_EFFECT_PULL_TOWARDS_DEST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 146 SPELL_EFFECT_ACTIVATE_RUNE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 147 SPELL_EFFECT_QUEST_FAIL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 148 SPELL_EFFECT_TRIGGER_MISSILE_SPELL_WITH_VALUE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 149 SPELL_EFFECT_CHARGE_DEST {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 150 SPELL_EFFECT_QUEST_START {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 151 SPELL_EFFECT_TRIGGER_SPELL_2 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 152 SPELL_EFFECT_SUMMON_RAF_FRIEND {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 153 SPELL_EFFECT_CREATE_TAMED_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 154 SPELL_EFFECT_DISCOVER_TAXI {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_UNIT}, // 155 SPELL_EFFECT_TITAN_GRIP {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 156 SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 157 SPELL_EFFECT_CREATE_ITEM_2 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 158 SPELL_EFFECT_MILLING {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 159 SPELL_EFFECT_ALLOW_RENAME_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 160 SPELL_EFFECT_FORCE_CAST_2 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 161 SPELL_EFFECT_TALENT_SPEC_COUNT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 162 SPELL_EFFECT_TALENT_SPEC_SELECT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 163 SPELL_EFFECT_163 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 164 SPELL_EFFECT_REMOVE_AURA {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 165 SPELL_EFFECT_165 {EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 166 SPELL_EFFECT_GIVE_CURRENCY {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 167 SPELL_EFFECT_167 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 168 SPELL_EFFECT_168 {EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 169 SPELL_EFFECT_DESTROY_ITEM {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 170 SPELL_EFFECT_170 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 171 SPELL_EFFECT_171 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 172 SPELL_EFFECT_172 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 173 SPELL_EFFECT_UNLOCK_GUILD_VAULT_TAB {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 174 SPELL_EFFECT_174 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 175 SPELL_EFFECT_175 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 176 SPELL_EFFECT_176 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 177 SPELL_EFFECT_177 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 178 SPELL_EFFECT_178 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 179 SPELL_EFFECT_CREATE_AREATRIGGER {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 180 SPELL_EFFECT_UPDATE_AREATRIGGER {EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 181 SPELL_EFFECT_REMOVE_TALENT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 182 SPELL_EFFECT_182 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 183 SPELL_EFFECT_183 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 184 SPELL_EFFECT_REPUTATION_2 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 185 SPELL_EFFECT_185 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 186 SPELL_EFFECT_186 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 187 SPELL_EFFECT_RANDOMIZE_ARCHAEOLOGY_DIGSITES {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 188 SPELL_EFFECT_188 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 189 SPELL_EFFECT_LOOT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 190 SPELL_EFFECT_190 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 191 SPELL_EFFECT_TELEPORT_TO_DIGSITE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 192 SPELL_EFFECT_192 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 193 SPELL_EFFECT_193 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 194 SPELL_EFFECT_194 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 195 SPELL_EFFECT_195 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 196 SPELL_EFFECT_196 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 197 SPELL_EFFECT_197 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 198 SPELL_EFFECT_PLAY_SCENE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 199 SPELL_EFFECT_199 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 200 SPELL_EFFECT_HEAL_BATTLEPET_PCT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 201 SPELL_EFFECT_ENABLE_BATTLE_PETS {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 202 SPELL_EFFECT_202 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 203 SPELL_EFFECT_203 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 206 SPELL_EFFECT_ALTER_IETM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 207 SPELL_EFFECT_LAUNCH_QUEST_TASK {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 208 SPELL_EFFECT_208 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 209 SPELL_EFFECT_209 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 210 SPELL_EFFECT_LEARN_GARRISON_BUILDING {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 211 SPELL_EFFECT_LEARN_GARRISON_SPECIALIZATION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 212 SPELL_EFFECT_212 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 213 SPELL_EFFECT_213 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 214 SPELL_EFFECT_CREATE_GARRISON {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 215 SPELL_EFFECT_UPGRADE_CHARACTER_SPELLS {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 216 SPELL_EFFECT_CREATE_SHIPMENT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 217 SPELL_EFFECT_UPGRADE_GARRISON {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 218 SPELL_EFFECT_218 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 219 SPELL_EFFECT_CREATE_CONVERSATION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 221 SPELL_EFFECT_221 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 223 SPELL_EFFECT_CHANGE_ITEM_BONUSES {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 224 SPELL_EFFECT_ACTIVATE_GARRISON_BUILDING {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 225 SPELL_EFFECT_GRANT_BATTLEPET_LEVEL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 226 SPELL_EFFECT_226 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 227 SPELL_EFFECT_227 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 228 SPELL_EFFECT_228 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 229 SPELL_EFFECT_SET_FOLLOWER_QUALITY {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 230 SPELL_EFFECT_INCREASE_FOLLOWER_ITEM_LEVEL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 231 SPELL_EFFECT_INCREASE_FOLLOWER_EXPERIENCE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 232 SPELL_EFFECT_REMOVE_PHASE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 233 SPELL_EFFECT_RANDOMIZE_FOLLOWER_ABILITIES {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 234 SPELL_EFFECT_234 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 235 SPELL_EFFECT_235 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 236 SPELL_EFFECT_GIVE_EXPERIENCE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 237 SPELL_EFFECT_GIVE_RESTED_EXPERIENCE_BONUS {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 238 SPELL_EFFECT_INCREASE_SKILL {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 239 SPELL_EFFECT_END_GARRISON_BUILDING_CONSTRUCTION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 240 SPELL_EFFECT_240 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 241 SPELL_EFFECT_241 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 242 SPELL_EFFECT_242 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 243 SPELL_EFFECT_APPLY_ENCHANT_ILLUSION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 244 SPELL_EFFECT_LEARN_FOLLOWER_ABILITY {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 245 SPELL_EFFECT_UPGRADE_HEIRLOOM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 246 SPELL_EFFECT_FINISH_GARRISON_MISSION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 247 SPELL_EFFECT_ADD_GARRISON_MISSION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 248 SPELL_EFFECT_FINISH_SHIPMENT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 249 SPELL_EFFECT_249 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 250 SPELL_EFFECT_TAKE_SCREENSHOT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 251 SPELL_EFFECT_SET_GARRISON_CACHE_SIZE {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 252 SPELL_EFFECT_252 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 253 SPELL_EFFECT_GIVE_HONOR {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 254 SPELL_EFFECT_254 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 255 SPELL_EFFECT_LEARN_TRANSMOG_SET {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 256 SPELL_EFFECT_256 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 257 SPELL_EFFECT_257 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 258 SPELL_EFFECT_MODIFY_KEYSTONE {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 259 SPELL_EFFECT_RESPEC_AZERITE_EMPOWERED_ITEM {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 260 SPELL_EFFECT_SUMMON_STABLED_PET {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 261 SPELL_EFFECT_SCRAP_ITEM }; SpellInfo::SpellInfo(SpellInfoLoadHelper const& data, SpellEffectEntryMap const& effectsMap, SpellVisualMap&& visuals) : _hasPowerDifficultyData(false) { Id = data.Entry->ID; for (SpellEffectEntryMap::value_type const& itr : effectsMap) { SpellEffectEntryVector const& effects = itr.second; _effects[itr.first].resize(effects.size()); for (size_t i = 0; i < effects.size(); ++i) if (SpellEffectEntry const* effect = effects[i]) _effects[itr.first][effect->EffectIndex] = new SpellEffectInfo(this, effect->EffectIndex, effect); } SpellName = data.Entry->Name; // SpellMiscEntry SpellMiscEntry const* _misc = data.Misc; Attributes = _misc ? _misc->Attributes[0] : 0; AttributesEx = _misc ? _misc->Attributes[1] : 0; AttributesEx2 = _misc ? _misc->Attributes[2] : 0; AttributesEx3 = _misc ? _misc->Attributes[3] : 0; AttributesEx4 = _misc ? _misc->Attributes[4] : 0; AttributesEx5 = _misc ? _misc->Attributes[5] : 0; AttributesEx6 = _misc ? _misc->Attributes[6] : 0; AttributesEx7 = _misc ? _misc->Attributes[7] : 0; AttributesEx8 = _misc ? _misc->Attributes[8] : 0; AttributesEx9 = _misc ? _misc->Attributes[9] : 0; AttributesEx10 = _misc ? _misc->Attributes[10] : 0; AttributesEx11 = _misc ? _misc->Attributes[11] : 0; AttributesEx12 = _misc ? _misc->Attributes[12] : 0; AttributesEx13 = _misc ? _misc->Attributes[13] : 0; CastTimeEntry = _misc ? (_misc->CastingTimeIndex ? sSpellCastTimesStore.LookupEntry(_misc->CastingTimeIndex) : NULL) : NULL; DurationEntry = _misc ? (_misc->DurationIndex ? sSpellDurationStore.LookupEntry(_misc->DurationIndex) : NULL) : NULL; RangeIndex = _misc ? _misc->RangeIndex : 0; RangeEntry = _misc ? (_misc->RangeIndex ? sSpellRangeStore.LookupEntry(_misc->RangeIndex) : NULL) : NULL; Speed = _misc ? _misc->Speed : 0; SchoolMask = _misc ? _misc->SchoolMask : 0; AttributesCu = 0; IconFileDataId = _misc ? _misc->SpellIconFileDataID : 0; ActiveIconFileDataId = _misc ? _misc->ActiveIconFileDataID : 0; _visuals = std::move(visuals); // sort all visuals so that the ones without a condition requirement are last on the list for (auto& visualPair : _visuals) std::sort(visualPair.second.begin(), visualPair.second.end(), [](SpellXSpellVisualEntry const* first, SpellXSpellVisualEntry const* second) { return first->CasterPlayerConditionID > second->CasterPlayerConditionID; }); // SpellScalingEntry SpellScalingEntry const* _scaling = data.Scaling; Scaling.Class = _scaling ? _scaling->Class : 0; Scaling.MinScalingLevel = _scaling ? _scaling->MinScalingLevel : 0; Scaling.MaxScalingLevel = _scaling ? _scaling->MaxScalingLevel : 0; Scaling.ScalesFromItemLevel = _scaling ? _scaling->ScalesFromItemLevel : 0; // SpellAuraOptionsEntry SpellAuraOptionsEntry const* _options = data.AuraOptions; SpellProcsPerMinuteEntry const* _ppm = _options ? sSpellProcsPerMinuteStore.LookupEntry(_options->SpellProcsPerMinuteID) : nullptr; ProcFlags = _options ? _options->ProcTypeMask[0] : 0; ProcChance = _options ? _options->ProcChance : 0; ProcCharges = _options ? _options->ProcCharges : 0; ProcCooldown = _options ? _options->ProcCategoryRecovery : 0; ProcBasePPM = _ppm ? _ppm->BaseProcRate : 0.0f; if (_options) ProcPPMMods = sDB2Manager.GetSpellProcsPerMinuteMods(_options->SpellProcsPerMinuteID); StackAmount = _options ? _options->CumulativeAura : 0; // SpellAuraRestrictionsEntry SpellAuraRestrictionsEntry const* _aura = data.AuraRestrictions; CasterAuraState = _aura ? _aura->CasterAuraState : 0; TargetAuraState = _aura ? _aura->TargetAuraState : 0; ExcludeCasterAuraState = _aura ? _aura->ExcludeCasterAuraState : 0; ExcludeTargetAuraState = _aura ? _aura->ExcludeTargetAuraState : 0; CasterAuraSpell = _aura ? _aura->CasterAuraSpell : 0; TargetAuraSpell = _aura ? _aura->TargetAuraSpell : 0; ExcludeCasterAuraSpell = _aura ? _aura->ExcludeCasterAuraSpell : 0; ExcludeTargetAuraSpell = _aura ? _aura->ExcludeTargetAuraSpell : 0; // SpellCastingRequirementsEntry SpellCastingRequirementsEntry const* _castreq = data.CastingRequirements; RequiresSpellFocus = _castreq ? _castreq->RequiresSpellFocus : 0; FacingCasterFlags = _castreq ? _castreq->FacingCasterFlags : 0; RequiredAreasID = _castreq ? _castreq->RequiredAreasID : -1; // SpellCategoriesEntry SpellCategoriesEntry const* _categorie = data.Categories; CategoryId = _categorie ? _categorie->Category : 0; Dispel = _categorie ? _categorie->DispelType : 0; Mechanic = _categorie ? _categorie->Mechanic : 0; StartRecoveryCategory = _categorie ? _categorie->StartRecoveryCategory : 0; DmgClass = _categorie ? _categorie->DefenseType : 0; PreventionType = _categorie ? _categorie->PreventionType : 0; ChargeCategoryId = _categorie ? _categorie->ChargeCategory : 0; // SpellClassOptionsEntry SpellClassOptionsEntry const* _class = data.ClassOptions; SpellFamilyName = _class ? _class->SpellClassSet : 0; SpellFamilyFlags = _class ? _class->SpellClassMask : flag128(); // SpellCooldownsEntry SpellCooldownsEntry const* _cooldowns = data.Cooldowns; RecoveryTime = _cooldowns ? _cooldowns->RecoveryTime : 0; CategoryRecoveryTime = _cooldowns ? _cooldowns->CategoryRecoveryTime : 0; StartRecoveryTime = _cooldowns ? _cooldowns->StartRecoveryTime : 0; // SpellEquippedItemsEntry SpellEquippedItemsEntry const* _equipped = data.EquippedItems; EquippedItemClass = _equipped ? _equipped->EquippedItemClass : -1; EquippedItemSubClassMask = _equipped ?_equipped->EquippedItemSubclass : -1; EquippedItemInventoryTypeMask = _equipped ? _equipped->EquippedItemInvTypes : -1; // SpellInterruptsEntry if (SpellInterruptsEntry const* _interrupt = data.Interrupts) { InterruptFlags = _interrupt->InterruptFlags; std::copy(std::begin(_interrupt->AuraInterruptFlags), std::end(_interrupt->AuraInterruptFlags), AuraInterruptFlags.begin()); std::copy(std::begin(_interrupt->ChannelInterruptFlags), std::end(_interrupt->ChannelInterruptFlags), ChannelInterruptFlags.begin()); } else { InterruptFlags = 0; AuraInterruptFlags.fill(0); ChannelInterruptFlags.fill(0); } // SpellLevelsEntry SpellLevelsEntry const* _levels = data.Levels; MaxLevel = _levels ? _levels->MaxLevel : 0; BaseLevel = _levels ? _levels->BaseLevel : 0; SpellLevel = _levels ? _levels->SpellLevel : 0; // SpellPowerEntry PowerCosts = sDB2Manager.GetSpellPowers(Id, DIFFICULTY_NONE, &_hasPowerDifficultyData); // SpellReagentsEntry SpellReagentsEntry const* _reagents = data.Reagents; for (uint8 i = 0; i < MAX_SPELL_REAGENTS; ++i) Reagent[i] = _reagents ? _reagents->Reagent[i] : 0; for (uint8 i = 0; i < MAX_SPELL_REAGENTS; ++i) ReagentCount[i] = _reagents ? _reagents->ReagentCount[i] : 0; // SpellShapeshiftEntry SpellShapeshiftEntry const* _shapeshift = data.Shapeshift; Stances = _shapeshift ? MAKE_PAIR64(_shapeshift->ShapeshiftMask[0], _shapeshift->ShapeshiftMask[1]) : 0; StancesNot = _shapeshift ? MAKE_PAIR64(_shapeshift->ShapeshiftExclude[0], _shapeshift->ShapeshiftExclude[1]) : 0; // SpellTargetRestrictionsEntry SpellTargetRestrictionsEntry const* _target = data.TargetRestrictions; ConeAngle = _target ? _target->ConeDegrees : 0.f; Width = _target ? _target->Width : 0.f; Targets = _target ? _target->Targets : 0; TargetCreatureType = _target ? _target->TargetCreatureType : 0; MaxAffectedTargets = _target ? _target->MaxTargets : 0; MaxTargetLevel = _target ? _target->MaxTargetLevel : 0; // SpellTotemsEntry SpellTotemsEntry const* _totem = data.Totems; for (uint8 i = 0; i < 2; ++i) TotemCategory[i] = _totem ? _totem->RequiredTotemCategoryID[i] : 0; for (uint8 i = 0; i < 2; ++i) Totem[i] = _totem ? _totem->Totem[i] : 0; ChainEntry = NULL; ExplicitTargetMask = 0; _spellSpecific = SPELL_SPECIFIC_NORMAL; _auraState = AURA_STATE_NONE; } SpellInfo::~SpellInfo() { _UnloadImplicitTargetConditionLists(); _UnloadSpellEffects(); } void SpellInfo::_UnloadSpellEffects() { for (SpellEffectInfoMap::value_type& i : _effects) for (size_t j = 0; j < i.second.size(); ++j) delete i.second[j]; _effects.clear(); } uint32 SpellInfo::GetCategory() const { return CategoryId; } bool SpellInfo::HasEffect(uint32 difficulty, SpellEffectName effect) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* eff : effects) { if (eff && eff->IsEffect(effect)) return true; } return false; } bool SpellInfo::HasEffect(SpellEffectName effect) const { for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* eff : itr->second) { if (eff && eff->IsEffect(effect)) return true; } } return false; } bool SpellInfo::HasAura(uint32 difficulty, AuraType aura) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* effect : effects) { if (effect && effect->IsAura(aura)) return true; } return false; } bool SpellInfo::HasAreaAuraEffect(uint32 difficulty) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* effect : effects) { if (effect && effect->IsAreaAuraEffect()) return true; } return false; } bool SpellInfo::HasAreaAuraEffect() const { for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (effect && effect->IsAreaAuraEffect()) return true; } } return false; } bool SpellInfo::HasOnlyDamageEffects() const { for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (!effect) continue; switch (effect->Effect) { case SPELL_EFFECT_WEAPON_DAMAGE: case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: case SPELL_EFFECT_SCHOOL_DAMAGE: case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE: case SPELL_EFFECT_HEALTH_LEECH: case SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT: continue; default: return false; } } } return true; } bool SpellInfo::HasAnyAuraInterruptFlag() const { return std::find_if(AuraInterruptFlags.begin(), AuraInterruptFlags.end(), [](uint32 flag) { return flag != 0; }) != AuraInterruptFlags.end(); } bool SpellInfo::IsExplicitDiscovery() const { SpellEffectInfo const* effect0 = GetEffect(DIFFICULTY_NONE, EFFECT_0); SpellEffectInfo const* effect1 = GetEffect(DIFFICULTY_NONE, EFFECT_1); return ((effect0 && (effect0->Effect == SPELL_EFFECT_CREATE_RANDOM_ITEM || effect0->Effect == SPELL_EFFECT_CREATE_LOOT)) && effect1 && effect1->Effect == SPELL_EFFECT_SCRIPT_EFFECT) || Id == 64323; } bool SpellInfo::IsLootCrafting() const { return HasEffect(SPELL_EFFECT_CREATE_RANDOM_ITEM) || HasEffect(SPELL_EFFECT_CREATE_LOOT); } bool SpellInfo::IsQuestTame() const { SpellEffectInfo const* effect0 = GetEffect(DIFFICULTY_NONE, EFFECT_0); SpellEffectInfo const* effect1 = GetEffect(DIFFICULTY_NONE, EFFECT_1); return effect0 && effect1 && effect0->Effect == SPELL_EFFECT_THREAT && effect1->Effect == SPELL_EFFECT_APPLY_AURA && effect1->ApplyAuraName == SPELL_AURA_DUMMY; } bool SpellInfo::IsProfession(uint32 difficulty) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* effect : effects) { if (effect && effect->Effect == SPELL_EFFECT_SKILL) { uint32 skill = effect->MiscValue; if (IsProfessionSkill(skill)) return true; } } return false; } bool SpellInfo::IsPrimaryProfession(uint32 difficulty) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for(SpellEffectInfo const* effect : effects) { if (effect && effect->Effect == SPELL_EFFECT_SKILL) { uint32 skill = effect->MiscValue; if (IsPrimaryProfessionSkill(skill)) return true; } } return false; } bool SpellInfo::IsPrimaryProfessionFirstRank(uint32 difficulty) const { return IsPrimaryProfession(difficulty) && GetRank() == 1; } bool SpellInfo::IsAbilityOfSkillType(uint32 skillType) const { SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(Id); for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) if (_spell_idx->second->SkillLine == int32(skillType)) return true; return false; } bool SpellInfo::IsAffectingArea(uint32 difficulty) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* effect : effects) { if (effect && effect->IsEffect() && (effect->IsTargetingArea() || effect->IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) || effect->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(uint32 difficulty) const { SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* effect : effects) { if (effect && effect->IsEffect() && effect->IsTargetingArea()) return true; } return false; } bool SpellInfo::NeedsExplicitUnitTarget() const { return (GetExplicitTargetMask() & TARGET_FLAG_UNIT_MASK) != 0; } bool SpellInfo::NeedsToBeTriggeredByCaster(SpellInfo const* triggeringSpell, uint32 difficulty) const { if (NeedsExplicitUnitTarget()) return true; /* for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (Effects[i].IsEffect()) { if (Effects[i].TargetA.GetSelectionCategory() == TARGET_SELECT_CATEGORY_CHANNEL || Effects[i].TargetB.GetSelectionCategory() == TARGET_SELECT_CATEGORY_CHANNEL) return true; } } */ if (triggeringSpell->IsChanneled()) { uint32 mask = 0; SpellEffectInfoVector effects = GetEffectsForDifficulty(difficulty); for (SpellEffectInfo const* effect : effects) { if (!effect) continue; if (effect->TargetA.GetTarget() != TARGET_UNIT_CASTER && effect->TargetA.GetTarget() != TARGET_DEST_CASTER && effect->TargetB.GetTarget() != TARGET_UNIT_CASTER && effect->TargetB.GetTarget() != TARGET_DEST_CASTER) { mask |= effect->GetProvidedTargetMask(); } } if (mask & TARGET_FLAG_UNIT_MASK) return true; } return false; } bool SpellInfo::IsPassive() const { return HasAttribute(SPELL_ATTR0_PASSIVE); } bool SpellInfo::IsAutocastable() const { if (IsPassive()) return false; if (HasAttribute(SPELL_ATTR1_UNAUTOCASTABLE_BY_PET)) return false; return true; } bool SpellInfo::IsStackableWithRanks() const { if (IsPassive()) return false; // All stance spells. if any better way, change it. SpellEffectInfoVector effects = GetEffectsForDifficulty(DIFFICULTY_NONE); for (SpellEffectInfo const* effect : effects) { if (!effect) continue; switch (SpellFamilyName) { case SPELLFAMILY_PALADIN: // Paladin aura Spell if (effect->Effect == SPELL_EFFECT_APPLY_AREA_AURA_RAID) return false; break; case SPELLFAMILY_DRUID: // Druid form Spell if (effect->Effect == SPELL_EFFECT_APPLY_AURA && effect->ApplyAuraName == SPELL_AURA_MOD_SHAPESHIFT) return false; break; } } return true; } bool SpellInfo::IsPassiveStackableWithRanks(uint32 difficulty) const { return IsPassive() && !HasEffect(difficulty, SPELL_EFFECT_APPLY_AURA); } bool SpellInfo::IsMultiSlotAura() const { return IsPassive() || Id == 55849 || Id == 40075 || Id == 44413; // Power Spark, Fel Flak Fire, Incanter's Absorption } bool SpellInfo::IsStackableOnOneSlotWithDifferentCasters() const { /// TODO: Re-verify meaning of SPELL_ATTR3_STACK_FOR_DIFF_CASTERS and update conditions here return StackAmount > 1 && !IsChanneled() && !HasAttribute(SPELL_ATTR3_STACK_FOR_DIFF_CASTERS); } bool SpellInfo::IsCooldownStartedOnEvent() const { if (HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE)) return true; SpellCategoryEntry const* category = sSpellCategoryStore.LookupEntry(CategoryId); return category && category->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT; } bool SpellInfo::IsDeathPersistent() const { return HasAttribute(SPELL_ATTR3_DEATH_PERSISTENT); } bool SpellInfo::IsRequiringDeadTarget() const { return HasAttribute(SPELL_ATTR3_ONLY_TARGET_GHOSTS); } bool SpellInfo::IsAllowingDeadTarget() const { return HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD) || Targets & (TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_DEAD); } bool SpellInfo::IsGroupBuff() const { SpellEffectInfoVector effects = GetEffectsForDifficulty(DIFFICULTY_NONE); for (SpellEffectInfo const* effect : effects) { if (!effect) continue; switch (effect->TargetA.GetCheckType()) { case TARGET_CHECK_PARTY: case TARGET_CHECK_RAID: case TARGET_CHECK_RAID_CLASS: return true; default: break; } } return false; } bool SpellInfo::CanBeUsedInCombat() const { return !HasAttribute(SPELL_ATTR0_CANT_USED_IN_COMBAT); } bool SpellInfo::IsPositive() const { return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE); } bool SpellInfo::IsPositiveEffect(uint8 effIndex) const { switch (effIndex) { default: case 0: return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE_EFF0); case 1: return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE_EFF1); case 2: return !HasAttribute(SPELL_ATTR0_CU_NEGATIVE_EFF2); } } bool SpellInfo::IsChanneled() const { return HasAttribute(SpellAttr1(SPELL_ATTR1_CHANNELED_1 | SPELL_ATTR1_CHANNELED_2)); } bool SpellInfo::IsMoveAllowedChannel() const { return IsChanneled() && HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING); } bool SpellInfo::NeedsComboPoints() const { return HasAttribute(SpellAttr1(SPELL_ATTR1_REQ_COMBO_POINTS1 | SPELL_ATTR1_REQ_COMBO_POINTS2)); } bool SpellInfo::IsNextMeleeSwingSpell() const { return HasAttribute(SpellAttr0(SPELL_ATTR0_ON_NEXT_SWING | SPELL_ATTR0_ON_NEXT_SWING_2)); } bool SpellInfo::IsBreakingStealth() const { return !HasAttribute(SPELL_ATTR1_NOT_BREAK_STEALTH); } bool SpellInfo::IsRangedWeaponSpell() const { return (SpellFamilyName == SPELLFAMILY_HUNTER && !(SpellFamilyFlags[1] & 0x10000000)) // for 53352, cannot find better way || (EquippedItemSubClassMask & ITEM_SUBCLASS_MASK_WEAPON_RANGED); } bool SpellInfo::IsAutoRepeatRangedSpell() const { return HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG); } bool SpellInfo::HasInitialAggro() const { return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)); } WeaponAttackType SpellInfo::GetAttackType() const { WeaponAttackType result; switch (DmgClass) { case SPELL_DAMAGE_CLASS_MELEE: if (HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) result = OFF_ATTACK; else result = BASE_ATTACK; break; case SPELL_DAMAGE_CLASS_RANGED: result = IsRangedWeaponSpell() ? RANGED_ATTACK : BASE_ATTACK; break; default: // Wands if (IsAutoRepeatRangedSpell()) result = RANGED_ATTACK; else result = BASE_ATTACK; break; } return result; } bool SpellInfo::IsItemFitToSpellRequirements(Item const* item) const { // item neutral spell if (EquippedItemClass == -1) return true; // item dependent spell if (item && item->IsFitToSpellRequirements(this)) return true; return false; } bool SpellInfo::IsAffected(uint32 familyName, flag128 const& familyFlags) const { if (!familyName) return true; if (familyName != SpellFamilyName) return false; if (familyFlags && !(familyFlags & SpellFamilyFlags)) return false; return true; } bool SpellInfo::IsAffectedBySpellMods() const { return !HasAttribute(SPELL_ATTR3_NO_DONE_BONUS); } bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const { if (!IsAffectedBySpellMods()) return false; SpellInfo const* affectSpell = sSpellMgr->GetSpellInfo(mod->spellId); if (!affectSpell) return false; return IsAffected(affectSpell->SpellFamilyName, mod->mask); } bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const { // aura can't be pierced if (!auraSpellInfo || auraSpellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; // these spells pierce all available spells (Resurrection Sickness for example) if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return true; // these spells (Cyclone for example) can pierce all... if (HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) { // ...but not these (Divine shield, Ice block, Cyclone and Banish for example) if (!auraSpellInfo || (auraSpellInfo->Mechanic != MECHANIC_IMMUNE_SHIELD && auraSpellInfo->Mechanic != MECHANIC_INVULNERABILITY && (auraSpellInfo->Mechanic != MECHANIC_BANISH || (IsRankOf(auraSpellInfo) && auraSpellInfo->Dispel != DISPEL_NONE)))) // Banish shouldn't be immune to itself, but Cyclone should return true; } // Dispels other auras on immunity, check if this spell makes the unit immune to aura if (HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY) && CanSpellProvideImmunityAgainstAura(auraSpellInfo)) return true; return false; } bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const { // These auras (like Divine Shield) can't be dispelled if (auraSpellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; // These spells (like Mass Dispel) can dispel all auras if (HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return true; // These auras (Cyclone for example) are not dispelable if (auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) return false; return true; } bool SpellInfo::IsSingleTarget() const { // all other single target spells have if it has AttributesEx5 if (HasAttribute(SPELL_ATTR5_SINGLE_TARGET_SPELL)) return true; switch (GetSpellSpecific()) { case SPELL_SPECIFIC_JUDGEMENT: return true; default: break; } return false; } bool SpellInfo::IsAuraExclusiveBySpecificWith(SpellInfo const* spellInfo) const { SpellSpecificType spellSpec1 = GetSpellSpecific(); SpellSpecificType spellSpec2 = spellInfo->GetSpellSpecific(); switch (spellSpec1) { case SPELL_SPECIFIC_WARLOCK_ARMOR: case SPELL_SPECIFIC_MAGE_ARMOR: case SPELL_SPECIFIC_ELEMENTAL_SHIELD: case SPELL_SPECIFIC_MAGE_POLYMORPH: case SPELL_SPECIFIC_PRESENCE: case SPELL_SPECIFIC_CHARM: case SPELL_SPECIFIC_SCROLL: case SPELL_SPECIFIC_WARRIOR_ENRAGE: case SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE: case SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT: return spellSpec1 == spellSpec2; case SPELL_SPECIFIC_FOOD: return spellSpec2 == SPELL_SPECIFIC_FOOD || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK; case SPELL_SPECIFIC_DRINK: return spellSpec2 == SPELL_SPECIFIC_DRINK || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK; case SPELL_SPECIFIC_FOOD_AND_DRINK: return spellSpec2 == SPELL_SPECIFIC_FOOD || spellSpec2 == SPELL_SPECIFIC_DRINK || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK; default: return false; } } bool SpellInfo::IsAuraExclusiveBySpecificPerCasterWith(SpellInfo const* spellInfo) const { SpellSpecificType spellSpec = GetSpellSpecific(); switch (spellSpec) { case SPELL_SPECIFIC_SEAL: case SPELL_SPECIFIC_HAND: case SPELL_SPECIFIC_AURA: case SPELL_SPECIFIC_STING: case SPELL_SPECIFIC_CURSE: case SPELL_SPECIFIC_BANE: case SPELL_SPECIFIC_ASPECT: case SPELL_SPECIFIC_JUDGEMENT: case SPELL_SPECIFIC_WARLOCK_CORRUPTION: return spellSpec == spellInfo->GetSpellSpecific(); default: return false; } } SpellCastResult SpellInfo::CheckShapeshift(uint32 form) const { // talents that learn spells can have stance requirements that need ignore // (this requirement only for client-side stance show in talent description) /* TODO: 6.x fix this in proper way (probably spell flags/attributes?) if (GetTalentSpellCost(Id) > 0 && (Effects[0].Effect == SPELL_EFFECT_LEARN_SPELL || Effects[1].Effect == SPELL_EFFECT_LEARN_SPELL || Effects[2].Effect == SPELL_EFFECT_LEARN_SPELL)) return SPELL_CAST_OK;*/ //if (HasAttribute(SPELL_ATTR13_ACTIVATES_REQUIRED_SHAPESHIFT)) // return SPELL_CAST_OK; uint64 stanceMask = (form ? UI64LIT(1) << (form - 1) : 0); if (stanceMask & StancesNot) // can explicitly not be cast in this stance return SPELL_FAILED_NOT_SHAPESHIFT; if (stanceMask & Stances) // can explicitly be cast in this stance return SPELL_CAST_OK; bool actAsShifted = false; SpellShapeshiftFormEntry const* shapeInfo = NULL; if (form > 0) { shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form); if (!shapeInfo) { TC_LOG_ERROR("spells", "GetErrorAtShapeshiftedCast: unknown shapeshift %u", form); return SPELL_CAST_OK; } actAsShifted = !(shapeInfo->Flags & SHAPESHIFT_FORM_IS_NOT_A_SHAPESHIFT); } if (actAsShifted) { if (HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT) || (shapeInfo && shapeInfo->Flags & SHAPESHIFT_FORM_PREVENT_USING_OWN_SKILLS)) // not while shapeshifted return SPELL_FAILED_NOT_SHAPESHIFT; else if (Stances != 0) // needs other shapeshift return SPELL_FAILED_ONLY_SHAPESHIFT; } else { // needs shapeshift if (!HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) && Stances != 0) return SPELL_FAILED_ONLY_SHAPESHIFT; } return SPELL_CAST_OK; } SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) const { // normal case if (RequiredAreasID > 0) { bool found = false; std::vector areaGroupMembers = sDB2Manager.GetAreasForGroup(RequiredAreasID); for (uint32 areaId : areaGroupMembers) { if (areaId == zone_id || areaId == area_id) { found = true; break; } } if (!found) return SPELL_FAILED_INCORRECT_AREA; } // continent limitation (virtual continent) if (HasAttribute(SPELL_ATTR4_CAST_ONLY_IN_OUTLAND)) { uint32 mountFlags = 0; if (player && player->HasAuraType(SPELL_AURA_MOUNT_RESTRICTIONS)) { for (AuraEffect const* auraEffect : player->GetAuraEffectsByType(SPELL_AURA_MOUNT_RESTRICTIONS)) mountFlags |= auraEffect->GetMiscValue(); } else if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(area_id)) mountFlags = areaTable->MountFlags; if (!(mountFlags & AREA_MOUNT_FLAG_FLYING_ALLOWED)) return SPELL_FAILED_INCORRECT_AREA; if (player) { uint32 mapToCheck = map_id; if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) mapToCheck = mapEntry->CosmeticParentMapID; if ((mapToCheck == 1116 || mapToCheck == 1464) && !player->HasSpell(191645)) // Draenor Pathfinder return SPELL_FAILED_INCORRECT_AREA; else if (mapToCheck == 1220 && !player->HasSpell(233368)) // Broken Isles Pathfinder return SPELL_FAILED_INCORRECT_AREA; else if ((mapToCheck == 1642 || mapToCheck == 1643) && !player->HasSpell(278833)) // Battle for Azeroth Pathfinder return SPELL_FAILED_INCORRECT_AREA; } } // raid instance limitation if (HasAttribute(SPELL_ATTR6_NOT_IN_RAID_INSTANCE)) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry || mapEntry->IsRaid()) return SPELL_FAILED_NOT_IN_RAID_INSTANCE; } // DB base check (if non empty then must fit at least single for allow) SpellAreaMapBounds saBounds = sSpellMgr->GetSpellAreaMapBounds(Id); if (saBounds.first != saBounds.second) { for (SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { if (itr->second.IsFitToRequirements(player, zone_id, area_id)) return SPELL_CAST_OK; } return SPELL_FAILED_INCORRECT_AREA; } // bg spell checks switch (Id) { case 23333: // Warsong Flag case 23335: // Silverwing Flag return map_id == 489 && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; case 34976: // Netherstorm Flag return map_id == 566 && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; case 2584: // Waiting to Resurrect case 22011: // Spirit Heal Channel case 22012: // Spirit Heal case 24171: // Resurrection Impact Visual case 42792: // Recently Dropped Flag case 43681: // Inactive case 44535: // Spirit Heal (mana) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; return zone_id == 4197 || (mapEntry->IsBattleground() && player && player->InBattleground()) ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 44521: // Preparation { if (!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; if (!mapEntry->IsBattleground()) return SPELL_FAILED_REQUIRES_AREA; Battleground* bg = player->GetBattleground(); return bg && bg->GetStatus() == STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32724: // Gold Team (Alliance) case 32725: // Green Team (Alliance) case 35774: // Gold Team (Horde) case 35775: // Green Team (Horde) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; return mapEntry->IsBattleArena() && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32727: // Arena Preparation { if (!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; if (!mapEntry->IsBattleArena()) return SPELL_FAILED_REQUIRES_AREA; Battleground* bg = player->GetBattleground(); return bg && bg->GetStatus() == STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } } // aura limitations if (player) { for (SpellEffectInfo const* effect : GetEffectsForDifficulty(player->GetMap()->GetDifficultyID())) { if (!effect || !effect->IsAura()) continue; switch (effect->ApplyAuraName) { case SPELL_AURA_MOD_SHAPESHIFT: { if (SpellShapeshiftFormEntry const* spellShapeshiftForm = sSpellShapeshiftFormStore.LookupEntry(effect->MiscValue)) if (uint32 mountType = spellShapeshiftForm->MountTypeID) if (!player->GetMountCapability(mountType)) return SPELL_FAILED_NOT_HERE; break; } case SPELL_AURA_MOUNTED: { uint32 mountType = effect->MiscValueB; if (MountEntry const* mountEntry = sDB2Manager.GetMount(Id)) mountType = mountEntry->MountTypeID; if (mountType && !player->GetMountCapability(mountType)) return SPELL_FAILED_NOT_HERE; break; } } } } return SPELL_CAST_OK; } SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* target, bool implicit) const { if (HasAttribute(SPELL_ATTR1_CANT_TARGET_SELF) && caster == target) return SPELL_FAILED_BAD_TARGETS; // check visibility - ignore stealth for implicit (area) targets if (!HasAttribute(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 (HasAttribute(SPELL_ATTR1_CANT_TARGET_IN_COMBAT)) { if (unitTarget->IsInCombat()) return SPELL_FAILED_TARGET_AFFECTING_COMBAT; // player with active pet counts as a player in combat else if (Player const* player = unitTarget->ToPlayer()) if (Pet* pet = player->GetPet()) if (pet->GetVictim() && !pet->HasUnitState(UNIT_STATE_CONTROLLED)) return SPELL_FAILED_TARGET_AFFECTING_COMBAT; } // only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts if (HasAttribute(SPELL_ATTR3_ONLY_TARGET_GHOSTS) != unitTarget->HasAuraType(SPELL_AURA_GHOST)) { if (HasAttribute(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 (HasAttribute(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 (HasAttribute(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; } // 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->GetVirtualItemId(0)) 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 (HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS) && unitTarget->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER; if (!IsAllowingDeadTarget() && !unitTarget->IsAlive()) return SPELL_FAILED_TARGETS_DEAD; // check this flag only for implicit targets (chain and area), allow to explicitly target units for spells like Shield of Righteousness if (implicit && HasAttribute(SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED) && !unitTarget->CanFreeMove()) return SPELL_FAILED_BAD_TARGETS; // checked in Unit::IsValidAttack/AssistTarget, shouldn't be checked for ENTRY targets //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) // return SPELL_FAILED_BAD_TARGETS; //if (!HasAttribute(SPELL_ATTR6_CAN_TARGET_POSSESSED_FRIENDS)) if (!CheckTargetCreatureType(unitTarget)) { if (target->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_TARGET_IS_PLAYER; else return SPELL_FAILED_BAD_TARGETS; } // check GM mode and GM invisibility - only for player casts (npc casts are controlled by AI) and negative spells if (unitTarget != caster && (caster->IsControlledByPlayer() || !IsPositive()) && unitTarget->GetTypeId() == TYPEID_PLAYER) { if (!unitTarget->ToPlayer()->IsVisible()) return SPELL_FAILED_BM_OR_INVISGOD; if (unitTarget->ToPlayer()->IsGameMaster()) return SPELL_FAILED_BM_OR_INVISGOD; } // not allow casting on flying player if (unitTarget->HasUnitState(UNIT_STATE_IN_FLIGHT) && !HasAttribute(SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET)) return SPELL_FAILED_BAD_TARGETS; /* TARGET_UNIT_MASTER gets blocked here for passengers, because the whole idea of this check is to not allow passengers to be implicitly hit by spells, however this target type should be an exception, if this is left it kills spells that award kill credit from vehicle to master (few spells), the use of these 2 covers passenger target check, logically, if vehicle cast this to master it should always hit him, because it would be it's passenger, there's no such case where this gets to fail legitimacy, this problem cannot be solved from within the check in other way since target type cannot be called for the spell currently Spell examples: [ID - 52864 Devour Water, ID - 52862 Devour Wind, ID - 49370 Wyrmrest Defender: Destabilize Azure Dragonshrine Effect] */ if (!caster->IsVehicle() && !(caster->GetCharmerOrOwner() == target)) { if (TargetAuraState && !unitTarget->HasAuraState(AuraStateType(TargetAuraState), this, caster)) return SPELL_FAILED_TARGET_AURASTATE; if (ExcludeTargetAuraState && unitTarget->HasAuraState(AuraStateType(ExcludeTargetAuraState), this, caster)) return SPELL_FAILED_TARGET_AURASTATE; } if (TargetAuraSpell && !unitTarget->HasAura(TargetAuraSpell)) return SPELL_FAILED_TARGET_AURASTATE; if (ExcludeTargetAuraSpell && unitTarget->HasAura(ExcludeTargetAuraSpell)) return SPELL_FAILED_TARGET_AURASTATE; if (unitTarget->HasAuraType(SPELL_AURA_PREVENT_RESURRECTION)) if (HasEffect(caster->GetMap()->GetDifficultyID(), SPELL_EFFECT_SELF_RESURRECT) || HasEffect(caster->GetMap()->GetDifficultyID(), SPELL_EFFECT_RESURRECT)) return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED; if (HasAttribute(SPELL_ATTR8_BATTLE_RESURRECTION)) if (Map* map = caster->GetMap()) if (InstanceMap* iMap = map->ToInstanceMap()) if (InstanceScript* instance = iMap->GetInstanceScript()) if (instance->GetCombatResurrectionCharges() == 0 && instance->IsEncounterInProgress()) return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED; return SPELL_CAST_OK; } SpellCastResult SpellInfo::CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget) const { uint32 neededTargets = GetExplicitTargetMask(); if (!target) { if (neededTargets & (TARGET_FLAG_UNIT_MASK | TARGET_FLAG_GAMEOBJECT_MASK | TARGET_FLAG_CORPSE_MASK)) if (!(neededTargets & TARGET_FLAG_GAMEOBJECT_ITEM) || !itemTarget) return SPELL_FAILED_BAD_TARGETS; return SPELL_CAST_OK; } if (Unit const* unitTarget = target->ToUnit()) { if (neededTargets & (TARGET_FLAG_UNIT_ENEMY | TARGET_FLAG_UNIT_ALLY | TARGET_FLAG_UNIT_RAID | TARGET_FLAG_UNIT_PARTY | TARGET_FLAG_UNIT_MINIPET | TARGET_FLAG_UNIT_PASSENGER)) { if (neededTargets & TARGET_FLAG_UNIT_ENEMY) if (caster->_IsValidAttackTarget(unitTarget, this)) return SPELL_CAST_OK; if (neededTargets & TARGET_FLAG_UNIT_ALLY || (neededTargets & TARGET_FLAG_UNIT_PARTY && caster->IsInPartyWith(unitTarget)) || (neededTargets & TARGET_FLAG_UNIT_RAID && caster->IsInRaidWith(unitTarget))) if (caster->_IsValidAssistTarget(unitTarget, this)) return SPELL_CAST_OK; if (neededTargets & TARGET_FLAG_UNIT_MINIPET) if (unitTarget->GetGUID() == caster->GetCritterGUID()) return SPELL_CAST_OK; if (neededTargets & TARGET_FLAG_UNIT_PASSENGER) if (unitTarget->IsOnVehicle(caster)) return SPELL_CAST_OK; return SPELL_FAILED_BAD_TARGETS; } } return SPELL_CAST_OK; } SpellCastResult SpellInfo::CheckVehicle(Unit const* caster) const { // All creatures should be able to cast as passengers freely, restriction and attribute are only for players if (caster->GetTypeId() != TYPEID_PLAYER) return SPELL_CAST_OK; Vehicle* vehicle = caster->GetVehicle(); if (vehicle) { uint16 checkMask = 0; for (SpellEffectInfo const* effect : GetEffectsForDifficulty(caster->GetMap()->GetDifficultyID())) { if (effect && effect->ApplyAuraName == SPELL_AURA_MOD_SHAPESHIFT) { SpellShapeshiftFormEntry const* shapeShiftFromEntry = sSpellShapeshiftFormStore.LookupEntry(effect->MiscValue); if (shapeShiftFromEntry && (shapeShiftFromEntry->Flags & 1) == 0) // unk flag checkMask |= VEHICLE_SEAT_FLAG_UNCONTROLLED; break; } } if (HasAura(caster->GetMap()->GetDifficultyID(), SPELL_AURA_MOUNTED)) checkMask |= VEHICLE_SEAT_FLAG_CAN_CAST_MOUNT_SPELL; if (!checkMask) checkMask = VEHICLE_SEAT_FLAG_CAN_ATTACK; VehicleSeatEntry const* vehicleSeat = vehicle->GetSeatForPassenger(caster); if (!HasAttribute(SPELL_ATTR6_CASTABLE_WHILE_ON_VEHICLE) && !HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED) && (vehicleSeat->Flags & checkMask) != checkMask) return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; // Can only summon uncontrolled minions/guardians when on controlled vehicle if (vehicleSeat->Flags & (VEHICLE_SEAT_FLAG_CAN_CONTROL | VEHICLE_SEAT_FLAG_UNK2)) { for (SpellEffectInfo const* effect : GetEffectsForDifficulty(caster->GetMap()->GetDifficultyID())) { if (!effect || effect->Effect != SPELL_EFFECT_SUMMON) continue; SummonPropertiesEntry const* props = sSummonPropertiesStore.LookupEntry(effect->MiscValueB); if (props && props->Control != SUMMON_CATEGORY_WILD) return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; } } } 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 && GetCategory() == 1179) { // not allow cast at player if (target->GetTypeId() == TYPEID_PLAYER) return false; else return true; } // if target is magnet (i.e Grounding Totem) the check is skipped if (target->IsMagnet()) return true; uint32 creatureType = target->GetCreatureTypeMask(); return !TargetCreatureType || !creatureType || (creatureType & TargetCreatureType); } SpellSchoolMask SpellInfo::GetSchoolMask() const { return SpellSchoolMask(SchoolMask); } uint32 SpellInfo::GetAllEffectsMechanicMask() const { uint32 mask = 0; if (Mechanic) mask |= 1 << Mechanic; for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (effect && effect->IsEffect() && effect->Mechanic) mask |= 1 << effect->Mechanic; } } return mask; } uint32 SpellInfo::GetEffectMechanicMask(uint32 effIndex) const { uint32 mask = 0; if (Mechanic) mask |= 1 << Mechanic; for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (effect && effect->EffectIndex == effIndex && effect->IsEffect() && effect->Mechanic) mask |= 1 << effect->Mechanic; } } return mask; } uint32 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const { uint32 mask = 0; if (Mechanic) mask |= 1 << Mechanic; for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (effect && (effectMask & (1 << effect->EffectIndex)) && effect->Mechanic) mask |= 1 << effect->Mechanic; } } return mask; } Mechanics SpellInfo::GetEffectMechanic(uint32 effIndex, uint32 difficulty) const { SpellEffectInfo const* effect = GetEffect(difficulty, effIndex); if (effect && effect->IsEffect() && effect->Mechanic) return Mechanics(effect->Mechanic); if (Mechanic) return Mechanics(Mechanic); return MECHANIC_NONE; } /*bool SpellInfo::HasAnyEffectMechanic() const { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (Effects[i].Mechanic) return true; return false; }*/ uint32 SpellInfo::GetDispelMask() const { return GetDispelMask(DispelType(Dispel)); } uint32 SpellInfo::GetDispelMask(DispelType type) { // If dispel all if (type == DISPEL_ALL) return DISPEL_ALL_MASK; else return uint32(1 << type); } uint32 SpellInfo::GetExplicitTargetMask() const { return ExplicitTargetMask; } AuraStateType SpellInfo::GetAuraState() const { return _auraState; } void SpellInfo::_LoadAuraState() { _auraState = [this]()->AuraStateType { // Seals if (GetSpellSpecific() == SPELL_SPECIFIC_SEAL) return AURA_STATE_JUDGEMENT; // Conflagrate aura state on Immolate and Shadowflame if (SpellFamilyName == SPELLFAMILY_WARLOCK && // Immolate ((SpellFamilyFlags[0] & 4) || // Shadowflame (SpellFamilyFlags[2] & 2))) return AURA_STATE_CONFLAGRATE; // Faerie Fire (druid versions) if (SpellFamilyName == SPELLFAMILY_DRUID && SpellFamilyFlags[0] & 0x400) return AURA_STATE_FAERIE_FIRE; // Sting (hunter's pet ability) if (GetCategory() == 1133) return AURA_STATE_FAERIE_FIRE; // Victorious if (SpellFamilyName == SPELLFAMILY_WARRIOR && SpellFamilyFlags[1] & 0x00040000) return AURA_STATE_WARRIOR_VICTORY_RUSH; // Swiftmend state on Regrowth & Rejuvenation if (SpellFamilyName == SPELLFAMILY_DRUID && SpellFamilyFlags[0] & 0x50) return AURA_STATE_SWIFTMEND; // Deadly poison aura state if (SpellFamilyName == SPELLFAMILY_ROGUE && SpellFamilyFlags[0] & 0x10000) return AURA_STATE_DEADLY_POISON; // Enrage aura state if (Dispel == DISPEL_ENRAGE) return AURA_STATE_ENRAGE; // Bleeding aura state if (GetAllEffectsMechanicMask() & 1<second) if (effect && (effect->IsAura(SPELL_AURA_MOD_STUN) || effect->IsAura(SPELL_AURA_MOD_ROOT))) return AURA_STATE_FROZEN; switch (Id) { case 71465: // Divine Surge case 50241: // Evasive Charges return AURA_STATE_UNKNOWN22; default: break; } return AURA_STATE_NONE; }(); } SpellSpecificType SpellInfo::GetSpellSpecific() const { return _spellSpecific; }; void SpellInfo::_LoadSpellSpecific() { _spellSpecific = [this]()->SpellSpecificType { switch (SpellFamilyName) { case SPELLFAMILY_GENERIC: { // Food / Drinks (mostly) if (HasAuraInterruptFlag(AURA_INTERRUPT_FLAG_NOT_SEATED)) { bool food = false; bool drink = false; for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (!effect || !effect->IsAura()) continue; switch (effect->ApplyAuraName) { // Food case SPELL_AURA_MOD_REGEN: case SPELL_AURA_OBS_MOD_HEALTH: food = true; break; // Drink case SPELL_AURA_MOD_POWER_REGEN: case SPELL_AURA_OBS_MOD_POWER: drink = true; break; default: break; } } } if (food && drink) return SPELL_SPECIFIC_FOOD_AND_DRINK; else if (food) return SPELL_SPECIFIC_FOOD; else if (drink) return SPELL_SPECIFIC_DRINK; } // scrolls effects else { SpellInfo const* firstRankSpellInfo = GetFirstRankSpell(); switch (firstRankSpellInfo->Id) { case 8118: // Strength case 8099: // Stamina case 8112: // Spirit case 8096: // Intellect case 8115: // Agility case 8091: // Armor return SPELL_SPECIFIC_SCROLL; default: break; } } break; } case SPELLFAMILY_MAGE: { // family flags 18(Molten), 25(Frost/Ice), 28(Mage) if (SpellFamilyFlags[0] & 0x12040000) return SPELL_SPECIFIC_MAGE_ARMOR; // Arcane brillance and Arcane intelect (normal check fails because of flags difference) if (SpellFamilyFlags[0] & 0x400) return SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE; SpellEffectInfo const* effect = GetEffect(DIFFICULTY_NONE, EFFECT_0); if (effect && (SpellFamilyFlags[0] & 0x1000000) && effect->ApplyAuraName == SPELL_AURA_MOD_CONFUSE) return SPELL_SPECIFIC_MAGE_POLYMORPH; break; } case SPELLFAMILY_WARRIOR: { if (Id == 12292) // Death Wish return SPELL_SPECIFIC_WARRIOR_ENRAGE; break; } case SPELLFAMILY_WARLOCK: { // Warlock (Bane of Doom | Bane of Agony | Bane of Havoc) if (Id == 603 || Id == 980 || Id == 80240) return SPELL_SPECIFIC_BANE; // only warlock curses have this if (Dispel == DISPEL_CURSE) return SPELL_SPECIFIC_CURSE; // Warlock (Demon Armor | Demon Skin | Fel Armor) if (SpellFamilyFlags[1] & 0x20000020 || SpellFamilyFlags[2] & 0x00000010) return SPELL_SPECIFIC_WARLOCK_ARMOR; //seed of corruption and corruption if (SpellFamilyFlags[1] & 0x10 || SpellFamilyFlags[0] & 0x2) return SPELL_SPECIFIC_WARLOCK_CORRUPTION; break; } case SPELLFAMILY_PRIEST: { // Divine Spirit and Prayer of Spirit if (SpellFamilyFlags[0] & 0x20) return SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT; break; } case SPELLFAMILY_HUNTER: { // only hunter stings have this if (Dispel == DISPEL_POISON) return SPELL_SPECIFIC_STING; // only hunter aspects have this (but not all aspects in hunter family) if (SpellFamilyFlags & flag128(0x00200000, 0x00000000, 0x00001010, 0x00000000)) return SPELL_SPECIFIC_ASPECT; break; } case SPELLFAMILY_PALADIN: { // Collection of all the seal family flags. No other paladin spell has any of those. if (SpellFamilyFlags[1] & 0xA2000800) return SPELL_SPECIFIC_SEAL; if (SpellFamilyFlags[0] & 0x00002190) return SPELL_SPECIFIC_HAND; // Judgement if (Id == 20271) return SPELL_SPECIFIC_JUDGEMENT; // only paladin auras have this (for palaldin class family) if (SpellFamilyFlags[2] & 0x00000020) return SPELL_SPECIFIC_AURA; break; } case SPELLFAMILY_SHAMAN: { // family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus if (SpellFamilyFlags[1] & 0x420 || SpellFamilyFlags[0] & 0x00000400 || Id == 23552) return SPELL_SPECIFIC_ELEMENTAL_SHIELD; break; } case SPELLFAMILY_DEATHKNIGHT: if (Id == 48266 || Id == 48263 || Id == 48265) return SPELL_SPECIFIC_PRESENCE; break; } for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (effect && effect->Effect == SPELL_EFFECT_APPLY_AURA) { switch (effect->ApplyAuraName) { case SPELL_AURA_MOD_CHARM: case SPELL_AURA_MOD_POSSESS_PET: case SPELL_AURA_MOD_POSSESS: case SPELL_AURA_AOE_CHARM: return SPELL_SPECIFIC_CHARM; case SPELL_AURA_TRACK_CREATURES: /// @workaround For non-stacking tracking spells (We need generic solution) if (Id == 30645) // Gas Cloud Tracking return SPELL_SPECIFIC_NORMAL; case SPELL_AURA_TRACK_RESOURCES: case SPELL_AURA_TRACK_STEALTHED: return SPELL_SPECIFIC_TRACKER; } } } } return SPELL_SPECIFIC_NORMAL; }(); } void SpellInfo::_LoadSpellDiminishInfo() { auto diminishingGroupCompute = [this]() -> DiminishingGroup { if (IsPositive()) return DIMINISHING_NONE; if (HasAura(DIFFICULTY_NONE, SPELL_AURA_MOD_TAUNT)) return DIMINISHING_TAUNT; switch (Id) { case 20549: // War Stomp (Racial - Tauren) case 24394: // Intimidation case 118345: // Pulverize (Primal Earth Elemental) case 118905: // Static Charge (Capacitor Totem) return DIMINISHING_STUN; case 107079: // Quaking Palm return DIMINISHING_INCAPACITATE; case 155145: // Arcane Torrent (Racial - Blood Elf) return DIMINISHING_SILENCE; case 108199: // Gorefiend's Grasp case 191244: // Sticky Bomb return DIMINISHING_AOE_KNOCKBACK; default: break; } // Explicit Diminishing Groups switch (SpellFamilyName) { case SPELLFAMILY_GENERIC: break; case SPELLFAMILY_MAGE: { // Frost Nova -- 122 if (SpellFamilyFlags[0] & 0x40) return DIMINISHING_ROOT; // Freeze (Water Elemental) -- 33395 if (SpellFamilyFlags[2] & 0x200) return DIMINISHING_ROOT; // Dragon's Breath -- 31661 if (SpellFamilyFlags[0] & 0x800000) return DIMINISHING_INCAPACITATE; // Polymorph -- 118 if (SpellFamilyFlags[0] & 0x1000000) return DIMINISHING_INCAPACITATE; // Ring of Frost -- 82691 if (SpellFamilyFlags[2] & 0x40) return DIMINISHING_INCAPACITATE; // Ice Nova -- 157997 if (SpellFamilyFlags[2] & 0x800000) return DIMINISHING_INCAPACITATE; break; } case SPELLFAMILY_WARRIOR: { // Shockwave -- 132168 if (SpellFamilyFlags[1] & 0x8000) return DIMINISHING_STUN; // Storm Bolt -- 132169 if (SpellFamilyFlags[2] & 0x1000) return DIMINISHING_STUN; // Intimidating Shout -- 5246 if (SpellFamilyFlags[0] & 0x40000) return DIMINISHING_DISORIENT; break; } case SPELLFAMILY_WARLOCK: { // Mortal Coil -- 6789 if (SpellFamilyFlags[0] & 0x80000) return DIMINISHING_INCAPACITATE; // Banish -- 710 if (SpellFamilyFlags[1] & 0x8000000) return DIMINISHING_INCAPACITATE; // Fear -- 118699 if (SpellFamilyFlags[1] & 0x400) return DIMINISHING_DISORIENT; // Howl of Terror -- 5484 if (SpellFamilyFlags[1] & 0x8) return DIMINISHING_DISORIENT; // Shadowfury -- 30283 if (SpellFamilyFlags[1] & 0x1000) return DIMINISHING_STUN; // Summon Infernal -- 22703 if (SpellFamilyFlags[0] & 0x1000) return DIMINISHING_STUN; // 170995 -- Cripple if (Id == 170995) return DIMINISHING_LIMITONLY; break; } case SPELLFAMILY_WARLOCK_PET: { // Fellash -- 115770 // Whiplash -- 6360 if (SpellFamilyFlags[0] & 0x8000000) return DIMINISHING_AOE_KNOCKBACK; // Mesmerize (Shivarra pet) -- 115268 // Seduction (Succubus pet) -- 6358 if (SpellFamilyFlags[0] & 0x2000000) return DIMINISHING_DISORIENT; // Axe Toss (Felguard pet) -- 89766 if (SpellFamilyFlags[1] & 0x4) return DIMINISHING_STUN; break; } case SPELLFAMILY_DRUID: { // Maim -- 22570 if (SpellFamilyFlags[1] & 0x80) return DIMINISHING_STUN; // Mighty Bash -- 5211 if (SpellFamilyFlags[0] & 0x2000) return DIMINISHING_STUN; // Rake -- 163505 -- no flags on the stun if (Id == 163505) return DIMINISHING_STUN; // Incapacitating Roar -- 99, no flags on the stun, 14 if (SpellFamilyFlags[1] & 0x1) return DIMINISHING_INCAPACITATE; // Cyclone -- 33786 if (SpellFamilyFlags[1] & 0x20) return DIMINISHING_DISORIENT; // Solar Beam -- 81261 if (Id == 81261) return DIMINISHING_SILENCE; // Typhoon -- 61391 if (SpellFamilyFlags[1] & 0x1000000) return DIMINISHING_AOE_KNOCKBACK; // Ursol's Vortex -- 118283, no family flags if (Id == 118283) return DIMINISHING_AOE_KNOCKBACK; // Entangling Roots -- 339 if (SpellFamilyFlags[0] & 0x200) return DIMINISHING_ROOT; // Mass Entanglement -- 102359 if (SpellFamilyFlags[2] & 0x4) return DIMINISHING_ROOT; break; } case SPELLFAMILY_ROGUE: { // Between the Eyes -- 199804 if (SpellFamilyFlags[0] & 0x800000) return DIMINISHING_STUN; // Cheap Shot -- 1833 if (SpellFamilyFlags[0] & 0x400) return DIMINISHING_STUN; // Kidney Shot -- 408 if (SpellFamilyFlags[0] & 0x200000) return DIMINISHING_STUN; // Gouge -- 1776 if (SpellFamilyFlags[0] & 0x8) return DIMINISHING_INCAPACITATE; // Sap -- 6770 if (SpellFamilyFlags[0] & 0x80) return DIMINISHING_INCAPACITATE; // Blind -- 2094 if (SpellFamilyFlags[0] & 0x1000000) return DIMINISHING_DISORIENT; // Garrote -- 1330 if (SpellFamilyFlags[1] & 0x20000000) return DIMINISHING_SILENCE; break; } case SPELLFAMILY_HUNTER: { // Charge (Tenacity pet) -- 53148, no flags if (Id == 53148) return DIMINISHING_ROOT; // Ranger's Net -- 200108 // Tracker's Net -- 212638 if (Id == 200108 || Id == 212638) return DIMINISHING_ROOT; // Binding Shot -- 117526, no flags if (Id == 117526) return DIMINISHING_STUN; // Freezing Trap -- 3355 if (SpellFamilyFlags[0] & 0x8) return DIMINISHING_INCAPACITATE; // Wyvern Sting -- 19386 if (SpellFamilyFlags[1] & 0x1000) return DIMINISHING_INCAPACITATE; // Bursting Shot -- 224729 if (SpellFamilyFlags[2] & 0x40) return DIMINISHING_DISORIENT; // Scatter Shot -- 213691 if (SpellFamilyFlags[2] & 0x8000) return DIMINISHING_DISORIENT; // Spider Sting -- 202933 if (Id == 202933) return DIMINISHING_SILENCE; break; } case SPELLFAMILY_PALADIN: { // Repentance -- 20066 if (SpellFamilyFlags[0] & 0x4) return DIMINISHING_INCAPACITATE; // Blinding Light -- 105421 if (Id == 105421) return DIMINISHING_DISORIENT; // Avenger's Shield -- 31935 if (SpellFamilyFlags[0] & 0x4000) return DIMINISHING_SILENCE; // Hammer of Justice -- 853 if (SpellFamilyFlags[0] & 0x800) return DIMINISHING_STUN; break; } case SPELLFAMILY_SHAMAN: { // Hex -- 51514 // Hex -- 196942 (Voodoo Totem) if (SpellFamilyFlags[1] & 0x8000) return DIMINISHING_INCAPACITATE; // Thunderstorm -- 51490 if (SpellFamilyFlags[1] & 0x2000) return DIMINISHING_AOE_KNOCKBACK; // Earthgrab Totem -- 64695 if (SpellFamilyFlags[2] & 0x4000) return DIMINISHING_ROOT; // Lightning Lasso -- 204437 if (SpellFamilyFlags[3] & 0x2000000) return DIMINISHING_STUN; break; } case SPELLFAMILY_DEATHKNIGHT: { // Chains of Ice -- 96294 if (Id == 96294) return DIMINISHING_ROOT; // Blinding Sleet -- 207167 if (Id == 207167) return DIMINISHING_DISORIENT; // Strangulate -- 47476 if (SpellFamilyFlags[0] & 0x200) return DIMINISHING_SILENCE; // Asphyxiate -- 108194 if (SpellFamilyFlags[2] & 0x100000) return DIMINISHING_STUN; // Gnaw (Ghoul) -- 91800, no flags if (Id == 91800) return DIMINISHING_STUN; // Monstrous Blow (Ghoul w/ Dark Transformation active) -- 91797 if (Id == 91797) return DIMINISHING_STUN; // Winter is Coming -- 207171 if (Id == 207171) return DIMINISHING_STUN; break; } case SPELLFAMILY_PRIEST: { // Holy Word: Chastise -- 200200 if (SpellFamilyFlags[2] & 0x20 && GetSpellVisual() == 52021) return DIMINISHING_STUN; // Mind Bomb -- 226943 if (Id == 226943) return DIMINISHING_STUN; // Mind Control -- 605 if (SpellFamilyFlags[0] & 0x20000 && GetSpellVisual() == 39068) return DIMINISHING_INCAPACITATE; // Holy Word: Chastise -- 200196 if (SpellFamilyFlags[2] & 0x20 && GetSpellVisual() == 52019) return DIMINISHING_INCAPACITATE; // Psychic Scream -- 8122 if (SpellFamilyFlags[0] & 0x10000) return DIMINISHING_DISORIENT; // Silence -- 15487 if (SpellFamilyFlags[1] & 0x200000 && GetSpellVisual() == 39025) return DIMINISHING_SILENCE; // Shining Force -- 204263 if (Id == 204263) return DIMINISHING_AOE_KNOCKBACK; break; } case SPELLFAMILY_MONK: { // Disable -- 116706, no flags if (Id == 116706) return DIMINISHING_ROOT; // Fists of Fury -- 120086 if (SpellFamilyFlags[1] & 0x800000 && !(SpellFamilyFlags[2] & 0x8)) return DIMINISHING_STUN; // Leg Sweep -- 119381 if (SpellFamilyFlags[1] & 0x200) return DIMINISHING_STUN; // Incendiary Breath (honor talent) -- 202274, no flags if (Id == 202274) return DIMINISHING_INCAPACITATE; // Paralysis -- 115078 if (SpellFamilyFlags[2] & 0x800000) return DIMINISHING_INCAPACITATE; // Song of Chi-Ji -- 198909 if (Id == 198909) return DIMINISHING_DISORIENT; break; } case SPELLFAMILY_DEMON_HUNTER: { switch (Id) { case 179057: // Chaos Nova case 211881: // Fel Eruption case 200166: // Metamorphosis case 205630: // Illidan's Grasp return DIMINISHING_STUN; case 217832: // Imprison case 221527: // Imprison return DIMINISHING_INCAPACITATE; default: break; } break; } default: break; } return DIMINISHING_NONE; }; auto diminishingTypeCompute = [](DiminishingGroup group) -> DiminishingReturnsType { switch (group) { case DIMINISHING_TAUNT: case DIMINISHING_STUN: return DRTYPE_ALL; case DIMINISHING_LIMITONLY: case DIMINISHING_NONE: return DRTYPE_NONE; default: return DRTYPE_PLAYER; } }; auto diminishingMaxLevelCompute = [](DiminishingGroup group) -> DiminishingLevels { switch (group) { case DIMINISHING_TAUNT: return DIMINISHING_LEVEL_TAUNT_IMMUNE; case DIMINISHING_AOE_KNOCKBACK: return DIMINISHING_LEVEL_2; default: return DIMINISHING_LEVEL_IMMUNE; } }; auto diminishingLimitDurationCompute = [this]() -> int32 { // Explicit diminishing duration switch (SpellFamilyName) { case SPELLFAMILY_MAGE: { // Dragon's Breath - 3 seconds in PvP if (SpellFamilyFlags[0] & 0x800000) return 3 * IN_MILLISECONDS; break; } case SPELLFAMILY_WARLOCK: { // Cripple - 4 seconds in PvP if (Id == 170995) return 4 * IN_MILLISECONDS; break; } case SPELLFAMILY_HUNTER: { // Binding Shot - 3 seconds in PvP if (Id == 117526) return 3 * IN_MILLISECONDS; // Wyvern Sting - 6 seconds in PvP if (SpellFamilyFlags[1] & 0x1000) return 6 * IN_MILLISECONDS; break; } case SPELLFAMILY_MONK: { // Paralysis - 4 seconds in PvP regardless of if they are facing you if (SpellFamilyFlags[2] & 0x800000) return 4 * IN_MILLISECONDS; break; } case SPELLFAMILY_DEMON_HUNTER: { switch (Id) { case 217832: // Imprison case 221527: // Imprison return 4 * IN_MILLISECONDS; default: break; } break; } default: break; } return 8 * IN_MILLISECONDS; }; SpellDiminishInfo diminishInfo; diminishInfo.DiminishGroup = diminishingGroupCompute(); diminishInfo.DiminishReturnType = diminishingTypeCompute(diminishInfo.DiminishGroup); diminishInfo.DiminishMaxLevel = diminishingMaxLevelCompute(diminishInfo.DiminishGroup); diminishInfo.DiminishDurationLimit = diminishingLimitDurationCompute(); _diminishInfo = diminishInfo; } DiminishingGroup SpellInfo::GetDiminishingReturnsGroupForSpell() const { return _diminishInfo.DiminishGroup; } DiminishingReturnsType SpellInfo::GetDiminishingReturnsGroupType() const { return _diminishInfo.DiminishReturnType; } DiminishingLevels SpellInfo::GetDiminishingReturnsMaxLevel() const { return _diminishInfo.DiminishMaxLevel; } int32 SpellInfo::GetDiminishingReturnsLimitDuration() const { return _diminishInfo.DiminishDurationLimit; } void SpellInfo::_LoadImmunityInfo() { auto loadImmunityInfoFn = [this](SpellEffectInfo* effectInfo) { uint32 schoolImmunityMask = 0; uint32 applyHarmfulAuraImmunityMask = 0; uint32 mechanicImmunityMask = 0; uint32 dispelImmunity = 0; uint32 damageImmunityMask = 0; int32 miscVal = effectInfo->MiscValue; int32 amount = effectInfo->CalcValue(); ImmunityInfo& immuneInfo = *const_cast(effectInfo->GetImmunityInfo()); switch (effectInfo->ApplyAuraName) { case SPELL_AURA_MECHANIC_IMMUNITY_MASK: { switch (miscVal) { case 96: // Free Friend, Uncontrollable Frenzy, Warlord's Presence { mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); break; } case 1615: // Incite Rage, Wolf Spirit, Overload, Lightning Tendrils { switch (Id) { case 43292: // Incite Rage case 49172: // Wolf Spirit mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); // no break intended case 61869: // Overload case 63481: case 61887: // Lightning Tendrils case 63486: mechanicImmunityMask |= (1 << MECHANIC_INTERRUPT) | (1 << MECHANIC_SILENCE); immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK); immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK_DEST); break; default: break; } break; } case 679: // Mind Control, Avenging Fury { if (Id == 57742) // Avenging Fury { mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); } break; } case 1557: // Startling Roar, Warlord Roar, Break Bonds, Stormshield { if (Id == 64187) // Stormshield { mechanicImmunityMask |= (1 << MECHANIC_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); } else { mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); } break; } case 1614: // Fixate case 1694: // Fixated, Lightning Tendrils { immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT); break; } case 1630: // Fervor, Berserk { if (Id == 64112) // Berserk { immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT); } else { mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); } break; } case 477: // Bladestorm case 1733: // Bladestorm, Killing Spree { if (!amount) { mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK); immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK_DEST); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); } break; } case 878: // Whirlwind, Fog of Corruption, Determination { if (Id == 66092) // Determination { mechanicImmunityMask |= (1 << MECHANIC_SNARE) | (1 << MECHANIC_STUN) | (1 << MECHANIC_DISORIENTED) | (1 << MECHANIC_FREEZE); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); } break; } default: break; } if (immuneInfo.AuraTypeImmune.empty()) { if (miscVal & (1 << 10)) immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); if (miscVal & (1 << 1)) immuneInfo.AuraTypeImmune.insert(SPELL_AURA_TRANSFORM); // These flag can be recognized wrong: if (miscVal & (1 << 6)) immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); if (miscVal & (1 << 0)) { immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2); } if (miscVal & (1 << 2)) immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); if (miscVal & (1 << 9)) immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); if (miscVal & (1 << 7)) immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DISARM); } break; } case SPELL_AURA_MECHANIC_IMMUNITY: { switch (Id) { case 34471: // The Beast Within case 19574: // Bestial Wrath case 42292: // PvP trinket case 46227: // Medallion of Immunity case 59752: // Every Man for Himself case 53490: // Bullheaded case 65547: // PvP Trinket case 134946: // Supremacy of the Alliance case 134956: // Supremacy of the Horde case 195710: // Honorable Medallion case 208683: // Gladiator's Medallion mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; break; case 54508: // Demonic Empowerment mechanicImmunityMask |= (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) | (1 << MECHANIC_STUN); break; default: if (miscVal < 1) return; mechanicImmunityMask |= 1 << miscVal; break; } break; } case SPELL_AURA_EFFECT_IMMUNITY: { immuneInfo.SpellEffectImmune.insert(static_cast(miscVal)); break; } case SPELL_AURA_STATE_IMMUNITY: { immuneInfo.AuraTypeImmune.insert(static_cast(miscVal)); break; } case SPELL_AURA_SCHOOL_IMMUNITY: { schoolImmunityMask |= uint32(miscVal); break; } case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: { applyHarmfulAuraImmunityMask |= uint32(miscVal); break; } case SPELL_AURA_DAMAGE_IMMUNITY: { damageImmunityMask |= uint32(miscVal); break; } case SPELL_AURA_DISPEL_IMMUNITY: { dispelImmunity = uint32(miscVal); break; } default: break; } immuneInfo.SchoolImmuneMask = schoolImmunityMask; immuneInfo.ApplyHarmfulAuraImmuneMask = applyHarmfulAuraImmunityMask; immuneInfo.MechanicImmuneMask = mechanicImmunityMask; immuneInfo.DispelImmune = dispelImmunity; immuneInfo.DamageSchoolMask = damageImmunityMask; immuneInfo.AuraTypeImmune.shrink_to_fit(); immuneInfo.SpellEffectImmune.shrink_to_fit(); }; for (auto const& effects : _effects) { for (SpellEffectInfo const* effect : effects.second) { if (!effect) continue; loadImmunityInfoFn(const_cast(effect)); } } } void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const* effect, bool apply) const { ImmunityInfo const* immuneInfo = effect->GetImmunityInfo(); if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) { target->ApplySpellImmune(Id, IMMUNITY_SCHOOL, schoolImmunity, apply); if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) { target->RemoveAppliedAuras([this, schoolImmunity](AuraApplication const* aurApp) -> bool { SpellInfo const* auraSpellInfo = aurApp->GetBase()->GetSpellInfo(); return ((auraSpellInfo->GetSchoolMask() & schoolImmunity) != 0 && // Check for school mask CanDispelAura(auraSpellInfo) && (IsPositive() != aurApp->IsPositive()) && // Check spell vs aura possitivity !auraSpellInfo->IsPassive() && // Don't remove passive auras auraSpellInfo->Id != Id); // Don't remove self }); } } if (uint32 mechanicImmunity = immuneInfo->MechanicImmuneMask) { for (uint32 i = 0; i < MAX_MECHANIC; ++i) if (mechanicImmunity & (1 << i)) target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply); if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id); } if (uint32 dispelImmunity = immuneInfo->DispelImmune) { target->ApplySpellImmune(Id, IMMUNITY_DISPEL, dispelImmunity, apply); if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) { target->RemoveAppliedAuras([dispelImmunity](AuraApplication const* aurApp) -> bool { SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); if (spellInfo->Dispel == dispelImmunity) return true; return false; }); } } if (uint32 damageImmunity = immuneInfo->DamageSchoolMask) target->ApplySpellImmune(Id, IMMUNITY_DAMAGE, damageImmunity, apply); for (AuraType auraType : immuneInfo->AuraTypeImmune) { target->ApplySpellImmune(Id, IMMUNITY_STATE, auraType, apply); if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) target->RemoveAurasByType(auraType); } for (SpellEffectName effectType : immuneInfo->SpellEffectImmune) target->ApplySpellImmune(Id, IMMUNITY_EFFECT, effectType, apply); } bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const { if (!auraSpellInfo) return false; for (SpellEffectInfo const* effectInfo : GetEffectsForDifficulty(DIFFICULTY_NONE)) { if (!effectInfo) continue; ImmunityInfo const* immuneInfo = effectInfo->GetImmunityInfo(); if (!auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) { if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) if ((auraSpellInfo->SchoolMask & schoolImmunity) != 0) return true; } if (uint32 mechanicImmunity = immuneInfo->MechanicImmuneMask) if ((mechanicImmunity & (1 << auraSpellInfo->Mechanic)) != 0) return true; if (uint32 dispelImmunity = immuneInfo->DispelImmune) if (auraSpellInfo->Dispel == dispelImmunity) return true; bool immuneToAllEffects = true; for (SpellEffectInfo const* auraSpellEffectInfo : auraSpellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) { if (!auraSpellEffectInfo) continue; uint32 effectName = auraSpellEffectInfo->Effect; if (!effectName) continue; auto spellImmuneItr = immuneInfo->SpellEffectImmune.find(static_cast(effectName)); if (spellImmuneItr == immuneInfo->SpellEffectImmune.cend()) { immuneToAllEffects = false; break; } if (uint32 mechanic = auraSpellEffectInfo->Mechanic) { if (!(immuneInfo->MechanicImmuneMask & (1 << mechanic))) { immuneToAllEffects = false; break; } } if (!auraSpellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) { if (uint32 auraName = auraSpellEffectInfo->ApplyAuraName) { bool isImmuneToAuraEffectApply = false; auto auraImmuneItr = immuneInfo->AuraTypeImmune.find(static_cast(auraName)); if (auraImmuneItr != immuneInfo->AuraTypeImmune.cend()) isImmuneToAuraEffectApply = true; if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(auraSpellEffectInfo->EffectIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) { if (uint32 applyHarmfulAuraImmunityMask = immuneInfo->ApplyHarmfulAuraImmuneMask) if ((auraSpellInfo->GetSchoolMask() & applyHarmfulAuraImmunityMask) != 0) isImmuneToAuraEffectApply = true; } if (!isImmuneToAuraEffectApply) { immuneToAllEffects = false; break; } } } } if (immuneToAllEffects) return true; } return false; } // based on client Spell_C::CancelsAuraEffect bool SpellInfo::SpellCancelsAuraEffect(AuraEffect const* aurEff) const { if (!HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) return false; if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) return false; for (SpellEffectInfo const* effectInfo : GetEffectsForDifficulty(DIFFICULTY_NONE)) { if (!effectInfo) continue; if (effectInfo->Effect != SPELL_EFFECT_APPLY_AURA) continue; uint32 const miscValue = static_cast(effectInfo->MiscValue); switch (effectInfo->ApplyAuraName) { case SPELL_AURA_STATE_IMMUNITY: if (miscValue != aurEff->GetSpellEffectInfo()->ApplyAuraName) continue; break; case SPELL_AURA_SCHOOL_IMMUNITY: case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE) || !(aurEff->GetSpellInfo()->SchoolMask & miscValue)) continue; break; case SPELL_AURA_DISPEL_IMMUNITY: if (miscValue != aurEff->GetSpellInfo()->Dispel) continue; break; case SPELL_AURA_MECHANIC_IMMUNITY: if (miscValue != aurEff->GetSpellInfo()->Mechanic) { if (miscValue != aurEff->GetSpellEffectInfo()->Mechanic) continue; } break; default: continue; } return true; } return false; } float SpellInfo::GetMinRange(bool positive) const { if (!RangeEntry) return 0.0f; return RangeEntry->RangeMin[positive ? 1 : 0]; } float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const { if (!RangeEntry) return 0.0f; float range = RangeEntry->RangeMax[positive ? 1 : 0]; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(Id, SPELLMOD_RANGE, range, spell); return range; } int32 SpellInfo::CalcDuration(Unit* caster /*= nullptr*/) const { int32 duration = GetDuration(); if (caster) if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(Id, SPELLMOD_DURATION, duration); return duration; } int32 SpellInfo::GetDuration() const { if (!DurationEntry) return 0; return (DurationEntry->Duration == -1) ? -1 : abs(DurationEntry->Duration); } int32 SpellInfo::GetMaxDuration() const { if (!DurationEntry) return 0; return (DurationEntry->MaxDuration == -1) ? -1 : abs(DurationEntry->MaxDuration); } uint32 SpellInfo::CalcCastTime(uint8 level, Spell* spell /*= NULL*/) const { int32 castTime = 0; if (CastTimeEntry) { int32 calcLevel = spell ? spell->GetCaster()->getLevel() : 0; if (MaxLevel && uint32(calcLevel) > MaxLevel) calcLevel = MaxLevel; if (HasAttribute(SPELL_ATTR13_UNK17)) calcLevel *= 5; if (MaxLevel && uint32(calcLevel) > MaxLevel) calcLevel = MaxLevel; if (BaseLevel) calcLevel -= BaseLevel; if (calcLevel < 0) calcLevel = 0; castTime = CastTimeEntry->Base + CastTimeEntry->PerLevel * level; if (castTime < CastTimeEntry->Minimum) castTime = CastTimeEntry->Minimum; } if (castTime <= 0) return 0; if (spell) spell->GetCaster()->ModSpellCastTime(this, castTime, spell); if (HasAttribute(SPELL_ATTR0_REQ_AMMO) && !IsAutoRepeatRangedSpell() && !HasAttribute(SPELL_ATTR9_AIMED_SHOT)) castTime += 500; return (castTime > 0) ? uint32(castTime) : 0; } uint32 SpellInfo::GetMaxTicks(uint32 difficulty) const { int32 DotDuration = GetDuration(); if (DotDuration == 0) return 1; for (SpellEffectInfo const* effect : GetEffectsForDifficulty(difficulty)) { if (effect && effect->Effect == SPELL_EFFECT_APPLY_AURA) switch (effect->ApplyAuraName) { case SPELL_AURA_PERIODIC_DAMAGE: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: case SPELL_AURA_PERIODIC_HEAL: case SPELL_AURA_OBS_MOD_HEALTH: case SPELL_AURA_OBS_MOD_POWER: case SPELL_AURA_48: case SPELL_AURA_POWER_BURN: case SPELL_AURA_PERIODIC_LEECH: case SPELL_AURA_PERIODIC_MANA_LEECH: case SPELL_AURA_PERIODIC_ENERGIZE: case SPELL_AURA_PERIODIC_DUMMY: case SPELL_AURA_PERIODIC_TRIGGER_SPELL: case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: case SPELL_AURA_PERIODIC_HEALTH_FUNNEL: if (effect->ApplyAuraPeriod != 0) return DotDuration / effect->ApplyAuraPeriod; break; } } return 6; } uint32 SpellInfo::GetRecoveryTime() const { return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime; } std::vector SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const { std::vector costs; auto collector = [this, caster, schoolMask, &costs](std::vector const& powers) { costs.reserve(powers.size()); int32 healthCost = 0; for (SpellPowerEntry const* power : powers) { if (power->RequiredAuraSpellID && !caster->HasAura(power->RequiredAuraSpellID)) continue; // Spell drain all exist power on cast (Only paladin lay of Hands) if (HasAttribute(SPELL_ATTR1_DRAIN_ALL_POWER)) { // If power type - health drain all if (power->PowerType == POWER_HEALTH) { healthCost = caster->GetHealth(); continue; } // Else drain all power if (power->PowerType < MAX_POWERS) { SpellPowerCost cost; cost.Power = Powers(power->PowerType); cost.Amount = caster->GetPower(cost.Power); costs.push_back(cost); continue; } TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id); continue; } // Base powerCost int32 powerCost = power->ManaCost; // PCT cost from total amount if (power->PowerCostPct) { switch (power->PowerType) { // health as power used case POWER_HEALTH: powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostPct)); break; case POWER_MANA: powerCost += int32(CalculatePct(caster->GetCreateMana(), power->PowerCostPct)); break; case POWER_RAGE: case POWER_FOCUS: case POWER_ENERGY: powerCost += int32(CalculatePct(caster->GetMaxPower(Powers(power->PowerType)), power->PowerCostPct)); break; case POWER_RUNES: case POWER_RUNIC_POWER: TC_LOG_DEBUG("spells", "CalculateManaCost: Not implemented yet!"); break; default: TC_LOG_ERROR("spells", "CalculateManaCost: Unknown power type '%d' in spell %d", power->PowerType, Id); continue; } } if (power->PowerCostMaxPct) healthCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostMaxPct)); int32 optionalCost = int32(power->OptionalCost); optionalCost += caster->GetTotalAuraModifier(SPELL_AURA_MOD_ADDITIONAL_POWER_COST, [this, power](AuraEffect const* aurEff) -> bool { return aurEff->GetMiscValue() == power->PowerType && aurEff->IsAffectingSpell(this); }); if (optionalCost) { int32 remainingPower = caster->GetPower(Powers(power->PowerType)) - powerCost; powerCost += RoundToInterval(remainingPower, 0, optionalCost); } if (power->PowerType != POWER_HEALTH) { // Flat mod from caster auras by spell school and power type Unit::AuraEffectList const& auras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) { if (!((*i)->GetMiscValue() & schoolMask)) continue; if (!((*i)->GetMiscValueB() & (1 << power->PowerType))) continue; powerCost += (*i)->GetAmount(); } } // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST)) { uint32 speed = 0; if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm())) speed = ss->CombatRoundTime; else { WeaponAttackType slot = BASE_ATTACK; if (HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) slot = OFF_ATTACK; speed = caster->GetBaseAttackTime(slot); } powerCost += speed / 100; } // Apply cost mod by spell if (Player* modOwner = caster->GetSpellModOwner()) { if (power->OrderIndex == 0) modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost); else if (power->OrderIndex == 1) modOwner->ApplySpellMod(Id, SPELLMOD_SPELL_COST2, powerCost); } if (!caster->IsControlledByPlayer() && G3D::fuzzyEq(power->PowerCostPct, 0.0f) && SpellLevel) { if (HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) { GtNpcManaCostScalerEntry const* spellScaler = sNpcManaCostScalerGameTable.GetRow(SpellLevel); GtNpcManaCostScalerEntry const* casterScaler = sNpcManaCostScalerGameTable.GetRow(caster->getLevel()); if (spellScaler && casterScaler) powerCost *= casterScaler->Scaler / spellScaler->Scaler; } } // PCT mod from user auras by spell school and power type Unit::AuraEffectList const& aurasPct = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT); for (Unit::AuraEffectList::const_iterator i = aurasPct.begin(); i != aurasPct.end(); ++i) { if (!((*i)->GetMiscValue() & schoolMask)) continue; if (!((*i)->GetMiscValueB() & (1 << power->PowerType))) continue; powerCost += CalculatePct(powerCost, (*i)->GetAmount()); } if (power->PowerType == POWER_HEALTH) { healthCost += powerCost; continue; } bool found = false; for (SpellPowerCost& cost : costs) { if (cost.Power == Powers(power->PowerType)) { cost.Amount += powerCost; found = true; } } if (!found) { SpellPowerCost cost; cost.Power = Powers(power->PowerType); cost.Amount = powerCost; costs.push_back(cost); } } if (healthCost > 0) { SpellPowerCost cost; cost.Power = POWER_HEALTH; cost.Amount = healthCost; costs.push_back(cost); } }; if (!_hasPowerDifficultyData) // optimization - use static data for 99.5% cases (4753 of 4772 in build 6.1.0.19702) collector(PowerCosts); else collector(sDB2Manager.GetSpellPowers(Id, caster->GetMap()->GetDifficultyID())); return costs; } inline float CalcPPMHasteMod(SpellProcsPerMinuteModEntry const* mod, Unit* caster) { float haste = caster->GetFloatValue(UNIT_FIELD_MOD_HASTE); float rangedHaste = caster->GetFloatValue(UNIT_FIELD_MOD_RANGED_HASTE); float spellHaste = caster->GetFloatValue(UNIT_MOD_CAST_HASTE); float regenHaste = caster->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN); switch (mod->Param) { case 1: return (1.0f / haste - 1.0f) * mod->Coeff; case 2: return (1.0f / rangedHaste - 1.0f) * mod->Coeff; case 3: return (1.0f / spellHaste - 1.0f) * mod->Coeff; case 4: return (1.0f / regenHaste - 1.0f) * mod->Coeff; case 5: return (1.0f / std::min(std::min(std::min(haste, rangedHaste), spellHaste), regenHaste) - 1.0f) * mod->Coeff; default: break; } return 0.0f; } inline float CalcPPMCritMod(SpellProcsPerMinuteModEntry const* mod, Unit* caster) { if (caster->GetTypeId() != TYPEID_PLAYER) return 0.0f; float crit = caster->GetFloatValue(ACTIVE_PLAYER_FIELD_CRIT_PERCENTAGE); float rangedCrit = caster->GetFloatValue(ACTIVE_PLAYER_FIELD_RANGED_CRIT_PERCENTAGE); float spellCrit = caster->GetFloatValue(ACTIVE_PLAYER_FIELD_SPELL_CRIT_PERCENTAGE1); switch (mod->Param) { case 1: return crit * mod->Coeff * 0.01f; case 2: return rangedCrit * mod->Coeff * 0.01f; case 3: return spellCrit * mod->Coeff * 0.01f; case 4: return std::min(std::min(crit, rangedCrit), spellCrit) * mod->Coeff * 0.01f; default: break; } return 0.0f; } inline float CalcPPMItemLevelMod(SpellProcsPerMinuteModEntry const* mod, int32 itemLevel) { if (itemLevel == mod->Param) return 0.0f; float itemLevelPoints = GetRandomPropertyPoints(itemLevel, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); float basePoints = GetRandomPropertyPoints(mod->Param, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0); if (itemLevelPoints == basePoints) return 0.0f; return ((itemLevelPoints / basePoints) - 1.0f) * mod->Coeff; } float SpellInfo::CalcProcPPM(Unit* caster, int32 itemLevel) const { float ppm = ProcBasePPM; if (!caster) return ppm; for (SpellProcsPerMinuteModEntry const* mod : ProcPPMMods) { switch (mod->Type) { case SPELL_PPM_MOD_HASTE: { ppm *= 1.0f + CalcPPMHasteMod(mod, caster); break; } case SPELL_PPM_MOD_CRIT: { ppm *= 1.0f + CalcPPMCritMod(mod, caster); break; } case SPELL_PPM_MOD_CLASS: { if (caster->getClassMask() & mod->Param) ppm *= 1.0f + mod->Coeff; break; } case SPELL_PPM_MOD_SPEC: { if (Player* plrCaster = caster->ToPlayer()) if (plrCaster->GetInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == mod->Param) ppm *= 1.0f + mod->Coeff; break; } case SPELL_PPM_MOD_RACE: { if (caster->getRaceMask() & mod->Param) ppm *= 1.0f + mod->Coeff; break; } case SPELL_PPM_MOD_ITEM_LEVEL: { ppm *= 1.0f + CalcPPMItemLevelMod(mod, itemLevel); break; } case SPELL_PPM_MOD_BATTLEGROUND: { if (caster->GetMap()->IsBattlegroundOrArena()) ppm *= 1.0f + mod->Coeff; break; } default: break; } } return ppm; } bool SpellInfo::IsRanked() const { return ChainEntry != NULL; } uint8 SpellInfo::GetRank() const { if (!ChainEntry) return 1; return ChainEntry->rank; } SpellInfo const* SpellInfo::GetFirstRankSpell() const { if (!ChainEntry) return this; return ChainEntry->first; } SpellInfo const* SpellInfo::GetLastRankSpell() const { if (!ChainEntry) return NULL; return ChainEntry->last; } SpellInfo const* SpellInfo::GetNextRankSpell() const { if (!ChainEntry) return NULL; return ChainEntry->next; } SpellInfo const* SpellInfo::GetPrevRankSpell() const { if (!ChainEntry) return NULL; return ChainEntry->prev; } SpellInfo const* SpellInfo::GetAuraRankForLevel(uint8 level) const { // ignore passive spells if (IsPassive()) return this; // Client ignores spell with these attributes (sub_53D9D0) if (HasAttribute(SPELL_ATTR0_NEGATIVE_1) || HasAttribute(SPELL_ATTR2_UNK3)) return this; bool needRankSelection = false; for (SpellEffectInfo const* effect : GetEffectsForDifficulty(DIFFICULTY_NONE)) { if (effect && IsPositiveEffect(effect->Effect) && (effect->Effect == SPELL_EFFECT_APPLY_AURA || effect->Effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || effect->Effect == SPELL_EFFECT_APPLY_AREA_AURA_RAID) && !effect->Scaling.Coefficient) { needRankSelection = true; break; } } // not required if (!needRankSelection) return this; for (SpellInfo const* nextSpellInfo = this; nextSpellInfo != NULL; nextSpellInfo = nextSpellInfo->GetPrevRankSpell()) { // if found appropriate level if (uint32(level + 10) >= nextSpellInfo->SpellLevel) return nextSpellInfo; // one rank less then } // not found return NULL; } bool SpellInfo::IsRankOf(SpellInfo const* spellInfo) const { return GetFirstRankSpell() == spellInfo->GetFirstRankSpell(); } bool SpellInfo::IsDifferentRankOf(SpellInfo const* spellInfo) const { if (Id == spellInfo->Id) return false; return IsRankOf(spellInfo); } bool SpellInfo::IsHighRankOf(SpellInfo const* spellInfo) const { if (ChainEntry && spellInfo->ChainEntry) if (ChainEntry->first == spellInfo->ChainEntry->first) if (ChainEntry->rank > spellInfo->ChainEntry->rank) return true; return false; } uint32 SpellInfo::GetSpellXSpellVisualId(Unit const* caster /*= nullptr*/) const { if (caster) { Difficulty difficulty = caster->GetMap()->GetDifficultyID(); DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty); while (difficultyEntry) { auto itr = _visuals.find(difficulty); if (itr != _visuals.end()) { for (SpellXSpellVisualEntry const* visual : itr->second) { PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(visual->CasterPlayerConditionID); if (!playerCondition || (caster->GetTypeId() == TYPEID_PLAYER && sConditionMgr->IsPlayerMeetingCondition(caster->ToPlayer(), playerCondition))) return visual->ID; } } difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID); } } auto itr = _visuals.find(DIFFICULTY_NONE); if (itr != _visuals.end()) { for (SpellXSpellVisualEntry const* visual : itr->second) { PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(visual->CasterPlayerConditionID); if (!playerCondition || (caster && caster->GetTypeId() == TYPEID_PLAYER && sConditionMgr->IsPlayerMeetingCondition(caster->ToPlayer(), playerCondition))) return visual->ID; } } return 0; } uint32 SpellInfo::GetSpellVisual(Unit const* caster /*= nullptr*/) const { if (SpellXSpellVisualEntry const* visual = sSpellXSpellVisualStore.LookupEntry(GetSpellXSpellVisualId(caster))) { //if (visual->LowViolenceSpellVisualID && forPlayer->GetViolenceLevel() operator 2) // return visual->LowViolenceSpellVisualID; return visual->SpellVisualID; } return 0; } void SpellInfo::_InitializeExplicitTargetMask() { bool srcSet = false; bool dstSet = false; uint32 targetMask = Targets; // prepare target mask using effect target entries for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (!effect || !effect->IsEffect()) continue; targetMask |= effect->TargetA.GetExplicitTargetMask(srcSet, dstSet); targetMask |= effect->TargetB.GetExplicitTargetMask(srcSet, dstSet); // add explicit target flags based on spell effects which have EFFECT_IMPLICIT_TARGET_EXPLICIT and no valid target provided if (effect->GetImplicitTargetType() != EFFECT_IMPLICIT_TARGET_EXPLICIT) continue; // extend explicit target mask only if valid targets for effect could not be provided by target types uint32 effectTargetMask = effect->GetMissingTargetMask(srcSet, dstSet, targetMask); // don't add explicit object/dest flags when spell has no max range if (GetMaxRange(true) == 0.0f && GetMaxRange(false) == 0.0f) effectTargetMask &= ~(TARGET_FLAG_UNIT_MASK | TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_CORPSE_MASK | TARGET_FLAG_DEST_LOCATION); targetMask |= effectTargetMask; } } ExplicitTargetMask = targetMask; } bool SpellInfo::_IsPositiveEffect(uint32 effIndex, bool deep) const { // not found a single positive spell with this attribute if (HasAttribute(SPELL_ATTR0_NEGATIVE_1)) return false; switch (SpellFamilyName) { case SPELLFAMILY_GENERIC: switch (Id) { case 29214: // Wrath of the Plaguebringer case 34700: // Allergic Reaction case 54836: // Wrath of the Plaguebringer return false; case 30877: // Tag Murloc case 61716: // Rabbit Costume case 61734: // Noblegarden Bunny case 62344: // Fists of Stone case 61819: // Manabonked! (item) case 61834: // Manabonked! (minigob) case 73523: // Rigor Mortis return true; default: break; } break; case SPELLFAMILY_MAGE: // Arcane Missiles if (SpellFamilyFlags[0] == 0x00000800) return false; break; case SPELLFAMILY_PRIEST: switch (Id) { case 64844: // Divine Hymn case 64904: // Hymn of Hope case 47585: // Dispersion return true; default: break; } break; case SPELLFAMILY_ROGUE: switch (Id) { // Envenom must be considered as a positive effect even though it deals damage case 32645: // Envenom return true; default: break; } break; default: break; } switch (Mechanic) { case MECHANIC_IMMUNE_SHIELD: return true; default: break; } // Special case: effects which determine positivity of whole spell for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (effect && effect->IsAura() && effect->ApplyAuraName == SPELL_AURA_MOD_STEALTH) return true; } } for (SpellEffectInfoMap::const_iterator itr = _effects.begin(); itr != _effects.end(); ++itr) { for (SpellEffectInfo const* effect : itr->second) { if (!effect || effect->EffectIndex != effIndex) continue; switch (effect->Effect) { case SPELL_EFFECT_DUMMY: // some explicitly required dummy effect sets switch (Id) { case 28441: return false; // AB Effect 000 default: break; } break; // always positive effects (check before target checks that provided non-positive result in some case for positive effects) case SPELL_EFFECT_HEAL: case SPELL_EFFECT_LEARN_SPELL: case SPELL_EFFECT_SKILL_STEP: case SPELL_EFFECT_HEAL_PCT: case SPELL_EFFECT_ENERGIZE_PCT: return true; case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: return false; // non-positive aura use case SPELL_EFFECT_APPLY_AURA: case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: { switch (effect->ApplyAuraName) { case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative) case SPELL_AURA_MOD_STAT: case SPELL_AURA_MOD_SKILL: case SPELL_AURA_MOD_SKILL_2: case SPELL_AURA_MOD_DODGE_PERCENT: case SPELL_AURA_MOD_HEALING_PCT: case SPELL_AURA_MOD_HEALING_DONE: case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: if (effect->CalcValue() < 0) return false; break; case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative) if (effect->CalcValue() > 0) return false; break; case SPELL_AURA_MOD_CRIT_PCT: case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: if (effect->CalcValue() > 0) return true; // some expected positive spells have SPELL_ATTR1_NEGATIVE break; case SPELL_AURA_ADD_TARGET_TRIGGER: return true; case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: case SPELL_AURA_PERIODIC_TRIGGER_SPELL: if (!deep) { if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(effect->TriggerSpell)) { // negative targets of main spell return early for (SpellEffectInfoMap::const_iterator it = spellTriggeredProto->_effects.begin(); it != spellTriggeredProto->_effects.end(); ++it) { for (SpellEffectInfo const* eff : itr->second) { if (!eff || !eff->Effect) continue; // if non-positive trigger cast targeted to positive target this main cast is non-positive // this will place this spell auras as debuffs if (_IsPositiveTarget(eff->TargetA.GetTarget(), eff->TargetB.GetTarget()) && !spellTriggeredProto->_IsPositiveEffect(eff->EffectIndex, true)) return false; } } } } case SPELL_AURA_PROC_TRIGGER_SPELL: // many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example) break; case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment. { bool more = false; for (SpellEffectInfoMap::const_iterator i = _effects.begin(); i != _effects.end(); ++i) { for (SpellEffectInfo const* eff : i->second) { if (eff && eff->EffectIndex != 0) { more = true; break; } } } if (effIndex == 0 && !more) return false; // but all single stun aura spells is negative break; } case SPELL_AURA_MOD_PACIFY_SILENCE: if (Id == 24740) // Wisp Costume return true; return false; case SPELL_AURA_MOD_ROOT: case SPELL_AURA_MOD_ROOT_2: case SPELL_AURA_MOD_SILENCE: case SPELL_AURA_GHOST: case SPELL_AURA_PERIODIC_LEECH: case SPELL_AURA_MOD_STALKED: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: case SPELL_AURA_PREVENT_RESURRECTION: return false; case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also. // part of negative spell if cast at self (prevent cancel) if (effect->TargetA.GetTarget() == TARGET_UNIT_CASTER) return false; break; case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also // part of positive spell if cast at self if (effect->TargetA.GetTarget() != TARGET_UNIT_CASTER) return false; // but not this if this first effect (didn't find better check) if (HasAttribute(SPELL_ATTR0_NEGATIVE_1) && effIndex == 0) return false; break; case SPELL_AURA_MECHANIC_IMMUNITY: { // non-positive immunities switch (effect->MiscValue) { case MECHANIC_BANDAGE: case MECHANIC_SHIELD: case MECHANIC_MOUNT: case MECHANIC_INVULNERABILITY: return false; default: break; } break; } case SPELL_AURA_ADD_FLAT_MODIFIER: // mods case SPELL_AURA_ADD_PCT_MODIFIER: { // non-positive mods switch (effect->MiscValue) { case SPELLMOD_COST: // dependent from bas point sign (negative -> positive) if (effect->CalcValue() > 0) { if (!deep) { bool negative = true; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (i != effIndex) if (_IsPositiveEffect(i, true)) { negative = false; break; } } if (negative) return false; } } break; default: break; } break; } default: break; } break; } default: break; } // non-positive targets if (!_IsPositiveTarget(effect->TargetA.GetTarget(), effect->TargetB.GetTarget())) return false; // negative spell if triggered spell is negative if (!deep && !effect->ApplyAuraName && effect->TriggerSpell) { if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(effect->TriggerSpell)) if (!spellTriggeredProto->_IsPositiveSpell()) return false; } } } // ok, positive return true; } bool SpellInfo::_IsPositiveSpell() const { // spells with at least one negative effect are considered negative // some self-applied spells have negative effects but in self casting case negative check ignored. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (!_IsPositiveEffect(i, true)) return false; return true; } bool SpellInfo::_IsPositiveTarget(uint32 targetA, uint32 targetB) { // non-positive targets switch (targetA) { case TARGET_UNIT_NEARBY_ENEMY: case TARGET_UNIT_TARGET_ENEMY: case TARGET_UNIT_SRC_AREA_ENEMY: case TARGET_UNIT_DEST_AREA_ENEMY: case TARGET_UNIT_CONE_ENEMY_24: case TARGET_UNIT_CONE_ENEMY_104: case TARGET_DEST_DYNOBJ_ENEMY: case TARGET_DEST_TARGET_ENEMY: return false; default: break; } if (targetB) return _IsPositiveTarget(targetB, 0); return true; } void SpellInfo::_UnloadImplicitTargetConditionLists() { // find the same instances of ConditionList and delete them. for (auto itr = _effects.begin(); itr != _effects.end(); ++itr) { for (uint32 i = 0; i < itr->second.size(); ++i) { if (SpellEffectInfo const* effect = itr->second[i]) { ConditionContainer* cur = effect->ImplicitTargetConditions; if (!cur) continue; for (uint8 j = i; j < itr->second.size(); ++j) { if (SpellEffectInfo const* eff = itr->second[j]) { if (eff->ImplicitTargetConditions == cur) const_cast(eff)->ImplicitTargetConditions = NULL; } } delete cur; } } } } SpellEffectInfoVector SpellInfo::GetEffectsForDifficulty(uint32 difficulty) const { SpellEffectInfoVector effList; // downscale difficulty if original was not found DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty); while (difficultyEntry) { SpellEffectInfoMap::const_iterator effectItr = _effects.find(difficulty); if (effectItr != _effects.end()) { for (SpellEffectInfo const* effect : effectItr->second) { if (effect) { if (effect->EffectIndex >= effList.size()) effList.resize(effect->EffectIndex + 1); if (!effList[effect->EffectIndex]) effList[effect->EffectIndex] = effect; } } } difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID); } // DIFFICULTY_NONE effects are the default effects, always active if current difficulty's effects don't overwrite SpellEffectInfoMap::const_iterator itr = _effects.find(DIFFICULTY_NONE); if (itr != _effects.end()) { for (SpellEffectInfo const* effect : itr->second) { if (effect) { if (effect->EffectIndex >= effList.size()) effList.resize(effect->EffectIndex + 1); if (!effList[effect->EffectIndex]) effList[effect->EffectIndex] = effect; } } } return effList; } SpellEffectInfo const* SpellInfo::GetEffect(uint32 difficulty, uint32 index) const { DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty); while (difficultyEntry) { SpellEffectInfoMap::const_iterator itr = _effects.find(difficulty); if (itr != _effects.end()) if (itr->second.size() > index && itr->second[index]) return itr->second[index]; difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID); } SpellEffectInfoMap::const_iterator itr = _effects.find(DIFFICULTY_NONE); if (itr != _effects.end()) if (itr->second.size() > index) return itr->second[index]; return nullptr; }