refactor(Core/Travel): Simplify transport config to TransportSkipRide bool; drop mode-0 deck-walk approximations

This commit is contained in:
bash 2026-05-31 15:38:45 +02:00
parent fff692e3f3
commit c6e0cc9cef
5 changed files with 59 additions and 166 deletions

View File

@ -1065,14 +1065,15 @@ AiPlayerbot.EnableNewRpgStrategy = 1
# Default: 0 (disabled)
AiPlayerbot.EnableTravelNodes = 0
# Transport handling mode (travel node graph only):
# 0 = bot walks to dock, boards transport, rides, walks off (fully physical)
# 1 = same as 0, but if walk-on/off fails the boarding teleports the bot
# 2 = bot teleports across the route, skipping the ride entirely
# Reference default is 0; raise only if invisible/random-bot mass travel
# performance becomes an issue.
# Transport ride mode (travel node graph only):
# 0 = bot walks to dock, teleport-snaps onto transport, rides, teleport-snaps off
# 1 = bot teleports directly across the transport route, skipping the ride
# Default 0 is the visible behavior. Set to 1 for invisible/random-bot mass
# travel performance — the bot never actually boards anything.
# (AC has no transport-surface mmap, so an on-deck walking mode can't be
# faithfully implemented; the on-board phase always teleport-snaps.)
# Default: 0
AiPlayerbot.TransportTeleportType = 0
AiPlayerbot.TransportSkipRide = 0
# Control probability weights for RPG status of bots. Takes effect only when the status meets its premise.
# Sum of weights need not be 100. Set to 0 to disable the status.

View File

@ -3145,20 +3145,12 @@ bool MovementAction::WaitForTransport()
return false;
// Disembark: head is the transport node where we should get off,
// next is the world-position dock to land at. Reference UseTransport
// with doTeleport=(transportTeleportType > 0) routes to either
// MoveOffTransport-teleport (mode 1+) or MoveOffTransport-walk (mode 0).
PathNodePoint const& dock = path[0];
// next is the world-position dock to land at.
if (path.size() < 2)
return true; // no telePoint to land at; keep waiting
PathNodePoint const& tele = path[1];
(void)dock; // kept for reference parity (UseTransport signature)
transport->RemovePassenger(bot);
if (sPlayerbotAIConfig.transportTeleportType > 0)
{
// Mode 1+: teleport directly to exit.
bot->StopMovingOnCurrentPos();
bool const teleported = bot->TeleportTo(tele.point.GetMapId(),
tele.point.GetPositionX(),
@ -3167,25 +3159,6 @@ bool MovementAction::WaitForTransport()
bot->GetOrientation());
if (!teleported)
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;
return false;
@ -3274,10 +3247,7 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
case PathNodeType::NODE_TRANSPORT:
{
// Disembark: head is a transport node and bot is on one.
// Reference dispatch: MoveOffTransport(exit, doTeleport) where
// doTeleport = (transportTeleportType > 0). Mode 1+ teleports
// straight to exit; mode 0 NearTeleports off transport plane
// then walks to exit.
// RemovePassenger + TeleportTo the next-step world position.
if (!hasNext)
return false;
@ -3287,9 +3257,6 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
PathNodePoint const& dst = path[1];
transport->RemovePassenger(bot);
if (sPlayerbotAIConfig.transportTeleportType > 0)
{
bot->StopMovingOnCurrentPos();
bool const teleported = bot->TeleportTo(dst.point.GetMapId(),
dst.point.GetPositionX(),
@ -3300,24 +3267,6 @@ bool MovementAction::HandleSpecialMovement(TravelPath& path)
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:
break;
}
@ -3527,87 +3476,29 @@ bool MovementAction::BoardTransport(Transport* transport)
return true;
}
// Teleport-onto mode (transportTeleportType >= 1): skip the walk-toward
// phase and jump directly to a boarding edge. Matches reference's
// MoveOnTransport(doTeleport=true) — PlayerRelocation + AddPassenger.
if (sPlayerbotAIConfig.transportTeleportType >= 1)
{
// Bot off transport: find a boarding edge and teleport-snap directly
// onto it, then AddPassenger. We can't walk on the deck (no transport-
// surface mmap), so the snap-board is the only universal approach.
float edgeX, edgeY, edgeZ;
if (FindBoardingPointOnTransport(map, transport, transport,
if (!FindBoardingPointOnTransport(map, transport, transport,
transport->GetPositionX(), transport->GetPositionY(),
transport->GetPositionZ(),
bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
edgeX, edgeY, edgeZ))
{
if (bot->TeleportTo(map->GetId(), edgeX, edgeY, edgeZ, bot->GetOrientation()))
{
// No boarding edge found — wait a tick. Caller's WaitForReach
// upstream throttles the retry.
EmitDebugMove("Transport:board", "no-edge",
transport->GetPositionX(), transport->GetPositionY(),
transport->GetPositionZ());
return false;
}
if (!bot->TeleportTo(map->GetId(), edgeX, edgeY, edgeZ, bot->GetOrientation()))
return false;
transport->AddPassenger(bot, true);
bot->StopMovingOnCurrentPos();
EmitDebugMove("Transport:board", "teleport-skip-walk",
edgeX, edgeY, edgeZ);
EmitDebugMove("Transport:board", "snap", edgeX, edgeY, edgeZ);
return true;
}
}
// Fall through to mode-0 if no boarding point — better than refusing.
}
// Mode 0: reference MoveOnTransport(doTeleport=false) — AddPassenger
// FIRST (snaps bot onto transport plane), then walk a path on deck.
// We don't have RandomPointOnTrans / transport-surface mmap, so when
// 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();
float edgeX, edgeY, edgeZ;
bool haveEdge = FindBoardingPointOnTransport(map, transport, transport,
transport->GetPositionX(), transport->GetPositionY(),
transport->GetPositionZ(), bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), edgeX, edgeY, edgeZ);
if (haveEdge)
{
boardX = edgeX;
boardY = edgeY;
boardZ = edgeZ;
}
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 (!bot->IsStandState())
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;
}
// Too far to snap — walk toward boarding edge.
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;
}

