aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp1053
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.h60
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp12
-rw-r--r--src/server/game/Spells/Spell.cpp26
4 files changed, 1137 insertions, 14 deletions
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 65b8e6662a1..2f84ad32f2c 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -26,10 +26,368 @@ enum Spells
SPELL_SHOOT = 3018,
SPELL_THROW = 2764,
SPELL_SHOOT_WAND = 5019,
+
+ /* Warrior - Generic */
+ SPELL_BATTLE_STANCE = 2457,
+ SPELL_BERSERKER_STANCE = 2458,
+ SPELL_DEFENSIVE_STANCE = 71,
+ SPELL_CHARGE = 11578,
+ SPELL_INTERCEPT = 20252,
+ SPELL_ENRAGED_REGEN = 55694,
+ SPELL_INTIMIDATING_SHOUT= 5246,
+ SPELL_PUMMEL = 6552,
+ SPELL_SHIELD_BASH = 72,
+ SPELL_BLOODRAGE = 2687,
+
+ /* Warrior - Arms */
+ SPELL_SWEEPING_STRIKES = 12328,
+ SPELL_MORTAL_STRIKE = 12294,
+ SPELL_BLADESTORM = 46924,
+ SPELL_REND = 47465,
+ SPELL_RETALIATION = 20230,
+ SPELL_SHATTERING_THROW = 64382,
+ SPELL_THUNDER_CLAP = 47502,
+
+ /* Warrior - Fury */
+ SPELL_DEATH_WISH = 12292,
+ SPELL_BLOODTHIRST = 23881,
+ PASSIVE_TITANS_GRIP = 46917,
+ SPELL_DEMO_SHOUT = 47437,
+ SPELL_EXECUTE = 47471,
+ SPELL_HEROIC_FURY = 60970,
+ SPELL_RECKLESSNESS = 1719,
+ SPELL_PIERCING_HOWL = 12323,
+
+ /* Warrior - Protection */
+ SPELL_VIGILANCE = 50720,
+ SPELL_DEVASTATE = 20243,
+ SPELL_SHOCKWAVE = 46968,
+ SPELL_CONCUSSION_BLOW = 12809,
+ SPELL_DISARM = 676,
+ SPELL_LAST_STAND = 12975,
+ SPELL_SHIELD_BLOCK = 2565,
+ SPELL_SHIELD_SLAM = 47488,
+ SPELL_SHIELD_WALL = 871,
+ SPELL_SPELL_REFLECTION = 23920,
+
+ /* Paladin - Generic */
+ SPELL_PAL_AURA_MASTERY = 31821,
+ SPELL_LAY_ON_HANDS = 48788,
+ SPELL_BLESSING_OF_MIGHT = 48932,
+ SPELL_AVENGING_WRATH = 31884,
+ SPELL_DIVINE_PROTECTION = 498,
+ SPELL_DIVINE_SHIELD = 642,
+ SPELL_HAMMER_OF_JUSTICE = 10308,
+ SPELL_HAND_OF_FREEDOM = 1044,
+ SPELL_HAND_OF_PROTECTION = 10278,
+ SPELL_HAND_OF_SACRIFICE = 6940,
+
+ /* Paladin - Holy*/
+ PASSIVE_ILLUMINATION = 20215,
+ SPELL_HOLY_SHOCK = 20473,
+ SPELL_BEACON_OF_LIGHT = 53563,
+ SPELL_CONSECRATION = 48819,
+ SPELL_FLASH_OF_LIGHT = 48785,
+ SPELL_HOLY_LIGHT = 48782,
+ SPELL_DIVINE_FAVOR = 20216,
+ SPELL_DIVINE_ILLUMINATION = 31842,
+
+ /* Paladin - Protection */
+ SPELL_BLESS_OF_SANC = 20911,
+ SPELL_HOLY_SHIELD = 20925,
+ SPELL_AVENGERS_SHIELD = 48827,
+ SPELL_DIVINE_SACRIFICE = 64205,
+ SPELL_HAMMER_OF_RIGHTEOUS = 53595,
+ SPELL_RIGHTEOUS_FURY = 25780,
+ SPELL_SHIELD_OF_RIGHTEOUS = 61411,
+
+ /* Paladin - Retribution */
+ SPELL_SEAL_OF_COMMAND = 20375,
+ SPELL_CRUSADER_STRIKE = 35395,
+ SPELL_DIVINE_STORM = 53385,
+ SPELL_JUDGEMENT = 20271,
+ SPELL_HAMMER_OF_WRATH = 48806,
+
+ /* Hunter - Generic */
+ SPELL_DETERRENCE = 19263,
+ SPELL_EXPLOSIVE_TRAP = 49067,
+ SPELL_FREEZING_ARROW = 60192,
+ SPELL_RAPID_FIRE = 3045,
+ SPELL_KILL_SHOT = 61006,
+ SPELL_MULTI_SHOT = 49048,
+ SPELL_VIPER_STING = 3034,
+
+ /* Hunter - Beast Mastery */
+ SPELL_BESTIAL_WRATH = 19574,
+ PASSIVE_BEAST_WITHIN = 34692,
+ PASSIVE_BEAST_MASTERY = 53270,
+
+ /* Hunter - Marksmanship */
+ SPELL_AIMED_SHOT = 19434,
+ PASSIVE_TRUESHOT_AURA = 19506,
+ SPELL_CHIMERA_SHOT = 53209,
+ SPELL_ARCANE_SHOT = 49045,
+ SPELL_STEADY_SHOT = 49052,
+ SPELL_READINESS = 23989,
+ SPELL_SILENCING_SHOT = 34490,
+
+ /* Hunter - Survival */
+ PASSIVE_LOCK_AND_LOAD = 56344,
+ SPELL_WYVERN_STING = 19386,
+ SPELL_EXPLOSIVE_SHOT = 53301,
+ SPELL_BLACK_ARROW = 3674,
+
+ /* Rogue - Generic */
+ SPELL_DISMANTLE = 51722,
+ SPELL_EVASION = 26669,
+ SPELL_KICK = 1766,
+ SPELL_VANISH = 26889,
+ SPELL_BLIND = 2094,
+ SPELL_CLOAK_OF_SHADOWS = 31224,
+
+ /* Rogue - Assassination */
+ SPELL_COLD_BLOOD = 14177,
+ SPELL_MUTILATE = 1329,
+ SPELL_HUNGER_FOR_BLOOD = 51662,
+ SPELL_ENVENOM = 57993,
+
+ /* Rogue - Combat */
+ SPELL_SINISTER_STRIKE = 48637,
+ SPELL_BLADE_FLURRY = 13877,
+ SPELL_ADRENALINE_RUSH = 13750,
+ SPELL_KILLING_SPREE = 51690,
+ SPELL_EVISCERATE = 48668,
+
+ /* Rogue - Sublety */
+ SPELL_HEMORRHAGE = 16511,
+ SPELL_PREMEDITATION = 14183,
+ SPELL_SHADOW_DANCE = 51713,
+ SPELL_PREPARATION = 14185,
+ SPELL_SHADOWSTEP = 36554,
+
+ /* Priest - Generic */
+ SPELL_FEAR_WARD = 6346,
+ SPELL_POWER_WORD_FORT = 48161,
+ SPELL_DIVINE_SPIRIT = 48073,
+ SPELL_SHADOW_PROTECTION = 48169,
+ SPELL_DIVINE_HYMN = 64843,
+ SPELL_HYMN_OF_HOPE = 64901,
+ SPELL_SHADOW_WORD_DEATH = 48158,
+ SPELL_PSYCHIC_SCREAM = 10890,
+
+ /* Priest - Discipline */
+ PASSIVE_SOUL_WARDING = 63574,
+ SPELL_POWER_INFUSION = 10060,
+ SPELL_PENANCE = 47540,
+ SPELL_PAIN_SUPPRESSION = 33206,
+ SPELL_INNER_FOCUS = 14751,
+ SPELL_POWER_WORD_SHIELD = 48066,
+
+ /* Priest - Holy */
+ PASSIVE_SPIRIT_REDEMPTION = 20711,
+ SPELL_DESPERATE_PRAYER = 19236,
+ SPELL_GUARDIAN_SPIRIT = 47788,
+ SPELL_FLASH_HEAL = 48071,
+ SPELL_RENEW = 48068,
+
+ /* Priest - Shadow */
+ SPELL_VAMPIRIC_EMBRACE = 15286,
+ SPELL_SHADOWFORM = 15473,
+ SPELL_VAMPIRIC_TOUCH = 34914,
+ SPELL_MIND_FLAY = 15407,
+ SPELL_MIND_BLAST = 48127,
+ SPELL_SHADOW_WORD_PAIN = 48125,
+ SPELL_DEVOURING_PLAGUE = 48300,
+ SPELL_DISPERSION = 47585,
+
+ /* Death Knight - Generic */
+ SPELL_DEATH_GRIP = 49576,
+ SPELL_STRANGULATE = 47476,
+ SPELL_EMPOWER_RUNE_WEAP = 47568,
+ SPELL_ICEBORN_FORTITUDE = 48792,
+ SPELL_ANTI_MAGIC_SHELL = 48707,
+ SPELL_DEATH_COIL_DK = 49895,
+ SPELL_MIND_FREEZE = 47528,
+ SPELL_ICY_TOUCH = 49909,
+ AURA_FROST_FEVER = 55095,
+ SPELL_PLAGUE_STRIKE = 49921,
+ AURA_BLOOD_PLAGUE = 55078,
+ SPELL_PESTILENCE = 50842,
+
+ /* Death Knight - Blood */
+ SPELL_RUNE_TAP = 48982,
+ SPELL_HYSTERIA = 49016,
+ SPELL_HEART_STRIKE = 55050,
+ SPELL_DEATH_STRIKE = 49924,
+ SPELL_BLOOD_STRIKE = 49930,
+ SPELL_MARK_OF_BLOOD = 49005,
+ SPELL_VAMPIRIC_BLOOD = 55233,
+
+ /* Death Knight - Frost */
+ PASSIVE_ICY_TALONS = 50887,
+ SPELL_FROST_STRIKE = 49143,
+ SPELL_HOWLING_BLAST = 49184,
+ SPELL_UNBREAKABLE_ARMOR = 51271,
+ SPELL_OBLITERATE = 51425,
+ SPELL_DEATHCHILL = 49796,
+
+ /* Death Knight - Unholy */
+ PASSIVE_UNHOLY_BLIGHT = 49194,
+ PASSIVE_MASTER_OF_GHOUL = 52143,
+ SPELL_SCOURGE_STRIKE = 55090,
+ SPELL_DEATH_AND_DECAY = 49938,
+ SPELL_ANTI_MAGIC_ZONE = 51052,
+ SPELL_SUMMON_GARGOYLE = 49206,
+
+ /* Shaman - Generic */
+ SPELL_HEROISM = 32182,
+ SPELL_BLOODLUST = 2825,
+ SPELL_GROUNDING_TOTEM = 8177,
+
+ /* Shaman - Elemental*/
+ PASSIVE_ELEMENTAL_FOCUS = 16164,
+ SPELL_TOTEM_OF_WRATH = 30706,
+ SPELL_THUNDERSTORM = 51490,
+ SPELL_LIGHTNING_BOLT = 49238,
+ SPELL_EARTH_SHOCK = 49231,
+ SPELL_FLAME_SHOCK = 49233,
+ SPELL_LAVA_BURST = 60043,
+ SPELL_CHAIN_LIGHTNING = 49271,
+ SPELL_ELEMENTAL_MASTERY = 16166,
+
+ /* Shaman - Enhancement */
+ PASSIVE_SPIRIT_WEAPONS = 16268,
+ SPELL_LAVA_LASH = 60103,
+ SPELL_FERAL_SPIRIT = 51533,
+ AURA_MAELSTROM_WEAPON = 53817,
+ SPELL_STORMSTRIKE = 17364,
+ SPELL_SHAMANISTIC_RAGE = 30823,
+
+ /* Shaman - Restoration*/
+ SPELL_SHA_NATURE_SWIFT = 591,
+ SPELL_MANA_TIDE_TOTEM = 590,
+ SPELL_EARTH_SHIELD = 49284,
+ SPELL_RIPTIDE = 61295,
+ SPELL_HEALING_WAVE = 49273,
+ SPELL_LESSER_HEAL_WAVE = 49276,
+ SPELL_TIDAL_FORCE = 55198,
+
+ /* Mage - Generic */
+ SPELL_DAMPEN_MAGIC = 43015,
+ SPELL_EVOCATION = 12051,
+ SPELL_MANA_SHIELD = 43020,
+ SPELL_MIRROR_IMAGE = 55342,
+ SPELL_SPELLSTEAL = 30449,
+ SPELL_COUNTERSPELL = 2139,
+ SPELL_ICE_BLOCK = 45438,
+
+ /* Mage - Arcane */
+ SPELL_FOCUS_MAGIC = 54646,
+ SPELL_ARCANE_POWER = 12042,
+ SPELL_ARCANE_BARRAGE = 44425,
+ SPELL_ARCANE_BLAST = 42897,
+ AURA_ARCANE_BLAST = 36032,
+ SPELL_ARCANE_MISSILES = 42846,
+ SPELL_PRESENCE_OF_MIND = 12043,
+
+ /* Mage - Fire */
+ SPELL_PYROBLAST = 11366,
+ SPELL_COMBUSTION = 11129,
+ SPELL_LIVING_BOMB = 44457,
+ SPELL_FIREBALL = 42833,
+ SPELL_FIRE_BLAST = 42873,
+ SPELL_DRAGONS_BREATH = 31661,
+ SPELL_BLAST_WAVE = 11113,
+
+ /* Mage - Frost */
+ SPELL_ICY_VEINS = 12472,
+ SPELL_ICE_BARRIER = 11426,
+ SPELL_DEEP_FREEZE = 44572,
+ SPELL_FROST_NOVA = 42917,
+ SPELL_FROSTBOLT = 42842,
+ SPELL_COLD_SNAP = 11958,
+ SPELL_ICE_LANCE = 42914,
+
+ /* Warlock - Generic */
+ SPELL_FEAR = 6215,
+ SPELL_HOWL_OF_TERROR = 17928,
+ SPELL_CORRUPTION = 47813,
+ SPELL_DEATH_COIL_W = 47860,
+ SPELL_SHADOW_BOLT = 47809,
+ SPELL_INCINERATE = 47838,
+ SPELL_IMMOLATE = 47811,
+ SPELL_SEED_OF_CORRUPTION = 47836,
+
+ /* Warlock - Affliction */
+ PASSIVE_SIPHON_LIFE = 63108,
+ SPELL_UNSTABLE_AFFLICTION = 30108,
+ SPELL_HAUNT = 48181,
+ SPELL_CURSE_OF_AGONY = 47864,
+ SPELL_DRAIN_SOUL = 47855,
+
+ /* Warlock - Demonology */
+ SPELL_SOUL_LINK = 19028,
+ SPELL_DEMONIC_EMPOWERMENT = 47193,
+ SPELL_METAMORPHOSIS = 59672,
+ SPELL_IMMOLATION_AURA = 50589,
+ SPELL_DEMON_CHARGE = 54785,
+ AURA_DECIMATION = 63167,
+ AURA_MOLTEN_CORE = 71165,
+ SPELL_SOUL_FIRE = 47825,
+
+ /* Warlock - Destruction */
+ SPELL_SHADOWBURN = 17877,
+ SPELL_CONFLAGRATE = 17962,
+ SPELL_CHAOS_BOLT = 50796,
+ SPELL_SHADOWFURY = 47847,
+
+ /* Druid - Generic */
+ SPELL_BARKSKIN = 22812,
+ SPELL_INNERVATE = 29166,
+
+ /* Druid - Balance */
+ SPELL_INSECT_SWARM = 5570,
+ SPELL_MOONKIN_FORM = 24858,
+ SPELL_STARFALL = 48505,
+ SPELL_TYPHOON = 61384,
+ AURA_ECLIPSE_LUNAR = 48518,
+ SPELL_MOONFIRE = 48463,
+ SPELL_STARFIRE = 48465,
+ SPELL_WRATH = 48461,
+
+ /* Druid - Feral */
+ SPELL_CAT_FORM = 768,
+ SPELL_SURVIVAL_INSTINCTS = 61336,
+ SPELL_MANGLE = 33917,
+ SPELL_BERSERK = 50334,
+ SPELL_MANGLE_CAT = 48566,
+ SPELL_FERAL_CHARGE_CAT = 49376,
+ SPELL_RAKE = 48574,
+ SPELL_RIP = 49800,
+ SPELL_SAVAGE_ROAR = 52610,
+ SPELL_TIGER_FURY = 50213,
+ SPELL_CLAW = 48570,
+ SPELL_DASH = 33357,
+ SPELL_MAIM = 49802,
+
+ /* Druid - Restoration */
+ SPELL_SWIFTMEND = 18562,
+ SPELL_TREE_OF_LIFE = 33891,
+ SPELL_WILD_GROWTH = 48438,
+ SPELL_NATURE_SWIFTNESS = 17116,
+ SPELL_TRANQUILITY = 48447,
+ SPELL_NOURISH = 50464,
+ SPELL_HEALING_TOUCH = 48378,
+ SPELL_REJUVENATION = 48441,
+ SPELL_REGROWTH = 48443,
+ SPELL_LIFEBLOOM = 48451
};
bool PlayerAI::IsPlayerHealer(Player const* who)
{
+ if (!who)
+ return false;
+
switch (who->getClass())
{
case CLASS_WARRIOR:
@@ -38,6 +396,7 @@ bool PlayerAI::IsPlayerHealer(Player const* who)
case CLASS_DEATH_KNIGHT:
case CLASS_MAGE:
case CLASS_WARLOCK:
+ case CLASS_DEMON_HUNTER:
default:
return false;
case CLASS_PALADIN:
@@ -46,6 +405,8 @@ bool PlayerAI::IsPlayerHealer(Player const* who)
return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_PRIEST_DISCIPLINE || who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_PRIEST_HOLY;
case CLASS_SHAMAN:
return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_SHAMAN_RESTORATION;
+ case CLASS_MONK:
+ return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_MONK_MISTWEAVER;
case CLASS_DRUID:
return who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == TALENT_SPEC_DRUID_RESTORATION;
}
@@ -53,6 +414,9 @@ bool PlayerAI::IsPlayerHealer(Player const* who)
bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
{
+ if (!who)
+ return false;
+
switch (who->getClass())
{
case CLASS_WARRIOR:
@@ -82,6 +446,94 @@ bool PlayerAI::IsPlayerRangedAttacker(Player const* who)
}
}
+PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, Unit* target)
+{
+ // Find highest spell rank that we know
+ uint32 knownRank, nextRank;
+ if (me->HasSpell(spellId))
+ // this will save us some lookups if the player has the highest rank (expected case)
+ nextRank = sSpellMgr->GetNextSpellInChain(knownRank = spellId);
+ else
+ knownRank = 0, nextRank = sSpellMgr->GetFirstSpellInChain(spellId);
+
+ while (nextRank && me->HasSpell(nextRank))
+ nextRank = sSpellMgr->GetNextSpellInChain(knownRank = nextRank);
+
+ if (!knownRank)
+ return {};
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(knownRank);
+ if (!spellInfo)
+ return {};
+
+ if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo))
+ return {};
+
+ Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE);
+ if (spell->CanAutoCast(target))
+ return{ spell, target };
+
+ delete spell;
+ return {};
+}
+
+PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, SpellTarget target)
+{
+ Unit* pTarget = nullptr;
+ switch (target)
+ {
+ case TARGET_NONE:
+ break;
+ case TARGET_VICTIM:
+ pTarget = me->GetVictim();
+ if (!pTarget)
+ return {};
+ break;
+ case TARGET_CHARMER:
+ pTarget = me->GetCharmer();
+ if (!pTarget)
+ return {};
+ break;
+ case TARGET_SELF:
+ pTarget = me;
+ break;
+ }
+
+ return VerifySpellCast(spellId, pTarget);
+}
+
+PlayerAI::TargetedSpell PlayerAI::SelectSpellCast(PossibleSpellVector& spells)
+{
+ if (spells.empty())
+ return{};
+
+ uint32 totalWeights = 0;
+ for (PossibleSpell const& wSpell : spells)
+ totalWeights += wSpell.second;
+
+ TargetedSpell selected;
+ uint32 randNum = urand(0, totalWeights - 1);
+ for (PossibleSpell const& wSpell : spells)
+ {
+ if (selected)
+ {
+ delete wSpell.first.first;
+ continue;
+ }
+
+ if (randNum < wSpell.second)
+ selected = wSpell.first;
+ else
+ {
+ randNum -= wSpell.second;
+ delete wSpell.first.first;
+ }
+ }
+
+ spells.clear();
+ return selected;
+}
+
void PlayerAI::DoRangedAttackIfReady()
{
if (me->HasUnitState(UNIT_STATE_CASTING))
@@ -130,6 +582,29 @@ void PlayerAI::DoAutoAttackIfReady()
DoMeleeAttackIfReady();
}
+void PlayerAI::CancelAllShapeshifts()
+{
+ std::list<AuraEffect*> const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT);
+ std::set<Aura*> removableShapeshifts;
+ for (AuraEffect* auraEff : shapeshiftAuras)
+ {
+ Aura* aura = auraEff->GetBase();
+ if (!aura)
+ continue;
+ SpellInfo const* auraInfo = aura->GetSpellInfo();
+ if (!auraInfo)
+ continue;
+ if (auraInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL))
+ continue;
+ if (!auraInfo->IsPositive() || auraInfo->IsPassive())
+ continue;
+ removableShapeshifts.insert(aura);
+ }
+
+ for (Aura* aura : removableShapeshifts)
+ me->RemoveOwnedAura(aura, AURA_REMOVE_BY_CANCEL);
+}
+
struct UncontrolledTargetSelectPredicate : public std::unary_function<Unit*, bool>
{
bool operator()(Unit const* target) const
@@ -144,7 +619,534 @@ Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const
return nullptr;
}
-void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
+PlayerAI::TargetedSpell SimpleCharmedPlayerAI::SelectAppropriateCastForSpec()
+{
+ PossibleSpellVector spells;
+ /*
+ switch (me->getClass())
+ {
+ case CLASS_WARRIOR:
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_CHARGE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_INTERCEPT, TARGET_VICTIM, 10);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_ENRAGED_REGEN, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INTIMIDATING_SHOUT, TARGET_VICTIM, 4);
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_PUMMEL, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_BASH, TARGET_VICTIM, 15);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_BLOODRAGE, TARGET_NONE, 5);
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_WARRIOR_PROTECTION:
+ VerifyAndPushSpellCast(spells, SPELL_SHOCKWAVE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_CONCUSSION_BLOW, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DISARM, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LAST_STAND, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_BLOCK, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_SLAM, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_WALL, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SPELL_REFLECTION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DEVASTATE, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 1);
+ break;
+ case TALENT_SPEC_WARRIOR_ARMS:
+ VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_MORTAL_STRIKE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_BLADESTORM, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_RETALIATION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SHATTERING_THROW, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15);
+ break;
+ case TALENT_SPEC_WARRIOR_FURY:
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_WISH, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_BLOODTHIRST, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_HEROIC_FURY, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_RECKLESSNESS, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_PIERCING_HOWL, TARGET_VICTIM, 2);
+ break;
+ }
+ break;
+ case CLASS_PALADIN:
+ VerifyAndPushSpellCast(spells, SPELL_AURA_MASTERY, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_LAY_ON_HANDS, TARGET_CHARMER, 8);
+ VerifyAndPushSpellCast(spells, SPELL_BLESSING_OF_MIGHT, TARGET_CHARMER, 8);
+ VerifyAndPushSpellCast(spells, SPELL_AVENGING_WRATH, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_PROTECTION, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SHIELD, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_JUSTICE, TARGET_VICTIM, 6);
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_FREEDOM, TARGET_SELF, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, TARGET_SELF, 1);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ {
+ if (creatureCharmer->IsDungeonBoss() || creatureCharmer->isWorldBoss())
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_SACRIFICE, creatureCharmer, 10);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, creatureCharmer, 3);
+ }
+
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_PALADIN_PROTECTION:
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_RIGHTEOUS, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SACRIFICE, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_SHIELD_OF_RIGHTEOUS, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CONSECRATION, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHIELD, TARGET_NONE, 1);
+ break;
+ case TALENT_SPEC_PALADIN_HOLY:
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_OF_LIGHT, TARGET_CHARMER, 4);
+ VerifyAndPushSpellCast(spells, SPELL_HOLY_LIGHT, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_FAVOR, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_ILLUMINATION, TARGET_NONE, 3);
+ break;
+ case TALENT_SPEC_PALADIN_RETRIBUTION:
+ VerifyAndPushSpellCast(spells, SPELL_CRUSADER_STRIKE, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_STORM, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_WRATH, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_RIGHTEOUS_FURY, TARGET_NONE, 2);
+ break;
+ }
+ break;
+ case CLASS_HUNTER:
+ VerifyAndPushSpellCast(spells, SPELL_DETERRENCE, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_TRAP, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_FREEZING_ARROW, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_RAPID_FIRE, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_KILL_SHOT, TARGET_VICTIM, 10);
+ if (me->GetVictim() && me->GetVictim()->getPowerType() == POWER_MANA && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_VIPER_STING, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_VIPER_STING, TARGET_VICTIM, 5);
+
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_HUNTER_BEASTMASTER:
+ VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 2);
+ break;
+ case TALENT_SPEC_HUNTER_MARKSMAN:
+ VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CHIMERA_SHOT, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_READINESS, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_SILENCING_SHOT, TARGET_VICTIM, 5);
+ break;
+ case TALENT_SPEC_HUNTER_SURVIVAL:
+ VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_SHOT, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_BLACK_ARROW, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 1);
+ break;
+ }
+ break;
+ case CLASS_ROGUE:
+ {
+ VerifyAndPushSpellCast(spells, SPELL_DISMANTLE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_EVASION, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_VANISH, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_BLIND, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_CLOAK_OF_SHADOWS, TARGET_NONE, 2);
+
+ uint32 builder, finisher;
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_ROGUE_ASSASSINATION:
+ builder = SPELL_MUTILATE, finisher = SPELL_ENVENOM;
+ VerifyAndPushSpellCast(spells, SPELL_COLD_BLOOD, TARGET_NONE, 20);
+ break;
+ case TALENT_SPEC_ROGUE_COMBAT:
+ builder = SPELL_SINISTER_STRIKE, finisher = SPELL_EVISCERATE;
+ VerifyAndPushSpellCast(spells, SPELL_ADRENALINE_RUSH, TARGET_NONE, 6);
+ VerifyAndPushSpellCast(spells, SPELL_BLADE_FLURRY, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_KILLING_SPREE, TARGET_NONE, 25);
+ break;
+ case TALENT_SPEC_ROGUE_SUBTLETY:
+ builder = SPELL_HEMORRHAGE, finisher = SPELL_EVISCERATE;
+ VerifyAndPushSpellCast(spells, SPELL_PREPARATION, TARGET_NONE, 10);
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWSTEP, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_DANCE, TARGET_NONE, 10);
+ break;
+ }
+
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_KICK, TARGET_VICTIM, 25);
+
+ uint8 const cp = me->GetPower(POWER_COMBO_POINTS);
+ if (cp >= 4)
+ VerifyAndPushSpellCast(spells, finisher, TARGET_VICTIM, 10);
+ if (cp <= 4)
+ VerifyAndPushSpellCast(spells, builder, TARGET_VICTIM, 5);
+ }
+ break;
+ }
+ case CLASS_PRIEST:
+ VerifyAndPushSpellCast(spells, SPELL_FEAR_WARD, TARGET_SELF, 2);
+ VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_FORT, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_SPIRIT, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_PROTECTION, TARGET_CHARMER, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DIVINE_HYMN, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_HYMN_OF_HOPE, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_DEATH, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_PSYCHIC_SCREAM, TARGET_VICTIM, 3);
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_PRIEST_DISCIPLINE:
+ VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_SHIELD, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INNER_FOCUS, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_PAIN_SUPPRESSION, TARGET_CHARMER, 15);
+ VerifyAndPushSpellCast(spells, SPELL_POWER_INFUSION, TARGET_CHARMER, 10);
+ VerifyAndPushSpellCast(spells, SPELL_PENANCE, TARGET_CHARMER, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1);
+ break;
+ case TALENT_SPEC_PRIEST_HOLY:
+ VerifyAndPushSpellCast(spells, SPELL_DESPERATE_PRAYER, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_GUARDIAN_SPIRIT, TARGET_CHARMER, 5);
+ VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_RENEW, TARGET_CHARMER, 3);
+ break;
+ case TALENT_SPEC_PRIEST_SHADOW:
+ if (!me->HasAura(SPELL_SHADOWFORM))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWFORM, TARGET_NONE, 100);
+ break;
+ }
+ if (Unit* victim = me->GetVictim())
+ {
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_VAMPIRIC_TOUCH, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_TOUCH, TARGET_VICTIM, 4);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_SHADOW_WORD_PAIN, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_PAIN, TARGET_VICTIM, 3);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_DEVOURING_PLAGUE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_DEVOURING_PLAGUE, TARGET_VICTIM, 4);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_MIND_BLAST, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_MIND_FLAY, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_DISPERSION, TARGET_NONE, 10);
+ break;
+ }
+ break;
+ case CLASS_DEATH_KNIGHT:
+ {
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_GRIP, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_STRANGULATE, TARGET_VICTIM, 15);
+ VerifyAndPushSpellCast(spells, SPELL_EMPOWER_RUNE_WEAP, TARGET_NONE, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ICEBORN_FORTITUDE, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_SHELL, TARGET_NONE, 10);
+
+ bool hasFF = false, hasBP = false;
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_MIND_FREEZE, TARGET_VICTIM, 25);
+
+ hasFF = !!victim->GetAuraApplicationOfRankedSpell(AURA_FROST_FEVER, me->GetGUID()), hasBP = !!victim->GetAuraApplicationOfRankedSpell(AURA_BLOOD_PLAGUE, me->GetGUID());
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_PESTILENCE, TARGET_VICTIM, 3);
+ if (!hasFF)
+ VerifyAndPushSpellCast(spells, SPELL_ICY_TOUCH, TARGET_VICTIM, 4);
+ if (!hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_PLAGUE_STRIKE, TARGET_VICTIM, 4);
+ }
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_DEATHKNIGHT_BLOOD:
+ VerifyAndPushSpellCast(spells, SPELL_RUNE_TAP, TARGET_NONE, 2);
+ VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, TARGET_SELF, 5);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ if (!creatureCharmer->IsDungeonBoss() && !creatureCharmer->isWorldBoss())
+ VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, creatureCharmer, 15);
+ VerifyAndPushSpellCast(spells, SPELL_HEART_STRIKE, TARGET_VICTIM, 2);
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_STRIKE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_MARK_OF_BLOOD, TARGET_VICTIM, 20);
+ VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_BLOOD, TARGET_NONE, 10);
+ break;
+ case TALENT_SPEC_DEATHKNIGHT_FROST:
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_OBLITERATE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_HOWLING_BLAST, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_UNBREAKABLE_ARMOR, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_DEATHCHILL, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_FROST_STRIKE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1);
+ break;
+ case TALENT_SPEC_DEATHKNIGHT_UNHOLY:
+ if (hasFF && hasBP)
+ VerifyAndPushSpellCast(spells, SPELL_SCOURGE_STRIKE, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_AND_DECAY, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_ZONE, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_SUMMON_GARGOYLE, TARGET_VICTIM, 7);
+ VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 3);
+ break;
+ }
+ break;
+ }
+ case CLASS_SHAMAN:
+ VerifyAndPushSpellCast(spells, SPELL_HEROISM, TARGET_NONE, 25);
+ VerifyAndPushSpellCast(spells, SPELL_BLOODLUST, TARGET_NONE, 25);
+ VerifyAndPushSpellCast(spells, SPELL_GROUNDING_TOTEM, TARGET_NONE, 2);
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_SHAMAN_RESTORATION:
+ if (Unit* charmer = me->GetCharmer())
+ if (!charmer->GetAuraApplicationOfRankedSpell(SPELL_EARTH_SHIELD, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_EARTH_SHIELD, charmer, 2);
+ if (me->HasAura(SPELL_SHA_NATURE_SWIFT))
+ VerifyAndPushSpellCast(spells, SPELL_HEALING_WAVE, TARGET_CHARMER, 20);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_LESSER_HEAL_WAVE, TARGET_CHARMER, 1);
+ VerifyAndPushSpellCast(spells, SPELL_TIDAL_FORCE, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_SHA_NATURE_SWIFT, TARGET_NONE, 4);
+ VerifyAndPushSpellCast(spells, SPELL_MANA_TIDE_TOTEM, TARGET_NONE, 3);
+ break;
+ case TALENT_SPEC_SHAMAN_ELEMENTAL:
+ if (Unit* victim = me->GetVictim())
+ {
+ if (victim->GetAuraOfRankedSpell(SPELL_FLAME_SHOCK, GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_LAVA_BURST, TARGET_VICTIM, 5);
+ else
+ VerifyAndPushSpellCast(spells, SPELL_FLAME_SHOCK, TARGET_VICTIM, 3);
+ }
+ VerifyAndPushSpellCast(spells, SPELL_CHAIN_LIGHTNING, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ELEMENTAL_MASTERY, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_THUNDERSTORM, TARGET_NONE, 3);
+ break;
+ case TALENT_SPEC_SHAMAN_ENHANCEMENT:
+ if (Aura const* maelstrom = me->GetAura(AURA_MAELSTROM_WEAPON))
+ if (maelstrom->GetStackAmount() == 5)
+ VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_STORMSTRIKE, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_EARTH_SHOCK, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_LAVA_LASH, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_SHAMANISTIC_RAGE, TARGET_NONE, 10);
+ break;
+ }
+ break;
+ case CLASS_MAGE:
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_COUNTERSPELL, TARGET_VICTIM, 25);
+ VerifyAndPushSpellCast(spells, SPELL_DAMPEN_MAGIC, TARGET_CHARMER, 2);
+ VerifyAndPushSpellCast(spells, SPELL_EVOCATION, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_MANA_SHIELD, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_MIRROR_IMAGE, TARGET_NONE, 3);
+ VerifyAndPushSpellCast(spells, SPELL_SPELLSTEAL, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_ICE_BLOCK, TARGET_NONE, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ICY_VEINS, TARGET_NONE, 3);
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_MAGE_ARCANE:
+ if (Aura* abAura = me->GetAura(AURA_ARCANE_BLAST))
+ if (abAura->GetStackAmount() >= 3)
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_MISSILES, TARGET_VICTIM, 7);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_BLAST, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_BARRAGE, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_ARCANE_POWER, TARGET_NONE, 8);
+ VerifyAndPushSpellCast(spells, SPELL_PRESENCE_OF_MIND, TARGET_NONE, 7);
+ break;
+ case TALENT_SPEC_MAGE_FIRE:
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_LIVING_BOMB))
+ VerifyAndPushSpellCast(spells, SPELL_LIVING_BOMB, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_COMBUSTION, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FIREBALL, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_FIRE_BLAST, TARGET_VICTIM, 1);
+ VerifyAndPushSpellCast(spells, SPELL_DRAGONS_BREATH, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_BLAST_WAVE, TARGET_VICTIM, 1);
+ break;
+ case TALENT_SPEC_MAGE_FROST:
+ VerifyAndPushSpellCast(spells, SPELL_DEEP_FREEZE, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_FROST_NOVA, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_FROSTBOLT, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_COLD_SNAP, TARGET_VICTIM, 5);
+ if (me->GetVictim() && me->GetVictim()->HasAuraState(AURA_STATE_FROZEN, nullptr, me))
+ VerifyAndPushSpellCast(spells, SPELL_ICE_LANCE, TARGET_VICTIM, 5);
+ break;
+ }
+ break;
+ case CLASS_WARLOCK:
+ VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_W, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_FEAR, TARGET_VICTIM, 2);
+ VerifyAndPushSpellCast(spells, SPELL_SEED_OF_CORRUPTION, TARGET_VICTIM, 4);
+ VerifyAndPushSpellCast(spells, SPELL_HOWL_OF_TERROR, TARGET_NONE, 2);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_CORRUPTION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CORRUPTION, TARGET_VICTIM, 10);
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_WARLOCK_AFFLICTION:
+ if (Unit* victim = me->GetVictim())
+ {
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_UNSTABLE_AFFLICTION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_UNSTABLE_AFFLICTION, TARGET_VICTIM, 8);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_HAUNT, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_HAUNT, TARGET_VICTIM, 8);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_CURSE_OF_AGONY, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CURSE_OF_AGONY, TARGET_VICTIM, 4);
+ if (victim->HealthBelowPct(25))
+ VerifyAndPushSpellCast(spells, SPELL_DRAIN_SOUL, TARGET_VICTIM, 100);
+ }
+ break;
+ case TALENT_SPEC_WARLOCK_DEMONOLOGY:
+ VerifyAndPushSpellCast(spells, SPELL_METAMORPHOSIS, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7);
+ if (me->HasAura(AURA_DECIMATION))
+ VerifyAndPushSpellCast(spells, SPELL_SOUL_FIRE, TARGET_VICTIM, 100);
+ if (me->HasAura(SPELL_METAMORPHOSIS))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATION_AURA, TARGET_NONE, 30);
+ if (!me->IsWithinMeleeRange(me->GetVictim()))
+ VerifyAndPushSpellCast(spells, SPELL_DEMON_CHARGE, TARGET_VICTIM, 20);
+ }
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 5);
+ if (me->HasAura(AURA_MOLTEN_CORE))
+ VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 10);
+ break;
+ case TALENT_SPEC_WARLOCK_DESTRUCTION:
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 8);
+ if (me->GetVictim() && me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_CONFLAGRATE, TARGET_VICTIM, 8);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWFURY, TARGET_VICTIM, 5);
+ VerifyAndPushSpellCast(spells, SPELL_CHAOS_BOLT, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_SHADOWBURN, TARGET_VICTIM, 3);
+ VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 7);
+ break;
+ }
+ break;
+ case CLASS_MONK:
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_MONK_BREWMASTER:
+ case TALENT_SPEC_MONK_BATTLEDANCER:
+ case TALENT_SPEC_MONK_MISTWEAVER:
+ break;
+ }
+ break;
+ case CLASS_DRUID:
+ VerifyAndPushSpellCast(spells, SPELL_INNERVATE, TARGET_CHARMER, 5);
+ VerifyAndPushSpellCast(spells, SPELL_BARKSKIN, TARGET_NONE, 5);
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_DRUID_RESTORATION:
+ if (!me->HasAura(SPELL_TREE_OF_LIFE))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_TREE_OF_LIFE, TARGET_NONE, 100);
+ break;
+ }
+ VerifyAndPushSpellCast(spells, SPELL_TRANQUILITY, TARGET_NONE, 10);
+ VerifyAndPushSpellCast(spells, SPELL_NATURE_SWIFTNESS, TARGET_NONE, 7);
+ if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID()))
+ {
+ VerifyAndPushSpellCast(spells, SPELL_NOURISH, creatureCharmer, 5);
+ VerifyAndPushSpellCast(spells, SPELL_WILD_GROWTH, creatureCharmer, 5);
+ if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_REJUVENATION, creatureCharmer, 8);
+ if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_REGROWTH, creatureCharmer, 8);
+ uint8 lifebloomStacks = 0;
+ if (Aura const* lifebloom = creatureCharmer->GetAura(SPELL_LIFEBLOOM, me->GetGUID()))
+ lifebloomStacks = lifebloom->GetStackAmount();
+ if (lifebloomStacks < 3)
+ VerifyAndPushSpellCast(spells, SPELL_LIFEBLOOM, creatureCharmer, 5);
+ if (creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION) ||
+ creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH))
+ VerifyAndPushSpellCast(spells, SPELL_SWIFTMEND, creatureCharmer, 10);
+ if (me->HasAura(SPELL_NATURE_SWIFTNESS))
+ VerifyAndPushSpellCast(spells, SPELL_HEALING_TOUCH, creatureCharmer, 100);
+ }
+ break;
+ case TALENT_SPEC_DRUID_BALANCE:
+ {
+ if (!me->HasAura(SPELL_MOONKIN_FORM))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_MOONKIN_FORM, TARGET_NONE, 100);
+ break;
+ }
+ uint32 const mainAttackSpell = me->HasAura(AURA_ECLIPSE_LUNAR) ? SPELL_STARFIRE : SPELL_WRATH;
+ VerifyAndPushSpellCast(spells, SPELL_STARFALL, TARGET_NONE, 20);
+ VerifyAndPushSpellCast(spells, mainAttackSpell, TARGET_VICTIM, 10);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_INSECT_SWARM, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_INSECT_SWARM, TARGET_VICTIM, 7);
+ if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_MOONFIRE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_MOONFIRE, TARGET_VICTIM, 5);
+ if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
+ VerifyAndPushSpellCast(spells, SPELL_TYPHOON, TARGET_NONE, 15);
+ break;
+ }
+ case TALENT_SPEC_DRUID_CAT:
+ case TALENT_SPEC_DRUID_BEAR:
+ if (!me->HasAura(SPELL_CAT_FORM))
+ {
+ CancelAllShapeshifts();
+ VerifyAndPushSpellCast(spells, SPELL_CAT_FORM, TARGET_NONE, 100);
+ break;
+ }
+ VerifyAndPushSpellCast(spells, SPELL_BERSERK, TARGET_NONE, 20);
+ VerifyAndPushSpellCast(spells, SPELL_SURVIVAL_INSTINCTS, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_TIGER_FURY, TARGET_NONE, 15);
+ VerifyAndPushSpellCast(spells, SPELL_DASH, TARGET_NONE, 5);
+ if (Unit* victim = me->GetVictim())
+ {
+ uint8 const cp = me->GetPower(POWER_COMBO_POINTS);
+ if (victim->HasUnitState(UNIT_STATE_CASTING) && cp >= 1)
+ VerifyAndPushSpellCast(spells, SPELL_MAIM, TARGET_VICTIM, 25);
+ if (!me->IsWithinMeleeRange(victim))
+ VerifyAndPushSpellCast(spells, SPELL_FERAL_CHARGE_CAT, TARGET_VICTIM, 25);
+ if (cp >= 4)
+ VerifyAndPushSpellCast(spells, SPELL_RIP, TARGET_VICTIM, 50);
+ if (cp <= 4)
+ {
+ VerifyAndPushSpellCast(spells, SPELL_MANGLE_CAT, TARGET_VICTIM, 10);
+ VerifyAndPushSpellCast(spells, SPELL_CLAW, TARGET_VICTIM, 5);
+ if (!victim->GetAuraApplicationOfRankedSpell(SPELL_RAKE, me->GetGUID()))
+ VerifyAndPushSpellCast(spells, SPELL_RAKE, TARGET_VICTIM, 8);
+ if (!me->HasAura(SPELL_SAVAGE_ROAR))
+ VerifyAndPushSpellCast(spells, SPELL_SAVAGE_ROAR, TARGET_NONE, 15);
+ }
+ }
+ break;
+ }
+ break;
+ case CLASS_DEMON_HUNTER:
+ switch (GetSpec())
+ {
+ case TALENT_SPEC_DEMON_HUNTER_HAVOC:
+ case TALENT_SPEC_DEMON_HUNTER_VENGEANCE:
+ break;
+ }
+ break;
+ }
+ */
+ return SelectSpellCast(spells);
+}
+
+static const float CASTER_CHASE_DISTANCE = 28.0f;
+void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff)
{
Creature* charmer = me->GetCharmer() ? me->GetCharmer()->ToCreature() : nullptr;
if (!charmer)
@@ -172,10 +1174,54 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/)
return;
if (IsRangedAttacker())
- AttackStartCaster(target, 28.0f);
+ {
+ _chaseCloser = !me->IsWithinLOSInMap(target);
+ if (_chaseCloser)
+ AttackStart(target);
+ else
+ AttackStartCaster(target, CASTER_CHASE_DISTANCE);
+ }
else
AttackStart(target);
+ _forceFacing = true;
+ }
+
+ if (me->IsStopped() && !me->HasUnitState(UNIT_STATE_CANNOT_TURN))
+ {
+ float targetAngle = me->GetAngle(target);
+ if (_forceFacing || fabs(me->GetOrientation() - targetAngle) > 0.4f)
+ {
+ me->SetFacingTo(targetAngle);
+ _forceFacing = false;
+ }
}
+
+ if (_castCheckTimer <= diff)
+ {
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ _castCheckTimer = 0;
+ else
+ {
+ if (IsRangedAttacker())
+ { // chase to zero if the target isn't in line of sight
+ bool inLOS = me->IsWithinLOSInMap(target);
+ if (_chaseCloser != !inLOS)
+ {
+ _chaseCloser = !inLOS;
+ if (_chaseCloser)
+ AttackStart(target);
+ else
+ AttackStartCaster(target, CASTER_CHASE_DISTANCE);
+ }
+ }
+ if (TargetedSpell shouldCast = SelectAppropriateCastForSpec())
+ DoCastAtTarget(shouldCast);
+ _castCheckTimer = 500;
+ }
+ }
+ else
+ _castCheckTimer -= diff;
+
DoAutoAttackIfReady();
}
else
@@ -194,6 +1240,9 @@ void SimpleCharmedPlayerAI::OnCharmed(bool apply)
{
me->CastStop();
me->AttackStop();
+ me->StopMoving();
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MovePoint(0, me->GetPosition(), false); // force re-sync of current position for all clients
}
else
{
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h
index 794a87bd77e..2d7adc6c551 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.h
+++ b/src/server/game/AI/PlayerAI/PlayerAI.h
@@ -20,41 +20,95 @@
#include "UnitAI.h"
#include "Player.h"
+#include "Spell.h"
#include "Creature.h"
class TC_GAME_API PlayerAI : public UnitAI
{
public:
- explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
+ explicit PlayerAI(Player* player) : UnitAI(player), me(player), _selfSpec(player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID)), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { }
void OnCharmed(bool /*apply*/) override { } // charm AI application for players is handled by Unit::SetCharmedBy / Unit::RemoveCharmedBy
// helper functions to determine player info
+ uint16 GetSpec(Player const* who = nullptr) const { return (!who || who == me) ? _selfSpec : who->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID); }
static bool IsPlayerHealer(Player const* who);
bool IsHealer(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who); }
static bool IsPlayerRangedAttacker(Player const* who);
bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); }
protected:
+ struct TargetedSpell : public std::pair<Spell*, Unit*>
+ {
+ using std::pair<Spell*, Unit*>::pair;
+ explicit operator bool() { return !!first; }
+ };
+ typedef std::pair<TargetedSpell, uint32> PossibleSpell;
+ typedef std::vector<PossibleSpell> PossibleSpellVector;
+
Player* const me;
void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection
+ enum SpellTarget
+ {
+ TARGET_NONE,
+ TARGET_VICTIM,
+ TARGET_CHARMER,
+ TARGET_SELF
+ };
+ /* Check if the specified spell can be cast on that target.
+ Caller is responsible for cleaning up created Spell object from pointer. */
+ TargetedSpell VerifySpellCast(uint32 spellId, Unit* target);
+ /* Check if the specified spell can be cast on that target.
+ Caller is responsible for cleaning up created Spell object from pointer. */
+ TargetedSpell VerifySpellCast(uint32 spellId, SpellTarget target);
+
+ /* Helper method - checks spell cast, then pushes it onto provided vector if valid. */
+ template<typename T> inline void VerifyAndPushSpellCast(PossibleSpellVector& spells, uint32 spellId, T target, uint32 weight)
+ {
+ if (TargetedSpell spell = VerifySpellCast(spellId, target))
+ spells.push_back({ spell,weight });
+ }
+
+ /* Helper method - selects one spell from the vector and returns it, while deleting everything else.
+ This invalidates the vector, and empties it to prevent accidental misuse. */
+ TargetedSpell SelectSpellCast(PossibleSpellVector& spells);
+ /* Helper method - casts the included spell at the included target */
+ inline void DoCastAtTarget(TargetedSpell spell)
+ {
+ SpellCastTargets targets;
+ targets.SetUnitTarget(spell.second);
+ spell.first->prepare(&targets);
+ }
+
virtual Unit* SelectAttackTarget() const { return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; }
void DoRangedAttackIfReady();
void DoAutoAttackIfReady();
+ // Cancels all shapeshifts that the player could voluntarily cancel
+ void CancelAllShapeshifts();
+
private:
- bool _isSelfHealer;
+ uint16 const _selfSpec;
+ bool const _isSelfHealer;
bool _isSelfRangedAttacker;
};
class TC_GAME_API SimpleCharmedPlayerAI : public PlayerAI
{
public:
- SimpleCharmedPlayerAI(Player* player) : PlayerAI(player) { }
+ SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { }
void UpdateAI(uint32 diff) override;
void OnCharmed(bool apply) override;
+
+ protected:
Unit* SelectAttackTarget() const override;
+
+ private:
+ TargetedSpell SelectAppropriateCastForSpec();
+ uint32 _castCheckTimer;
+ bool _chaseCloser;
+ bool _forceFacing;
};
#endif
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 5a99eea7dae..70b082d6d88 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -9411,8 +9411,11 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
&& (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)))
return false;
+ // Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction)
+ if (GetTypeId() == TYPEID_PLAYER && IsCharmed() && GetCharmerGUID().IsCreature())
+ return true;
// PvP case
- if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
+ else if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
{
Player const* targetPlayerOwner = target->GetAffectingPlayer();
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
@@ -11066,6 +11069,7 @@ void Unit::UpdateCharmAI()
if (!newAI) // otherwise, we default to the generic one
newAI = new SimpleCharmedPlayerAI(ToPlayer());
i_AI = newAI;
+ newAI->OnCharmed(true);
}
else
{
@@ -13462,7 +13466,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
{
// change AI to charmed AI on next Update tick
NeedChangeAI = true;
- IsAIEnabled = false;
+ if (IsAIEnabled)
+ {
+ IsAIEnabled = false;
+ player->AI()->OnCharmed(true);
+ }
}
player->SetClientControl(this, false);
}
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 672ae8ce38e..05e8ff05e7b 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2959,7 +2959,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetSpellModTakingSpell(this, true);
- // Fill cost data (not use power for item casts
+ // Fill cost data (do not use power for item casts)
if (!m_CastItem)
m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask);
if (m_caster->GetTypeId() == TYPEID_PLAYER)
@@ -3043,7 +3043,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered
// exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING
// (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in)
// don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
- if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() &&
+ if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() &&
m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo))
{
// 1. Is a channel spell, 2. Has no casttime, 3. And has flag to allow movement during channel
@@ -3603,7 +3603,13 @@ void Spell::update(uint32 difftime)
{
// don't cancel for melee, autorepeat, triggered and instant spells
if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING)))
- cancel();
+ {
+ // if charmed by creature, trust the AI not to cheat and allow the cast to proceed
+ // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now
+ // however, checking what type of movement the spline is for every single spline would be really expensive
+ if (!m_caster->GetCharmerGUID().IsCreature())
+ cancel();
+ }
}
switch (m_spellState)
@@ -4861,7 +4867,7 @@ SpellCastResult Spell::CheckCast(bool strict)
// cancel autorepeat spells if cast start when moving
// (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
// Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo))
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo))
{
// skip stuck spell to allow use it in falling case and apply spell limitations at movement
SpellEffectInfo const* effect = GetEffect(EFFECT_0);
@@ -5703,7 +5709,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target)
m_targets.SetUnitTarget(target);
}
- // cooldown
+ // check cooldown
if (Creature* creatureCaster = m_caster->ToCreature())
if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo))
return SPELL_FAILED_NOT_READY;
@@ -5893,6 +5899,9 @@ SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules()
bool Spell::CanAutoCast(Unit* target)
{
+ if (!target)
+ return (CheckPetCast(target) == SPELL_CAST_OK);
+
ObjectGuid targetguid = target->GetGUID();
// check if target already has the same or a more powerful aura
@@ -5932,16 +5941,19 @@ bool Spell::CanAutoCast(Unit* target)
}
SpellCastResult result = CheckPetCast(target);
-
if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
{
+ // do not check targets for ground-targeted spells (we target them on top of the intended target anyway)
+ if (GetSpellInfo()->ExplicitTargetMask & TARGET_FLAG_DEST_LOCATION)
+ return true;
SelectSpellTargets();
//check if among target units, our WANTED target is as well (->only self cast spells return false)
for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
if (ihit->targetGUID == targetguid)
return true;
}
- return false; //target invalid
+ // either the cast failed or the intended target wouldn't be hit
+ return false;
}
SpellCastResult Spell::CheckRange(bool strict)