diff --git a/data/sql/updates/pending_db_world/rev_smooth_waypoint_movement.sql b/data/sql/updates/pending_db_world/rev_smooth_waypoint_movement.sql new file mode 100644 index 000000000..085777f2d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_smooth_waypoint_movement.sql @@ -0,0 +1,15 @@ +-- Add velocity and smoothTransition columns to waypoint_data +ALTER TABLE `waypoint_data` + ADD COLUMN `velocity` FLOAT NOT NULL DEFAULT 0 AFTER `orientation`, + ADD COLUMN `smoothTransition` TINYINT NOT NULL DEFAULT 0 AFTER `delay`; + +-- Create waypoint_data_addon table for custom spline points +CREATE TABLE IF NOT EXISTS `waypoint_data_addon` ( + `PathID` INT UNSIGNED NOT NULL, + `PointID` INT UNSIGNED NOT NULL, + `SplinePointIndex` INT UNSIGNED NOT NULL, + `PositionX` FLOAT NOT NULL DEFAULT 0, + `PositionY` FLOAT NOT NULL DEFAULT 0, + `PositionZ` FLOAT NOT NULL DEFAULT 0, + PRIMARY KEY (`PathID`, `PointID`, `SplinePointIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 32b568b77..128c0c7c0 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -52,7 +52,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID, "UPDATE waypoint_data SET wpguid = ? WHERE id = ? and point = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID, "SELECT MAX(id) FROM waypoint_data", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT, "SELECT MAX(point) FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, velocity, delay, smoothTransition, move_type, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT position_x, position_y, position_z FROM waypoint_data WHERE point = 1 AND id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1", CONNECTION_SYNCH); diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 6509e4c0e..06d250a38 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -186,6 +186,13 @@ public: // Called at MovePath End virtual void PathEndReached(uint32 /*pathId*/) {} + /// == Waypoints system ============================= + + virtual void WaypointPathStarted(uint32 /*pathId*/) { } + virtual void WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointReached(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointPathEnded(uint32 /*nodeId*/, uint32 /*pathId*/) { } + void OnCharmed(bool apply) override; // Called at reaching home after evade diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index a1e2aeee4..83039da38 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -87,6 +87,7 @@ public: void GenerateWaypointArray(Movement::PointsArray* points); + using CreatureAI::WaypointReached; virtual void WaypointReached(uint32 pointId) = 0; virtual void WaypointStart(uint32 /*pointId*/) {} diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index fc4481d50..80a65b113 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -148,27 +148,28 @@ void SmartAI::UpdateFollow(const uint32 diff) } } -WaypointData const* SmartAI::GetNextWayPoint() +WaypointNode const* SmartAI::GetNextWayPoint() { - if (!mWayPoints || mWayPoints->empty()) + if (!mWayPoints || mWayPoints->Nodes.empty()) return nullptr; mCurrentWPID++; - auto itr = mWayPoints->find(mCurrentWPID); - if (itr != mWayPoints->end()) - { - mLastWP = &(*itr).second; - if (mLastWP->id != mCurrentWPID) - LOG_ERROR("scripts.ai.sai", "SmartAI::GetNextWayPoint: Got not expected waypoint id {}, expected {}", mLastWP->id, mCurrentWPID); + // mCurrentWPID is 1-based for SmartAI escort paths + if (mCurrentWPID > mWayPoints->Nodes.size()) + return nullptr; - return &(*itr).second; - } - return nullptr; + // Nodes are 0-indexed, mCurrentWPID is 1-based + WaypointNode const& node = mWayPoints->Nodes[mCurrentWPID - 1]; + mLastWP = &node; + if (mLastWP->Id != mCurrentWPID) + LOG_ERROR("scripts.ai.sai", "SmartAI::GetNextWayPoint: Got not expected waypoint id {}, expected {}", mLastWP->Id, mCurrentWPID); + + return mLastWP; } void SmartAI::GenerateWayPointArray(Movement::PointsArray* points) { - if (!mWayPoints || mWayPoints->empty()) + if (!mWayPoints || mWayPoints->Nodes.empty()) return; // Flying unit, just fill array @@ -177,16 +178,12 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points) // xinef: first point in vector is unit real position points->clear(); points->push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - uint32 wpCounter = mCurrentWPID; - auto itr = mWayPoints->find(wpCounter++); - do + // mCurrentWPID is 1-based + for (uint32 i = mCurrentWPID > 0 ? mCurrentWPID - 1 : 0; i < mWayPoints->Nodes.size(); ++i) { - WaypointData const& wp = (*itr).second; - points->push_back(G3D::Vector3(wp.x, wp.y, wp.z)); - - itr = mWayPoints->find(wpCounter++); + WaypointNode const& wp = mWayPoints->Nodes[i]; + points->push_back(G3D::Vector3(wp.X, wp.Y, wp.Z)); } - while (itr != mWayPoints->end()); } else { @@ -195,15 +192,15 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points) std::vector pVector; // xinef: first point in vector is unit real position pVector.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - uint32 wpCounter = mCurrentWPID; - uint32 length = (mWayPoints->size() - mCurrentWPID) * size; + uint32 startIdx = mCurrentWPID > 0 ? mCurrentWPID - 1 : 0; + uint32 length = (uint32)((mWayPoints->Nodes.size() - startIdx) * size); uint32 cnt = 0; - for (auto itr = mWayPoints->find(wpCounter); itr != mWayPoints->end() && cnt++ <= length; ++itr) + for (uint32 i = startIdx; i < mWayPoints->Nodes.size() && cnt++ <= length; ++i) { - WaypointData const& wp = (*itr).second; - pVector.push_back(G3D::Vector3(wp.x, wp.y, wp.z)); + WaypointNode const& wp = mWayPoints->Nodes[i]; + pVector.push_back(G3D::Vector3(wp.X, wp.Y, wp.Z)); } if (pVector.size() > 2) // more than source + dest @@ -243,10 +240,10 @@ void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat, return; } - if (!mWayPoints || mWayPoints->empty()) + if (!mWayPoints || mWayPoints->Nodes.empty()) return; - if (WaypointData const* wp = GetNextWayPoint()) + if (WaypointNode const* wp = GetNextWayPoint()) { AddEscortState(SMART_ESCORT_ESCORTING); mCanRepeatPath = repeat; @@ -262,7 +259,7 @@ void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat, GenerateWayPointArray(&pathPoints); me->GetMotionMaster()->MoveSplinePath(&pathPoints, mForcedMovement); - GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_START, nullptr, wp->id, GetScript()->GetPathId()); + GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_START, nullptr, wp->Id, GetScript()->GetPathId()); } } @@ -321,10 +318,11 @@ void SmartAI::PausePath(uint32 delay, bool forced) me->StopMoving(); me->GetMotionMaster()->MoveIdle();//force stop - auto waypoint = mWayPoints->find(mCurrentWPID); - if (waypoint->second.orientation.has_value()) + if (mCurrentWPID > 0 && mCurrentWPID <= mWayPoints->Nodes.size()) { - me->SetFacingTo(*waypoint->second.orientation); + WaypointNode const& waypoint = mWayPoints->Nodes[mCurrentWPID - 1]; + if (waypoint.Orientation.has_value()) + me->SetFacingTo(*waypoint.Orientation); } } GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_PAUSED, nullptr, mCurrentWPID, GetScript()->GetPathId()); @@ -655,7 +653,7 @@ void SmartAI::MovepointReached(uint32 id) if (mLastWP) { - me->SetPosition(mLastWP->x, mLastWP->y, mLastWP->z, me->GetOrientation()); + me->SetPosition(mLastWP->X, mLastWP->Y, mLastWP->Z, me->GetOrientation()); me->SetHomePosition(me->GetPosition()); } @@ -1278,6 +1276,32 @@ void SmartAI::PathEndReached(uint32 /*pathId*/) me->LoadPath(0); } +void SmartAI::WaypointPathStarted(uint32 /*pathId*/) +{ +} + +void SmartAI::WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) +{ +} + +void SmartAI::WaypointReached(uint32 nodeId, uint32 pathId) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, nodeId, pathId); + return; + } +} + +void SmartAI::WaypointPathEnded(uint32 nodeId, uint32 pathId) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, nodeId, pathId); + return; + } +} + void SmartAI::DistancingEnded() { SetCurrentRangeMode(true, _pendingDistancing); diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 34e6aa658..5449e4970 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -58,7 +58,7 @@ public: void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false); void EndPath(bool fail = false); void ResumePath(); - WaypointData const* GetNextWayPoint(); + WaypointNode const* GetNextWayPoint(); void GenerateWayPointArray(Movement::PointsArray* points); bool HasEscortState(uint32 uiEscortState) { return (mEscortState & uiEscortState); } void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; } @@ -206,6 +206,11 @@ public: void PathEndReached(uint32 pathId) override; + void WaypointPathStarted(uint32 pathId) override; + void WaypointStarted(uint32 nodeId, uint32 pathId) override; + void WaypointReached(uint32 nodeId, uint32 pathId) override; + void WaypointPathEnded(uint32 nodeId, uint32 pathId) override; + bool CanRespawn() override { return mcanSpawn; }; void SetCanRespawn(bool canSpawn) { mcanSpawn = canSpawn; } @@ -239,9 +244,9 @@ private: bool mWPReached; bool mOOCReached; uint32 mWPPauseTimer; - WaypointData const* mLastWP; + WaypointNode const* mLastWP; uint32 mEscortNPCFlags; - uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; } + uint32 GetWPCount() { return mWayPoints ? mWayPoints->Nodes.size() : 0; } bool mCanRepeatPath; bool mEvadeDisabled; bool mCanAutoAttack; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 82d9512f9..4024052b4 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2533,14 +2533,12 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; } - if (!path || path->empty()) + if (!path || path->Nodes.empty()) continue; - auto itrWp = path->find(1); - if (itrWp != path->end()) { - WaypointData const& wpData = itrWp->second; - float distToThisPath = creature->GetExactDistSq(wpData.x, wpData.y, wpData.z); + WaypointNode const& wpData = path->Nodes[0]; + float distToThisPath = creature->GetExactDistSq(wpData.X, wpData.Y, wpData.Z); if (distToThisPath < distanceToClosest) { diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 3f0cbdef0..847856b35 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -48,11 +48,6 @@ void SmartWaypointMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - for (auto itr : waypoint_map) - { - delete itr.second; - } - waypoint_map.clear(); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); @@ -78,14 +73,15 @@ void SmartWaypointMgr::LoadFromDB() float x = fields[2].Get(); float y = fields[3].Get(); float z = fields[4].Get(); - Optional o; + std::optional o; if (!fields[5].IsNull()) o = fields[5].Get(); uint32 delay = fields[6].Get(); if (last_entry != entry) { - waypoint_map[entry] = new WaypointPath(); + waypoint_map[entry] = WaypointPath(); + waypoint_map[entry].Id = entry; last_id = 1; count++; } @@ -94,15 +90,15 @@ void SmartWaypointMgr::LoadFromDB() LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry {}, unexpected point id {}, expected {}.", entry, id, last_id); last_id++; - WaypointData data; - data.id = id; - data.x = x; - data.y = y; - data.z = z; - data.orientation = o; - data.delay = delay; - data.move_type = WAYPOINT_MOVE_TYPE_MAX; - (*waypoint_map[entry]).emplace(id, data); + WaypointNode node; + node.Id = id; + node.X = x; + node.Y = y; + node.Z = z; + node.Orientation = o; + node.Delay = delay; + node.MoveType = WAYPOINT_MOVE_TYPE_MAX; + waypoint_map[entry].Nodes.push_back(std::move(node)); last_entry = entry; total++; @@ -112,14 +108,6 @@ void SmartWaypointMgr::LoadFromDB() LOG_INFO("server.loading", " "); } -SmartWaypointMgr::~SmartWaypointMgr() -{ - for (auto itr : waypoint_map) - { - delete itr.second; - } -} - SmartAIMgr* SmartAIMgr::instance() { static SmartAIMgr instance; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index e87e01e62..8163866e2 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -2064,21 +2064,22 @@ class SmartWaypointMgr { SmartWaypointMgr() {} public: - ~SmartWaypointMgr(); + ~SmartWaypointMgr() = default; static SmartWaypointMgr* instance(); void LoadFromDB(); - WaypointPath* GetPath(uint32 id) + WaypointPath const* GetPath(uint32 id) const { - if (waypoint_map.find(id) != waypoint_map.end()) - return waypoint_map[id]; - else return 0; + auto itr = waypoint_map.find(id); + if (itr != waypoint_map.end()) + return &itr->second; + return nullptr; } private: - std::unordered_map waypoint_map; + std::unordered_map waypoint_map; }; // all events for a single entry diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d714343c9..b1ab32562 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -388,6 +388,33 @@ void Creature::SearchFormation() } } +bool Creature::IsFormationLeader() const +{ + if (!m_formation) + return false; + + return m_formation->GetLeader() == this; +} + +void Creature::SignalFormationMovement() +{ + if (!m_formation) + return; + + if (!m_formation->GetLeader() || m_formation->GetLeader() != this) + return; + + m_formation->LeaderStartedMoving(); +} + +bool Creature::IsFormationLeaderMoveAllowed() const +{ + if (!m_formation) + return true; + + return m_formation->CanLeaderStartMoving(); +} + void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility) { if (getDeathState() != DeathState::Corpse) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 2218ab8ec..f95946ab9 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -358,6 +358,14 @@ public: [[nodiscard]] uint32 GetCurrentWaypointID() const { return m_waypointID; } void UpdateWaypointID(uint32 wpID) { m_waypointID = wpID; } + // nodeId, pathId + std::pair GetCurrentWaypointInfo() const { return _currentWaypointNodeInfo; } + void UpdateCurrentWaypointInfo(uint32 nodeId, uint32 pathId) { _currentWaypointNodeInfo = { nodeId, pathId }; } + + bool IsFormationLeader() const; + void SignalFormationMovement(); + bool IsFormationLeaderMoveAllowed() const; + void SearchFormation(); [[nodiscard]] CreatureGroup const* GetFormation() const { return m_formation; } [[nodiscard]] CreatureGroup* GetFormation() { return m_formation; } @@ -519,6 +527,7 @@ private: // WaypointMovementGenerator variable uint32 m_waypointID; uint32 m_path_id; + std::pair _currentWaypointNodeInfo{0, 0}; // Formation variable CreatureGroup* m_formation; diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 7625a6b21..e9649225c 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -538,9 +538,9 @@ void MotionMaster::MovePath(uint32 path_id, ForcedMovement forcedMovement, PathS } Movement::PointsArray points; - for (auto& point : *path) + for (auto const& node : path->Nodes) { - points.push_back(G3D::Vector3(point.second.x, point.second.y, point.second.z)); + points.push_back(G3D::Vector3(node.X, node.Y, node.Z)); } // pass the new PointsArray* to the appropriate MoveSplinePath function @@ -918,7 +918,7 @@ void MotionMaster::MoveWaypoint(uint32 path_id, bool repeatable, PathSource path if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; - Mutate(new WaypointMovementGenerator(path_id, pathSource, repeatable), MOTION_SLOT_IDLE); + Mutate(new WaypointMovementGenerator(path_id, repeatable, pathSource), MOTION_SLOT_IDLE); LOG_DEBUG("movement.motionmaster", "{} ({}) start moving over path(Id:{}, repeatable: {})", _owner->IsPlayer() ? "Player" : "Creature", _owner->GetGUID().ToString(), path_id, repeatable ? "YES" : "NO"); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 47a83f5ae..f1db91ac9 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -27,181 +27,283 @@ #include "Player.h" #include "Spell.h" #include "Transport.h" -#include "World.h" #include "SmartScriptMgr.h" +#include "World.h" -void WaypointMovementGenerator::LoadPath(Creature* creature) +inline G3D::Vector3 PositionToVector3(Position const& p) { return { p.GetPositionX(), p.GetPositionY(), p.GetPositionZ() }; } + +WaypointMovementGenerator::WaypointMovementGenerator(uint32 pathId, bool repeating, PathSource pathSource) : PathMovementBase((WaypointPath const*)nullptr), + _lastSplineId(0), _pathId(pathId), _waypointDelay(0), + _waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(true), _stalled(false), _hasBeenStalled(false), _done(false), _pathSource(pathSource), + _smoothSplineLaunched(false), _lastPassedSplineIdx(0) { - switch (i_pathSource) - { - case PathSource::WAYPOINT_MGR: - { - if (!path_id) - path_id = creature->GetWaypointPath(); +} - i_path = sWaypointMgr->GetPath(path_id); - break; - } - case PathSource::SMART_WAYPOINT_MGR: +WaypointMovementGenerator::WaypointMovementGenerator(WaypointPath& path, bool repeating) : PathMovementBase((WaypointPath const*)nullptr), + _lastSplineId(0), _pathId(0), _waypointDelay(0), + _waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(false), _stalled(false), _hasBeenStalled(false), _done(false), _pathSource(PathSource::WAYPOINT_MGR), + _smoothSplineLaunched(false), _lastPassedSplineIdx(0) +{ + i_path = &path; +} + +void WaypointMovementGenerator::DoInitialize(Creature* creature) +{ + _done = false; + + if (_loadedFromDB) + { + if (!_pathId) + _pathId = creature->GetWaypointPath(); + + switch (_pathSource) { - i_path = sSmartWaypointMgr->GetPath(path_id); - break; + default: + case PathSource::WAYPOINT_MGR: + i_path = sWaypointMgr->GetPath(_pathId); + break; + case PathSource::SMART_WAYPOINT_MGR: + i_path = sSmartWaypointMgr->GetPath(_pathId); + break; } } if (!i_path) { - // No movement found for entry - LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature {} ({}) doesn't have waypoint path id: {}", - creature->GetName(), creature->GetGUID().ToString(), path_id); + LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: creature {} ({}) doesn't have waypoint path id: {}", + creature->GetName(), creature->GetGUID().ToString(), _pathId); return; } - i_currentNode = i_path->begin()->first; + // Determine our first waypoint from the creature's stored waypoint + if (CreatureData const* creatureData = creature->GetCreatureData()) + { + if (i_path->Nodes.size() > creatureData->currentwaypoint) + { + creature->UpdateCurrentWaypointInfo(creatureData->currentwaypoint, i_path->Id); + i_currentNode = creatureData->currentwaypoint; + } + } - StartMoveNow(creature); -} - -void WaypointMovementGenerator::DoInitialize(Creature* creature) -{ - LoadPath(creature); creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); + + // Inform AI + if (CreatureAI* AI = creature->AI()) + AI->WaypointPathStarted(i_path->Id); } void WaypointMovementGenerator::DoFinalize(Creature* creature) { creature->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); + creature->SetWalk(false); } void WaypointMovementGenerator::DoReset(Creature* creature) { - if (stalled) + // We did not reach our last waypoint before reset, treat this scenario as resuming movement. + if (!_done && !_waypointReached) + _hasBeenStalled = true; + else if (_done) { - return; - } - creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); - StartMoveNow(creature); -} - -void WaypointMovementGenerator::OnArrived(Creature* creature) -{ - if (!i_path || i_path->empty()) - return; - if (m_isArrivalDone) - return; - - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - m_isArrivalDone = true; - - auto currentNodeItr = i_path->find(i_currentNode); - - if (currentNodeItr->second.event_id && urand(0, 99) < currentNodeItr->second.event_chance) - { - LOG_DEBUG("maps.script", "Creature movement start script {} at point {} for {}.", - currentNodeItr->second.event_id, i_currentNode, creature->GetGUID().ToString()); - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - creature->GetMap()->ScriptsStart(sWaypointScripts, currentNodeItr->second.event_id, creature, nullptr); - } - - // Inform script - MovementInform(creature); - creature->UpdateWaypointID(i_currentNode); - - if (currentNodeItr->second.delay) - { - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - Stop(currentNodeItr->second.delay); + // mimic IdleMovementGenerator + if (!creature->IsStopped()) + creature->StopMoving(); } } -bool WaypointMovementGenerator::StartMove(Creature* creature) +inline void UpdateHomePosition(Creature* creature, WaypointNode const& waypointNode) { - if (!i_path || i_path->empty()) - return false; - - // Xinef: Dont allow dead creatures to move - if (!creature->IsAlive()) - return false; - - if (Stopped()) - return true; + float x = waypointNode.X; + float y = waypointNode.Y; + float z = waypointNode.Z; + float o = creature->GetOrientation(); bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); - - if (m_isArrivalDone) + if (!transportPath) + creature->SetHomePosition(x, y, z, o); + else { + if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : nullptr)) { - auto currentNodeItr = i_path->find(i_currentNode); - float x = currentNodeItr->second.x; - float y = currentNodeItr->second.y; - float z = currentNodeItr->second.z; - float o = creature->GetOrientation(); + o -= trans->GetOrientation(); + creature->SetTransportHomePosition(x, y, z, o); + trans->CalculatePassengerPosition(x, y, z, &o); + creature->SetHomePosition(x, y, z, o); + } + } +} - if (!transportPath) - creature->SetHomePosition(x, y, z, o); - else +void WaypointMovementGenerator::ProcessWaypointArrival(Creature* creature, WaypointNode const& waypoint) +{ + if (_waypointReached) + return; + + if (waypoint.Delay > 0) + { + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + _waypointDelay = waypoint.Delay; + } + + // Check if the waypoint path has reached its end and may not repeat. Inform AI. + if ((i_currentNode == i_path->Nodes.size() - 1) && !_repeating && !_done) + { + _done = true; + creature->UpdateCurrentWaypointInfo(0, 0); + + if (CreatureAI* AI = creature->AI()) + { + AI->PathEndReached(i_path->Id); + AI->WaypointPathEnded(waypoint.Id, i_path->Id); + } + } + + UpdateHomePosition(creature, waypoint); + + if (waypoint.EventId && urand(0, 99) < waypoint.EventChance) + { + LOG_DEBUG("maps.script", "Creature movement start script {} at point {} for {}.", + waypoint.EventId, i_currentNode, creature->GetGUID().ToString()); + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.EventId, creature, nullptr); + } + + creature->UpdateWaypointID(waypoint.Id); + creature->UpdateCurrentWaypointInfo(waypoint.Id, i_path->Id); + + // Inform AI + if (CreatureAI* AI = creature->AI()) + { + AI->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); + AI->WaypointReached(waypoint.Id, i_path->Id); + } + + if (Unit* owner = creature->GetCharmerOrOwner()) + { + if (UnitAI* AI = owner->GetAI()) + AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode); + } + else + { + if (TempSummon* tempSummon = creature->ToTempSummon()) + if (Unit* owner2 = tempSummon->GetSummonerUnit()) + if (UnitAI* AI = owner2->GetAI()) + AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode); + } + + // All hooks called and infos updated. Time to increment the waypoint node id + if (i_path && !i_path->Nodes.empty()) // ensure that the path has not been changed in one of the hooks. + i_currentNode = (i_currentNode + 1) % i_path->Nodes.size(); + + _waypointReached = true; +} + +void WaypointMovementGenerator::StartMove(Creature* creature, bool relaunch /*= false*/) +{ + // Formation checks. Do not launch a new spline when one of our formation members is currently in combat. + if (!relaunch) + { + if (!IsAllowedToMove(creature) || (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed())) + { + _waypointDelay = 1000; + return; + } + } + + // Dont allow dead creatures to move + if (!creature->IsAlive()) + return; + + // Step two: node selection is done, build spline data + creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); + WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode); + bool const useTransportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); + + Movement::MoveSplineInit init(creature); + //! If the creature is on transport, we assume waypoints set in DB are already transport offsets + if (useTransportPath) + init.DisableTransportPathTransformations(); + + if (waypoint.SmoothTransition && i_path->Nodes.size() > 2) + { + // Build a catmullrom spline segment, stopping at delay waypoints + init.Path().push_back(PositionToVector3(creature->GetPosition())); + + bool hasDelayInSegment = false; + uint32 segmentNodes = 0; + for (uint32 i = 0; i < i_path->Nodes.size(); ++i) + { + uint32 idx = (i_currentNode + i) % i_path->Nodes.size(); + WaypointNode const& node = i_path->Nodes.at(idx); + init.Path().push_back(G3D::Vector3(node.X, node.Y, node.Z)); + segmentNodes++; + + // Stop the segment at a waypoint with a delay + if (node.Delay > 0) { - if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : nullptr)) - { - o -= trans->GetOrientation(); - creature->SetTransportHomePosition(x, y, z, o); - trans->CalculatePassengerPosition(x, y, z, &o); - creature->SetHomePosition(x, y, z, o); - } - else - transportPath = false; - // else if (vehicle) - this should never happen, vehicle offsets are const + hasDelayInSegment = true; + break; } } - // Xinef: moved the upper IF here - uint32 lastPoint = i_path->rbegin()->first; - if ((i_currentNode == lastPoint) && !repeating) // If that's our last waypoint + // If no delays found and repeating, add wrap-around points for seamless loop + if (!hasDelayInSegment && _repeating) { - creature->AI()->PathEndReached(path_id); - creature->GetMotionMaster()->Initialize(); - return false; + for (uint32 i = 0; i < std::min(3, i_path->Nodes.size()); ++i) + { + uint32 idx = (i_currentNode + i) % i_path->Nodes.size(); + WaypointNode const& node = i_path->Nodes.at(idx); + init.Path().push_back(G3D::Vector3(node.X, node.Y, node.Z)); + } } - ++i_currentNode; - if (lastPoint < i_currentNode) - i_currentNode = i_path->begin()->first; + // Need at least 3 waypoints for a meaningful catmullrom spline + if (segmentNodes >= 3) + { + init.SetFirstPointId(i_currentNode); + init.SetSmooth(); + _smoothSplineLaunched = true; + _lastPassedSplineIdx = i_currentNode; + } + else + { + // Too few points for catmullrom, fall back to linear point-to-point + init.Path().clear(); + init.MoveTo(G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); + } } - - // xinef: do not initialize motion if we got stunned in movementinform - if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + else if (!waypoint.SplinePoints.empty()) { - return true; + // We have spline points in waypoint_data_addon table + int32 splineIndex = 0; + + auto itr = waypoint.SplinePoints.begin(); + if (splineIndex) + std::advance(itr, splineIndex); + + init.Path().reserve(waypoint.SplinePoints.size() - splineIndex); + std::copy(itr, waypoint.SplinePoints.end(), std::back_inserter(init.Path())); + + // Add starting vertex and destination + init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition())); + init.Path().insert(init.Path().end(), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); } - - auto currentNodeItr = i_path->find(i_currentNode); - WaypointData const& node = currentNodeItr->second; - - m_isArrivalDone = false; - - creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); - - Movement::Location formationDest(node.x, node.y, node.z, 0.0f); - Movement::MoveSplineInit init(creature); - - //! If creature is on transport, we assume waypoints set in DB are already transport offsets - if (transportPath) + else { - init.DisableTransportPathTransformations(); - if (TransportBase* trans = creature->GetDirectTransport()) - trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); + // Smooth transition for short paths (<=2 nodes): use previous spline endpoint as start + if (waypoint.SmoothTransition && !creature->movespline->Finalized() && _lastSplineId == creature->movespline->GetId()) + { + init.MoveTo(creature->movespline->FinalDestination(), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); + if (!init.Path().empty()) + init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition())); + } + else + init.MoveTo(PositionToVector3(creature->GetPosition()), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); } - float z = node.z; - creature->UpdateAllowedPositionZ(node.x, node.y, z); - //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call - //! but formationDest contains global coordinates - init.MoveTo(node.x, node.y, z, true, true); + if (waypoint.Orientation.has_value() && waypoint.Delay > 0) + init.SetFacing(*waypoint.Orientation); - if (node.orientation.has_value() && node.delay > 0) - init.SetFacing(*node.orientation); - - switch (node.move_type) + switch (waypoint.MoveType) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(AnimTier::Ground); @@ -219,89 +321,203 @@ bool WaypointMovementGenerator::StartMove(Creature* creature) break; } + if (creature->CanFly()) + init.SetFly(); + + if (waypoint.Velocity > 0.f) + init.SetVelocity(waypoint.Velocity); + init.Launch(); - //Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature && creature->GetFormation()->CanLeaderStartMoving()) - creature->GetFormation()->LeaderStartedMoving(); + if (!creature->movespline->Finalized()) + _lastSplineId = creature->movespline->GetId(); - return true; + // Inform formation + creature->SignalFormationMovement(); + + // Inform AI + if (!relaunch) + if (CreatureAI* AI = creature->AI()) + AI->WaypointStarted(waypoint.Id, i_path->Id); + + _waypointReached = false; + _recalculateSpeed = false; + _hasBeenStalled = false; } bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 diff) { - // Waypoint movement can be switched on/off - // This is quite handy for escort quests and other stuff - if (stalled) - { - Stop(1000); + if (!creature || !creature->IsAlive()) return true; - } - if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + + if (_done || !i_path || i_path->Nodes.empty()) + return true; + + // Stop movement if paused, rooted, or casting + if (!IsAllowedToMove(creature) && !creature->movespline->Finalized()) { creature->StopMoving(); - Stop(1000); + _lastSplineId = 0; + _smoothSplineLaunched = false; + } + + // Set home position to current position. + if (!creature->movespline->Finalized()) + { + bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); + if (!transportPath) + creature->SetHomePosition(creature->GetPosition()); + } + + // Smooth spline: track waypoint passages without rebuilding + if (_smoothSplineLaunched && creature->movespline->GetId() == _lastSplineId) + { + int32 currentIdx = creature->movespline->currentPathIdx(); + + // Process passed waypoints + while (_lastPassedSplineIdx < currentIdx) + { + _lastPassedSplineIdx++; + WaypointNode const& passedWp = i_path->Nodes.at(i_currentNode); + + UpdateHomePosition(creature, passedWp); + creature->UpdateWaypointID(passedWp.Id); + creature->UpdateCurrentWaypointInfo(passedWp.Id, i_path->Id); + + if (passedWp.EventId && urand(0, 99) < passedWp.EventChance) + { + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + creature->GetMap()->ScriptsStart(sWaypointScripts, passedWp.EventId, creature, nullptr); + } + + if (CreatureAI* AI = creature->AI()) + { + AI->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); + AI->WaypointReached(passedWp.Id, i_path->Id); + } + + // Advance node + i_currentNode = (i_currentNode + 1) % i_path->Nodes.size(); + + // If this waypoint has a delay, stop the spline and pause + if (passedWp.Delay > 0) + { + creature->StopMoving(); + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + _waypointDelay = passedWp.Delay; + _waypointReached = true; + _smoothSplineLaunched = false; + if (passedWp.Orientation.has_value()) + creature->SetFacingTo(*passedWp.Orientation); + + return true; + } + } + + if (creature->movespline->Finalized()) + { + if (!_repeating) + { + // Path ended + _done = true; + _smoothSplineLaunched = false; + creature->UpdateCurrentWaypointInfo(0, 0); + if (CreatureAI* AI = creature->AI()) + { + AI->PathEndReached(i_path->Id); + AI->WaypointPathEnded(i_path->Nodes.at(i_currentNode).Id, i_path->Id); + } + } + else + { + // Repeating: rebuild spline + _smoothSplineLaunched = false; + StartMove(creature); + } + } + return true; } - // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) - return false; + // Non-smooth: per-waypoint logic + WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode); + UpdateWaypointState(creature, waypoint); - // Xinef: Dont allow dead creatures to move - if (!creature->IsAlive()) - return false; + // Process movement preventing timers + if (_waypointDelay > 0) + { + _waypointDelay -= diff; + if (_waypointDelay > 0) + return true; + } - if (Stopped()) + if (_pauseTime.has_value()) { - if (CanMove(diff)) - return StartMove(creature); - } - else - { - if (creature->movespline->Finalized()) - { - OnArrived(creature); - return StartMove(creature); - } + *_pauseTime -= diff; + if (*_pauseTime > 0) + return true; + else + _pauseTime.reset(); } + + // Timers are ready, let's try to move + if (IsAllowedToMove(creature) && (_waypointReached || _recalculateSpeed || _hasBeenStalled)) + StartMove(creature, _recalculateSpeed || _hasBeenStalled); + return true; } -void WaypointMovementGenerator::MovementInform(Creature* creature) +void WaypointMovementGenerator::Pause(uint32 timer /*= 0*/) { - if (creature->AI()) - creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); - - if (Unit* owner = creature->GetCharmerOrOwner()) - { - if (UnitAI* AI = owner->GetAI()) - AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode); - } - else - { - if (TempSummon* tempSummon = creature->ToTempSummon()) - if (Unit* owner = tempSummon->GetSummonerUnit()) - if (UnitAI* AI = owner->GetAI()) - AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode); - } + _stalled = timer ? false : true; + _hasBeenStalled = !_waypointReached; + _pauseTime = timer; } -void WaypointMovementGenerator::Pause(uint32 timer) +void WaypointMovementGenerator::Resume(uint32 overrideTimer /*= 0*/) { - if (timer) - i_nextMoveTime.Reset(timer); - else - { - // No timer? Will be paused forever until ::Resume is called - stalled = true; - i_nextMoveTime.Reset(1); - } + _hasBeenStalled = !_waypointReached; + _stalled = false; + if (overrideTimer) + _pauseTime = overrideTimer; } -void WaypointMovementGenerator::Resume(uint32 /*overrideTimer/*/) +bool WaypointMovementGenerator::GetResetPosition(float& x, float& y, float& z) { - stalled = false; + // prevent a crash at empty waypoint path. + if (!i_path || i_path->Nodes.empty()) + return false; + + ASSERT(i_currentNode < i_path->Nodes.size(), "WaypointMovementGenerator::GetResetPos: tried to reference a node id ({}) which is not included in path ({})", i_currentNode, i_path->Id); + WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode); + + x = waypoint.X; + y = waypoint.Y; + z = waypoint.Z; + return true; +} + +bool WaypointMovementGenerator::IsAllowedToMove(Creature* creature) const +{ + if (_stalled || _done) + return false; + + if (_pauseTime.has_value()) + return false; + + if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + return false; + + return true; +} + +void WaypointMovementGenerator::UpdateWaypointState(Creature* creature, WaypointNode const& waypointNode) +{ + if (creature->movespline->GetId() != _lastSplineId) + return; + + if (creature->movespline->Finalized()) + ProcessWaypointArrival(creature, waypointNode); } //----------------------------------------------------// diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index 44194581e..12479820a 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -54,49 +54,43 @@ class WaypointMovementGenerator : public MovementGeneratorMedium< Crea public PathMovementBase { public: - WaypointMovementGenerator(uint32 _path_id = 0, PathSource pathSource = PathSource::WAYPOINT_MGR, bool _repeating = true, bool _stalled = false) - : PathMovementBase((WaypointPath const*)nullptr), i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating), stalled(_stalled), i_pathSource(pathSource) {} + explicit WaypointMovementGenerator(uint32 pathId = 0, bool repeating = true, PathSource pathSource = PathSource::WAYPOINT_MGR); + explicit WaypointMovementGenerator(WaypointPath& path, bool repeating = true); ~WaypointMovementGenerator() { i_path = nullptr; } + void DoInitialize(Creature*); void DoFinalize(Creature*); void DoReset(Creature*); bool DoUpdate(Creature*, uint32 diff); - void Pause(uint32 timer = 0); - void Resume(uint32 overrideTimer/* = 0*/); - void MovementInform(Creature*); + void unitSpeedChanged() override { _recalculateSpeed = true; } + void Pause(uint32 timer = 0) override; + void Resume(uint32 overrideTimer = 0) override; + bool GetResetPosition(float& x, float& y, float& z) override; - MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; } - - // now path movement implmementation - void LoadPath(Creature*); + MovementGeneratorType GetMovementGeneratorType() override { return WAYPOINT_MOTION_TYPE; } private: - void Stop(int32 time) { i_nextMoveTime.Reset(time);} + void ProcessWaypointArrival(Creature*, WaypointNode const&); + void StartMove(Creature*, bool relaunch = false); + bool IsAllowedToMove(Creature*) const; + void UpdateWaypointState(Creature*, WaypointNode const&); - bool Stopped() { return !i_nextMoveTime.Passed();} + uint32 _lastSplineId; + uint32 _pathId; + int32 _waypointDelay; + std::optional _pauseTime; + bool _waypointReached; - bool CanMove(int32 diff) - { - i_nextMoveTime.Update(diff); - return i_nextMoveTime.Passed(); - } - - void OnArrived(Creature*); - bool StartMove(Creature*); - - void StartMoveNow(Creature* creature) - { - i_nextMoveTime.Reset(0); - StartMove(creature); - } - - TimeTrackerSmall i_nextMoveTime; - bool m_isArrivalDone; - uint32 path_id; - bool repeating; - bool stalled; - PathSource i_pathSource; + bool _recalculateSpeed; + bool _repeating; + bool _loadedFromDB; + bool _stalled; + bool _hasBeenStalled; + bool _done; + PathSource _pathSource; + bool _smoothSplineLaunched; + int32 _lastPassedSplineIdx; }; /** FlightPathMovementGenerator generates movement of the player for the paths diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h index 3b134d44e..07cfcb41f 100644 --- a/src/server/game/Movement/Spline/MoveSpline.h +++ b/src/server/game/Movement/Spline/MoveSpline.h @@ -122,6 +122,7 @@ namespace Movement [[nodiscard]] Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last()) : Vector3(); } [[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1) : Vector3(); } [[nodiscard]] int32 currentPathIdx() const; + [[nodiscard]] int32 MaxPathIdx() const { return spline.last() - 1; } [[nodiscard]] bool HasAnimation() const { return splineflags.animation; } [[nodiscard]] uint8 GetAnimationType() const { return splineflags.animId; } diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp index 4ae646e37..16b58a45d 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.cpp +++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp @@ -199,6 +199,25 @@ namespace Movement args.flags.EnableFacingAngle(); } + void MoveSplineInit::MoveTo(Vector3 const& start, Vector3 const& dest, bool generatePath, bool forceDestination) + { + if (generatePath) + { + PathGenerator path(unit); + bool result = path.CalculatePath(start.x, start.y, start.z, dest.x, dest.y, dest.z, forceDestination); + if (result && !(path.GetPathType() & PATHFIND_NOPATH)) + { + MovebyPath(path.GetPath()); + return; + } + } + + args.path_Idx_offset = 0; + args.path.resize(2); + TransportPathTransform transform(unit, args.TransformForTransport); + args.path[1] = transform(dest); + } + void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination) { if (generatePath) diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h index 5e26dad04..16120d9bb 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.h +++ b/src/server/game/Movement/Spline/MoveSplineInit.h @@ -99,6 +99,7 @@ namespace Movement /* Initializes simple A to B motion, A is current unit's position, B is destination */ + void MoveTo(Vector3 const& start, Vector3 const& destination, bool generatePath = true, bool forceDestination = false); void MoveTo(const Vector3& destination, bool generatePath = false, bool forceDestination = false); void MoveTo(float x, float y, float z, bool generatePath = false, bool forceDestination = false); diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h new file mode 100644 index 000000000..6b55ab5af --- /dev/null +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -0,0 +1,72 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * 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 . + */ + +#ifndef ACORE_WAYPOINTDEFINES_H +#define ACORE_WAYPOINTDEFINES_H + +#include "Define.h" +#include "G3D/Vector3.h" +#include +#include + +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), Velocity(0.f), Delay(0), EventId(0), MoveType(WAYPOINT_MOVE_TYPE_RUN), EventChance(0), SmoothTransition(false) { } + WaypointNode(uint32 id, float x, float y, float z, std::optional orientation = { }, float velocity = 0.f, uint32 delay = 0, bool smoothTransition = false) : + Id(id), X(x), Y(y), Z(z), Orientation(orientation), Velocity(velocity), Delay(delay), SmoothTransition(smoothTransition) + { + EventId = 0; + MoveType = WAYPOINT_MOVE_TYPE_WALK; + EventChance = 100; + } + + uint32 Id; + float X, Y, Z; + std::optional Orientation; + float Velocity; + uint32 Delay; + uint32 EventId; + uint32 MoveType; + uint8 EventChance; + bool SmoothTransition; + std::vector SplinePoints; +}; + +struct WaypointPath +{ + WaypointPath() : Id(0) { } + WaypointPath(uint32 _id, std::vector&& _nodes) + { + Id = _id; + Nodes = _nodes; + } + + std::vector Nodes; + uint32 Id; +}; + +#endif diff --git a/src/server/game/Movement/Waypoints/WaypointMgr.cpp b/src/server/game/Movement/Waypoints/WaypointMgr.cpp index 5d673b35a..0d0334d73 100644 --- a/src/server/game/Movement/Waypoints/WaypointMgr.cpp +++ b/src/server/game/Movement/Waypoints/WaypointMgr.cpp @@ -22,20 +22,6 @@ #include "QueryResult.h" #include "Timer.h" -WaypointMgr::WaypointMgr() -{ -} - -WaypointMgr::~WaypointMgr() -{ - for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr) - { - itr->second.clear(); - } - - _waypointStore.clear(); -} - WaypointMgr* WaypointMgr::instance() { static WaypointMgr instance; @@ -46,8 +32,8 @@ void WaypointMgr::Load() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 - QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data ORDER BY id, point"); + // 0 1 2 3 4 5 6 7 8 9 10 11 + QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, velocity, delay, smoothTransition, move_type, action, action_chance FROM waypoint_data ORDER BY id, point"); if (!result) { @@ -61,66 +47,113 @@ void WaypointMgr::Load() do { Field* fields = result->Fetch(); - WaypointData data; - uint32 pathId = fields[0].Get(); - WaypointPath& path = _waypointStore[pathId]; - float x = fields[2].Get(); float y = fields[3].Get(); float z = fields[4].Get(); - std::optional o; + std::optional o; if (!fields[5].IsNull()) o = fields[5].Get(); + float velocity = fields[6].Get(); + Acore::NormalizeMapCoord(x); Acore::NormalizeMapCoord(y); - data.id = fields[1].Get(); - data.x = x; - data.y = y; - data.z = z; - data.orientation = o; - data.move_type = fields[6].Get(); + WaypointNode waypoint; + waypoint.Id = fields[1].Get(); + waypoint.X = x; + waypoint.Y = y; + waypoint.Z = z; + if (o.has_value()) + waypoint.Orientation = o; + waypoint.Velocity = velocity; + waypoint.Delay = fields[7].Get(); + waypoint.SmoothTransition = fields[8].Get(); + waypoint.MoveType = fields[9].Get(); - if (data.move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.MoveType >= WAYPOINT_MOVE_TYPE_MAX) { - //LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", wp->id); + LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", waypoint.Id); continue; } - data.delay = fields[7].Get(); - data.event_id = fields[8].Get(); - data.event_chance = fields[9].Get(); + waypoint.EventId = fields[10].Get(); + waypoint.EventChance = fields[11].Get(); - path.emplace(data.id, data); + WaypointPath& path = _waypointStore[pathId]; + path.Id = pathId; + path.Nodes.push_back(std::move(waypoint)); ++count; } while (result->NextRow()); - for (auto itr = _waypointStore.begin(); itr != _waypointStore.end(); ) - { - uint32 first = itr->second.begin()->first; - uint32 last = itr->second.rbegin()->first; - if (last - first + 1 != itr->second.size()) - { - LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has non-contiguous pointids, skipping", itr->first); - itr = _waypointStore.erase(itr); - } - else - ++itr; - } - LOG_INFO("server.loading", ">> Loaded {} waypoints in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } +void WaypointMgr::LoadWaypointAddons() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT PathID, PointID, SplinePointIndex, PositionX, PositionY, PositionZ FROM waypoint_data_addon ORDER BY PathID, PointID, SplinePointIndex"); + + if (!result) + { + LOG_INFO("server.loading", ">> Loaded 0 waypoint addon data. DB table `waypoint_data_addon` is empty!"); + LOG_INFO("server.loading", " "); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 pathId = fields[0].Get(); + + auto it = _waypointStore.find(pathId); + if (it == _waypointStore.end()) + { + LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PathID {} but there is no such path in waypoint_data. Ignoring.", pathId); + continue; + } + + WaypointPath& path = it->second; + uint32 pointId = fields[1].Get(); + + auto itr = std::find_if(path.Nodes.begin(), path.Nodes.end(), [pointId](WaypointNode const& node) + { + return node.Id == pointId; + }); + + if (itr == path.Nodes.end()) + { + LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PointID {} of PathID {} but there is no such point in waypoint_data. Ignoring.", pointId, pathId); + continue; + } + + float x = fields[3].Get(); + float y = fields[4].Get(); + float z = fields[5].Get(); + + Acore::NormalizeMapCoord(x); + Acore::NormalizeMapCoord(y); + + itr->SplinePoints.push_back(G3D::Vector3(x, y, z)); + + ++count; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} waypoint addon data in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); +} + void WaypointMgr::ReloadPath(uint32 id) { - WaypointPathContainer::iterator itr = _waypointStore.find(id); + auto itr = _waypointStore.find(id); if (itr != _waypointStore.end()) - { _waypointStore.erase(itr); - } WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID); @@ -131,13 +164,10 @@ void WaypointMgr::ReloadPath(uint32 id) if (!result) return; - WaypointPath& path = _waypointStore[id]; - + std::vector values; do { Field* fields = result->Fetch(); - WaypointData data; - float x = fields[1].Get(); float y = fields[2].Get(); float z = fields[3].Get(); @@ -145,26 +175,34 @@ void WaypointMgr::ReloadPath(uint32 id) if (!fields[4].IsNull()) o = fields[4].Get(); + float velocity = fields[5].Get(); + Acore::NormalizeMapCoord(x); Acore::NormalizeMapCoord(y); - data.id = fields[0].Get(); - data.x = x; - data.y = y; - data.z = z; - data.orientation = o; - data.move_type = fields[5].Get(); + WaypointNode waypoint; + waypoint.Id = fields[0].Get(); + waypoint.X = x; + waypoint.Y = y; + waypoint.Z = z; + if (o.has_value()) + waypoint.Orientation = o; + waypoint.Velocity = velocity; + waypoint.Delay = fields[6].Get(); + waypoint.SmoothTransition = fields[7].Get(); + waypoint.MoveType = fields[8].Get(); - if (data.move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.MoveType >= WAYPOINT_MOVE_TYPE_MAX) { - //LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", wp->id); + LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", waypoint.Id); continue; } - data.delay = fields[6].Get(); - data.event_id = fields[7].Get(); - data.event_chance = fields[8].Get(); + waypoint.EventId = fields[9].Get(); + waypoint.EventChance = fields[10].Get(); - path.emplace(data.id, data); + values.push_back(std::move(waypoint)); } while (result->NextRow()); + + _waypointStore[id] = WaypointPath(id, std::move(values)); } diff --git a/src/server/game/Movement/Waypoints/WaypointMgr.h b/src/server/game/Movement/Waypoints/WaypointMgr.h index f9c0d59df..d6bb3ec21 100644 --- a/src/server/game/Movement/Waypoints/WaypointMgr.h +++ b/src/server/game/Movement/Waypoints/WaypointMgr.h @@ -18,35 +18,8 @@ #ifndef ACORE_WAYPOINTMANAGER_H #define ACORE_WAYPOINTMANAGER_H -#include "Define.h" -#include +#include "WaypointDefines.h" #include -#include -#include - -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; - std::optional orientation; - uint32 delay; - uint32 event_id = 0; - uint32 move_type = 0; - uint8 event_chance = 0; -}; - -typedef std::map WaypointPath; -typedef std::unordered_map WaypointPathContainer; class WaypointMgr { @@ -59,10 +32,13 @@ public: // Loads all paths from database, should only run on startup void Load(); + // Loads additional path data for waypoints from database. Should only be called on startup. + void LoadWaypointAddons(); + // Returns the path from a given id WaypointPath const* GetPath(uint32 id) const { - WaypointPathContainer::const_iterator itr = _waypointStore.find(id); + auto itr = _waypointStore.find(id); if (itr != _waypointStore.end()) return &itr->second; @@ -70,10 +46,9 @@ public: } private: - WaypointMgr(); - ~WaypointMgr(); + WaypointMgr() { } - WaypointPathContainer _waypointStore; + std::unordered_map _waypointStore; }; #define sWaypointMgr WaypointMgr::instance() diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 2f87bbb00..ffaf405cb 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -779,6 +779,9 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", "Loading Waypoints..."); sWaypointMgr->Load(); + LOG_INFO("server.loading", "Loading Waypoint Addons..."); + sWaypointMgr->LoadWaypointAddons(); + LOG_INFO("server.loading", "Loading SmartAI Waypoints..."); sSmartWaypointMgr->LoadFromDB(); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp index 3409fe6fa..55f33f152 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp @@ -213,6 +213,7 @@ struct npc_grimstone : public npc_escortAI } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -568,6 +569,7 @@ struct npc_rocknot : public npc_escortAI go->SetGoState((GOState)state); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp index 484a0ada7..910f95557 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp @@ -176,6 +176,7 @@ public: void JustEngagedWith(Unit* /*who*/) override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp b/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp index 1979f0d7f..c4ea03272 100644 --- a/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp @@ -52,6 +52,7 @@ public: { npc_professor_phizzlethorpeAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp index 13d92bc96..365d7fe6b 100644 --- a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp @@ -45,6 +45,7 @@ struct npc_ranger_lilatha : public npc_escortAI { npc_ranger_lilatha(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp index 8d08ca387..08a111e59 100644 --- a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp @@ -137,6 +137,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp index e6c8fbeae..4d3072519 100644 --- a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp +++ b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp @@ -68,6 +68,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp index dcf642d0e..1147fdf04 100644 --- a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp +++ b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp @@ -55,6 +55,7 @@ public: { npc_deathstalker_erlandAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp b/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp index 2cfb08110..d06fd85d8 100644 --- a/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp +++ b/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp @@ -88,6 +88,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -318,6 +319,7 @@ public: uiPhase = 0; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index 747a452b3..eab33d378 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -1423,6 +1423,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -2820,6 +2821,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/zone_westfall.cpp b/src/server/scripts/EasternKingdoms/zone_westfall.cpp index 7e2bbb70f..ab89105c8 100644 --- a/src/server/scripts/EasternKingdoms/zone_westfall.cpp +++ b/src/server/scripts/EasternKingdoms/zone_westfall.cpp @@ -81,6 +81,7 @@ public: textCounter = SAY_DS_DOWN_1; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp index 62bbb72f7..85749ea1c 100644 --- a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp @@ -55,6 +55,7 @@ public: IsFriendSummoned = false; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index 7165323ce..2ad76cf20 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -527,6 +527,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 uiPointId) override { switch (uiPointId) diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp index fc54b6215..0030f23bc 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp @@ -303,6 +303,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -1014,6 +1015,7 @@ public: Start(false); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (waypointId == 7) diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp index bcae88f33..8286e98e3 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp @@ -227,6 +227,7 @@ struct npc_general_andorov : public npc_escortAI events.ScheduleEvent(EVENT_STRIKE, 2s, 5s); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/zone_ashenvale.cpp b/src/server/scripts/Kalimdor/zone_ashenvale.cpp index 5fee325ec..50b7dccef 100644 --- a/src/server/scripts/Kalimdor/zone_ashenvale.cpp +++ b/src/server/scripts/Kalimdor/zone_ashenvale.cpp @@ -126,6 +126,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp index 2316a2e33..9779fb315 100644 --- a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp @@ -246,6 +246,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_darkshore.cpp b/src/server/scripts/Kalimdor/zone_darkshore.cpp index c2a452f18..7b40eac34 100644 --- a/src/server/scripts/Kalimdor/zone_darkshore.cpp +++ b/src/server/scripts/Kalimdor/zone_darkshore.cpp @@ -330,6 +330,7 @@ public: // pSummoned->AI()->AttackStart(me); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp index aea34b3c7..649b4adea 100644 --- a/src/server/scripts/Kalimdor/zone_desolace.cpp +++ b/src/server/scripts/Kalimdor/zone_desolace.cpp @@ -257,6 +257,7 @@ public: me->SetFaction(faction); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { RelocateSummons(); diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index d521c6693..bc1538d2c 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -553,6 +553,7 @@ public: else if (EventOnWait) EventTimer -= diff; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { CurrWP = waypointId; diff --git a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp index 3392248da..2aa681190 100644 --- a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp +++ b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp @@ -102,6 +102,7 @@ public: { npc_kaya_flathoofAI(Creature* creature) : npc_escortAI(creature) {} + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Kalimdor/zone_tanaris.cpp b/src/server/scripts/Kalimdor/zone_tanaris.cpp index 602d1ea43..4a995ca46 100644 --- a/src/server/scripts/Kalimdor/zone_tanaris.cpp +++ b/src/server/scripts/Kalimdor/zone_tanaris.cpp @@ -168,6 +168,7 @@ public: { npc_custodian_of_timeAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index fa4eabc52..8fe236fe5 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -74,6 +74,7 @@ public: void Reset() override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -494,6 +495,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp index ac747b38b..9c60689c5 100644 --- a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp +++ b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp @@ -85,6 +85,7 @@ public: void Reset() override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -170,6 +171,7 @@ public: void Reset() override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp b/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp index 885ddac1f..ff4845c7e 100644 --- a/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp +++ b/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp @@ -69,6 +69,7 @@ public: uint32 DemoralizingShoutTimer; + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_winterspring.cpp b/src/server/scripts/Kalimdor/zone_winterspring.cpp index 2130ce14e..4da2a9f6a 100644 --- a/src/server/scripts/Kalimdor/zone_winterspring.cpp +++ b/src/server/scripts/Kalimdor/zone_winterspring.cpp @@ -383,6 +383,7 @@ public: StartNextDialogueText(SAY_PRIESTESS_ALTAR_3); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 pointId) override { switch (pointId) diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp index fb6b32d03..faf314e6f 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp @@ -233,6 +233,7 @@ public: zarithrian->AI()->JustSummoned(me); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (waypointId == MAX_PATH_FLAMECALLER_WAYPOINTS) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp index 8d1bc1864..4f8d0fcea 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp @@ -519,6 +519,7 @@ public: bCheck = false; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 uiPoint) override { if (uiPoint == 1) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp index 99d69be84..6b3ac0d9e 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp @@ -340,6 +340,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 i) override { if (i == 12) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp index 374f1d2b5..d1cd70bd0 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp @@ -592,6 +592,7 @@ public: void EnterEvadeMode(EvadeReason /*why*/) override {} + using CreatureAI::WaypointReached; void WaypointReached(uint32 i) override { if (!pInstance) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 90e53cf4a..77f6c7069 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -791,6 +791,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp index cd8c36c9e..55ea322f0 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp @@ -267,6 +267,7 @@ struct boss_bjarngrim : public npc_escortAI m_uiStance = stance; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 Point) override { if (Point == 1) diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp index 7d5f74059..41f9cf1f0 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp @@ -312,6 +312,7 @@ public: TalkEvent = false; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 id) override; void InitializeEvent(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 7b1728615..550beac7f 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -974,6 +974,7 @@ struct npc_mimirons_inferno : public npc_escortAI void AttackStart(Unit*) override { } void MoveInLineOfSight(Unit*) override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*waypointId*/) override { } void DoAction(int32 param) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index 4fc2897f6..a73aafa68 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -895,6 +895,7 @@ struct boss_thorim_lightning_orb : public npc_escortAI me->CastSpell(me, SPELL_LIGHTNING_DESTRUCTION, true); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*point*/) override { } @@ -960,6 +961,7 @@ struct boss_thorim_sif_blizzard : public npc_escortAI me->CastSpell(me, SPELL_BLIZZARD, true); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*point*/) override { } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp index f0f759e74..407793986 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp @@ -934,6 +934,7 @@ struct boss_yoggsaron_cloud : public npc_escortAI void MoveInLineOfSight(Unit* /*who*/) override {} void AttackStart(Unit* /*who*/) override {} + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*point*/) override {} void Reset() override diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index 4d1bc0924..c805c43f0 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -312,6 +312,7 @@ struct violet_hold_trashAI : public npc_escortAI CreatureStartAttackDoor(); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 id) override { if (PLoc < 6) @@ -1021,6 +1022,7 @@ public: uint32 timer; uint8 count; + using CreatureAI::WaypointReached; void WaypointReached(uint32 uiWPointId) override { if (!pInstance) diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index af5491db7..a3cab28af 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -377,6 +377,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -752,6 +753,7 @@ public: player->FailQuest(QUEST_ESCAPING_THE_MIST); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -849,6 +851,7 @@ public: else Bonker_agro = 0; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index bccd217ad..c83884871 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -75,6 +75,7 @@ public: summoned->AI()->AttackStart(me->GetVictim()); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 928a0a71e..731f022e9 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -126,6 +126,7 @@ public: DoMeleeAttackIfReady(); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -210,6 +211,7 @@ public: Start(false, summonerGUID); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (waypointId != 26) diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp index 9afba1804..639991ba5 100644 --- a/src/server/scripts/Northrend/zone_icecrown.cpp +++ b/src/server/scripts/Northrend/zone_icecrown.cpp @@ -747,6 +747,7 @@ public: summons.Despawn(summon); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 pointId) override { switch (pointId) diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 4af05b4b9..a9de0bf08 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -521,6 +521,7 @@ public: uint32 m_uiChatTimer; + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 7f3c5873a..b94dabc73 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -73,6 +73,7 @@ struct npc_frosthound : public npc_escortAI return; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -568,6 +569,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*waypointId*/) override { } void JustDied(Unit* /*killer*/) override { } void OnCharmed(bool /*apply*/) override { } diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index f281cae2c..140ef72fe 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -195,6 +195,7 @@ public: npc_escortAI::MoveInLineOfSight(who); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -320,6 +321,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Outland/zone_nagrand.cpp b/src/server/scripts/Outland/zone_nagrand.cpp index 609391185..fbef4231c 100644 --- a/src/server/scripts/Outland/zone_nagrand.cpp +++ b/src/server/scripts/Outland/zone_nagrand.cpp @@ -114,6 +114,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -340,6 +341,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index 5136e89dd..9c3cf495e 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -164,6 +164,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 i) override { Player* player = GetPlayerForEscort(); @@ -626,6 +627,7 @@ public: player->FailQuest(Q_ALMABTRIEB); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -709,6 +711,7 @@ public: uiTakeTimer = 3000; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Outland/zone_shattrath_city.cpp b/src/server/scripts/Outland/zone_shattrath_city.cpp index 0cd938e3b..4447b2797 100644 --- a/src/server/scripts/Outland/zone_shattrath_city.cpp +++ b/src/server/scripts/Outland/zone_shattrath_city.cpp @@ -161,6 +161,7 @@ public: public: npc_kservantAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Outland/zone_terokkar_forest.cpp b/src/server/scripts/Outland/zone_terokkar_forest.cpp index 63f623cd5..d58ff430b 100644 --- a/src/server/scripts/Outland/zone_terokkar_forest.cpp +++ b/src/server/scripts/Outland/zone_terokkar_forest.cpp @@ -374,6 +374,7 @@ public: { npc_isla_starmaneAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index ba6deb25d..3e7ebb34e 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -1356,6 +1356,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*waypointId*/) override { }