Files
CypherCore/Source/Game/AI/PlayerAI/PlayerAI.cs
T

1393 lines
68 KiB
C#

/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* 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 3 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/>.
*/
using Framework.Constants;
using Game.Entities;
using Game.Spells;
using System;
using System.Collections.Generic;
namespace Game.AI
{
public struct Spells
{
/* Generic */
public const uint AutoShot = 75;
public const uint Shoot = 3018;
public const uint Throw = 2764;
public const uint Wand = 5019;
/* Warrior - Generic */
public const uint BattleStance = 2457;
public const uint BerserkerStance = 2458;
public const uint DefensiveStance = 71;
public const uint Charge = 11578;
public const uint Intercept = 20252;
public const uint EnragedRegen = 55694;
public const uint IntimidatingShout = 5246;
public const uint Pummel = 6552;
public const uint ShieldBash = 72;
public const uint Bloodrage = 2687;
/* Warrior - Arms */
public const uint SweepingStrikes = 12328;
public const uint MortalStrike = 12294;
public const uint Bladestorm = 46924;
public const uint Rend = 47465;
public const uint Retaliation = 20230;
public const uint ShatteringThrow = 64382;
public const uint ThunderClap = 47502;
/* Warrior - Fury */
public const uint DeathWish = 12292;
public const uint Bloodthirst = 23881;
public const uint PassiveTitansGrip = 46917;
public const uint DemoShout = 47437;
public const uint Execute = 47471;
public const uint HeroicFury = 60970;
public const uint Recklessness = 1719;
public const uint PiercingHowl = 12323;
/* Warrior - Protection */
public const uint Vigilance = 50720;
public const uint Devastate = 20243;
public const uint Shockwave = 46968;
public const uint ConcussionBlow = 12809;
public const uint Disarm = 676;
public const uint LastStand = 12975;
public const uint ShieldBlock = 2565;
public const uint ShieldSlam = 47488;
public const uint ShieldWall = 871;
public const uint Reflection = 23920;
/* Paladin - Generic */
public const uint PalAuraMastery = 31821;
public const uint LayOnHands = 48788;
public const uint BlessingOfMight = 48932;
public const uint AvengingWrath = 31884;
public const uint DivineProtection = 498;
public const uint DivineShield = 642;
public const uint HammerOfJustice = 10308;
public const uint HandOfFreedom = 1044;
public const uint HandOfProtection = 10278;
public const uint HandOfSacrifice = 6940;
/* Paladin - Holy*/
public const uint PassiveIllumination = 20215;
public const uint HolyShock = 20473;
public const uint BeaconOfLight = 53563;
public const uint Consecration = 48819;
public const uint FlashOfLight = 48785;
public const uint HolyLight = 48782;
public const uint DivineFavor = 20216;
public const uint DivineIllumination = 31842;
/* Paladin - Protection */
public const uint BlessOfSanc = 20911;
public const uint HolyShield = 20925;
public const uint AvengersShield = 48827;
public const uint DivineSacrifice = 64205;
public const uint HammerOfRighteous = 53595;
public const uint RighteousFury = 25780;
public const uint ShieldOfRighteous = 61411;
/* Paladin - Retribution */
public const uint SealOfCommand = 20375;
public const uint CrusaderStrike = 35395;
public const uint DivineStorm = 53385;
public const uint Judgement = 20271;
public const uint HammerOfWrath = 48806;
/* Hunter - Generic */
public const uint Deterrence = 19263;
public const uint ExplosiveTrap = 49067;
public const uint FreezingArrow = 60192;
public const uint RapidFire = 3045;
public const uint KillShot = 61006;
public const uint MultiShot = 49048;
public const uint ViperSting = 3034;
/* Hunter - Beast Mastery */
public const uint BestialWrath = 19574;
public const uint PassiveBeastWithin = 34692;
public const uint PassiveBeastMastery = 53270;
/* Hunter - Marksmanship */
public const uint AimedShot = 19434;
public const uint PassiveTrueshotAura = 19506;
public const uint ChimeraShot = 53209;
public const uint ArcaneShot = 49045;
public const uint SteadyShot = 49052;
public const uint Readiness = 23989;
public const uint SilencingShot = 34490;
/* Hunter - Survival */
public const uint PassiveLockAndLoad = 56344;
public const uint WyvernSting = 19386;
public const uint ExplosiveShot = 53301;
public const uint BlackArrow = 3674;
/* Rogue - Generic */
public const uint Dismantle = 51722;
public const uint Evasion = 26669;
public const uint Kick = 1766;
public const uint Vanish = 26889;
public const uint Blind = 2094;
public const uint CloakOfShadows = 31224;
/* Rogue - Assassination */
public const uint ColdBlood = 14177;
public const uint Mutilate = 1329;
public const uint HungerForBlood = 51662;
public const uint Envenom = 57993;
/* Rogue - Combat */
public const uint SinisterStrike = 48637;
public const uint BladeFlurry = 13877;
public const uint AdrenalineRush = 13750;
public const uint KillingSpree = 51690;
public const uint Eviscerate = 48668;
/* Rogue - Sublety */
public const uint Hemorrhage = 16511;
public const uint Premeditation = 14183;
public const uint ShadowDance = 51713;
public const uint Preparation = 14185;
public const uint Shadowstep = 36554;
/* Priest - Generic */
public const uint FearWard = 6346;
public const uint PowerWordFort = 48161;
public const uint DivineSpirit = 48073;
public const uint ShadowProtection = 48169;
public const uint DivineHymn = 64843;
public const uint HymnOfHope = 64901;
public const uint ShadowWordDeath = 48158;
public const uint PsychicScream = 10890;
/* Priest - Discipline */
public const uint PassiveSoulWarding = 63574;
public const uint PowerInfusion = 10060;
public const uint Penance = 47540;
public const uint PainSuppression = 33206;
public const uint InnerFocus = 14751;
public const uint PowerWordShield = 48066;
/* Priest - Holy */
public const uint PassiveSpiritRedemption = 20711;
public const uint DesperatePrayer = 19236;
public const uint GuardianSpirit = 47788;
public const uint FlashHeal = 48071;
public const uint Renew = 48068;
/* Priest - Shadow */
public const uint VampiricEmbrace = 15286;
public const uint Shadowform = 15473;
public const uint VampiricTouch = 34914;
public const uint MindFlay = 15407;
public const uint MindBlast = 48127;
public const uint ShadowWordPain = 48125;
public const uint DevouringPlague = 48300;
public const uint Dispersion = 47585;
/* Death Knight - Generic */
public const uint DeathGrip = 49576;
public const uint Strangulate = 47476;
public const uint EmpowerRuneWeap = 47568;
public const uint IcebornFortitude = 48792;
public const uint AntiMagicShell = 48707;
public const uint DeathCoilDk = 49895;
public const uint MindFreeze = 47528;
public const uint IcyTouch = 49909;
public const uint AuraFrostFever = 55095;
public const uint PlagueStrike = 49921;
public const uint AuraBloodPlague = 55078;
public const uint Pestilence = 50842;
/* Death Knight - Blood */
public const uint RuneTap = 48982;
public const uint Hysteria = 49016;
public const uint HeartStrike = 55050;
public const uint DeathStrike = 49924;
public const uint BloodStrike = 49930;
public const uint MarkOfBlood = 49005;
public const uint VampiricBlood = 55233;
/* Death Knight - Frost */
public const uint PassiveIcyTalons = 50887;
public const uint FrostStrike = 49143;
public const uint HowlingBlast = 49184;
public const uint UnbreakableArmor = 51271;
public const uint Obliterate = 51425;
public const uint Deathchill = 49796;
/* Death Knight - Unholy */
public const uint PassiveUnholyBlight = 49194;
public const uint PassiveMasterOfGhoul = 52143;
public const uint ScourgeStrike = 55090;
public const uint DeathAndDecay = 49938;
public const uint AntiMagicZone = 51052;
public const uint SummonGargoyle = 49206;
/* Shaman - Generic */
public const uint Heroism = 32182;
public const uint Bloodlust = 2825;
public const uint GroundingTotem = 8177;
/* Shaman - Elemental*/
public const uint PassiveElementalFocus = 16164;
public const uint TotemOfWrath = 30706;
public const uint Thunderstorm = 51490;
public const uint LightningBolt = 49238;
public const uint EarthShock = 49231;
public const uint FlameShock = 49233;
public const uint LavaBurst = 60043;
public const uint ChainLightning = 49271;
public const uint ElementalMastery = 16166;
/* Shaman - Enhancement */
public const uint PassiveSpiritWeapons = 16268;
public const uint LavaLash = 60103;
public const uint FeralSpirit = 51533;
public const uint AuraMaelstromWeapon = 53817;
public const uint Stormstrike = 17364;
public const uint ShamanisticRage = 30823;
/* Shaman - Restoration*/
public const uint ShaNatureSwift = 591;
public const uint ManaTideTotem = 590;
public const uint EarthShield = 49284;
public const uint Riptide = 61295;
public const uint HealingWave = 49273;
public const uint LesserHealWave = 49276;
public const uint TidalForce = 55198;
/* Mage - Generic */
public const uint DampenMagic = 43015;
public const uint Evocation = 12051;
public const uint ManaShield = 43020;
public const uint MirrorImage = 55342;
public const uint Spellsteal = 30449;
public const uint Counterspell = 2139;
public const uint IceBlock = 45438;
/* Mage - Arcane */
public const uint FocusMagic = 54646;
public const uint ArcanePower = 12042;
public const uint ArcaneBarrage = 44425;
public const uint ArcaneBlast = 42897;
public const uint AuraArcaneBlast = 36032;
public const uint ArcaneMissiles = 42846;
public const uint PresenceOfMind = 12043;
/* Mage - Fire */
public const uint Pyroblast = 11366;
public const uint Combustion = 11129;
public const uint LivingBomb = 44457;
public const uint Fireball = 42833;
public const uint FireBlast = 42873;
public const uint DragonsBreath = 31661;
public const uint BlastWave = 11113;
/* Mage - Frost */
public const uint IcyVeins = 12472;
public const uint IceBarrier = 11426;
public const uint DeepFreeze = 44572;
public const uint FrostNova = 42917;
public const uint Frostbolt = 42842;
public const uint ColdSnap = 11958;
public const uint IceLance = 42914;
/* Warlock - Generic */
public const uint Fear = 6215;
public const uint HowlOfTerror = 17928;
public const uint Corruption = 47813;
public const uint DeathCoilW = 47860;
public const uint ShadowBolt = 47809;
public const uint Incinerate = 47838;
public const uint Immolate = 47811;
public const uint SeedOfCorruption = 47836;
/* Warlock - Affliction */
public const uint PassiveSiphonLife = 63108;
public const uint UnstableAffliction = 30108;
public const uint Haunt = 48181;
public const uint CurseOfAgony = 47864;
public const uint DrainSoul = 47855;
/* Warlock - Demonology */
public const uint SoulLink = 19028;
public const uint DemonicEmpowerment = 47193;
public const uint Metamorphosis = 59672;
public const uint ImmolationAura = 50589;
public const uint DemonCharge = 54785;
public const uint AuraDecimation = 63167;
public const uint AuraMoltenCore = 71165;
public const uint SoulFire = 47825;
/* Warlock - Destruction */
public const uint Shadowburn = 17877;
public const uint Conflagrate = 17962;
public const uint ChaosBolt = 50796;
public const uint Shadowfury = 47847;
/* Druid - Generic */
public const uint Barkskin = 22812;
public const uint Innervate = 29166;
/* Druid - Balance */
public const uint InsectSwarm = 5570;
public const uint MoonkinForm = 24858;
public const uint Starfall = 48505;
public const uint Typhoon = 61384;
public const uint AuraEclipseLunar = 48518;
public const uint Moonfire = 48463;
public const uint Starfire = 48465;
public const uint Wrath = 48461;
/* Druid - Feral */
public const uint CatForm = 768;
public const uint SurvivalInstincts = 61336;
public const uint Mangle = 33917;
public const uint Berserk = 50334;
public const uint MangleCat = 48566;
public const uint FeralChargeCat = 49376;
public const uint Rake = 48574;
public const uint Rip = 49800;
public const uint SavageRoar = 52610;
public const uint TigerFury = 50213;
public const uint Claw = 48570;
public const uint Dash = 33357;
public const uint Maim = 49802;
/* Druid - Restoration */
public const uint Swiftmend = 18562;
public const uint TreeOfLife = 33891;
public const uint WildGrowth = 48438;
public const uint NatureSwiftness = 17116;
public const uint Tranquility = 48447;
public const uint Nourish = 50464;
public const uint HealingTouch = 48378;
public const uint Rejuvenation = 48441;
public const uint Regrowth = 48443;
public const uint Lifebloom = 48451;
}
public class PlayerAI : UnitAI
{
protected new Player me;
uint _selfSpec;
bool _isSelfHealer;
bool _isSelfRangedAttacker;
public PlayerAI(Player player) : base(player)
{
me = player;
_selfSpec = player.GetPrimarySpecialization();
_isSelfHealer = IsPlayerHealer(player);
_isSelfRangedAttacker = IsPlayerRangedAttacker(player);
}
bool IsPlayerHealer(Player who)
{
if (!who)
return false;
return who.GetClass() switch
{
Class.Paladin => who.GetPrimarySpecialization() == (uint)TalentSpecialization.PaladinHoly,
Class.Priest => who.GetPrimarySpecialization() == (uint)TalentSpecialization.PriestDiscipline || who.GetPrimarySpecialization() == (uint)TalentSpecialization.PriestHoly,
Class.Shaman => who.GetPrimarySpecialization() == (uint)TalentSpecialization.ShamanRestoration,
Class.Monk => who.GetPrimarySpecialization() == (uint)TalentSpecialization.MonkMistweaver,
Class.Druid => who.GetPrimarySpecialization() == (uint)TalentSpecialization.DruidRestoration,
_ => false,
};
}
bool IsPlayerRangedAttacker(Player who)
{
if (!who)
return false;
switch (who.GetClass())
{
case Class.Warrior:
case Class.Paladin:
case Class.Rogue:
case Class.Deathknight:
default:
return false;
case Class.Mage:
case Class.Warlock:
return true;
case Class.Hunter:
{
// check if we have a ranged weapon equipped
Item rangedSlot = who.GetItemByPos(InventorySlots.Bag0, EquipmentSlot.Ranged);
ItemTemplate rangedTemplate = rangedSlot ? rangedSlot.GetTemplate() : null;
if (rangedTemplate != null)
if (Convert.ToBoolean((1 << (int)rangedTemplate.GetSubClass()) & (int)ItemSubClassWeapon.MaskRanged))
return true;
return false;
}
case Class.Priest:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.PriestShadow;
case Class.Shaman:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.ShamanElemental;
case Class.Druid:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.DruidBalance;
}
}
Tuple<Spell, Unit> VerifySpellCast(uint spellId, Unit target)
{
// Find highest spell rank that we know
uint knownRank, nextRank;
if (me.HasSpell(spellId))
{
// this will save us some lookups if the player has the highest rank (expected case)
knownRank = spellId;
nextRank = Global.SpellMgr.GetNextSpellInChain(spellId);
}
else
{
knownRank = 0;
nextRank = Global.SpellMgr.GetFirstSpellInChain(spellId);
}
while (nextRank != 0 && me.HasSpell(nextRank))
{
knownRank = nextRank;
nextRank = Global.SpellMgr.GetNextSpellInChain(knownRank);
}
if (knownRank == 0)
return null;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(knownRank, me.GetMap().GetDifficultyID());
if (spellInfo == null)
return null;
if (me.GetSpellHistory().HasGlobalCooldown(spellInfo))
return null;
Spell spell = new(me, spellInfo, TriggerCastFlags.None);
if (spell.CanAutoCast(target))
return Tuple.Create(spell, target);
return null;
}
public Tuple<Spell, Unit> VerifySpellCast(uint spellId, SpellTarget target)
{
Unit pTarget = null;
switch (target)
{
case SpellTarget.None:
break;
case SpellTarget.Victim:
pTarget = me.GetVictim();
if (!pTarget)
return null;
break;
case SpellTarget.Charmer:
pTarget = me.GetCharmer();
if (!pTarget)
return null;
break;
case SpellTarget.Self:
pTarget = me;
break;
}
return VerifySpellCast(spellId, pTarget);
}
public Tuple<Spell, Unit> SelectSpellCast(List<Tuple<Tuple<Spell, Unit>, uint>> spells)
{
if (spells.Empty())
return null;
uint totalWeights = 0;
foreach (var wSpell in spells)
totalWeights += wSpell.Item2;
Tuple<Spell, Unit> selected = null;
uint randNum = RandomHelper.URand(0, totalWeights - 1);
foreach (var wSpell in spells)
{
if (selected != null)
{
//delete wSpell.first.first;
continue;
}
if (randNum < wSpell.Item2)
selected = wSpell.Item1;
else
{
randNum -= wSpell.Item2;
//delete wSpell.first.first;
}
}
spells.Clear();
return selected;
}
public void VerifyAndPushSpellCast<T>(List<Tuple<Tuple<Spell, Unit>, uint>> spells, uint spellId, T target, uint weight) where T : Unit
{
Tuple<Spell, Unit> spell = VerifySpellCast(spellId, target);
if (spell != null)
spells.Add(Tuple.Create(spell, weight));
}
public void DoCastAtTarget(Tuple<Spell, Unit> spell)
{
SpellCastTargets targets = new();
targets.SetUnitTarget(spell.Item2);
spell.Item1.Prepare(targets);
}
void DoRangedAttackIfReady()
{
if (me.HasUnitState(UnitState.Casting))
return;
if (!me.IsAttackReady(WeaponAttackType.RangedAttack))
return;
Unit victim = me.GetVictim();
if (!victim)
return;
uint rangedAttackSpell = 0;
Item rangedItem = me.GetItemByPos(InventorySlots.Bag0, EquipmentSlot.Ranged);
ItemTemplate rangedTemplate = rangedItem ? rangedItem.GetTemplate() : null;
if (rangedTemplate != null)
{
switch ((ItemSubClassWeapon)rangedTemplate.GetSubClass())
{
case ItemSubClassWeapon.Bow:
case ItemSubClassWeapon.Gun:
case ItemSubClassWeapon.Crossbow:
rangedAttackSpell = Spells.Shoot;
break;
case ItemSubClassWeapon.Thrown:
rangedAttackSpell = Spells.Throw;
break;
case ItemSubClassWeapon.Wand:
rangedAttackSpell = Spells.Wand;
break;
}
}
if (rangedAttackSpell == 0)
return;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(rangedAttackSpell, me.GetMap().GetDifficultyID());
if (spellInfo == null)
return;
Spell spell = new(me, spellInfo, TriggerCastFlags.CastDirectly);
if (spell.CheckPetCast(victim) != SpellCastResult.SpellCastOk)
return;
SpellCastTargets targets = new();
targets.SetUnitTarget(victim);
spell.Prepare(targets);
me.ResetAttackTimer(WeaponAttackType.RangedAttack);
}
public void DoAutoAttackIfReady()
{
if (IsRangedAttacker())
DoRangedAttackIfReady();
else
DoMeleeAttackIfReady();
}
public void CancelAllShapeshifts()
{
List<AuraEffect> shapeshiftAuras = me.GetAuraEffectsByType(AuraType.ModShapeshift);
List<Aura> removableShapeshifts = new();
foreach (AuraEffect auraEff in shapeshiftAuras)
{
Aura aura = auraEff.GetBase();
if (aura == null)
continue;
SpellInfo auraInfo = aura.GetSpellInfo();
if (auraInfo == null)
continue;
if (auraInfo.HasAttribute(SpellAttr0.CantCancel))
continue;
if (!auraInfo.IsPositive() || auraInfo.IsPassive())
continue;
removableShapeshifts.Add(aura);
}
foreach (Aura aura in removableShapeshifts)
me.RemoveOwnedAura(aura, AuraRemoveMode.Cancel);
}
public Creature GetCharmer()
{
if (me.GetCharmerGUID().IsCreature())
return ObjectAccessor.GetCreature(me, me.GetCharmerGUID());
return null;
}
// helper functions to determine player info
public bool IsHealer(Player who = null)
{
return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who);
}
public bool IsRangedAttacker(Player who = null) { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); }
public uint GetSpec(Player who = null) { return (!who || who == me) ? _selfSpec : who.GetPrimarySpecialization(); }
public void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection
public virtual Unit SelectAttackTarget() { return me.GetCharmer() ? me.GetCharmer().GetVictim() : null; }
public enum SpellTarget
{
None,
Victim,
Charmer,
Self
}
}
class SimpleCharmedPlayerAI : PlayerAI
{
const float CASTER_CHASE_DISTANCE = 28.0f;
uint _castCheckTimer;
bool _chaseCloser;
bool _forceFacing;
bool _isFollowing;
public SimpleCharmedPlayerAI(Player player) : base(player)
{
_castCheckTimer = 2500;
_chaseCloser = false;
_forceFacing = true;
}
public override bool CanAIAttack(Unit who)
{
if (!me.IsValidAttackTarget(who) || who.HasBreakableByDamageCrowdControlAura())
return false;
Unit charmer = me.GetCharmer();
if (charmer != null)
if (!charmer.IsValidAttackTarget(who))
return false;
return base.CanAIAttack(who);
}
public override Unit SelectAttackTarget()
{
Unit charmer = me.GetCharmer();
if (charmer)
{
UnitAI charmerAI = charmer.GetAI();
if (charmerAI != null)
return charmerAI.SelectTarget(SelectAggroTarget.Random, 0, new ValidTargetSelectPredicate(this));
return charmer.GetVictim();
}
return null;
}
Tuple<Spell, Unit> SelectAppropriateCastForSpec()
{
List<Tuple<Tuple<Spell, Unit>, uint>> spells = new();
/*
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 = GetCharmer())
{
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 = GetCharmer())
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 = GetCharmer())
{
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);
}
public override void UpdateAI(uint diff)
{
Creature charmer = GetCharmer();
if (!charmer)
return;
//kill self if charm aura has infinite duration
if (charmer.IsInEvadeMode())
{
var auras = me.GetAuraEffectsByType(AuraType.ModCharm);
foreach (var effect in auras)
{
if (effect.GetCasterGUID() == charmer.GetGUID() && effect.GetBase().IsPermanent())
{
me.KillSelf();
return;
}
}
}
if (charmer.IsEngaged())
{
Unit target = me.GetVictim();
if (!target || !CanAIAttack(target))
{
target = SelectAttackTarget();
if (!target || !CanAIAttack(target))
{
if (!_isFollowing)
{
_isFollowing = true;
me.AttackStop();
me.CastStop();
if (me.HasUnitState(UnitState.Chase))
me.GetMotionMaster().Remove(MovementGeneratorType.Chase);
me.GetMotionMaster().MoveFollow(charmer, SharedConst.PetFollowDist, SharedConst.PetFollowAngle);
}
return;
}
_isFollowing = false;
if (IsRangedAttacker())
{
_chaseCloser = !me.IsWithinLOSInMap(target);
if (_chaseCloser)
AttackStart(target);
else
AttackStartCaster(target, CASTER_CHASE_DISTANCE);
}
else
AttackStart(target);
_forceFacing = true;
}
if (me.IsStopped() && !me.HasUnitState(UnitState.CannotTurn))
{
float targetAngle = me.GetAbsoluteAngle(target);
if (_forceFacing || Math.Abs(me.GetOrientation() - targetAngle) > 0.4f)
{
me.SetFacingTo(targetAngle);
_forceFacing = false;
}
}
if (_castCheckTimer <= diff)
{
if (me.HasUnitState(UnitState.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);
}
}
Tuple<Spell, Unit> shouldCast = SelectAppropriateCastForSpec();
if (shouldCast != null)
DoCastAtTarget(shouldCast);
_castCheckTimer = 500;
}
}
else
_castCheckTimer -= diff;
DoAutoAttackIfReady();
}
else if (!_isFollowing)
{
_isFollowing = true;
me.AttackStop();
me.CastStop();
if (me.HasUnitState(UnitState.Chase))
me.GetMotionMaster().Remove(MovementGeneratorType.Chase);
me.GetMotionMaster().MoveFollow(charmer, SharedConst.PetFollowDist, SharedConst.PetFollowAngle);
}
}
public override void OnCharmed(bool isNew)
{
if (me.IsCharmed())
{
me.CastStop();
me.AttackStop();
if (me.GetMotionMaster().Size() <= 1) // if there is no current movement (we dont want to erase/overwrite any existing stuff)
me.GetMotionMaster().MovePoint(0, me.GetPosition(), false); // force re-sync of current position for all clients
}
else
{
me.CastStop();
me.AttackStop();
me.GetMotionMaster().Clear(MovementGeneratorPriority.Normal);
}
base.OnCharmed(isNew);
}
}
struct ValidTargetSelectPredicate : ICheck<Unit>
{
UnitAI _ai;
public ValidTargetSelectPredicate(UnitAI ai)
{
_ai = ai;
}
public bool Invoke(Unit target)
{
return _ai.CanAIAttack(target);
}
}
}