fix: parkour diagonal flight path, wall-adjacent parkour, sprint descend checks

- MoveParkour: replace full-rectangle intermediate check with diagonal
  strip check (CheckFlightPath) so walls outside the actual flight
  corridor no longer block valid parkour jumps.
- MoveParkour: require both cardinal neighbors passable for diagonal
  parkour takeoff; a wall on either side clips the AABB and prevents
  reaching the target.
- MoveSprintDescend: replace full-rectangle check with explicit
  per-axis intermediate column check.
- SprintJumpTemplate: track diagonal jumps and skip approach delay for
  short diagonal jumps to avoid overshooting small starting platforms.

Made-with: Cursor
This commit is contained in:
BruceChen 2026-04-12 02:02:58 +08:00
parent 9e6b689dd6
commit 00494078df
3 changed files with 125 additions and 40 deletions

View file

@ -18,6 +18,7 @@ namespace MinecraftClient.Pathing.Execution.Templates
public Location ExpectedEnd { get; }
private readonly double _horizDist;
private readonly bool _isDiagonal;
private int _tickCount;
private Phase _phase = Phase.Approach;
@ -28,6 +29,7 @@ namespace MinecraftClient.Pathing.Execution.Templates
double dx = end.X - start.X;
double dz = end.Z - start.Z;
_horizDist = Math.Sqrt(dx * dx + dz * dz);
_isDiagonal = Math.Abs(dx) > 0.5 && Math.Abs(dz) > 0.5;
}
public TemplateState Tick(Location pos, PlayerPhysics physics, MovementInput input)
@ -57,10 +59,12 @@ namespace MinecraftClient.Pathing.Execution.Templates
// toward the block edge. Baritone waits until playerFeet is in
// the next block (~0.5 blocks from center) for dist >= 4.
// For medium jumps (dist 3), wait 0.35 blocks (Baritone: 0.7).
// For short diagonal jumps (<= 3 blocks), jump immediately
// to avoid overshooting the small starting platform.
double minApproachSq;
if (_horizDist >= 3.5)
minApproachSq = 0.25; // 0.5 blocks
else if (_horizDist >= 2.5)
else if (_horizDist >= 2.5 && !_isDiagonal)
minApproachSq = 0.12; // ~0.35 blocks
else
minApproachSq = 0.0;

View file

