diff --git a/sql/updates/world/4.3.4/2020_99_99_00_world.sql b/sql/updates/world/4.3.4/2020_99_99_00_world.sql new file mode 100644 index 00000000000..dad48d7a97d --- /dev/null +++ b/sql/updates/world/4.3.4/2020_99_99_00_world.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS `waypoint_data_addon`; +CREATE TABLE `waypoint_data_addon` ( + `PathID` INT(10) UNSIGNED NOT NULL DEFAULT 0, + `PointID` INT(10) UNSIGNED NOT NULL DEFAULT 0, + `SplinePointIndex` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0, + `PositionX` FLOAT NOT NULL DEFAULT 0, + `PositionY` FLOAT NOT NULL DEFAULT 0, + `PositionZ` FLOAT NOT NULL DEFAULT 0 +); + +ALTER TABLE `waypoint_data` CHANGE `delay` `delay` INT(10) DEFAULT 0 NOT NULL; diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index a5b1a67f866..8cf08901fac 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -18,6 +18,7 @@ #include "WaypointMovementGenerator.h" #include "Creature.h" #include "CreatureAI.h" +#include "G3DPosition.hpp" #include "Log.h" #include "Map.h" #include "MoveSpline.h" @@ -26,13 +27,13 @@ #include "Transport.h" #include "WaypointManager.h" -WaypointMovementGenerator::WaypointMovementGenerator(uint32 pathId, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(pathId), -_repeating(repeating), _loadedFromDB(true), _stalled(false), _done(false) +WaypointMovementGenerator::WaypointMovementGenerator(uint32 pathId, bool repeating) : _nextMoveTimer(0), _movementInformTimer(0), _lastSplineId(0), _pathId(pathId), +_waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(true), _stalled(false), _hasBeenStalled(false), _done(false) { } -WaypointMovementGenerator::WaypointMovementGenerator(WaypointPath& path, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(0), -_repeating(repeating), _loadedFromDB(false), _stalled(false), _done(false) +WaypointMovementGenerator::WaypointMovementGenerator(WaypointPath& path, bool repeating) : _nextMoveTimer(0), _movementInformTimer(0), _lastSplineId(0), _pathId(0), +_waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(false), _stalled(false), _hasBeenStalled(false), _done(false) { _path = &path; } @@ -56,7 +57,8 @@ void WaypointMovementGenerator::DoInitialize(Creature* creature) return; } - _nextMoveTime.Reset(1000); + // We launch the first movement after a initial 1s delay. + _nextMoveTimer.Reset(1000); // inform AI if (creature->IsAIEnabled) @@ -71,8 +73,9 @@ void WaypointMovementGenerator::DoFinalize(Creature* creature) void WaypointMovementGenerator::DoReset(Creature* creature) { - if (!_done && _nextMoveTime.Passed() && CanMove(creature)) - StartMove(creature); + // We did not reach our last waypoint before reset, treat this scenario as resuming movement. + if (!_done && !_waypointReached) + _hasBeenStalled = true; else if (_done) { // mimic IdleMovementGenerator @@ -81,17 +84,45 @@ void WaypointMovementGenerator::DoReset(Creature* creature) } } -void WaypointMovementGenerator::OnArrived(Creature* creature) +void WaypointMovementGenerator::HandleMovementInformationHooks(Creature* creature) { if (!_path || _path->Nodes.empty()) return; - ASSERT(_currentNode < _path->Nodes.size(), "WaypointMovementGenerator::OnArrived: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->Id); - WaypointNode const &waypoint = _path->Nodes.at(_currentNode); - if (waypoint.Delay) - { + WaypointNode const& waypoint = _path->Nodes.at(_currentNode); + if (waypoint.Delay > 0) creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - _nextMoveTime.Reset(waypoint.Delay); + + bool const useTransportPath = !creature->GetTransGUID().IsEmpty(); + + // Check if the waypoint path has reached its end and may not repeat. Inform AI and update home position. + if ((_currentNode == _path->Nodes.size() - 1) && !_repeating && !_done) + { + WaypointNode const& waypoint = _path->Nodes.at(_currentNode); + float x = waypoint.X; + float y = waypoint.Y; + float z = waypoint.Z; + float o = creature->GetOrientation(); + + if (!useTransportPath) + creature->SetHomePosition(x, y, z, o); + else + { + if (Transport* trans = creature->GetTransport()) + { + o -= trans->GetOrientation(); + creature->SetTransportHomePosition(x, y, z, o); + trans->CalculatePassengerPosition(x, y, z, &o); + creature->SetHomePosition(x, y, z, o); + } + } + + _done = true; + creature->UpdateCurrentWaypointInfo(0, 0); + + // Inform the AI that the path has ended. + if (creature->IsAIEnabled) + creature->AI()->WaypointPathEnded(_path->Nodes.at(_currentNode).Id, _path->Id); } if (waypoint.EventId && urand(0, 99) < waypoint.EventChance) @@ -109,88 +140,72 @@ void WaypointMovementGenerator::OnArrived(Creature* creature) } creature->UpdateCurrentWaypointInfo(waypoint.Id, _path->Id); + + // All hooks called and infos updated. Time to increment the waypoint node id + _currentNode = (_currentNode + 1) % _path->Nodes.size(); + + _waypointReached = true; } -void WaypointMovementGenerator::StartMove(Creature* creature, bool relaunch/* = false*/) +void WaypointMovementGenerator::StartMove(Creature* creature, bool relaunch /*= false*/) { - // sanity checks - if (!creature || !creature->IsAlive() || _done || !_path || _path->Nodes.empty() || (relaunch && _isArrivalDone)) - return; - - if (!relaunch) // on relaunch, can avoid this since its only called on valid movement + // Formation checks. Do not launch a new spline when one of our formation members is currently in combat. + if (!relaunch) { - if (!CanMove(creature) || (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed())) // if cannot move OR cannot move because of formation + if (!IsAllowedToMove(creature) || (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed())) { - _nextMoveTime.Reset(1000); // delay 1s + _nextMoveTimer.Reset(1000); return; } } - bool transportPath = !creature->GetTransGUID().IsEmpty(); - - if (_isArrivalDone) - { - ASSERT(_currentNode < _path->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->Id); - WaypointNode const &waypoint = _path->Nodes.at(_currentNode); - - if ((_currentNode == _path->Nodes.size() - 1) && !_repeating) // If that's our last waypoint - { - float x = waypoint.X; - float y = waypoint.Y; - float z = waypoint.Z; - float o = creature->GetOrientation(); - - if (!transportPath) - creature->SetHomePosition(x, y, z, o); - else - { - if (Transport* trans = creature->GetTransport()) - { - 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 - } - _done = true; - creature->UpdateCurrentWaypointInfo(0, 0); - - // inform AI - if (creature->IsAIEnabled) - creature->AI()->WaypointPathEnded(waypoint.Id, _path->Id); - return; - } - - _currentNode = (_currentNode + 1) % _path->Nodes.size(); - - // inform AI - if (creature->IsAIEnabled) - creature->AI()->WaypointStarted(waypoint.Id, _path->Id); - } - - ASSERT(_currentNode < _path->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->Id); - WaypointNode const &waypoint = _path->Nodes[_currentNode]; - - _isArrivalDone = false; - _recalculateSpeed = false; - + // Step two: node selection is done, build spline data creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); + WaypointNode const& waypoint = _path->Nodes.at(_currentNode); + bool const useTransportPath = !creature->GetTransGUID().IsEmpty(); Movement::MoveSplineInit init(creature); - - //! If creature is on transport, we assume waypoints set in DB are already transport offsets - if (transportPath) + //! If the creature is on transport, we assume waypoints set in DB are already transport offsets + if (useTransportPath) init.DisableTransportPathTransformations(); - //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call - //! but formationDest contains global coordinates - init.MoveTo(waypoint.X, waypoint.Y, waypoint.Z); + // Determining if we have pre-set spline points in database. Use database data when present, otherwise do own calculations (custom and legacy support) + if (!waypoint.SplinePoints.empty()) + { + // We have spline points in waypoint_data_addon table so instead of calculating a path, we append the db side spline points and run that one instead. + int32 splineIndex = 0; + + // We are re-launching our spline. We want to pick up all spline points that have not been passed yet. + if (!creature->movespline->Finalized() && relaunch && _lastSplineId == creature->movespline->GetId()) + splineIndex = creature->movespline->currentPathIdx(); + + std::vector::const_iterator 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())); + + // Spline points are appended, now add our starting vertex and destination to the path and we're good to go. + init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition())); + init.Path().insert(init.Path().end(), PositionToVector3({ waypoint.X, waypoint.Y, waypoint.Z })); + } + else + { + // We have no db spline points, so we are going to fall back to default waypoint behaivior. + if (!creature->movespline->Finalized() && _lastSplineId == creature->movespline->GetId()) + { + // We are still running our previous waypoint spline. Use it's final destination as starting point for our next path. + init.MoveTo(creature->movespline->FinalDestination(), PositionToVector3({ waypoint.X, waypoint.Y, waypoint.Z })); + if (!init.Path().empty()) + init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition())); + } + else + init.MoveTo(waypoint.X, waypoint.Y, waypoint.Z); + } //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table - if (waypoint.Orientation && waypoint.Delay) + if (waypoint.Orientation && waypoint.Delay > 0) init.SetFacing(waypoint.Orientation); switch (waypoint.MoveType) @@ -214,79 +229,93 @@ void WaypointMovementGenerator::StartMove(Creature* creature, bool rel if (waypoint.Velocity > 0.f) init.SetVelocity(waypoint.Velocity); - init.Launch(); + // All set and ready, launch spline and initialize timers. + int32 moveTime = init.Launch(); + int32 estimatedArrivalTime = std::max(0, moveTime + waypoint.Delay); + _movementInformTimer.Reset(std::min(moveTime, estimatedArrivalTime)); + _nextMoveTimer.Reset(estimatedArrivalTime); - // inform formation + if (!creature->movespline->Finalized()) + _lastSplineId = creature->movespline->GetId(); + + // Inform formation creature->SignalFormationMovement(); - return; + // Inform AI + if (creature->IsAIEnabled && !relaunch) + creature->AI()->WaypointStarted(waypoint.Id, _path->Id); + + _waypointReached = false; + _recalculateSpeed = false; + _hasBeenStalled = false; } bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 diff) { - if (!creature || !creature->IsAlive()) + // Creature has died. Do not update anymore. + if (!creature->IsAlive()) return true; + // The creature has completed its waypoint path or the path is no longer available. if (_done || !_path || _path->Nodes.empty()) return true; - if (_stalled || creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + // Stop the creature's movement when it has been stalled or when it's currently unable to move + if (!IsAllowedToMove(creature)) { + _hasBeenStalled = !_waypointReached; creature->StopMoving(); return true; } - // if it's moving - if (!creature->movespline->Finalized()) + // Update movement inform timer. Inform AI that we have reached a waypoint or that the path has ended. + if (!_hasBeenStalled && !_waypointReached) { - // set home position at place (every MotionMaster::UpdateMotion) + _movementInformTimer.Update(diff); + if (_movementInformTimer.Passed()) + HandleMovementInformationHooks(creature); + } + + // Reached the end of the path or the path has been paused in one of the AI hooks. Do not launch movements anymore. + if (_done || _stalled) + return true; + + // Update next movement timer. Launch new spline when timer has passed or when we have to recalculate the speed of the spline. + _nextMoveTimer.Update(diff); + if (_nextMoveTimer.Passed() || (!creature->movespline->Finalized() && _recalculateSpeed)) + StartMove(creature, (_hasBeenStalled || _recalculateSpeed)); + + // Set home position to current position. + if (!creature->movespline->Finalized()) if (creature->GetTransGUID().IsEmpty()) creature->SetHomePosition(creature->GetPosition()); - // relaunch movement if its speed has changed - if (_recalculateSpeed) - StartMove(creature, true); - } - else - { - // check if there is a wait time for the next movement - if (!_nextMoveTime.Passed()) - { - // dont update wait timer while moving - _nextMoveTime.Update(diff); - if (_nextMoveTime.Passed()) - { - _nextMoveTime.Reset(0); - StartMove(creature); // check path status, get next point and move if necessary & can - } - } - else // if it's not moving and there is no timer, assume node is reached - { - OnArrived(creature); // hooks and wait timer reset (if necessary) - _isArrivalDone = true; // signals that the next move will happen after reaching a node - - if (_nextMoveTime.Passed()) - StartMove(creature); // check path status, get next point and move if necessary & can - } - } - return true; } -void WaypointMovementGenerator::MovementInform(Creature* creature) +void WaypointMovementGenerator::Pause(uint32 timer/* = 0*/) { - if (creature->AI()) - creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode); + _stalled = timer ? false : true; + _hasBeenStalled = !_waypointReached; + _nextMoveTimer.Reset(timer ? timer : 1); } -bool WaypointMovementGenerator::GetResetPos(Creature*, float& x, float& y, float& z) +void WaypointMovementGenerator::Resume(uint32 overrideTimer/* = 0*/) +{ + _hasBeenStalled = !_waypointReached; + _stalled = false; + if (overrideTimer) + _nextMoveTimer.Reset(overrideTimer); +} + +bool WaypointMovementGenerator::GetResetPosition(Unit* /*owner*/, float& x, float& y, float& z) { // prevent a crash at empty waypoint path. if (!_path || _path->Nodes.empty()) return false; ASSERT(_currentNode < _path->Nodes.size(), "WaypointMovementGenerator::GetResetPos: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->Id); - WaypointNode const &waypoint = _path->Nodes.at(_currentNode); + WaypointNode const& waypoint = _path->Nodes.at(_currentNode); x = waypoint.X; y = waypoint.Y; @@ -294,20 +323,7 @@ bool WaypointMovementGenerator::GetResetPos(Creature*, float& x, float return true; } -void WaypointMovementGenerator::Pause(uint32 timer/* = 0*/) +bool WaypointMovementGenerator::IsAllowedToMove(Creature* creature) { - _stalled = timer ? false : true; - _nextMoveTime.Reset(timer ? timer : 1); -} - -void WaypointMovementGenerator::Resume(uint32 overrideTimer/* = 0*/) -{ - _stalled = false; - if (overrideTimer) - _nextMoveTime.Reset(overrideTimer); -} - -/*static*/ bool WaypointMovementGenerator::CanMove(Creature* creature) -{ - return !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting(); + return (!_stalled && !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting()); } diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index 07c434e69f5..d57f2906b3e 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -54,23 +54,24 @@ class WaypointMovementGenerator : public MovementGeneratorMediumGetGUID().GetCounter()); } -bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest) +bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest /*= false*/) { - float x, y, z; - _source->GetPosition(x, y, z); + return CalculatePath(PositionToVector3(_source->GetPosition()), G3D::Vector3(destX, destY, destZ), forceDest); +} - if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z)) +bool PathGenerator::CalculatePath(G3D::Vector3 const& startPoint, G3D::Vector3 const& endPoint, bool forceDest /*= false*/) +{ + if (!Trinity::IsValidMapCoord(startPoint.x, startPoint.y, startPoint.z) || !Trinity::IsValidMapCoord(endPoint.x, endPoint.y, endPoint.z)) return false; TC_METRIC_EVENT("mmap_events", "CalculatePath", ""); - G3D::Vector3 dest(destX, destY, destZ); - SetEndPosition(dest); - - G3D::Vector3 start(x, y, z); - SetStartPosition(start); + SetEndPosition(endPoint); + SetStartPosition(startPoint); _forceDestination = forceDest; @@ -79,7 +79,7 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?) const Unit* _sourceUnit = _source->ToUnit(); if (!_navMesh || !_navMeshQuery || (_sourceUnit && _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING)) || - !HaveTile(start) || !HaveTile(dest)) + !HaveTile(startPoint) || !HaveTile(endPoint)) { BuildShortcut(); _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); @@ -88,7 +88,7 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo UpdateFilter(); - BuildPolyPath(start, dest); + BuildPolyPath(startPoint, endPoint); return true; } diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h index d66165cef5a..826a25205a6 100644 --- a/src/server/game/Movement/PathGenerator.h +++ b/src/server/game/Movement/PathGenerator.h @@ -62,6 +62,8 @@ class TC_GAME_API PathGenerator // Calculate the path from owner to given destination // return: true if new path was calculated, false otherwise (no change needed) bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false); + // Calculates the path from start point to given destination + bool CalculatePath(G3D::Vector3 const& startPoint, G3D::Vector3 const& endPoint, bool forceDest = false); bool IsInvalidDestinationZ(Unit const* target) const; // option setters - use optional diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp index 361f33de75a..fad1880d8e0 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.cpp +++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp @@ -20,6 +20,7 @@ #include "MoveSpline.h" #include "MovementPacketBuilder.h" #include "Creature.h" +#include "G3DPosition.hpp" #include "Unit.h" #include "PathGenerator.h" #include "Transport.h" @@ -242,17 +243,12 @@ namespace Movement std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport)); } - void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination) - { - MoveTo(G3D::Vector3(x, y, z), generatePath, forceDestination); - } - - void MoveSplineInit::MoveTo(Vector3 const& dest, bool generatePath, bool forceDestination) + void MoveSplineInit::MoveTo(Vector3 const& start, Vector3 const& dest, bool generatePath, bool forceDestination) { if (generatePath) { PathGenerator path(unit); - bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination); + bool result = path.CalculatePath(start, dest, forceDestination); if (result && !(path.GetPathType() & PATHFIND_NOPATH)) { MovebyPath(path.GetPath()); @@ -266,6 +262,16 @@ namespace Movement args.path[1] = transform(dest); } + void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination) + { + MoveTo(PositionToVector3(unit->GetPosition()), G3D::Vector3(x, y, z), generatePath, forceDestination); + } + + void MoveSplineInit::MoveTo(Vector3 const& dest, bool generatePath, bool forceDestination) + { + MoveTo(PositionToVector3(unit->GetPosition()), dest, generatePath, forceDestination); + } + void MoveSplineInit::SetFall() { args.flags.EnableFalling(); diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h index 10e257ec378..62af6aff069 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.h +++ b/src/server/game/Movement/Spline/MoveSplineInit.h @@ -87,6 +87,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(Vector3 const& destination, bool generatePath = true, bool forceDestination = false); void MoveTo(float x, float y, float z, bool generatePath = true, bool forceDestination = false); diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h index 0488cbfc4cb..b455d4cfbf7 100644 --- a/src/server/game/Movement/Waypoints/WaypointDefines.h +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -19,6 +19,7 @@ #define TRINITY_WAYPOINTDEFINES_H #include "Define.h" +#include "G3D/Vector3.h" #include enum WaypointMoveType @@ -45,10 +46,11 @@ struct WaypointNode uint32 Id; float X, Y, Z, Orientation; float Velocity; - uint32 Delay; + int32 Delay; uint32 EventId; uint32 MoveType; uint8 EventChance; + std::vector SplinePoints; }; struct WaypointPath diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index 21c3ee462f0..5ff4f2c8156 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -64,7 +64,7 @@ void WaypointMgr::Load() continue; } - waypoint.Delay = fields[8].GetUInt32(); + waypoint.Delay = fields[8].GetInt32(); waypoint.EventId = fields[9].GetUInt32(); waypoint.EventChance = fields[10].GetInt16(); @@ -78,6 +78,63 @@ void WaypointMgr::Load() TC_LOG_INFO("server.loading", ">> Loaded %u waypoints in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +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) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 waypoints. DB table `waypoint_data_addon` is empty!"); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 pathId = fields[0].GetUInt32(); + + std::unordered_map::iterator it = _waypointStore.find(pathId); + if (it == _waypointStore.end()) + { + TC_LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PathID %u but there is no such path in waypoint_data. Ignoring.", pathId); + continue; + } + + WaypointPath& path = it->second; + uint32 pointId = fields[1].GetUInt32(); + + + std::vector::iterator itr = std::find_if(path.Nodes.begin(), path.Nodes.end(), [pointId](WaypointNode const& node) + { + return node.Id == pointId; + }); + + if (itr == path.Nodes.end()) + { + TC_LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PointID %u of PathID %u but there is no such point in waypoint_data. Ignoring.", pointId, pathId); + continue; + } + + float x = fields[3].GetFloat(); + float y = fields[4].GetFloat(); + float z = fields[5].GetFloat(); + + Trinity::NormalizeMapCoord(x); + Trinity::NormalizeMapCoord(y); + + itr->SplinePoints.push_back(G3D::Vector3(x, y, z)); + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u waypoint addon data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + WaypointMgr* WaypointMgr::instance() { static WaypointMgr instance; diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h index 19e5caae826..a7356a6ed0c 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.h +++ b/src/server/game/Movement/Waypoints/WaypointManager.h @@ -31,9 +31,12 @@ class TC_GAME_API WaypointMgr // Attempts to reload a single path from database void ReloadPath(uint32 id); - // Loads all paths from database, should only run on startup + // Loads all base waypoint data from database. Should only be called 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; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 950d90329c2..ebf45927108 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2108,6 +2108,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Waypoints..."); sWaypointMgr->Load(); + TC_LOG_INFO("server.loading", "Loading Waypoint Addons..."); + sWaypointMgr->LoadWaypointAddons(); + TC_LOG_INFO("server.loading", "Loading SmartAI Waypoints..."); sSmartWaypointMgr->LoadFromDB();