mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
refactor(Core/Movement): Drop dead ExecuteTravelPlan + LaunchWalkSpline + MoveToSpline + GetTravelPlan + RefineWalkPoints
This commit is contained in:
parent
b1dc9c787a
commit
7d507a6922
@ -3047,202 +3047,6 @@ bool MoveAwayFromPlayerWithDebuffAction::isPossible()
|
|||||||
return bot->CanFreeMove();
|
return bot->CanFreeMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovementAction::LaunchWalkSpline(TravelPlan& state)
|
|
||||||
{
|
|
||||||
if (state.walkPoints.size() < 2)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Trim past any stored points the bot has already moved past — useful
|
|
||||||
// when a spline is interrupted (combat, knockback, mid-spline reissue)
|
|
||||||
// and we re-launch from a position later in the route.
|
|
||||||
G3D::Vector3 botPos(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
|
|
||||||
float closestDist = FLT_MAX;
|
|
||||||
size_t closestIdx = 0;
|
|
||||||
for (size_t i = 0; i < state.walkPoints.size(); ++i)
|
|
||||||
{
|
|
||||||
float distance = (state.walkPoints[i] - botPos).squaredLength();
|
|
||||||
if (distance < closestDist)
|
|
||||||
{
|
|
||||||
closestDist = distance;
|
|
||||||
closestIdx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (closestIdx > 0)
|
|
||||||
state.walkPoints.erase(state.walkPoints.begin(), state.walkPoints.begin() + closestIdx);
|
|
||||||
|
|
||||||
if (state.walkPoints.size() < 2)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sparse-segment clip (cmangos parity): truncate the chain at the
|
|
||||||
// first segment longer than ~11.18y. Spline interpolation between
|
|
||||||
// sparse waypoints can cut corners through visual obstacles (trees,
|
|
||||||
// walls) the navmesh routed around. Bot re-plans from a closer
|
|
||||||
// position next tick where the resolved poly chain is denser.
|
|
||||||
{
|
|
||||||
constexpr float SPARSE_SEG_SQ = 125.0f; // sqrt(125) ≈ 11.18y
|
|
||||||
for (size_t i = 1; i < state.walkPoints.size(); ++i)
|
|
||||||
{
|
|
||||||
G3D::Vector3 d = state.walkPoints[i] - state.walkPoints[i - 1];
|
|
||||||
if (d.squaredLength() > SPARSE_SEG_SQ)
|
|
||||||
{
|
|
||||||
state.walkPoints.resize(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state.walkPoints.size() < 2)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-clamp cached waypoints to current valid Z. Rows in
|
|
||||||
// playerbots_travelnode_path store absolute coords baked at
|
|
||||||
// offline generation; if the live navmesh has shifted since
|
|
||||||
// (mmap regen, terrain change, vmap update), the stored z can
|
|
||||||
// be above ground — MoveSplinePath plays back coords verbatim
|
|
||||||
// and the bot looks like it's walking through the air.
|
|
||||||
// UpdateAllowedPositionZ factors mmap polygon Z, water surface,
|
|
||||||
// swimming, flying and transport state, so cave floors above
|
|
||||||
// the terrain plane snap correctly.
|
|
||||||
for (auto& pt : state.walkPoints)
|
|
||||||
bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z);
|
|
||||||
|
|
||||||
// Mount up
|
|
||||||
if (!bot->IsMounted() && !bot->IsInCombat() && bot->IsOutdoors() && bot->IsAlive())
|
|
||||||
botAI->DoSpecificAction("check mount state", Event(), true);
|
|
||||||
|
|
||||||
float totalDist = 0;
|
|
||||||
for (size_t i = 1; i < state.walkPoints.size(); ++i)
|
|
||||||
totalDist += (state.walkPoints[i] - state.walkPoints[i - 1]).length();
|
|
||||||
|
|
||||||
float speed = bot->GetSpeed(MOVE_RUN);
|
|
||||||
state.expectedDuration = static_cast<uint32>((totalDist / speed) * IN_MILLISECONDS);
|
|
||||||
|
|
||||||
bot->GetMotionMaster()->MoveSplinePath(&state.walkPoints, FORCED_MOVEMENT_RUN);
|
|
||||||
|
|
||||||
G3D::Vector3 const& last = state.walkPoints.back();
|
|
||||||
|
|
||||||
// Mirror what MoveTo does after dispatching a spline so the
|
|
||||||
// lastPath cache below picks up the in-flight waypoint chain.
|
|
||||||
{
|
|
||||||
float delay = static_cast<float>(state.expectedDuration);
|
|
||||||
delay = std::min(delay, static_cast<float>(sPlayerbotAIConfig.maxWaitForMove));
|
|
||||||
delay = std::max(delay, 0.f);
|
|
||||||
LastMovement& lastMove = AI_VALUE(LastMovement&, "last movement");
|
|
||||||
lastMove.Set(bot->GetMapId(), last.x, last.y, last.z,
|
|
||||||
bot->GetOrientation(), delay, MovementPriority::MOVEMENT_NORMAL);
|
|
||||||
|
|
||||||
// Cache the dispatched waypoint chain so MoveFarTo's 10%
|
|
||||||
// lastPath reuse and "no worse" reuse can pick it up next tick.
|
|
||||||
std::vector<WorldPosition> wpts;
|
|
||||||
wpts.reserve(state.walkPoints.size());
|
|
||||||
for (auto const& pt : state.walkPoints)
|
|
||||||
wpts.emplace_back(bot->GetMapId(), pt.x, pt.y, pt.z);
|
|
||||||
lastMove.setPath(TravelPath(wpts));
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitDebugMove("TravelPlan:walk-start", "mmap", last.x, last.y, last.z);
|
|
||||||
|
|
||||||
return false; // Walking
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MovementAction::RefineWalkPoints(std::vector<G3D::Vector3>& walkPoints)
|
|
||||||
{
|
|
||||||
if (walkPoints.size() < 2)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
std::vector<G3D::Vector3> refined;
|
|
||||||
refined.reserve(walkPoints.size() * 4);
|
|
||||||
|
|
||||||
uint32 const mapId = bot->GetMapId();
|
|
||||||
|
|
||||||
for (size_t i = 0; i + 1 < walkPoints.size(); ++i)
|
|
||||||
{
|
|
||||||
G3D::Vector3 const& a = walkPoints[i];
|
|
||||||
G3D::Vector3 const& b = walkPoints[i + 1];
|
|
||||||
|
|
||||||
WorldPosition aPos(mapId, a.x, a.y, a.z);
|
|
||||||
WorldPosition bPos(mapId, b.x, b.y, b.z);
|
|
||||||
|
|
||||||
// Per-segment mmap query: routes around geometry the offline
|
|
||||||
// graph didn't account for, or returns empty if unreachable.
|
|
||||||
std::vector<WorldPosition> segPath = bPos.getPathStepFrom(aPos, bot);
|
|
||||||
|
|
||||||
// Trust the raw waypoint pair when mmap can't validate it —
|
|
||||||
// navmesh gaps/tile-edge artifacts shouldn't kill an active plan.
|
|
||||||
bool const trustRaw = segPath.empty() ||
|
|
||||||
TravelPath::IsPathCheating(segPath, aPos.distance(bPos));
|
|
||||||
|
|
||||||
if (trustRaw)
|
|
||||||
{
|
|
||||||
if (i == 0)
|
|
||||||
refined.emplace_back(a);
|
|
||||||
refined.emplace_back(b);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include the first segment's start; skip subsequent starts
|
|
||||||
// to avoid duplicating the prior segment's tail.
|
|
||||||
size_t startK = (i == 0) ? 0 : 1;
|
|
||||||
for (size_t k = startK; k < segPath.size(); ++k)
|
|
||||||
refined.emplace_back(segPath[k].GetPositionX(),
|
|
||||||
segPath[k].GetPositionY(),
|
|
||||||
segPath[k].GetPositionZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
walkPoints = std::move(refined);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MovementAction::MoveToSpline(TravelPlan& state, WorldPosition target)
|
|
||||||
{
|
|
||||||
if (!IsMovingAllowed())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
EmitDebugMove("TravelPlan:walk-waypoint", "mmap", target.GetPositionX(), target.GetPositionY(), target.GetPositionZ());
|
|
||||||
|
|
||||||
// Generate path
|
|
||||||
state.walkPoints.clear();
|
|
||||||
PathResult path = GeneratePath(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ());
|
|
||||||
// Reject paths that PathGenerator marked unreachable. The default
|
|
||||||
// accept mask is NORMAL | INCOMPLETE; anything else (NOT_USING_PATH
|
|
||||||
// from BuildShortcut on invalid polys, NOPATH, etc.) means the
|
|
||||||
// dispatched waypoints would either be a straight-line through
|
|
||||||
// geometry or stop short of the target. Abort the plan instead so
|
|
||||||
// MoveFarTo can re-derive via its own probe.
|
|
||||||
if (!path.reachable)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (auto const& pt : path.points)
|
|
||||||
state.walkPoints.push_back(G3D::Vector3(pt.x, pt.y, pt.z));
|
|
||||||
|
|
||||||
if (state.walkPoints.size() < 2)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch spline movement
|
|
||||||
LaunchWalkSpline(state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MovementAction::GetTravelPlan(TravelPlan& plan, WorldPosition destination)
|
|
||||||
{
|
|
||||||
WorldPosition botPos(bot->GetMapId(), bot->GetPositionX(),
|
|
||||||
bot->GetPositionY(), bot->GetPositionZ());
|
|
||||||
return sTravelNodeMap.GetFullPath(plan, botPos, bot->GetZoneId(), destination, bot);
|
|
||||||
}
|
|
||||||
|
|
||||||
TravelPath MovementAction::ResolveMovePath(WorldPosition const& startPos,
|
TravelPath MovementAction::ResolveMovePath(WorldPosition const& startPos,
|
||||||
WorldPosition const& endPos,
|
WorldPosition const& endPos,
|
||||||
@ -3453,341 +3257,6 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovementAction::ExecuteTravelPlan(TravelPlan& state)
|
|
||||||
{
|
|
||||||
if (!state.IsActive())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (bot->IsInFlight())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Per-step labels (`walk`, `segment`, `flight`, `transport-*`,
|
|
||||||
// `teleport(reason)`) cover every actual movement decision; emitting
|
|
||||||
// an executor-ran-this-tick label here would whisper every tick
|
|
||||||
// while the plan is active.
|
|
||||||
|
|
||||||
if (state.stepIdx >= state.steps.size())
|
|
||||||
{
|
|
||||||
state.Reset();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PathNodePoint& pt = state.steps[state.stepIdx];
|
|
||||||
|
|
||||||
switch (pt.type)
|
|
||||||
{
|
|
||||||
case PathNodeType::NODE_PREPATH:
|
|
||||||
case PathNodeType::NODE_PATH:
|
|
||||||
case PathNodeType::NODE_NODE:
|
|
||||||
{
|
|
||||||
// Batch consecutive walkable points (PREPATH, PATH, NODE) into
|
|
||||||
// one spline. With per-tick re-resolve the plan starts at
|
|
||||||
// stepIdx=0 each tick, so we must dispatch a real spline (not
|
|
||||||
// a single-waypoint MoveTo) — otherwise the executor's
|
|
||||||
// "near closest waypoint" heuristic returns true without
|
|
||||||
// moving and the bot never advances.
|
|
||||||
//
|
|
||||||
// Capped at 20 points per dispatch as a cheap upper bound on
|
|
||||||
// per-tick work. The engine plays the spline; next tick
|
|
||||||
// re-resolves from the bot's new position and dispatches the
|
|
||||||
// next batch.
|
|
||||||
static constexpr uint32 MAX_SPLINE_POINTS = 20;
|
|
||||||
state.walkPoints.clear();
|
|
||||||
while (state.stepIdx < state.steps.size() && state.walkPoints.size() < MAX_SPLINE_POINTS)
|
|
||||||
{
|
|
||||||
const PathNodePoint& wp = state.steps[state.stepIdx];
|
|
||||||
if (wp.type != PathNodeType::NODE_PREPATH &&
|
|
||||||
wp.type != PathNodeType::NODE_PATH &&
|
|
||||||
wp.type != PathNodeType::NODE_NODE)
|
|
||||||
break; // stop at portal/transport/etc — can't walk past
|
|
||||||
state.walkPoints.push_back(G3D::Vector3(wp.point.GetPositionX(),
|
|
||||||
wp.point.GetPositionY(), wp.point.GetPositionZ()));
|
|
||||||
state.stepIdx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.walkPoints.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Already near end of batch?
|
|
||||||
G3D::Vector3 const& last = state.walkPoints.back();
|
|
||||||
float dist = bot->GetExactDist(last.x, last.y, last.z);
|
|
||||||
if (dist < 10.0f)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Too far from first point — abort the plan and let the
|
|
||||||
// caller's stuck-recovery decide what to do. An abandoned
|
|
||||||
// plan is recovered by the next MoveFarTo cycle.
|
|
||||||
if (state.walkPoints.size() >= 2)
|
|
||||||
{
|
|
||||||
G3D::Vector3 const& first = state.walkPoints.front();
|
|
||||||
float distToFirst = bot->GetExactDist(first.x, first.y, first.z);
|
|
||||||
if (distToFirst > MAX_PATHFINDING_DISTANCE)
|
|
||||||
{
|
|
||||||
state.walkPoints.clear();
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Single point — use PathGenerator directly
|
|
||||||
if (state.walkPoints.size() < 2)
|
|
||||||
{
|
|
||||||
WorldPosition target(bot->GetMapId(), last.x, last.y, last.z);
|
|
||||||
MoveToSpline(state, target);
|
|
||||||
state.walkPoints.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-validate each segment against the live navmesh and
|
|
||||||
// substitute mmap-routed sub-paths where needed.
|
|
||||||
if (!RefineWalkPoints(state.walkPoints))
|
|
||||||
{
|
|
||||||
G3D::Vector3 const& failPt = state.walkPoints.empty()
|
|
||||||
? G3D::Vector3(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())
|
|
||||||
: state.walkPoints.front();
|
|
||||||
EmitDebugMove("TravelPlan", "segment-unwalkable",
|
|
||||||
failPt.x, failPt.y, failPt.z);
|
|
||||||
state.walkPoints.clear();
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchWalkSpline(state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PathNodeType::NODE_AREA_TRIGGER:
|
|
||||||
{
|
|
||||||
// Pair: trigger (pointIdx) + dest (pointIdx+1).
|
|
||||||
// Bot walks into the area trigger volume; server teleports
|
|
||||||
// on entry. Bot may need quest/key prereqs to actually cross.
|
|
||||||
if (state.stepIdx + 1 >= state.steps.size())
|
|
||||||
{
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PathNodePoint& trigger = state.steps[state.stepIdx];
|
|
||||||
const PathNodePoint& dst = state.steps[state.stepIdx + 1];
|
|
||||||
|
|
||||||
// Already on destination map — trigger fired, advance.
|
|
||||||
if (bot->GetMapId() == dst.point.GetMapId())
|
|
||||||
{
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk to the trigger position; collision with the trigger
|
|
||||||
// volume teleports us.
|
|
||||||
float dist = bot->GetExactDist(trigger.point.GetPositionX(),
|
|
||||||
trigger.point.GetPositionY(),
|
|
||||||
trigger.point.GetPositionZ());
|
|
||||||
if (dist > INTERACTION_DISTANCE)
|
|
||||||
return MoveTo(trigger.point.GetMapId(),
|
|
||||||
trigger.point.GetPositionX(),
|
|
||||||
trigger.point.GetPositionY(),
|
|
||||||
trigger.point.GetPositionZ());
|
|
||||||
|
|
||||||
// At trigger but didn't teleport — likely missing quest/key.
|
|
||||||
// Abort; the do-quest yield-to-grind multiplier or next
|
|
||||||
// POI pick can reroute.
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PathNodeType::NODE_STATIC_PORTAL:
|
|
||||||
{
|
|
||||||
// Pair: portal-GO position (pointIdx) + dest (pointIdx+1).
|
|
||||||
// Bot walks within interact range of the portal GameObject
|
|
||||||
// and sends CMSG_GAMEOBJ_USE to trigger its teleport spell.
|
|
||||||
if (state.stepIdx + 1 >= state.steps.size())
|
|
||||||
{
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PathNodePoint& portal = state.steps[state.stepIdx];
|
|
||||||
const PathNodePoint& dst = state.steps[state.stepIdx + 1];
|
|
||||||
|
|
||||||
if (bot->GetMapId() == dst.point.GetMapId())
|
|
||||||
{
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk to portal GO position
|
|
||||||
float dist = bot->GetExactDist(portal.point.GetPositionX(),
|
|
||||||
portal.point.GetPositionY(),
|
|
||||||
portal.point.GetPositionZ());
|
|
||||||
if (dist > INTERACTION_DISTANCE)
|
|
||||||
return MoveTo(portal.point.GetMapId(),
|
|
||||||
portal.point.GetPositionX(),
|
|
||||||
portal.point.GetPositionY(),
|
|
||||||
portal.point.GetPositionZ());
|
|
||||||
|
|
||||||
// In range — find the portal GameObject and interact
|
|
||||||
if (!portal.entry)
|
|
||||||
{
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->IsMounted())
|
|
||||||
bot->Dismount();
|
|
||||||
botAI->RemoveShapeshift();
|
|
||||||
|
|
||||||
GuidVector nearGOs = AI_VALUE(GuidVector, "nearest game objects");
|
|
||||||
for (ObjectGuid const& guid : nearGOs)
|
|
||||||
{
|
|
||||||
GameObject* go = botAI->GetGameObject(guid);
|
|
||||||
if (!go || go->GetEntry() != portal.entry)
|
|
||||||
continue;
|
|
||||||
if (!bot->GetGameObjectIfCanInteractWith(guid, GAMEOBJECT_TYPE_SPELLCASTER))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
WorldPacket packet(CMSG_GAMEOBJ_USE);
|
|
||||||
packet << guid;
|
|
||||||
bot->GetSession()->QueuePacket(new WorldPacket(packet));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GO not found nearby — abort and let next tick try again
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PathNodeType::NODE_TRANSPORT:
|
|
||||||
{
|
|
||||||
if (state.stepIdx + 1 >= state.steps.size())
|
|
||||||
{
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PathNodePoint& board = state.steps[state.stepIdx];
|
|
||||||
const PathNodePoint& arrive = state.steps[state.stepIdx + 1];
|
|
||||||
// Arrived at destination?
|
|
||||||
if (bot->GetMapId() == arrive.point.GetMapId() && !bot->GetTransport())
|
|
||||||
{
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// On transport — wait
|
|
||||||
if (bot->GetTransport())
|
|
||||||
{
|
|
||||||
if (bot->GetMapId() == arrive.point.GetMapId())
|
|
||||||
{
|
|
||||||
bot->GetTransport()->RemovePassenger(bot);
|
|
||||||
bot->StopMovingOnCurrentPos();
|
|
||||||
state.stepIdx += 2;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk to boarding point
|
|
||||||
float dist = bot->GetExactDist(board.point.GetPositionX(), board.point.GetPositionY(), board.point.GetPositionZ());
|
|
||||||
if (dist > 60.0f)
|
|
||||||
return MoveTo(board.point.GetMapId(), board.point.GetPositionX(), board.point.GetPositionY(), board.point.GetPositionZ());
|
|
||||||
|
|
||||||
// Try to board
|
|
||||||
if (board.entry)
|
|
||||||
{
|
|
||||||
Map* map = bot->GetMap();
|
|
||||||
if (map)
|
|
||||||
{
|
|
||||||
Transport* transport =
|
|
||||||
GetTransportForPosTolerant(map, bot, bot->GetPhaseMask(), board.point.GetPositionX(),
|
|
||||||
board.point.GetPositionY(), board.point.GetPositionZ());
|
|
||||||
if (transport && transport->GetEntry() == board.entry)
|
|
||||||
{
|
|
||||||
BoardTransport(transport);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wait at boarding point
|
|
||||||
if (dist > INTERACTION_DISTANCE)
|
|
||||||
return MoveTo(board.point.GetMapId(), board.point.GetPositionX(), board.point.GetPositionY(), board.point.GetPositionZ());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PathNodeType::NODE_FLIGHTPATH:
|
|
||||||
{
|
|
||||||
if (state.stepIdx + 1 >= state.steps.size())
|
|
||||||
{
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PathNodePoint& dep = state.steps[state.stepIdx];
|
|
||||||
const PathNodePoint& arr = state.steps[state.stepIdx + 1];
|
|
||||||
|
|
||||||
if (bot->IsInFlight())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Resolve taxi path
|
|
||||||
if (state.route.empty())
|
|
||||||
{
|
|
||||||
uint32 fromTaxi = sObjectMgr->GetNearestTaxiNode(dep.point.GetPositionX(), dep.point.GetPositionY(),
|
|
||||||
dep.point.GetPositionZ(), dep.point.GetMapId(), bot->GetTeamId());
|
|
||||||
uint32 toTaxi = sObjectMgr->GetNearestTaxiNode(arr.point.GetPositionX(), arr.point.GetPositionY(),
|
|
||||||
arr.point.GetPositionZ(), arr.point.GetMapId(), bot->GetTeamId());
|
|
||||||
|
|
||||||
if (fromTaxi && toTaxi && fromTaxi != toTaxi)
|
|
||||||
state.route = sTravelNodeMap.FindTaxiPath(fromTaxi, toTaxi);
|
|
||||||
|
|
||||||
if (state.route.empty())
|
|
||||||
{
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TravelMgr::FlightMasterInfo const* fmInfo = sTravelMgr.GetNearestFlightMasterInfo(bot);
|
|
||||||
if (!fmInfo)
|
|
||||||
{
|
|
||||||
state.route.clear();
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->GetDistance(fmInfo->pos) > INTERACTION_DISTANCE)
|
|
||||||
return MoveTo(fmInfo->pos.GetMapId(), fmInfo->pos.GetPositionX(),
|
|
||||||
fmInfo->pos.GetPositionY(), fmInfo->pos.GetPositionZ());
|
|
||||||
|
|
||||||
ObjectGuid fmGuid = ObjectGuid::Create<HighGuid::Unit>(fmInfo->templateEntry, fmInfo->dbGuid);
|
|
||||||
Creature* flightMaster = ObjectAccessor::GetCreature(*bot, fmGuid);
|
|
||||||
if (!flightMaster || !flightMaster->IsAlive())
|
|
||||||
{
|
|
||||||
state.route.clear();
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
botAI->RemoveShapeshift();
|
|
||||||
if (bot->IsMounted())
|
|
||||||
bot->Dismount();
|
|
||||||
|
|
||||||
bot->ActivateTaxiPathTo(state.route, flightMaster, 0);
|
|
||||||
|
|
||||||
state.route.clear();
|
|
||||||
state.stepIdx += 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
LOG_ERROR("playerbots",
|
|
||||||
"[TravelPlan] Bot {} encountered unknown PathNodeType ({}); resetting plan",
|
|
||||||
bot->GetName(), static_cast<uint32>(pt.type));
|
|
||||||
state.Reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Transport* MovementAction::GetTransportForPosTolerant(Map* map, WorldObject* ref, uint32 phaseMask, float x, float y, float z)
|
Transport* MovementAction::GetTransportForPosTolerant(Map* map, WorldObject* ref, uint32 phaseMask, float x, float y, float z)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -89,9 +89,6 @@ protected:
|
|||||||
|
|
||||||
PathResult GeneratePath(float x, float y, float z, uint32 acceptMask = DEFAULT_PATH_ACCEPT_MASK, bool forceDestination = false);
|
PathResult GeneratePath(float x, float y, float z, uint32 acceptMask = DEFAULT_PATH_ACCEPT_MASK, bool forceDestination = false);
|
||||||
|
|
||||||
bool GetTravelPlan(TravelPlan& plan, WorldPosition destination);
|
|
||||||
bool ExecuteTravelPlan(TravelPlan& state);
|
|
||||||
|
|
||||||
// Returns a unified TravelPath for the move. Mirror of the reference
|
// Returns a unified TravelPath for the move. Mirror of the reference
|
||||||
// ResolveMovePath shape: 10% lastPath reuse short-circuit, choose
|
// ResolveMovePath shape: 10% lastPath reuse short-circuit, choose
|
||||||
// graph (cross-map / >sightDistance) or live mmap probe, regression
|
// graph (cross-map / >sightDistance) or live mmap probe, regression
|
||||||
@ -126,16 +123,6 @@ protected:
|
|||||||
float& outX, float& outY, float& outZ);
|
float& outX, float& outY, float& outZ);
|
||||||
bool BoardTransport(Transport* transport);
|
bool BoardTransport(Transport* transport);
|
||||||
|
|
||||||
private:
|
|
||||||
bool LaunchWalkSpline(TravelPlan& state);
|
|
||||||
bool MoveToSpline(TravelPlan& state, WorldPosition target);
|
|
||||||
// Per-segment mmap refinement of a travel-node-graph walk batch.
|
|
||||||
// The graph stores offline-baked coords whose straight-line
|
|
||||||
// interpolation may pass through geometry the bot can't actually
|
|
||||||
// traverse. Returns false if any segment is unwalkable per the
|
|
||||||
// live navmesh, in which case the caller should abort the plan.
|
|
||||||
bool RefineWalkPoints(std::vector<G3D::Vector3>& walkPoints);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct CheckAngle
|
struct CheckAngle
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user