Core/Movement: implement smooth waypoints

* delays can now be negative to support sniffed transition times from one waypoint path to another
* early transitions will now pick up the ongoing spline destination and start calculating their path from that point on
* waypoints may now use db side spline pathings in a new db table to support special spline shapes are not generated by normal pathfinding (e.g. scripted flight paths)
* current waypoints will still the work the way they did before for legacy and custom support
This commit is contained in:
Ovahlord committed 2021-01-15 12:10:49 +01:00
1 parent 4a158fdf22
commit d5f9b8c26f
11 files changed
+266 -164

No files matched your search

@@ -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;
@@ -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<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(pathId),
_repeating(repeating), _loadedFromDB(true), _stalled(false), _done(false)
WaypointMovementGenerator<Creature>::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<Creature>::WaypointMovementGenerator(WaypointPath& path, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(0),
_repeating(repeating), _loadedFromDB(false), _stalled(false), _done(false)
WaypointMovementGenerator<Creature>::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<Creature>::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<Creature>::DoFinalize(Creature* creature)
void WaypointMovementGenerator<Creature>::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<Creature>::DoReset(Creature* creature)
}
}
void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
void WaypointMovementGenerator<Creature>::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<Creature>::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<Creature>::StartMove(Creature* creature, bool relaunch/* = false*/)
void WaypointMovementGenerator<Creature>::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<G3D::Vector3>::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<Creature>::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<int32>(0, moveTime + waypoint.Delay);
_movementInformTimer.Reset(std::min<int32>(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<Creature>::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<Creature>::MovementInform(Creature* creature)
void WaypointMovementGenerator<Creature>::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<Creature>::GetResetPos(Creature*, float& x, float& y, float& z)
void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer/* = 0*/)
{
_hasBeenStalled = !_waypointReached;
_stalled = false;
if (overrideTimer)
_nextMoveTimer.Reset(overrideTimer);
}
bool WaypointMovementGenerator<Creature>::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<Creature>::GetResetPos(Creature*, float& x, float
return true;
}
void WaypointMovementGenerator<Creature>::Pause(uint32 timer/* = 0*/)
bool WaypointMovementGenerator<Creature>::IsAllowedToMove(Creature* creature)
{
_stalled = timer ? false : true;
_nextMoveTime.Reset(timer ? timer : 1);
}
void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer/* = 0*/)
{
_stalled = false;
if (overrideTimer)
_nextMoveTime.Reset(overrideTimer);
}
/*static*/ bool WaypointMovementGenerator<Creature>::CanMove(Creature* creature)
{
return !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting();
return (!_stalled && !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting());
}
@@ -54,23 +54,24 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat
void UnitSpeedChanged() override { _recalculateSpeed = true; }
void Pause(uint32 timer = 0) override;
void Resume(uint32 overrideTimer = 0) override;
void MovementInform(Creature*);
bool GetResetPos(Creature*, float& x, float& y, float& z);
bool GetResetPosition(Unit*, float& x, float& y, float& z) override;
private:
void OnArrived(Creature*);
void HandleMovementInformationHooks(Creature*);
void StartMove(Creature*, bool relaunch = false);
static bool CanMove(Creature*);
bool IsAllowedToMove(Creature*);
TimeTrackerSmall _nextMoveTime;
bool _recalculateSpeed;
bool _isArrivalDone;
TimeTrackerSmall _nextMoveTimer;
TimeTrackerSmall _movementInformTimer;
uint32 _lastSplineId;
uint32 _pathId;
bool _waypointReached;
bool _recalculateSpeed;
bool _repeating;
bool _loadedFromDB;
bool _stalled;
bool _hasBeenStalled;
bool _done;
};
+11 -11
View File
@@ -19,6 +19,7 @@
#include "PathGenerator.h"
#include "Map.h"
#include "Creature.h"
#include "G3DPosition.hpp"
#include "MMapFactory.h"
#include "MMapManager.h"
#include "Log.h"
@@ -55,21 +56,20 @@ PathGenerator::~PathGenerator()
TC_LOG_DEBUG("maps.mmaps", "++ PathGenerator::~PathGenerator() for %u", _source->GetGUID().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;
}
+2
View File
@@ -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
@@ -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();
@@ -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);
@@ -19,6 +19,7 @@
#define TRINITY_WAYPOINTDEFINES_H
#include "Define.h"
#include "G3D/Vector3.h"
#include <vector>
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<G3D::Vector3> SplinePoints;
};
struct WaypointPath
@@ -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<uint32, WaypointPath>::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<WaypointNode>::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;
@@ -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;
+3
View File
@@ -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();