c3dc452024
Port From (https://github.com/TrinityCore/TrinityCore/commit/6814b4d32b283c3a1b676d2f13953894a9f1ff7a)
1393 lines
68 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|