mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Movement: Merge waypoints without delay into a single movement packet
This commit is contained in:
@@ -24,8 +24,10 @@
|
||||
#include "MoveSpline.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MovementDefines.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Transport.h"
|
||||
#include "WaypointManager.h"
|
||||
#include <span>
|
||||
|
||||
WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating, Optional<Milliseconds> duration, Optional<float> speed,
|
||||
MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd,
|
||||
@@ -61,6 +63,8 @@ WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath cons
|
||||
ScriptResult = std::move(scriptResult);
|
||||
if (duration)
|
||||
_duration.emplace(*duration);
|
||||
|
||||
std::get<std::unique_ptr<WaypointPath>>(_path)->BuildSegments();
|
||||
}
|
||||
|
||||
WaypointMovementGenerator<Creature>::~WaypointMovementGenerator() = default;
|
||||
@@ -215,6 +219,16 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff)
|
||||
if (owner->GetTransGUID().IsEmpty())
|
||||
owner->SetHomePosition(owner->GetPosition());
|
||||
|
||||
// handle switching points in continuous segments
|
||||
if (_waypointTransitionSplinePoints.size() > 1 && owner->movespline->currentPathIdx() >= _waypointTransitionSplinePoints.front())
|
||||
{
|
||||
OnArrived(owner);
|
||||
_waypointTransitionSplinePoints.erase(_waypointTransitionSplinePoints.begin());
|
||||
if (ComputeNextNode())
|
||||
if (CreatureAI* ai = owner->AI())
|
||||
ai->WaypointStarted(path->Nodes[_currentNode].Id, path->Id);
|
||||
}
|
||||
|
||||
// relaunch movement if its speed has changed
|
||||
if (HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING))
|
||||
StartMove(owner, true);
|
||||
@@ -336,6 +350,7 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun
|
||||
|
||||
bool const transportPath = !owner->GetTransGUID().IsEmpty();
|
||||
|
||||
uint32 previousNode = _currentNode;
|
||||
if (HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) && HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED))
|
||||
{
|
||||
if (ComputeNextNode())
|
||||
@@ -388,7 +403,69 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
std::span<WaypointNode const> segment = [&]
|
||||
{
|
||||
// find the continuous segment that our destination waypoint is on
|
||||
auto segmentItr = std::ranges::find_if(path->ContinuousSegments, [&](std::pair<std::size_t, std::size_t> segmentRange)
|
||||
{
|
||||
auto isInSegmentRange = [&](uint32 node) { return node >= segmentRange.first && node < segmentRange.first + segmentRange.second; };
|
||||
return isInSegmentRange(_currentNode) && isInSegmentRange(previousNode);
|
||||
});
|
||||
|
||||
// handle path returning directly from last point to first
|
||||
if (segmentItr == path->ContinuousSegments.end())
|
||||
{
|
||||
if (_currentNode != 0 || previousNode != path->Nodes.size() - 1)
|
||||
return std::span(&path->Nodes[_currentNode], 1);
|
||||
|
||||
segmentItr = path->ContinuousSegments.begin();
|
||||
}
|
||||
|
||||
if (!_isReturningToStart)
|
||||
return std::span(&path->Nodes[_currentNode], segmentItr->second - (_currentNode - segmentItr->first));
|
||||
|
||||
return std::span(&path->Nodes[segmentItr->first], _currentNode - segmentItr->first + 1);
|
||||
}();
|
||||
|
||||
WaypointNode const& lastWaypointForSegment = !_isReturningToStart ? segment.back() : segment.front();
|
||||
|
||||
_waypointTransitionSplinePoints.clear();
|
||||
Movement::PointsArray points;
|
||||
auto fillPath = [this, owner, &points]<typename iterator>(iterator itr, iterator end)
|
||||
{
|
||||
Optional<PathGenerator> generator;
|
||||
if (_generatePath)
|
||||
generator.emplace(owner);
|
||||
|
||||
Position source = owner->GetPosition();
|
||||
points.emplace_back(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ());
|
||||
|
||||
while (itr != end)
|
||||
{
|
||||
if (generator)
|
||||
{
|
||||
bool result = generator->CalculatePath(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ(), itr->X, itr->Y, itr->Z);
|
||||
if (result && !(generator->GetPathType() & PATHFIND_NOPATH))
|
||||
points.insert(points.end(), generator->GetPath().begin() + 1, generator->GetPath().end());
|
||||
else
|
||||
generator.reset(); // when path generation to a waypoint fails, add all remaining points without pathfinding (preserve legacy behavior of MoveSplineInit::MoveTo)
|
||||
}
|
||||
|
||||
if (!generator)
|
||||
points.emplace_back(itr->X, itr->Y, itr->Z);
|
||||
|
||||
_waypointTransitionSplinePoints.push_back(points.size() - 1);
|
||||
|
||||
source.Relocate(itr->X, itr->Y, itr->Z);
|
||||
++itr;
|
||||
}
|
||||
};
|
||||
|
||||
if (!_isReturningToStart)
|
||||
fillPath(segment.begin(), segment.end());
|
||||
else
|
||||
fillPath(segment.rbegin(), segment.rend());
|
||||
|
||||
RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_INFORM_ENABLED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED);
|
||||
|
||||
@@ -396,22 +473,28 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
|
||||
// because steering flag can cause position on client to not be perfectly accurate, dont do it in combat
|
||||
if (!owner->IsInCombat())
|
||||
init.SetSteering();
|
||||
|
||||
//! If creature is on transport, we assume waypoints set in DB are already transport offsets
|
||||
if (transportPath)
|
||||
init.DisableTransportPathTransformations();
|
||||
|
||||
init.MoveTo(waypoint.X, waypoint.Y, waypoint.Z, _generatePath);
|
||||
init.MovebyPath(points);
|
||||
|
||||
if (waypoint.Orientation.has_value() && (waypoint.Delay || _currentNode == path->Nodes.size() - 1))
|
||||
init.SetFacing(*waypoint.Orientation);
|
||||
if (lastWaypointForSegment.Orientation.has_value() && (lastWaypointForSegment.Delay || _currentNode == path->Nodes.size() - 1))
|
||||
init.SetFacing(*lastWaypointForSegment.Orientation);
|
||||
|
||||
switch (path->MoveType)
|
||||
{
|
||||
case WaypointMoveType::Land:
|
||||
init.SetAnimation(AnimTier::Ground);
|
||||
init.SetFly();
|
||||
break;
|
||||
case WaypointMoveType::TakeOff:
|
||||
init.SetAnimation(AnimTier::Fly);
|
||||
init.SetFly();
|
||||
break;
|
||||
case WaypointMoveType::Run:
|
||||
init.SetWalk(false);
|
||||
@@ -443,6 +526,25 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun
|
||||
if (_speed)
|
||||
init.SetVelocity(*_speed);
|
||||
|
||||
if (init.Path().size() > 2)
|
||||
{
|
||||
G3D::Vector3 mid = (init.Path().front() + init.Path().back()) / 2.0f;
|
||||
auto itr = init.Path().begin() + 1;
|
||||
auto end = itr + (init.Path().size() - 2);
|
||||
while (itr != end)
|
||||
{
|
||||
G3D::Vector3 offset = *itr - mid;
|
||||
if (std::fabs(offset.x) >= 128.0f || std::fabs(offset.y) >= 128.0f || std::fabs(offset.z) >= 64.0f)
|
||||
{
|
||||
// when distance is too great, send path in uncompressed state otherwise too much precision is lost on each point
|
||||
init.SetUncompressed();
|
||||
break;
|
||||
}
|
||||
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
init.Launch();
|
||||
|
||||
// inform formation
|
||||
|
||||
@@ -84,6 +84,7 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat
|
||||
|
||||
bool IsLoadedFromDB() const { return std::holds_alternative<WaypointPath const*>(_path); }
|
||||
|
||||
std::vector<int32> _waypointTransitionSplinePoints;
|
||||
TimeTracker _nextMoveTime;
|
||||
uint32 _pathId;
|
||||
bool _repeating;
|
||||
|
||||
@@ -79,10 +79,13 @@ struct WaypointPath
|
||||
}
|
||||
|
||||
std::vector<WaypointNode> Nodes;
|
||||
std::vector<std::pair<std::size_t, std::size_t>> ContinuousSegments;
|
||||
uint32 Id = 0;
|
||||
WaypointMoveType MoveType = WaypointMoveType::Walk;
|
||||
EnumFlag<WaypointPathFlags> Flags = WaypointPathFlags::None;
|
||||
Optional<float> Velocity;
|
||||
|
||||
void BuildSegments();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -139,9 +139,10 @@ void WaypointMgr::LoadPathNodesFromDB(Field* fields)
|
||||
|
||||
void WaypointMgr::DoPostLoadingChecks()
|
||||
{
|
||||
for (auto const& path : _pathStore)
|
||||
for (auto& [pathId, pathInfo] : _pathStore)
|
||||
{
|
||||
WaypointPath pathInfo = path.second;
|
||||
pathInfo.BuildSegments();
|
||||
|
||||
if (pathInfo.Nodes.empty())
|
||||
TC_LOG_ERROR("sql.sql", "PathId {} in `waypoint_path` has no assigned nodes in `waypoint_path_node`", pathInfo.Id);
|
||||
|
||||
@@ -194,6 +195,9 @@ void WaypointMgr::ReloadPath(uint32 pathId)
|
||||
{
|
||||
LoadPathNodesFromDB(result->Fetch());
|
||||
} while (result->NextRow());
|
||||
|
||||
if (WaypointPath* path = Trinity::Containers::MapGetValuePtr(_pathStore, pathId))
|
||||
path->BuildSegments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,3 +334,16 @@ ObjectGuid const& WaypointMgr::GetVisualGUIDByNode(uint32 pathId, uint32 nodeId)
|
||||
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
void WaypointPath::BuildSegments()
|
||||
{
|
||||
ContinuousSegments.assign(1, { 0, 0 });
|
||||
for (std::size_t i = 0; i < Nodes.size(); ++i)
|
||||
{
|
||||
++ContinuousSegments.back().second;
|
||||
|
||||
// split on delay
|
||||
if (i + 1 != Nodes.size() && Nodes[i].Delay)
|
||||
ContinuousSegments.emplace_back(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user