mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
fix(Core/Travel): Audit fixes and probe correctness in MoveFarTo
This commit is contained in:
parent
3710c35a41
commit
753248510a
@ -9,23 +9,6 @@
|
||||
|
||||
LastMovement::LastMovement() { clear(); }
|
||||
|
||||
LastMovement::LastMovement(LastMovement& other)
|
||||
: taxiNodes(other.taxiNodes),
|
||||
taxiMaster(other.taxiMaster),
|
||||
lastFollow(other.lastFollow),
|
||||
lastAreaTrigger(other.lastAreaTrigger),
|
||||
lastMoveToX(other.lastMoveToX),
|
||||
lastMoveToY(other.lastMoveToY),
|
||||
lastMoveToZ(other.lastMoveToZ),
|
||||
lastMoveToOri(other.lastMoveToOri),
|
||||
lastFlee(other.lastFlee)
|
||||
{
|
||||
lastMoveShort = other.lastMoveShort;
|
||||
nextTeleport = other.nextTeleport;
|
||||
lastPath = other.lastPath;
|
||||
priority = other.priority;
|
||||
}
|
||||
|
||||
void LastMovement::clear()
|
||||
{
|
||||
lastMoveShort = WorldPosition();
|
||||
|
||||
@ -27,20 +27,11 @@ class LastMovement
|
||||
{
|
||||
public:
|
||||
LastMovement();
|
||||
LastMovement(LastMovement& other);
|
||||
|
||||
LastMovement& operator=(LastMovement const& other)
|
||||
{
|
||||
taxiNodes = other.taxiNodes;
|
||||
taxiMaster = other.taxiMaster;
|
||||
lastFollow = other.lastFollow;
|
||||
lastAreaTrigger = other.lastAreaTrigger;
|
||||
lastMoveShort = other.lastMoveShort;
|
||||
lastPath = other.lastPath;
|
||||
nextTeleport = other.nextTeleport;
|
||||
priority = other.priority;
|
||||
return *this;
|
||||
};
|
||||
// Non-copyable: holds a std::future and several state fields the
|
||||
// historical copy-helpers omitted. Always pass by reference.
|
||||
LastMovement(LastMovement const&) = delete;
|
||||
LastMovement& operator=(LastMovement const&) = delete;
|
||||
|
||||
void clear();
|
||||
|
||||
@ -75,7 +66,7 @@ public:
|
||||
LastMovementValue(PlayerbotAI* botAI) : ManualSetValue<LastMovement&>(botAI, data) {}
|
||||
|
||||
private:
|
||||
LastMovement data = LastMovement();
|
||||
LastMovement data{};
|
||||
};
|
||||
|
||||
class StayTimeValue : public ManualSetValue<time_t>
|
||||
|
||||
@ -137,24 +137,40 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
|
||||
bool const needsLongPath = (bot->GetMapId() != dest.GetMapId()) ||
|
||||
(dis > sPlayerbotAIConfig.sightDistance);
|
||||
|
||||
// Skip travel-node planning inside battlegrounds — the graph is
|
||||
// built for the open world and yields nonsense routes inside BGs.
|
||||
// Mirrors cmangos MovementActions.cpp:705.
|
||||
if (needsLongPath && sPlayerbotAIConfig.enableTravelNodes &&
|
||||
!bot->InBattleground())
|
||||
// needsLongPath branch — graph routing only. cmangos's getFullPath
|
||||
// already attempts a 40-step mmap probe internally before falling
|
||||
// back to A* node routing, so we don't need a second probe here.
|
||||
// If the graph yields no plan, fall straight to the single-point
|
||||
// fallback (cmangos addPoint(endPosition)). BG gate per
|
||||
// MovementActions.cpp:705.
|
||||
if (needsLongPath)
|
||||
{
|
||||
StartTravelPlan(dest);
|
||||
if (botAI->rpgInfo.HasActiveTravelPlan())
|
||||
if (sPlayerbotAIConfig.enableTravelNodes && !bot->InBattleground())
|
||||
{
|
||||
LOG_INFO("playerbots", "[MoveFar] {} nodetravel | dis={:.0f}",
|
||||
bot->GetName(), dis);
|
||||
EmitDebugMove("MoveFar", "travelplan",
|
||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
|
||||
return UpdateTravelPlan();
|
||||
StartTravelPlan(dest);
|
||||
if (botAI->rpgInfo.HasActiveTravelPlan())
|
||||
{
|
||||
LOG_INFO("playerbots", "[MoveFar] {} nodetravel | dis={:.0f}",
|
||||
bot->GetName(), dis);
|
||||
EmitDebugMove("MoveFar", "travelplan",
|
||||
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
|
||||
return UpdateTravelPlan();
|
||||
}
|
||||
}
|
||||
|
||||
// Long-path fallback. Cross-map can't be served by a same-map
|
||||
// straight-line spline — bail rather than splining toward
|
||||
// unreachable coords.
|
||||
if (bot->GetMapId() != dest.GetMapId())
|
||||
return false;
|
||||
|
||||
Movement::PointsArray fallback;
|
||||
fallback.emplace_back(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
||||
fallback.emplace_back(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ());
|
||||
return DispatchPathPoints(dest, fallback, "spline-long");
|
||||
}
|
||||
|
||||
// 40-step chained mmap probe.
|
||||
// Short-path branch — 40-step chained mmap probe.
|
||||
WorldPosition botPos(bot);
|
||||
std::vector<WorldPosition> probe = botPos.getPathTo(dest, bot);
|
||||
|
||||
@ -245,14 +261,12 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
||||
lm.setPath(TravelPath(wpts));
|
||||
}
|
||||
|
||||
// Item 5 — underwater fixup. Push waypoints submerged below the
|
||||
// water surface up to the surface itself, unless the destination
|
||||
// is itself underwater (e.g. fishing-loot quest GO, sunken ruins).
|
||||
// Push submerged waypoints to the water surface unless dest is itself underwater.
|
||||
if (Map* map = bot->GetMap())
|
||||
{
|
||||
WorldPosition destWp = dest;
|
||||
if (!destWp.isUnderWater())
|
||||
{
|
||||
Map* map = bot->GetMap();
|
||||
for (auto& pt : points)
|
||||
{
|
||||
WorldPosition wp(dest.GetMapId(), pt.x, pt.y, pt.z);
|
||||
@ -307,11 +321,21 @@ bool NewRpgBaseAction::DispatchPathPoints(WorldPosition const& dest,
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clipAt > 0 && clipAt + 1 < points.size())
|
||||
// Truncate after the first hostile waypoint. cmangos's
|
||||
// analog at TravelNode.cpp:1158 sets endP=path.begin() if
|
||||
// i=0 (mob right at start) and erases everything after,
|
||||
// collapsing to a 1-point path. Our caller then sees
|
||||
// points.size() < 2 and bails out — the bot just stops.
|
||||
if (clipAt < points.size() && clipAt + 1 < points.size())
|
||||
points.erase(points.begin() + clipAt + 1, points.end());
|
||||
}
|
||||
}
|
||||
|
||||
// After clip, points may have collapsed to one element (mob right
|
||||
// at start). Bail rather than dispatching a degenerate spline.
|
||||
if (points.size() < 2)
|
||||
return false;
|
||||
|
||||
G3D::Vector3 const& last = points.back();
|
||||
|
||||
float totalDist = 0.f;
|
||||
|
||||
@ -50,7 +50,7 @@ void NewRpgInfo::ChangeToTravelFlight(uint32 flightMasterEntry, WorldPosition fl
|
||||
|
||||
void NewRpgInfo::ChangeToOutdoorPvp(ObjectGuid::LowType capturePointSpawnId)
|
||||
{
|
||||
startT = getMSTime();
|
||||
Reset();
|
||||
OutdoorPvP pvp;
|
||||
pvp.capturePointSpawnId = capturePointSpawnId;
|
||||
data = pvp;
|
||||
|
||||
@ -873,14 +873,20 @@ std::vector<WorldPosition> WorldPosition::getPathFromPath(std::vector<WorldPosit
|
||||
PathGenerator path(pathUnit);
|
||||
path.AddExcludeFlag(NAV_GROUND_STEEP);
|
||||
|
||||
// Area-cost biases. Mirrors cmangos WorldPosition.cpp:1098-1100.
|
||||
// NAV_WATER weighted 10x so A* prefers shore routes over wading
|
||||
// through lakes when both are reachable.
|
||||
path.SetAreaCost(NAV_WATER, 10.0f);
|
||||
|
||||
// Limit the pathfinding attempts
|
||||
for (uint32 i = 0; i < maxAttempt; i++)
|
||||
{
|
||||
// Drop cached poly state from the previous step. AC's
|
||||
// BuildPolyPath has a subpath-prefix optimization
|
||||
// (PathGenerator.cpp:285-389) that keeps ~80% of the prior
|
||||
// call's poly chain when the new startPoly is anywhere in
|
||||
// it, then computes a suffix from there. For a chained
|
||||
// walker this implicitly snaps the start to the previous
|
||||
// corridor instead of using the explicit startPos we pass
|
||||
// in, bending the route. Reset wipes the cache so each step
|
||||
// is a fresh A*.
|
||||
path.Clear();
|
||||
|
||||
// Try to pathfind to this position.
|
||||
subPath = getPathStepFrom(currentPos, path);
|
||||
|
||||
|
||||
@ -1297,7 +1297,13 @@ bool TravelNodeMap::GetFullPath(TravelPlan& plan,
|
||||
if (botPos.GetMapId() == destination.GetMapId())
|
||||
{
|
||||
std::vector<WorldPosition> probe = destination.getPathFromPath({botPos}, bot, 40);
|
||||
if (destination.isPathTo(probe, sPlayerbotAIConfig.spellDistance))
|
||||
// Guard the degenerate case: getPathFromPath always returns at
|
||||
// least {botPos}, and isPathTo(probe, spellDistance) succeeds
|
||||
// when the bot is already within spellDistance of dest.
|
||||
// Without this guard we'd emit a 1-element plan [botPos as
|
||||
// PREPATH] and ExecuteTravelPlan would silently no-op every
|
||||
// tick.
|
||||
if (probe.size() >= 2 && destination.isPathTo(probe, sPlayerbotAIConfig.spellDistance))
|
||||
{
|
||||
plan.steps.addPoint(botPos, PathNodeType::NODE_PREPATH);
|
||||
for (size_t i = 1; i < probe.size(); ++i)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user