Core/Movement: waypoint movement

Port From (https://github.com/TrinityCore/TrinityCore/commit/97585597f0b1aff93873fe4d757556731bc0c1b2)
This commit is contained in:
hondacrx
2020-08-24 17:02:02 -04:00
parent c9e535bb3a
commit 3d3fd0f55f
31 changed files with 960 additions and 950 deletions
+4
View File
@@ -526,6 +526,10 @@ namespace Game.AI
// Called when the dialog status between a player and the creature is requested.
public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedNoStatus; }
public virtual void WaypointStarted(uint nodeId, uint pathId) { }
public virtual void WaypointReached(uint nodeId, uint pathId) { }
public AISpellInfoType GetAISpellInfo(uint spellId, Difficulty difficulty)
{
return AISpellInfo.LookupByKey((spellId, difficulty));
+212 -324
View File
@@ -19,30 +19,27 @@ using Framework.Constants;
using Game.Entities;
using Game.Groups;
using Game.Maps;
using Game.Movement;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.AI
{
public class NpcEscortAI : ScriptedAI
public class EscortAI : ScriptedAI
{
public NpcEscortAI(Creature creature) : base(creature)
public EscortAI(Creature creature) : base(creature)
{
m_uiPlayerGUID = ObjectGuid.Empty;
m_uiWPWaitTimer = 2500;
m_uiPlayerCheckTimer = 1000;
m_uiEscortState = EscortState.None;
MaxPlayerDistance = 50;
m_pQuestForEscort = null;
m_bIsActiveAttacker = true;
m_bIsRunning = false;
m_bCanInstantRespawn = false;
m_bCanReturnToStart = false;
DespawnAtEnd = true;
DespawnAtFar = true;
ScriptWP = false;
HasImmuneToNPCFlags = false;
_pauseTimer = 2500;
_playerCheckTimer = 1000;
_maxPlayerDistance = 50;
_activeAttacker = true;
_despawnAtEnd = true;
_despawnAtFar = true;
}
public Player GetPlayerForEscort()
{
return Global.ObjAccessor.GetPlayer(me, _playerGUID);
}
public override void AttackStart(Unit target)
@@ -66,6 +63,9 @@ namespace Game.AI
if (!who || !who.GetVictim())
return false;
if (me.HasReactState(ReactStates.Passive))
return false;
//experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
return false;
@@ -75,23 +75,14 @@ namespace Game.AI
return false;
//never attack friendly
if (me.IsFriendlyTo(who))
if (me.IsValidAssistTarget(who.GetVictim()))
return false;
//too far away and no free sight?
if (me.IsWithinDistInMap(who, GetMaxPlayerDistance()) && me.IsWithinLOSInMap(who))
{
//already fighting someone?
if (!me.GetVictim())
{
AttackStart(who);
return true;
}
else
{
me.EngageWithTarget(who);
return true;
}
me.EngageWithTarget(who);
return true;
}
return false;
@@ -99,28 +90,20 @@ namespace Game.AI
public override void MoveInLineOfSight(Unit who)
{
if (me.HasReactState(ReactStates.Aggressive) && !me.HasUnitState(UnitState.Stunned) && who.IsTargetableForAttack() && who.IsInAccessiblePlaceFor(me))
{
if (HasEscortState(EscortState.Escorting) && AssistPlayerInCombatAgainst(who))
return;
if (who == null)
return;
if (!me.CanFly() && me.GetDistanceZ(who) > SharedConst.CreatureAttackRangeZ)
return;
if (HasEscortState(EscortState.Escorting) && AssistPlayerInCombatAgainst(who))
return;
if (me.IsHostileTo(who))
{
float fAttackRadius = me.GetAttackDistance(who);
if (me.IsWithinDistInMap(who, fAttackRadius) && me.IsWithinLOSInMap(who))
me.EngageWithTarget(who);
}
}
base.MoveInLineOfSight(who);
}
public override void JustDied(Unit killer)
{
if (!HasEscortState(EscortState.Escorting) || m_uiPlayerGUID.IsEmpty() || m_pQuestForEscort == null)
if (!HasEscortState(EscortState.Escorting) || _playerGUID.IsEmpty() || _escortQuest == null)
return;
Player player = GetPlayerForEscort();
if (player)
{
@@ -132,23 +115,23 @@ namespace Game.AI
Player member = groupRef.GetSource();
if (member)
if (member.IsInMap(player))
member.FailQuest(m_pQuestForEscort.Id);
member.FailQuest(_escortQuest.Id);
}
}
else
player.FailQuest(m_pQuestForEscort.Id);
player.FailQuest(_escortQuest.Id);
}
}
public override void JustAppeared()
{
m_uiEscortState = EscortState.None;
_escortState = EscortState.None;
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
//add a small delay before going to first waypoint, normal in near all cases
m_uiWPWaitTimer = 2500;
_pauseTimer = 2000;
if (me.GetFaction() != me.GetCreatureTemplate().Faction)
me.RestoreFaction();
@@ -172,12 +155,12 @@ namespace Game.AI
{
AddEscortState(EscortState.Returning);
ReturnToLastPoint();
Log.outDebug(LogFilter.Scripts, "EscortAI has left combat and is now returning to last point");
Log.outDebug(LogFilter.Scripts, "EscortAI.EnterEvadeMode has left combat and is now returning to last point");
}
else
{
me.GetMotionMaster().MoveTargetedHome();
if (HasImmuneToNPCFlags)
if (_hasImmuneToNPCFlags)
me.AddUnitFlag(UnitFlags.ImmuneToNpc);
Reset();
}
@@ -209,87 +192,78 @@ namespace Game.AI
public override void UpdateAI(uint diff)
{
//Waypoint Updating
if (HasEscortState(EscortState.Escorting) && !me.GetVictim() && m_uiWPWaitTimer != 0 && !HasEscortState(EscortState.Returning))
if (HasEscortState(EscortState.Escorting) && !me.IsEngaged() && !HasEscortState(EscortState.Returning))
{
if (m_uiWPWaitTimer <= diff)
if (_pauseTimer <= diff)
{
//End of the line
if (WaypointList[CurrentWPIndex] == null)
{
m_uiWPWaitTimer = 0;
if (DespawnAtEnd)
{
Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints");
if (m_bCanReturnToStart)
{
float fRetX, fRetY, fRetZ;
me.GetRespawnPosition(out fRetX, out fRetY, out fRetZ);
me.GetMotionMaster().MovePoint(EscortPointIds.Home, fRetX, fRetY, fRetZ);
m_uiWPWaitTimer = 0;
Log.outDebug(LogFilter.Scripts, $"EscortAI are returning home to spawn location: {EscortPointIds.Home}, {fRetX}, {fRetY}, {fRetZ}");
return;
}
if (m_bCanInstantRespawn && !WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
{
me.SetDeathState(DeathState.JustDied);
me.Respawn();
}
else
{
if (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc))
me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
me.DespawnOrUnsummon();
}
return;
}
else
{
Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints with Despawn off");
return;
}
}
if (!HasEscortState(EscortState.Paused))
{
var currentWp = WaypointList[CurrentWPIndex];
me.GetMotionMaster().MovePoint(currentWp.Id, currentWp.X, currentWp.Y, currentWp.Z);
Log.outDebug(LogFilter.Scripts, $"EscortAI start waypoint {currentWp.Id} ({currentWp.X}, {currentWp.Y}, {currentWp.Z}).");
_pauseTimer = 0;
WaypointStart(currentWp.Id);
if (_ended)
{
_ended = false;
me.GetMotionMaster().MoveIdle();
m_uiWPWaitTimer = 0;
if (_despawnAtEnd)
{
Log.outDebug(LogFilter.Scripts, "EscortAI.UpdateAI: reached end of waypoints, despawning at end");
if (_returnToStart)
{
Position respawnPosition = new Position();
float orientation;
me.GetRespawnPosition(out respawnPosition.posX, out respawnPosition.posY, out respawnPosition.posZ, out orientation);
respawnPosition.SetOrientation(orientation);
me.GetMotionMaster().MovePoint(EscortPointIds.Home, respawnPosition);
Log.outDebug(LogFilter.Scripts, $"EscortAI.UpdateAI: returning to spawn location: {respawnPosition}");
}
else if (_instantRespawn)
me.Respawn();
else
me.DespawnOrUnsummon();
}
Log.outDebug(LogFilter.Scripts, "EscortAI.UpdateAI: reached end of waypoints");
RemoveEscortState(EscortState.Escorting);
return;
}
if (!_started)
{
_started = true;
me.GetMotionMaster().MovePath(_path, false);
}
else if (_resume)
{
_resume = false;
IMovementGenerator movementGenerator = me.GetMotionMaster().GetMotionSlot(MovementSlot.Idle);
if (movementGenerator != null)
movementGenerator.Resume(0);
}
}
}
else
_pauseTimer -= diff;
}
//Check if player or any member of his group is within range
if (HasEscortState(EscortState.Escorting) && !m_uiPlayerGUID.IsEmpty() && !me.GetVictim() && !HasEscortState(EscortState.Returning))
if (_despawnAtFar && HasEscortState(EscortState.Escorting) && !_playerGUID.IsEmpty() && !me.GetVictim() && !HasEscortState(EscortState.Returning))
{
if (m_uiPlayerCheckTimer <= diff)
if (_playerCheckTimer <= diff)
{
if (DespawnAtFar && !IsPlayerOrGroupInRange())
if (!IsPlayerOrGroupInRange())
{
Log.outDebug(LogFilter.Scripts, "EscortAI failed because player/group was to far away or not found");
bool isEscort = false;
CreatureData cdata = me.GetCreatureData();
if (cdata != null)
isEscort = (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && cdata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc));
CreatureData creatureData = me.GetCreatureData();
if (creatureData != null)
isEscort = (WorldConfig.GetBoolValue(WorldCfg.RespawnDynamicEscortNpc) && creatureData.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.EscortQuestNpc));
if (m_bCanInstantRespawn && !isEscort)
{
me.SetDeathState(DeathState.JustDied);
me.Respawn();
}
else if (m_bCanInstantRespawn && isEscort)
if (_instantRespawn && !isEscort)
me.DespawnOrUnsummon(0, TimeSpan.FromSeconds(1));
else if (_instantRespawn && isEscort)
me.GetMap().RemoveRespawnTime(SpawnObjectType.Creature, me.GetSpawnId(), true);
else
me.DespawnOrUnsummon();
@@ -297,10 +271,10 @@ namespace Game.AI
return;
}
m_uiPlayerCheckTimer = 1000;
_playerCheckTimer = 1000;
}
else
m_uiPlayerCheckTimer -= diff;
_playerCheckTimer -= diff;
}
UpdateEscortAI(diff);
@@ -314,90 +288,94 @@ namespace Game.AI
DoMeleeAttackIfReady();
}
public override void MovementInform(MovementGeneratorType moveType, uint pointId)
public override void MovementInform(MovementGeneratorType moveType, uint Id)
{
if (moveType != MovementGeneratorType.Point || !HasEscortState(EscortState.Escorting))
// no action allowed if there is no escort
if (!HasEscortState(EscortState.Escorting))
return;
//Combat start position reached, continue waypoint movement
if (pointId == EscortPointIds.LastPoint)
if (moveType == MovementGeneratorType.Point)
{
Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original position before combat");
if (_pauseTimer == 0)
_pauseTimer = 2000;
me.SetWalk(!m_bIsRunning);
RemoveEscortState(EscortState.Returning);
if (m_uiWPWaitTimer == 0)
m_uiWPWaitTimer = 1;
}
else if (pointId == EscortPointIds.Home)
{
Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original home location and will continue from beginning of waypoint list.");
CurrentWPIndex = 0;
m_uiWPWaitTimer = 1;
}
else
{
var currentWp = WaypointList[CurrentWPIndex];
//Make sure that we are still on the right waypoint
if (currentWp.Id != pointId)
if (Id == EscortPointIds.LastPoint)
{
Log.outError(LogFilter.Misc, $"TSCR ERROR: EscortAI reached waypoint out of order {pointId}, expected {currentWp.Id}, creature entry {me.GetEntry()}");
return;
Log.outDebug(LogFilter.Scripts, "EscortAI.MovementInform has returned to original position before combat");
me.SetWalk(!_running);
RemoveEscortState(EscortState.Returning);
}
else if (Id == EscortPointIds.Home)
{
Log.outDebug(LogFilter.Scripts, "EscortAI.MovementInform: returned to home location and restarting waypoint path");
_started = false;
}
}
else if (moveType == MovementGeneratorType.Waypoint)
{
Cypher.Assert(Id < _path.nodes.Count, $"EscortAI.MovementInform: referenced movement id ({Id}) points to non-existing node in loaded path");
WaypointNode waypoint = _path.nodes[(int)Id];
Log.outDebug(LogFilter.Scripts, $"EscortAI Waypoint {currentWp.Id} reached");
Log.outDebug(LogFilter.Scripts, $"EscortAI.MovementInform: waypoint node {waypoint.id} reached");
//Call WP function
WaypointReached(currentWp.Id);
m_uiWPWaitTimer = currentWp.WaitTimeMs + 1;
++CurrentWPIndex;
// last point
if (Id == _path.nodes.Count - 1)
{
_started = false;
_ended = true;
_pauseTimer = 1000;
}
}
}
public void AddWaypoint(uint id, float x, float y, float z, uint waitTime = 0)
public void AddWaypoint(uint id, float x, float y, float z, float orientation = 0, uint waitTime = 0)
{
Escort_Waypoint t = new Escort_Waypoint(id, x, y, z, waitTime);
GridDefines.NormalizeMapCoord(ref x);
GridDefines.NormalizeMapCoord(ref y);
WaypointList.Add(t);
WaypointNode waypoint = new WaypointNode();
waypoint.id = id;
waypoint.x = x;
waypoint.y = y;
waypoint.z = z;
waypoint.orientation = orientation;
waypoint.moveType = _running ? WaypointMoveType.Run : WaypointMoveType.Walk;
waypoint.delay = waitTime;
waypoint.eventId = 0;
waypoint.eventChance = 100;
_path.nodes.Add(waypoint);
ScriptWP = true;
_manualPath = true;
}
void FillPointMovementListForCreature()
{
var movePoints = Global.ScriptMgr.GetPointMoveList(me.GetEntry());
if (movePoints.Empty())
WaypointPath path = Global.WaypointMgr.GetPath(me.GetEntry());
if (path == null)
return;
foreach (var point in movePoints)
foreach (WaypointNode value in path.nodes)
{
Escort_Waypoint wayPoint = new Escort_Waypoint(point.uiPointId, point.fX, point.fY, point.fZ, point.uiWaitTime);
WaypointList.Add(wayPoint);
WaypointNode node = value;
GridDefines.NormalizeMapCoord(ref node.x);
GridDefines.NormalizeMapCoord(ref node.y);
node.moveType = _running ? WaypointMoveType.Run : WaypointMoveType.Walk;
_path.nodes.Add(node);
}
}
public void SetRun(bool on = true)
{
if (on)
{
if (!m_bIsRunning)
me.SetWalk(false);
else
Log.outDebug(LogFilter.Scripts, "EscortAI attempt to set run mode, but is already running.");
}
else
{
if (m_bIsRunning)
me.SetWalk(true);
else
Log.outDebug(LogFilter.Scripts, "EscortAI attempt to set walk mode, but is already walking.");
}
if (on && !_running)
me.SetWalk(false);
else if (!on && _running)
me.SetWalk(true);
m_bIsRunning = on;
_running = on;
}
/// todo get rid of this many variables passed in function.
@@ -424,69 +402,54 @@ namespace Game.AI
if (me.GetVictim())
{
Log.outError(LogFilter.Server, "TSCR ERROR: EscortAI (script: {0}, creature entry: {1}) attempts to Start while in combat", me.GetScriptName(), me.GetEntry());
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) attempts to Start while in combat");
return;
}
if (HasEscortState(EscortState.Escorting))
{
Log.outError(LogFilter.Scripts, "EscortAI (script: {0}, creature entry: {1}) attempts to Start while already escorting", me.GetScriptName(), me.GetEntry());
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) attempts to Start while already escorting");
return;
}
if (!ScriptWP && resetWaypoints)
{
if (!WaypointList.Empty())
WaypointList.Clear();
if (!_manualPath && resetWaypoints)
FillPointMovementListForCreature();
}
if (WaypointList.Empty())
if (_path.nodes.Empty())
{
Log.outError(LogFilter.Scripts, $"EscortAI (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {(quest != null ? quest.Id : 0)}).");
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {(quest != null ? quest.Id : 0)}).");
return;
}
//set variables
m_bIsActiveAttacker = isActiveAttacker;
m_bIsRunning = run;
// set variables
_activeAttacker = isActiveAttacker;
_running = run;
_playerGUID = playerGUID;
_escortQuest = quest;
_instantRespawn = instantRespawn;
_returnToStart = canLoopPath;
m_uiPlayerGUID = playerGUID;
m_pQuestForEscort = quest;
if (_returnToStart && _instantRespawn)
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
m_bCanInstantRespawn = instantRespawn;
m_bCanReturnToStart = canLoopPath;
if (m_bCanReturnToStart && m_bCanInstantRespawn)
Log.outDebug(LogFilter.Scripts, "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint)
{
me.GetMotionMaster().MovementExpired();
me.GetMotionMaster().MoveIdle();
Log.outDebug(LogFilter.Scripts, "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
}
me.GetMotionMaster().MoveIdle();
me.GetMotionMaster().Clear(MovementSlot.Active);
//disable npcflags
me.SetNpcFlags(NPCFlags.None);
me.SetNpcFlags2(NPCFlags2.None);
if (me.HasUnitFlag(UnitFlags.ImmuneToNpc))
{
HasImmuneToNPCFlags = true;
_hasImmuneToNPCFlags = true;
me.RemoveUnitFlag(UnitFlags.ImmuneToNpc);
}
Log.outDebug(LogFilter.Scripts, $"EscortAI started. ActiveAttacker = {m_bIsActiveAttacker}, Run = {m_bIsRunning}, PlayerGUID = {m_uiPlayerGUID}");
Log.outDebug(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) started with {_path.nodes.Count} waypoints. ActiveAttacker = {_activeAttacker}, Run = {_running}, Player = {_playerGUID}");
CurrentWPIndex = 0;
//Set initial speed
if (m_bIsRunning)
me.SetWalk(false);
else
me.SetWalk(true);
// set initial speed
me.SetWalk(!_running);
_started = false;
AddEscortState(EscortState.Escorting);
}
@@ -496,75 +459,17 @@ namespace Game.AI
return;
if (on)
{
AddEscortState(EscortState.Paused);
IMovementGenerator movementGenerator = me.GetMotionMaster().GetMotionSlot(MovementSlot.Idle);
if (movementGenerator != null)
movementGenerator.Pause(0);
}
else
{
RemoveEscortState(EscortState.Paused);
}
bool SetNextWaypoint(uint pointId, float x, float y, float z, float orientation)
{
me.UpdatePosition(x, y, z, orientation);
return SetNextWaypoint(pointId, false, true);
}
bool SetNextWaypoint(uint pointId, bool setPosition, bool resetWaypointsOnFail)
{
if (!WaypointList.Empty())
WaypointList.Clear();
FillPointMovementListForCreature();
if (WaypointList.Empty())
return false;
int size = WaypointList.Count;
Escort_Waypoint waypoint;
do
{
waypoint = WaypointList.First();
WaypointList.RemoveAt(0);
if (waypoint.Id == pointId)
{
if (setPosition)
me.UpdatePosition(waypoint.X, waypoint.Y, waypoint.Z, me.GetOrientation());
CurrentWPIndex = 0;
return true;
}
_resume = true;
}
while (!WaypointList.Empty());
// we failed.
// we reset the waypoints in the start; if we pulled any, reset it again
if (resetWaypointsOnFail && size != WaypointList.Count)
{
if (!WaypointList.Empty())
WaypointList.Clear();
FillPointMovementListForCreature();
}
return false;
}
bool GetWaypointPosition(uint pointId, ref float x, ref float y, ref float z)
{
var waypoints = Global.ScriptMgr.GetPointMoveList(me.GetEntry());
if (waypoints == null)
return false;
foreach (var pointMove in waypoints)
{
if (pointMove.uiPointId == pointId)
{
x = pointMove.fX;
y = pointMove.fY;
z = pointMove.fZ;
return true;
}
}
return false;
}
public override bool IsEscortNPC(bool onlyIfActive)
@@ -577,72 +482,55 @@ namespace Game.AI
return false;
}
public virtual void WaypointReached(uint pointId) { }
public virtual void WaypointStart(uint pointId) { }
public bool HasEscortState(EscortState escortState) { return m_uiEscortState.HasAnyFlag(escortState); }
public override bool IsEscorted() { return m_uiEscortState.HasAnyFlag(EscortState.Escorting); }
void SetPauseTimer(uint Timer) { _pauseTimer = Timer; }
public void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; }
public float GetMaxPlayerDistance() { return MaxPlayerDistance; }
public bool HasEscortState(EscortState escortState) { return (_escortState & escortState) != 0; }
public override bool IsEscorted() { return _escortState.HasAnyFlag(EscortState.Escorting); }
public void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; }
public void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; }
public bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
public void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
public ObjectGuid GetEventStarterGUID() { return m_uiPlayerGUID; }
void SetMaxPlayerDistance(float newMax) { _maxPlayerDistance = newMax; }
float GetMaxPlayerDistance() { return _maxPlayerDistance; }
public Player GetPlayerForEscort() { return Global.ObjAccessor.GetPlayer(me, m_uiPlayerGUID); }
public void SetDespawnAtEnd(bool despawn) { _despawnAtEnd = despawn; }
public void SetDespawnAtFar(bool despawn) { _despawnAtFar = despawn; }
void AddEscortState(EscortState escortState) { m_uiEscortState |= escortState; }
void RemoveEscortState(EscortState escortState) { m_uiEscortState &= ~escortState; }
bool GetAttack() { return _activeAttacker; } // used in EnterEvadeMode override
void SetCanAttack(bool attack) { _activeAttacker = attack; }
ObjectGuid m_uiPlayerGUID;
uint m_uiWPWaitTimer;
uint m_uiPlayerCheckTimer;
EscortState m_uiEscortState;
float MaxPlayerDistance;
ObjectGuid GetEventStarterGUID() { return _playerGUID; }
Quest m_pQuestForEscort; //generally passed in Start() when regular escort script.
void AddEscortState(EscortState escortState) { _escortState |= escortState; }
void RemoveEscortState(EscortState escortState) { _escortState &= ~escortState; }
List<Escort_Waypoint> WaypointList = new List<Escort_Waypoint>();
int CurrentWPIndex;
ObjectGuid _playerGUID;
uint _pauseTimer;
uint _playerCheckTimer;
EscortState _escortState;
float _maxPlayerDistance;
bool m_bIsActiveAttacker; //obsolete, determined by faction.
bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used)
bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests.
bool DespawnAtEnd;
bool DespawnAtFar;
bool ScriptWP;
bool HasImmuneToNPCFlags;
Quest _escortQuest; //generally passed in Start() when regular escort script.
WaypointPath _path;
bool _activeAttacker; // obsolete, determined by faction.
bool _running; // all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
bool _instantRespawn; // if creature should respawn instantly after escort over (if not, database respawntime are used)
bool _returnToStart; // if creature can walk same path (loop) without despawn. Not for regular escort quests.
bool _despawnAtEnd;
bool _despawnAtFar;
bool _manualPath;
bool _hasImmuneToNPCFlags;
bool _started;
bool _ended;
bool _resume;
}
public enum EscortState
{
None = 0x000, //nothing in progress
Escorting = 0x001, //escort are in progress
Returning = 0x002, //escort is returning after being in combat
Paused = 0x004 //will not proceed with waypoints before state is removed
}
class Escort_Waypoint
{
public Escort_Waypoint(uint id, float x, float y, float z, uint w)
{
Id = id;
X = x;
Y = y;
Z = z;
WaitTimeMs = w;
}
public uint Id;
public float X;
public float Y;
public float Z;
public uint WaitTimeMs;
None = 0x00, //nothing in progress
Escorting = 0x01, //escort are in progress
Returning = 0x02, //escort is returning after being in combat
Paused = 0x04 //will not proceed with waypoints before state is removed
}
struct EscortPointIds
+227 -244
View File
@@ -18,6 +18,7 @@
using Framework.Constants;
using Game.Entities;
using Game.Groups;
using Game.Maps;
using Game.Spells;
using System;
using System.Collections.Generic;
@@ -32,18 +33,11 @@ namespace Game.AI
public SmartAI(Creature creature) : base(creature)
{
// Spawn in run mode
me.SetWalk(false);
_escortInvokerCheckTimer = 1000;
mRun = true;
mLastOOCPos = me.GetPosition();
mCanAutoAttack = true;
mCanCombatMove = true;
mEscortInvokerCheckTimer = 1000;
mFollowGuid = ObjectGuid.Empty;
mHasConditions = Global.ConditionMgr.HasConditionsForNotGroupedEntry(ConditionSourceType.CreatureTemplateVehicle, creature.GetEntry());
}
@@ -72,70 +66,43 @@ namespace Game.AI
mDespawnTime -= diff;
}
public override void Reset()
{
if (!HasEscortState(SmartEscortState.Escorting))//dont mess up escort movement after combat
SetRun(mRun);
GetScript().OnReset();
}
WayPoint GetNextWayPoint()
{
if (mWayPoints.Empty())
return null;
mCurrentWPID++;
var wayPoint = mWayPoints.Find(p => p.Id == mCurrentWPID);
if (wayPoint != null)
{
mLastWP = wayPoint;
if (mLastWP.Id != mCurrentWPID)
{
Log.outError(LogFilter.Misc, "SmartAI.GetNextWayPoint: Got not expected waypoint id {mLastWP.id}, expected {mCurrentWPID}");
}
return wayPoint;
}
return null;
}
public void StartPath(bool run = false, uint path = 0, bool repeat = false, Unit invoker = null)
public void StartPath(bool run = false, uint pathId = 0, bool repeat = false, Unit invoker = null, uint nodeId = 1)
{
if (me.IsInCombat())// no wp movement in combat
{
Log.outError(LogFilter.Server, "SmartAI.StartPath: Creature entry {0} wanted to start waypoint movement while in combat, ignoring.", me.GetEntry());
Log.outError(LogFilter.Server, $"SmartAI.StartPath: Creature entry {me.GetEntry()} wanted to start waypoint movement while in combat, ignoring.");
return;
}
if (HasEscortState(SmartEscortState.Escorting))
StopPath();
if (path != 0)
if (pathId != 0)
{
if (!LoadPath(path))
if (!LoadPath(pathId))
return;
}
if (mWayPoints.Empty())
if (_path.nodes.Empty())
return;
WayPoint wp = GetNextWayPoint();
if (wp != null)
_currentWaypointNode = nodeId;
_waypointPathEnded = false;
_repeatWaypointPath = repeat;
// Do not use AddEscortState, removing everything from previous
_escortState = SmartEscortState.Escorting;
if (invoker && invoker.IsPlayer())
{
AddEscortState(SmartEscortState.Escorting);
mCanRepeatPath = repeat;
SetRun(run);
if (invoker != null && invoker.GetTypeId() == TypeId.Player)
{
mEscortNPCFlags = me.m_unitData.NpcFlags[0];
me.SetNpcFlags(NPCFlags.None);
}
mLastOOCPos = me.GetPosition();
me.GetMotionMaster().MovePoint(wp.Id, wp.X, wp.Y, wp.Z);
GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, wp.Id, GetScript().GetPathId());
_escortNPCFlags = me.m_unitData.NpcFlags[0];
me.SetNpcFlags((NPCFlags)0);
}
GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, _currentWaypointNode, GetScript().GetPathId());
me.GetMotionMaster().MovePath(_path, _repeatWaypointPath);
}
bool LoadPath(uint entry)
@@ -143,13 +110,22 @@ namespace Game.AI
if (HasEscortState(SmartEscortState.Escorting))
return false;
mWayPoints = Global.SmartAIMgr.GetPath(entry);
if (mWayPoints == null)
WaypointPath path = Global.SmartAIMgr.GetPath(entry);
if (path == null || path.nodes.Empty())
{
GetScript().SetPathId(0);
return false;
}
_path.id = path.id;
_path.nodes = path.nodes;
foreach (WaypointNode waypoint in _path.nodes)
{
GridDefines.NormalizeMapCoord(ref waypoint.x);
GridDefines.NormalizeMapCoord(ref waypoint.y);
waypoint.moveType = mRun ? WaypointMoveType.Run : WaypointMoveType.Walk;
}
GetScript().SetPathId(entry);
return true;
}
@@ -165,20 +141,23 @@ namespace Game.AI
return;
}
mForcedPaused = forced;
mLastOOCPos = me.GetPosition();
AddEscortState(SmartEscortState.Paused);
mWPPauseTimer = delay;
_waypointPauseTimer = delay;
if (forced)
{
_waypointPauseForced = forced;
SetRun(mRun);
me.StopMoving();//force stop
me.GetMotionMaster().MoveIdle();//force stop
me.PauseMovement();
me.SetHomePosition(me.GetPosition());
}
GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, mLastWP.Id, GetScript().GetPathId());
else
_waypointReached = false;
AddEscortState(SmartEscortState.Paused);
GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, _currentWaypointNode, GetScript().GetPathId());
}
public void StopPath(uint DespawnTime = 0, uint quest = 0, bool fail = false)
public void StopPath(uint despawnTime = 0, uint quest = 0, bool fail = false)
{
if (!HasEscortState(SmartEscortState.Escorting))
return;
@@ -186,40 +165,28 @@ namespace Game.AI
if (quest != 0)
mEscortQuestID = quest;
SetDespawnTime(DespawnTime);
//mDespawnTime = DespawnTime;
if (mDespawnState != 2)
SetDespawnTime(despawnTime);
mLastOOCPos = me.GetPosition();
me.StopMoving();//force stop
me.GetMotionMaster().MoveIdle();
GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, mLastWP.Id, GetScript().GetPathId());
GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, _currentWaypointNode, GetScript().GetPathId());
EndPath(fail);
}
public void EndPath(bool fail = false)
{
GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, mLastWP.Id, GetScript().GetPathId());
RemoveEscortState(SmartEscortState.Escorting | SmartEscortState.Paused | SmartEscortState.Returning);
mWayPoints = null;
mCurrentWPID = 0;
mWPPauseTimer = 0;
mLastWP = null;
_path.nodes.Clear();
_waypointPauseTimer = 0;
if (mEscortNPCFlags != 0)
if (_escortNPCFlags != 0)
{
me.SetNpcFlags((NPCFlags)mEscortNPCFlags);
mEscortNPCFlags = 0;
me.SetNpcFlags((NPCFlags)_escortNPCFlags);
_escortNPCFlags = 0;
}
if (mCanRepeatPath)
{
if (IsAIControlled())
StartPath(mRun, GetScript().GetPathId(), true);
}
else
GetScript().SetPathId(0);
List<WorldObject> targets = GetScript().GetStoredTargetList(SharedConst.SmartEscortTargets, me);
if (targets != null && mEscortQuestID != 0)
{
@@ -264,16 +231,36 @@ namespace Game.AI
}
}
// End Path events should be only processed if it was SUCCESSFUL stop or stop called by SMART_ACTION_WAYPOINT_STOP
if (fail)
return;
GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, _currentWaypointNode, GetScript().GetPathId());
if (_repeatWaypointPath)
{
if (IsAIControlled())
StartPath(mRun, GetScript().GetPathId(), _repeatWaypointPath);
}
else
GetScript().SetPathId(0);
if (mDespawnState == 1)
StartDespawn();
}
public void ResumePath()
{
SetRun(mRun);
GetScript().ProcessEventsFor(SmartEvents.WaypointResumed, null, _currentWaypointNode, GetScript().GetPathId());
if (mLastWP != null)
me.GetMotionMaster().MovePoint(mLastWP.Id, mLastWP.X, mLastWP.Y, mLastWP.Z);
RemoveEscortState(SmartEscortState.Paused);
_waypointPauseForced = false;
_waypointReached = false;
_waypointPauseTimer = 0;
SetRun(mRun);
me.ResumeMovement();
}
void ReturnToLastOOCPos()
@@ -281,8 +268,8 @@ namespace Game.AI
if (!IsAIControlled())
return;
SetRun(mRun);
me.GetMotionMaster().MovePoint(EventId.SmartEscortLastOCCPoint, mLastOOCPos);
me.SetWalk(false);
me.GetMotionMaster().MovePoint(EventId.SmartEscortLastOCCPoint, me.GetHomePosition());
}
void UpdatePath(uint diff)
@@ -290,77 +277,48 @@ namespace Game.AI
if (!HasEscortState(SmartEscortState.Escorting))
return;
if (mEscortInvokerCheckTimer < diff)
if (_escortInvokerCheckTimer < diff)
{
// Escort failed, no players in range
if (!IsEscortInvokerInRange())
{
StopPath(0, mEscortQuestID, 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);
me.DespawnOrUnsummon(1);
me.DespawnOrUnsummon();
return;
}
mEscortInvokerCheckTimer = 1000;
_escortInvokerCheckTimer = 1000;
}
else
mEscortInvokerCheckTimer -= diff;
_escortInvokerCheckTimer -= diff;
// handle pause
if (HasEscortState(SmartEscortState.Paused))
if (HasEscortState(SmartEscortState.Paused) && (_waypointReached || _waypointPauseForced))
{
if (mWPPauseTimer < diff)
if (_waypointPauseTimer < diff)
{
if (!me.IsInCombat() && !HasEscortState(SmartEscortState.Returning) && (mWPReached || mLastWPIDReached == EventId.SmartEscortLastOCCPoint || mForcedPaused))
{
GetScript().ProcessEventsFor(SmartEvents.WaypointResumed, null, mLastWP.Id, GetScript().GetPathId());
RemoveEscortState(SmartEscortState.Paused);
if (mForcedPaused)// if paused between 2 wps resend movement
{
ResumePath();
mWPReached = false;
mForcedPaused = false;
}
if (mLastWPIDReached == EventId.SmartEscortLastOCCPoint)
mWPReached = true;
}
mWPPauseTimer = 0;
if (!me.IsInCombat() && !HasEscortState(SmartEscortState.Returning))
ResumePath();
}
else
mWPPauseTimer -= diff;
_waypointPauseTimer -= diff;
}
else if (_waypointPathEnded) // end path
{
_waypointPathEnded = false;
StopPath();
return;
}
if (HasEscortState(SmartEscortState.Returning))
{
if (mWPReached)//reached OOC WP
if (_OOCReached)//reached OOC WP
{
_OOCReached = false;
RemoveEscortState(SmartEscortState.Returning);
if (!HasEscortState(SmartEscortState.Paused))
ResumePath();
mWPReached = false;
}
}
if ((!me.HasReactState(ReactStates.Passive) && me.IsInCombat()) || HasEscortState(SmartEscortState.Paused | SmartEscortState.Returning))
return;
// handle next wp
if (mWPReached)//reached WP
{
mWPReached = false;
if (mCurrentWPID == GetWPCount())
{
EndPath();
}
else
{
WayPoint wp = GetNextWayPoint();
if (wp != null)
{
SetRun(mRun);
me.GetMotionMaster().MovePoint(wp.Id, wp.X, wp.Y, wp.Z);
}
}
}
}
@@ -372,7 +330,21 @@ namespace Game.AI
UpdatePath(diff);
UpdateDespawn(diff);
UpdateFollow(diff);
if (!mFollowGuid.IsEmpty())
{
if (mFollowArrivedTimer < diff)
{
if (me.FindNearestCreature(mFollowArrivedEntry, SharedConst.InteractionDistance, true))
{
StopFollow(true);
return;
}
mFollowArrivedTimer = 1000;
}
else
mFollowArrivedTimer -= diff;
}
if (!IsAIControlled())
return;
@@ -427,25 +399,44 @@ namespace Game.AI
return true;
}
void MovepointReached(uint id)
public override void WaypointStarted(uint nodeId, uint pathId)
{
if (id != EventId.SmartEscortLastOCCPoint && mLastWPIDReached != id)
GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, id);
mLastWPIDReached = id;
mWPReached = true;
}
public override void MovementInform(MovementGeneratorType MovementType, uint Data)
public override void WaypointReached(uint nodeId, uint pathId)
{
if ((MovementType == MovementGeneratorType.Point && Data == EventId.SmartEscortLastOCCPoint) || MovementType == MovementGeneratorType.Follow)
_currentWaypointNode = nodeId;
GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, _currentWaypointNode, pathId);
if (_waypointPauseTimer != 0 && !_waypointPauseForced)
{
_waypointReached = true;
me.PauseMovement();
me.SetHomePosition(me.GetPosition());
}
else if (HasEscortState(SmartEscortState.Escorting) && me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint)
{
if (_currentWaypointNode == _path.nodes.Count)
_waypointPathEnded = true;
else
SetRun(mRun);
}
}
public override void MovementInform(MovementGeneratorType movementType, uint id)
{
if (movementType == MovementGeneratorType.Point && id == EventId.SmartEscortLastOCCPoint)
me.ClearUnitState(UnitState.Evade);
GetScript().ProcessEventsFor(SmartEvents.Movementinform, null, (uint)MovementType, Data);
if (MovementType != MovementGeneratorType.Point || !HasEscortState(SmartEscortState.Escorting))
GetScript().ProcessEventsFor(SmartEvents.Movementinform, null, (uint)movementType, id);
if (!HasEscortState(SmartEscortState.Escorting))
return;
MovepointReached(Data);
if (movementType != MovementGeneratorType.Point && id == EventId.SmartEscortLastOCCPoint)
_OOCReached = true;
}
public override void EnterEvadeMode(EvadeReason why = EvadeReason.Other)
@@ -470,7 +461,6 @@ namespace Game.AI
GetScript().ProcessEventsFor(SmartEvents.Evade);//must be after aura clear so we can cast spells from db
SetRun(mRun);
if (HasEscortState(SmartEscortState.Escorting))
{
AddEscortState(SmartEscortState.Returning);
@@ -496,8 +486,8 @@ namespace Game.AI
me.GetMotionMaster().MoveTargetedHome();
}
if (!HasEscortState(SmartEscortState.Escorting)) //dont mess up escort movement after combat
SetRun(mRun);
if (!me.HasUnitState(UnitState.Evade))
GetScript().OnReset();
}
public override void MoveInLineOfSight(Unit who)
@@ -510,7 +500,7 @@ namespace Game.AI
if (!IsAIControlled())
return;
if (AssistPlayerInCombatAgainst(who))
if (HasEscortState(SmartEscortState.Escorting) && AssistPlayerInCombatAgainst(who))
return;
base.MoveInLineOfSight(who);
@@ -537,11 +527,24 @@ namespace Game.AI
if (who.GetVictim().GetCharmerOrOwnerPlayerOrPlayerItself() == null)
return false;
//never attack friendly
if (!who.IsInAccessiblePlaceFor(me))
return false;
if (!CanAIAttack(who))
return false;
// we cannot attack in evade mode
if (me.IsInEvadeMode())
return false;
// or if enemy is in evade mode
if (who.IsCreature() && who.ToCreature().IsInEvadeMode())
return false;
if (!me.IsValidAssistTarget(who.GetVictim()))
return false;
//too far away and no free sight?
//too far away and no free sight
if (me.IsWithinDistInMap(who, SMART_MAX_AID_DIST) && me.IsWithinLOSInMap(who))
{
me.EngageWithTarget(who);
@@ -556,7 +559,7 @@ namespace Game.AI
mDespawnTime = 0;
mRespawnTime = 0;
mDespawnState = 0;
mEscortState = SmartEscortState.None;
_escortState = SmartEscortState.None;
me.SetVisible(true);
if (me.GetFaction() != me.GetCreatureTemplate().Faction)
me.RestoreFaction();
@@ -579,9 +582,18 @@ namespace Game.AI
{
GetScript().ProcessEventsFor(SmartEvents.ReachedHome);
if (!UpdateVictim() && me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Idle && me.GetWaypointPath() != 0)
me.GetMotionMaster().MovePath(me.GetWaypointPath(), true);
CreatureGroup formation = me.GetFormation();
if (formation == null || formation.GetLeader() == me || !formation.IsFormed())
{
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Idle && me.GetWaypointPath() != 0)
me.GetMotionMaster().MovePath(me.GetWaypointPath(), true);
else
me.ResumeMovement();
}
else if (formation.IsFormed())
me.GetMotionMaster().MoveIdle(); // wait the order of leader
}
mJustReset = false;
}
@@ -591,25 +603,14 @@ namespace Game.AI
me.InterruptNonMeleeSpells(false); // must be before ProcessEvents
GetScript().ProcessEventsFor(SmartEvents.Aggro, victim);
if (!IsAIControlled())
return;
mLastOOCPos = me.GetPosition();
SetRun(mRun);
if (me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Point)
me.GetMotionMaster().MovementExpired();
}
public override void JustDied(Unit killer)
{
GetScript().ProcessEventsFor(SmartEvents.Death, killer);
if (HasEscortState(SmartEscortState.Escorting))
{
EndPath(true);
me.StopMoving();//force stop
me.GetMotionMaster().MoveIdle();
}
GetScript().ProcessEventsFor(SmartEvents.Death, killer);
}
public override void KilledUnit(Unit victim)
@@ -627,26 +628,32 @@ namespace Game.AI
// dont allow charmed npcs to act on their own
if (!IsAIControlled())
{
if (who != null && mCanAutoAttack)
me.Attack(who, true);
if (who != null)
me.Attack(who, mCanAutoAttack);
return;
}
if (who != null && me.Attack(who, me.IsWithinMeleeRange(who)))
if (who != null && me.Attack(who, mCanAutoAttack))
{
me.GetMotionMaster().Clear(MovementSlot.Active);
me.PauseMovement();
if (mCanCombatMove)
{
SetRun(mRun);
me.GetMotionMaster().MoveChase(who);
}
}
}
public override void SpellHit(Unit caster, SpellInfo spell)
public override void SpellHit(Unit caster, SpellInfo spellInfo)
{
GetScript().ProcessEventsFor(SmartEvents.SpellHit, caster, 0, 0, false, spell);
GetScript().ProcessEventsFor(SmartEvents.SpellHit, caster, 0, 0, false, spellInfo);
}
public override void SpellHitTarget(Unit target, SpellInfo spell)
public override void SpellHitTarget(Unit target, SpellInfo spellInfo)
{
GetScript().ProcessEventsFor(SmartEvents.SpellHitTarget, target, 0, 0, false, spell);
GetScript().ProcessEventsFor(SmartEvents.SpellHitTarget, target, 0, 0, false, spellInfo);
}
public override void DamageTaken(Unit attacker, ref uint damage)
@@ -698,10 +705,10 @@ namespace Game.AI
public override void InitializeAI()
{
mScript.OnInitialize(me);
if (!me.IsDead())
{
mJustReset = true;
JustReachedHome();
GetScript().OnReset();
GetScript().ProcessEventsFor(SmartEvents.Respawn);
}
}
@@ -712,13 +719,13 @@ namespace Game.AI
{
if (HasEscortState(SmartEscortState.Escorting | SmartEscortState.Paused | SmartEscortState.Returning))
EndPath(true);
me.StopMoving();
}
mIsCharmed = apply;
if (!apply && !me.IsInEvadeMode())
{
if (mCanRepeatPath)
if (_repeatWaypointPath)
StartPath(mRun, GetScript().GetPathId(), true);
else
me.SetWalk(!mRun);
@@ -808,41 +815,25 @@ namespace Game.AI
GetScript().ProcessEventsFor(SmartEvents.RewardQuest, player, quest.Id, opt);
}
public override void OnGameEvent(bool start, ushort eventId)
{
GetScript().ProcessEventsFor(start ? SmartEvents.GameEventStart : SmartEvents.GameEventEnd, null, eventId);
}
public void SetCombatMove(bool on)
{
if (mCanCombatMove == on)
return;
mCanCombatMove = on;
if (!IsAIControlled())
return;
if (!HasEscortState(SmartEscortState.Escorting))
if (me.IsEngaged())
{
if (on && me.GetVictim() != null)
if (on && !me.HasReactState(ReactStates.Passive) && me.GetVictim() && me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Max)
{
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Idle)
{
SetRun(mRun);
me.GetMotionMaster().MoveChase(me.GetVictim());
me.CastStop();
}
}
else
{
if (me.HasUnitState(UnitState.ConfusedMove | UnitState.FleeingMove))
return;
me.GetMotionMaster().MovementExpired();
me.GetMotionMaster().Clear(true);
me.StopMoving();
me.GetMotionMaster().MoveIdle();
SetRun(mRun);
me.GetMotionMaster().MoveChase(me.GetVictim());
}
else if (!on && me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Chase)
me.GetMotionMaster().Clear(MovementSlot.Active);
}
}
@@ -901,6 +892,11 @@ namespace Game.AI
GetScript().SetScript9(e, entry);
}
public override void OnGameEvent(bool start, ushort eventId)
{
GetScript().ProcessEventsFor(start ? SmartEvents.GameEventStart : SmartEvents.GameEventEnd, null, eventId);
}
public override void OnSpellClick(Unit clicker, ref bool result)
{
if (!result)
@@ -943,29 +939,18 @@ namespace Game.AI
mConditionsTimer -= diff;
}
public void UpdateFollow(uint diff)
public override void Reset()
{
if (!mFollowGuid.IsEmpty())
{
if (mFollowArrivedTimer < diff)
{
if (me.FindNearestCreature(mFollowArrivedEntry, SharedConst.InteractionDistance, true) != null)
{
StopFollow(true);
return;
}
mFollowArrivedTimer = 1000;
}
else
mFollowArrivedTimer -= diff;
}
if (!HasEscortState(SmartEscortState.Escorting))//dont mess up escort movement after combat
SetRun(mRun);
GetScript().OnReset();
}
bool HasEscortState(SmartEscortState uiEscortState) { return mEscortState.HasAnyFlag(uiEscortState); }
void AddEscortState(SmartEscortState uiEscortState) { mEscortState |= uiEscortState; }
void RemoveEscortState(SmartEscortState uiEscortState) { mEscortState &= ~uiEscortState; }
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 bool CanCombatMove() { return mCanCombatMove; }
public SmartScript GetScript() { return mScript; }
@@ -976,17 +961,19 @@ namespace Game.AI
{
mDespawnTime = t;
mRespawnTime = r;
mDespawnState = (uint)(t != 0 ? 1 : 0);
mDespawnState = t != 0 ? 1 : 0u;
}
public void StartDespawn() { mDespawnState = 2; }
uint GetWPCount() { return (uint)mWayPoints?.Count; }
public void SetWPPauseTimer(uint time) { mWPPauseTimer = time; }
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;
@@ -996,31 +983,27 @@ namespace Game.AI
float mFollowDist;
float mFollowAngle;
SmartScript mScript = new SmartScript();
List<WayPoint> mWayPoints;
SmartEscortState mEscortState;
uint mCurrentWPID;
uint mLastWPIDReached;
bool mWPReached;
uint mWPPauseTimer;
uint mEscortNPCFlags;
WayPoint mLastWP;
Position mLastOOCPos;//set on enter combat
bool mCanRepeatPath;
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;
bool mForcedPaused;
uint mInvincibilityHpLevel;
uint mDespawnTime;
uint mRespawnTime;
uint mDespawnState;
public uint mEscortQuestID;
uint mEscortInvokerCheckTimer;
bool mJustReset;
// Vehicle conditions
+29 -36
View File
@@ -22,6 +22,7 @@ using Game.Entities;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
namespace Game.AI
@@ -296,11 +297,12 @@ namespace Game.AI
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} SmartAI scripts in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadWaypointFromDB()
{
uint oldMSTime = Time.GetMSTime();
waypoint_map.Clear();
_waypointStore.Clear();
PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.SEL_SMARTAI_WP);
SQLResult result = DB.World.Query(stmt);
@@ -314,8 +316,8 @@ namespace Game.AI
uint count = 0;
uint total = 0;
uint last_entry = 0;
uint last_id = 1;
uint lastEntry = 0;
uint lastId = 1;
do
{
@@ -325,25 +327,30 @@ namespace Game.AI
float y = result.Read<float>(3);
float z = result.Read<float>(4);
if (last_entry != entry)
if (lastEntry != entry)
{
last_id = 1;
count++;
lastId = 1;
++count;
}
if (last_id != id)
Log.outError(LogFilter.Sql, "SmartWaypointMgr.LoadFromDB: Path entry {0}, unexpected point id {1}, expected {2}.", entry, id, last_id);
if (lastId != id)
Log.outError(LogFilter.Sql, $"SmartWaypointMgr.LoadFromDB: Path entry {entry}, unexpected point id {id}, expected {lastId}.");
last_id++;
++lastId;
waypoint_map.Add(entry, new WayPoint(id, x, y, z));
if (!_waypointStore.ContainsKey(entry))
_waypointStore[entry] = new WaypointPath();
last_entry = entry;
total++;
WaypointPath path = _waypointStore[entry];
path.id = entry;
path.nodes.Add(new WaypointNode(id, x, y, z));
lastEntry = entry;
++total;
}
while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} SmartAI waypoint paths (total {1} waypoints) in {2} ms", count, total, Time.GetMSTimeDiffToNow(oldMSTime));
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} SmartAI waypoint paths (total {total} waypoints) in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
bool IsTargetValid(SmartScriptHolder e)
@@ -1110,17 +1117,19 @@ namespace Game.AI
break;
case SmartActions.WpStart:
{
List<WayPoint> path = Global.SmartAIMgr.GetPath(e.Action.wpStart.pathID);
if (path.Empty())
WaypointPath path = GetPath(e.Action.wpStart.pathID);
if (path == null || path.nodes.Empty())
{
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses non-existent WaypointPath id {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.wpStart.pathID);
Log.outError(LogFilter.ScriptsAi, $"SmartAIMgr: {e} uses non-existent WaypointPath id {e.Action.wpStart.pathID}, skipped.");
return false;
}
if (e.Action.wpStart.quest != 0 && !IsQuestValid(e, e.Action.wpStart.quest))
return false;
if (e.Action.wpStart.reactState > (uint)ReactStates.Aggressive)
{
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses invalid React State {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.wpStart.reactState);
Log.outError(LogFilter.ScriptsAi, $"SmartAIMgr: {e} uses invalid React State {e.Action.wpStart.reactState}, skipped.");
return false;
}
break;
@@ -1558,9 +1567,9 @@ namespace Game.AI
return temp;
}
public List<WayPoint> GetPath(uint id)
public WaypointPath GetPath(uint id)
{
return waypoint_map.LookupByKey(id);
return _waypointStore.LookupByKey(id);
}
public SmartScriptHolder FindLinkedSourceEvent(List<SmartScriptHolder> list, uint eventId)
@@ -1582,7 +1591,7 @@ namespace Game.AI
}
MultiMap<int, SmartScriptHolder>[] mEventMap = new MultiMap<int, SmartScriptHolder>[(int)SmartScriptType.Max];
MultiMap<uint, WayPoint> waypoint_map = new MultiMap<uint, WayPoint>();
Dictionary<uint, WaypointPath> _waypointStore = new Dictionary<uint, WaypointPath>();
Dictionary<SmartScriptType, uint> SmartAITypeMask = new Dictionary<SmartScriptType, uint>
{
@@ -1688,22 +1697,6 @@ namespace Game.AI
};
}
public class WayPoint
{
public WayPoint(uint id, float x, float y, float z)
{
Id = id;
X = x;
Y = y;
Z = z;
}
public uint Id;
public float X;
public float Y;
public float Z;
}
public class SmartScriptHolder
{
public SmartScriptHolder() { }
+11 -10
View File
@@ -1988,7 +1988,8 @@ namespace Game.AI
waypoints.Add(id);
float distanceToClosest = float.MaxValue;
WayPoint closestWp = null;
uint closestPathId = 0;
uint closestWaypointId = 0;
foreach (var target in targets)
{
@@ -1997,26 +1998,26 @@ namespace Game.AI
{
if (IsSmart(creature))
{
foreach (uint wpId in waypoints)
foreach (uint pathId in waypoints)
{
var path = Global.SmartAIMgr.GetPath(wpId);
if (path == null || path.Empty())
WaypointPath path = Global.SmartAIMgr.GetPath(pathId);
if (path == null || path.nodes.Empty())
continue;
WayPoint wp = path[0];
if (wp != null)
foreach (var waypoint in path.nodes)
{
float distToThisPath = creature.GetDistance(wp.X, wp.Y, wp.Z);
float distToThisPath = creature.GetDistance(waypoint.x, waypoint.y, waypoint.z);
if (distToThisPath < distanceToClosest)
{
distanceToClosest = distToThisPath;
closestWp = wp;
closestPathId = pathId;
closestWaypointId = waypoint.id;
}
}
}
if (closestWp != null)
((SmartAI)creature.GetAI()).StartPath(false, closestWp.Id, true);
if (closestPathId != 0)
((SmartAI)creature.GetAI()).StartPath(false, closestPathId, true, null, closestWaypointId);
}
}
}