Core/Movement: Merge waypoints without delay into a single movement packet

This commit is contained in:
Shauren
2024-04-20 00:22:34 +02:00
parent cf4c3b5467
commit 204f65684c
4 changed files with 129 additions and 6 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
}