Core: SOme code cleanup, more to follow.

This commit is contained in:
hondacrx
2021-03-20 22:48:48 -04:00
parent 62f554f2e0
commit 62ec699ec6
318 changed files with 5080 additions and 5125 deletions
+2 -2
View File
@@ -21,6 +21,8 @@ namespace Game.AI
{
public class AreaTriggerAI
{
protected AreaTrigger at;
public AreaTriggerAI(AreaTrigger a)
{
at = a;
@@ -49,8 +51,6 @@ namespace Game.AI
// Called when the AreaTrigger is removed
public virtual void OnRemove() { }
protected AreaTrigger at;
}
class NullAreaTriggerAI : AreaTriggerAI
+53 -53
View File
@@ -23,13 +23,15 @@ namespace Game.AI
{
public class CombatAI : CreatureAI
{
public List<uint> Spells = new();
public CombatAI(Creature c) : base(c) { }
public override void InitializeAI()
{
for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i)
if (me.m_spells[i] != 0 && Global.SpellMgr.HasSpellInfo(me.m_spells[i], me.GetMap().GetDifficultyID()))
spells.Add(me.m_spells[i]);
Spells.Add(me.m_spells[i]);
base.InitializeAI();
}
@@ -41,7 +43,7 @@ namespace Game.AI
public override void JustDied(Unit killer)
{
foreach (var id in spells)
foreach (var id in Spells)
{
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Die)
@@ -51,7 +53,7 @@ namespace Game.AI
public override void EnterCombat(Unit victim)
{
foreach (var id in spells)
foreach (var id in Spells)
{
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Aggro)
@@ -86,8 +88,6 @@ namespace Game.AI
{
_events.RescheduleEvent(spellId, unTimeMs);
}
public List<uint> spells = new List<uint>();
}
public class AggressorAI : CreatureAI
@@ -105,41 +105,43 @@ namespace Game.AI
public class CasterAI : CombatAI
{
float _attackDist;
public CasterAI(Creature c)
: base(c)
{
m_attackDist = SharedConst.MeleeRange;
_attackDist = SharedConst.MeleeRange;
}
public override void InitializeAI()
{
base.InitializeAI();
m_attackDist = 30.0f;
foreach (var id in spells)
_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 (info.condition == AICondition.Combat && _attackDist > info.maxRange)
_attackDist = info.maxRange;
}
if (m_attackDist == 30.0f)
m_attackDist = SharedConst.MeleeRange;
if (_attackDist == 30.0f)
_attackDist = SharedConst.MeleeRange;
}
public override void AttackStart(Unit victim)
{
AttackStartCaster(victim, m_attackDist);
AttackStartCaster(victim, _attackDist);
}
public override void EnterCombat(Unit victim)
{
if (spells.Empty())
if (Spells.Empty())
return;
int spell = (int)(RandomHelper.Rand32() % spells.Count);
int spell = (int)(RandomHelper.Rand32() % Spells.Count);
uint count = 0;
foreach (var id in spells)
foreach (var id in Spells)
{
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Aggro)
@@ -149,7 +151,7 @@ namespace Game.AI
uint cooldown = info.realCooldown;
if (count == spell)
{
DoCast(spells[spell]);
DoCast(Spells[spell]);
cooldown += (uint)me.GetCurrentSpellCastTime(id);
}
_events.ScheduleEvent(id, cooldown);
@@ -182,23 +184,22 @@ namespace Game.AI
_events.ScheduleEvent(spellId, (casttime != 0 ? casttime : 500) + info.realCooldown);
}
}
float m_attackDist;
}
public class ArcherAI : CreatureAI
{
public ArcherAI(Creature c)
: base(c)
float _minRange;
public ArcherAI(Creature c) : base(c)
{
if (me.m_spells[0] == 0)
Log.outError(LogFilter.ScriptsAi, "ArcherAI set for creature (entry = {0}) with spell1=0. AI will do nothing", me.GetEntry());
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0], me.GetMap().GetDifficultyID());
m_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
if (m_minRange == 0)
m_minRange = SharedConst.MeleeRange;
if (_minRange == 0)
_minRange = SharedConst.MeleeRange;
me.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
me.m_SightDistance = me.m_CombatDistance;
}
@@ -208,7 +209,7 @@ namespace Game.AI
if (who == null)
return;
if (me.IsWithinCombatRange(who, m_minRange))
if (me.IsWithinCombatRange(who, _minRange))
{
if (me.Attack(who, true) && !who.IsFlying())
me.GetMotionMaster().MoveChase(who);
@@ -222,22 +223,23 @@ namespace Game.AI
if (who.IsFlying())
me.GetMotionMaster().MoveIdle();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
if (!me.IsWithinCombatRange(me.GetVictim(), m_minRange))
if (!me.IsWithinCombatRange(me.GetVictim(), _minRange))
DoSpellAttackIfReady(me.m_spells[0]);
else
DoMeleeAttackIfReady();
}
float m_minRange;
}
public class TurretAI : CreatureAI
{
float _minRange;
public TurretAI(Creature c)
: base(c)
{
@@ -245,7 +247,7 @@ namespace Game.AI
Log.outError(LogFilter.Server, "TurretAI set for creature (entry = {0}) with spell1=0. AI will do nothing", me.GetEntry());
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0], me.GetMap().GetDifficultyID());
m_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
me.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
me.m_SightDistance = me.m_CombatDistance;
}
@@ -254,7 +256,7 @@ namespace Game.AI
{
// todo use one function to replace it
if (!me.IsWithinCombatRange(me.GetVictim(), me.m_CombatDistance)
|| (m_minRange != 0 && me.IsWithinCombatRange(me.GetVictim(), m_minRange)))
|| (_minRange != 0 && me.IsWithinCombatRange(me.GetVictim(), _minRange)))
return false;
return true;
}
@@ -272,8 +274,6 @@ namespace Game.AI
DoSpellAttackIfReady(me.m_spells[0]);
}
float m_minRange;
}
public class VehicleAI : CreatureAI
@@ -281,27 +281,32 @@ namespace Game.AI
const int VEHICLE_CONDITION_CHECK_TIME = 1000;
const int VEHICLE_DISMISS_TIME = 5000;
bool _hasConditions;
uint _conditionsTimer;
bool _doDismiss;
uint _dismissTimer;
public VehicleAI(Creature creature) : base(creature)
{
m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
_conditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
LoadConditions();
m_DoDismiss = false;
m_DismissTimer = VEHICLE_DISMISS_TIME;
_doDismiss = false;
_dismissTimer = VEHICLE_DISMISS_TIME;
}
public override void UpdateAI(uint diff)
{
CheckConditions(diff);
if (m_DoDismiss)
if (_doDismiss)
{
if (m_DismissTimer < diff)
if (_dismissTimer < diff)
{
m_DoDismiss = false;
_doDismiss = false;
me.DespawnOrUnsummon();
}
else
m_DismissTimer -= diff;
_dismissTimer -= diff;
}
}
@@ -311,25 +316,25 @@ namespace Game.AI
public override void OnCharmed(bool apply)
{
if (!me.GetVehicleKit().IsVehicleInUse() && !apply && m_HasConditions)//was used and has conditions
m_DoDismiss = true;//needs reset
if (!me.GetVehicleKit().IsVehicleInUse() && !apply && _hasConditions)//was used and has conditions
_doDismiss = true;//needs reset
else if (apply)
m_DoDismiss = false;//in use again
_doDismiss = false;//in use again
m_DismissTimer = VEHICLE_DISMISS_TIME;//reset timer
_dismissTimer = VEHICLE_DISMISS_TIME;//reset timer
}
void LoadConditions()
{
m_HasConditions = Global.ConditionMgr.HasConditionsForNotGroupedEntry(ConditionSourceType.CreatureTemplateVehicle, me.GetEntry());
_hasConditions = Global.ConditionMgr.HasConditionsForNotGroupedEntry(ConditionSourceType.CreatureTemplateVehicle, me.GetEntry());
}
void CheckConditions(uint diff)
{
if (!m_HasConditions)
if (!_hasConditions)
return;
if (m_ConditionsTimer <= diff)
if (_conditionsTimer <= diff)
{
Vehicle vehicleKit = me.GetVehicleKit();
if (vehicleKit)
@@ -352,16 +357,11 @@ namespace Game.AI
}
}
m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
_conditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
}
else
m_ConditionsTimer -= diff;
_conditionsTimer -= diff;
}
bool m_HasConditions;
uint m_ConditionsTimer;
bool m_DoDismiss;
uint m_DismissTimer;
}
public class ReactorAI : CreatureAI
+33 -32
View File
@@ -27,10 +27,20 @@ namespace Game.AI
{
public class CreatureAI : UnitAI
{
bool _moveInLineOfSightLocked;
List<AreaBoundary> _boundary = new();
bool _negateBoundary;
protected new Creature me;
protected EventMap _events = new();
protected TaskScheduler _scheduler = new();
protected InstanceScript _instanceScript;
public CreatureAI(Creature _creature) : base(_creature)
{
me = _creature;
MoveInLineOfSight_locked = false;
_moveInLineOfSightLocked = false;
}
public override void OnCharmed(bool apply)
@@ -109,12 +119,12 @@ namespace Game.AI
public virtual void MoveInLineOfSight_Safe(Unit who)
{
if (MoveInLineOfSight_locked)
if (_moveInLineOfSightLocked)
return;
MoveInLineOfSight_locked = true;
_moveInLineOfSightLocked = true;
MoveInLineOfSight(who);
MoveInLineOfSight_locked = false;
_moveInLineOfSightLocked = false;
}
public virtual void MoveInLineOfSight(Unit who)
@@ -126,7 +136,7 @@ namespace Game.AI
me.EngageWithTarget(who);
}
void _OnOwnerCombatInteraction(Unit target)
void OnOwnerCombatInteraction(Unit target)
{
if (target == null || !me.IsAlive())
return;
@@ -165,7 +175,7 @@ namespace Game.AI
if (!_EnterEvadeMode(why))
return;
Log.outDebug( LogFilter.Unit, "Creature {0} enters evade mode.", me.GetEntry());
Log.outDebug(LogFilter.Unit, "Creature {0} enters evade mode.", me.GetEntry());
if (me.GetVehicle() == null) // otherwise me will be in evade mode forever
{
@@ -190,7 +200,7 @@ namespace Game.AI
me.GetVehicleKit().Reset(true);
}
void SetGazeOn(Unit target)
public void SetGazeOn(Unit target)
{
if (me.IsValidAttackTarget(target) && target != me.GetVictim())
{
@@ -276,9 +286,9 @@ namespace Game.AI
if (_boundary.Empty())
return CypherStrings.CreatureMovementNotBounded;
List<KeyValuePair<int, int>> Q = new List<KeyValuePair<int, int>>();
List<KeyValuePair<int, int>> alreadyChecked = new List<KeyValuePair<int, int>>();
List<KeyValuePair<int, int>> outOfBounds = new List<KeyValuePair<int, int>>();
List<KeyValuePair<int, int>> Q = new();
List<KeyValuePair<int, int>> alreadyChecked = new();
List<KeyValuePair<int, int>> outOfBounds = new();
Position startPosition = owner.GetPosition();
if (!CheckBoundary(startPosition)) // fall back to creature position
@@ -309,7 +319,7 @@ namespace Game.AI
}
if (!alreadyChecked.Contains(next)) // never check a coordinate twice
{
Position nextPos = new Position(startPosition.GetPositionX() + next.Key * SharedConst.BoundaryVisualizeStepSize, startPosition.GetPositionY() + next.Value * SharedConst.BoundaryVisualizeStepSize, startPosition.GetPositionZ());
Position nextPos = new(startPosition.GetPositionX() + next.Key * SharedConst.BoundaryVisualizeStepSize, startPosition.GetPositionY() + next.Value * SharedConst.BoundaryVisualizeStepSize, startPosition.GetPositionZ());
if (CheckBoundary(nextPos))
Q.Add(next);
else
@@ -390,7 +400,7 @@ namespace Game.AI
return true;
}
public void SetBoundary(List<AreaBoundary> boundary, bool negateBoundaries = false)
{
_boundary = boundary;
@@ -408,8 +418,8 @@ namespace Game.AI
public virtual void JustDied(Unit killer) { }
// Called when the creature kills a unit
public virtual void KilledUnit(Unit victim) {}
public virtual void KilledUnit(Unit victim) { }
// Called when the creature summon successfully other creature
public virtual void JustSummoned(Creature summon) { }
public virtual void IsSummonedBy(Unit summoner) { }
@@ -430,11 +440,11 @@ namespace Game.AI
public virtual void JustUnregisteredAreaTrigger(AreaTrigger areaTrigger) { }
// Called when hit by a spell
public virtual void SpellHit(Unit caster, SpellInfo spell) {}
public virtual void SpellHit(Unit caster, SpellInfo spell) { }
// Called when spell hits a target
public virtual void SpellHitTarget(Unit target, SpellInfo spell) {}
public virtual void SpellHitTarget(Unit target, SpellInfo spell) { }
public virtual bool IsEscorted() { return false; }
// Called when creature appears in the world (spawn, respawn, grid load etc...)
@@ -450,18 +460,18 @@ namespace Game.AI
// Called at reaching home after evade
public virtual void JustReachedHome() { }
// Called at text emote receive from player
public virtual void ReceiveEmote(Player player, TextEmotes emoteId) { }
// Called when owner takes damage
public virtual void OwnerAttackedBy(Unit attacker) { _OnOwnerCombatInteraction(attacker); }
public virtual void OwnerAttackedBy(Unit attacker) { OnOwnerCombatInteraction(attacker); }
// Called when owner attacks something
public virtual void OwnerAttacked(Unit target) { _OnOwnerCombatInteraction(target); }
public virtual void OwnerAttacked(Unit target) { OnOwnerCombatInteraction(target); }
// called when the corpse of this creature gets removed
public virtual void CorpseRemoved(long respawnDelay) {}
public virtual void CorpseRemoved(long respawnDelay) { }
public virtual void PassengerBoarded(Unit passenger, sbyte seatId, bool apply) { }
@@ -481,18 +491,9 @@ namespace Game.AI
/// </summary>
/// <param name="onlyIfActive"></param>
/// <returns></returns>
public virtual bool IsEscortNPC(bool onlyIfActive) { return false; }
List<AreaBoundary> GetBoundary() { return _boundary; }
public virtual bool IsEscortNPC(bool onlyIfActive) { return false; }
bool MoveInLineOfSight_locked;
protected new Creature me;
List<AreaBoundary> _boundary = new List<AreaBoundary>();
bool _negateBoundary;
protected EventMap _events = new EventMap();
protected TaskScheduler _scheduler = new TaskScheduler();
protected InstanceScript _instance;
public List<AreaBoundary> GetBoundary() { return _boundary; }
}
public struct AISpellInfoType
+6 -6
View File
@@ -15,15 +15,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Framework.Dynamic;
using Game.Entities;
using Game.Spells;
using Framework.Constants;
namespace Game.AI
{
public class GameObjectAI
{
protected TaskScheduler _scheduler;
protected EventMap _events;
public GameObject me;
public GameObjectAI(GameObject gameObject)
{
me = gameObject;
@@ -93,10 +98,5 @@ namespace Game.AI
public virtual void OnStateChanged(GameObjectState state) { }
public virtual void EventInform(uint eventId) { }
public virtual void SpellHit(Unit unit, SpellInfo spellInfo) { }
protected TaskScheduler _scheduler;
protected EventMap _events;
public GameObject me;
}
}
+21 -24
View File
@@ -26,14 +26,15 @@ namespace Game.AI
{
public class PetAI : CreatureAI
{
List<ObjectGuid> _allySet = new();
uint _updateAlliesTimer;
public PetAI(Creature c) : base(c)
{
i_tracker = new TimeTracker(5000);
UpdateAllies();
}
bool _needToStop()
bool NeedToStop()
{
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
if (me.IsCharmed() && me.GetVictim() == me.GetCharmer())
@@ -48,7 +49,7 @@ namespace Game.AI
return !me.IsValidAttackTarget(me.GetVictim());
}
void _stopAttack()
void StopAttack()
{
if (!me.IsAlive())
{
@@ -74,11 +75,11 @@ namespace Game.AI
Unit owner = me.GetCharmerOrOwner();
if (m_updateAlliesTimer <= diff)
if (_updateAlliesTimer <= diff)
// UpdateAllies self set update timer
UpdateAllies();
else
m_updateAlliesTimer -= diff;
_updateAlliesTimer -= diff;
if (me.GetVictim() && me.GetVictim().IsAlive())
{
@@ -90,10 +91,10 @@ namespace Game.AI
return;
}
if (_needToStop())
if (NeedToStop())
{
Log.outDebug(LogFilter.Server, "Pet AI stopped attacking [{0}]", me.GetGUID().ToString());
_stopAttack();
StopAttack();
return;
}
@@ -129,7 +130,7 @@ namespace Game.AI
// Autocast (casted only in combat or persistent spells in any state)
if (!me.HasUnitState(UnitState.Casting))
{
List<Tuple<Unit, Spell>> targetSpellStore = new List<Tuple<Unit, Spell>>();
List<Tuple<Unit, Spell>> targetSpellStore = new();
for (byte i = 0; i < me.GetPetAutoSpellSize(); ++i)
{
@@ -157,7 +158,7 @@ namespace Game.AI
continue;
}
Spell spell = new Spell(me, spellInfo, TriggerCastFlags.None);
Spell spell = new(me, spellInfo, TriggerCastFlags.None);
bool spellUsed = false;
// Some spells can target enemy or friendly (DK Ghoul's Leap)
@@ -185,7 +186,7 @@ namespace Game.AI
// No enemy, check friendly
if (!spellUsed)
{
foreach (var tar in m_AllySet)
foreach (var tar in _allySet)
{
Unit ally = Global.ObjAccessor.GetUnit(me, tar);
@@ -208,7 +209,7 @@ namespace Game.AI
}
else if (me.GetVictim() && CanAIAttack(me.GetVictim()) && spellInfo.CanBeUsedInCombat())
{
Spell spell = new Spell(me, spellInfo, TriggerCastFlags.None);
Spell spell = new(me, spellInfo, TriggerCastFlags.None);
if (spell.CanAutoCast(me.GetVictim()))
targetSpellStore.Add(Tuple.Create(me.GetVictim(), spell));
else
@@ -226,7 +227,7 @@ namespace Game.AI
targetSpellStore.RemoveAt(index);
SpellCastTargets targets = new SpellCastTargets();
SpellCastTargets targets = new();
targets.SetUnitTarget(target);
spell.Prepare(targets);
@@ -245,7 +246,7 @@ namespace Game.AI
void UpdateAllies()
{
m_updateAlliesTimer = 10 * Time.InMilliseconds; // update friendly targets every 10 seconds, lesser checks increase performance
_updateAlliesTimer = 10 * Time.InMilliseconds; // update friendly targets every 10 seconds, lesser checks increase performance
Unit owner = me.GetCharmerOrOwner();
if (!owner)
@@ -257,15 +258,15 @@ namespace Game.AI
group = player.GetGroup();
//only pet and owner/not in group.ok
if (m_AllySet.Count == 2 && !group)
if (_allySet.Count == 2 && !group)
return;
//owner is in group; group members filled in already (no raid . subgroupcount = whole count)
if (group && !group.IsRaidGroup() && m_AllySet.Count == (group.GetMembersCount() + 2))
if (group && !group.IsRaidGroup() && _allySet.Count == (group.GetMembersCount() + 2))
return;
m_AllySet.Clear();
m_AllySet.Add(me.GetGUID());
_allySet.Clear();
_allySet.Add(me.GetGUID());
if (group) //add group
{
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
@@ -277,11 +278,11 @@ namespace Game.AI
if (target.GetGUID() == owner.GetGUID())
continue;
m_AllySet.Add(target.GetGUID());
_allySet.Add(target.GetGUID());
}
}
else //remove group
m_AllySet.Add(owner.GetGUID());
_allySet.Add(owner.GetGUID());
}
public override void KilledUnit(Unit victim)
@@ -639,9 +640,5 @@ namespace Game.AI
public override void MoveInLineOfSight(Unit who) { }
public override void MoveInLineOfSight_Safe(Unit who) { }
public override void EnterEvadeMode(EvadeReason why) { }
TimeTracker i_tracker;
List<ObjectGuid> m_AllySet = new List<ObjectGuid>();
uint m_updateAlliesTimer;
}
}
+6 -6
View File
@@ -23,9 +23,11 @@ namespace Game.AI
{
public class TotemAI : CreatureAI
{
ObjectGuid _victimGuid;
public TotemAI(Creature c) : base(c)
{
i_victimGuid = ObjectGuid.Empty;
_victimGuid = ObjectGuid.Empty;
}
public override void EnterEvadeMode(EvadeReason why)
@@ -51,7 +53,7 @@ namespace Game.AI
// SpellModOp.Range not applied in this place just because not existence range mods for attacking totems
Unit victim = !i_victimGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, i_victimGuid) : null;
Unit victim = !_victimGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, _victimGuid) : null;
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
if (victim == null || !victim.IsTargetableForAttack() || !me.IsWithinDistInMap(victim, max_range) ||
@@ -67,15 +69,13 @@ namespace Game.AI
if (victim != null)
{
// remember
i_victimGuid = victim.GetGUID();
_victimGuid = victim.GetGUID();
// attack
me.CastSpell(victim, me.ToTotem().GetSpell());
}
else
i_victimGuid.Clear();
_victimGuid.Clear();
}
ObjectGuid i_victimGuid;
}
}
+39 -46
View File
@@ -27,6 +27,10 @@ namespace Game.AI
{
public class UnitAI
{
static Dictionary<(uint id, Difficulty difficulty), AISpellInfoType> _aiSpellInfo = new();
protected Unit me { get; private set; }
public UnitAI(Unit _unit)
{
me = _unit;
@@ -59,7 +63,7 @@ namespace Game.AI
void SortByDistance(List<Unit> targets, bool ascending)
{
targets.Sort(new ObjectDistanceOrderPred(me, true));
targets.Sort(new ObjectDistanceOrderPred(me, ascending));
}
public void DoMeleeAttackIfReady()
@@ -145,18 +149,12 @@ namespace Game.AI
if (targetList.Empty())
return null;
switch (targetType)
return targetType switch
{
case SelectAggroTarget.MaxThreat:
case SelectAggroTarget.MinThreat:
case SelectAggroTarget.MaxDistance:
case SelectAggroTarget.MinDistance:
return targetList[0];
case SelectAggroTarget.Random:
return targetList.SelectRandom();
default:
return null;
}
SelectAggroTarget.MaxThreat or SelectAggroTarget.MinThreat or SelectAggroTarget.MaxDistance or SelectAggroTarget.MinDistance => targetList[0],
SelectAggroTarget.Random => targetList.SelectRandom(),
_ => null,
};
}
/// <summary>
@@ -287,7 +285,7 @@ namespace Game.AI
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers);
float range = spellInfo.GetMaxRange(false);
DefaultTargetSelector targetSelector = new DefaultTargetSelector(me, range, playerOnly, true, -(int)spellId);
DefaultTargetSelector targetSelector = new(me, range, playerOnly, true, -(int)spellId);
if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim)
&& targetSelector.Invoke(me.GetVictim()))
target = me.GetVictim();
@@ -334,7 +332,7 @@ namespace Game.AI
Global.SpellMgr.ForEachSpellInfo(spellInfo =>
{
AISpellInfoType AIInfo = new AISpellInfoType();
AISpellInfoType AIInfo = new();
if (spellInfo.HasAttribute(SpellAttr0.CastableWhileDead))
AIInfo.condition = AICondition.Die;
else if (spellInfo.IsPassive() || spellInfo.GetDuration() == -1)
@@ -461,7 +459,7 @@ namespace Game.AI
AIInfo.Effects |= 1 << ((int)SelectEffect.Aura - 1);
}
AISpellInfo[(spellInfo.Id, spellInfo.Difficulty)] = AIInfo;
_aiSpellInfo[(spellInfo.Id, spellInfo.Difficulty)] = AIInfo;
});
}
@@ -471,16 +469,16 @@ namespace Game.AI
public virtual void InitializeAI()
{
if (!me.IsDead())
if (!me.IsDead())
Reset();
}
public virtual void Reset() { }
public virtual void OnCharmed(bool apply) { }
public virtual bool ShouldSparWith(Unit target) { return false; }
public virtual void DoAction(int action) { }
public virtual uint GetData(uint id = 0) { return 0; }
public virtual void SetData(uint id, uint value) { }
@@ -491,7 +489,7 @@ namespace Game.AI
public virtual void DamageTaken(Unit attacker, ref uint damage) { }
public virtual void HealReceived(Unit by, uint addhealth) { }
public virtual void HealDone(Unit to, uint addhealth) { }
public virtual void SpellInterrupted(uint spellId, uint unTimeMs) {}
public virtual void SpellInterrupted(uint spellId, uint unTimeMs) { }
/// <summary>
/// Called when a player opens a gossip dialog with the creature.
@@ -522,11 +520,10 @@ namespace Game.AI
}
public virtual void QuestReward(Player player, Quest quest, LootItemType type, uint opt) { }
/// <summary>
/// Called when a game event starts or ends
/// </summary>
public virtual void OnGameEvent(bool start, ushort eventId) { }
/// <summary>
/// Called when a game event starts or ends
/// </summary>
public virtual void OnGameEvent(bool start, ushort eventId) { }
// Called when the dialog status between a player and the creature is requested.
public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedDefault; }
@@ -539,14 +536,10 @@ namespace Game.AI
public virtual void WaypointPathEnded(uint nodeId, uint pathId) { }
public AISpellInfoType GetAISpellInfo(uint spellId, Difficulty difficulty)
public static AISpellInfoType GetAISpellInfo(uint spellId, Difficulty difficulty)
{
return AISpellInfo.LookupByKey((spellId, difficulty));
return _aiSpellInfo.LookupByKey((spellId, difficulty));
}
public static Dictionary<(uint id, Difficulty difficulty), AISpellInfoType> AISpellInfo = new Dictionary<(uint id, Difficulty difficulty), AISpellInfoType>();
protected Unit me { get; private set; }
}
public enum SelectAggroTarget
@@ -623,6 +616,9 @@ namespace Game.AI
// todo Add more checks from Spell.CheckCast
public class SpellTargetSelector : ICheck<Unit>
{
Unit _caster;
SpellInfo _spellInfo;
public SpellTargetSelector(Unit caster, uint spellId)
{
_caster = caster;
@@ -694,9 +690,6 @@ namespace Game.AI
return true;
}
Unit _caster;
SpellInfo _spellInfo;
}
// Very simple target selector, will just skip main target
@@ -704,6 +697,9 @@ namespace Game.AI
// because tank will not be in the temporary list
public class NonTankTargetSelector : ICheck<Unit>
{
Unit _source;
bool _playerOnly;
public NonTankTargetSelector(Unit source, bool playerOnly = true)
{
_source = source;
@@ -724,14 +720,16 @@ namespace Game.AI
return target != _source.GetVictim();
}
Unit _source;
bool _playerOnly;
}
// Simple selector for units using mana
class PowerUsersSelector : ICheck<Unit>
{
Unit _me;
PowerType _power;
float _dist;
bool _playerOnly;
public PowerUsersSelector(Unit unit, PowerType power, float dist, bool playerOnly)
{
_me = unit;
@@ -759,15 +757,15 @@ namespace Game.AI
return true;
}
Unit _me;
PowerType _power;
float _dist;
bool _playerOnly;
}
class FarthestTargetSelector : ICheck<Unit>
{
Unit _me;
float _dist;
bool _playerOnly;
bool _inLos;
public FarthestTargetSelector(Unit unit, float dist, bool playerOnly, bool inLos)
{
_me = unit;
@@ -792,10 +790,5 @@ namespace Game.AI
return true;
}
Unit _me;
float _dist;
bool _playerOnly;
bool _inLos;
}
}