@ -103,32 +103,14 @@ namespace MinecraftClient.Pathing.Moves.Impl
int xAbs = Math.Abs(XOffset);
int zAbs = Math.Abs(ZOffset);
// Check intermediate space for passability (the player's bounding box sweeps
// through a rectangle from start to end; check all blocks in that rectangle)
for (int i = 0; i <= xAbs; i++)
// Check intermediate blocks along the flight path.
// Cardinal: check all blocks in the column along the primary axis.
// Diagonal: check blocks along the diagonal strip, not the full rectangle.
// Player AABB is 0.6 wide, so only blocks near the diagonal line matter.
if (!CheckFlightPath(ctx, x, y, z, xSign, zSign, xAbs, zAbs))
{
for (int j = 0; j <= zAbs; j++)
{
if (i == 0 && j == 0) continue;
if (i == xAbs && j == zAbs) continue;
int gx = x + xSign * i;
int gz = z + zSign * j;
if (!ctx.CanWalkThrough(gx, y, gz) ||
!ctx.CanWalkThrough(gx, y + 1, gz) ||
!ctx.CanWalkThrough(gx, y + 2, gz))
{
result.SetImpossible();
return;
}
if (_yDelta > 0 && !ctx.CanWalkThrough(gx, y + 3, gz))
{
result.SetImpossible();
return;
}
}
result.SetImpossible();
return;
}
// Gap check: first block(s) adjacent to start must lack ground.
@ -159,6 +141,23 @@ namespace MinecraftClient.Pathing.Moves.Impl
}
}
// For diagonal parkour, the player's AABB (0.6 wide) must clear both
// cardinal neighbors at the start. A wall on either side will clip the
// AABB during the initial sprint, preventing enough X or Z velocity to
// reach the target. Require BOTH cardinal exits to be passable.
if (xAbs > 0 && zAbs > 0)
{
bool canExitViaX = ctx.CanWalkThrough(x + xSign, y, z) &&
ctx.CanWalkThrough(x + xSign, y + 1, z);
bool canExitViaZ = ctx.CanWalkThrough(x, y, z + zSign) &&
ctx.CanWalkThrough(x, y + 1, z + zSign);
if (!canExitViaX || !canExitViaZ)
{
result.SetImpossible();
return;
}
}
// Overshoot safety: after landing, player continues moving.
// The block(s) past the destination in the jump direction must be passable.
int overX = destX + xSign;
@ -190,6 +189,85 @@ namespace MinecraftClient.Pathing.Moves.Impl
result.Set(destX, destY, destZ, cost);
}
/// <summary>
/// Check body clearance along the flight path from start toward the destination.
/// For cardinal moves, checks a straight line. For diagonal moves, checks
/// only blocks near the actual diagonal trajectory rather than the full bounding
/// rectangle, allowing jumps that pass a wall on one side.
/// </summary>
private bool CheckFlightPath(
CalculationContext ctx, int x, int y, int z,
int xSign, int zSign, int xAbs, int zAbs)
{
if (xAbs == 0 || zAbs == 0)
{
// Cardinal: single axis, check each block along the line
for (int step = 1; step < Math.Max(xAbs, zAbs); step++)
{
int gx = x + xSign * (xAbs > 0 ? step : 0);
int gz = z + zSign * (zAbs > 0 ? step : 0);
if (!ClearColumn(ctx, gx, y, gz))
return false;
}
return true;
}
// Diagonal: walk the diagonal and check each block the AABB touches.
// At each step t along the diagonal, the player center is near
// (x + t*xSign, z + t*zSign). The AABB extends 0.3 blocks each side,
// so check the diagonal cell and one neighbor on each axis-aligned side
// only when the trajectory is close to a cell boundary (always for short
// diagonals). We enumerate cells by stepping through the longer axis
// and computing the corresponding position on the shorter axis.
int maxSteps = Math.Max(xAbs, zAbs);
for (int step = 1; step < maxSteps; step++)
{
// Proportional position along each axis
double fx = (double)step * xAbs / maxSteps;
double fz = (double)step * zAbs / maxSteps;
int ix = (int)Math.Round(fx);
int iz = (int)Math.Round(fz);
int gx = x + xSign * ix;
int gz = z + zSign * iz;
if (!ClearColumn(ctx, gx, y, gz))
return false;
// Also check the neighboring cell across the shorter axis when close
// to a cell boundary (player AABB overlaps adjacent cell)
if (xAbs != zAbs)
{
double fracX = fx - Math.Floor(fx);
double fracZ = fz - Math.Floor(fz);
if (fracX > 0.2 && fracX < 0.8 && ix > 0 && ix < xAbs)
{
if (!ClearColumn(ctx, x + xSign * (ix - 1), y, gz))
return false;
}
if (fracZ > 0.2 && fracZ < 0.8 && iz > 0 && iz < zAbs)
{
if (!ClearColumn(ctx, gx, y, z + zSign * (iz - 1)))
return false;
}
}
}
return true;
}
private bool ClearColumn(CalculationContext ctx, int gx, int y, int gz)
{
if (!ctx.CanWalkThrough(gx, y, gz) ||
!ctx.CanWalkThrough(gx, y + 1, gz) ||
!ctx.CanWalkThrough(gx, y + 2, gz))
return false;
if (_yDelta > 0 && !ctx.CanWalkThrough(gx, y + 3, gz))
return false;
return true;
}
public override string ToString()
{
double dist = Math.Sqrt((double)(XOffset * XOffset + ZOffset * ZOffset));

View file

@ -46,21 +46,24 @@ namespace MinecraftClient.Pathing.Moves.Impl
int xAbs = Math.Abs(XOffset);
int zAbs = Math.Abs(ZOffset);
// The flight path sweeps through intermediate blocks at current Y.
// Check body clearance for all intermediate and destination columns.
for (int i = 0; i <= xAbs; i++)
// Check body clearance at the destination column and along the flight path.
if (!ctx.CanWalkThrough(destX, y, destZ) || !ctx.CanWalkThrough(destX, y + 1, destZ))
{
for (int j = 0; j <= zAbs; j++)
{
if (i == 0 && j == 0) continue;
int gx = x + xSign * i;
int gz = z + zSign * j;
if (!ctx.CanWalkThrough(gx, y, gz) || !ctx.CanWalkThrough(gx, y + 1, gz))
{
result.SetImpossible();
return;
}
}
result.SetImpossible();
return;
}
// For cardinal (2,0)/(0,2): check the one intermediate column.
// For diagonal (1,1): destination IS one step away, no intermediate.
if (xAbs == 2 && zAbs == 0)
{
if (!ctx.CanWalkThrough(x + xSign, y, z) || !ctx.CanWalkThrough(x + xSign, y + 1, z))
{ result.SetImpossible(); return; }
}
else if (xAbs == 0 && zAbs == 2)
{
if (!ctx.CanWalkThrough(x, y, z + zSign) || !ctx.CanWalkThrough(x, y + 1, z + zSign))
{ result.SetImpossible(); return; }
}
// The first step in the primary direction must lack ground (this IS a drop).