diff options
author | Riztazz <felianther15@gmail.com> | 2016-11-25 00:31:10 +0100 |
---|---|---|
committer | joschiwald <joschiwald.trinity@gmail.com> | 2018-02-11 15:53:32 +0100 |
commit | 05fb27dae4e8af859e01e5b9e52b082cba217657 (patch) | |
tree | da858a540a26344d3e6af66017d46d7b1df7eb71 | |
parent | 27cdd4b257d01005d9e342b39a4fdf76567d13cf (diff) |
[3.3.5][master] Core/Movement: Smooth movement #13467 (#18020)
Implement smooth movement for all waypoint pathing and escortai
(cherry picked from commit 28050f338dfc66e0c40b6a3915bf96e38e0613e5)
17 files changed, 646 insertions, 619 deletions
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<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(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<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(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<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(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<Escort_Waypoint> WaypointList; - std::list<Escort_Waypoint>::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 diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index e1c3d879265..d0b9a9b23b0 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -26,33 +26,30 @@ #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; - mLastWP = nullptr; + mOOCReached = false; + mEscortNPCFlags = 0; mCanRepeatPath = false; - // spawn in run mode - me->SetWalk(false); - mRun = false; - mEvadeDisabled = false; - - mLastOOCPos = me->GetPosition(); + // Spawn in run mode + mRun = true; + m_Ended = false; mCanAutoAttack = true; mCanCombatMove = true; mForcedPaused = false; - mLastWPIDReached = 0; mEscortQuestID = 0; @@ -72,6 +69,10 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) mJustReset = false; } +SmartAI::~SmartAI() +{ +} + bool SmartAI::IsAIControlled() const { return !mIsCharmed; @@ -95,63 +96,79 @@ void SmartAI::UpdateDespawn(const uint32 diff) } else mDespawnTime -= diff; } -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*/) +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 (!mWayPoints || mWayPoints->empty()) + + if (_path.nodes.empty()) return; - AddEscortState(SMART_ESCORT_ESCORTING); - mCanRepeatPath = repeat; + mCurrentWPID = 1; + m_Ended = false; - SetRun(run); + // Do not use AddEscortState, removing everything from previous cycle + mEscortState = SMART_ESCORT_ESCORTING; + mCanRepeatPath = repeat; - if (WayPoint* wp = GetNextWayPoint()) + if (invoker && invoker->GetTypeId() == TYPEID_PLAYER) { - 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()); + mEscortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS); + me->SetFlag(UNIT_NPC_FLAGS, 0); } + + 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; - mWayPoints = sSmartWaypointMgr->GetPath(entry); - if (!mWayPoints) + + WPPath const* path = sSmartWaypointMgr->GetPath(entry); + if (!path || path->empty()) { 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; } @@ -160,22 +177,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 movement while already paused, ignoring.", me->GetEntry()); + TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint (current waypoint: %u) movement while already paused, ignoring.", me->GetEntry(), mCurrentWPID); return; } - mForcedPaused = forced; - mLastOOCPos = me->GetPosition(); + AddEscortState(SMART_ESCORT_PAUSED); mWPPauseTimer = delay; - if (forced) + if (forced && !mWPReached) { + mForcedPaused = forced; SetRun(mRun); - me->StopMoving();//force stop - me->GetMotionMaster()->MoveIdle();//force stop + me->StopMoving(); } - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mLastWP->id, GetScript()->GetPathId()); + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mCurrentWPID, GetScript()->GetPathId()); } void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail) @@ -185,33 +202,28 @@ void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail) if (quest) mEscortQuestID = quest; - SetDespawnTime(DespawnTime); - //mDespawnTime = DespawnTime; - mLastOOCPos = me->GetPosition(); - me->StopMoving();//force stop + if (mDespawnState != 2) + SetDespawnTime(DespawnTime); + + me->StopMoving(); + me->GetMotionMaster()->MovementExpired(false); me->GetMotionMaster()->MoveIdle(); - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mLastWP->id, GetScript()->GetPathId()); + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mCurrentWPID, 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); - mWayPoints = nullptr; - mCurrentWPID = 0; + _path.nodes.clear(); mWPPauseTimer = 0; - mLastWP = nullptr; - if (mCanRepeatPath) + if (mEscortNPCFlags) { - if (IsAIControlled()) - StartPath(mRun, GetScript()->GetPathId(), true); + me->SetFlag(UNIT_NPC_FLAGS, mEscortNPCFlags); + mEscortNPCFlags = 0; } - else - GetScript()->SetPathId(0); ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS); if (targets && mEscortQuestID) @@ -253,15 +265,36 @@ 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 (mLastWP) - me->GetMotionMaster()->MovePoint(mLastWP->id, mLastWP->x, mLastWP->y, mLastWP->z); + + if (WaypointMovementGenerator<Creature>* move = dynamic_cast<WaypointMovementGenerator<Creature>*>(me->GetMotionMaster()->top())) + move->GetTrackerTimer().Reset(1); } void SmartAI::ReturnToLastOOCPos() @@ -269,69 +302,61 @@ void SmartAI::ReturnToLastOOCPos() if (!IsAIControlled()) return; - SetRun(mRun); - me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, mLastOOCPos); + 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); } void SmartAI::UpdatePath(const uint32 diff) { if (!HasEscortState(SMART_ESCORT_ESCORTING)) return; + if (mEscortInvokerCheckTimer < diff) { if (!IsEscortInvokerInRange()) { - StopPath(mDespawnTime, mEscortQuestID, true); + 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; } 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 || mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT || mForcedPaused)) + if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mForcedPaused)) { - 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; + ResumePath(); } - mWPPauseTimer = 0; - } else { - mWPPauseTimer -= diff; } + else + mWPPauseTimer -= diff; } + else if (m_Ended) // end path + { + m_Ended = false; + StopPath(); + return; + } + if (HasEscortState(SMART_ESCORT_RETURNING)) { - if (mWPReached)//reached OOC WP + if (mOOCReached)//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); } } } @@ -408,22 +433,44 @@ bool SmartAI::IsEscortInvokerInRange() void SmartAI::MovepointReached(uint32 id) { - if (id != SMART_ESCORT_LAST_OOC_POINT && mLastWPIDReached != id) - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, 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 - 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) || MovementType == FOLLOW_MOTION_TYPE) + if (MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT) me->ClearUnitState(UNIT_STATE_EVADE); GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, MovementType, Data); - if (MovementType != POINT_MOTION_TYPE || !HasEscortState(SMART_ESCORT_ESCORTING)) + if (!HasEscortState(SMART_ESCORT_ESCORTING)) return; - MovepointReached(Data); + + if (MovementType == WAYPOINT_MOTION_TYPE || (MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT)) + MovepointReached(Data); } void SmartAI::EnterEvadeMode(EvadeReason /*why*/) @@ -451,6 +498,7 @@ 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()) { @@ -460,8 +508,8 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) else me->GetMotionMaster()->MoveTargetedHome(); - if (!HasEscortState(SMART_ESCORT_ESCORTING)) //dont mess up escort movement after combat - SetRun(mRun); + if (!me->HasUnitState(UNIT_STATE_EVADE)) + GetScript()->OnReset(); } void SmartAI::MoveInLineOfSight(Unit* who) @@ -574,24 +622,13 @@ 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) @@ -606,10 +643,26 @@ void SmartAI::JustSummoned(Creature* creature) void SmartAI::AttackStart(Unit* who) { + // dont allow charmed npcs to act on their own + if (me->HasFlag(UNIT_FIELD_FLAGS, 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); + } } } @@ -777,6 +830,7 @@ void SmartAI::SetCombatMove(bool on) { if (mCanCombatMove == on) return; + mCanCombatMove = on; if (!IsAIControlled()) return; @@ -793,12 +847,9 @@ 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 fbfa2783db8..4e46339bcaa 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -23,8 +23,7 @@ #include "GameObjectAI.h" #include "Position.h" #include "SmartScript.h" - -struct WayPoint; +#include "WaypointDefines.h" enum SmartEscortState { @@ -43,7 +42,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 @@ -56,7 +55,6 @@ 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; } @@ -209,15 +207,13 @@ 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; - WayPoint* mLastWP; - Position mLastOOCPos;//set on enter combat - uint32 GetWPCount() const { return mWayPoints ? uint32(mWayPoints->size()) : 0; } + uint32 mEscortNPCFlags; bool mCanRepeatPath; bool mRun; bool mEvadeDisabled; @@ -227,6 +223,7 @@ 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 b20329cd319..f5ccf93006b 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2461,64 +2461,6 @@ 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 30433bc328b..60b5e63936b 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -41,14 +41,6 @@ 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(); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); @@ -76,9 +68,10 @@ 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++; } @@ -87,7 +80,14 @@ 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_map[entry])[id] = new WayPoint(id, x, y, z); + + WayPoint point; + point.id = id; + point.x = x; + point.y = y; + point.z = z; + + path.push_back(std::move(point)); last_entry = entry; total++; @@ -97,17 +97,6 @@ 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; @@ -1249,7 +1238,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) break; case SMART_ACTION_WP_START: { - if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID)) + WPPath const* path = sSmartWaypointMgr->GetPath(e.action.wpStart.pathID); + if (!path || path->empty()) { 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; @@ -1411,7 +1401,6 @@ 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 2c2768830fd..87d142aa0ab 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -29,13 +29,7 @@ enum SpellEffIndex : uint8; struct WayPoint { - WayPoint(uint32 _id, float _x, float _y, float _z) - { - id = _id; - x = _x; - y = _y; - z = _z; - } + WayPoint() : id(0), x(0.0f), y(0.0f), z(0.0f) { } uint32 id; float x; @@ -563,7 +557,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 - SMART_ACTION_START_CLOSEST_WAYPOINT = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7 + // Not used = 113, SMART_ACTION_MOVE_OFFSET = 114, SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf SMART_ACTION_SET_CORPSE_DELAY = 116, // timer @@ -1460,7 +1454,7 @@ struct SmartScriptHolder operator bool() const { return entryOrGuid != 0; } }; -typedef std::unordered_map<uint32, WayPoint*> WPPath; +typedef std::vector<WayPoint> WPPath; typedef std::list<WorldObject*> ObjectList; @@ -1492,22 +1486,23 @@ class TC_GAME_API SmartWaypointMgr { private: SmartWaypointMgr() { } - ~SmartWaypointMgr(); + ~SmartWaypointMgr() { } public: static SmartWaypointMgr* instance(); void LoadFromDB(); - WPPath* GetPath(uint32 id) + WPPath const* GetPath(uint32 id) { - if (waypoint_map.find(id) != waypoint_map.end()) - return waypoint_map[id]; - else return nullptr; + auto itr = waypoint_map.find(id); + if (itr != waypoint_map.end()) + return &itr->second; + return nullptr; } private: - std::unordered_map<uint32, WPPath*> waypoint_map; + std::unordered_map<uint32, WPPath> waypoint_map; }; // all events for a single entry diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 463ba3a5da1..f8836d01e52 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -701,6 +701,14 @@ void MotionMaster::MovePath(uint32 path_id, bool repeatable) _owner->GetGUID().ToString().c_str(), path_id, repeatable ? "YES" : "NO"); } +void MotionMaster::MovePath(WaypointPath& path, bool repeatable) +{ + Mutate(new WaypointMovementGenerator<Creature>(path, repeatable), MOTION_SLOT_IDLE); + + TC_LOG_DEBUG("misc", "%s starts moving over path (repeatable: %s).", + _owner->GetGUID().ToString().c_str(), repeatable ? "YES" : "NO"); +} + void MotionMaster::MoveRotate(uint32 time, RotateDirection direction) { if (!time) diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index fcf20e2aa7e..57dbbf29634 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -32,6 +32,7 @@ class PathGenerator; struct Position; struct SplineChainLink; struct SplineChainResumeInfo; +struct WaypointPath; namespace G3D { @@ -180,6 +181,7 @@ class TC_GAME_API MotionMaster void MoveTaxiFlight(uint32 path, uint32 pathnode); void MoveDistract(uint32 time); void MovePath(uint32 path_id, bool repeatable); + void MovePath(WaypointPath& path, bool repeatable); void MoveRotate(uint32 time, RotateDirection direction); private: diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 05116f42e97..f766909a796 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -25,14 +25,18 @@ #include "MoveSplineInit.h" #include "ObjectMgr.h" #include "Transport.h" +#include "WaypointManager.h" #include "World.h" void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature) { - if (!path_id) - path_id = creature->GetWaypointPath(); + if (LoadedFromDB) + { + if (!path_id) + path_id = creature->GetWaypointPath(); - i_path = sWaypointMgr->GetPath(path_id); + i_path = sWaypointMgr->GetPath(path_id); + } if (!i_path) { @@ -41,71 +45,92 @@ void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature) return; } - StartMoveNow(creature); + if (!Stopped()) + StartMoveNow(creature); } void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature) { LoadPath(creature); - creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); } void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature) { - creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); + creature->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); creature->SetWalk(false); } void WaypointMovementGenerator<Creature>::DoReset(Creature* creature) { - creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); - StartMoveNow(creature); + if (!Stopped()) + StartMoveNow(creature); } void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature) { - if (!i_path || i_path->empty()) - return; - if (m_isArrivalDone) + if (!i_path || i_path->nodes.empty()) return; - m_isArrivalDone = true; + WaypointNode const &waypoint = i_path->nodes.at(i_currentNode); + if (waypoint.delay) + { + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + Stop(waypoint.delay); + } - if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance) + if (waypoint.eventId && urand(0, 99) < waypoint.eventChance) { - TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID().ToString().c_str()); + TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", waypoint.eventId, i_currentNode, creature->GetGUID().ToString().c_str()); creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL); + creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.eventId, creature, nullptr); } // Inform script MovementInform(creature); creature->UpdateWaypointID(i_currentNode); - if (i_path->at(i_currentNode)->delay) + creature->SetWalk(waypoint.moveType != WAYPOINT_MOVE_TYPE_RUN); +} + +void WaypointMovementGenerator<Creature>::FormationMove(Creature* creature) +{ + bool transportPath = creature->GetTransport() != nullptr; + + WaypointNode const &waypoint = i_path->nodes.at(i_currentNode); + + Movement::Location formationDest(waypoint.x, waypoint.y, waypoint.z, 0.0f); + + //! If creature is on transport, we assume waypoints set in DB are already transport offsets + if (transportPath) { - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - Stop(i_path->at(i_currentNode)->delay); + if (TransportBase* trans = creature->GetDirectTransport()) + trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); } + + // Call for creature group update + if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) + creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z); } bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { - if (!i_path || i_path->empty()) + if (!creature || !creature->IsAlive()) return false; - if (Stopped()) - return true; + if (!i_path || i_path->nodes.empty()) + return false; - bool transportPath = creature->GetTransport() != NULL; + bool transportPath = creature->GetTransport() != nullptr; - if (m_isArrivalDone) + if (IsArrivalDone) { - if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint + if ((i_currentNode == i_path->nodes.size() - 1) && !repeating) // If that's our last waypoint { - float x = i_path->at(i_currentNode)->x; - float y = i_path->at(i_currentNode)->y; - float z = i_path->at(i_currentNode)->z; + WaypointNode const &waypoint = i_path->nodes.at(i_currentNode); + + float x = waypoint.x; + float y = waypoint.y; + float z = waypoint.z; float o = creature->GetOrientation(); if (!transportPath) @@ -123,23 +148,45 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) transportPath = false; // else if (vehicle) - this should never happen, vehicle offsets are const } - - creature->GetMotionMaster()->Initialize(); return false; } - i_currentNode = (i_currentNode+1) % i_path->size(); + i_currentNode = (i_currentNode + 1) % i_path->nodes.size(); } - WaypointData const* node = i_path->at(i_currentNode); + float finalOrient = 0.0f; + uint8 finalMove = WAYPOINT_MOVE_TYPE_WALK; + + Movement::PointsArray pathing; + pathing.reserve((i_path->nodes.size() - i_currentNode) + 1); + + pathing.push_back(G3D::Vector3(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ())); + for (uint32 i = i_currentNode; i < i_path->nodes.size(); ++i) + { + WaypointNode const &waypoint = i_path->nodes.at(i); - m_isArrivalDone = false; + pathing.push_back(G3D::Vector3(waypoint.x, waypoint.y, waypoint.z)); + + finalOrient = waypoint.orientation; + finalMove = waypoint.moveType; + + if (waypoint.delay) + break; + } + + // if we have only 1 point, only current position, we shall return + if (pathing.size() < 2) + return false; + + IsArrivalDone = false; + i_recalculateSpeed = false; creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); - Movement::Location formationDest(node->x, node->y, node->z, 0.0f); Movement::MoveSplineInit init(creature); + Movement::Location formationDest(i_path->nodes.at(i_currentNode).x, i_path->nodes.at(i_currentNode).y, i_path->nodes.at(i_currentNode).z, 0.0f); + //! If creature is on transport, we assume waypoints set in DB are already transport offsets if (transportPath) { @@ -148,15 +195,9 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); } - //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call - //! but formationDest contains global coordinates - init.MoveTo(node->x, node->y, node->z); - - //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table - if (node->orientation && node->delay) - init.SetFacing(node->orientation); + init.MovebyPath(pathing, i_currentNode); - switch (node->move_type) + switch (finalMove) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(Movement::ToGround); @@ -172,20 +213,23 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) break; } + if (finalOrient != 0.0f) + init.SetFacing(finalOrient); + init.Launch(); //Call for creature group update if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - { - creature->SetWalk(node->move_type != WAYPOINT_MOVE_TYPE_RUN); creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z); - } return true; } bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff) { + if (!creature || !creature->IsAlive()) + return false; + // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if (creature->HasUnitState(UNIT_STATE_NOT_MOVE)) @@ -193,14 +237,15 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); return true; } + // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) + if (!i_path || i_path->nodes.empty()) return false; if (Stopped()) { if (CanMove(diff)) - return StartMove(creature); + return StartMoveNow(creature); } else { @@ -209,15 +254,45 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di creature->SetHomePosition(creature->GetPosition()); if (creature->IsStopped()) - Stop(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); + Stop(LoadedFromDB ? sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER) : 2 * HOUR * IN_MILLISECONDS); else if (creature->movespline->Finalized()) { OnArrived(creature); - return StartMove(creature); + + IsArrivalDone = true; + + if (!Stopped()) + { + if (creature->IsStopped()) + Stop(LoadedFromDB ? sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER) : 2 * HOUR * IN_MILLISECONDS); + else + return StartMove(creature); + } + } + else + { + // speed changed during path execution, calculate remaining path and launch it once more + if (i_recalculateSpeed) + { + i_recalculateSpeed = false; + + if (!Stopped()) + return StartMove(creature); + } + else + { + uint32 pointId = uint32(creature->movespline->currentPathIdx()); + if (pointId > i_currentNode) + { + OnArrived(creature); + i_currentNode = pointId; + FormationMove(creature); + } + } } } - return true; - } + return true; +} void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature) { @@ -228,11 +303,14 @@ void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature) bool WaypointMovementGenerator<Creature>::GetResetPos(Creature*, float& x, float& y, float& z) { // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) + if (!i_path || i_path->nodes.empty()) return false; - const WaypointData* node = i_path->at(i_currentNode); - x = node->x; y = node->y; z = node->z; + WaypointNode const &waypoint = i_path->nodes.at(i_currentNode); + + x = waypoint.x; + y = waypoint.y; + z = waypoint.z; return true; } @@ -338,12 +416,12 @@ void FlightPathMovementGenerator::DoReset(Player* player) Movement::MoveSplineInit init(player); uint32 end = GetPathAtMapEnd(); - for (uint32 i = GetCurrentNode(); i != end; ++i) + for (uint32 i = i_currentNode; i != end; ++i) { G3D::Vector3 vertice(i_path[i]->Loc.X, i_path[i]->Loc.Y, i_path[i]->Loc.Z); init.Path().push_back(vertice); } - init.SetFirstPointId(GetCurrentNode()); + init.SetFirstPointId(i_currentNode); init.SetFly(); init.SetSmooth(); init.SetUncompressed(); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index b63f9eb91c8..03c1585090a 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -30,7 +30,6 @@ #include "DB2Stores.h" #include "Player.h" #include "Timer.h" -#include "WaypointManager.h" #define FLIGHT_TRAVEL_UPDATE 100 #define TIMEDIFF_NEXT_WP 250 @@ -58,8 +57,16 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea { public: WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true) - : i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) { } - ~WaypointMovementGenerator() { i_path = NULL; } + : i_nextMoveTime(0), IsArrivalDone(false), path_id(_path_id), repeating(_repeating), LoadedFromDB(true) { } + + WaypointMovementGenerator(WaypointPath& path, bool _repeating = true) + : i_nextMoveTime(0), IsArrivalDone(false), path_id(0), repeating(_repeating), LoadedFromDB(false) + { + i_path = &path; + } + + ~WaypointMovementGenerator() { i_path = nullptr; } + void DoInitialize(Creature*); void DoFinalize(Creature*); void DoReset(Creature*); @@ -74,6 +81,10 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea bool GetResetPos(Creature*, float& x, float& y, float& z); + TimeTrackerSmall & GetTrackerTimer() { return i_nextMoveTime; } + + void UnitSpeedChanged() { i_recalculateSpeed = true; } + private: void Stop(int32 time) { i_nextMoveTime.Reset(time);} @@ -88,17 +99,21 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea void OnArrived(Creature*); bool StartMove(Creature*); + void FormationMove(Creature*); - void StartMoveNow(Creature* creature) + bool StartMoveNow(Creature* creature) { i_nextMoveTime.Reset(0); - StartMove(creature); + return StartMove(creature); } TimeTrackerSmall i_nextMoveTime; - bool m_isArrivalDone; + bool i_recalculateSpeed; + + bool IsArrivalDone; uint32 path_id; bool repeating; + bool LoadedFromDB; }; /** FlightPathMovementGenerator generates movement of the player for the paths diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h new file mode 100644 index 00000000000..063f7ffdd2b --- /dev/null +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_WAYPOINTDEFINES_H +#define TRINITY_WAYPOINTDEFINES_H + +#include "Define.h" +#include <vector> + +enum WaypointMoveType +{ + WAYPOINT_MOVE_TYPE_WALK, + WAYPOINT_MOVE_TYPE_RUN, + WAYPOINT_MOVE_TYPE_LAND, + WAYPOINT_MOVE_TYPE_TAKEOFF, + + WAYPOINT_MOVE_TYPE_MAX +}; + +struct WaypointNode +{ + WaypointNode() : id(0), x(0.f), y(0.f), z(0.f), orientation(0.f), delay(0), eventId(0), moveType(WAYPOINT_MOVE_TYPE_RUN), eventChance(0) { } + WaypointNode(uint32 _id, float _x, float _y, float _z, float _orientation = 0.f, uint32 _delay = 0) + { + id = _id; + x = _x; + y = _y; + z = _z; + orientation = _orientation; + delay = _delay; + eventId = 0; + moveType = WAYPOINT_MOVE_TYPE_WALK; + eventChance = 100; + } + + uint32 id; + float x, y, z, orientation; + uint32 delay; + uint32 eventId; + uint32 moveType; + uint8 eventChance; +}; + +struct WaypointPath +{ + WaypointPath() : id(0) { } + WaypointPath(uint32 _id, std::vector<WaypointNode>&& _nodes) + { + id = _id; + nodes = _nodes; + } + + std::vector<WaypointNode> nodes; + uint32 id; +}; + +#endif diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index c83684763f5..404ad2f9be6 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -24,19 +24,6 @@ WaypointMgr::WaypointMgr() { } -WaypointMgr::~WaypointMgr() -{ - for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr) - { - for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it) - delete *it; - - itr->second.clear(); - } - - _waypointStore.clear(); -} - void WaypointMgr::Load() { uint32 oldMSTime = getMSTime(); @@ -55,7 +42,6 @@ void WaypointMgr::Load() do { Field* fields = result->Fetch(); - WaypointData* wp = new WaypointData(); uint32 pathId = fields[0].GetUInt32(); WaypointPath& path = _waypointStore[pathId]; @@ -68,25 +54,25 @@ void WaypointMgr::Load() Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - wp->id = fields[1].GetUInt32(); - wp->x = x; - wp->y = y; - wp->z = z; - wp->orientation = o; - wp->move_type = fields[6].GetUInt32(); + WaypointNode wp; + wp.id = fields[1].GetUInt32(); + wp.x = x; + wp.y = y; + wp.z = z; + wp.orientation = o; + wp.moveType = fields[6].GetUInt32(); - if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (wp.moveType >= WAYPOINT_MOVE_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id); - delete wp; + TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp.id); continue; } - wp->delay = fields[7].GetUInt32(); - wp->event_id = fields[8].GetUInt32(); - wp->event_chance = fields[9].GetInt16(); + wp.delay = fields[7].GetUInt32(); + wp.eventId = fields[8].GetUInt32(); + wp.eventChance = fields[9].GetInt16(); - path.push_back(wp); + path.nodes.push_back(std::move(wp)); ++count; } while (result->NextRow()); @@ -105,9 +91,6 @@ void WaypointMgr::ReloadPath(uint32 id) WaypointPathContainer::iterator itr = _waypointStore.find(id); if (itr != _waypointStore.end()) { - for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it) - delete *it; - _waypointStore.erase(itr); } @@ -125,7 +108,6 @@ void WaypointMgr::ReloadPath(uint32 id) do { Field* fields = result->Fetch(); - WaypointData* wp = new WaypointData(); float x = fields[1].GetFloat(); float y = fields[2].GetFloat(); @@ -135,25 +117,25 @@ void WaypointMgr::ReloadPath(uint32 id) Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - wp->id = fields[0].GetUInt32(); - wp->x = x; - wp->y = y; - wp->z = z; - wp->orientation = o; - wp->move_type = fields[5].GetUInt32(); + WaypointNode wp; + wp.id = fields[0].GetUInt32(); + wp.x = x; + wp.y = y; + wp.z = z; + wp.orientation = o; + wp.moveType = fields[5].GetUInt32(); - if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (wp.moveType >= WAYPOINT_MOVE_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id); - delete wp; + TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp.id); continue; } - wp->delay = fields[6].GetUInt32(); - wp->event_id = fields[7].GetUInt32(); - wp->event_chance = fields[8].GetUInt8(); + wp.delay = fields[6].GetUInt32(); + wp.eventId = fields[7].GetUInt32(); + wp.eventChance = fields[8].GetUInt8(); - path.push_back(wp); + path.nodes.push_back(std::move(wp)); } while (result->NextRow()); diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h index d7b9ff2016a..76fe4d67384 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.h +++ b/src/server/game/Movement/Waypoints/WaypointManager.h @@ -20,30 +20,9 @@ #define TRINITY_WAYPOINTMANAGER_H #include "Define.h" -#include <vector> +#include "WaypointDefines.h" #include <unordered_map> -enum WaypointMoveType -{ - WAYPOINT_MOVE_TYPE_WALK, - WAYPOINT_MOVE_TYPE_RUN, - WAYPOINT_MOVE_TYPE_LAND, - WAYPOINT_MOVE_TYPE_TAKEOFF, - - WAYPOINT_MOVE_TYPE_MAX -}; - -struct WaypointData -{ - uint32 id; - float x, y, z, orientation; - uint32 delay; - uint32 event_id; - uint32 move_type; - uint8 event_chance; -}; - -typedef std::vector<WaypointData*> WaypointPath; typedef std::unordered_map<uint32, WaypointPath> WaypointPathContainer; class TC_GAME_API WaypointMgr @@ -64,14 +43,14 @@ class TC_GAME_API WaypointMgr if (itr != _waypointStore.end()) return &itr->second; - return NULL; + return nullptr; } private: WaypointMgr(); - ~WaypointMgr(); + ~WaypointMgr() { } - WaypointPathContainer _waypointStore; + std::unordered_map<uint32, WaypointPath> _waypointStore; }; #define sWaypointMgr WaypointMgr::instance() diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5d714db551f..90b6da26a3b 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -86,6 +86,7 @@ #include "VMapFactory.h" #include "VMapManager2.h" #include "WardenCheckMgr.h" +#include "WaypointManager.h" #include "WaypointMovementGenerator.h" #include "WeatherMgr.h" #include "WorldSession.h" diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp index 09f7f8e7c07..9dc5d5bed5b 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp @@ -15,6 +15,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "AreaBoundary.h" #include "ScriptMgr.h" #include "Creature.h" #include "forge_of_souls.h" diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index a0a390f9aa5..6aeaafbd4ec 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -27,6 +27,7 @@ #include "PassiveAI.h" #include "Player.h" #include "ScriptedCreature.h" +#include "ScriptedEscortAI.h" #include "Spell.h" #include "SpellInfo.h" #include "SpellScript.h" @@ -876,11 +877,15 @@ class npc_brann_bronzebeard_algalon : public CreatureScript public: npc_brann_bronzebeard_algalon() : CreatureScript("npc_brann_bronzebeard_algalon") { } - struct npc_brann_bronzebeard_algalonAI : public CreatureAI + struct npc_brann_bronzebeard_algalonAI : public npc_escortAI { - npc_brann_bronzebeard_algalonAI(Creature* creature) : CreatureAI(creature) + npc_brann_bronzebeard_algalonAI(Creature* creature) : npc_escortAI(creature) { - _currentPoint = 0; + SetDespawnAtEnd(false); + SetDespawnAtFar(false); + + for (uint8 i = 0; i < MAX_BRANN_WAYPOINTS_INTRO; ++i) + AddWaypoint(i, BrannIntroWaypoint[i].GetPositionX(), BrannIntroWaypoint[i].GetPositionY(), BrannIntroWaypoint[i].GetPositionZ()); } void DoAction(int32 action) override @@ -888,14 +893,12 @@ class npc_brann_bronzebeard_algalon : public CreatureScript switch (action) { case ACTION_START_INTRO: - _currentPoint = 0; _events.Reset(); - me->SetWalk(false); - _events.ScheduleEvent(EVENT_BRANN_MOVE_INTRO, 1); + Start(false, true); break; case ACTION_FINISH_INTRO: Talk(SAY_BRANN_ALGALON_INTRO_2); - _events.ScheduleEvent(EVENT_BRANN_MOVE_INTRO, 1); + SetEscortPaused(false); break; case ACTION_OUTRO: me->GetMotionMaster()->MovePoint(POINT_BRANN_OUTRO, BrannOutroPos[1]); @@ -905,38 +908,27 @@ class npc_brann_bronzebeard_algalon : public CreatureScript } } - void MovementInform(uint32 movementType, uint32 pointId) override + void WaypointReached(uint32 pointId) override { - if (movementType != POINT_MOTION_TYPE) - return; - - uint32 delay = 1; - _currentPoint = pointId + 1; switch (pointId) { case 2: - delay = 8000; - me->SetWalk(true); + SetEscortPaused(true); + _events.ScheduleEvent(EVENT_BRANN_MOVE_INTRO, 8000); + SetRun(false); break; case 5: - me->SetWalk(false); + SetRun(true); + SetEscortPaused(true); Talk(SAY_BRANN_ALGALON_INTRO_1); _events.ScheduleEvent(EVENT_SUMMON_ALGALON, 7500); - return; - case 9: - me->DespawnOrUnsummon(1); - return; - case POINT_BRANN_OUTRO: - case POINT_BRANN_OUTRO_END: - return; + break; } - - _events.ScheduleEvent(EVENT_BRANN_MOVE_INTRO, delay); } void UpdateAI(uint32 diff) override { - UpdateVictim(); + npc_escortAI::UpdateAI(diff); if (_events.Empty()) return; @@ -948,8 +940,7 @@ class npc_brann_bronzebeard_algalon : public CreatureScript switch (eventId) { case EVENT_BRANN_MOVE_INTRO: - if (_currentPoint < MAX_BRANN_WAYPOINTS_INTRO) - me->GetMotionMaster()->MovePoint(_currentPoint, BrannIntroWaypoint[_currentPoint]); + SetEscortPaused(false); break; case EVENT_SUMMON_ALGALON: if (Creature* algalon = me->GetMap()->SummonCreature(NPC_ALGALON, AlgalonSummonPos)) @@ -967,7 +958,6 @@ class npc_brann_bronzebeard_algalon : public CreatureScript private: EventMap _events; - uint32 _currentPoint; }; CreatureAI* GetAI(Creature* creature) const override |