feat(Core/Movement): Mode-0 transport board/disembark — snap-and-deck-walk on board, NearTeleport+walk on disembark

This commit is contained in:
bash 2026-05-31 15:29:36 +02:00
parent 343ab7ac30
commit f90f365e94

View File

@ -3145,15 +3145,20 @@ bool MovementAction::WaitForTransport()
return false; return false;
// Disembark: head is the transport node where we should get off, // Disembark: head is the transport node where we should get off,
// next is the world-position dock to land at. Reference uses // next is the world-position dock to land at. Reference UseTransport
// UseTransport(ai, dock.entry, dock.point, tele.point, type>0) // with doTeleport=(transportTeleportType > 0) routes to either
// — we don't have UseTransport, so inline the equivalent. // MoveOffTransport-teleport (mode 1+) or MoveOffTransport-walk (mode 0).
PathNodePoint const& dock = path[0]; PathNodePoint const& dock = path[0];
if (path.size() < 2) if (path.size() < 2)
return true; // no telePoint to land at; keep waiting return true; // no telePoint to land at; keep waiting
PathNodePoint const& tele = path[1]; PathNodePoint const& tele = path[1];
(void)dock; // kept for reference parity (UseTransport signature)
transport->RemovePassenger(bot); transport->RemovePassenger(bot);
if (sPlayerbotAIConfig.transportTeleportType > 0)
{
// Mode 1+: teleport directly to exit.
bot->StopMovingOnCurrentPos(); bot->StopMovingOnCurrentPos();
bool const teleported = bot->TeleportTo(tele.point.GetMapId(), bool const teleported = bot->TeleportTo(tele.point.GetMapId(),
tele.point.GetPositionX(), tele.point.GetPositionX(),
@ -3162,11 +3167,27 @@ bool MovementAction::WaitForTransport()
bot->GetOrientation()); bot->GetOrientation());
if (!teleported) if (!teleported)
return true; // try again next tick return true; // try again next tick
lastMove.lastTransportEntry = 0;
return false;
}
// Mode 0: NearTeleportTo current world position (drops bot off
// transport plane to world coords without unloading client), then
// walk to the exit position. Matches reference MoveOffTransport
// with doTeleport=false.
bot->NearTeleportTo(bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), bot->GetOrientation());
if (MotionMaster* mm = bot->GetMotionMaster())
{
mm->Clear();
mm->MovePoint(0, tele.point.GetPositionX(),
tele.point.GetPositionY(),
tele.point.GetPositionZ(),
FORCED_MOVEMENT_RUN, 0.0f, 0.0f, true, false);
}
lastMove.lastTransportEntry = 0; lastMove.lastTransportEntry = 0;
// Suppress unused-variable on `dock` — kept for parity with reference's
// UseTransport(entry, dockPoint, telePoint, ...) signature shape.
(void)dock;
return false; return false;
} }
@ -3253,9 +3274,10 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
case PathNodeType::NODE_TRANSPORT: case PathNodeType::NODE_TRANSPORT:
{ {
// Disembark: head is a transport node and bot is on one. // Disembark: head is a transport node and bot is on one.
// Remove passenger + teleport to the next-step world position // Reference dispatch: MoveOffTransport(exit, doTeleport) where
// (cmangos uses UseTransport with current→next teleport here; // doTeleport = (transportTeleportType > 0). Mode 1+ teleports
// our equivalent is RemovePassenger + TeleportTo). // straight to exit; mode 0 NearTeleports off transport plane
// then walks to exit.
if (!hasNext) if (!hasNext)
return false; return false;
@ -3265,6 +3287,9 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
PathNodePoint const& dst = path[1]; PathNodePoint const& dst = path[1];
transport->RemovePassenger(bot); transport->RemovePassenger(bot);
if (sPlayerbotAIConfig.transportTeleportType > 0)
{
bot->StopMovingOnCurrentPos(); bot->StopMovingOnCurrentPos();
bool const teleported = bot->TeleportTo(dst.point.GetMapId(), bool const teleported = bot->TeleportTo(dst.point.GetMapId(),
dst.point.GetPositionX(), dst.point.GetPositionX(),
@ -3275,6 +3300,24 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
return teleported; return teleported;
} }
// Mode 0: NearTeleportTo current world pos (drops bot off
// transport plane without unloading the client), then walk
// to the exit.
bot->NearTeleportTo(bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), bot->GetOrientation());
if (MotionMaster* mm = bot->GetMotionMaster())
{
mm->Clear();
mm->MovePoint(0, dst.point.GetPositionX(),
dst.point.GetPositionY(),
dst.point.GetPositionZ(),
FORCED_MOVEMENT_RUN, 0.0f, 0.0f, true, false);
}
AI_VALUE(LastMovement&, "last movement").lastTransportEntry = 0;
return true;
}
default: default:
break; break;
} }
@ -3479,15 +3522,14 @@ bool MovementAction::BoardTransport(Transport* transport)
{ {
transport->AddPassenger(bot, true); transport->AddPassenger(bot, true);
bot->StopMovingOnCurrentPos(); bot->StopMovingOnCurrentPos();
EmitDebugMove("Transport:board", "teleport", transport->GetPositionX(), EmitDebugMove("Transport:board", "on-surface", transport->GetPositionX(),
transport->GetPositionY(), transport->GetPositionZ()); transport->GetPositionY(), transport->GetPositionZ());
return true; return true;
} }
// Teleport-onto mode (transportTeleportType >= 1): skip the walk-toward // Teleport-onto mode (transportTeleportType >= 1): skip the walk-toward
// phase and jump directly to a boarding edge. Matches reference's // phase and jump directly to a boarding edge. Matches reference's
// `UseTransport(...telep=true)` shortcut for invisible / random bots // MoveOnTransport(doTeleport=true) — PlayerRelocation + AddPassenger.
// and any caller that doesn't want the visible boarding sequence.
if (sPlayerbotAIConfig.transportTeleportType >= 1) if (sPlayerbotAIConfig.transportTeleportType >= 1)
{ {
float edgeX, edgeY, edgeZ; float edgeX, edgeY, edgeZ;
@ -3506,33 +3548,65 @@ bool MovementAction::BoardTransport(Transport* transport)
return true; return true;
} }
} }
// Fall through to walk-toward if no boarding point or teleport // Fall through to mode-0 if no boarding point — better than refusing.
// failed — better than refusing the board outright.
} }
// Walk-toward mode (transportTeleportType == 0, or mode>=1 fallback). // Mode 0: reference MoveOnTransport(doTeleport=false) — AddPassenger
float destX = transport->GetPositionX(); // FIRST (snaps bot onto transport plane), then walk a path on deck.
float destY = transport->GetPositionY(); // We don't have RandomPointOnTrans / transport-surface mmap, so when
float destZ = transport->GetPositionZ(); // the bot is within INTERACTION_DISTANCE of the transport's boarding
// edge we approximate: AddPassenger + MovePoint toward the transport
// center as a "deck destination." Bot motion is then transport-
// relative because of the AddPassenger.
float boardX = transport->GetPositionX();
float boardY = transport->GetPositionY();
float boardZ = transport->GetPositionZ();
// Try to find nearest boarding edge
float edgeX, edgeY, edgeZ; float edgeX, edgeY, edgeZ;
if (FindBoardingPointOnTransport(map, transport, transport, transport->GetPositionX(), transport->GetPositionY(), bool haveEdge = FindBoardingPointOnTransport(map, transport, transport,
transport->GetPositionZ(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), edgeX, edgeY, edgeZ)) transport->GetPositionX(), transport->GetPositionY(),
transport->GetPositionZ(), bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), edgeX, edgeY, edgeZ);
if (haveEdge)
{ {
destX = edgeX; boardX = edgeX;
destY = edgeY; boardY = edgeY;
destZ = edgeZ; boardZ = edgeZ;
} }
// MovePoint without pathfinding (transport is a moving object) float const dxBoard = bot->GetPositionX() - boardX;
float const dyBoard = bot->GetPositionY() - boardY;
bool const nearBoardingPoint =
(dxBoard * dxBoard + dyBoard * dyBoard) <
INTERACTION_DISTANCE * INTERACTION_DISTANCE;
if (nearBoardingPoint)
{
// Snap onto transport plane, then walk a short deck path so the
// bot is visibly on the boat (mirrors reference mode-0 deck walk).
transport->AddPassenger(bot, true);
if (MotionMaster* mm = bot->GetMotionMaster()) if (MotionMaster* mm = bot->GetMotionMaster())
{ {
if (!bot->IsStandState()) if (!bot->IsStandState())
bot->SetStandState(UNIT_STAND_STATE_STAND); bot->SetStandState(UNIT_STAND_STATE_STAND);
mm->Clear();
mm->MovePoint(0, transport->GetPositionX(), transport->GetPositionY(),
transport->GetPositionZ(),
FORCED_MOVEMENT_RUN, 0.0f, 0.0f, false, false);
}
EmitDebugMove("Transport:board", "snap-and-deck-walk",
boardX, boardY, boardZ);
return true;
}
mm->MovePoint(0, destX, destY, destZ, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, false, false); // Too far to snap — walk toward boarding edge.
EmitDebugMove("Transport:walk", "spline", destX, destY, destZ); if (MotionMaster* mm = bot->GetMotionMaster())
{
if (!bot->IsStandState())
bot->SetStandState(UNIT_STAND_STATE_STAND);
mm->MovePoint(0, boardX, boardY, boardZ,
FORCED_MOVEMENT_NONE, 0.0f, 0.0f, false, false);
EmitDebugMove("Transport:walk", "approach-boarding", boardX, boardY, boardZ);
} }
return false; return false;