pathing: tighten diagonal step-descend and Descend-carry runway

* JumpFeasibility.EvaluateStepDescend now requires BOTH cardinal
  shoulder columns to be passable. Axis-separated collision
  resolution at the corner zeros one velocity component when a
  shoulder is walled, sliding the bot straight down past the intended
  diagonal landing into a multi-block fall-through (observed on the
  251 -> 244 route around (249,136,207)).
* ParkourFeasibility now distinguishes Parkour-carry from
  Descend-carry for flat (yDelta == 0) jumps. A Descend often
  overshoots the takeoff block, killing the sprint runway the
  SprintJumpTemplate needs to reach 5 c2c. Treat Descend-carry like a
  cold start: 3.5 m threshold and 2 aligned back-runway blocks. This
  prevents the planner from picking impossible 5 c2c flat jumps right
  after a Descend (e.g. (248,122,197) -> (248,122,192)).

Verified live on 1.21.11 across all 6 directions between
(251.5,141,210.5), (252.5,138,220.5) and (244.5,122,188.5):
0 replans, 0 segment failures, no mid-path stalls.

Made-with: Cursor
This commit is contained in:
BruceChen 2026-04-25 18:43:18 +00:00
parent 8ec1ccd45d
commit 1c2e6fba2b
2 changed files with 32 additions and 7 deletions

View file

@ -242,7 +242,18 @@ internal static class JumpFeasibility
bool pathViaZ = ctx.CanWalkThrough(x, y, z + dz) &&
ctx.CanWalkThrough(x, y + 1, z + dz);
if (!pathViaX && !pathViaZ)
// A diagonal Step descend forces the player off the corner of the
// current standing block. Vanilla axis-separated collision resolves
// -X and -Z movement independently: when ONE cardinal shoulder is
// blocked by a wall, the matching velocity component is zeroed and
// the bot slides along the open axis only. If the open-axis column
// (e.g. (x, y-1, z+dz) when only pathViaZ is clear) lacks a floor,
// the bot falls straight down past the intended landing block at
// (x+dx, y-2, z+dz) into whatever solid surface lies further below
// — exactly the multi-block fall-through observed on the 251→244
// route around (249,136,207). Require BOTH shoulder columns to be
// passable so the bot can actually clear the corner diagonally.
if (!pathViaX || !pathViaZ)
{
result.SetImpossible();
return;

View file

@ -47,13 +47,27 @@ internal static class ParkourFeasibility
{
double horiz = Math.Sqrt(xOffset * xOffset + zOffset * zOffset);
bool carriedEntry = ctx.PreviousMoveType is MoveType.Parkour or MoveType.Descend;
// Sprint-momentum carry semantics for flat (yDelta == 0) parkour:
// * Parkour-carry: the previous move's airborne sprint is preserved
// cleanly through landing, so a chained 5 c2c flat parkour can
// fire immediately from the takeoff edge.
// * Descend-carry: the previous Descend often overshoots the
// takeoff block by ~0.5 m (the bot lands inside the takeoff
// block but already past the leading edge), eating the runway
// the SprintJumpTemplate needs to spin sprint back up. In
// practice this lets the bot launch with sub-12-tick momentum
// and short-fall the 5 c2c gap by ~0.7 m. Treat Descend-carry
// as a cold start for flat run-up so the planner inserts an
// explicit traverse runway or picks a shorter parkour.
bool parkourCarry = ctx.PreviousMoveType == MoveType.Parkour;
double threshold = yDelta switch
{
> 0 when carriedEntry => 4.5,
> 0 => 2.5,
< 0 when carriedEntry => 5.5,
< 0 => 3.5,
_ when carriedEntry => 5.5,
_ when parkourCarry => 5.5,
_ => 3.5,
};
if (horiz < threshold)
@ -67,14 +81,14 @@ internal static class ParkourFeasibility
// Long flat sprint parkour (5 c2c, horiz~5) requires the player to be
// launched at full vanilla sprint velocity (~12 momentum ticks). A
// standing-jump cold start only reaches gap=3 (=4 c2c). When the
// previous move is not a momentum-carrying Parkour/Descend, one back
// block of runway is not enough to spin sprint up; demand at least
// two aligned back blocks so the executor has a real run-up window.
// standing-jump cold start only reaches gap=3 (=4 c2c). Without a
// clean Parkour-carry, one back block of runway is not enough to
// spin sprint up; demand at least two aligned back blocks so the
// executor has a real run-up window.
// tools/sim_jump_reach.py "Standing sprint jump (0t momentum)" matrix
// shows gap=4 dy=0 is unreachable, while 12t-momentum gap=4 reaches
// 5.1075 m.
int requiredBackBlocks = (yDelta == 0 && !carriedEntry && horiz >= 4.5) ? 2 : 1;
int requiredBackBlocks = (yDelta == 0 && !parkourCarry && horiz >= 4.5) ? 2 : 1;
for (int i = 1; i <= requiredBackBlocks; i++)
{