diff --git a/MinecraftClient/Pathing/Core/AStarPathFinder.cs b/MinecraftClient/Pathing/Core/AStarPathFinder.cs
index 14c4ca08..dd2d7a4e 100644
--- a/MinecraftClient/Pathing/Core/AStarPathFinder.cs
+++ b/MinecraftClient/Pathing/Core/AStarPathFinder.cs
@@ -59,6 +59,16 @@ namespace MinecraftClient.Pathing.Core
moves.Add(new MoveFall());
+ // Sprint descend: sprint off ledge, 2 blocks horizontal + 1-3 drop
+ foreach (int dx in offsets)
+ {
+ moves.Add(new MoveSprintDescend(dx * 2, 0));
+ moves.Add(new MoveSprintDescend(dx, dx));
+ moves.Add(new MoveSprintDescend(dx, -dx));
+ }
+ foreach (int dz in offsets)
+ moves.Add(new MoveSprintDescend(0, dz * 2));
+
// Cardinal parkour: 2-4 block sprint jumps along +-X and +-Z
foreach (int dx in offsets)
{
@@ -67,6 +77,13 @@ namespace MinecraftClient.Pathing.Core
// Ascending: +1Y, dist 2-3 (dist 4 ascend not physically reliable)
for (int dist = 2; dist <= 3; dist++)
moves.Add(new MoveParkour(dx * dist, 0, yDelta: 1));
+ // Descending parkour: sprint-jump, land 1-2 blocks lower
+ for (int dist = 2; dist <= 4; dist++)
+ {
+ moves.Add(new MoveParkour(dx * dist, 0, yDelta: -1));
+ if (dist <= 3)
+ moves.Add(new MoveParkour(dx * dist, 0, yDelta: -2));
+ }
}
foreach (int dz in offsets)
{
@@ -74,6 +91,12 @@ namespace MinecraftClient.Pathing.Core
moves.Add(new MoveParkour(0, dz * dist));
for (int dist = 2; dist <= 3; dist++)
moves.Add(new MoveParkour(0, dz * dist, yDelta: 1));
+ for (int dist = 2; dist <= 4; dist++)
+ {
+ moves.Add(new MoveParkour(0, dz * dist, yDelta: -1));
+ if (dist <= 3)
+ moves.Add(new MoveParkour(0, dz * dist, yDelta: -2));
+ }
}
// Diagonal parkour: sprint jumps at angles.
@@ -90,6 +113,11 @@ namespace MinecraftClient.Pathing.Core
// (3,1)/(1,3): sqrt(10) ~ 3.16 blocks
moves.Add(new MoveParkour(dx * 3, dz * 1));
moves.Add(new MoveParkour(dx * 1, dz * 3));
+
+ // Diagonal descending parkour
+ moves.Add(new MoveParkour(dx * 2, dz * 1, yDelta: -1));
+ moves.Add(new MoveParkour(dx * 1, dz * 2, yDelta: -1));
+ moves.Add(new MoveParkour(dx * 2, dz * 2, yDelta: -1));
}
}
diff --git a/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs
index 21185816..9cec3f78 100644
--- a/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs
@@ -7,6 +7,7 @@ namespace MinecraftClient.Pathing.Execution.Templates
///
/// Walk off a ledge and drop 1-N blocks to a landing spot.
/// Walks toward the destination; gravity handles the fall.
+ /// Sprints when the horizontal distance is large (> 1.5 blocks).
/// Supports solid landings, water landings, and mid-fall vine/ladder grabs.
///
public sealed class DescendTemplate : IActionTemplate
@@ -16,11 +17,15 @@ namespace MinecraftClient.Pathing.Execution.Templates
private int _tickCount;
private bool _hasFallen;
+ private readonly bool _needsSprint;
public DescendTemplate(Location start, Location end)
{
ExpectedStart = start;
ExpectedEnd = end;
+ double hdx = end.X - start.X;
+ double hdz = end.Z - start.Z;
+ _needsSprint = (hdx * hdx + hdz * hdz) > 2.25;
}
public TemplateState Tick(Location pos, PlayerPhysics physics, MovementInput input)
@@ -70,6 +75,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
{
physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
input.Forward = true;
+ if (_needsSprint)
+ input.Sprint = true;
}
return TemplateState.InProgress;
diff --git a/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs
index 7483b083..dc70940f 100644
--- a/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs
@@ -82,9 +82,9 @@ namespace MinecraftClient.Pathing.Execution.Templates
goto case Phase.Landing;
case Phase.Landing:
- // Tolerance scales with jump distance
double horizTolerance = _horizDist >= 3.5 ? 3.0 : 2.0;
- if (horizDistSq < horizTolerance && Math.Abs(dy) < 1.0)
+ double vertTolerance = Math.Abs(ExpectedEnd.Y - ExpectedStart.Y) > 0.5 ? 1.5 : 1.0;
+ if (horizDistSq < horizTolerance && Math.Abs(dy) < vertTolerance)
return TemplateState.Complete;
return TemplateState.Failed;
}
diff --git a/MinecraftClient/Pathing/Moves/Impl/MoveDiagonal.cs b/MinecraftClient/Pathing/Moves/Impl/MoveDiagonal.cs
index 70e78d77..8b43a2bd 100644
--- a/MinecraftClient/Pathing/Moves/Impl/MoveDiagonal.cs
+++ b/MinecraftClient/Pathing/Moves/Impl/MoveDiagonal.cs
@@ -4,7 +4,9 @@ namespace MinecraftClient.Pathing.Moves.Impl
{
///
/// Diagonal walk (1 block in both X and Z, same Y).
- /// Checks both intermediate cardinal columns for clearance.
+ /// Allows corner walks: if one intermediate cardinal is blocked by a wall
+ /// but the other is clear, the player can hug the open side to cut the
+ /// corner. Both sides blocked is impossible (player AABB too wide).
///
public sealed class MoveDiagonal : IMove
{
@@ -36,19 +38,22 @@ namespace MinecraftClient.Pathing.Moves.Impl
return;
}
- if (!ctx.CanWalkThrough(x + XOffset, y, z) || !ctx.CanWalkThrough(x + XOffset, y + 1, z))
+ bool sideX = ctx.CanWalkThrough(x + XOffset, y, z) &&
+ ctx.CanWalkThrough(x + XOffset, y + 1, z);
+ bool sideZ = ctx.CanWalkThrough(x, y, z + ZOffset) &&
+ ctx.CanWalkThrough(x, y + 1, z + ZOffset);
+
+ if (!sideX && !sideZ)
{
result.SetImpossible();
return;
}
- if (!ctx.CanWalkThrough(x, y, z + ZOffset) || !ctx.CanWalkThrough(x, y + 1, z + ZOffset))
- {
- result.SetImpossible();
- return;
- }
+ double cost = ctx.SprintCost * ActionCosts.DiagonalMultiplier;
+ if (!sideX || !sideZ)
+ cost = ctx.WalkCost * ActionCosts.DiagonalMultiplier;
- result.Set(destX, y, destZ, ctx.SprintCost * ActionCosts.DiagonalMultiplier);
+ result.Set(destX, y, destZ, cost);
}
}
}
diff --git a/MinecraftClient/Pathing/Moves/Impl/MoveParkour.cs b/MinecraftClient/Pathing/Moves/Impl/MoveParkour.cs
index 278ffe06..fc442b9c 100644
--- a/MinecraftClient/Pathing/Moves/Impl/MoveParkour.cs
+++ b/MinecraftClient/Pathing/Moves/Impl/MoveParkour.cs
@@ -6,7 +6,8 @@ namespace MinecraftClient.Pathing.Moves.Impl
{
///
/// Sprint jump across a gap in cardinal or diagonal direction.
- /// Supports horizontal distances of 2-4 blocks and optional +1Y ascent.
+ /// Supports horizontal distances of 2-4 blocks, optional +1Y ascent,
+ /// and -1/-2Y descent (land on a lower platform after the jump).
/// Based on Baritone's MovementParkour design with diagonal extensions.
///
public sealed class MoveParkour : IMove
@@ -44,6 +45,12 @@ namespace MinecraftClient.Pathing.Moves.Impl
return;
}
+ if (_yDelta < 0 && -_yDelta > ctx.MaxFallHeight)
+ {
+ result.SetImpossible();
+ return;
+ }
+
if (!ctx.CanSprint)
{
result.SetImpossible();
@@ -172,6 +179,9 @@ namespace MinecraftClient.Pathing.Moves.Impl
double cost;
if (_yDelta > 0)
cost = horizDist * ctx.SprintCost + ctx.JumpPenalty * 2;
+ else if (_yDelta < 0)
+ cost = horizDist * ctx.SprintCost + ctx.JumpPenalty
+ + ActionCosts.FallCost(-_yDelta);
else if (horizDist >= 3.5)
cost = horizDist * ctx.SprintCost + ctx.JumpPenalty;
else
diff --git a/MinecraftClient/Pathing/Moves/Impl/MoveSprintDescend.cs b/MinecraftClient/Pathing/Moves/Impl/MoveSprintDescend.cs
new file mode 100644
index 00000000..6acd9813
--- /dev/null
+++ b/MinecraftClient/Pathing/Moves/Impl/MoveSprintDescend.cs
@@ -0,0 +1,122 @@
+using System;
+using MinecraftClient.Mapping;
+using MinecraftClient.Pathing.Core;
+
+namespace MinecraftClient.Pathing.Moves.Impl
+{
+ ///
+ /// Sprint off a ledge and land 2 blocks away horizontally while dropping 1-3 blocks.
+ /// At sprint speed (~5.6 blocks/s), falling 1-3 blocks gives enough airtime to
+ /// cover 2 horizontal blocks without needing a jump.
+ /// Supports cardinal (2,0)/(0,2) and diagonal (1,1) offsets.
+ ///
+ public sealed class MoveSprintDescend : IMove
+ {
+ public MoveType Type => MoveType.Descend;
+ public int XOffset { get; }
+ public int ZOffset { get; }
+ public bool DynamicY => true;
+
+ public MoveSprintDescend(int xOffset, int zOffset)
+ {
+ XOffset = xOffset;
+ ZOffset = zOffset;
+ }
+
+ public void Calculate(CalculationContext ctx, int x, int y, int z, ref MoveResult result)
+ {
+ if (!ctx.CanSprint)
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ int destX = x + XOffset;
+ int destZ = z + ZOffset;
+
+ Material fromDown = ctx.GetMaterial(x, y - 1, z);
+ if (fromDown.CanBeClimbedOn())
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ int xSign = Math.Sign(XOffset);
+ int zSign = Math.Sign(ZOffset);
+ 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++)
+ {
+ 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;
+ }
+ }
+ }
+
+ // The first step in the primary direction must lack ground (this IS a drop).
+ if (xAbs > 0 && zAbs == 0)
+ {
+ if (ctx.CanWalkOn(x + xSign, y - 1, z))
+ {
+ result.SetImpossible();
+ return;
+ }
+ }
+ else if (xAbs == 0 && zAbs > 0)
+ {
+ if (ctx.CanWalkOn(x, y - 1, z + zSign))
+ {
+ result.SetImpossible();
+ return;
+ }
+ }
+ else
+ {
+ if (ctx.CanWalkOn(x + xSign, y - 1, z + zSign))
+ {
+ result.SetImpossible();
+ return;
+ }
+ }
+
+ // Scan downward from destination column for a landing spot.
+ double horizDist = Math.Sqrt((double)(XOffset * XOffset + ZOffset * ZOffset));
+ for (int drop = 1; drop <= ctx.MaxFallHeight; drop++)
+ {
+ int landY = y - drop - 1;
+ if (landY < -64) break;
+
+ if (!ctx.CanWalkOn(destX, landY, destZ))
+ continue;
+
+ Material landMat = ctx.GetMaterial(destX, landY, destZ);
+ if (MoveHelper.IsHazardous(landMat))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ // Body space at landing
+ if (!ctx.CanWalkThrough(destX, landY + 1, destZ) ||
+ !ctx.CanWalkThrough(destX, landY + 2, destZ))
+ continue;
+
+ double cost = horizDist * ctx.SprintCost + ActionCosts.FallCost(drop);
+ result.Set(destX, landY + 1, destZ, cost);
+ return;
+ }
+
+ result.SetImpossible();
+ }
+ }
+}