From 05fb27dae4e8af859e01e5b9e52b082cba217657 Mon Sep 17 00:00:00 2001 From: Riztazz Date: Fri, 25 Nov 2016 00:31:10 +0100 Subject: [3.3.5][master] Core/Movement: Smooth movement #13467 (#18020) Implement smooth movement for all waypoint pathing and escortai (cherry picked from commit 28050f338dfc66e0c40b6a3915bf96e38e0613e5) --- src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp | 350 +++++++++------------ src/server/game/AI/ScriptedAI/ScriptedEscortAI.h | 38 +-- 2 files changed, 157 insertions(+), 231 deletions(-) (limited to 'src/server/game/AI/ScriptedAI') diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 09545c3b394..7619446bfd0 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -31,6 +31,8 @@ EndScriptData */ #include "MotionMaster.h" #include "ObjectAccessor.h" #include "Player.h" +#include "WaypointDefines.h" +#include "WaypointMovementGenerator.h" enum Points { @@ -39,10 +41,11 @@ enum Points }; npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature), - m_uiWPWaitTimer(2500), - m_uiPlayerCheckTimer(1000), + m_uiWPWaitTimer(1000), + m_uiPlayerCheckTimer(0), m_uiEscortState(STATE_ESCORT_NONE), MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE), + LastWP(0), m_pQuestForEscort(NULL), m_bIsActiveAttacker(true), m_bIsRunning(false), @@ -51,24 +54,11 @@ npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature), DespawnAtEnd(true), DespawnAtFar(true), ScriptWP(false), - HasImmuneToNPCFlags(false) + HasImmuneToNPCFlags(false), + m_bStarted(false), + m_bEnded(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); @@ -114,38 +104,15 @@ 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->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); - } - } - } - } + if (me->CanStartAttack(who, false)) + AttackStart(who); } void npc_escortAI::JustDied(Unit* /*killer*/) @@ -168,13 +135,13 @@ void npc_escortAI::JustDied(Unit* /*killer*/) void npc_escortAI::JustRespawned() { - m_uiEscortState = STATE_ESCORT_NONE; + RemoveEscortState(STATE_ESCORT_ESCORTING | STATE_ESCORT_RETURNING | STATE_ESCORT_PAUSED); if (!IsCombatMovementAllowed()) SetCombatMovement(true); //add a small delay before going to first waypoint, normal in near all cases - m_uiWPWaitTimer = 2500; + m_uiWPWaitTimer = 1000; if (me->getFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); @@ -184,6 +151,7 @@ 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); @@ -231,57 +199,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) { - //End of the line - if (CurrentWP == WaypointList.end()) + if (!HasEscortState(STATE_ESCORT_PAUSED)) { - if (DespawnAtEnd) - { - 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); + m_uiWPWaitTimer = 0; - m_uiWPWaitTimer = 0; + if (m_bEnded) + { + me->StopMoving(); + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveIdle(); - TC_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ); - return; - } + m_bEnded = false; - if (m_bCanInstantRespawn) + if (DespawnAtEnd) { - me->setDeathState(JUST_DIED); - me->Respawn(); + 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(); } else - me->DespawnOrUnsummon(); + TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off"); + RemoveEscortState(STATE_ESCORT_ESCORTING); return; } - else - { - TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off"); - return; + if (!m_bStarted) + { + m_bStarted = true; + me->GetMotionMaster()->MovePath(_path, false); } - } - - 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 if (WaypointMovementGenerator* move = dynamic_cast*>(me->GetMotionMaster()->top())) + WaypointStart(move->GetCurrentNode()); } } else @@ -291,12 +259,11 @@ 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)) { - if (m_uiPlayerCheckTimer <= diff) + m_uiPlayerCheckTimer += diff; + if (m_uiPlayerCheckTimer > 1000) { 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); @@ -308,10 +275,8 @@ void npc_escortAI::UpdateAI(uint32 diff) return; } - m_uiPlayerCheckTimer = 1000; + m_uiPlayerCheckTimer = 0; } - else - m_uiPlayerCheckTimer -= diff; } UpdateEscortAI(diff); @@ -327,44 +292,63 @@ void npc_escortAI::UpdateEscortAI(uint32 /*diff*/) void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId) { - if (moveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING)) + // no action allowed if there is no escort + if (!HasEscortState(STATE_ESCORT_ESCORTING)) return; - //Combat start position reached, continue waypoint movement - if (pointId == POINT_LAST_POINT) + if (moveType == POINT_MOTION_TYPE) { - TC_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat"); - - me->SetWalk(!m_bIsRunning); - RemoveEscortState(STATE_ESCORT_RETURNING); - if (!m_uiWPWaitTimer) m_uiWPWaitTimer = 1; - } - else if (pointId == POINT_HOME) - { - TC_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list."); - CurrentWP = WaypointList.begin(); - 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"); + + 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."); + + m_bStarted = false; + } } - else + else if (moveType == WAYPOINT_MOTION_TYPE) { - //Make sure that we are still on the right waypoint - if (CurrentWP->id != pointId) + //Call WP function + WaypointReached(pointId); + + //End of the line + if (LastWP && LastWP == pointId) { - TC_LOG_ERROR("misc", "TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", pointId, CurrentWP->id, me->GetEntry()); + LastWP = 0; + + m_bStarted = false; + m_bEnded = true; + + m_uiWPWaitTimer = 50; + return; } - TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", CurrentWP->id); + TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", pointId); - //Call WP function - WaypointReached(CurrentWP->id); + WaypointMovementGenerator* move = dynamic_cast*>(me->GetMotionMaster()->top()); + + if (move) + m_uiWPWaitTimer = move->GetTrackerTimer().GetExpiry(); - m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1; + //Call WP start function + if (!m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_PAUSED) && move) + WaypointStart(move->GetCurrentNode()); - ++CurrentWP; + if (m_bIsRunning) + me->SetWalk(false); + else + me->SetWalk(true); } } @@ -389,9 +373,24 @@ void npc_escortAI::OnPossess(bool apply) void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime) { - Escort_Waypoint t(id, x, y, z, waitTime); + Trinity::NormalizeMapCoord(x); + Trinity::NormalizeMapCoord(y); - WaypointList.push_back(t); + 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; // i think SD2 no longer uses this function ScriptWP = true; @@ -411,10 +410,30 @@ void npc_escortAI::FillPointMovementListForCreature() if (!movePoints) return; - for (ScriptPointVector::const_iterator itr = movePoints->begin(); itr != movePoints->end(); ++itr) + LastWP = movePoints->back().uiPointId; + + for (const ScriptPointMove &point : *movePoints) { - Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime); - WaypointList.push_back(point); + 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)); } } @@ -453,20 +472,6 @@ 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; @@ -477,12 +482,16 @@ 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->GetMotionMaster()->MovementExpired(); + me->StopMoving(); + me->GetMotionMaster()->Clear(false); me->GetMotionMaster()->MoveIdle(); TC_LOG_DEBUG("scripts", "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); } @@ -495,9 +504,7 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); } - 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(); + TC_LOG_DEBUG("scripts", "EscortAI started. ActiveAttacker = %d, Run = %d, PlayerGUID = %s", uint32(m_bIsActiveAttacker), uint32(m_bIsRunning), m_uiPlayerGUID.ToString().c_str()); //Set initial speed if (m_bIsRunning) @@ -505,6 +512,8 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false else me->SetWalk(true); + m_bStarted = false; + AddEscortState(STATE_ESCORT_ESCORTING); } @@ -514,73 +523,14 @@ void npc_escortAI::SetEscortPaused(bool on) return; if (on) - AddEscortState(STATE_ESCORT_PAUSED); - else - RemoveEscortState(STATE_ESCORT_PAUSED); -} - -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(); + AddEscortState(STATE_ESCORT_PAUSED); + me->StopMoving(); } - - 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) + else { - if (itr->uiPointId == pointId) - { - x = itr->fX; - y = itr->fY; - z = itr->fZ; - return true; - } + RemoveEscortState(STATE_ESCORT_PAUSED); + if (WaypointMovementGenerator* move = dynamic_cast*>(me->GetMotionMaster()->top())) + move->GetTrackerTimer().Reset(1); } - - return false; } diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index bc3be71af68..91646c53270 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -21,29 +21,12 @@ #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 @@ -59,7 +42,6 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI ~npc_escortAI() { } // CreatureAI functions - void AttackStart(Unit* who) override; void MoveInLineOfSight(Unit* who) override; @@ -79,15 +61,6 @@ 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*/) { } @@ -107,6 +80,7 @@ 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(); @@ -124,11 +98,11 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI uint32 m_uiPlayerCheckTimer; uint32 m_uiEscortState; float MaxPlayerDistance; + uint32 LastWP; - Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script. + WaypointPath _path; - std::list WaypointList; - std::list::iterator CurrentWP; + Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script. bool m_bIsActiveAttacker; //obsolete, determined by faction. bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK) @@ -138,5 +112,7 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI bool DespawnAtFar; bool ScriptWP; bool HasImmuneToNPCFlags; + bool m_bStarted; + bool m_bEnded; }; #endif -- cgit v1.2.3