Core: SOme code cleanup, more to follow.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user