refactor(Core/Travel): Drop TravelPlan struct; GetFullPath returns TravelPath

This commit is contained in:
bash 2026-05-30 22:56:48 +02:00
parent 7d507a6922
commit f7df64e4c2
5 changed files with 20 additions and 76 deletions

View File

@ -3069,11 +3069,7 @@ TravelPath MovementAction::ResolveMovePath(WorldPosition const& startPos,
if (needsLongPath && !sTravelNodeMap.getNodes().empty() && !bot->InBattleground()) if (needsLongPath && !sTravelNodeMap.getNodes().empty() && !bot->InBattleground())
{ {
// Wrap the legacy TravelPlan-populating call; the steps field on out = sTravelNodeMap.GetFullPath(startPos, bot->GetZoneId(), endPos, bot);
// TravelPlan IS a TravelPath, so extract it directly.
TravelPlan tmp;
if (sTravelNodeMap.GetFullPath(tmp, startPos, bot->GetZoneId(), endPos, bot))
out = tmp.steps;
} }
else else
{ {

View File

@ -77,7 +77,6 @@ void NewRpgInfo::Reset()
{ {
data = Idle{}; data = Idle{};
startT = getMSTime(); startT = getMSTime();
ClearTravel();
} }
NewRpgStatus NewRpgInfo::GetStatus() NewRpgStatus NewRpgInfo::GetStatus()

View File

@ -78,11 +78,6 @@ struct NewRpgInfo
uint32 startT{0}; // start timestamp of the current status uint32 startT{0}; // start timestamp of the current status
// Travel Node System
TravelPlan travelPlan;
bool HasActiveTravelPlan() const { return travelPlan.IsActive(); }
void ClearTravel() { travelPlan.Reset(); }
using RpgData = std::variant< using RpgData = std::variant<
Idle, Idle,
GoGrind, GoGrind,

View File

@ -1512,23 +1512,10 @@ TravelNodeRoute TravelNodeMap::FindRouteNearestNodes(WorldPosition startPos, Wor
return TravelNodeRoute(); return TravelNodeRoute();
} }
bool TravelNodeMap::GetFullPath(TravelPlan& plan, TravelPath TravelNodeMap::GetFullPath(WorldPosition botPos, uint32 botZoneId,
WorldPosition botPos, uint32 botZoneId,
WorldPosition destination, Unit* bot) WorldPosition destination, Unit* bot)
{ {
// Capture previous pathToStart from the about-to-be-reset plan so we TravelPath path;
// can try cropPathTo to reuse it across the per-tick re-resolve.
std::vector<WorldPosition> prevPathToStart;
for (auto const& pt : plan.steps.GetPathRef())
{
if (pt.type == PathNodeType::NODE_PREPATH)
prevPathToStart.push_back(pt.point);
else
break; // PREPATH is always at the head
}
plan.Reset();
plan.destination = destination;
// mmap-probe first: if a 40-step probe makes meaningful progress, // mmap-probe first: if a 40-step probe makes meaningful progress,
// prefer it over the graph. Loosened from "reaches within spellDistance" // prefer it over the graph. Loosened from "reaches within spellDistance"
@ -1556,10 +1543,10 @@ bool TravelNodeMap::GetFullPath(TravelPlan& plan,
if (closeEnough || meaningfulProgress) if (closeEnough || meaningfulProgress)
{ {
plan.steps.addPoint(botPos, PathNodeType::NODE_PREPATH); path.addPoint(botPos, PathNodeType::NODE_PREPATH);
for (size_t i = 1; i < probe.size(); ++i) for (size_t i = 1; i < probe.size(); ++i)
plan.steps.addPoint(probe[i], PathNodeType::NODE_PATH); path.addPoint(probe[i], PathNodeType::NODE_PATH);
return true; return path;
} }
} }
} }
@ -1595,7 +1582,7 @@ bool TravelNodeMap::GetFullPath(TravelPlan& plan,
std::vector<TravelNode*> endCandidates = pickKNearest(destination, destZone); std::vector<TravelNode*> endCandidates = pickKNearest(destination, destZone);
if (startCandidates.empty() || endCandidates.empty()) if (startCandidates.empty() || endCandidates.empty())
return false; return path; // empty
TravelNode* startNode = nullptr; TravelNode* startNode = nullptr;
TravelNode* endNode = nullptr; TravelNode* endNode = nullptr;
@ -1621,24 +1608,14 @@ bool TravelNodeMap::GetFullPath(TravelPlan& plan,
} }
if (route.isEmpty() || !startNode || !endNode) if (route.isEmpty() || !startNode || !endNode)
return false; return path; // empty
WorldPosition startNodePos = *startNode->getPosition(); WorldPosition startNodePos = *startNode->getPosition();
WorldPosition endNodePos = *endNode->getPosition(); WorldPosition endNodePos = *endNode->getPosition();
// pathToStart: mmap-path from bot to the first node. Try cropping // pathToStart: fresh mmap-path from bot to the first node.
// the previous pathToStart first (cmangos parity) — if it still
// reaches the chosen startNode within reactDistance we avoid a full
// re-probe. Falls back to fresh getPathTo if crop fails or invalid.
std::vector<WorldPosition> pathToStart; std::vector<WorldPosition> pathToStart;
if (!prevPathToStart.empty()) if (bot && botPos.GetMapId() == startNodePos.GetMapId())
{
std::vector<WorldPosition> cropped = prevPathToStart;
bool ok = startNodePos.cropPathTo(cropped, sPlayerbotAIConfig.reactDistance);
if (ok && cropped.size() >= 2)
pathToStart = cropped;
}
if (pathToStart.empty() && bot && botPos.GetMapId() == startNodePos.GetMapId())
{ {
std::vector<WorldPosition> probe = botPos.getPathTo(startNodePos, bot); std::vector<WorldPosition> probe = botPos.getPathTo(startNodePos, bot);
if (probe.size() >= 2) if (probe.size() >= 2)
@ -1663,9 +1640,9 @@ bool TravelNodeMap::GetFullPath(TravelPlan& plan,
if (pathToEnd.empty()) if (pathToEnd.empty())
pathToEnd = {destination}; pathToEnd = {destination};
plan.steps = route.BuildPath(pathToStart, pathToEnd, nullptr); path = route.BuildPath(pathToStart, pathToEnd, nullptr);
return !plan.steps.empty(); return path;
} }
bool TravelNodeMap::cropUselessNode(TravelNode* startNode) bool TravelNodeMap::cropUselessNode(TravelNode* startNode)

View File

@ -68,7 +68,9 @@
// //
// GetFullPath finds nearest nodes (zone-indexed), runs A* to get a node route, then // GetFullPath finds nearest nodes (zone-indexed), runs A* to get a node route, then
// BuildPath assembles a flat TravelPath with typed waypoints (walk, portal, transport, flight). // BuildPath assembles a flat TravelPath with typed waypoints (walk, portal, transport, flight).
// ExecuteTravelPlan iterates the path by stepIdx, dispatching on each point's PathNodeType. // MoveFarTo re-resolves a fresh TravelPath each tick; UpcommingSpecialMovement cuts
// to the head segment when special; HandleSpecialMovement dispatches the matching
// action (portal interact, area-trigger marker, transport board, flight taxi).
// Cross-map travel is handled naturally by portal/transport edges in the A* graph. // Cross-map travel is handled naturally by portal/transport edges in the A* graph.
// //
// If setup cannot resolve (no node, no route, no flight), the bot teleports directly to the destination // If setup cannot resolve (no node, no route, no flight), the bot teleports directly to the destination
@ -585,34 +587,6 @@ public:
uint32 currentGold = 0; uint32 currentGold = 0;
}; };
struct TravelPlan
{
WorldPosition destination;
// Flat waypoint path built upfront by GetFullPath:
TravelPath steps;
uint32 stepIdx{0};
// Spline scratch (used by executor):
std::vector<G3D::Vector3> walkPoints;
uint32 expectedDuration{0}; // used to derive the lastMove delay
// Taxi scratch:
std::vector<uint32> route;
bool IsActive() const { return !steps.empty(); }
void Reset()
{
destination = WorldPosition();
steps.clear();
stepIdx = 0;
walkPoints.clear();
expectedDuration = 0;
route.clear();
}
};
// The container of all nodes. // The container of all nodes.
class TravelNodeMap class TravelNodeMap
{ {
@ -742,8 +716,11 @@ public:
// empty static vector for unknown zones. // empty static vector for unknown zones.
std::vector<TravelNode*> const& GetNodesInZone(uint32 zoneId) const; std::vector<TravelNode*> const& GetNodesInZone(uint32 zoneId) const;
bool GetFullPath(TravelPlan& plan, WorldPosition botPos, // Resolve a full TravelPath from botPos to destination. Returns an
uint32 botZoneId, WorldPosition destination, Unit* bot = nullptr); // empty TravelPath if no graph route + mmap stitch is reachable;
// the caller is then expected to fall back to a single-point path.
TravelPath GetFullPath(WorldPosition botPos, uint32 botZoneId,
WorldPosition destination, Unit* bot = nullptr);
// Resolve A* route between two world positions (returns node vector) // Resolve A* route between two world positions (returns node vector)
std::vector<TravelNode*> ResolveRoute(WorldPosition startPos, std::vector<TravelNode*> ResolveRoute(WorldPosition startPos,