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
+9 -14
View File
@@ -99,14 +99,12 @@ namespace Game.AI
public static IMovementGenerator SelectMovementAI(Creature creature)
{
switch (creature.DefaultMovementType)
return creature.DefaultMovementType switch
{
case MovementGeneratorType.Random:
return new RandomMovementGenerator();
case MovementGeneratorType.Waypoint:
return new WaypointMovementGenerator();
}
return null;
MovementGeneratorType.Random => new RandomMovementGenerator(),
MovementGeneratorType.Waypoint => new WaypointMovementGenerator(),
_ => null,
};
}
public static GameObjectAI SelectGameObjectAI(GameObject go)
@@ -116,14 +114,11 @@ namespace Game.AI
if (scriptedAI != null)
return scriptedAI;
switch (go.GetAIName())
return go.GetAIName() switch
{
case "GameObjectAI":
default:
return new GameObjectAI(go);
case "SmartGameObjectAI":
return new SmartGameObjectAI(go);
}
"SmartGameObjectAI" => new SmartGameObjectAI(go),
_ => new GameObjectAI(go),
};
}
}
}
+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;
}
}
+32 -44
View File
@@ -389,6 +389,11 @@ namespace Game.AI
public class PlayerAI : UnitAI
{
protected new Player me;
uint _selfSpec;
bool _isSelfHealer;
bool _isSelfRangedAttacker;
public PlayerAI(Player player) : base(player)
{
me = player;
@@ -402,29 +407,17 @@ namespace Game.AI
if (!who)
return false;
switch (who.GetClass())
return who.GetClass() switch
{
case Class.Warrior:
case Class.Hunter:
case Class.Rogue:
case Class.Deathknight:
case Class.Mage:
case Class.Warlock:
case Class.DemonHunter:
default:
return false;
case Class.Paladin:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.PaladinHoly;
case Class.Priest:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.PriestDiscipline || who.GetPrimarySpecialization() == (uint)TalentSpecialization.PriestHoly;
case Class.Shaman:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.ShamanRestoration;
case Class.Monk:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.MonkMistweaver;
case Class.Druid:
return who.GetPrimarySpecialization() == (uint)TalentSpecialization.DruidRestoration;
}
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)
@@ -494,14 +487,14 @@ namespace Game.AI
if (me.GetSpellHistory().HasGlobalCooldown(spellInfo))
return null;
Spell spell = new Spell(me, spellInfo, TriggerCastFlags.None);
Spell spell = new(me, spellInfo, TriggerCastFlags.None);
if (spell.CanAutoCast(target))
return Tuple.Create(spell, target);
return null;
}
Tuple<Spell, Unit> VerifySpellCast(uint spellId, SpellTarget target)
public Tuple<Spell, Unit> VerifySpellCast(uint spellId, SpellTarget target)
{
Unit pTarget = null;
switch (target)
@@ -558,7 +551,7 @@ namespace Game.AI
return selected;
}
void VerifyAndPushSpellCast<T>(List<Tuple<Tuple<Spell, Unit>, uint>> spells, uint spellId, T target, uint weight) where T : Unit
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)
@@ -567,7 +560,7 @@ namespace Game.AI
public void DoCastAtTarget(Tuple<Spell, Unit> spell)
{
SpellCastTargets targets = new SpellCastTargets();
SpellCastTargets targets = new();
targets.SetUnitTarget(spell.Item2);
spell.Item1.Prepare(targets);
}
@@ -621,10 +614,10 @@ namespace Game.AI
DoMeleeAttackIfReady();
}
void CancelAllShapeshifts()
public void CancelAllShapeshifts()
{
List<AuraEffect> shapeshiftAuras = me.GetAuraEffectsByType(AuraType.ModShapeshift);
List<Aura> removableShapeshifts = new List<Aura>();
List<Aura> removableShapeshifts = new();
foreach (AuraEffect auraEff in shapeshiftAuras)
{
Aura aura = auraEff.GetBase();
@@ -654,22 +647,17 @@ namespace Game.AI
public override void OnCharmed(bool apply) { }
// helper functions to determine player info
bool IsHealer(Player who = null)
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); }
uint GetSpec(Player who = null) { return (!who || who == me) ? _selfSpec : who.GetPrimarySpecialization(); }
void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection
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; }
protected new Player me;
uint _selfSpec;
bool _isSelfHealer;
bool _isSelfRangedAttacker;
enum SpellTarget
public enum SpellTarget
{
None,
Victim,
@@ -680,6 +668,13 @@ namespace Game.AI
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;
@@ -710,7 +705,7 @@ namespace Game.AI
Tuple<Spell, Unit> SelectAppropriateCastForSpec()
{
List<Tuple<Tuple<Spell, Unit>, uint>> spells = new List<Tuple<Tuple<Spell, Unit>, uint>>();
List<Tuple<Tuple<Spell, Unit>, uint>> spells = new();
/*
switch (me.getClass())
{
@@ -1234,8 +1229,6 @@ namespace Game.AI
return SelectSpellCast(spells);
}
const float CASTER_CHASE_DISTANCE = 28.0f;
public override void UpdateAI(uint diff)
{
Creature charmer = GetCharmer();
@@ -1359,11 +1352,6 @@ namespace Game.AI
me.StopMoving();
}
}
uint _castCheckTimer;
bool _chaseCloser;
bool _forceFacing;
bool _isFollowing;
}
struct ValidTargetSelectPredicate : ICheck<Unit>
+50 -60
View File
@@ -28,6 +28,10 @@ namespace Game.AI
{
public class ScriptedAI : CreatureAI
{
Difficulty _difficulty;
bool _isCombatMovementAllowed;
bool _isHeroic;
public ScriptedAI(Creature creature) : base(creature)
{
_isCombatMovementAllowed = true;
@@ -87,7 +91,7 @@ namespace Game.AI
}
//Cast spell by spell info
void DoCastSpell(Unit target, SpellInfo spellInfo, bool triggered = false)
public void DoCastSpell(Unit target, SpellInfo spellInfo, bool triggered = false)
{
if (target == null || me.IsNonMeleeSpellCast(false))
return;
@@ -97,7 +101,7 @@ namespace Game.AI
}
//Plays a sound to all nearby players
public void DoPlaySoundToSet(WorldObject source, uint soundId)
public static void DoPlaySoundToSet(WorldObject source, uint soundId)
{
if (source == null)
return;
@@ -268,7 +272,7 @@ namespace Game.AI
me.MonsterMoveWithSpeed(x, y, z, speed);
}
void DoTeleportTo(float[] position)
public void DoTeleportTo(float[] position)
{
me.NearTeleportTo(position[0], position[1], position[2], position[3]);
}
@@ -286,7 +290,7 @@ namespace Game.AI
me.GetGUID(), me.GetEntry(), unit.GetTypeId(), unit.GetGUID(), x, y, z, o);
}
void DoTeleportAll(float x, float y, float z, float o)
public void DoTeleportAll(float x, float y, float z, float o)
{
Map map = me.GetMap();
if (!map.IsDungeon())
@@ -310,9 +314,9 @@ namespace Game.AI
}
//Returns a list of friendly CC'd units within range
List<Creature> DoFindFriendlyCC(float range)
public List<Creature> DoFindFriendlyCC(float range)
{
List<Creature> list = new List<Creature>();
List<Creature> list = new();
var u_check = new FriendlyCCedInRange(me, range);
var searcher = new CreatureListSearcher(me, list, u_check);
Cell.VisitAllObjects(me, searcher, range);
@@ -323,7 +327,7 @@ namespace Game.AI
//Returns a list of all friendly units missing a specific buff within range
public List<Creature> DoFindFriendlyMissingBuff(float range, uint spellId)
{
List<Creature> list = new List<Creature>();
List<Creature> list = new();
var u_check = new FriendlyMissingBuffInRange(me, range, spellId);
var searcher = new CreatureListSearcher(me, list, u_check);
Cell.VisitAllObjects(me, searcher, range);
@@ -332,7 +336,7 @@ namespace Game.AI
}
//Return a player with at least minimumRange from me
Player GetPlayerAtMinimumRange(float minimumRange)
public Player GetPlayerAtMinimumRange(float minimumRange)
{
var check = new PlayerAtMinimumRangeAway(me, minimumRange);
var searcher = new PlayerSearcher(me, check);
@@ -398,7 +402,7 @@ namespace Game.AI
return source.FindNearestCreature(entry, maxSearchRange, alive);
}
public GameObject GetClosestGameObjectWithEntry(WorldObject source, uint entry, float maxSearchRange)
public static GameObject GetClosestGameObjectWithEntry(WorldObject source, uint entry, float maxSearchRange)
{
return source.FindNearestGameObject(entry, maxSearchRange);
}
@@ -429,50 +433,40 @@ namespace Game.AI
public T DungeonMode<T>(T normal5, T heroic10)
{
switch (_difficulty)
return _difficulty switch
{
case Difficulty.Normal:
return normal5;
case Difficulty.Heroic:
default:
return heroic10;
}
Difficulty.Normal => normal5,
_ => heroic10,
};
}
public T RaidMode<T>(T normal10, T normal25)
{
switch (_difficulty)
return _difficulty switch
{
case Difficulty.Raid10N:
return normal10;
case Difficulty.Raid25N:
default:
return normal25;
}
}
public T RaidMode<T>(T normal10, T normal25, T heroic10, T heroic25)
{
switch (_difficulty)
{
case Difficulty.Raid10N:
return normal10;
case Difficulty.Raid25N:
return normal25;
case Difficulty.Raid10HC:
return heroic10;
case Difficulty.Raid25HC:
default:
return heroic25;
}
Difficulty.Raid10N => normal10,
_ => normal25,
};
}
Difficulty _difficulty;
bool _isCombatMovementAllowed;
bool _isHeroic;
public T RaidMode<T>(T normal10, T normal25, T heroic10, T heroic25)
{
return _difficulty switch
{
Difficulty.Raid10N => normal10,
Difficulty.Raid25N => normal25,
Difficulty.Raid10HC => heroic10,
_ => heroic25,
};
}
}
public class BossAI : ScriptedAI
{
public InstanceScript instance;
public SummonList summons;
uint _bossId;
public BossAI(Creature creature, uint bossId) : base(creature)
{
instance = creature.GetInstanceScript();
@@ -615,14 +609,12 @@ namespace Game.AI
public override bool CanAIAttack(Unit victim) { return CheckBoundary(victim); }
public void _JustReachedHome() { me.SetActive(false); }
public InstanceScript instance;
public SummonList summons;
uint _bossId;
}
public class WorldBossAI : ScriptedAI
{
SummonList summons;
public WorldBossAI(Creature creature) : base(creature)
{
summons = new SummonList(creature);
@@ -695,15 +687,15 @@ namespace Game.AI
public override void EnterCombat(Unit who) { _EnterCombat(); }
public override void JustDied(Unit killer) { _JustDied(); }
SummonList summons;
}
public class SummonList : List<ObjectGuid>
{
Creature _me;
public SummonList(Creature creature)
{
me = creature;
_me = creature;
}
public void Summon(Creature summon) { Add(summon.GetGUID()); }
@@ -712,7 +704,7 @@ namespace Game.AI
{
foreach (var id in this)
{
Creature summon = ObjectAccessor.GetCreature(me, id);
Creature summon = ObjectAccessor.GetCreature(_me, id);
if (summon && summon.IsAIEnabled && (entry == 0 || summon.GetEntry() == entry))
{
summon.GetAI().DoZoneInCombat(null, maxRangeToNearestTarget);
@@ -724,7 +716,7 @@ namespace Game.AI
{
foreach (var id in this)
{
Creature summon = ObjectAccessor.GetCreature(me, id);
Creature summon = ObjectAccessor.GetCreature(_me, id);
if (!summon)
Remove(id);
else if (summon.GetEntry() == entry)
@@ -739,7 +731,7 @@ namespace Game.AI
{
while (!this.Empty())
{
Creature summon = ObjectAccessor.GetCreature(me, this.FirstOrDefault());
Creature summon = ObjectAccessor.GetCreature(_me, this.FirstOrDefault());
RemoveAt(0);
if (summon)
summon.DespawnOrUnsummon();
@@ -762,7 +754,7 @@ namespace Game.AI
{
foreach (var id in this)
{
if (!ObjectAccessor.GetCreature(me, id))
if (!ObjectAccessor.GetCreature(_me, id))
Remove(id);
}
}
@@ -770,7 +762,7 @@ namespace Game.AI
public void DoAction(int info, ICheck<ObjectGuid> predicate, ushort max = 0)
{
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
List<ObjectGuid> listCopy = new List<ObjectGuid>(this);
List<ObjectGuid> listCopy = new(this);
listCopy.RandomResize(predicate.Invoke, max);
DoActionImpl(info, listCopy);
}
@@ -778,7 +770,7 @@ namespace Game.AI
public void DoAction(int info, Predicate<ObjectGuid> predicate, ushort max = 0)
{
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
List<ObjectGuid> listCopy = new List<ObjectGuid>(this);
List<ObjectGuid> listCopy = new(this);
listCopy.RandomResize(predicate, max);
DoActionImpl(info, listCopy);
}
@@ -787,7 +779,7 @@ namespace Game.AI
{
foreach (var id in this)
{
Creature summon = ObjectAccessor.GetCreature(me, id);
Creature summon = ObjectAccessor.GetCreature(_me, id);
if (summon && summon.GetEntry() == entry)
return true;
}
@@ -799,24 +791,22 @@ namespace Game.AI
{
foreach (var guid in summons)
{
Creature summon = ObjectAccessor.GetCreature(me, guid);
Creature summon = ObjectAccessor.GetCreature(_me, guid);
if (summon && summon.IsAIEnabled)
summon.GetAI().DoAction(action);
}
}
Creature me;
}
public class EntryCheckPredicate : ICheck<ObjectGuid>
{
uint _entry;
public EntryCheckPredicate(uint entry)
{
_entry = entry;
}
public bool Invoke(ObjectGuid guid) { return guid.GetEntry() == _entry; }
uint _entry;
}
}
@@ -212,7 +212,7 @@ namespace Game.AI
Log.outDebug(LogFilter.Scripts, "EscortAI.UpdateAI: reached end of waypoints, despawning at end");
if (_returnToStart)
{
Position respawnPosition = new Position();
Position respawnPosition = new();
float orientation;
me.GetRespawnPosition(out respawnPosition.posX, out respawnPosition.posY, out respawnPosition.posZ, out orientation);
respawnPosition.SetOrientation(orientation);
@@ -338,7 +338,7 @@ namespace Game.AI
GridDefines.NormalizeMapCoord(ref x);
GridDefines.NormalizeMapCoord(ref y);
WaypointNode waypoint = new WaypointNode();
WaypointNode waypoint = new();
waypoint.id = id;
waypoint.x = x;
waypoint.y = y;
+26 -26
View File
@@ -35,11 +35,17 @@ namespace Game.AI
class FollowerAI : ScriptedAI
{
ObjectGuid _leaderGUID;
uint _updateFollowTimer;
FollowState _followState;
Quest _questForFollow; //normally we have a quest
public FollowerAI(Creature creature) : base(creature)
{
m_uiUpdateFollowTimer = 2500;
m_uiFollowState = FollowState.None;
m_pQuestForFollow = null;
_updateFollowTimer = 2500;
_followState = FollowState.None;
_questForFollow = null;
}
public override void AttackStart(Unit who)
@@ -124,7 +130,7 @@ namespace Game.AI
public override void JustDied(Unit killer)
{
if (!HasFollowState(FollowState.Inprogress) || m_uiLeaderGUID.IsEmpty() || m_pQuestForFollow == null)
if (!HasFollowState(FollowState.Inprogress) || _leaderGUID.IsEmpty() || _questForFollow == null)
return;
// @todo need a better check for quests with time limit.
@@ -139,17 +145,17 @@ namespace Game.AI
Player member = groupRef.GetSource();
if (member)
if (member.IsInMap(player))
member.FailQuest(m_pQuestForFollow.Id);
member.FailQuest(_questForFollow.Id);
}
}
else
player.FailQuest(m_pQuestForFollow.Id);
player.FailQuest(_questForFollow.Id);
}
}
public override void JustAppeared()
{
m_uiFollowState = FollowState.None;
_followState = FollowState.None;
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
@@ -191,7 +197,7 @@ namespace Game.AI
{
if (HasFollowState(FollowState.Inprogress) && !me.GetVictim())
{
if (m_uiUpdateFollowTimer <= uiDiff)
if (_updateFollowTimer <= uiDiff)
{
if (HasFollowState(FollowState.Complete) && !HasFollowState(FollowState.PostEvent))
{
@@ -241,10 +247,10 @@ namespace Game.AI
return;
}
m_uiUpdateFollowTimer = 1000;
_updateFollowTimer = 1000;
}
else
m_uiUpdateFollowTimer -= uiDiff;
_updateFollowTimer -= uiDiff;
}
UpdateFollowerAI(uiDiff);
@@ -275,7 +281,7 @@ namespace Game.AI
}
}
void StartFollow(Player player, uint factionForFollower = 0, Quest quest = null)
public void StartFollow(Player player, uint factionForFollower = 0, Quest quest = null)
{
if (me.GetVictim())
{
@@ -290,12 +296,12 @@ namespace Game.AI
}
//set variables
m_uiLeaderGUID = player.GetGUID();
_leaderGUID = player.GetGUID();
if (factionForFollower != 0)
me.SetFaction(factionForFollower);
m_pQuestForFollow = quest;
_questForFollow = quest;
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint)
{
@@ -311,12 +317,12 @@ namespace Game.AI
me.GetMotionMaster().MoveFollow(player, SharedConst.PetFollowDist, SharedConst.PetFollowAngle);
Log.outDebug(LogFilter.Scripts, "FollowerAI start follow {0} ({1})", player.GetName(), m_uiLeaderGUID.ToString());
Log.outDebug(LogFilter.Scripts, "FollowerAI start follow {0} ({1})", player.GetName(), _leaderGUID.ToString());
}
Player GetLeaderForFollower()
{
Player player = Global.ObjAccessor.GetPlayer(me, m_uiLeaderGUID);
Player player = Global.ObjAccessor.GetPlayer(me, _leaderGUID);
if (player)
{
if (player.IsAlive())
@@ -332,7 +338,7 @@ namespace Game.AI
if (member && me.IsWithinDistInMap(member, 100.0f) && member.IsAlive())
{
Log.outDebug(LogFilter.Scripts, "FollowerAI GetLeader changed and returned new leader.");
m_uiLeaderGUID = member.GetGUID();
_leaderGUID = member.GetGUID();
return member;
}
}
@@ -344,7 +350,7 @@ namespace Game.AI
return null;
}
void SetFollowComplete(bool bWithEndEvent = false)
public void SetFollowComplete(bool bWithEndEvent = false)
{
if (me.HasUnitState(UnitState.Follow))
{
@@ -366,15 +372,9 @@ namespace Game.AI
AddFollowState(FollowState.Complete);
}
bool HasFollowState(FollowState uiFollowState) { return (m_uiFollowState & uiFollowState) != 0; }
bool HasFollowState(FollowState uiFollowState) { return (_followState & uiFollowState) != 0; }
void AddFollowState(FollowState uiFollowState) { m_uiFollowState |= uiFollowState; }
void RemoveFollowState(FollowState uiFollowState) { m_uiFollowState &= ~uiFollowState; }
ObjectGuid m_uiLeaderGUID;
uint m_uiUpdateFollowTimer;
FollowState m_uiFollowState;
Quest m_pQuestForFollow; //normally we have a quest
void AddFollowState(FollowState uiFollowState) { _followState |= uiFollowState; }
void RemoveFollowState(FollowState uiFollowState) { _followState &= ~uiFollowState; }
}
}
+158 -158
View File
@@ -31,19 +31,61 @@ namespace Game.AI
const int SMART_ESCORT_MAX_PLAYER_DIST = 60;
const int SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2;
public uint EscortQuestID;
SmartScript _script = new();
bool _isCharmed;
uint _followCreditType;
uint _followArrivedTimer;
uint _followCredit;
uint _followArrivedEntry;
ObjectGuid _followGuid;
float _followDist;
float _followAngle;
SmartEscortState _escortState;
uint _escortNPCFlags;
uint _escortInvokerCheckTimer;
WaypointPath _path;
uint _currentWaypointNode;
bool _waypointReached;
uint _waypointPauseTimer;
bool _waypointPauseForced;
bool _repeatWaypointPath;
bool _OOCReached;
bool _waypointPathEnded;
bool _run;
bool _evadeDisabled;
bool _canAutoAttack;
bool _canCombatMove;
uint _invincibilityHpLevel;
uint _despawnTime;
uint _respawnTime;
uint _despawnState;
// Vehicle conditions
bool _hasConditions;
uint _conditionsTimer;
// Gossip
bool _gossipReturn;
public SmartAI(Creature creature) : base(creature)
{
_escortInvokerCheckTimer = 1000;
mRun = true;
mCanAutoAttack = true;
mCanCombatMove = true;
_run = true;
_canAutoAttack = true;
_canCombatMove = true;
mHasConditions = Global.ConditionMgr.HasConditionsForNotGroupedEntry(ConditionSourceType.CreatureTemplateVehicle, creature.GetEntry());
_hasConditions = Global.ConditionMgr.HasConditionsForNotGroupedEntry(ConditionSourceType.CreatureTemplateVehicle, creature.GetEntry());
}
bool IsAIControlled()
{
return !mIsCharmed;
return !_isCharmed;
}
public void StartPath(bool run = false, uint pathId = 0, bool repeat = false, Unit invoker = null, uint nodeId = 1)
@@ -57,6 +99,8 @@ namespace Game.AI
if (HasEscortState(SmartEscortState.Escorting))
StopPath();
SetRun(run);
if (pathId != 0)
{
if (!LoadPath(pathId))
@@ -104,7 +148,7 @@ namespace Game.AI
{
GridDefines.NormalizeMapCoord(ref waypoint.x);
GridDefines.NormalizeMapCoord(ref waypoint.y);
waypoint.moveType = mRun ? WaypointMoveType.Run : WaypointMoveType.Walk;
waypoint.moveType = _run ? WaypointMoveType.Run : WaypointMoveType.Walk;
}
GetScript().SetPathId(entry);
@@ -118,8 +162,8 @@ namespace Game.AI
me.PauseMovement(delay, MovementSlot.Idle, forced);
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint)
{
var waypointInfo = me.GetCurrentWaypointInfo();
GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, waypointInfo.nodeId, waypointInfo.pathId);
var (nodeId, pathId) = me.GetCurrentWaypointInfo();
GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, nodeId, pathId);
}
return;
}
@@ -135,7 +179,7 @@ namespace Game.AI
if (forced)
{
_waypointPauseForced = forced;
SetRun(mRun);
SetRun(_run);
me.PauseMovement();
me.SetHomePosition(me.GetPosition());
}
@@ -154,7 +198,7 @@ namespace Game.AI
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint)
waypointInfo = me.GetCurrentWaypointInfo();
if (mDespawnState != 2)
if (_despawnState != 2)
SetDespawnTime(despawnTime);
me.GetMotionMaster().MoveIdle();
@@ -166,16 +210,16 @@ namespace Game.AI
{
if (waypointInfo.Item1 != 0)
GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, waypointInfo.Item1, waypointInfo.Item2);
if (mDespawnState == 1)
if (_despawnState == 1)
StartDespawn();
}
return;
}
if (quest != 0)
mEscortQuestID = quest;
EscortQuestID = quest;
if (mDespawnState != 2)
if (_despawnState != 2)
SetDespawnTime(despawnTime);
me.GetMotionMaster().MoveIdle();
@@ -198,16 +242,16 @@ namespace Game.AI
}
List<WorldObject> targets = GetScript().GetStoredTargetList(SharedConst.SmartEscortTargets, me);
if (targets != null && mEscortQuestID != 0)
if (targets != null && EscortQuestID != 0)
{
if (targets.Count == 1 && GetScript().IsPlayer(targets.First()))
{
Player player = targets.First().ToPlayer();
if (!fail && player.IsAtGroupRewardDistance(me) && player.GetCorpse() == null)
player.GroupEventHappens(mEscortQuestID, me);
player.GroupEventHappens(EscortQuestID, me);
if (fail)
player.FailQuest(mEscortQuestID);
player.FailQuest(EscortQuestID);
Group group = player.GetGroup();
if (group)
@@ -219,9 +263,9 @@ namespace Game.AI
continue;
if (!fail && groupGuy.IsAtGroupRewardDistance(me) && !groupGuy.GetCorpse())
groupGuy.AreaExploredOrEventHappens(mEscortQuestID);
groupGuy.AreaExploredOrEventHappens(EscortQuestID);
else if (fail)
groupGuy.FailQuest(mEscortQuestID);
groupGuy.FailQuest(EscortQuestID);
}
}
}
@@ -233,9 +277,9 @@ namespace Game.AI
{
Player player = obj.ToPlayer();
if (!fail && player.IsAtGroupRewardDistance(me) && player.GetCorpse() == null)
player.AreaExploredOrEventHappens(mEscortQuestID);
player.AreaExploredOrEventHappens(EscortQuestID);
else if (fail)
player.FailQuest(mEscortQuestID);
player.FailQuest(EscortQuestID);
}
}
}
@@ -250,12 +294,12 @@ namespace Game.AI
if (_repeatWaypointPath)
{
if (IsAIControlled())
StartPath(mRun, GetScript().GetPathId(), _repeatWaypointPath);
StartPath(_run, GetScript().GetPathId(), _repeatWaypointPath);
}
else
GetScript().SetPathId(0);
if (mDespawnState == 1)
if (_despawnState == 1)
StartDespawn();
}
@@ -269,7 +313,7 @@ namespace Game.AI
_waypointReached = false;
_waypointPauseTimer = 0;
SetRun(mRun);
SetRun(_run);
me.ResumeMovement();
}
@@ -298,7 +342,7 @@ namespace Game.AI
if (!UpdateVictim())
return;
if (mCanAutoAttack)
if (_canAutoAttack)
DoMeleeAttackIfReady();
}
@@ -382,7 +426,7 @@ namespace Game.AI
if (_currentWaypointNode == _path.nodes.Count)
_waypointPathEnded = true;
else
SetRun(mRun);
SetRun(_run);
}
}
@@ -411,7 +455,7 @@ namespace Game.AI
public override void EnterEvadeMode(EvadeReason why = EvadeReason.Other)
{
if (mEvadeDisabled)
if (_evadeDisabled)
{
GetScript().ProcessEventsFor(SmartEvents.Evade);
return;
@@ -430,7 +474,7 @@ namespace Game.AI
GetScript().ProcessEventsFor(SmartEvents.Evade); // must be after _EnterEvadeMode (spells, auras, ...)
SetRun(mRun);
SetRun(_run);
Unit owner = me.GetCharmerOrOwner();
if (owner != null)
@@ -445,10 +489,10 @@ namespace Game.AI
}
else
{
Unit target = !mFollowGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, mFollowGuid) : null;
Unit target = !_followGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, _followGuid) : null;
if (target)
{
me.GetMotionMaster().MoveFollow(target, mFollowDist, mFollowAngle);
me.GetMotionMaster().MoveFollow(target, _followDist, _followAngle);
// evade is not cleared in MoveFollow, so we can't keep it
me.ClearUnitState(UnitState.Evade);
}
@@ -526,9 +570,9 @@ namespace Game.AI
public override void JustAppeared()
{
mDespawnTime = 0;
mRespawnTime = 0;
mDespawnState = 0;
_despawnTime = 0;
_respawnTime = 0;
_despawnState = 0;
_escortState = SmartEscortState.None;
me.SetVisible(true);
@@ -539,13 +583,13 @@ namespace Game.AI
GetScript().OnReset();
GetScript().ProcessEventsFor(SmartEvents.Respawn);
mFollowGuid.Clear();//do not reset follower on Reset(), we need it after combat evade
mFollowDist = 0;
mFollowAngle = 0;
mFollowCredit = 0;
mFollowArrivedTimer = 1000;
mFollowArrivedEntry = 0;
mFollowCreditType = 0;
_followGuid.Clear();//do not reset follower on Reset(), we need it after combat evade
_followDist = 0;
_followAngle = 0;
_followCredit = 0;
_followArrivedTimer = 1000;
_followArrivedEntry = 0;
_followCreditType = 0;
}
public override void JustReachedHome()
@@ -597,18 +641,18 @@ namespace Game.AI
if (!IsAIControlled())
{
if (who != null)
me.Attack(who, mCanAutoAttack);
me.Attack(who, _canAutoAttack);
return;
}
if (who != null && me.Attack(who, mCanAutoAttack))
if (who != null && me.Attack(who, _canAutoAttack))
{
me.GetMotionMaster().Clear(MovementSlot.Active);
me.PauseMovement();
if (mCanCombatMove)
if (_canCombatMove)
{
SetRun(mRun);
SetRun(_run);
me.GetMotionMaster().MoveChase(who);
}
}
@@ -631,8 +675,8 @@ namespace Game.AI
if (!IsAIControlled()) // don't allow players to use unkillable units
return;
if (mInvincibilityHpLevel != 0 && (damage >= me.GetHealth() - mInvincibilityHpLevel))
damage = (uint)(me.GetHealth() - mInvincibilityHpLevel); // damage should not be nullified, because of player damage req.
if (_invincibilityHpLevel != 0 && (damage >= me.GetHealth() - _invincibilityHpLevel))
damage = (uint)(me.GetHealth() - _invincibilityHpLevel); // damage should not be nullified, because of player damage req.
}
public override void HealReceived(Unit by, uint addhealth)
@@ -672,7 +716,7 @@ namespace Game.AI
public override void InitializeAI()
{
mScript.OnInitialize(me);
_script.OnInitialize(me);
if (!me.IsDead())
GetScript().OnReset();
@@ -686,14 +730,14 @@ namespace Game.AI
EndPath(true);
}
mIsCharmed = apply;
_isCharmed = apply;
if (!apply && !me.IsInEvadeMode())
{
if (_repeatWaypointPath)
StartPath(mRun, GetScript().GetPathId(), true);
StartPath(_run, GetScript().GetPathId(), true);
else
me.SetWalk(!mRun);
me.SetWalk(!_run);
Unit charmer = me.GetCharmer();
if (charmer)
@@ -728,7 +772,7 @@ namespace Game.AI
public void SetRun(bool run)
{
me.SetWalk(!run);
mRun = run;
_run = run;
}
public void SetDisableGravity(bool disable = true)
@@ -748,7 +792,7 @@ namespace Game.AI
public void SetEvadeDisabled(bool disable)
{
mEvadeDisabled = disable;
_evadeDisabled = disable;
}
public override bool GossipHello(Player player)
@@ -782,10 +826,10 @@ namespace Game.AI
public void SetCombatMove(bool on)
{
if (mCanCombatMove == on)
if (_canCombatMove == on)
return;
mCanCombatMove = on;
_canCombatMove = on;
if (!IsAIControlled())
return;
@@ -794,7 +838,7 @@ namespace Game.AI
{
if (on && !me.HasReactState(ReactStates.Passive) && me.GetVictim() && me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Max)
{
SetRun(mRun);
SetRun(_run);
me.GetMotionMaster().MoveChase(me.GetVictim());
}
else if (!on && me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Chase)
@@ -810,39 +854,39 @@ namespace Game.AI
return;
}
mFollowGuid = target.GetGUID();
mFollowDist = dist;
mFollowAngle = angle;
mFollowArrivedTimer = 1000;
mFollowCredit = credit;
mFollowArrivedEntry = end;
mFollowCreditType = creditType;
SetRun(mRun);
me.GetMotionMaster().MoveFollow(target, mFollowDist, mFollowAngle);
_followGuid = target.GetGUID();
_followDist = dist;
_followAngle = angle;
_followArrivedTimer = 1000;
_followCredit = credit;
_followArrivedEntry = end;
_followCreditType = creditType;
SetRun(_run);
me.GetMotionMaster().MoveFollow(target, _followDist, _followAngle);
}
public void StopFollow(bool complete)
{
mFollowGuid.Clear();
mFollowDist = 0;
mFollowAngle = 0;
mFollowCredit = 0;
mFollowArrivedTimer = 1000;
mFollowArrivedEntry = 0;
mFollowCreditType = 0;
_followGuid.Clear();
_followDist = 0;
_followAngle = 0;
_followCredit = 0;
_followArrivedTimer = 1000;
_followArrivedEntry = 0;
_followCreditType = 0;
me.StopMoving();
me.GetMotionMaster().MoveIdle();
if (!complete)
return;
Player player = Global.ObjAccessor.GetPlayer(me, mFollowGuid);
Player player = Global.ObjAccessor.GetPlayer(me, _followGuid);
if (player != null)
{
if (mFollowCreditType == 0)
player.RewardPlayerAndGroupAtEvent(mFollowCredit, me);
if (_followCreditType == 0)
player.RewardPlayerAndGroupAtEvent(_followCredit, me);
else
player.GroupEventHappens(mFollowCredit, me);
player.GroupEventHappens(_followCredit, me);
}
SetDespawnTime(5000);
@@ -853,7 +897,7 @@ namespace Game.AI
public void SetScript9(SmartScriptHolder e, uint entry, Unit invoker)
{
if (invoker != null)
GetScript().mLastInvoker = invoker.GetGUID();
GetScript().LastInvoker = invoker.GetGUID();
GetScript().SetScript9(e, entry);
}
@@ -872,10 +916,10 @@ namespace Game.AI
void CheckConditions(uint diff)
{
if (!mHasConditions)
if (!_hasConditions)
return;
if (mConditionsTimer <= diff)
if (_conditionsTimer <= diff)
{
Vehicle vehicleKit = me.GetVehicleKit();
if (vehicleKit != null)
@@ -898,10 +942,10 @@ namespace Game.AI
}
}
mConditionsTimer = 1000;
_conditionsTimer = 1000;
}
else
mConditionsTimer -= diff;
_conditionsTimer -= diff;
}
void UpdatePath(uint diff)
@@ -913,7 +957,7 @@ namespace Game.AI
{
if (!IsEscortInvokerInRange())
{
StopPath(0, mEscortQuestID, true);
StopPath(0, EscortQuestID, true);
// allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying
GetScript().ProcessEventsFor(SmartEvents.Death, me);
@@ -957,123 +1001,83 @@ namespace Game.AI
void UpdateFollow(uint diff)
{
if (mFollowGuid.IsEmpty())
if (_followGuid.IsEmpty())
{
if (mFollowArrivedTimer < diff)
if (_followArrivedTimer < diff)
{
if (me.FindNearestCreature(mFollowArrivedEntry, SharedConst.InteractionDistance, true))
if (me.FindNearestCreature(_followArrivedEntry, SharedConst.InteractionDistance, true))
{
StopFollow(true);
return;
}
mFollowArrivedTimer = 1000;
_followArrivedTimer = 1000;
}
else
mFollowArrivedTimer -= diff;
_followArrivedTimer -= diff;
}
}
void UpdateDespawn(uint diff)
{
if (mDespawnState <= 1 || mDespawnState > 3)
if (_despawnState <= 1 || _despawnState > 3)
return;
if (mDespawnTime < diff)
if (_despawnTime < diff)
{
if (mDespawnState == 2)
if (_despawnState == 2)
{
me.SetVisible(false);
mDespawnTime = 5000;
mDespawnState++;
_despawnTime = 5000;
_despawnState++;
}
else
me.DespawnOrUnsummon(0, TimeSpan.FromSeconds(mRespawnTime));
me.DespawnOrUnsummon(0, TimeSpan.FromSeconds(_respawnTime));
}
else
mDespawnTime -= diff;
_despawnTime -= diff;
}
public override void Reset()
{
if (!HasEscortState(SmartEscortState.Escorting))//dont mess up escort movement after combat
SetRun(mRun);
SetRun(_run);
GetScript().OnReset();
}
public bool HasEscortState(SmartEscortState uiEscortState) { return (_escortState & uiEscortState) != 0; }
public void AddEscortState(SmartEscortState uiEscortState) { _escortState |= uiEscortState; }
public void RemoveEscortState(SmartEscortState uiEscortState) { _escortState &= ~uiEscortState; }
public void SetAutoAttack(bool on) { mCanAutoAttack = on; }
public void SetAutoAttack(bool on) { _canAutoAttack = on; }
public bool CanCombatMove() { return mCanCombatMove; }
public bool CanCombatMove() { return _canCombatMove; }
public SmartScript GetScript() { return mScript; }
public SmartScript GetScript() { return _script; }
public void SetInvincibilityHpLevel(uint level) { mInvincibilityHpLevel = level; }
public void SetInvincibilityHpLevel(uint level) { _invincibilityHpLevel = level; }
public void SetDespawnTime(uint t, uint r = 0)
{
mDespawnTime = t;
mRespawnTime = r;
mDespawnState = t != 0 ? 1 : 0u;
_despawnTime = t;
_respawnTime = r;
_despawnState = t != 0 ? 1 : 0u;
}
public void StartDespawn() { mDespawnState = 2; }
public void StartDespawn() { _despawnState = 2; }
public void SetWPPauseTimer(uint time) { _waypointPauseTimer = time; }
public void SetGossipReturn(bool val) { _gossipReturn = val; }
public uint mEscortQuestID;
SmartScript mScript = new SmartScript();
bool mIsCharmed;
uint mFollowCreditType;
uint mFollowArrivedTimer;
uint mFollowCredit;
uint mFollowArrivedEntry;
ObjectGuid mFollowGuid;
float mFollowDist;
float mFollowAngle;
SmartEscortState _escortState;
uint _escortNPCFlags;
uint _escortInvokerCheckTimer;
WaypointPath _path;
uint _currentWaypointNode;
bool _waypointReached;
uint _waypointPauseTimer;
bool _waypointPauseForced;
bool _repeatWaypointPath;
bool _OOCReached;
bool _waypointPathEnded;
bool mRun;
bool mEvadeDisabled;
bool mCanAutoAttack;
bool mCanCombatMove;
uint mInvincibilityHpLevel;
uint mDespawnTime;
uint mRespawnTime;
uint mDespawnState;
// Vehicle conditions
bool mHasConditions;
uint mConditionsTimer;
// Gossip
bool _gossipReturn;
}
public class SmartGameObjectAI : GameObjectAI
{
public SmartGameObjectAI(GameObject g) : base(g)
{
mScript = new SmartScript();
}
SmartScript _script = new();
// Gossip
bool _gossipReturn;
public SmartGameObjectAI(GameObject g) : base(g) { }
public override void UpdateAI(uint diff)
{
@@ -1142,7 +1146,7 @@ namespace Game.AI
public void SetScript9(SmartScriptHolder e, uint entry, Unit invoker)
{
if (invoker != null)
GetScript().mLastInvoker = invoker.GetGUID();
GetScript().LastInvoker = invoker.GetGUID();
GetScript().SetScript9(e, entry);
}
@@ -1168,16 +1172,13 @@ namespace Game.AI
public void SetGossipReturn(bool val) { _gossipReturn = val; }
public SmartScript GetScript() { return mScript; }
SmartScript mScript;
// Gossip
bool _gossipReturn;
public SmartScript GetScript() { return _script; }
}
public class SmartAreaTriggerAI : AreaTriggerAI
{
SmartScript _script = new();
public SmartAreaTriggerAI(AreaTrigger areaTrigger) : base(areaTrigger) { }
public override void OnInitialize()
@@ -1198,13 +1199,12 @@ namespace Game.AI
public void SetScript9(SmartScriptHolder e, uint entry, Unit invoker)
{
if (invoker)
GetScript().mLastInvoker = invoker.GetGUID();
GetScript().LastInvoker = invoker.GetGUID();
GetScript().SetScript9(e, entry);
}
SmartScript GetScript() { return mScript; }
SmartScript mScript;
public SmartScript GetScript() { return _script; }
}
public enum SmartEscortState
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff