mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
5079 lines
224 KiB
C++
5079 lines
224 KiB
C++
/*
|
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "SpellInfo.h"
|
|
#include "Battleground.h"
|
|
#include "Containers.h"
|
|
#include "Corpse.h"
|
|
#include "DB2Stores.h"
|
|
#include "FlatSet.h"
|
|
#include "GameTables.h"
|
|
#include "InstanceScript.h"
|
|
#include "Item.h"
|
|
#include "ItemTemplate.h"
|
|
#include "Log.h"
|
|
#include "LootMgr.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 <G3D/g3dmath.h>
|
|
|
|
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<float>(M_PI);
|
|
case TARGET_DIR_RIGHT:
|
|
return static_cast<float>(-M_PI/2);
|
|
case TARGET_DIR_LEFT:
|
|
return static_cast<float>(M_PI/2);
|
|
case TARGET_DIR_FRONT_RIGHT:
|
|
return static_cast<float>(-M_PI/4);
|
|
case TARGET_DIR_BACK_RIGHT:
|
|
return static_cast<float>(-3*M_PI/4);
|
|
case TARGET_DIR_BACK_LEFT:
|
|
return static_cast<float>(3*M_PI/4);
|
|
case TARGET_DIR_FRONT_LEFT:
|
|
return static_cast<float>(M_PI/4);
|
|
case TARGET_DIR_RANDOM:
|
|
return float(rand_norm())*static_cast<float>(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:
|
|
[[fallthrough]];
|
|
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;
|
|
}
|
|
|
|
struct SpellEffectInfo::ImmunityInfo
|
|
{
|
|
ImmunityInfo() = default;
|
|
~ImmunityInfo() = default;
|
|
|
|
ImmunityInfo(ImmunityInfo const&) = delete;
|
|
ImmunityInfo(ImmunityInfo&&) noexcept = delete;
|
|
ImmunityInfo& operator=(ImmunityInfo const&) = delete;
|
|
ImmunityInfo& operator=(ImmunityInfo&&) noexcept = delete;
|
|
|
|
uint32 SchoolImmuneMask = 0;
|
|
uint32 ApplyHarmfulAuraImmuneMask = 0;
|
|
uint64 MechanicImmuneMask = 0;
|
|
uint32 DispelImmune = 0;
|
|
uint32 DamageSchoolMask = 0;
|
|
|
|
Trinity::Containers::FlatSet<AuraType> AuraTypeImmune;
|
|
Trinity::Containers::FlatSet<SpellEffectName> SpellEffectImmune;
|
|
};
|
|
|
|
std::array<SpellImplicitTargetInfo::StaticData, TOTAL_SPELL_TARGETS> SpellImplicitTargetInfo::_data =
|
|
{ {
|
|
{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_ALLY, TARGET_DIR_NONE}, // 3 TARGET_UNIT_NEARBY_ALLY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_PARTY, TARGET_DIR_NONE}, // 4 TARGET_UNIT_NEARBY_PARTY
|
|
{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_180_DEG_ENEMY
|
|
{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_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 62 TARGET_DEST_CASTER_GROUND
|
|
{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_TRAJ, 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_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 104 TARGET_UNIT_CONE_CASTER_TO_DEST_ENEMY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 105 TARGET_UNIT_CASTER_AND_PASSENGERS
|
|
{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_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 107 TARGET_DEST_NEARBY_ENTRY_2
|
|
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ENEMY
|
|
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ALLY, TARGET_DIR_FRONT}, // 109 TARGET_GAMEOBJECT_CONE_CASTER_TO_DEST_ALLY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENTRY, TARGET_DIR_FRONT}, // 110 TARGET_UNIT_CONE_CASTER_TO_DEST_ENTRY
|
|
{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_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, 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_UNIT, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 115 TARGET_UNIT_SRC_AREA_FURTHEST_ENEMY
|
|
{TARGET_OBJECT_TYPE_UNIT_AND_DEST, TARGET_REFERENCE_TYPE_LAST, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 116 TARGET_UNIT_AND_DEST_LAST_ENEMY
|
|
{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_TARGET, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 118 TARGET_UNIT_TARGET_ALLY_OR_RAID
|
|
{TARGET_OBJECT_TYPE_CORPSE, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 119 TARGET_CORPSE_SRC_AREA_RAID
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_SUMMONED, TARGET_DIR_NONE}, // 120 TARGET_UNIT_SELF_AND_SUMMONS
|
|
{TARGET_OBJECT_TYPE_CORPSE, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 121 TARGET_CORPSE_TARGET_ALLY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_THREAT, TARGET_DIR_NONE}, // 122 TARGET_UNIT_AREA_THREAT_LIST
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_TAP, TARGET_DIR_NONE}, // 123 TARGET_UNIT_AREA_TAP_LIST
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 124 TARGET_UNIT_TARGET_TAP_LIST
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 125 TARGET_DEST_CASTER_GROUND_2
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 126 TARGET_UNIT_CASTER_AREA_ENEMY_CLUMP
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 127 TARGET_DEST_CASTER_ENEMY_CLUMP_CENTROID
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ALLY, TARGET_DIR_FRONT}, // 128 TARGET_UNIT_RECT_CASTER_ALLY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENTRY, TARGET_DIR_FRONT}, // 129 TARGET_UNIT_RECT_CASTER_ENEMY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 130 TARGET_UNIT_RECT_CASTER
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 131 TARGET_DEST_SUMMONER
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 132 TARGET_DEST_TARGET_ALLY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_LINE, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 133 TARGET_UNIT_LINE_CASTER_TO_DEST_ALLY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_LINE, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 134 TARGET_UNIT_LINE_CASTER_TO_DEST_ENEMY
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_LINE, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 135 TARGET_UNIT_LINE_CASTER_TO_DEST
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ALLY, TARGET_DIR_FRONT}, // 136 TARGET_UNIT_CONE_CASTER_TO_DEST_ALLY
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 137 TARGET_DEST_CASTER_MOVEMENT_DIRECTION
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 138 TARGET_DEST_DEST_GROUND
|
|
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 139
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 140 TARGET_DEST_CASTER_CLUMP_CENTROID
|
|
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 141
|
|
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_ENTRY, TARGET_DIR_FRONT_RIGHT}, // 142 TARGET_DEST_NEARBY_ENTRY_OR_DB
|
|
{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_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 148 TARGET_DEST_DEST_TARGET_TOWARDS_CASTER
|
|
{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 TARGET_UNIT_OWN_CRITTER
|
|
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 151
|
|
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 152
|
|
} };
|
|
|
|
SpellEffectInfo::SpellEffectInfo(SpellInfo const* spellInfo): _spellInfo(spellInfo), EffectIndex(EFFECT_0), Effect(SPELL_EFFECT_NONE), ApplyAuraName(AuraType(0)), ApplyAuraPeriod(0),
|
|
BasePoints(0), RealPointsPerLevel(0), PointsPerResource(0), Amplitude(0), ChainAmplitude(0),
|
|
BonusCoefficient(0), MiscValue(0), MiscValueB(0), Mechanic(MECHANIC_NONE), PositionFacing(0),
|
|
TargetARadiusEntry(nullptr), TargetBRadiusEntry(nullptr), ChainTargets(0), ItemType(0), TriggerSpell(0),
|
|
BonusCoefficientFromAP(0.0f), ImplicitTargetConditions(nullptr),
|
|
EffectAttributes(SpellEffectAttributes::None), Scaling(), _immunityInfo(nullptr)
|
|
{
|
|
}
|
|
|
|
SpellEffectInfo::SpellEffectInfo(SpellInfo const* spellInfo, SpellEffectEntry const& _effect)
|
|
: EffectAttributes(SpellEffectAttributes::None)
|
|
{
|
|
ASSERT(spellInfo);
|
|
|
|
_spellInfo = spellInfo;
|
|
EffectIndex = SpellEffIndex(_effect.EffectIndex);
|
|
Effect = SpellEffectName(_effect.Effect);
|
|
ApplyAuraName = AuraType(_effect.EffectAura);
|
|
ApplyAuraPeriod = _effect.EffectAuraPeriod;
|
|
BasePoints = _effect.EffectBasePoints;
|
|
RealPointsPerLevel = _effect.EffectRealPointsPerLevel;
|
|
PointsPerResource = _effect.EffectPointsPerResource;
|
|
Amplitude = _effect.EffectAmplitude;
|
|
ChainAmplitude = _effect.EffectChainAmplitude;
|
|
BonusCoefficient = _effect.EffectBonusCoefficient;
|
|
MiscValue = _effect.EffectMiscValue[0];
|
|
MiscValueB = _effect.EffectMiscValue[1];
|
|
Mechanic = Mechanics(_effect.EffectMechanic);
|
|
PositionFacing = _effect.EffectPosFacing;
|
|
TargetA = SpellImplicitTargetInfo(_effect.ImplicitTarget[0]);
|
|
TargetB = SpellImplicitTargetInfo(_effect.ImplicitTarget[1]);
|
|
TargetARadiusEntry = sSpellRadiusStore.LookupEntry(_effect.EffectRadiusIndex[0]);
|
|
TargetBRadiusEntry = sSpellRadiusStore.LookupEntry(_effect.EffectRadiusIndex[1]);
|
|
ChainTargets = _effect.EffectChainTargets;
|
|
ItemType = _effect.EffectItemType;
|
|
TriggerSpell = _effect.EffectTriggerSpell;
|
|
SpellClassMask = _effect.EffectSpellClassMask;
|
|
BonusCoefficientFromAP = _effect.BonusCoefficientFromAP;
|
|
Scaling.Class = _effect.ScalingClass;
|
|
Scaling.Coefficient = _effect.Coefficient;
|
|
Scaling.Variance = _effect.Variance;
|
|
Scaling.ResourceCoefficient = _effect.ResourceCoefficient;
|
|
ImplicitTargetConditions = nullptr;
|
|
EffectAttributes = _effect.GetEffectAttributes();
|
|
_immunityInfo = nullptr;
|
|
}
|
|
|
|
SpellEffectInfo::SpellEffectInfo(SpellEffectInfo const&) = default;
|
|
SpellEffectInfo::SpellEffectInfo(SpellEffectInfo&&) noexcept = default;
|
|
SpellEffectInfo& SpellEffectInfo::operator=(SpellEffectInfo const&) = default;
|
|
SpellEffectInfo& SpellEffectInfo::operator=(SpellEffectInfo&&) noexcept = default;
|
|
|
|
SpellEffectInfo::~SpellEffectInfo()
|
|
{
|
|
delete _immunityInfo;
|
|
}
|
|
|
|
bool SpellEffectInfo::IsEffect() const
|
|
{
|
|
return Effect != 0;
|
|
}
|
|
|
|
bool SpellEffectInfo::IsEffect(SpellEffectName effectName) const
|
|
{
|
|
return Effect == 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 ||
|
|
Effect == SPELL_EFFECT_APPLY_AREA_AURA_SUMMONS ||
|
|
Effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool SpellEffectInfo::IsUnitOwnedAuraEffect() const
|
|
{
|
|
return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_ON_PET;
|
|
}
|
|
|
|
int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/, Unit const* target /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const
|
|
{
|
|
double basePointsPerLevel = RealPointsPerLevel;
|
|
// TODO: this needs to be a float, not rounded
|
|
int32 basePoints = CalcBaseValue(caster, target, castItemId, itemLevel);
|
|
double value = bp ? *bp : basePoints;
|
|
double comboDamage = PointsPerResource;
|
|
|
|
Unit const* casterUnit = nullptr;
|
|
if (caster)
|
|
casterUnit = caster->ToUnit();
|
|
|
|
if (Scaling.Variance)
|
|
{
|
|
float delta = fabs(Scaling.Variance * 0.5f);
|
|
double valueVariance = frand(-delta, delta);
|
|
value += double(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)
|
|
{
|
|
if (casterUnit && basePointsPerLevel != 0.0)
|
|
{
|
|
int32 level = int32(casterUnit->GetLevel());
|
|
if (level > int32(_spellInfo->MaxLevel) && _spellInfo->MaxLevel > 0)
|
|
level = int32(_spellInfo->MaxLevel);
|
|
|
|
// if base level is greater than spell level, reduce by base level (eg. pilgrims foods)
|
|
level -= int32(std::max(_spellInfo->BaseLevel, _spellInfo->SpellLevel));
|
|
if (level < 0)
|
|
level = 0;
|
|
value += level * basePointsPerLevel;
|
|
}
|
|
}
|
|
|
|
// random damage
|
|
if (casterUnit)
|
|
{
|
|
// bonus amount from combo points
|
|
if (comboDamage)
|
|
if (int32 comboPoints = casterUnit->GetPower(POWER_COMBO_POINTS))
|
|
value += comboDamage * comboPoints;
|
|
}
|
|
|
|
if (caster)
|
|
value = caster->ApplyEffectModifiers(_spellInfo, EffectIndex, value);
|
|
|
|
return int32(round(value));
|
|
}
|
|
|
|
int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const
|
|
{
|
|
if (Scaling.Coefficient != 0.0f)
|
|
{
|
|
uint32 level = _spellInfo->SpellLevel;
|
|
if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING))
|
|
level = target->GetLevel();
|
|
else if (caster && caster->IsUnit())
|
|
level = caster->ToUnit()->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 (!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 (Scaling.Class == -8 || Scaling.Class == -9)
|
|
{
|
|
RandPropPointsEntry const* randPropPoints = sRandPropPointsStore.LookupEntry(effectiveItemLevel);
|
|
if (!randPropPoints)
|
|
randPropPoints = sRandPropPointsStore.AssertEntry(sRandPropPointsStore.GetNumRows() - 1);
|
|
|
|
value = Scaling.Class == -8 ? randPropPoints->DamageReplaceStatF : randPropPoints->DamageSecondaryF;
|
|
}
|
|
else
|
|
value = GetRandomPropertyPoints(effectiveItemLevel, ITEM_QUALITY_RARE, INVTYPE_CHEST, 0);
|
|
}
|
|
else
|
|
value = GetSpellScalingColumnForClass(sSpellScalingGameTable.GetRow(level), Scaling.Class);
|
|
|
|
if (Scaling.Class == -7)
|
|
if (GtCombatRatingsMultByILvl const* ratingMult = sCombatRatingsMultByILvlGameTable.GetRow(effectiveItemLevel))
|
|
if (ItemSparseEntry const* itemSparse = sItemSparseStore.LookupEntry(itemId))
|
|
value *= GetIlvlStatMultiplier(ratingMult, InventoryType(itemSparse->InventoryType));
|
|
|
|
if (Scaling.Class == -6)
|
|
if (GtStaminaMultByILvl const* staminaMult = sStaminaMultByILvlGameTable.GetRow(effectiveItemLevel))
|
|
if (ItemSparseEntry const* itemSparse = sItemSparseStore.LookupEntry(itemId))
|
|
value *= GetIlvlStatMultiplier(staminaMult, InventoryType(itemSparse->InventoryType));
|
|
}
|
|
|
|
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_SCALES_WITH_CREATURE_LEVEL))
|
|
stat = ExpectedStatType::CreatureAutoAttackDps;
|
|
|
|
// TODO - add expansion and content tuning id args?
|
|
uint32 contentTuningId = _spellInfo->ContentTuningId; // content tuning should be passed as arg, the one stored in SpellInfo is fallback
|
|
int32 expansion = -2;
|
|
if (ContentTuningEntry const* contentTuning = sContentTuningStore.LookupEntry(contentTuningId))
|
|
expansion = contentTuning->ExpansionID;
|
|
|
|
int32 level = 1;
|
|
if (target && _spellInfo->HasAttribute(SPELL_ATTR8_USE_TARGETS_LEVEL_FOR_SPELL_SCALING))
|
|
level = target->GetLevel();
|
|
else if (caster && caster->IsUnit())
|
|
level = caster->ToUnit()->GetLevel();
|
|
|
|
value = sDB2Manager.EvaluateExpectedStat(stat, level, expansion, 0, CLASS_NONE, 0) * BasePoints / 100.0f;
|
|
}
|
|
|
|
return int32(round(value));
|
|
}
|
|
}
|
|
|
|
float SpellEffectInfo::CalcValueMultiplier(WorldObject* caster, Spell* spell /*= nullptr*/) const
|
|
{
|
|
float multiplier = Amplitude;
|
|
if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr))
|
|
modOwner->ApplySpellMod(_spellInfo, SpellModOp::Amplitude, multiplier, spell);
|
|
return multiplier;
|
|
}
|
|
|
|
float SpellEffectInfo::CalcDamageMultiplier(WorldObject* caster, Spell* spell /*= nullptr*/) const
|
|
{
|
|
float multiplierPercent = ChainAmplitude * 100.0f;
|
|
if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr))
|
|
modOwner->ApplySpellMod(_spellInfo, SpellModOp::ChainAmplitude, multiplierPercent, spell);
|
|
return multiplierPercent / 100.0f;
|
|
}
|
|
|
|
bool SpellEffectInfo::HasRadius(SpellTargetIndex targetIndex) const
|
|
{
|
|
switch (targetIndex)
|
|
{
|
|
case SpellTargetIndex::TargetA:
|
|
return TargetARadiusEntry != nullptr;
|
|
case SpellTargetIndex::TargetB:
|
|
return TargetBRadiusEntry != nullptr;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
float SpellEffectInfo::CalcRadius(WorldObject* caster /*= nullptr*/, SpellTargetIndex targetIndex /*=SpellTargetIndex::TargetA*/, Spell* spell /*= nullptr*/) const
|
|
{
|
|
// TargetA -> TargetARadiusEntry
|
|
// TargetB -> TargetBRadiusEntry
|
|
// Aura effects have TargetARadiusEntry == TargetBRadiusEntry (mostly)
|
|
SpellRadiusEntry const* entry = TargetARadiusEntry;
|
|
if (targetIndex == SpellTargetIndex::TargetB && HasRadius(targetIndex))
|
|
entry = TargetBRadiusEntry;
|
|
|
|
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)
|
|
{
|
|
if (Unit* casterUnit = caster->ToUnit())
|
|
radius += entry->RadiusPerLevel * casterUnit->GetLevel();
|
|
|
|
radius = std::min(radius, entry->RadiusMax);
|
|
|
|
if (Player* modOwner = caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(_spellInfo, SpellModOp::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 == POWER_MANA)
|
|
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_APPLY_AREA_AURA_SUMMONS:
|
|
case SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM:
|
|
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 == POWER_MANA)
|
|
return ExpectedStatType::PlayerMana;
|
|
return ExpectedStatType::None;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ExpectedStatType::None;
|
|
}
|
|
|
|
std::array<SpellEffectInfo::StaticData, TOTAL_SPELL_EFFECTS> SpellEffectInfo::_data =
|
|
{ {
|
|
// 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_ITEM}, // 5 SPELL_EFFECT_5
|
|
{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_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 13 SPELL_EFFECT_TELEPORT_TO_RETURN_POINT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 14 SPELL_EFFECT_INCREASE_CURRENCY_CAP
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 15 SPELL_EFFECT_TELEPORT_WITH_SPELL_VISUAL_KIT_LOADING_SCREEN
|
|
{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_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 52 SPELL_EFFECT_SET_MAX_BATTLE_PET_COUNT
|
|
{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_COMPLETE_AND_REWARD_WORLD_QUEST
|
|
{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_MODIFY_FOLLOWER_ITEM_LEVEL
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 81 SPELL_EFFECT_PUSH_ABILITY_TO_ACTION_BAR
|
|
{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_UNIT}, // 105 SPELL_EFFECT_SURVEY
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 106 SPELL_EFFECT_CHANGE_RAID_MARKER
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 107 SPELL_EFFECT_SHOW_CORPSE_LOOT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 108 SPELL_EFFECT_DISPEL_MECHANIC
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 109 SPELL_EFFECT_RESURRECT_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_UNIT}, // 113 SPELL_EFFECT_CANCEL_CONVERSATION
|
|
{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_RESTORE_GARRISON_TROOP_VITALITY
|
|
{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_LOOT
|
|
{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_ITEM}, // 163 SPELL_EFFECT_OBLITERATE_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 164 SPELL_EFFECT_REMOVE_AURA
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 165 SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT
|
|
{EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 166 SPELL_EFFECT_GIVE_CURRENCY
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 167 SPELL_EFFECT_UPDATE_PLAYER_PHASE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 168 SPELL_EFFECT_ALLOW_CONTROL_PET
|
|
{EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 169 SPELL_EFFECT_DESTROY_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 170 SPELL_EFFECT_UPDATE_ZONE_AURAS_AND_PHASES
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 171 SPELL_EFFECT_SUMMON_PERSONAL_GAMEOBJECT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_CORPSE_ALLY}, // 172 SPELL_EFFECT_RESURRECT_WITH_AURA
|
|
{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_APPLY_AURA_ON_PET
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 175 SPELL_EFFECT_175
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 176 SPELL_EFFECT_SANCTUARY_2
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 177 SPELL_EFFECT_DESPAWN_PERSISTENT_AREA_AURA
|
|
{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_DESPAWN_AREATRIGGER
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 183 SPELL_EFFECT_183
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 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_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 187 SPELL_EFFECT_RANDOMIZE_ARCHAEOLOGY_DIGSITES
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_DEST}, // 188 SPELL_EFFECT_SUMMON_STABLED_PET_AS_GUARDIAN
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 189 SPELL_EFFECT_LOOT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 190 SPELL_EFFECT_CHANGE_PARTY_MEMBERS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 191 SPELL_EFFECT_TELEPORT_TO_DIGSITE
|
|
{EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 192 SPELL_EFFECT_UNCAGE_BATTLEPET
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 193 SPELL_EFFECT_START_PET_BATTLE
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 194 SPELL_EFFECT_194
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 195 SPELL_EFFECT_PLAY_SCENE_SCRIPT_PACKAGE
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 196 SPELL_EFFECT_CREATE_SCENE_OBJECT
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 197 SPELL_EFFECT_CREATE_PERSONAL_SCENE_OBJECT
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 198 SPELL_EFFECT_PLAY_SCENE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 199 SPELL_EFFECT_DESPAWN_SUMMON
|
|
{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_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 202 SPELL_EFFECT_APPLY_AREA_AURA_SUMMONS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 203 SPELL_EFFECT_REMOVE_AURA_2
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 206 SPELL_EFFECT_ALTER_IETM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 207 SPELL_EFFECT_LAUNCH_QUEST_TASK
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 208 SPELL_EFFECT_SET_REPUTATION
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 209 SPELL_EFFECT_209
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 210 SPELL_EFFECT_LEARN_GARRISON_BUILDING
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 211 SPELL_EFFECT_LEARN_GARRISON_SPECIALIZATION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 212 SPELL_EFFECT_REMOVE_AURA_BY_SPELL_LABEL
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 213 SPELL_EFFECT_JUMP_DEST_2
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 214 SPELL_EFFECT_CREATE_GARRISON
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 215 SPELL_EFFECT_UPGRADE_CHARACTER_SPELLS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 216 SPELL_EFFECT_CREATE_SHIPMENT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 217 SPELL_EFFECT_UPGRADE_GARRISON
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 218 SPELL_EFFECT_218
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 219 SPELL_EFFECT_CREATE_CONVERSATION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 221 SPELL_EFFECT_ADD_GARRISON_MISSION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 223 SPELL_EFFECT_CHANGE_ITEM_BONUSES
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 224 SPELL_EFFECT_ACTIVATE_GARRISON_BUILDING
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 225 SPELL_EFFECT_GRANT_BATTLEPET_LEVEL
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 226 SPELL_EFFECT_TRIGGER_ACTION_SET
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 227 SPELL_EFFECT_TELEPORT_TO_LFG_DUNGEON
|
|
{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_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 230 SPELL_EFFECT_230
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 231 SPELL_EFFECT_INCREASE_FOLLOWER_EXPERIENCE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 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_DEST}, // 234 SPELL_EFFECT_234
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 235 SPELL_EFFECT_235
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 236 SPELL_EFFECT_GIVE_EXPERIENCE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 237 SPELL_EFFECT_GIVE_RESTED_EXPERIENCE_BONUS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 238 SPELL_EFFECT_INCREASE_SKILL
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 239 SPELL_EFFECT_END_GARRISON_BUILDING_CONSTRUCTION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 240 SPELL_EFFECT_GIVE_ARTIFACT_POWER
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 241 SPELL_EFFECT_241
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 242 SPELL_EFFECT_GIVE_ARTIFACT_POWER_NO_BONUS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 243 SPELL_EFFECT_APPLY_ENCHANT_ILLUSION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 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_SET
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 248 SPELL_EFFECT_FINISH_SHIPMENT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 249 SPELL_EFFECT_FORCE_EQUIP_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 250 SPELL_EFFECT_TAKE_SCREENSHOT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 251 SPELL_EFFECT_SET_GARRISON_CACHE_SIZE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT_AND_DEST}, // 252 SPELL_EFFECT_TELEPORT_UNITS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 253 SPELL_EFFECT_GIVE_HONOR
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_DEST}, // 254 SPELL_EFFECT_JUMP_CHARGE
|
|
{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
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 262 SPELL_EFFECT_262
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 263 SPELL_EFFECT_REPAIR_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 264 SPELL_EFFECT_REMOVE_GEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 265 SPELL_EFFECT_LEARN_AZERITE_ESSENCE_POWER
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 266 SPELL_EFFECT_SET_ITEM_BONUS_LIST_GROUP_ENTRY
|
|
{EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 267 SPELL_EFFECT_CREATE_PRIVATE_CONVERSATION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 268 SPELL_EFFECT_APPLY_MOUNT_EQUIPMENT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 269 SPELL_EFFECT_INCREASE_ITEM_BONUS_LIST_GROUP_STEP
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 270 SPELL_EFFECT_270
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 271 SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 272 SPELL_EFFECT_SET_COVENANT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 273 SPELL_EFFECT_CRAFT_RUNEFORGE_LEGENDARY
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 274 SPELL_EFFECT_274
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 275 SPELL_EFFECT_275
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 276 SPELL_EFFECT_LEARN_TRANSMOG_ILLUSION
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 277 SPELL_EFFECT_SET_CHROMIE_TIME
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 278 SPELL_EFFECT_278
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 279 SPELL_EFFECT_LEARN_GARR_TALENT
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 280 SPELL_EFFECT_280
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 281 SPELL_EFFECT_LEARN_SOULBIND_CONDUIT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 282 SPELL_EFFECT_CONVERT_ITEMS_TO_CURRENCY
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 283 SPELL_EFFECT_COMPLETE_CAMPAIGN
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 284 SPELL_EFFECT_SEND_CHAT_MESSAGE
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 285 SPELL_EFFECT_MODIFY_KEYSTONE_2
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 286 SPELL_EFFECT_GRANT_BATTLEPET_EXPERIENCE
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 287 SPELL_EFFECT_SET_GARRISON_FOLLOWER_LEVEL
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 288 SPELL_EFFECT_CRAFT_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 289 SPELL_EFFECT_MODIFY_AURA_STACKS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 290 SPELL_EFFECT_MODIFY_COOLDOWN
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 291 SPELL_EFFECT_MODIFY_COOLDOWNS
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 292 SPELL_EFFECT_MODIFY_COOLDOWNS_BY_CATEGORY
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 293 SPELL_EFFECT_MODIFY_CHARGES
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 294 SPELL_EFFECT_CRAFT_LOOT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 295 SPELL_EFFECT_SALVAGE_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 296 SPELL_EFFECT_CRAFT_SALVAGE_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 297 SPELL_EFFECT_RECRAFT_ITEM
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 298 SPELL_EFFECT_CANCEL_ALL_PRIVATE_CONVERSATIONS
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 299 SPELL_EFFECT_299
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 300 SPELL_EFFECT_300
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 301 SPELL_EFFECT_CRAFT_ENCHANT
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_NONE}, // 302 SPELL_EFFECT_GATHERING
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 303 SPELL_EFFECT_CREATE_TRAIT_TREE_CONFIG
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 304 SPELL_EFFECT_CHANGE_ACTIVE_COMBAT_TRAIT_CONFIG
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 305 SPELL_EFFECT_305
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 306 SPELL_EFFECT_UPDATE_INTERACTIONS
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 307 SPELL_EFFECT_307
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 308 SPELL_EFFECT_CANCEL_PRELOAD_WORLD
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 309 SPELL_EFFECT_PRELOAD_WORLD
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 310 SPELL_EFFECT_310
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 311 SPELL_EFFECT_ENSURE_WORLD_LOADED
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 312 SPELL_EFFECT_312
|
|
{EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 313 SPELL_EFFECT_CHANGE_ITEM_BONUSES_2
|
|
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 314 SPELL_EFFECT_ADD_SOCKET_BONUS
|
|
{EFFECT_IMPLICIT_TARGET_CASTER, TARGET_OBJECT_TYPE_UNIT}, // 315 SPELL_EFFECT_LEARN_TRANSMOG_APPEARANCE_FROM_ITEM_MOD_APPEARANCE_GROUP
|
|
} };
|
|
|
|
SpellInfo::SpellInfo(SpellNameEntry const* spellName, ::Difficulty difficulty, SpellInfoLoadHelper const& data)
|
|
: Id(spellName->ID), Difficulty(difficulty)
|
|
{
|
|
_effects.reserve(32);
|
|
for (SpellEffectEntry const* spellEffect : data.Effects)
|
|
{
|
|
if (!spellEffect)
|
|
continue;
|
|
|
|
Trinity::Containers::EnsureWritableVectorIndex(_effects, spellEffect->EffectIndex, SpellEffectInfo(this)) = SpellEffectInfo(this, *spellEffect);
|
|
}
|
|
|
|
// Correct EffectIndex for blank effects
|
|
for (size_t i = 0; i < _effects.size(); ++i)
|
|
_effects[i].EffectIndex = SpellEffIndex(i);
|
|
|
|
_effects.shrink_to_fit();
|
|
|
|
SpellName = &spellName->Name;
|
|
|
|
// SpellMiscEntry
|
|
if (SpellMiscEntry const* _misc = data.Misc)
|
|
{
|
|
Attributes = _misc->Attributes[0];
|
|
AttributesEx = _misc->Attributes[1];
|
|
AttributesEx2 = _misc->Attributes[2];
|
|
AttributesEx3 = _misc->Attributes[3];
|
|
AttributesEx4 = _misc->Attributes[4];
|
|
AttributesEx5 = _misc->Attributes[5];
|
|
AttributesEx6 = _misc->Attributes[6];
|
|
AttributesEx7 = _misc->Attributes[7];
|
|
AttributesEx8 = _misc->Attributes[8];
|
|
AttributesEx9 = _misc->Attributes[9];
|
|
AttributesEx10 = _misc->Attributes[10];
|
|
AttributesEx11 = _misc->Attributes[11];
|
|
AttributesEx12 = _misc->Attributes[12];
|
|
AttributesEx13 = _misc->Attributes[13];
|
|
AttributesEx14 = _misc->Attributes[14];
|
|
CastTimeEntry = sSpellCastTimesStore.LookupEntry(_misc->CastingTimeIndex);
|
|
DurationEntry = sSpellDurationStore.LookupEntry(_misc->DurationIndex);
|
|
RangeEntry = sSpellRangeStore.LookupEntry(_misc->RangeIndex);
|
|
Speed = _misc->Speed;
|
|
LaunchDelay = _misc->LaunchDelay;
|
|
SchoolMask = _misc->SchoolMask;
|
|
IconFileDataId = _misc->SpellIconFileDataID;
|
|
ActiveIconFileDataId = _misc->ActiveIconFileDataID;
|
|
ContentTuningId = _misc->ContentTuningID;
|
|
ShowFutureSpellPlayerConditionID = _misc->ShowFutureSpellPlayerConditionID;
|
|
}
|
|
|
|
// SpellScalingEntry
|
|
if (SpellScalingEntry const* _scaling = data.Scaling)
|
|
{
|
|
Scaling.MinScalingLevel = _scaling->MinScalingLevel;
|
|
Scaling.MaxScalingLevel = _scaling->MaxScalingLevel;
|
|
Scaling.ScalesFromItemLevel = _scaling->ScalesFromItemLevel;
|
|
}
|
|
|
|
// SpellAuraOptionsEntry
|
|
if (SpellAuraOptionsEntry const* _options = data.AuraOptions)
|
|
{
|
|
ProcFlags = _options->ProcTypeMask;
|
|
ProcChance = _options->ProcChance;
|
|
ProcCharges = _options->ProcCharges;
|
|
ProcCooldown = _options->ProcCategoryRecovery;
|
|
StackAmount = _options->CumulativeAura;
|
|
if (SpellProcsPerMinuteEntry const* _ppm = sSpellProcsPerMinuteStore.LookupEntry(_options->SpellProcsPerMinuteID))
|
|
{
|
|
ProcBasePPM = _ppm->BaseProcRate;
|
|
ProcPPMMods = sDB2Manager.GetSpellProcsPerMinuteMods(_ppm->ID);
|
|
}
|
|
}
|
|
|
|
// SpellAuraRestrictionsEntry
|
|
if (SpellAuraRestrictionsEntry const* _aura = data.AuraRestrictions)
|
|
{
|
|
CasterAuraState = _aura->CasterAuraState;
|
|
TargetAuraState = _aura->TargetAuraState;
|
|
ExcludeCasterAuraState = _aura->ExcludeCasterAuraState;
|
|
ExcludeTargetAuraState = _aura->ExcludeTargetAuraState;
|
|
CasterAuraSpell = _aura->CasterAuraSpell;
|
|
TargetAuraSpell = _aura->TargetAuraSpell;
|
|
ExcludeCasterAuraSpell = _aura->ExcludeCasterAuraSpell;
|
|
ExcludeTargetAuraSpell = _aura->ExcludeTargetAuraSpell;
|
|
CasterAuraType = AuraType(_aura->CasterAuraType);
|
|
TargetAuraType = AuraType(_aura->TargetAuraType);
|
|
ExcludeCasterAuraType = AuraType(_aura->ExcludeCasterAuraType);
|
|
ExcludeTargetAuraType = AuraType(_aura->ExcludeTargetAuraType);
|
|
}
|
|
|
|
// SpellCastingRequirementsEntry
|
|
if (SpellCastingRequirementsEntry const* _castreq = data.CastingRequirements)
|
|
{
|
|
RequiresSpellFocus = _castreq->RequiresSpellFocus;
|
|
FacingCasterFlags = _castreq->FacingCasterFlags;
|
|
RequiredAreasID = _castreq->RequiredAreasID;
|
|
}
|
|
|
|
// SpellCategoriesEntry
|
|
if (SpellCategoriesEntry const* _categories = data.Categories)
|
|
{
|
|
CategoryId = _categories->Category;
|
|
Dispel = _categories->DispelType;
|
|
Mechanic = _categories->Mechanic;
|
|
StartRecoveryCategory = _categories->StartRecoveryCategory;
|
|
DmgClass = _categories->DefenseType;
|
|
PreventionType = _categories->PreventionType;
|
|
ChargeCategoryId = _categories->ChargeCategory;
|
|
}
|
|
|
|
// SpellClassOptionsEntry
|
|
if (SpellClassOptionsEntry const* _class = data.ClassOptions)
|
|
{
|
|
SpellFamilyName = _class->SpellClassSet;
|
|
SpellFamilyFlags = _class->SpellClassMask;
|
|
}
|
|
|
|
// SpellCooldownsEntry
|
|
if (SpellCooldownsEntry const* _cooldowns = data.Cooldowns)
|
|
{
|
|
RecoveryTime = _cooldowns->RecoveryTime;
|
|
CategoryRecoveryTime = _cooldowns->CategoryRecoveryTime;
|
|
StartRecoveryTime = _cooldowns->StartRecoveryTime;
|
|
CooldownAuraSpellId = _cooldowns->AuraSpellID;
|
|
}
|
|
|
|
// SpellEquippedItemsEntry
|
|
if (SpellEquippedItemsEntry const* _equipped = data.EquippedItems)
|
|
{
|
|
EquippedItemClass = _equipped->EquippedItemClass;
|
|
EquippedItemSubClassMask = _equipped->EquippedItemSubclass;
|
|
EquippedItemInventoryTypeMask = _equipped->EquippedItemInvTypes;
|
|
}
|
|
|
|
// SpellInterruptsEntry
|
|
if (SpellInterruptsEntry const* _interrupt = data.Interrupts)
|
|
{
|
|
InterruptFlags = SpellInterruptFlags(_interrupt->InterruptFlags);
|
|
AuraInterruptFlags = SpellAuraInterruptFlags(_interrupt->AuraInterruptFlags[0]);
|
|
AuraInterruptFlags2 = SpellAuraInterruptFlags2(_interrupt->AuraInterruptFlags[1]);
|
|
ChannelInterruptFlags = SpellAuraInterruptFlags(_interrupt->ChannelInterruptFlags[0]);
|
|
ChannelInterruptFlags2 = SpellAuraInterruptFlags2(_interrupt->ChannelInterruptFlags[1]);
|
|
}
|
|
|
|
for (SpellLabelEntry const* label : data.Labels)
|
|
Labels.insert(label->LabelID);
|
|
|
|
// SpellLevelsEntry
|
|
if (SpellLevelsEntry const* _levels = data.Levels)
|
|
{
|
|
MaxLevel = _levels->MaxLevel;
|
|
BaseLevel = _levels->BaseLevel;
|
|
SpellLevel = _levels->SpellLevel;
|
|
}
|
|
|
|
// SpellPowerEntry
|
|
PowerCosts = data.Powers;
|
|
|
|
// SpellReagentsEntry
|
|
if (SpellReagentsEntry const* _reagents = data.Reagents)
|
|
{
|
|
Reagent = _reagents->Reagent;
|
|
ReagentCount = _reagents->ReagentCount;
|
|
}
|
|
|
|
ReagentsCurrency = data.ReagentsCurrency;
|
|
|
|
// SpellShapeshiftEntry
|
|
if (SpellShapeshiftEntry const* _shapeshift = data.Shapeshift)
|
|
{
|
|
Stances = MAKE_PAIR64(_shapeshift->ShapeshiftMask[0], _shapeshift->ShapeshiftMask[1]);
|
|
StancesNot = MAKE_PAIR64(_shapeshift->ShapeshiftExclude[0], _shapeshift->ShapeshiftExclude[1]);
|
|
}
|
|
|
|
// SpellTargetRestrictionsEntry
|
|
if (SpellTargetRestrictionsEntry const* _target = data.TargetRestrictions)
|
|
{
|
|
ConeAngle = _target->ConeDegrees;
|
|
Width = _target->Width;
|
|
Targets = _target->Targets;
|
|
TargetCreatureType = _target->TargetCreatureType;
|
|
MaxAffectedTargets = _target->MaxTargets;
|
|
MaxTargetLevel = _target->MaxTargetLevel;
|
|
}
|
|
|
|
// SpellTotemsEntry
|
|
if (SpellTotemsEntry const* _totem = data.Totems)
|
|
{
|
|
TotemCategory = _totem->RequiredTotemCategoryID;
|
|
Totem = _totem->Totem;
|
|
}
|
|
|
|
_visuals = data.Visuals;
|
|
}
|
|
|
|
SpellInfo::SpellInfo(SpellNameEntry const* spellName, ::Difficulty difficulty, std::vector<SpellEffectEntry> const& effects)
|
|
: Id(spellName->ID), Difficulty(difficulty)
|
|
{
|
|
SpellName = &spellName->Name;
|
|
|
|
_effects.reserve(32);
|
|
for (SpellEffectEntry const& spellEffect : effects)
|
|
Trinity::Containers::EnsureWritableVectorIndex(_effects, spellEffect.EffectIndex, SpellEffectInfo(this)) = SpellEffectInfo(this, spellEffect);
|
|
|
|
// Correct EffectIndex for blank effects
|
|
for (size_t i = 0; i < _effects.size(); ++i)
|
|
_effects[i].EffectIndex = SpellEffIndex(i);
|
|
|
|
_effects.shrink_to_fit();
|
|
}
|
|
|
|
SpellInfo::~SpellInfo()
|
|
{
|
|
_UnloadImplicitTargetConditionLists();
|
|
}
|
|
|
|
uint32 SpellInfo::GetCategory() const
|
|
{
|
|
return CategoryId;
|
|
}
|
|
|
|
bool SpellInfo::HasEffect(SpellEffectName effect) const
|
|
{
|
|
for (SpellEffectInfo const& eff : GetEffects())
|
|
if (eff.IsEffect(effect))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::HasAura(AuraType aura) const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (effect.IsAura(aura))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::HasAreaAuraEffect() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (effect.IsAreaAuraEffect())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::HasOnlyDamageEffects() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (effect.IsEffect())
|
|
{
|
|
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::HasTargetType(::Targets target) const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (effect.TargetA.GetTarget() == target || effect.TargetB.GetTarget() == target)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::CanBeInterrupted(WorldObject const* interruptCaster, Unit const* interruptTarget, bool ignoreImmunity /*= false*/) const
|
|
{
|
|
return HasAttribute(SPELL_ATTR7_NO_UI_NOT_INTERRUPTIBLE)
|
|
|| HasChannelInterruptFlag(SpellAuraInterruptFlags::Damage | SpellAuraInterruptFlags::EnteringCombat)
|
|
|| (interruptTarget->IsPlayer() && InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly))
|
|
|| InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels)
|
|
|| (interruptCaster && interruptCaster->IsUnit() && interruptCaster->ToUnit()->HasAuraTypeWithMiscvalue(SPELL_AURA_ALLOW_INTERRUPT_SPELL, Id))
|
|
|| ((!(interruptTarget->GetMechanicImmunityMask() & (1 << MECHANIC_INTERRUPT)) || ignoreImmunity)
|
|
&& !interruptTarget->HasAuraTypeWithAffectMask(SPELL_AURA_PREVENT_INTERRUPT, this)
|
|
&& PreventionType & SPELL_PREVENTION_TYPE_SILENCE);
|
|
}
|
|
|
|
bool SpellInfo::HasAnyAuraInterruptFlag() const
|
|
{
|
|
return AuraInterruptFlags != SpellAuraInterruptFlags::None || AuraInterruptFlags2 != SpellAuraInterruptFlags2::None;
|
|
}
|
|
|
|
bool SpellInfo::IsExplicitDiscovery() const
|
|
{
|
|
if (GetEffects().size() < 2)
|
|
return false;
|
|
|
|
return ((GetEffect(EFFECT_0).Effect == SPELL_EFFECT_CREATE_RANDOM_ITEM
|
|
|| GetEffect(EFFECT_0).Effect == SPELL_EFFECT_CREATE_LOOT)
|
|
&& GetEffect(EFFECT_1).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::IsProfession() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (effect.Effect == SPELL_EFFECT_SKILL)
|
|
{
|
|
uint32 skill = effect.MiscValue;
|
|
|
|
if (IsProfessionSkill(skill))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::IsPrimaryProfession() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (effect.Effect == SPELL_EFFECT_SKILL)
|
|
{
|
|
uint32 skill = effect.MiscValue;
|
|
|
|
if (IsPrimaryProfessionSkill(skill))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::IsPrimaryProfessionFirstRank() const
|
|
{
|
|
return IsPrimaryProfession() && 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() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (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() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (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) const
|
|
{
|
|
if (NeedsExplicitUnitTarget())
|
|
return true;
|
|
|
|
/*
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (effect.IsEffect())
|
|
{
|
|
if (effect.TargetA.GetSelectionCategory() == TARGET_SELECT_CATEGORY_CHANNEL
|
|
|| effect.TargetB.GetSelectionCategory() == TARGET_SELECT_CATEGORY_CHANNEL)
|
|
return true;
|
|
}
|
|
}
|
|
*/
|
|
|
|
if (triggeringSpell->IsChanneled())
|
|
{
|
|
uint32 mask = 0;
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
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_NO_AUTOCAST_AI))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool SpellInfo::IsStackableWithRanks() const
|
|
{
|
|
if (IsPassive())
|
|
return false;
|
|
|
|
// All stance spells. if any better way, change it.
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
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() const
|
|
{
|
|
return IsPassive() && !HasEffect(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_DOT_STACKING_RULE);
|
|
}
|
|
|
|
bool SpellInfo::IsCooldownStartedOnEvent() const
|
|
{
|
|
if (HasAttribute(SPELL_ATTR0_COOLDOWN_ON_EVENT))
|
|
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_ALLOW_AURA_WHILE_DEAD);
|
|
}
|
|
|
|
bool SpellInfo::IsRequiringDeadTarget() const
|
|
{
|
|
return HasAttribute(SPELL_ATTR3_ONLY_ON_GHOSTS);
|
|
}
|
|
|
|
bool SpellInfo::IsAllowingDeadTarget() const
|
|
{
|
|
if (HasAttribute(SPELL_ATTR2_ALLOW_DEAD_TARGET) || Targets & (TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_DEAD))
|
|
return true;
|
|
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (!effect.IsEffect())
|
|
continue;
|
|
|
|
if (effect.TargetA.GetObjectType() == TARGET_OBJECT_TYPE_CORPSE || effect.TargetB.GetObjectType() == TARGET_OBJECT_TYPE_CORPSE)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::IsGroupBuff() const
|
|
{
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
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(Unit const* caster) const
|
|
{
|
|
return !HasAttribute(SPELL_ATTR0_NOT_IN_COMBAT_ONLY_PEACEFUL)
|
|
|| (caster->HasAuraType(SPELL_AURA_ALLOW_MOUNT_IN_COMBAT) && HasAura(SPELL_AURA_MOUNTED));
|
|
}
|
|
|
|
bool SpellInfo::IsPositive() const
|
|
{
|
|
return NegativeEffects.none();
|
|
}
|
|
|
|
bool SpellInfo::IsPositiveEffect(uint8 effIndex) const
|
|
{
|
|
return !NegativeEffects.test(effIndex);
|
|
}
|
|
|
|
bool SpellInfo::IsChanneled() const
|
|
{
|
|
return HasAttribute(SpellAttr1(SPELL_ATTR1_IS_CHANNELLED | SPELL_ATTR1_IS_SELF_CHANNELLED));
|
|
}
|
|
|
|
bool SpellInfo::IsMoveAllowedChannel() const
|
|
{
|
|
return IsChanneled() && !ChannelInterruptFlags.HasFlag(SpellAuraInterruptFlags::Moving | SpellAuraInterruptFlags::Turning);
|
|
}
|
|
|
|
bool SpellInfo::IsNextMeleeSwingSpell() const
|
|
{
|
|
return HasAttribute(SpellAttr0(SPELL_ATTR0_ON_NEXT_SWING_NO_DAMAGE | SPELL_ATTR0_ON_NEXT_SWING));
|
|
}
|
|
|
|
bool SpellInfo::IsRangedWeaponSpell() const
|
|
{
|
|
return (SpellFamilyName == SPELLFAMILY_HUNTER && !(SpellFamilyFlags[1] & 0x10000000)) // for 53352, cannot find better way
|
|
|| (EquippedItemSubClassMask & ITEM_SUBCLASS_MASK_WEAPON_RANGED)
|
|
|| (Attributes & SPELL_ATTR0_USES_RANGED_SLOT);
|
|
}
|
|
|
|
bool SpellInfo::IsAutoRepeatRangedSpell() const
|
|
{
|
|
return HasAttribute(SPELL_ATTR2_AUTO_REPEAT);
|
|
}
|
|
|
|
bool SpellInfo::HasInitialAggro() const
|
|
{
|
|
return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR2_NO_INITIAL_THREAT) || HasAttribute(SPELL_ATTR4_NO_HARMFUL_THREAT));
|
|
}
|
|
|
|
bool SpellInfo::HasHitDelay() const
|
|
{
|
|
return Speed > 0.0f || LaunchDelay > 0.0f;
|
|
}
|
|
|
|
WeaponAttackType SpellInfo::GetAttackType() const
|
|
{
|
|
WeaponAttackType result;
|
|
switch (DmgClass)
|
|
{
|
|
case SPELL_DAMAGE_CLASS_MELEE:
|
|
if (HasAttribute(SPELL_ATTR3_REQUIRES_OFF_HAND_WEAPON))
|
|
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_IGNORE_CASTER_MODIFIERS);
|
|
}
|
|
|
|
bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const
|
|
{
|
|
if (!IsAffectedBySpellMods())
|
|
return false;
|
|
|
|
SpellInfo const* affectSpell = sSpellMgr->GetSpellInfo(mod->spellId, Difficulty);
|
|
if (!affectSpell)
|
|
return false;
|
|
|
|
switch (mod->type)
|
|
{
|
|
case SPELLMOD_FLAT:
|
|
case SPELLMOD_PCT:
|
|
// TEMP: dont use IsAffected - !familyName and !familyFlags are not valid options for spell mods
|
|
// TODO: investigate if the !familyName and !familyFlags conditions are even valid for all other (nonmod) uses of SpellInfo::IsAffected
|
|
return affectSpell->SpellFamilyName == SpellFamilyName && static_cast<SpellModifierByClassMask const*>(mod)->mask & SpellFamilyFlags;
|
|
case SPELLMOD_LABEL_FLAT:
|
|
return HasLabel(static_cast<SpellFlatModifierByLabel const*>(mod)->value.LabelID);
|
|
case SPELLMOD_LABEL_PCT:
|
|
return HasLabel(static_cast<SpellPctModifierByLabel const*>(mod)->value.LabelID);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const
|
|
{
|
|
// aura can't be pierced
|
|
if (!auraSpellInfo || auraSpellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
|
|
return false;
|
|
|
|
// these spells pierce all available spells (Resurrection Sickness for example)
|
|
if (HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
|
|
return true;
|
|
|
|
// these spells (Cyclone for example) can pierce all...
|
|
if (HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
|
{
|
|
// ...but not these (Divine shield, Ice block, Cyclone and Banish for example)
|
|
if (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_IMMUNITY_PURGES_EFFECT) && 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_NO_IMMUNITIES))
|
|
return false;
|
|
|
|
// These spells (like Mass Dispel) can dispel all auras
|
|
if (HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
|
|
return true;
|
|
|
|
// These auras (Cyclone for example) are not dispelable
|
|
if ((auraSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && auraSpellInfo->Mechanic != MECHANIC_NONE)
|
|
|| auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SpellInfo::IsSingleTarget() const
|
|
{
|
|
// all other single target spells have if it has AttributesEx5
|
|
if (HasAttribute(SPELL_ATTR5_LIMIT_N))
|
|
return true;
|
|
|
|
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_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 && HasEffect(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 = nullptr;
|
|
if (form > 0)
|
|
{
|
|
shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form);
|
|
if (!shapeInfo)
|
|
{
|
|
TC_LOG_ERROR("spells", "GetErrorAtShapeshiftedCast: unknown shapeshift {}", form);
|
|
return SPELL_CAST_OK;
|
|
}
|
|
actAsShifted = !shapeInfo->GetFlags().HasFlag(SpellShapeshiftFormFlags::Stance);
|
|
}
|
|
|
|
if (actAsShifted)
|
|
{
|
|
if (HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFTED) || (shapeInfo && shapeInfo->GetFlags().HasFlag(SpellShapeshiftFormFlags::CanOnlyCastShapeshiftSpells))) // 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_ALLOW_WHILE_NOT_SHAPESHIFTED_CASTER_FORM) && 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<uint32> areaGroupMembers = sDB2Manager.GetAreasForGroup(RequiredAreasID);
|
|
for (uint32 areaId : areaGroupMembers)
|
|
{
|
|
if (DB2Manager::IsInArea(area_id, areaId))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return SPELL_FAILED_INCORRECT_AREA;
|
|
}
|
|
|
|
// continent limitation (virtual continent)
|
|
if (HasAttribute(SPELL_ATTR4_ONLY_FLYING_AREAS))
|
|
{
|
|
EnumFlag<AreaMountFlags> mountFlags = AreaMountFlags::None;
|
|
if (player && player->HasAuraType(SPELL_AURA_MOUNT_RESTRICTIONS))
|
|
{
|
|
for (AuraEffect const* auraEffect : player->GetAuraEffectsByType(SPELL_AURA_MOUNT_RESTRICTIONS))
|
|
mountFlags |= AreaMountFlags(auraEffect->GetMiscValue());
|
|
}
|
|
else if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(area_id))
|
|
mountFlags = areaTable->GetMountFlags();
|
|
|
|
if (!(mountFlags.HasFlag(AreaMountFlags::AllowFlyingMounts)))
|
|
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_INSTANCES))
|
|
{
|
|
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
|
|
if (!mapEntry || mapEntry->IsRaid())
|
|
return SPELL_FAILED_NOT_IN_RAID_INSTANCE;
|
|
}
|
|
|
|
if (HasAttribute(SPELL_ATTR8_REMOVE_OUTSIDE_DUNGEONS_AND_RAIDS))
|
|
{
|
|
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
|
|
if (!mapEntry || !mapEntry->IsDungeon())
|
|
return SPELL_FAILED_TARGET_NOT_IN_INSTANCE;
|
|
}
|
|
|
|
if (HasAttribute(SPELL_ATTR8_NOT_IN_BATTLEGROUND))
|
|
{
|
|
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
|
|
if (!mapEntry || mapEntry->IsBattleground())
|
|
return SPELL_FAILED_NOT_IN_BATTLEGROUND;
|
|
}
|
|
|
|
// 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 2584: // Waiting to Resurrect
|
|
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 == AREA_WINTERGRASP || (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 : GetEffects())
|
|
{
|
|
if (!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;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SPELL_CAST_OK;
|
|
}
|
|
|
|
SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject const* target, bool implicit /*= true*/) const
|
|
{
|
|
if (HasAttribute(SPELL_ATTR1_EXCLUDE_CASTER) && caster == target)
|
|
return SPELL_FAILED_BAD_TARGETS;
|
|
|
|
// check visibility - ignore invisibility/stealth for implicit (area) targets
|
|
if (!HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT) && !caster->CanSeeOrDetect(target, implicit))
|
|
return SPELL_FAILED_BAD_TARGETS;
|
|
|
|
Unit const* unitTarget = target->ToUnit();
|
|
|
|
if (HasAttribute(SPELL_ATTR8_ONLY_TARGET_IF_SAME_CREATOR))
|
|
{
|
|
auto getCreatorOrSelf = [](WorldObject const* obj)
|
|
{
|
|
ObjectGuid creator = obj->GetCreatorGUID();
|
|
if (creator.IsEmpty())
|
|
creator = obj->GetGUID();
|
|
|
|
return creator;
|
|
};
|
|
if (getCreatorOrSelf(caster) != getCreatorOrSelf(target))
|
|
return SPELL_FAILED_BAD_TARGETS;
|
|
}
|
|
|
|
// creature/player specific target checks
|
|
if (unitTarget)
|
|
{
|
|
// spells cannot be cast if target has a pet in combat either
|
|
if (HasAttribute(SPELL_ATTR1_ONLY_PEACEFUL_TARGETS) && (unitTarget->IsInCombat() || unitTarget->HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT)))
|
|
return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
|
|
|
|
// only spells with SPELL_ATTR3_ONLY_TARGET_GHOSTS can target ghosts
|
|
if (HasAttribute(SPELL_ATTR3_ONLY_ON_GHOSTS) != unitTarget->HasAuraType(SPELL_AURA_GHOST))
|
|
{
|
|
if (HasAttribute(SPELL_ATTR3_ONLY_ON_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_CANNOT_CAST_ON_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))
|
|
{
|
|
Creature const* targetCreature = unitTarget->ToCreature();
|
|
if (!targetCreature)
|
|
return SPELL_FAILED_BAD_TARGETS;
|
|
|
|
if (!targetCreature->CanHaveLoot() || !LootTemplates_Pickpocketing.HaveLootFor(targetCreature->GetCreatureDifficulty()->PickPocketLootID))
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HasAttribute(SPELL_ATTR8_ONLY_TARGET_OWN_SUMMONS))
|
|
if (!unitTarget->IsSummon() || unitTarget->ToTempSummon()->GetSummonerGUID() != caster->GetGUID())
|
|
return SPELL_FAILED_BAD_TARGETS;
|
|
}
|
|
// 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 (!unitTarget->IsPlayer())
|
|
{
|
|
if (HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER))
|
|
return SPELL_FAILED_TARGET_NOT_PLAYER;
|
|
|
|
if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && unitTarget->IsControlledByPlayer())
|
|
return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED;
|
|
}
|
|
else if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER))
|
|
return SPELL_FAILED_TARGET_IS_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_DO_NOT_CHAIN_TO_CROWD_CONTROLLED_TARGETS) && !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_UNINTERACTIBLE))
|
|
// return SPELL_FAILED_BAD_TARGETS;
|
|
|
|
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->GetAffectingPlayer() || !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 (Unit const* unitCaster = caster->ToUnit())
|
|
{
|
|
if (!unitCaster->IsVehicle() && !(unitCaster->GetCharmerOrOwner() == target))
|
|
{
|
|
if (TargetAuraState && !unitTarget->HasAuraState(AuraStateType(TargetAuraState), this, unitCaster))
|
|
return SPELL_FAILED_TARGET_AURASTATE;
|
|
|
|
if (ExcludeTargetAuraState && unitTarget->HasAuraState(AuraStateType(ExcludeTargetAuraState), this, unitCaster))
|
|
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) && !HasAttribute(SPELL_ATTR7_BYPASS_NO_RESURRECT_AURA))
|
|
if (HasEffect(SPELL_EFFECT_SELF_RESURRECT) || HasEffect(SPELL_EFFECT_RESURRECT))
|
|
return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED;
|
|
|
|
if (HasAttribute(SPELL_ATTR8_ENFORCE_IN_COMBAT_RESSURECTION_LIMIT))
|
|
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(WorldObject const* caster, WorldObject const* target, Item const* itemTarget /*= nullptr*/) 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))
|
|
{
|
|
Unit const* unitCaster = caster->ToUnit();
|
|
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) && unitCaster && unitCaster->IsInPartyWith(unitTarget))
|
|
|| ((neededTargets & TARGET_FLAG_UNIT_RAID) && unitCaster && unitCaster->IsInRaidWith(unitTarget)))
|
|
if (caster->IsValidAssistTarget(unitTarget, this))
|
|
return SPELL_CAST_OK;
|
|
if ((neededTargets & TARGET_FLAG_UNIT_MINIPET) && unitCaster)
|
|
if (unitTarget->GetGUID() == unitCaster->GetCritterGUID())
|
|
return SPELL_CAST_OK;
|
|
if ((neededTargets & TARGET_FLAG_UNIT_PASSENGER) && unitCaster)
|
|
if (unitTarget->IsOnVehicle(unitCaster))
|
|
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 : GetEffects())
|
|
{
|
|
if (effect.IsAura(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(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_ALLOW_WHILE_RIDING_VEHICLE) && !HasAttribute(SPELL_ATTR0_ALLOW_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 : GetEffects())
|
|
{
|
|
if (!effect.IsEffect(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) || target->HasAuraType(SPELL_AURA_IGNORE_SPELL_CREATURE_TYPE_REQUIREMENTS);
|
|
}
|
|
|
|
SpellSchoolMask SpellInfo::GetSchoolMask() const
|
|
{
|
|
return SpellSchoolMask(SchoolMask);
|
|
}
|
|
|
|
uint64 SpellInfo::GetAllEffectsMechanicMask() const
|
|
{
|
|
uint64 mask = 0;
|
|
if (Mechanic)
|
|
mask |= UI64LIT(1) << Mechanic;
|
|
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (effect.IsEffect() && effect.Mechanic)
|
|
mask |= UI64LIT(1) << effect.Mechanic;
|
|
|
|
return mask;
|
|
}
|
|
|
|
uint64 SpellInfo::GetEffectMechanicMask(SpellEffIndex effIndex) const
|
|
{
|
|
uint64 mask = 0;
|
|
if (Mechanic)
|
|
mask |= UI64LIT(1) << Mechanic;
|
|
|
|
if (GetEffect(effIndex).IsEffect() && GetEffect(effIndex).Mechanic)
|
|
mask |= UI64LIT(1) << GetEffect(effIndex).Mechanic;
|
|
|
|
return mask;
|
|
}
|
|
|
|
uint64 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const
|
|
{
|
|
uint64 mask = 0;
|
|
if (Mechanic)
|
|
mask |= UI64LIT(1) << Mechanic;
|
|
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if ((effectMask & (1 << effect.EffectIndex)) && effect.Mechanic)
|
|
mask |= UI64LIT(1) << effect.Mechanic;
|
|
|
|
return mask;
|
|
}
|
|
|
|
Mechanics SpellInfo::GetEffectMechanic(SpellEffIndex effIndex) const
|
|
{
|
|
if (GetEffect(effIndex).IsEffect() && GetEffect(effIndex).Mechanic)
|
|
return GetEffect(effIndex).Mechanic;
|
|
|
|
if (Mechanic)
|
|
return Mechanics(Mechanic);
|
|
|
|
return MECHANIC_NONE;
|
|
}
|
|
|
|
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
|
|
{
|
|
// Faerie Fire (Feral)
|
|
if (GetCategory() == 1133)
|
|
return AURA_STATE_FAERIE_FIRE;
|
|
|
|
// Swiftmend state on Regrowth, Rejuvenation, Wild Growth
|
|
if (SpellFamilyName == SPELLFAMILY_DRUID && (SpellFamilyFlags[0] & 0x50 || SpellFamilyFlags[1] & 0x4000000))
|
|
return AURA_STATE_DRUID_PERIODIC_HEAL;
|
|
|
|
// Deadly poison aura state
|
|
if (SpellFamilyName == SPELLFAMILY_ROGUE && SpellFamilyFlags[0] & 0x10000)
|
|
return AURA_STATE_ROGUE_POISONED;
|
|
|
|
// Enrage aura state
|
|
if (Dispel == DISPEL_ENRAGE)
|
|
return AURA_STATE_ENRAGED;
|
|
|
|
// Bleeding aura state
|
|
if (GetAllEffectsMechanicMask() & (1 << MECHANIC_BLEED))
|
|
return AURA_STATE_BLEED;
|
|
|
|
if (GetSchoolMask() & SPELL_SCHOOL_MASK_FROST)
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (effect.IsAura(SPELL_AURA_MOD_STUN) || effect.IsAura(SPELL_AURA_MOD_ROOT) || effect.IsAura(SPELL_AURA_MOD_ROOT_2))
|
|
return AURA_STATE_FROZEN;
|
|
|
|
switch (Id)
|
|
{
|
|
case 1064: // Dazed
|
|
return AURA_STATE_DAZED;
|
|
case 32216: // Victorious
|
|
return AURA_STATE_VICTORIOUS;
|
|
case 71465: // Divine Surge
|
|
case 50241: // Evasive Charges
|
|
case 81262: // Efflorescence
|
|
return AURA_STATE_RAID_ENCOUNTER;
|
|
case 6950: // Faerie Fire
|
|
case 9806: // Phantom Strike
|
|
case 9991: // Touch of Zanzil
|
|
case 13424: // Faerie Fire
|
|
case 13752: // Faerie Fire
|
|
case 16432: // Plague Mist
|
|
case 20656: // Faerie Fire
|
|
case 25602: // Faerie Fire
|
|
case 32129: // Faerie Fire
|
|
case 35325: // Glowing Blood
|
|
case 35328: // Lambent Blood
|
|
case 35329: // Vibrant Blood
|
|
case 35331: // Black Blood
|
|
case 49163: // Perpetual Instability
|
|
case 65863: // Faerie Fire
|
|
case 79559: // Luxscale Light
|
|
case 82855: // Dazzling
|
|
case 102953: // In the Rumpus
|
|
case 127907: // Phosphorescence
|
|
case 127913: // Phosphorescence
|
|
case 129007: // Zijin Sting
|
|
case 130159: // Fae Touch
|
|
case 142537: // Spotter Smoke
|
|
case 168455: // Spotted!
|
|
case 176905: // Super Sticky Glitter Bomb
|
|
case 189502: // Marked
|
|
case 201785: // Intruder Alert!
|
|
case 201786: // Intruder Alert!
|
|
case 201935: // Spotted!
|
|
case 239233: // Smoke Bomb
|
|
case 319400: // Glitter Burst
|
|
case 321470: // Dimensional Shifter Mishap
|
|
case 331134: // Spotted
|
|
return AURA_STATE_FAERIE_FIRE;
|
|
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(SpellAuraInterruptFlags::Standing))
|
|
{
|
|
bool food = false;
|
|
bool drink = false;
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (!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;
|
|
|
|
if ((SpellFamilyFlags[0] & 0x1000000) && GetEffect(EFFECT_0).IsAura(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;
|
|
|
|
// only paladin auras have this (for palaldin class family)
|
|
switch (Id)
|
|
{
|
|
case 465: // Devotion Aura
|
|
case 32223: // Crusader Aura
|
|
case 183435: // Retribution Aura
|
|
case 317920: // Concentration Aura
|
|
return SPELL_SPECIFIC_AURA;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
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 (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (effect.IsEffect(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;
|
|
[[fallthrough]];
|
|
case SPELL_AURA_TRACK_RESOURCES:
|
|
case SPELL_AURA_TRACK_STEALTHED:
|
|
return SPELL_SPECIFIC_TRACKER;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return SPELL_SPECIFIC_NORMAL;
|
|
}();
|
|
}
|
|
|
|
void SpellInfo::_LoadSpellDiminishInfo()
|
|
{
|
|
auto diminishingGroupCompute = [this]() -> DiminishingGroup
|
|
{
|
|
if (IsPositive())
|
|
return DIMINISHING_NONE;
|
|
|
|
if (HasAura(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:
|
|
// Frost Tomb
|
|
if (Id == 48400)
|
|
return DIMINISHING_NONE;
|
|
// Gnaw
|
|
else if (Id == 47481)
|
|
return DIMINISHING_STUN;
|
|
// ToC Icehowl Arctic Breath
|
|
else if (Id == 66689)
|
|
return DIMINISHING_NONE;
|
|
// Black Plague
|
|
else if (Id == 64155)
|
|
return DIMINISHING_NONE;
|
|
// Screams of the Dead (King Ymiron)
|
|
else if (Id == 51750)
|
|
return DIMINISHING_NONE;
|
|
// Crystallize (Keristrasza heroic)
|
|
else if (Id == 48179)
|
|
return DIMINISHING_NONE;
|
|
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()
|
|
{
|
|
std::unique_ptr<SpellEffectInfo::ImmunityInfo> workBuffer = std::make_unique<SpellEffectInfo::ImmunityInfo>();
|
|
|
|
for (SpellEffectInfo& effect : _effects)
|
|
{
|
|
uint32 schoolImmunityMask = 0;
|
|
uint32 applyHarmfulAuraImmunityMask = 0;
|
|
uint64 mechanicImmunityMask = 0;
|
|
uint32 dispelImmunity = 0;
|
|
uint32 damageImmunityMask = 0;
|
|
|
|
int32 miscVal = effect.MiscValue;
|
|
int32 amount = effect.CalcValue();
|
|
|
|
SpellEffectInfo::ImmunityInfo& immuneInfo = *workBuffer;
|
|
|
|
switch (effect.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);
|
|
[[fallthrough]];
|
|
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 42292: // PvP trinket
|
|
case 59752: // Every Man for Himself
|
|
mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
|
|
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED);
|
|
break;
|
|
case 34471: // The Beast Within
|
|
case 19574: // Bestial Wrath
|
|
case 46227: // Medallion of Immunity
|
|
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)
|
|
break;
|
|
|
|
mechanicImmunityMask |= UI64LIT(1) << miscVal;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_AURA_EFFECT_IMMUNITY:
|
|
{
|
|
immuneInfo.SpellEffectImmune.insert(static_cast<SpellEffectName>(miscVal));
|
|
break;
|
|
}
|
|
case SPELL_AURA_STATE_IMMUNITY:
|
|
{
|
|
immuneInfo.AuraTypeImmune.insert(static_cast<AuraType>(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();
|
|
|
|
if (immuneInfo.SchoolImmuneMask
|
|
|| immuneInfo.ApplyHarmfulAuraImmuneMask
|
|
|| immuneInfo.MechanicImmuneMask
|
|
|| immuneInfo.DispelImmune
|
|
|| immuneInfo.DamageSchoolMask
|
|
|| !immuneInfo.AuraTypeImmune.empty()
|
|
|| !immuneInfo.SpellEffectImmune.empty())
|
|
{
|
|
effect._immunityInfo = workBuffer.release();
|
|
workBuffer = std::make_unique<SpellEffectInfo::ImmunityInfo>();
|
|
}
|
|
|
|
_allowedMechanicMask |= immuneInfo.MechanicImmuneMask;
|
|
}
|
|
|
|
if (HasAttribute(SPELL_ATTR5_ALLOW_WHILE_STUNNED))
|
|
{
|
|
switch (Id)
|
|
{
|
|
case 22812: // Barkskin
|
|
case 47585: // Dispersion
|
|
_allowedMechanicMask |=
|
|
(1 << MECHANIC_STUN) |
|
|
(1 << MECHANIC_FREEZE) |
|
|
(1 << MECHANIC_KNOCKOUT) |
|
|
(1 << MECHANIC_SLEEP);
|
|
break;
|
|
case 49039: // Lichborne, don't allow normal stuns
|
|
break;
|
|
default:
|
|
_allowedMechanicMask |= (1 << MECHANIC_STUN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (HasAttribute(SPELL_ATTR5_ALLOW_WHILE_CONFUSED))
|
|
_allowedMechanicMask |= (1 << MECHANIC_DISORIENTED);
|
|
|
|
if (HasAttribute(SPELL_ATTR5_ALLOW_WHILE_FLEEING))
|
|
{
|
|
switch (Id)
|
|
{
|
|
case 22812: // Barkskin
|
|
case 47585: // Dispersion
|
|
_allowedMechanicMask |= (1 << MECHANIC_FEAR) | (1 << MECHANIC_HORROR);
|
|
break;
|
|
default:
|
|
_allowedMechanicMask |= (1 << MECHANIC_FEAR);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SpellInfo::_LoadSqrtTargetLimit(int32 maxTargets, int32 numNonDiminishedTargets, Optional<SpellEffIndex> maxTargetsEffectValueHolder,
|
|
Optional<SpellEffIndex> numNonDiminishedTargetsEffectValueHolder)
|
|
{
|
|
SqrtDamageAndHealingDiminishing.MaxTargets = maxTargets;
|
|
SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets = numNonDiminishedTargets;
|
|
|
|
if (maxTargetsEffectValueHolder)
|
|
{
|
|
if (maxTargetsEffectValueHolder < GetEffects().size())
|
|
{
|
|
SpellEffectInfo const& valueHolder = GetEffect(*maxTargetsEffectValueHolder);
|
|
int32 expectedValue = valueHolder.CalcBaseValue(nullptr, nullptr, 0, -1);
|
|
if (maxTargets != expectedValue)
|
|
TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} has different value in effect {} than expected, recheck target caps (expected {}, got {})",
|
|
Id, AsUnderlyingType(*maxTargetsEffectValueHolder), maxTargets, expectedValue);
|
|
}
|
|
else
|
|
TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} does not have effect {}", Id, AsUnderlyingType(*maxTargetsEffectValueHolder));
|
|
}
|
|
|
|
if (numNonDiminishedTargetsEffectValueHolder)
|
|
{
|
|
if (numNonDiminishedTargetsEffectValueHolder < GetEffects().size())
|
|
{
|
|
SpellEffectInfo const& valueHolder = GetEffect(*numNonDiminishedTargetsEffectValueHolder);
|
|
int32 expectedValue = valueHolder.CalcBaseValue(nullptr, nullptr, 0, -1);
|
|
if (numNonDiminishedTargets != expectedValue)
|
|
TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} has different value in effect {} than expected, recheck target caps (expected {}, got {})",
|
|
Id, AsUnderlyingType(*numNonDiminishedTargetsEffectValueHolder), numNonDiminishedTargets, expectedValue);
|
|
}
|
|
else
|
|
TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} does not have effect {}", Id, AsUnderlyingType(*numNonDiminishedTargetsEffectValueHolder));
|
|
}
|
|
}
|
|
|
|
void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const& spellEffectInfo, bool apply) const
|
|
{
|
|
SpellEffectInfo::ImmunityInfo const* immuneInfo = spellEffectInfo.GetImmunityInfo();
|
|
if (!immuneInfo)
|
|
return;
|
|
|
|
if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask)
|
|
{
|
|
target->ApplySpellImmune(Id, IMMUNITY_SCHOOL, schoolImmunity, apply);
|
|
|
|
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
|
{
|
|
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 (apply && schoolImmunity & SPELL_SCHOOL_MASK_NORMAL)
|
|
target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::InvulnerabilityBuff);
|
|
}
|
|
|
|
if (uint64 mechanicImmunity = immuneInfo->MechanicImmuneMask)
|
|
{
|
|
for (uint32 i = 0; i < MAX_MECHANIC; ++i)
|
|
if (mechanicImmunity & (UI64LIT(1) << i))
|
|
target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply);
|
|
|
|
if (HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
|
{
|
|
if (apply)
|
|
target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id);
|
|
else
|
|
{
|
|
std::vector<Aura*> aurasToUpdateTargets;
|
|
target->RemoveAppliedAuras([mechanicImmunity, &aurasToUpdateTargets](AuraApplication const* aurApp)
|
|
{
|
|
Aura* aura = aurApp->GetBase();
|
|
if (aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanicImmunity)
|
|
aurasToUpdateTargets.push_back(aura);
|
|
|
|
// only update targets, don't remove anything
|
|
return false;
|
|
});
|
|
|
|
for (Aura* aura : aurasToUpdateTargets)
|
|
aura->UpdateTargetMap(aura->GetCaster());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uint32 dispelImmunity = immuneInfo->DispelImmune)
|
|
{
|
|
target->ApplySpellImmune(Id, IMMUNITY_DISPEL, dispelImmunity, apply);
|
|
|
|
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
|
{
|
|
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);
|
|
|
|
if (apply && damageImmunity & SPELL_SCHOOL_MASK_NORMAL)
|
|
target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::InvulnerabilityBuff);
|
|
}
|
|
|
|
for (AuraType auraType : immuneInfo->AuraTypeImmune)
|
|
{
|
|
target->ApplySpellImmune(Id, IMMUNITY_STATE, auraType, apply);
|
|
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
|
target->RemoveAurasByType(auraType, [](AuraApplication const* aurApp) -> bool
|
|
{
|
|
// if the aura has SPELL_ATTR0_NO_IMMUNITIES, then it cannot be removed by immunity
|
|
return !aurApp->GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES);
|
|
});
|
|
}
|
|
|
|
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 : _effects)
|
|
{
|
|
if (!effectInfo.IsEffect())
|
|
continue;
|
|
|
|
SpellEffectInfo::ImmunityInfo const* immuneInfo = effectInfo.GetImmunityInfo();
|
|
if (!immuneInfo)
|
|
continue;
|
|
|
|
if (!auraSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
|
{
|
|
if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask)
|
|
if ((auraSpellInfo->SchoolMask & schoolImmunity) != 0)
|
|
return true;
|
|
}
|
|
|
|
if (uint64 mechanicImmunity = immuneInfo->MechanicImmuneMask)
|
|
if ((mechanicImmunity & (UI64LIT(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->GetEffects())
|
|
{
|
|
if (!auraSpellEffectInfo.IsEffect())
|
|
continue;
|
|
|
|
auto spellImmuneItr = immuneInfo->SpellEffectImmune.find(auraSpellEffectInfo.Effect);
|
|
if (spellImmuneItr == immuneInfo->SpellEffectImmune.end())
|
|
{
|
|
immuneToAllEffects = false;
|
|
break;
|
|
}
|
|
|
|
if (uint32 mechanic = auraSpellEffectInfo.Mechanic)
|
|
{
|
|
if (!(immuneInfo->MechanicImmuneMask & (UI64LIT(1) << mechanic)))
|
|
{
|
|
immuneToAllEffects = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!auraSpellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
|
|
{
|
|
if (AuraType auraName = auraSpellEffectInfo.ApplyAuraName)
|
|
{
|
|
bool isImmuneToAuraEffectApply = false;
|
|
auto auraImmuneItr = immuneInfo->AuraTypeImmune.find(auraName);
|
|
if (auraImmuneItr != immuneInfo->AuraTypeImmune.end())
|
|
isImmuneToAuraEffectApply = true;
|
|
|
|
if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(auraSpellEffectInfo.EffectIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
|
{
|
|
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_IMMUNITY_PURGES_EFFECT))
|
|
return false;
|
|
|
|
if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
|
|
return false;
|
|
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (!effect.IsEffect(SPELL_EFFECT_APPLY_AURA))
|
|
continue;
|
|
|
|
uint32 const miscValue = static_cast<uint32>(effect.MiscValue);
|
|
switch (effect.ApplyAuraName)
|
|
{
|
|
case SPELL_AURA_STATE_IMMUNITY:
|
|
if (miscValue != aurEff->GetAuraType())
|
|
continue;
|
|
break;
|
|
case SPELL_AURA_SCHOOL_IMMUNITY:
|
|
case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL:
|
|
if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) || !(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;
|
|
}
|
|
|
|
uint64 SpellInfo::GetAllowedMechanicMask() const
|
|
{
|
|
return _allowedMechanicMask;
|
|
}
|
|
|
|
uint64 SpellInfo::GetMechanicImmunityMask(Unit const* caster) const
|
|
{
|
|
uint64 casterMechanicImmunityMask = caster->GetMechanicImmunityMask();
|
|
uint64 mechanicImmunityMask = 0;
|
|
|
|
if (CanBeInterrupted(nullptr, caster, true))
|
|
{
|
|
if (casterMechanicImmunityMask & (1 << MECHANIC_SILENCE))
|
|
mechanicImmunityMask |= (1 << MECHANIC_SILENCE);
|
|
|
|
if (casterMechanicImmunityMask & (1 << MECHANIC_INTERRUPT))
|
|
mechanicImmunityMask |= (1 << MECHANIC_INTERRUPT);
|
|
}
|
|
|
|
return mechanicImmunityMask;
|
|
}
|
|
|
|
float SpellInfo::GetMinRange(bool positive /*= false*/) const
|
|
{
|
|
if (!RangeEntry)
|
|
return 0.0f;
|
|
return RangeEntry->RangeMin[positive ? 1 : 0];
|
|
}
|
|
|
|
float SpellInfo::GetMaxRange(bool positive /*= false*/, WorldObject* caster /*= nullptr*/, Spell* spell /*= nullptr*/) const
|
|
{
|
|
if (!RangeEntry)
|
|
return 0.0f;
|
|
float range = RangeEntry->RangeMax[positive ? 1 : 0];
|
|
if (caster)
|
|
if (Player* modOwner = caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(this, SpellModOp::Range, range, spell);
|
|
|
|
return range;
|
|
}
|
|
|
|
int32 SpellInfo::CalcDuration(WorldObject const* caster /*= nullptr*/) const
|
|
{
|
|
int32 duration = GetDuration();
|
|
|
|
if (caster)
|
|
if (Player* modOwner = caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(this, SpellModOp::Duration, duration);
|
|
|
|
return duration;
|
|
}
|
|
|
|
int32 SpellInfo::GetDuration() const
|
|
{
|
|
if (!DurationEntry)
|
|
return IsPassive() ? -1 : 0;
|
|
return (DurationEntry->Duration == -1) ? -1 : abs(DurationEntry->Duration);
|
|
}
|
|
|
|
int32 SpellInfo::GetMaxDuration() const
|
|
{
|
|
if (!DurationEntry)
|
|
return IsPassive() ? -1 : 0;
|
|
return (DurationEntry->MaxDuration == -1) ? -1 : abs(DurationEntry->MaxDuration);
|
|
}
|
|
|
|
uint32 SpellInfo::CalcCastTime(Spell* spell /*= nullptr*/) const
|
|
{
|
|
int32 castTime = 0;
|
|
if (CastTimeEntry)
|
|
castTime = std::max(CastTimeEntry->Base, CastTimeEntry->Minimum);
|
|
|
|
if (castTime <= 0)
|
|
return 0;
|
|
|
|
if (spell)
|
|
spell->GetCaster()->ModSpellCastTime(this, castTime, spell);
|
|
|
|
if (HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !IsAutoRepeatRangedSpell() && !HasAttribute(SPELL_ATTR9_AIMED_SHOT))
|
|
castTime += 500;
|
|
|
|
return (castTime > 0) ? uint32(castTime) : 0;
|
|
}
|
|
|
|
uint32 SpellInfo::GetMaxTicks() const
|
|
{
|
|
uint32 totalTicks = 0;
|
|
int32 DotDuration = GetDuration();
|
|
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (effect.IsEffect(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_PERIODIC_TRIGGER_SPELL_FROM_CLIENT:
|
|
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:
|
|
// skip infinite periodics
|
|
if (effect.ApplyAuraPeriod > 0 && DotDuration > 0)
|
|
{
|
|
totalTicks = static_cast<uint32>(DotDuration) / effect.ApplyAuraPeriod;
|
|
if (HasAttribute(SPELL_ATTR5_EXTRA_INITIAL_PERIOD))
|
|
++totalTicks;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return totalTicks;
|
|
}
|
|
|
|
uint32 SpellInfo::GetRecoveryTime() const
|
|
{
|
|
return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime;
|
|
}
|
|
|
|
Optional<SpellPowerCost> SpellInfo::CalcPowerCost(Powers powerType, bool optionalCost, WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const
|
|
{
|
|
// gameobject casts don't use power
|
|
Unit const* unitCaster = caster->ToUnit();
|
|
if (!unitCaster)
|
|
return {};
|
|
|
|
auto itr = std::find_if(PowerCosts.cbegin(), PowerCosts.cend(), [powerType](SpellPowerEntry const* spellPowerEntry)
|
|
{
|
|
return spellPowerEntry && spellPowerEntry->PowerType == powerType;
|
|
});
|
|
if (itr == PowerCosts.cend())
|
|
return {};
|
|
|
|
return CalcPowerCost(*itr, optionalCost, caster, schoolMask, spell);
|
|
}
|
|
|
|
Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const
|
|
{
|
|
// gameobject casts don't use power
|
|
Unit const* unitCaster = caster->ToUnit();
|
|
if (!unitCaster)
|
|
return {};
|
|
|
|
if (power->RequiredAuraSpellID && !unitCaster->HasAura(power->RequiredAuraSpellID))
|
|
return {};
|
|
|
|
// Spell drain all exist power on cast (Only paladin lay of Hands)
|
|
if (HasAttribute(SPELL_ATTR1_USE_ALL_MANA))
|
|
{
|
|
if (optionalCost)
|
|
return {};
|
|
|
|
// If power type - health drain all
|
|
if (power->PowerType == POWER_HEALTH)
|
|
return SpellPowerCost{ .Power = POWER_HEALTH, .Amount = int32(unitCaster->GetHealth()) };
|
|
|
|
// Else drain all power
|
|
if (power->PowerType < MAX_POWERS)
|
|
return SpellPowerCost{ .Power = Powers(power->PowerType), .Amount = unitCaster->GetPower(Powers(power->PowerType)) };
|
|
|
|
TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '{}' in spell {}", power->PowerType, Id);
|
|
return {};
|
|
}
|
|
|
|
// Base powerCost
|
|
int32 powerCost = 0;
|
|
if (!optionalCost)
|
|
{
|
|
powerCost = power->ManaCost;
|
|
// PCT cost from total amount
|
|
if (power->PowerCostPct)
|
|
{
|
|
switch (power->PowerType)
|
|
{
|
|
// health as power used
|
|
case POWER_HEALTH:
|
|
if (G3D::fuzzyEq(power->PowerCostPct, 0.0f))
|
|
powerCost += int32(CalculatePct(unitCaster->GetMaxHealth(), power->PowerCostMaxPct));
|
|
else
|
|
powerCost += int32(CalculatePct(unitCaster->GetMaxHealth(), power->PowerCostPct));
|
|
break;
|
|
case POWER_MANA:
|
|
powerCost += int32(CalculatePct(unitCaster->GetCreateMana(), power->PowerCostPct));
|
|
break;
|
|
case POWER_ALTERNATE_POWER:
|
|
TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type POWER_ALTERNATE_POWER in spell {}", Id);
|
|
return {};
|
|
default:
|
|
{
|
|
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(Powers(power->PowerType)))
|
|
{
|
|
powerCost += int32(CalculatePct(powerTypeEntry->MaxBasePower, power->PowerCostPct));
|
|
break;
|
|
}
|
|
|
|
TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '{}' in spell {}", power->PowerType, Id);
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
powerCost = int32(power->OptionalCost);
|
|
|
|
if (power->OptionalCostPct)
|
|
{
|
|
switch (power->PowerType)
|
|
{
|
|
// health as power used
|
|
case POWER_HEALTH:
|
|
powerCost += int32(CalculatePct(unitCaster->GetMaxHealth(), power->OptionalCostPct));
|
|
break;
|
|
case POWER_MANA:
|
|
powerCost += int32(CalculatePct(unitCaster->GetCreateMana(), power->OptionalCostPct));
|
|
break;
|
|
case POWER_ALTERNATE_POWER:
|
|
TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unsupported power type POWER_ALTERNATE_POWER in spell {} for optional cost percent", Id);
|
|
return {};
|
|
default:
|
|
{
|
|
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(Powers(power->PowerType)))
|
|
{
|
|
powerCost += int32(CalculatePct(powerTypeEntry->MaxBasePower, power->OptionalCostPct));
|
|
break;
|
|
}
|
|
|
|
TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '{}' in spell {} for optional cost percent", power->PowerType, Id);
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
|
|
powerCost += unitCaster->GetTotalAuraModifier(SPELL_AURA_MOD_ADDITIONAL_POWER_COST, [this, power](AuraEffect const* aurEff) -> bool
|
|
{
|
|
return aurEff->GetMiscValue() == power->PowerType
|
|
&& aurEff->IsAffectingSpell(this);
|
|
});
|
|
}
|
|
|
|
bool initiallyNegative = powerCost < 0;
|
|
|
|
// Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
|
|
if (HasAttribute(SPELL_ATTR4_WEAPON_SPEED_COST_SCALING))
|
|
{
|
|
uint32 speed = 0;
|
|
if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(unitCaster->GetShapeshiftForm()))
|
|
speed = ss->CombatRoundTime;
|
|
else
|
|
{
|
|
WeaponAttackType slot = BASE_ATTACK;
|
|
if (!HasAttribute(SPELL_ATTR3_REQUIRES_MAIN_HAND_WEAPON) && HasAttribute(SPELL_ATTR3_REQUIRES_OFF_HAND_WEAPON))
|
|
slot = OFF_ATTACK;
|
|
|
|
speed = unitCaster->GetBaseAttackTime(slot);
|
|
}
|
|
|
|
powerCost += speed / 100;
|
|
}
|
|
|
|
if (power->PowerType != POWER_HEALTH)
|
|
{
|
|
if (!optionalCost)
|
|
{
|
|
// Flat mod from caster auras by spell school and power type
|
|
for (AuraEffect const* aura : unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL))
|
|
{
|
|
if (!(aura->GetMiscValue() & schoolMask))
|
|
continue;
|
|
|
|
if (!(aura->GetMiscValueB() & (1 << power->PowerType)))
|
|
continue;
|
|
|
|
powerCost += aura->GetAmount();
|
|
}
|
|
}
|
|
|
|
// PCT mod from user auras by spell school and power type
|
|
for (auto schoolCostPct : unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT))
|
|
{
|
|
if (!(schoolCostPct->GetMiscValue() & schoolMask))
|
|
continue;
|
|
|
|
if (!(schoolCostPct->GetMiscValueB() & (1 << power->PowerType)))
|
|
continue;
|
|
|
|
powerCost += CalculatePct(powerCost, schoolCostPct->GetAmount());
|
|
}
|
|
}
|
|
|
|
// Apply cost mod by spell
|
|
if (Player* modOwner = unitCaster->GetSpellModOwner())
|
|
{
|
|
Optional<SpellModOp> mod;
|
|
switch (power->OrderIndex)
|
|
{
|
|
case 0:
|
|
mod = SpellModOp::PowerCost0;
|
|
break;
|
|
case 1:
|
|
mod = SpellModOp::PowerCost1;
|
|
break;
|
|
case 2:
|
|
mod = SpellModOp::PowerCost2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (mod)
|
|
{
|
|
if (!optionalCost)
|
|
modOwner->ApplySpellMod(this, *mod, powerCost, spell);
|
|
else
|
|
{
|
|
// optional cost ignores flat modifiers
|
|
int32 flatMod = 0;
|
|
float pctMod = 1.0f;
|
|
modOwner->GetSpellModValues(this, *mod, spell, powerCost, &flatMod, &pctMod);
|
|
powerCost = int32(powerCost * pctMod);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!unitCaster->IsControlledByPlayer() && G3D::fuzzyEq(power->PowerCostPct, 0.0f) && SpellLevel && power->PowerType == POWER_MANA)
|
|
{
|
|
if (HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL))
|
|
{
|
|
GtNpcManaCostScalerEntry const* spellScaler = sNpcManaCostScalerGameTable.GetRow(SpellLevel);
|
|
GtNpcManaCostScalerEntry const* casterScaler = sNpcManaCostScalerGameTable.GetRow(unitCaster->GetLevel());
|
|
if (spellScaler && casterScaler)
|
|
powerCost *= casterScaler->Scaler / spellScaler->Scaler;
|
|
}
|
|
}
|
|
|
|
if (power->PowerType == POWER_MANA)
|
|
powerCost = float(powerCost) * (1.0f + unitCaster->m_unitData->ManaCostMultiplier);
|
|
|
|
// power cost cannot become negative if initially positive
|
|
if (initiallyNegative != (powerCost < 0))
|
|
powerCost = 0;
|
|
|
|
return SpellPowerCost{ .Power = Powers(power->PowerType), .Amount = powerCost };
|
|
}
|
|
|
|
std::vector<SpellPowerCost> SpellInfo::CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell) const
|
|
{
|
|
std::vector<SpellPowerCost> costs;
|
|
if (caster->IsUnit())
|
|
{
|
|
costs.reserve(MAX_POWERS_PER_SPELL);
|
|
|
|
auto getOrCreatePowerCost = [&](Powers powerType) -> SpellPowerCost&
|
|
{
|
|
auto itr = std::find_if(costs.begin(), costs.end(), [powerType](SpellPowerCost const& cost)
|
|
{
|
|
return cost.Power == powerType;
|
|
});
|
|
if (itr != costs.end())
|
|
return *itr;
|
|
|
|
return costs.emplace_back<SpellPowerCost>({ .Power = powerType, .Amount = 0 });
|
|
};
|
|
|
|
for (SpellPowerEntry const* power : PowerCosts)
|
|
{
|
|
if (!power)
|
|
continue;
|
|
|
|
if (Optional<SpellPowerCost> cost = CalcPowerCost(power, false, caster, schoolMask, spell))
|
|
getOrCreatePowerCost(cost->Power).Amount += cost->Amount;
|
|
|
|
if (Optional<SpellPowerCost> optionalCost = CalcPowerCost(power, true, caster, schoolMask, spell))
|
|
{
|
|
SpellPowerCost& cost = getOrCreatePowerCost(optionalCost->Power);
|
|
int32 remainingPower = caster->ToUnit()->GetPower(optionalCost->Power) - cost.Amount;
|
|
if (remainingPower > 0)
|
|
cost.Amount += std::min(optionalCost->Amount, remainingPower);
|
|
}
|
|
}
|
|
}
|
|
|
|
return costs;
|
|
}
|
|
|
|
inline float CalcPPMHasteMod(SpellProcsPerMinuteModEntry const* mod, Unit* caster)
|
|
{
|
|
float haste = caster->m_unitData->ModHaste;
|
|
float rangedHaste = caster->m_unitData->ModRangedHaste;
|
|
float spellHaste = caster->m_unitData->ModSpellHaste;
|
|
float regenHaste = caster->m_unitData->ModHasteRegen;
|
|
|
|
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)
|
|
{
|
|
Player const* player = caster->ToPlayer();
|
|
if (!player)
|
|
return 0.0f;
|
|
|
|
float crit = player->m_activePlayerData->CritPercentage;
|
|
float rangedCrit = player->m_activePlayerData->RangedCritPercentage;
|
|
float spellCrit = player->m_activePlayerData->SpellCritPercentage;
|
|
|
|
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->GetPrimarySpecialization() == ChrSpecialization(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 != nullptr;
|
|
}
|
|
|
|
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 nullptr;
|
|
return ChainEntry->last;
|
|
}
|
|
|
|
SpellInfo const* SpellInfo::GetNextRankSpell() const
|
|
{
|
|
if (!ChainEntry)
|
|
return nullptr;
|
|
return ChainEntry->next;
|
|
}
|
|
|
|
SpellInfo const* SpellInfo::GetPrevRankSpell() const
|
|
{
|
|
if (!ChainEntry)
|
|
return nullptr;
|
|
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_AURA_IS_DEBUFF) || HasAttribute(SPELL_ATTR2_ALLOW_LOW_LEVEL_BUFF) || HasAttribute(SPELL_ATTR3_ONLY_PROC_ON_CASTER))
|
|
return this;
|
|
|
|
bool needRankSelection = false;
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (IsPositiveEffect(effect.EffectIndex) &&
|
|
(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 != nullptr; nextSpellInfo = nextSpellInfo->GetPrevRankSpell())
|
|
{
|
|
// if found appropriate level
|
|
if (uint32(level + 10) >= nextSpellInfo->SpellLevel)
|
|
return nextSpellInfo;
|
|
|
|
// one rank less then
|
|
}
|
|
|
|
// not found
|
|
return nullptr;
|
|
}
|
|
|
|
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(WorldObject const* caster /*= nullptr*/, WorldObject const* viewer /*= nullptr*/) const
|
|
{
|
|
for (SpellXSpellVisualEntry const* visual : _visuals)
|
|
{
|
|
if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(visual->CasterPlayerConditionID))
|
|
if (!caster || !caster->IsPlayer() || !ConditionMgr::IsPlayerMeetingCondition(caster->ToPlayer(), playerCondition))
|
|
continue;
|
|
|
|
if (UnitConditionEntry const* unitCondition = sUnitConditionStore.LookupEntry(visual->CasterUnitConditionID))
|
|
if (!caster || !caster->IsUnit() || !ConditionMgr::IsUnitMeetingCondition(caster->ToUnit(), Object::ToUnit(viewer), unitCondition))
|
|
continue;
|
|
|
|
return visual->ID;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32 SpellInfo::GetSpellVisual(WorldObject const* caster /*= nullptr*/, WorldObject const* viewer /*= nullptr*/) const
|
|
{
|
|
if (SpellXSpellVisualEntry const* visual = sSpellXSpellVisualStore.LookupEntry(GetSpellXSpellVisualId(caster, viewer)))
|
|
{
|
|
//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 (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (!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;
|
|
}
|
|
|
|
inline bool _isPositiveTarget(SpellEffectInfo const& effect)
|
|
{
|
|
if (!effect.IsEffect())
|
|
return true;
|
|
|
|
return (effect.TargetA.GetCheckType() != TARGET_CHECK_ENEMY &&
|
|
effect.TargetB.GetCheckType() != TARGET_CHECK_ENEMY);
|
|
}
|
|
|
|
bool _isPositiveEffectImpl(SpellInfo const* spellInfo, SpellEffectInfo const& effect, std::unordered_set<std::pair<SpellInfo const*, SpellEffIndex>>& visited)
|
|
{
|
|
if (!effect.IsEffect())
|
|
return true;
|
|
|
|
// attribute may be already set in DB
|
|
if (!spellInfo->IsPositiveEffect(effect.EffectIndex))
|
|
return false;
|
|
|
|
// passive auras like talents are all positive
|
|
if (spellInfo->IsPassive())
|
|
return true;
|
|
|
|
// not found a single positive spell with this attribute
|
|
if (spellInfo->HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF))
|
|
return false;
|
|
|
|
if (spellInfo->HasAttribute(SPELL_ATTR4_AURA_IS_BUFF))
|
|
return true;
|
|
|
|
if (effect.EffectAttributes.HasFlag(SpellEffectAttributes::IsHarmful))
|
|
return false;
|
|
|
|
visited.insert({ spellInfo, effect.EffectIndex });
|
|
|
|
//We need scaling level info for some auras that compute bp 0 or positive but should be debuffs
|
|
float bpScalePerLevel = effect.RealPointsPerLevel;
|
|
int32 bp = effect.CalcValue();
|
|
switch (spellInfo->SpellFamilyName)
|
|
{
|
|
case SPELLFAMILY_GENERIC:
|
|
switch (spellInfo->Id)
|
|
{
|
|
case 40268: // Spiritual Vengeance, Teron Gorefiend, Black Temple
|
|
case 61987: // Avenging Wrath Marker
|
|
case 61988: // Divine Shield exclude aura
|
|
case 64412: // Phase Punch, Algalon the Observer, Ulduar
|
|
case 72410: // Rune of Blood, Saurfang, Icecrown Citadel
|
|
case 71204: // Touch of Insignificance, Lady Deathwhisper, Icecrown Citadel
|
|
return false;
|
|
case 24732: // Bat Costume
|
|
case 30877: // Tag Murloc
|
|
case 61716: // Rabbit Costume
|
|
case 61734: // Noblegarden Bunny
|
|
case 62344: // Fists of Stone
|
|
case 50344: // Dream Funnel
|
|
case 61819: // Manabonked! (item)
|
|
case 61834: // Manabonked! (minigob)
|
|
case 73523: // Rigor Mortis
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SPELLFAMILY_ROGUE:
|
|
switch (spellInfo->Id)
|
|
{
|
|
// Envenom must be considered as a positive effect even though it deals damage
|
|
case 32645: // Envenom
|
|
return true;
|
|
case 40251: // Shadow of Death, Teron Gorefiend, Black Temple
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SPELLFAMILY_WARRIOR:
|
|
// Slam, Execute
|
|
if ((spellInfo->SpellFamilyFlags[0] & 0x20200000) != 0)
|
|
return false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (spellInfo->Mechanic)
|
|
{
|
|
case MECHANIC_IMMUNE_SHIELD:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Special case: effects which determine positivity of whole spell
|
|
if (spellInfo->HasAttribute(SPELL_ATTR1_AURA_UNIQUE))
|
|
{
|
|
// check for targets, there seems to be an assortment of dummy triggering spells that should be negative
|
|
for (SpellEffectInfo const& otherEffect : spellInfo->GetEffects())
|
|
if (!_isPositiveTarget(otherEffect))
|
|
return false;
|
|
}
|
|
|
|
for (SpellEffectInfo const& otherEffect : spellInfo->GetEffects())
|
|
{
|
|
switch (otherEffect.Effect)
|
|
{
|
|
case SPELL_EFFECT_HEAL:
|
|
case SPELL_EFFECT_LEARN_SPELL:
|
|
case SPELL_EFFECT_SKILL_STEP:
|
|
case SPELL_EFFECT_HEAL_PCT:
|
|
return true;
|
|
case SPELL_EFFECT_INSTAKILL:
|
|
if (otherEffect.EffectIndex != effect.EffectIndex && // for spells like 38044: instakill effect is negative but auras on target must count as buff
|
|
otherEffect.TargetA.GetTarget() == effect.TargetA.GetTarget() &&
|
|
otherEffect.TargetB.GetTarget() == effect.TargetB.GetTarget())
|
|
return false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (otherEffect.IsAura())
|
|
{
|
|
switch (otherEffect.ApplyAuraName)
|
|
{
|
|
case SPELL_AURA_MOD_STEALTH:
|
|
case SPELL_AURA_MOD_UNATTACKABLE:
|
|
return true;
|
|
case SPELL_AURA_SCHOOL_HEAL_ABSORB:
|
|
case SPELL_AURA_EMPATHY:
|
|
case SPELL_AURA_MOD_SPELL_DAMAGE_FROM_CASTER:
|
|
case SPELL_AURA_PREVENTS_FLEEING:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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_INSTAKILL:
|
|
case SPELL_EFFECT_POWER_DRAIN:
|
|
case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF:
|
|
case SPELL_EFFECT_INTERRUPT_CAST:
|
|
case SPELL_EFFECT_PICKPOCKET:
|
|
case SPELL_EFFECT_GAMEOBJECT_DAMAGE:
|
|
case SPELL_EFFECT_DURABILITY_DAMAGE:
|
|
case SPELL_EFFECT_DURABILITY_DAMAGE_PCT:
|
|
case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
|
|
case SPELL_EFFECT_TAMECREATURE:
|
|
case SPELL_EFFECT_DISTRACT:
|
|
return false;
|
|
case SPELL_EFFECT_ENERGIZE:
|
|
case SPELL_EFFECT_ENERGIZE_PCT:
|
|
case SPELL_EFFECT_HEAL_PCT:
|
|
case SPELL_EFFECT_HEAL_MAX_HEALTH:
|
|
case SPELL_EFFECT_HEAL_MECHANICAL:
|
|
return true;
|
|
case SPELL_EFFECT_KNOCK_BACK:
|
|
case SPELL_EFFECT_CHARGE:
|
|
case SPELL_EFFECT_PERSISTENT_AREA_AURA:
|
|
case SPELL_EFFECT_ATTACK_ME:
|
|
case SPELL_EFFECT_POWER_BURN:
|
|
// check targets
|
|
if (!_isPositiveTarget(effect))
|
|
return false;
|
|
break;
|
|
case SPELL_EFFECT_DISPEL:
|
|
// non-positive dispel
|
|
switch (effect.MiscValue)
|
|
{
|
|
case DISPEL_STEALTH:
|
|
case DISPEL_INVISIBILITY:
|
|
case DISPEL_ENRAGE:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// also check targets
|
|
if (!_isPositiveTarget(effect))
|
|
return false;
|
|
break;
|
|
case SPELL_EFFECT_DISPEL_MECHANIC:
|
|
if (!_isPositiveTarget(effect))
|
|
{
|
|
// non-positive mechanic dispel on negative target
|
|
switch (effect.MiscValue)
|
|
{
|
|
case MECHANIC_BANDAGE:
|
|
case MECHANIC_SHIELD:
|
|
case MECHANIC_MOUNT:
|
|
case MECHANIC_INVULNERABILITY:
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case SPELL_EFFECT_THREAT:
|
|
case SPELL_EFFECT_MODIFY_THREAT_PERCENT:
|
|
// check targets AND basepoints
|
|
if (!_isPositiveTarget(effect) && bp > 0)
|
|
return false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (effect.IsAura())
|
|
{
|
|
// non-positive aura use
|
|
switch (effect.ApplyAuraName)
|
|
{
|
|
case SPELL_AURA_MOD_STAT: // dependent from basepoint sign (negative -> negative)
|
|
case SPELL_AURA_MOD_SKILL:
|
|
case SPELL_AURA_MOD_SKILL_2:
|
|
case SPELL_AURA_MOD_DODGE_PERCENT:
|
|
case SPELL_AURA_MOD_HEALING_DONE:
|
|
case SPELL_AURA_MOD_DAMAGE_DONE_CREATURE:
|
|
case SPELL_AURA_OBS_MOD_HEALTH:
|
|
case SPELL_AURA_OBS_MOD_POWER:
|
|
case SPELL_AURA_MOD_CRIT_PCT:
|
|
case SPELL_AURA_MOD_HIT_CHANCE:
|
|
case SPELL_AURA_MOD_SPELL_HIT_CHANCE:
|
|
case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
|
|
case SPELL_AURA_MOD_RANGED_HASTE:
|
|
case SPELL_AURA_MOD_MELEE_RANGED_HASTE:
|
|
case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
|
|
case SPELL_AURA_HASTE_SPELLS:
|
|
case SPELL_AURA_MOD_RECOVERY_RATE_BY_SPELL_LABEL:
|
|
case SPELL_AURA_MOD_DETECT_RANGE:
|
|
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
|
|
case SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE:
|
|
case SPELL_AURA_MOD_INCREASE_SWIM_SPEED:
|
|
case SPELL_AURA_MOD_PERCENT_STAT:
|
|
case SPELL_AURA_MOD_INCREASE_HEALTH:
|
|
case SPELL_AURA_MOD_SPEED_ALWAYS:
|
|
if (bp < 0 || bpScalePerLevel < 0) //TODO: What if both are 0? Should it be a buff or debuff?
|
|
return false;
|
|
break;
|
|
case SPELL_AURA_MOD_ATTACKSPEED: // some buffs have negative bp, check both target and bp
|
|
case SPELL_AURA_MOD_MELEE_HASTE:
|
|
case SPELL_AURA_MOD_DAMAGE_DONE:
|
|
case SPELL_AURA_MOD_RESISTANCE:
|
|
case SPELL_AURA_MOD_RESISTANCE_PCT:
|
|
case SPELL_AURA_MOD_RATING:
|
|
case SPELL_AURA_MOD_ATTACK_POWER:
|
|
case SPELL_AURA_MOD_RANGED_ATTACK_POWER:
|
|
case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
|
|
case SPELL_AURA_MOD_SPEED_SLOW_ALL:
|
|
case SPELL_AURA_MELEE_SLOW:
|
|
case SPELL_AURA_MOD_ATTACK_POWER_PCT:
|
|
case SPELL_AURA_MOD_HEALING_DONE_PERCENT:
|
|
case SPELL_AURA_MOD_HEALING_PCT:
|
|
if (!_isPositiveTarget(effect) || bp < 0)
|
|
return false;
|
|
break;
|
|
case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from basepoint sign (positive -> negative)
|
|
case SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN:
|
|
case SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT:
|
|
case SPELL_AURA_MOD_POWER_COST_SCHOOL:
|
|
case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT:
|
|
case SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT:
|
|
if (bp > 0)
|
|
return false;
|
|
break;
|
|
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: // check targets and basepoints (ex Recklessness)
|
|
if (!_isPositiveTarget(effect) && bp > 0)
|
|
return false;
|
|
break;
|
|
case SPELL_AURA_MOD_HEALTH_REGEN_PERCENT: // check targets and basepoints (target enemy and negative bp -> negative)
|
|
if (!_isPositiveTarget(effect) && bp < 0)
|
|
return false;
|
|
break;
|
|
case SPELL_AURA_ADD_TARGET_TRIGGER:
|
|
return true;
|
|
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
|
|
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT:
|
|
if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(effect.TriggerSpell, spellInfo->Difficulty))
|
|
{
|
|
// negative targets of main spell return early
|
|
for (SpellEffectInfo const& spellTriggeredEffect : spellTriggeredProto->GetEffects())
|
|
{
|
|
// already seen this
|
|
if (visited.count({ spellTriggeredProto, spellTriggeredEffect.EffectIndex }) > 0)
|
|
continue;
|
|
|
|
if (!spellTriggeredEffect.IsEffect())
|
|
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(spellTriggeredEffect) && !_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect, visited))
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
|
|
case SPELL_AURA_MOD_STUN:
|
|
case SPELL_AURA_TRANSFORM:
|
|
case SPELL_AURA_MOD_DECREASE_SPEED:
|
|
case SPELL_AURA_MOD_FEAR:
|
|
case SPELL_AURA_MOD_TAUNT:
|
|
// special auras: they may have non negative target but still need to be marked as debuff
|
|
// checked again after all effects (SpellInfo::_InitializeSpellPositivity)
|
|
case SPELL_AURA_MOD_PACIFY:
|
|
case SPELL_AURA_MOD_PACIFY_SILENCE:
|
|
case SPELL_AURA_MOD_DISARM:
|
|
case SPELL_AURA_MOD_DISARM_OFFHAND:
|
|
case SPELL_AURA_MOD_DISARM_RANGED:
|
|
case SPELL_AURA_MOD_CHARM:
|
|
case SPELL_AURA_AOE_CHARM:
|
|
case SPELL_AURA_MOD_POSSESS:
|
|
case SPELL_AURA_MOD_LANGUAGE:
|
|
case SPELL_AURA_DAMAGE_SHIELD:
|
|
case SPELL_AURA_PROC_TRIGGER_SPELL:
|
|
case SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE:
|
|
case SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE:
|
|
case SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE:
|
|
case SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE:
|
|
case SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE:
|
|
case SPELL_AURA_DUMMY:
|
|
case SPELL_AURA_PERIODIC_DUMMY:
|
|
case SPELL_AURA_MOD_HEALING:
|
|
case SPELL_AURA_MOD_WEAPON_CRIT_PERCENT:
|
|
case SPELL_AURA_POWER_BURN:
|
|
case SPELL_AURA_MOD_COOLDOWN:
|
|
case SPELL_AURA_MOD_CHARGE_COOLDOWN:
|
|
case SPELL_AURA_MOD_INCREASE_SPEED:
|
|
case SPELL_AURA_MOD_PARRY_PERCENT:
|
|
case SPELL_AURA_SET_VEHICLE_ID:
|
|
case SPELL_AURA_PERIODIC_ENERGIZE:
|
|
case SPELL_AURA_EFFECT_IMMUNITY:
|
|
case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
|
|
case SPELL_AURA_MOD_SHAPESHIFT:
|
|
case SPELL_AURA_MOD_THREAT:
|
|
case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE:
|
|
// check target for positive and negative spells
|
|
if (!_isPositiveTarget(effect))
|
|
return false;
|
|
break;
|
|
case SPELL_AURA_MOD_CONFUSE:
|
|
case SPELL_AURA_CHANNEL_DEATH_ITEM:
|
|
case SPELL_AURA_MOD_ROOT:
|
|
case SPELL_AURA_MOD_ROOT_2:
|
|
case SPELL_AURA_MOD_SILENCE:
|
|
case SPELL_AURA_MOD_DETAUNT:
|
|
case SPELL_AURA_GHOST:
|
|
case SPELL_AURA_PERIODIC_LEECH:
|
|
case SPELL_AURA_PERIODIC_MANA_LEECH:
|
|
case SPELL_AURA_MOD_STALKED:
|
|
case SPELL_AURA_PREVENT_RESURRECTION:
|
|
case SPELL_AURA_PERIODIC_DAMAGE:
|
|
case SPELL_AURA_PERIODIC_WEAPON_PERCENT_DAMAGE:
|
|
case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
|
|
case SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS:
|
|
case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS:
|
|
return false;
|
|
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:
|
|
case SPELL_AURA_ADD_FLAT_MODIFIER_BY_SPELL_LABEL:
|
|
case SPELL_AURA_ADD_PCT_MODIFIER_BY_SPELL_LABEL:
|
|
{
|
|
switch (SpellModOp(effect.MiscValue))
|
|
{
|
|
case SpellModOp::ChangeCastTime: // dependent from basepoint sign (positive -> negative)
|
|
case SpellModOp::Period:
|
|
case SpellModOp::PowerCostOnMiss:
|
|
case SpellModOp::StartCooldown:
|
|
if (bp > 0)
|
|
return false;
|
|
break;
|
|
case SpellModOp::Cooldown:
|
|
case SpellModOp::PowerCost0:
|
|
case SpellModOp::PowerCost1:
|
|
case SpellModOp::PowerCost2:
|
|
if (!spellInfo->IsPositive() && bp > 0) // dependent on prev effects too (ex Arcane Power)
|
|
return false;
|
|
break;
|
|
case SpellModOp::PointsIndex0: // always positive
|
|
case SpellModOp::PointsIndex1:
|
|
case SpellModOp::PointsIndex2:
|
|
case SpellModOp::PointsIndex3:
|
|
case SpellModOp::PointsIndex4:
|
|
case SpellModOp::Points:
|
|
case SpellModOp::Hate:
|
|
case SpellModOp::ChainAmplitude:
|
|
case SpellModOp::Amplitude:
|
|
return true;
|
|
case SpellModOp::Duration:
|
|
case SpellModOp::CritChance:
|
|
case SpellModOp::HealingAndDamage:
|
|
case SpellModOp::ChainTargets:
|
|
if (!spellInfo->IsPositive() && bp < 0) // dependent on prev effects too
|
|
return false;
|
|
break;
|
|
default: // dependent from basepoint sign (negative -> negative)
|
|
if (bp < 0)
|
|
return false;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// negative spell if triggered spell is negative
|
|
if (!effect.ApplyAuraName && effect.TriggerSpell)
|
|
{
|
|
if (SpellInfo const* spellTriggeredProto = sSpellMgr->GetSpellInfo(effect.TriggerSpell, spellInfo->Difficulty))
|
|
{
|
|
// 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 (SpellEffectInfo const& spellTriggeredEffect : spellTriggeredProto->GetEffects())
|
|
{
|
|
// already seen this
|
|
if (visited.count({ spellTriggeredProto, spellTriggeredEffect.EffectIndex }) > 0)
|
|
continue;
|
|
|
|
if (!spellTriggeredEffect.IsEffect())
|
|
continue;
|
|
|
|
if (!_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect, visited))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ok, positive
|
|
return true;
|
|
}
|
|
|
|
void SpellInfo::_InitializeSpellPositivity()
|
|
{
|
|
std::unordered_set<std::pair<SpellInfo const*, SpellEffIndex /*effIndex*/>> visited;
|
|
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
if (!_isPositiveEffectImpl(this, effect, visited))
|
|
NegativeEffects[effect.EffectIndex] = true;
|
|
|
|
// additional checks after effects marked
|
|
for (SpellEffectInfo const& effect : GetEffects())
|
|
{
|
|
if (!effect.IsEffect() || !IsPositiveEffect(effect.EffectIndex))
|
|
continue;
|
|
|
|
switch (effect.ApplyAuraName)
|
|
{
|
|
// has other non positive effect?
|
|
// then it should be marked negative if has same target as negative effect (ex 8510, 8511, 8893, 10267)
|
|
case SPELL_AURA_DUMMY:
|
|
case SPELL_AURA_MOD_STUN:
|
|
case SPELL_AURA_MOD_FEAR:
|
|
case SPELL_AURA_MOD_TAUNT:
|
|
case SPELL_AURA_TRANSFORM:
|
|
case SPELL_AURA_MOD_ATTACKSPEED:
|
|
case SPELL_AURA_MOD_DECREASE_SPEED:
|
|
{
|
|
for (size_t j = effect.EffectIndex + 1; j < GetEffects().size(); ++j)
|
|
if (!IsPositiveEffect(j)
|
|
&& effect.TargetA.GetTarget() == GetEffect(SpellEffIndex(j)).TargetA.GetTarget()
|
|
&& effect.TargetB.GetTarget() == GetEffect(SpellEffIndex(j)).TargetB.GetTarget())
|
|
NegativeEffects[effect.EffectIndex] = true;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SpellInfo::_UnloadImplicitTargetConditionLists()
|
|
{
|
|
// find the same instances of ConditionList and delete them.
|
|
for (SpellEffectInfo const& effect : _effects)
|
|
{
|
|
ConditionContainer* cur = effect.ImplicitTargetConditions;
|
|
if (!cur)
|
|
continue;
|
|
|
|
for (size_t j = effect.EffectIndex; j < _effects.size(); ++j)
|
|
if (_effects[j].ImplicitTargetConditions == cur)
|
|
_effects[j].ImplicitTargetConditions = nullptr;
|
|
|
|
delete cur;
|
|
}
|
|
}
|
|
|
|
bool SpellInfo::MeetsFutureSpellPlayerCondition(Player const* player) const
|
|
{
|
|
if (ShowFutureSpellPlayerConditionID == 0)
|
|
return false;
|
|
|
|
PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(ShowFutureSpellPlayerConditionID);
|
|
return !playerCondition || ConditionMgr::IsPlayerMeetingCondition(player, playerCondition);
|
|
}
|
|
|
|
bool SpellInfo::HasLabel(uint32 labelId) const
|
|
{
|
|
return Labels.find(labelId) != Labels.end();
|
|
}
|