View File

@ -927,9 +927,9 @@ bool TravelPath::UpcommingSpecialMovement(WorldPosition startPos,
return true;
}
// Walk-on / teleport-to-transport boarding mode (modes 0 and 1).
// Cut to dock if off-transport, traverse to disembark if on-transport.
if (sPlayerbotAIConfig.transportTeleportType < 2 &&
// Board-and-ride mode (transportSkipRide == false). Cut to dock if
// off-transport, traverse to disembark if on-transport.
if (!sPlayerbotAIConfig.transportSkipRide &&
startP->type == PathNodeType::NODE_TRANSPORT)
{
uint32 const entry = nextP->entry;
@ -954,11 +954,11 @@ bool TravelPath::UpcommingSpecialMovement(WorldPosition startPos,
}
}
// Teleport-across mode (mode 2): bot is approaching a transport
// node — walk forward to find the first non-transport node (the
// disembark side), cut to prevP (last transport node) so
// Skip-ride mode (transportSkipRide == true): bot is approaching a
// transport node — walk forward to find the first non-transport node
// (the disembark side), cut to prevP (last transport node) so
// HandleSpecialMovement teleports the bot across directly.
if (sPlayerbotAIConfig.transportTeleportType == 2 &&
if (sPlayerbotAIConfig.transportSkipRide &&
nextP->type == PathNodeType::NODE_TRANSPORT)
{
for (auto p = std::next(startP); p != fullPath.end(); ++p)

View File

@ -88,7 +88,7 @@ bool PlayerbotAIConfig::Initialize()
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 100.0f);
transportTeleportType = sConfigMgr->GetOption<uint32>("AiPlayerbot.TransportTeleportType", 0);
transportSkipRide = sConfigMgr->GetOption<bool>("AiPlayerbot.TransportSkipRide", false);
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 28.5f);
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 5.0f);
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 38.5f);

View File

@ -93,11 +93,12 @@ public:
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, expireActionTime,
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
// Transport handling mode (matches reference `transportTeleportType`):
// 0 = walk on board, walk off (default, fully physical)
// 1 = walk on board, but UseTransport teleports on/off if walk fails
// 2 = skip the ride entirely — teleport directly across the route
uint32 transportTeleportType;
// Transport handling:
// false (default) = teleport-board, ride the transport, teleport-disembark
// true = skip the ride entirely (teleport directly across)
// AC has no transport-surface mmap so an in-deck walking mode can't be
// faithfully implemented — the on-board phase always teleport-snaps.
bool transportSkipRide;
bool dynamicReactDelay;
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance,
tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, aoeRadius, rpgDistance,