aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/AI
diff options
context:
space:
mode:
authorRiztazz <felianther15@gmail.com>2016-11-25 00:31:10 +0100
committerShauren <shauren.trinity@gmail.com>2019-08-17 20:04:14 +0200
commit2caec4f4d20b4c0f91abbcc60b756e00838c7bdd (patch)
tree6dcc7dcdd3ed501a1ca87809fec05f7ccfb2a8bc /src/server/game/AI
parent7d60b9a289ee0942cf57b49626c0f3f9caeed331 (diff)
Revert "[3.3.5][master] Core/Movement: Smooth movement #13467 (#18020)"
This reverts commit 05fb27dae4e8af859e01e5b9e52b082cba217657. (cherrypicked from a3c6880579f3326088ecbe5b8c08c4b75ed91a59)
Diffstat (limited to 'src/server/game/AI')
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp350
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h38
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp298
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h15
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp58
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp35
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h25
7 files changed, 459 insertions, 360 deletions
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index 1dda5017208..42f209baa7d 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -31,8 +31,6 @@ EndScriptData */
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
-#include "WaypointDefines.h"
-#include "WaypointMovementGenerator.h"
enum Points
{
@@ -41,11 +39,10 @@ enum Points
};
npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature),
- m_uiWPWaitTimer(1000),
- m_uiPlayerCheckTimer(0),
+ m_uiWPWaitTimer(2500),
+ m_uiPlayerCheckTimer(1000),
m_uiEscortState(STATE_ESCORT_NONE),
MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
- LastWP(0),
m_pQuestForEscort(NULL),
m_bIsActiveAttacker(true),
m_bIsRunning(false),
@@ -54,11 +51,24 @@ npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature),
DespawnAtEnd(true),
DespawnAtFar(true),
ScriptWP(false),
- HasImmuneToNPCFlags(false),
- m_bStarted(false),
- m_bEnded(false)
+ HasImmuneToNPCFlags(false)
{ }
+void npc_escortAI::AttackStart(Unit* who)
+{
+ if (!who)
+ return;
+
+ if (me->Attack(who, true))
+ {
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
+ me->GetMotionMaster()->MovementExpired();
+
+ if (IsCombatMovementAllowed())
+ me->GetMotionMaster()->MoveChase(who);
+ }
+}
+
Player* npc_escortAI::GetPlayerForEscort()
{
return ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID);
@@ -104,15 +114,38 @@ bool npc_escortAI::AssistPlayerInCombatAgainst(Unit* who)
void npc_escortAI::MoveInLineOfSight(Unit* who)
{
- if (me->GetVictim())
- return;
-
if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me))
+ {
if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who))
return;
- if (me->CanStartAttack(who, false))
- AttackStart(who);
+ if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
+ return;
+
+ if (me->IsHostileTo(who))
+ {
+ float fAttackRadius = me->GetAttackDistance(who);
+ if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
+ {
+ if (!me->GetVictim())
+ {
+ // Clear distracted state on combat
+ if (me->HasUnitState(UNIT_STATE_DISTRACTED))
+ {
+ me->ClearUnitState(UNIT_STATE_DISTRACTED);
+ me->GetMotionMaster()->Clear();
+ }
+
+ AttackStart(who);
+ }
+ else if (me->GetMap()->IsDungeon())
+ {
+ who->SetInCombatWith(me);
+ me->AddThreat(who, 0.0f);
+ }
+ }
+ }
+ }
}
void npc_escortAI::JustDied(Unit* /*killer*/)
@@ -136,13 +169,13 @@ void npc_escortAI::JustDied(Unit* /*killer*/)
void npc_escortAI::JustRespawned()
{
- RemoveEscortState(STATE_ESCORT_ESCORTING | STATE_ESCORT_RETURNING | STATE_ESCORT_PAUSED);
+ m_uiEscortState = STATE_ESCORT_NONE;
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
//add a small delay before going to first waypoint, normal in near all cases
- m_uiWPWaitTimer = 1000;
+ m_uiWPWaitTimer = 2500;
if (me->getFaction() != me->GetCreatureTemplate()->faction)
me->RestoreFaction();
@@ -152,7 +185,6 @@ void npc_escortAI::JustRespawned()
void npc_escortAI::ReturnToLastPoint()
{
- me->SetWalk(false);
float x, y, z, o;
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
@@ -200,57 +232,57 @@ bool npc_escortAI::IsPlayerOrGroupInRange()
void npc_escortAI::UpdateAI(uint32 diff)
{
+ //Waypoint Updating
if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->GetVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING))
{
if (m_uiWPWaitTimer <= diff)
{
- if (!HasEscortState(STATE_ESCORT_PAUSED))
+ //End of the line
+ if (CurrentWP == WaypointList.end())
{
- m_uiWPWaitTimer = 0;
-
- if (m_bEnded)
+ if (DespawnAtEnd)
{
- me->StopMoving();
- me->GetMotionMaster()->Clear(false);
- me->GetMotionMaster()->MoveIdle();
+ TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints");
+
+ if (m_bCanReturnToStart)
+ {
+ float fRetX, fRetY, fRetZ;
+ me->GetRespawnPosition(fRetX, fRetY, fRetZ);
- m_bEnded = false;
+ me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
- if (DespawnAtEnd)
+ m_uiWPWaitTimer = 0;
+
+ TC_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ);
+ return;
+ }
+
+ if (m_bCanInstantRespawn)
{
- TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints");
-
- if (m_bCanReturnToStart)
- {
- float fRetX, fRetY, fRetZ;
- me->GetRespawnPosition(fRetX, fRetY, fRetZ);
-
- me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
-
- TC_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ);
- }
- else if (m_bCanInstantRespawn)
- {
- me->setDeathState(JUST_DIED);
- me->Respawn();
- }
- else
- me->DespawnOrUnsummon();
+ me->setDeathState(JUST_DIED);
+ me->Respawn();
}
else
- TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off");
+ me->DespawnOrUnsummon();
- RemoveEscortState(STATE_ESCORT_ESCORTING);
return;
}
-
- if (!m_bStarted)
+ else
{
- m_bStarted = true;
- me->GetMotionMaster()->MovePath(_path, false);
+ TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off");
+
+ return;
}
- else if (WaypointMovementGenerator<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(me->GetMotionMaster()->top()))
- WaypointStart(move->GetCurrentNode());
+ }
+
+ if (!HasEscortState(STATE_ESCORT_PAUSED))
+ {
+ me->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
+ TC_LOG_DEBUG("scripts", "EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
+
+ WaypointStart(CurrentWP->id);
+
+ m_uiWPWaitTimer = 0;
}
}
else
@@ -260,11 +292,12 @@ void npc_escortAI::UpdateAI(uint32 diff)
//Check if player or any member of his group is within range
if (HasEscortState(STATE_ESCORT_ESCORTING) && !m_uiPlayerGUID.IsEmpty() && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
{
- m_uiPlayerCheckTimer += diff;
- if (m_uiPlayerCheckTimer > 1000)
+ if (m_uiPlayerCheckTimer <= diff)
{
if (DespawnAtFar && !IsPlayerOrGroupInRange())
{
+ TC_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found");
+
if (m_bCanInstantRespawn)
{
me->setDeathState(JUST_DIED);
@@ -276,8 +309,10 @@ void npc_escortAI::UpdateAI(uint32 diff)
return;
}
- m_uiPlayerCheckTimer = 0;
+ m_uiPlayerCheckTimer = 1000;
}
+ else
+ m_uiPlayerCheckTimer -= diff;
}
UpdateEscortAI(diff);
@@ -293,63 +328,44 @@ void npc_escortAI::UpdateEscortAI(uint32 /*diff*/)
void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId)
{
- // no action allowed if there is no escort
- if (!HasEscortState(STATE_ESCORT_ESCORTING))
+ if (moveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING))
return;
- if (moveType == POINT_MOTION_TYPE)
+ //Combat start position reached, continue waypoint movement
+ if (pointId == POINT_LAST_POINT)
{
- if (!m_uiWPWaitTimer)
- m_uiWPWaitTimer = 1;
-
- //Combat start position reached, continue waypoint movement
- if (pointId == POINT_LAST_POINT)
- {
- TC_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat");
+ TC_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat");
- me->SetWalk(!m_bIsRunning);
- RemoveEscortState(STATE_ESCORT_RETURNING);
- }
- else if (pointId == POINT_HOME)
- {
- TC_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list.");
+ me->SetWalk(!m_bIsRunning);
+ RemoveEscortState(STATE_ESCORT_RETURNING);
- m_bStarted = false;
- }
+ if (!m_uiWPWaitTimer)
+ m_uiWPWaitTimer = 1;
}
- else if (moveType == WAYPOINT_MOTION_TYPE)
+ else if (pointId == POINT_HOME)
{
- //Call WP function
- WaypointReached(pointId);
+ TC_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list.");
- //End of the line
- if (LastWP && LastWP == pointId)
+ CurrentWP = WaypointList.begin();
+ m_uiWPWaitTimer = 1;
+ }
+ else
+ {
+ //Make sure that we are still on the right waypoint
+ if (CurrentWP->id != pointId)
{
- LastWP = 0;
-
- m_bStarted = false;
- m_bEnded = true;
-
- m_uiWPWaitTimer = 50;
-
+ TC_LOG_ERROR("misc", "TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", pointId, CurrentWP->id, me->GetEntry());
return;
}
- TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", pointId);
+ TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", CurrentWP->id);
- WaypointMovementGenerator<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(me->GetMotionMaster()->top());
-
- if (move)
- m_uiWPWaitTimer = move->GetTrackerTimer().GetExpiry();
+ //Call WP function
+ WaypointReached(CurrentWP->id);
- //Call WP start function
- if (!m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_PAUSED) && move)
- WaypointStart(move->GetCurrentNode());
+ m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
- if (m_bIsRunning)
- me->SetWalk(false);
- else
- me->SetWalk(true);
+ ++CurrentWP;
}
}
@@ -374,24 +390,9 @@ void npc_escortAI::OnPossess(bool apply)
void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime)
{
- Trinity::NormalizeMapCoord(x);
- Trinity::NormalizeMapCoord(y);
+ Escort_Waypoint t(id, x, y, z, waitTime);
- WaypointNode wp;
-
- wp.id = id;
- wp.x = x;
- wp.y = y;
- wp.z = z;
- wp.orientation = 0.f;
- wp.moveType = m_bIsRunning ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK;
- wp.delay = waitTime;
- wp.eventId = 0;
- wp.eventChance = 100;
-
- _path.nodes.push_back(std::move(wp));
-
- LastWP = id;
+ WaypointList.push_back(t);
// i think SD2 no longer uses this function
ScriptWP = true;
@@ -411,30 +412,10 @@ void npc_escortAI::FillPointMovementListForCreature()
if (!movePoints)
return;
- LastWP = movePoints->back().uiPointId;
-
- for (const ScriptPointMove &point : *movePoints)
+ for (ScriptPointVector::const_iterator itr = movePoints->begin(); itr != movePoints->end(); ++itr)
{
- WaypointNode wp;
-
- float x = point.fX;
- float y = point.fY;
- float z = point.fZ;
-
- Trinity::NormalizeMapCoord(x);
- Trinity::NormalizeMapCoord(y);
-
- wp.id = point.uiPointId;
- wp.x = x;
- wp.y = y;
- wp.z = z;
- wp.orientation = 0.f;
- wp.moveType = m_bIsRunning ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK;
- wp.delay = point.uiWaitTime;
- wp.eventId = 0;
- wp.eventChance = 100;
-
- _path.nodes.push_back(std::move(wp));
+ Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
+ WaypointList.push_back(point);
}
}
@@ -473,6 +454,20 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false
return;
}
+ if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does
+ {
+ if (!WaypointList.empty())
+ WaypointList.clear();
+ FillPointMovementListForCreature();
+ }
+
+ if (WaypointList.empty())
+ {
+ TC_LOG_ERROR("scripts", "EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).",
+ me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0);
+ return;
+ }
+
//set variables
m_bIsActiveAttacker = isActiveAttacker;
m_bIsRunning = run;
@@ -483,16 +478,12 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false
m_bCanInstantRespawn = instantRespawn;
m_bCanReturnToStart = canLoopPath;
- if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does
- FillPointMovementListForCreature();
-
if (m_bCanReturnToStart && m_bCanInstantRespawn)
TC_LOG_DEBUG("scripts", "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
- me->StopMoving();
- me->GetMotionMaster()->Clear(false);
+ me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MoveIdle();
TC_LOG_DEBUG("scripts", "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
}
@@ -506,7 +497,9 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false
me->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
}
- TC_LOG_DEBUG("scripts", "EscortAI started. ActiveAttacker = %d, Run = %d, PlayerGUID = %s", uint32(m_bIsActiveAttacker), uint32(m_bIsRunning), m_uiPlayerGUID.ToString().c_str());
+ TC_LOG_DEBUG("scripts", "EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, %s", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID.ToString().c_str());
+
+ CurrentWP = WaypointList.begin();
//Set initial speed
if (m_bIsRunning)
@@ -514,8 +507,6 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false
else
me->SetWalk(true);
- m_bStarted = false;
-
AddEscortState(STATE_ESCORT_ESCORTING);
}
@@ -525,14 +516,73 @@ void npc_escortAI::SetEscortPaused(bool on)
return;
if (on)
- {
AddEscortState(STATE_ESCORT_PAUSED);
- me->StopMoving();
- }
else
- {
RemoveEscortState(STATE_ESCORT_PAUSED);
- if (WaypointMovementGenerator<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(me->GetMotionMaster()->top()))
- move->GetTrackerTimer().Reset(1);
+}
+
+bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation)
+{
+ me->UpdatePosition(x, y, z, orientation);
+ return SetNextWaypoint(pointId, false, true);
+}
+
+bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition, bool resetWaypointsOnFail)
+{
+ if (!WaypointList.empty())
+ WaypointList.clear();
+
+ FillPointMovementListForCreature();
+
+ if (WaypointList.empty())
+ return false;
+
+ size_t const size = WaypointList.size();
+ Escort_Waypoint waypoint(0, 0, 0, 0, 0);
+ do
+ {
+ waypoint = WaypointList.front();
+ WaypointList.pop_front();
+ if (waypoint.id == pointId)
+ {
+ if (setPosition)
+ me->UpdatePosition(waypoint.x, waypoint.y, waypoint.z, me->GetOrientation());
+
+ CurrentWP = WaypointList.begin();
+ return true;
+ }
}
+ while (!WaypointList.empty());
+
+ // we failed.
+ // we reset the waypoints in the start; if we pulled any, reset it again
+ if (resetWaypointsOnFail && size != WaypointList.size())
+ {
+ if (!WaypointList.empty())
+ WaypointList.clear();
+
+ FillPointMovementListForCreature();
+ }
+
+ return false;
+}
+
+bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z)
+{
+ ScriptPointVector const* waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
+ if (!waypoints)
+ return false;
+
+ for (ScriptPointVector::const_iterator itr = waypoints->begin(); itr != waypoints->end(); ++itr)
+ {
+ if (itr->uiPointId == pointId)
+ {
+ x = itr->fX;
+ y = itr->fY;
+ z = itr->fZ;
+ return true;
+ }
+ }
+
+ return false;
}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index d4837e940f8..2ef401e9fdd 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -21,12 +21,29 @@
#include "ScriptedCreature.h"
#include "ScriptSystem.h"
-#include "WaypointDefines.h"
class Quest;
#define DEFAULT_MAX_PLAYER_DISTANCE 50
+struct Escort_Waypoint
+{
+ Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w)
+ {
+ id = _id;
+ x = _x;
+ y = _y;
+ z = _z;
+ WaitTimeMs = _w;
+ }
+
+ uint32 id;
+ float x;
+ float y;
+ float z;
+ uint32 WaitTimeMs;
+};
+
enum eEscortState
{
STATE_ESCORT_NONE = 0x000, //nothing in progress
@@ -42,6 +59,7 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
~npc_escortAI() { }
// CreatureAI functions
+ void AttackStart(Unit* who) override;
void MoveInLineOfSight(Unit* who) override;
@@ -61,6 +79,15 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
// EscortAI functions
void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime = 0); // waitTime is in ms
+ //this will set the current position to x/y/z/o, and the current WP to pointId.
+ bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation);
+
+ //this will set the current position to WP start position (if setPosition == true),
+ //and the current WP to pointId
+ bool SetNextWaypoint(uint32 pointId, bool setPosition = true, bool resetWaypointsOnFail = true);
+
+ bool GetWaypointPosition(uint32 pointId, float& x, float& y, float& z);
+
virtual void WaypointReached(uint32 pointId) = 0;
virtual void WaypointStart(uint32 /*pointId*/) { }
@@ -80,7 +107,6 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
bool GetAttack() const { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
ObjectGuid GetEventStarterGUID() const { return m_uiPlayerGUID; }
- void SetWaitTimer(uint32 Timer) { m_uiWPWaitTimer = Timer; }
protected:
Player* GetPlayerForEscort();
@@ -98,12 +124,12 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
uint32 m_uiPlayerCheckTimer;
uint32 m_uiEscortState;
float MaxPlayerDistance;
- uint32 LastWP;
-
- WaypointPath _path;
Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script.
+ std::list<Escort_Waypoint> WaypointList;
+ std::list<Escort_Waypoint>::iterator CurrentWP;
+
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)
@@ -112,7 +138,5 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI
bool DespawnAtFar;
bool ScriptWP;
bool HasImmuneToNPCFlags;
- bool m_bStarted;
- bool m_bEnded;
};
#endif
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 96ed78b08de..254f1316204 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -26,31 +26,33 @@
#include "PetDefines.h"
#include "Player.h"
#include "ScriptMgr.h"
-#include "WaypointMovementGenerator.h"
SmartAI::SmartAI(Creature* c) : CreatureAI(c)
{
mIsCharmed = false;
// copy script to local (protection for table reload)
+ mWayPoints = nullptr;
mEscortState = SMART_ESCORT_NONE;
mCurrentWPID = 0;//first wp id is 1 !!
mWPReached = false;
mWPPauseTimer = 0;
- mOOCReached = false;
- mEscortNPCFlags = 0;
+ mLastWP = nullptr;
mCanRepeatPath = false;
- // Spawn in run mode
- mRun = true;
- m_Ended = false;
+ // spawn in run mode
+ me->SetWalk(false);
+ mRun = false;
mEvadeDisabled = false;
+ mLastOOCPos = me->GetPosition();
+
mCanAutoAttack = true;
mCanCombatMove = true;
mForcedPaused = false;
+ mLastWPIDReached = 0;
mEscortQuestID = 0;
@@ -70,10 +72,6 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mJustReset = false;
}
-SmartAI::~SmartAI()
-{
-}
-
bool SmartAI::IsAIControlled() const
{
return !mIsCharmed;
@@ -97,79 +95,63 @@ void SmartAI::UpdateDespawn(const uint32 diff)
} else mDespawnTime -= diff;
}
-void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
+WayPoint* SmartAI::GetNextWayPoint()
+{
+ if (!mWayPoints || mWayPoints->empty())
+ return nullptr;
+
+ mCurrentWPID++;
+ WPPath::const_iterator itr = mWayPoints->find(mCurrentWPID);
+ if (itr != mWayPoints->end())
+ {
+ mLastWP = (*itr).second;
+ if (mLastWP->id != mCurrentWPID)
+ {
+ TC_LOG_ERROR("misc", "SmartAI::GetNextWayPoint: Got not expected waypoint id %u, expected %u", mLastWP->id, mCurrentWPID);
+ }
+ return (*itr).second;
+ }
+ return nullptr;
+}
+
+void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* /*invoker*/)
{
if (me->IsInCombat())// no wp movement in combat
{
TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement while in combat, ignoring.", me->GetEntry());
return;
}
-
if (HasEscortState(SMART_ESCORT_ESCORTING))
StopPath();
-
- SetRun(run);
-
if (path)
if (!LoadPath(path))
return;
-
- if (_path.nodes.empty())
+ if (!mWayPoints || mWayPoints->empty())
return;
- mCurrentWPID = 1;
- m_Ended = false;
-
- // Do not use AddEscortState, removing everything from previous cycle
- mEscortState = SMART_ESCORT_ESCORTING;
+ AddEscortState(SMART_ESCORT_ESCORTING);
mCanRepeatPath = repeat;
- if (invoker && invoker->GetTypeId() == TYPEID_PLAYER)
+ SetRun(run);
+
+ if (WayPoint* wp = GetNextWayPoint())
{
- mEscortNPCFlags = me->m_unitData->NpcFlags[0];
- me->SetNpcFlags(UNIT_NPC_FLAG_NONE);
+ mLastOOCPos = me->GetPosition();
+ me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, wp->id, GetScript()->GetPathId());
}
-
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, mCurrentWPID, GetScript()->GetPathId());
-
- me->GetMotionMaster()->MovePath(_path, mCanRepeatPath);
}
bool SmartAI::LoadPath(uint32 entry)
{
if (HasEscortState(SMART_ESCORT_ESCORTING))
return false;
-
- WPPath const* path = sSmartWaypointMgr->GetPath(entry);
- if (!path || path->empty())
+ mWayPoints = sSmartWaypointMgr->GetPath(entry);
+ if (!mWayPoints)
{
GetScript()->SetPathId(0);
return false;
}
-
- for (WayPoint const &waypoint : *path)
- {
- float x = waypoint.x;
- float y = waypoint.y;
- float z = waypoint.z;
-
- Trinity::NormalizeMapCoord(x);
- Trinity::NormalizeMapCoord(y);
-
- WaypointNode wp;
- wp.id = waypoint.id;
- wp.x = x;
- wp.y = y;
- wp.z = z;
- wp.orientation = 0.f;
- wp.moveType = mRun ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK;
- wp.delay = 0;
- wp.eventId = 0;
- wp.eventChance = 100;
-
- _path.nodes.push_back(std::move(wp));
- }
-
GetScript()->SetPathId(entry);
return true;
}
@@ -178,22 +160,22 @@ void SmartAI::PausePath(uint32 delay, bool forced)
{
if (!HasEscortState(SMART_ESCORT_ESCORTING))
return;
-
if (HasEscortState(SMART_ESCORT_PAUSED))
{
- TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint (current waypoint: %u) movement while already paused, ignoring.", me->GetEntry(), mCurrentWPID);
+ TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint movement while already paused, ignoring.", me->GetEntry());
return;
}
-
+ mForcedPaused = forced;
+ mLastOOCPos = me->GetPosition();
AddEscortState(SMART_ESCORT_PAUSED);
mWPPauseTimer = delay;
- if (forced && !mWPReached)
+ if (forced)
{
- mForcedPaused = forced;
SetRun(mRun);
- me->StopMoving();
+ me->StopMoving();//force stop
+ me->GetMotionMaster()->MoveIdle();//force stop
}
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mCurrentWPID, GetScript()->GetPathId());
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mLastWP->id, GetScript()->GetPathId());
}
void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail)
@@ -203,28 +185,33 @@ void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail)
if (quest)
mEscortQuestID = quest;
+ SetDespawnTime(DespawnTime);
+ //mDespawnTime = DespawnTime;
- if (mDespawnState != 2)
- SetDespawnTime(DespawnTime);
-
- me->StopMoving();
- me->GetMotionMaster()->MovementExpired(false);
+ mLastOOCPos = me->GetPosition();
+ me->StopMoving();//force stop
me->GetMotionMaster()->MoveIdle();
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mCurrentWPID, GetScript()->GetPathId());
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mLastWP->id, GetScript()->GetPathId());
EndPath(fail);
}
void SmartAI::EndPath(bool fail)
{
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, mLastWP->id, GetScript()->GetPathId());
+
RemoveEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING);
- _path.nodes.clear();
+ mWayPoints = nullptr;
+ mCurrentWPID = 0;
mWPPauseTimer = 0;
+ mLastWP = nullptr;
- if (mEscortNPCFlags)
+ if (mCanRepeatPath)
{
- me->SetNpcFlags(NPCFlags(mEscortNPCFlags));
- mEscortNPCFlags = 0;
+ if (IsAIControlled())
+ StartPath(mRun, GetScript()->GetPathId(), true);
}
+ else
+ GetScript()->SetPathId(0);
ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
if (targets && mEscortQuestID)
@@ -268,36 +255,15 @@ void SmartAI::EndPath(bool fail)
}
}
}
-
- // 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(SMART_EVENT_WAYPOINT_ENDED, nullptr, mCurrentWPID, GetScript()->GetPathId());
-
- if (mCanRepeatPath)
- {
- if (IsAIControlled())
- StartPath(mRun, GetScript()->GetPathId(), mCanRepeatPath);
- }
- else
- GetScript()->SetPathId(0);
-
if (mDespawnState == 1)
StartDespawn();
}
void SmartAI::ResumePath()
{
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, NULL, mCurrentWPID, GetScript()->GetPathId());
- RemoveEscortState(SMART_ESCORT_PAUSED);
- mForcedPaused = false;
- mWPReached = false;
- mWPPauseTimer = 0;
SetRun(mRun);
-
- if (WaypointMovementGenerator<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(me->GetMotionMaster()->top()))
- move->GetTrackerTimer().Reset(1);
+ if (mLastWP)
+ me->GetMotionMaster()->MovePoint(mLastWP->id, mLastWP->x, mLastWP->y, mLastWP->z);
}
void SmartAI::ReturnToLastOOCPos()
@@ -305,61 +271,69 @@ void SmartAI::ReturnToLastOOCPos()
if (!IsAIControlled())
return;
- me->SetWalk(false);
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, x, y, z);
+ SetRun(mRun);
+ me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, mLastOOCPos);
}
void SmartAI::UpdatePath(const uint32 diff)
{
if (!HasEscortState(SMART_ESCORT_ESCORTING))
return;
-
if (mEscortInvokerCheckTimer < diff)
{
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(SMART_EVENT_DEATH, me);
- me->DespawnOrUnsummon(1);
- return;
+ StopPath(mDespawnTime, mEscortQuestID, true);
}
mEscortInvokerCheckTimer = 1000;
- }
- else
- mEscortInvokerCheckTimer -= diff;
-
+ } else mEscortInvokerCheckTimer -= diff;
// handle pause
if (HasEscortState(SMART_ESCORT_PAUSED))
{
- if (mWPPauseTimer <= diff)
+ if (mWPPauseTimer < diff)
{
- if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mForcedPaused))
+ if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT || mForcedPaused))
{
- ResumePath();
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, mLastWP->id, GetScript()->GetPathId());
+ RemoveEscortState(SMART_ESCORT_PAUSED);
+ if (mForcedPaused)// if paused between 2 wps resend movement
+ {
+ ResumePath();
+ mWPReached = false;
+ mForcedPaused = false;
+ }
+ if (mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT)
+ mWPReached = true;
}
- }
- else
+ mWPPauseTimer = 0;
+ } else {
mWPPauseTimer -= diff;
+ }
}
- else if (m_Ended) // end path
- {
- m_Ended = false;
- StopPath();
- return;
- }
-
if (HasEscortState(SMART_ESCORT_RETURNING))
{
- if (mOOCReached)//reached OOC WP
+ if (mWPReached)//reached OOC WP
{
- mOOCReached = false;
RemoveEscortState(SMART_ESCORT_RETURNING);
if (!HasEscortState(SMART_ESCORT_PAUSED))
ResumePath();
+ mWPReached = false;
+ }
+ }
+ if ((!me->HasReactState(REACT_PASSIVE) && me->IsInCombat()) || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING))
+ return;
+ // handle next wp
+ if (mWPReached)//reached WP
+ {
+ mWPReached = false;
+ if (mCurrentWPID == GetWPCount())
+ {
+ EndPath();
+ }
+ else if (WayPoint* wp = GetNextWayPoint())
+ {
+ SetRun(mRun);
+ me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
}
}
}
@@ -435,44 +409,22 @@ bool SmartAI::IsEscortInvokerInRange()
void SmartAI::MovepointReached(uint32 id)
{
- // override the id, path can be resumed any time and counter will reset
- // mCurrentWPID holds proper id
-
- // both point movement and escort generator can enter this function
- if (id == SMART_ESCORT_LAST_OOC_POINT)
- {
- mOOCReached = true;
- return;
- }
-
- mCurrentWPID = id + 1; // in SmartAI increase by 1
+ if (id != SMART_ESCORT_LAST_OOC_POINT && mLastWPIDReached != id)
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, id);
+ mLastWPIDReached = id;
mWPReached = true;
- GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, mCurrentWPID, GetScript()->GetPathId());
-
- if (HasEscortState(SMART_ESCORT_PAUSED))
- me->StopMoving();
- else if (HasEscortState(SMART_ESCORT_ESCORTING) && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
- {
- mWPReached = false;
- if (mCurrentWPID == _path.nodes.size())
- m_Ended = true;
- else
- SetRun(mRun);
- }
}
void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
{
- if (MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT)
+ if ((MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT) || MovementType == FOLLOW_MOTION_TYPE)
me->ClearUnitState(UNIT_STATE_EVADE);
GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, MovementType, Data);
- if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ if (MovementType != POINT_MOTION_TYPE || !HasEscortState(SMART_ESCORT_ESCORTING))
return;
-
- if (MovementType == WAYPOINT_MOTION_TYPE || (MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT))
- MovepointReached(Data);
+ MovepointReached(Data);
}
void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
@@ -500,7 +452,6 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
me->GetMotionMaster()->MoveFollow(target, mFollowDist, mFollowAngle);
// evade is not cleared in MoveFollow, so we can't keep it
me->ClearUnitState(UNIT_STATE_EVADE);
- GetScript()->OnReset();
}
else if (Unit* owner = me->GetCharmerOrOwner())
{
@@ -510,8 +461,8 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
else
me->GetMotionMaster()->MoveTargetedHome();
- if (!me->HasUnitState(UNIT_STATE_EVADE))
- GetScript()->OnReset();
+ if (!HasEscortState(SMART_ESCORT_ESCORTING)) //dont mess up escort movement after combat
+ SetRun(mRun);
}
void SmartAI::MoveInLineOfSight(Unit* who)
@@ -624,13 +575,24 @@ void SmartAI::EnterCombat(Unit* enemy)
me->InterruptNonMeleeSpells(false); // must be before ProcessEvents
GetScript()->ProcessEventsFor(SMART_EVENT_AGGRO, enemy);
+
+ if (!IsAIControlled())
+ return;
+ mLastOOCPos = me->GetPosition();
+ SetRun(mRun);
+ if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == POINT_MOTION_TYPE)
+ me->GetMotionMaster()->MovementExpired();
}
void SmartAI::JustDied(Unit* killer)
{
GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer);
if (HasEscortState(SMART_ESCORT_ESCORTING))
+ {
EndPath(true);
+ me->StopMoving();//force stop
+ me->GetMotionMaster()->MoveIdle();
+ }
}
void SmartAI::KilledUnit(Unit* victim)
@@ -645,26 +607,10 @@ void SmartAI::JustSummoned(Creature* creature)
void SmartAI::AttackStart(Unit* who)
{
- // dont allow charmed npcs to act on their own
- if (me->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED))
- {
- if (who && mCanAutoAttack)
- me->Attack(who, true);
- return;
- }
-
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
{
if (mCanCombatMove)
- {
- SetRun(mRun);
-
- MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
- if (type == WAYPOINT_MOTION_TYPE || type == POINT_MOTION_TYPE)
- me->StopMoving();
-
me->GetMotionMaster()->MoveChase(who);
- }
}
}
@@ -832,7 +778,6 @@ void SmartAI::SetCombatMove(bool on)
{
if (mCanCombatMove == on)
return;
-
mCanCombatMove = on;
if (!IsAIControlled())
return;
@@ -849,9 +794,12 @@ void SmartAI::SetCombatMove(bool on)
}
else
{
+ if (me->HasUnitState(UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE))
+ return;
+
+ me->GetMotionMaster()->MovementExpired();
+ me->GetMotionMaster()->Clear(true);
me->StopMoving();
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
- me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
}
}
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index e0843048280..0fd453c787b 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -23,7 +23,8 @@
#include "GameObjectAI.h"
#include "Position.h"
#include "SmartScript.h"
-#include "WaypointDefines.h"
+
+struct WayPoint;
enum SmartEscortState
{
@@ -42,7 +43,7 @@ enum SmartEscortVars
class TC_GAME_API SmartAI : public CreatureAI
{
public:
- ~SmartAI();
+ ~SmartAI(){ }
explicit SmartAI(Creature* c);
// Check whether we are currently permitted to make the creature take action
@@ -55,6 +56,7 @@ class TC_GAME_API SmartAI : public CreatureAI
void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
void EndPath(bool fail = false);
void ResumePath();
+ WayPoint* GetNextWayPoint();
bool HasEscortState(uint32 uiEscortState) const { return (mEscortState & uiEscortState) != 0; }
void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; }
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
@@ -207,13 +209,15 @@ class TC_GAME_API SmartAI : public CreatureAI
void ReturnToLastOOCPos();
void UpdatePath(const uint32 diff);
SmartScript mScript;
+ WPPath* mWayPoints;
uint32 mEscortState;
uint32 mCurrentWPID;
+ uint32 mLastWPIDReached;
bool mWPReached;
- bool mOOCReached;
- bool m_Ended;
uint32 mWPPauseTimer;
- uint32 mEscortNPCFlags;
+ WayPoint* mLastWP;
+ Position mLastOOCPos;//set on enter combat
+ uint32 GetWPCount() const { return mWayPoints ? uint32(mWayPoints->size()) : 0; }
bool mCanRepeatPath;
bool mRun;
bool mEvadeDisabled;
@@ -223,7 +227,6 @@ class TC_GAME_API SmartAI : public CreatureAI
uint32 mInvincibilityHpLevel;
bool AssistPlayerInCombatAgainst(Unit* who);
- WaypointPath _path;
uint32 mDespawnTime;
uint32 mRespawnTime;
uint32 mDespawnState;
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index d22aae3ae0f..62d6ff2f881 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -2533,6 +2533,64 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
sGameEventMgr->StartEvent(eventId, true);
break;
}
+ case SMART_ACTION_START_CLOSEST_WAYPOINT:
+ {
+ uint32 waypoints[SMART_ACTION_PARAM_COUNT];
+ waypoints[0] = e.action.closestWaypointFromList.wp1;
+ waypoints[1] = e.action.closestWaypointFromList.wp2;
+ waypoints[2] = e.action.closestWaypointFromList.wp3;
+ waypoints[3] = e.action.closestWaypointFromList.wp4;
+ waypoints[4] = e.action.closestWaypointFromList.wp5;
+ waypoints[5] = e.action.closestWaypointFromList.wp6;
+ float distanceToClosest = std::numeric_limits<float>::max();
+ WayPoint* closestWp = nullptr;
+
+ ObjectList* targets = GetTargets(e, unit);
+ if (targets)
+ {
+ for (ObjectList::iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ {
+ if (Creature* target = (*itr)->ToCreature())
+ {
+ if (IsSmart(target))
+ {
+ for (uint8 i = 0; i < SMART_ACTION_PARAM_COUNT; i++)
+ {
+ if (!waypoints[i])
+ continue;
+
+ WPPath* path = sSmartWaypointMgr->GetPath(waypoints[i]);
+
+ if (!path || path->empty())
+ continue;
+
+ WPPath::const_iterator itrWp = path->find(0);
+
+ if (itrWp != path->end())
+ {
+ if (WayPoint* wp = itrWp->second)
+ {
+ float distToThisPath = target->GetDistance(wp->x, wp->y, wp->z);
+
+ if (distToThisPath < distanceToClosest)
+ {
+ distanceToClosest = distToThisPath;
+ closestWp = wp;
+ }
+ }
+ }
+ }
+
+ if (closestWp)
+ CAST_AI(SmartAI, target->AI())->StartPath(false, closestWp->id, true);
+ }
+ }
+ }
+
+ delete targets;
+ }
+ break;
+ }
case SMART_ACTION_RANDOM_SOUND:
{
std::vector<uint32> sounds;
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 3be794278bc..39e5759e3a7 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -41,6 +41,14 @@ void SmartWaypointMgr::LoadFromDB()
{
uint32 oldMSTime = getMSTime();
+ for (std::unordered_map<uint32, WPPath*>::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr)
+ {
+ for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr)
+ delete pathItr->second;
+
+ delete itr->second;
+ }
+
waypoint_map.clear();
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP);
@@ -68,10 +76,9 @@ void SmartWaypointMgr::LoadFromDB()
y = fields[3].GetFloat();
z = fields[4].GetFloat();
- WPPath& path = waypoint_map[entry];
-
if (last_entry != entry)
{
+ waypoint_map[entry] = new WPPath();
last_id = 1;
count++;
}
@@ -80,14 +87,7 @@ void SmartWaypointMgr::LoadFromDB()
TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, last_id);
last_id++;
-
- WayPoint point;
- point.id = id;
- point.x = x;
- point.y = y;
- point.z = z;
-
- path.push_back(std::move(point));
+ (*waypoint_map[entry])[id] = new WayPoint(id, x, y, z);
last_entry = entry;
total++;
@@ -97,6 +97,17 @@ void SmartWaypointMgr::LoadFromDB()
TC_LOG_INFO("server.loading", ">> Loaded %u SmartAI waypoint paths (total %u waypoints) in %u ms", count, total, GetMSTimeDiffToNow(oldMSTime));
}
+SmartWaypointMgr::~SmartWaypointMgr()
+{
+ for (std::unordered_map<uint32, WPPath*>::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr)
+ {
+ for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr)
+ delete pathItr->second;
+
+ delete itr->second;
+ }
+}
+
SmartAIMgr* SmartAIMgr::instance()
{
static SmartAIMgr instance;
@@ -1240,8 +1251,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
break;
case SMART_ACTION_WP_START:
{
- WPPath const* path = sSmartWaypointMgr->GetPath(e.action.wpStart.pathID);
- if (!path || path->empty())
+ if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID))
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature " SI64FMTD " Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID);
return false;
@@ -1403,6 +1413,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
break;
}
+ case SMART_ACTION_START_CLOSEST_WAYPOINT:
case SMART_ACTION_FOLLOW:
case SMART_ACTION_SET_ORIENTATION:
case SMART_ACTION_STORE_TARGET_LIST:
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index cd8c6e6392a..8ced59ccc65 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -29,7 +29,13 @@ enum SpellEffIndex : uint8;
struct WayPoint
{
- WayPoint() : id(0), x(0.0f), y(0.0f), z(0.0f) { }
+ WayPoint(uint32 _id, float _x, float _y, float _z)
+ {
+ id = _id;
+ x = _x;
+ y = _y;
+ z = _z;
+ }
uint32 id;
float x;
@@ -558,7 +564,7 @@ enum SMART_ACTION
SMART_ACTION_REMOVE_POWER = 110, // PowerType, newPower
SMART_ACTION_GAME_EVENT_STOP = 111, // GameEventId
SMART_ACTION_GAME_EVENT_START = 112, // GameEventId
- // Not used = 113,
+ SMART_ACTION_START_CLOSEST_WAYPOINT = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7
SMART_ACTION_MOVE_OFFSET = 114,
SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf
SMART_ACTION_SET_CORPSE_DELAY = 116, // timer
@@ -1463,7 +1469,7 @@ struct SmartScriptHolder
operator bool() const { return entryOrGuid != 0; }
};
-typedef std::vector<WayPoint> WPPath;
+typedef std::unordered_map<uint32, WayPoint*> WPPath;
typedef std::list<WorldObject*> ObjectList;
@@ -1495,23 +1501,22 @@ class TC_GAME_API SmartWaypointMgr
{
private:
SmartWaypointMgr() { }
- ~SmartWaypointMgr() { }
+ ~SmartWaypointMgr();
public:
static SmartWaypointMgr* instance();
void LoadFromDB();
- WPPath const* GetPath(uint32 id)
+ WPPath* GetPath(uint32 id)
{
- auto itr = waypoint_map.find(id);
- if (itr != waypoint_map.end())
- return &itr->second;
- return nullptr;
+ if (waypoint_map.find(id) != waypoint_map.end())
+ return waypoint_map[id];
+ else return nullptr;
}
private:
- std::unordered_map<uint32, WPPath> waypoint_map;
+ std::unordered_map<uint32, WPPath*> waypoint_map;
};
// all events for a single entry