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