public override void UpdateAI(uint diff) { if (!UpdateVictim()) { return; } _events.Update(diff); if (me.HasUnitState(UnitState.Casting)) { return; } uint spellId = _events.ExecuteEvent(); if (spellId != 0) { DoCast(spellId); AISpellInfoType info = GetAISpellInfo(spellId, me.GetMap().GetDifficultyID()); _events.ScheduleEvent(spellId, info.cooldown + RandomHelper.Rand32() % info.cooldown); } else { DoMeleeAttackIfReady(); } }
public override void UpdateAI(uint diff) { if (!UpdateVictim()) { return; } _events.Update(diff); if (me.GetVictim().HasBreakableByDamageCrowdControlAura(me)) { me.InterruptNonMeleeSpells(false); return; } if (me.HasUnitState(UnitState.Casting)) { return; } uint spellId = _events.ExecuteEvent(); if (spellId != 0) { DoCast(spellId); uint casttime = (uint)me.GetCurrentSpellCastTime(spellId); AISpellInfoType info = GetAISpellInfo(spellId, me.GetMap().GetDifficultyID()); _events.ScheduleEvent(spellId, (casttime != 0 ? casttime : 500) + info.realCooldown); } }
public override void EnterCombat(Unit victim) { if (spells.Empty()) { return; } int spell = (int)(RandomHelper.Rand32() % spells.Count); uint count = 0; foreach (var id in spells) { AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID()); if (info.condition == AICondition.Aggro) { me.CastSpell(victim, id, false); } else if (info.condition == AICondition.Combat) { uint cooldown = info.realCooldown; if (count == spell) { DoCast(spells[spell]); cooldown += (uint)me.GetCurrentSpellCastTime(id); } _events.ScheduleEvent(id, cooldown); } } }
public override void JustDied(Unit killer) { foreach (var id in spells) { AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID()); if (info.condition == AICondition.Die) { me.CastSpell(killer, id, true); } } }
public override void EnterCombat(Unit victim) { foreach (var id in spells) { AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID()); if (info.condition == AICondition.Aggro) { me.CastSpell(victim, id, false); } else if (info.condition == AICondition.Combat) { _events.ScheduleEvent(id, info.cooldown + RandomHelper.Rand32() % info.cooldown); } } }
public override void InitializeAI() { base.InitializeAI(); m_attackDist = 30.0f; foreach (var id in spells) { AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID()); if (info.condition == AICondition.Combat && m_attackDist > info.maxRange) { m_attackDist = info.maxRange; } } if (m_attackDist == 30.0f) { m_attackDist = SharedConst.MeleeRange; } }
public static void FillAISpellInfo() { //AISpellInfo = new AISpellInfoType[spellStorage.Keys.Max() + 1]; Global.SpellMgr.ForEachSpellInfo(spellInfo => { AISpellInfoType AIInfo = new AISpellInfoType(); if (spellInfo.HasAttribute(SpellAttr0.CastableWhileDead)) { AIInfo.condition = AICondition.Die; } else if (spellInfo.IsPassive() || spellInfo.GetDuration() == -1) { AIInfo.condition = AICondition.Aggro; } else { AIInfo.condition = AICondition.Combat; } if (AIInfo.cooldown < spellInfo.RecoveryTime) { AIInfo.cooldown = spellInfo.RecoveryTime; } if (spellInfo.GetMaxRange(false) == 0) { if (AIInfo.target < AITarget.Self) { AIInfo.target = AITarget.Self; } } else { foreach (SpellEffectInfo effect in spellInfo.GetEffects()) { if (effect == null) { continue; } var targetType = effect.TargetA.GetTarget(); if (targetType == Targets.UnitTargetEnemy || targetType == Targets.DestTargetEnemy) { if (AIInfo.target < AITarget.Victim) { AIInfo.target = AITarget.Victim; } } else if (targetType == Targets.UnitDestAreaEnemy) { if (AIInfo.target < AITarget.Enemy) { AIInfo.target = AITarget.Enemy; } } if (effect.Effect == SpellEffectName.ApplyAura) { if (targetType == Targets.UnitTargetEnemy) { if (AIInfo.target < AITarget.Debuff) { AIInfo.target = AITarget.Debuff; } } else if (spellInfo.IsPositive()) { if (AIInfo.target < AITarget.Buff) { AIInfo.target = AITarget.Buff; } } } } } AIInfo.realCooldown = spellInfo.RecoveryTime + spellInfo.StartRecoveryTime; AIInfo.maxRange = spellInfo.GetMaxRange(false) * 3 / 4; AIInfo.Effects = 0; AIInfo.Targets = 0; foreach (SpellEffectInfo effect in spellInfo.GetEffects()) { if (effect == null) { continue; } // Spell targets self. if (effect.TargetA.GetTarget() == Targets.UnitCaster) { AIInfo.Targets |= 1 << ((int)SelectTargetType.Self - 1); } // Spell targets a single enemy. if (effect.TargetA.GetTarget() == Targets.UnitTargetEnemy || effect.TargetA.GetTarget() == Targets.DestTargetEnemy) { AIInfo.Targets |= 1 << ((int)SelectTargetType.SingleEnemy - 1); } // Spell targets AoE at enemy. if (effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || effect.TargetA.GetTarget() == Targets.SrcCaster || effect.TargetA.GetTarget() == Targets.DestDynobjEnemy) { AIInfo.Targets |= 1 << ((int)SelectTargetType.AoeEnemy - 1); } // Spell targets an enemy. if (effect.TargetA.GetTarget() == Targets.UnitTargetEnemy || effect.TargetA.GetTarget() == Targets.DestTargetEnemy || effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy || effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy || effect.TargetA.GetTarget() == Targets.SrcCaster || effect.TargetA.GetTarget() == Targets.DestDynobjEnemy) { AIInfo.Targets |= 1 << ((int)SelectTargetType.AnyEnemy - 1); } // Spell targets a single friend (or self). if (effect.TargetA.GetTarget() == Targets.UnitCaster || effect.TargetA.GetTarget() == Targets.UnitTargetAlly || effect.TargetA.GetTarget() == Targets.UnitTargetParty) { AIInfo.Targets |= 1 << ((int)SelectTargetType.SingleFriend - 1); } // Spell targets AoE friends. if (effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty || effect.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty || effect.TargetA.GetTarget() == Targets.SrcCaster) { AIInfo.Targets |= 1 << ((int)SelectTargetType.AoeFriend - 1); } // Spell targets any friend (or self). if (effect.TargetA.GetTarget() == Targets.UnitCaster || effect.TargetA.GetTarget() == Targets.UnitTargetAlly || effect.TargetA.GetTarget() == Targets.UnitTargetParty || effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty || effect.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty || effect.TargetA.GetTarget() == Targets.SrcCaster) { AIInfo.Targets |= 1 << ((int)SelectTargetType.AnyFriend - 1); } // Make sure that this spell includes a damage effect. if (effect.Effect == SpellEffectName.SchoolDamage || effect.Effect == SpellEffectName.Instakill || effect.Effect == SpellEffectName.EnvironmentalDamage || effect.Effect == SpellEffectName.HealthLeech) { AIInfo.Effects |= 1 << ((int)SelectEffect.Damage - 1); } // Make sure that this spell includes a healing effect (or an apply aura with a periodic heal). if (effect.Effect == SpellEffectName.Heal || effect.Effect == SpellEffectName.HealMaxHealth || effect.Effect == SpellEffectName.HealMechanical || (effect.Effect == SpellEffectName.ApplyAura && effect.ApplyAuraName == AuraType.PeriodicHeal)) { AIInfo.Effects |= 1 << ((int)SelectEffect.Healing - 1); } // Make sure that this spell applies an aura. if (effect.Effect == SpellEffectName.ApplyAura) { AIInfo.Effects |= 1 << ((int)SelectEffect.Aura - 1); } } AISpellInfo[(spellInfo.Id, spellInfo.Difficulty)] = AIInfo; });
public void DoCast(uint spellId) { Unit target = null; AISpellInfoType info = GetAISpellInfo(spellId, me.GetMap().GetDifficultyID()); switch (info.target) { default: case AITarget.Self: target = me; break; case AITarget.Victim: target = me.GetVictim(); break; case AITarget.Enemy: { var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID()); if (spellInfo != null) { bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers); target = SelectTarget(SelectAggroTarget.Random, 0, spellInfo.GetMaxRange(false), playerOnly); } break; } case AITarget.Ally: case AITarget.Buff: target = me; break; case AITarget.Debuff: { var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID()); if (spellInfo != null) { bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers); float range = spellInfo.GetMaxRange(false); DefaultTargetSelector targetSelector = new DefaultTargetSelector(me, range, playerOnly, true, -(int)spellId); if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim) && targetSelector.Check(me.GetVictim())) { target = me.GetVictim(); } else { target = SelectTarget(SelectAggroTarget.Random, 0, targetSelector); } } break; } } if (target != null) { me.CastSpell(target, spellId, false); } }
//Returns spells that meet the specified criteria from the creatures spell list public SpellInfo SelectSpell(Unit target, SpellSchoolMask school, Mechanics mechanic, SelectTargetType targets, float rangeMin, float rangeMax, SelectEffect effect) { //No target so we can't cast if (target == null) { return(null); } //Silenced so we can't cast if (me.HasUnitFlag(UnitFlags.Silenced)) { return(null); } //Using the extended script system we first create a list of viable spells SpellInfo[] apSpell = new SpellInfo[SharedConst.MaxCreatureSpells]; uint spellCount = 0; //Check if each spell is viable(set it to null if not) for (uint i = 0; i < SharedConst.MaxCreatureSpells; i++) { SpellInfo tempSpell = Global.SpellMgr.GetSpellInfo(me.m_spells[i], me.GetMap().GetDifficultyID()); AISpellInfoType aiSpell = GetAISpellInfo(me.m_spells[i], me.GetMap().GetDifficultyID()); //This spell doesn't exist if (tempSpell == null) { continue; } // Targets and Effects checked first as most used restrictions //Check the spell targets if specified if (targets != 0 && !Convert.ToBoolean(aiSpell.Targets & (1 << ((int)targets - 1)))) { continue; } //Check the type of spell if we are looking for a specific spell type if (effect != 0 && !Convert.ToBoolean(aiSpell.Effects & (1 << ((int)effect - 1)))) { continue; } //Check for school if specified if (school != 0 && (tempSpell.SchoolMask & school) == 0) { continue; } //Check for spell mechanic if specified if (mechanic != 0 && tempSpell.Mechanic != mechanic) { continue; } //Check if the spell meets our range requirements if (rangeMin != 0 && me.GetSpellMinRangeForTarget(target, tempSpell) < rangeMin) { continue; } if (rangeMax != 0 && me.GetSpellMaxRangeForTarget(target, tempSpell) > rangeMax) { continue; } //Check if our target is in range if (me.IsWithinDistInMap(target, me.GetSpellMinRangeForTarget(target, tempSpell)) || !me.IsWithinDistInMap(target, me.GetSpellMaxRangeForTarget(target, tempSpell))) { continue; } //All good so lets add it to the spell list apSpell[spellCount] = tempSpell; ++spellCount; } //We got our usable spells so now lets randomly pick one if (spellCount == 0) { return(null); } return(apSpell[RandomHelper.IRand(0, (int)(spellCount - 1))]); }
public static void FillAISpellInfo() { var spellStorage = Global.SpellMgr.GetSpellInfoStorage(); AISpellInfo = new AISpellInfoType[spellStorage.Keys.Max() + 1]; foreach (var spellInfo in spellStorage.Values) { AISpellInfoType AIInfo = AISpellInfo[spellInfo.Id]; if (spellInfo.HasAttribute(SpellAttr0.CastableWhileDead)) { AIInfo.condition = AICondition.Die; } else if (spellInfo.IsPassive() || spellInfo.GetDuration() == -1) { AIInfo.condition = AICondition.Aggro; } else { AIInfo.condition = AICondition.Combat; } if (AIInfo.cooldown < spellInfo.RecoveryTime) { AIInfo.cooldown = spellInfo.RecoveryTime; } if (spellInfo.GetMaxRange(false) == 0) { if (AIInfo.target < AITarget.Self) { AIInfo.target = AITarget.Self; } } else { foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None)) { if (effect == null) { continue; } var targetType = effect.TargetA.GetTarget(); if (targetType == Targets.UnitEnemy || targetType == Targets.DestEnemy) { if (AIInfo.target < AITarget.Victim) { AIInfo.target = AITarget.Victim; } } else if (targetType == Targets.UnitDestAreaEnemy) { if (AIInfo.target < AITarget.Enemy) { AIInfo.target = AITarget.Enemy; } } if (effect.Effect == SpellEffectName.ApplyAura) { if (targetType == Targets.UnitEnemy) { if (AIInfo.target < AITarget.Debuff) { AIInfo.target = AITarget.Debuff; } } else if (spellInfo.IsPositive()) { if (AIInfo.target < AITarget.Buff) { AIInfo.target = AITarget.Buff; } } } } } AIInfo.realCooldown = spellInfo.RecoveryTime + spellInfo.StartRecoveryTime; AIInfo.maxRange = spellInfo.GetMaxRange(false) * 3 / 4; } }