Core/Movement: Implemented cyclic waypoint movement (#29923)

Co-authored-by: Shauren <shauren.trinity@gmail.com>
This commit is contained in:
ModoX
2024-10-07 20:29:02 +02:00
committed by GitHub
parent 86f0337d64
commit 222a80a6d6
4 changed files with 58 additions and 11 deletions

View File

@@ -41,7 +41,7 @@ WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bo
: PathMovementBase(PathType(std::in_place_type<WaypointPath const*>)), _pathId(pathId), _speed(speed), _speedSelectionMode(speedSelectionMode),
_waitTimeRangeAtPathEnd(std::move(waitTimeRangeAtPathEnd)), _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds),
_followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _exactSplinePath(exactSplinePath), _repeating(repeating), _generatePath(generatePath),
_moveTimer(0), _nextMoveTime(0), _isReturningToStart(false)
_moveTimer(0), _nextMoveTime(0), _waypointTransitionSplinePointsIndex(0), _isReturningToStart(false)
{
Mode = MOTION_MODE_DEFAULT;
Priority = MOTION_PRIORITY_NORMAL;
@@ -59,7 +59,7 @@ WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath cons
: PathMovementBase(std::make_unique<WaypointPath>(path)), _pathId(0), _speed(speed), _speedSelectionMode(speedSelectionMode),
_waitTimeRangeAtPathEnd(std::move(waitTimeRangeAtPathEnd)), _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds),
_followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _exactSplinePath(exactSplinePath), _repeating(repeating), _generatePath(generatePath),
_moveTimer(0), _nextMoveTime(0), _isReturningToStart(false)
_moveTimer(0), _nextMoveTime(0), _waypointTransitionSplinePointsIndex(0), _isReturningToStart(false)
{
Mode = MOTION_MODE_DEFAULT;
Priority = MOTION_PRIORITY_NORMAL;
@@ -218,7 +218,7 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff)
}
// if it's moving
if (!UpdateMoveTimer(diff))
if (!UpdateMoveTimer(diff) && !owner->movespline->Finalized())
{
// set home position at place (every MotionMaster::UpdateMotion)
if (owner->GetTransGUID().IsEmpty())
@@ -227,10 +227,11 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff)
// handle switching points in continuous segments
if (IsExactSplinePath())
{
if (_waypointTransitionSplinePoints.size() > 1 && owner->movespline->currentPathIdx() >= _waypointTransitionSplinePoints.front())
if (_waypointTransitionSplinePointsIndex < _waypointTransitionSplinePoints.size()
&& owner->movespline->currentPathIdx() >= _waypointTransitionSplinePoints[_waypointTransitionSplinePointsIndex])
{
OnArrived(owner);
_waypointTransitionSplinePoints.erase(_waypointTransitionSplinePoints.begin());
++_waypointTransitionSplinePointsIndex;
if (ComputeNextNode())
if (CreatureAI* ai = owner->AI())
ai->WaypointStarted(path->Nodes[_currentNode].Id, path->Id);
@@ -363,13 +364,14 @@ void CreateSingularPointPath(Unit const* owner, WaypointPath const* path, uint32
waypointTransitionSplinePoints->push_back(points->size() - 1);
}
void CreateMergedPath(Unit const* owner, WaypointPath const* path, uint32 previousNode, uint32 currentNode, bool isReturningToStart, bool generatePath,
void CreateMergedPath(Unit const* owner, WaypointPath const* path, uint32 previousNode, uint32 currentNode,
bool isReturningToStart, bool generatePath, bool isCyclic,
Movement::PointsArray* points, std::vector<int32>* waypointTransitionSplinePoints, WaypointNode const** lastWaypointOnPath)
{
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 segmentItr = std::ranges::find_if(path->ContinuousSegments, [&](std::pair<std::size_t, std::size_t> const& segmentRange)
{
auto isInSegmentRange = [&](uint32 node) { return node >= segmentRange.first && node < segmentRange.first + segmentRange.second; };
return isInSegmentRange(currentNode) && isInSegmentRange(previousNode);
@@ -423,6 +425,15 @@ void CreateMergedPath(Unit const* owner, WaypointPath const* path, uint32 previo
}
};
if (isCyclic)
{
// create new cyclic path starting at current node
std::vector<WaypointNode> cyclicPath = path->Nodes;
std::rotate(cyclicPath.begin(), cyclicPath.begin() + currentNode, cyclicPath.end());
fillPath(cyclicPath.begin(), cyclicPath.end());
return;
}
if (!isReturningToStart)
fillPath(segment.begin(), segment.end());
else
@@ -503,18 +514,35 @@ 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* lastWaypointForSegment = &path->Nodes[_currentNode];
bool isCyclic = IsCyclic();
Movement::PointsArray points;
if (IsExactSplinePath())
CreateMergedPath(owner, path, previousNode, _currentNode, _isReturningToStart, false,
CreateMergedPath(owner, path, previousNode, _currentNode, _isReturningToStart, false, isCyclic,
&points, &_waypointTransitionSplinePoints, &lastWaypointForSegment);
else
CreateSingularPointPath(owner, path, _currentNode, _generatePath, &points, &_waypointTransitionSplinePoints);
_waypointTransitionSplinePointsIndex = 0;
RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_INFORM_ENABLED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED);
owner->AddUnitState(UNIT_STATE_ROAMING_MOVE);
if (isCyclic)
{
bool isFirstCycle = relaunch || owner->movespline->Finalized() || !owner->movespline->isCyclic();
if (!isFirstCycle)
{
for (int32& point : _waypointTransitionSplinePoints)
--point;
// cyclic paths are using identical duration to first cycle with EnterCycle
_moveTimer.Reset(Milliseconds(owner->movespline->Duration()));
return;
}
}
Movement::MoveSplineInit init(owner);
//! If creature is on transport, we assume waypoints set in DB are already transport offsets
@@ -567,7 +595,10 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun
if (_speed)
init.SetVelocity(*_speed);
if (IsExactSplinePath() && points.size() > 2 && owner->CanFly())
if (isCyclic)
init.SetCyclic();
if (IsExactSplinePath() && (points.size() > 2 && owner->CanFly()))
init.SetSmooth();
Milliseconds duration(init.Launch());
@@ -633,6 +664,14 @@ bool WaypointMovementGenerator<Creature>::IsExactSplinePath() const
return GetPath()->Flags.HasFlag(WaypointPathFlags::ExactSplinePath);
}
bool WaypointMovementGenerator<Creature>::IsCyclic() const
{
return !IsFollowingPathBackwardsFromEndToStart()
&& IsExactSplinePath()
&& _repeating
&& GetPath()->ContinuousSegments.size() == 1;
}
std::string WaypointMovementGenerator<Creature>::GetDebugInfo() const
{
return Trinity::StringFormat("{}\n{}",

View File

@@ -84,6 +84,7 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat
bool IsFollowingPathBackwardsFromEndToStart() const;
bool IsExactSplinePath() const;
bool IsCyclic() const;
bool IsLoadedFromDB() const { return std::holds_alternative<WaypointPath const*>(_path); }
@@ -101,6 +102,7 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat
TimeTracker _moveTimer;
TimeTracker _nextMoveTime;
std::vector<int32> _waypointTransitionSplinePoints;
uint32 _waypointTransitionSplinePointsIndex;
bool _isReturningToStart;
};

View File

@@ -65,7 +65,7 @@ namespace Movement
// flags that shouldn't be appended into SMSG_MONSTER_MOVE\SMSG_MONSTER_MOVE_TRANSPORT packet, should be more probably
Mask_No_Monster_Move = Done, // SKIP
// Unused, not suported flags
Mask_Unused = No_Spline | Enter_Cycle | Frozen | Unknown_0x100 | Unknown_0x20000 | Unknown_0x40000
Mask_Unused = No_Spline | Frozen | Unknown_0x100 | Unknown_0x20000 | Unknown_0x40000
| Unknown_0x800000 | FadeObject | UnlimitedSpeed | Unknown_0x40000000 | Unknown_0x80000000 // SKIP
};

View File

@@ -372,7 +372,13 @@ void WorldPackets::Movement::CommonMovement::WriteCreateObjectSplineDataBlock(::
if (!moveSpline.isCyclic()) // Destination
dest = moveSpline.FinalDestination();
else
dest = G3D::Vector3::zero();
{
::Movement::MoveSpline::MySpline const& spline = moveSpline._Spline();
if (spline.getPointCount() <= 1)
dest = G3D::Vector3::zero();
else
dest = spline.getPoint(spline.last() - 1);
}
data << dest.x << dest.y << dest.z;