diff --git a/MinecraftClient/Pathing/Core/AStarPathFinder.cs b/MinecraftClient/Pathing/Core/AStarPathFinder.cs
index 70bc0254..14c4ca08 100644
--- a/MinecraftClient/Pathing/Core/AStarPathFinder.cs
+++ b/MinecraftClient/Pathing/Core/AStarPathFinder.cs
@@ -44,6 +44,16 @@ namespace MinecraftClient.Pathing.Core
moves.Add(new MoveDiagonal(-1, 1));
moves.Add(new MoveDiagonal(-1, -1));
+ // Diagonal ascend/descend: corner jumps and drops
+ foreach (int dx in offsets)
+ {
+ foreach (int dz in offsets)
+ {
+ moves.Add(new MoveDiagonalAscend(dx, dz));
+ moves.Add(new MoveDiagonalDescend(dx, dz));
+ }
+ }
+
moves.Add(new MoveClimb(true));
moves.Add(new MoveClimb(false));
diff --git a/MinecraftClient/Pathing/Execution/Templates/AscendTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/AscendTemplate.cs
index 16ad6b55..3ad3c41e 100644
--- a/MinecraftClient/Pathing/Execution/Templates/AscendTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/AscendTemplate.cs
@@ -33,8 +33,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
double dy = ExpectedEnd.Y - pos.Y;
double horizDistSq = dx * dx + dz * dz;
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
- physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
+ physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
input.Forward = true;
input.Sprint = true;
diff --git a/MinecraftClient/Pathing/Execution/Templates/ClimbTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/ClimbTemplate.cs
index 0f562bbe..0ffea56d 100644
--- a/MinecraftClient/Pathing/Execution/Templates/ClimbTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/ClimbTemplate.cs
@@ -39,7 +39,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
if (_tickCount > 120)
return TemplateState.Failed;
- physics.Pitch = _goingUp ? -70f : 70f;
+ float targetPitch = _goingUp ? -70f : 70f;
+ physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
if (physics.OnClimbable)
{
@@ -48,7 +49,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
input.Jump = true;
input.Forward = true;
if (horizDistSq > 0.01)
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
+ {
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
+ }
}
else
{
@@ -58,7 +62,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
// Keep centered horizontally by gently steering if drifting.
if (horizDistSq > 0.15)
{
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
input.Forward = true;
}
}
@@ -67,7 +72,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
{
if (horizDistSq > 0.01)
{
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
input.Forward = true;
}
}
diff --git a/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs
index 31df4abd..21185816 100644
--- a/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/DescendTemplate.cs
@@ -54,19 +54,21 @@ namespace MinecraftClient.Pathing.Execution.Templates
if (_tickCount > 200)
return TemplateState.Failed;
- physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
+ physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
if (physics.OnClimbable)
{
if (horizDistSq > 0.25)
{
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
input.Forward = true;
}
}
else if (horizDistSq > 0.01)
{
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
input.Forward = true;
}
diff --git a/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs
index 96971656..7483b083 100644
--- a/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/SprintJumpTemplate.cs
@@ -39,8 +39,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
double dy = ExpectedEnd.Y - pos.Y;
double horizDistSq = dx * dx + dz * dz;
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
- physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
+ physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
input.Forward = true;
input.Sprint = true;
diff --git a/MinecraftClient/Pathing/Execution/Templates/TemplateHelper.cs b/MinecraftClient/Pathing/Execution/Templates/TemplateHelper.cs
index 0e888821..f724906c 100644
--- a/MinecraftClient/Pathing/Execution/Templates/TemplateHelper.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/TemplateHelper.cs
@@ -5,6 +5,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
{
internal static class TemplateHelper
{
+ private const double EyeHeight = 1.62;
+ private const float MaxYawStepPerTick = 35f;
+ private const float MaxPitchStepPerTick = 25f;
+
internal static float CalculateYaw(double dx, double dz)
{
float yaw = (float)(-Math.Atan2(dx, dz) / Math.PI * 180.0);
@@ -13,17 +17,49 @@ namespace MinecraftClient.Pathing.Execution.Templates
}
///
- /// Calculate the pitch angle (in degrees) to look toward a 3D offset.
- /// Negative = look up, positive = look down. Clamped to [-90, 90].
- /// The dy is relative to eye height (~1.62 blocks above feet).
+ /// Calculate the pitch angle to look from current eye position toward
+ /// the target's feet-level Y. dy = targetFeetY - playerFeetY.
///
internal static float CalculatePitch(double dx, double dy, double dz)
{
double horizDist = Math.Sqrt(dx * dx + dz * dz);
+ // Look toward the target's eye level, not feet.
+ // Both player and target are at feet+EyeHeight, so the vertical
+ // difference is just dy (target feet Y - player feet Y).
float pitch = (float)(-Math.Atan2(dy, horizDist) / Math.PI * 180.0);
return Math.Clamp(pitch, -90f, 90f);
}
+ ///
+ /// Smoothly interpolate yaw toward a target, respecting wrap-around at 0/360.
+ ///
+ internal static float SmoothYaw(float current, float target, float maxStep = MaxYawStepPerTick)
+ {
+ float delta = target - current;
+ // Normalize to [-180, 180]
+ while (delta > 180f) delta -= 360f;
+ while (delta < -180f) delta += 360f;
+
+ if (Math.Abs(delta) <= maxStep)
+ return target;
+
+ float result = current + Math.Sign(delta) * maxStep;
+ if (result < 0) result += 360f;
+ if (result >= 360f) result -= 360f;
+ return result;
+ }
+
+ ///
+ /// Smoothly interpolate pitch toward a target.
+ ///
+ internal static float SmoothPitch(float current, float target, float maxStep = MaxPitchStepPerTick)
+ {
+ float delta = target - current;
+ if (Math.Abs(delta) <= maxStep)
+ return target;
+ return current + Math.Sign(delta) * maxStep;
+ }
+
internal static double HorizontalDistanceSq(Location a, Location b)
{
double dx = a.X - b.X;
diff --git a/MinecraftClient/Pathing/Execution/Templates/WalkTemplate.cs b/MinecraftClient/Pathing/Execution/Templates/WalkTemplate.cs
index 55e0f73f..0cdbf96e 100644
--- a/MinecraftClient/Pathing/Execution/Templates/WalkTemplate.cs
+++ b/MinecraftClient/Pathing/Execution/Templates/WalkTemplate.cs
@@ -31,8 +31,10 @@ namespace MinecraftClient.Pathing.Execution.Templates
double dx = ExpectedEnd.X - pos.X;
double dz = ExpectedEnd.Z - pos.Z;
double dy = ExpectedEnd.Y - pos.Y;
- physics.Yaw = TemplateHelper.CalculateYaw(dx, dz);
- physics.Pitch = TemplateHelper.CalculatePitch(dx, dy - 1.62, dz);
+ float targetYaw = TemplateHelper.CalculateYaw(dx, dz);
+ float targetPitch = TemplateHelper.CalculatePitch(dx, dy, dz);
+ physics.Yaw = TemplateHelper.SmoothYaw(physics.Yaw, targetYaw);
+ physics.Pitch = TemplateHelper.SmoothPitch(physics.Pitch, targetPitch);
input.Forward = true;
input.Sprint = true;
diff --git a/MinecraftClient/Pathing/Moves/Impl/MoveDiagonalAscend.cs b/MinecraftClient/Pathing/Moves/Impl/MoveDiagonalAscend.cs
new file mode 100644
index 00000000..b0bde700
--- /dev/null
+++ b/MinecraftClient/Pathing/Moves/Impl/MoveDiagonalAscend.cs
@@ -0,0 +1,69 @@
+using MinecraftClient.Pathing.Core;
+
+namespace MinecraftClient.Pathing.Moves.Impl
+{
+ ///
+ /// Jump diagonally (1 block in X and Z) and land 1 block higher.
+ /// Handles the "corner jump" pattern: jump around a wall edge and land
+ /// one block higher on a platform that is diagonally adjacent.
+ ///
+ public sealed class MoveDiagonalAscend : IMove
+ {
+ public MoveType Type => MoveType.Ascend;
+ public int XOffset { get; }
+ public int ZOffset { get; }
+ public bool DynamicY => false;
+
+ public MoveDiagonalAscend(int xOffset, int zOffset)
+ {
+ XOffset = xOffset;
+ ZOffset = zOffset;
+ }
+
+ public void Calculate(CalculationContext ctx, int x, int y, int z, ref MoveResult result)
+ {
+ int destX = x + XOffset;
+ int destZ = z + ZOffset;
+ int destY = y + 1;
+
+ // Need headroom to jump (y+2 at start)
+ if (!ctx.CanWalkThrough(x, y + 2, z))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ // Destination: solid ground, body passable, head passable
+ if (!ctx.CanWalkOn(destX, y, destZ))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ if (!ctx.CanWalkThrough(destX, destY, destZ) ||
+ !ctx.CanWalkThrough(destX, destY + 1, destZ))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ // At least one of the two intermediate cardinal directions must be passable
+ // at both the current and destination height (player sweeps through).
+ bool pathViaX = ctx.CanWalkThrough(x + XOffset, y, z) &&
+ ctx.CanWalkThrough(x + XOffset, y + 1, z) &&
+ ctx.CanWalkThrough(x + XOffset, y + 2, z);
+ bool pathViaZ = ctx.CanWalkThrough(x, y, z + ZOffset) &&
+ ctx.CanWalkThrough(x, y + 1, z + ZOffset) &&
+ ctx.CanWalkThrough(x, y + 2, z + ZOffset);
+
+ if (!pathViaX && !pathViaZ)
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ double cost = ctx.SprintCost * ActionCosts.DiagonalMultiplier + ctx.JumpPenalty;
+ result.Set(destX, destY, destZ, cost);
+ }
+ }
+}
diff --git a/MinecraftClient/Pathing/Moves/Impl/MoveDiagonalDescend.cs b/MinecraftClient/Pathing/Moves/Impl/MoveDiagonalDescend.cs
new file mode 100644
index 00000000..2f9e2578
--- /dev/null
+++ b/MinecraftClient/Pathing/Moves/Impl/MoveDiagonalDescend.cs
@@ -0,0 +1,77 @@
+using MinecraftClient.Mapping;
+using MinecraftClient.Pathing.Core;
+
+namespace MinecraftClient.Pathing.Moves.Impl
+{
+ ///
+ /// Walk diagonally (1 block in X and Z) and drop 1 block.
+ /// Handles the "corner drop" pattern: step around a wall edge and land
+ /// one block lower on a platform that is diagonally adjacent.
+ ///
+ public sealed class MoveDiagonalDescend : IMove
+ {
+ public MoveType Type => MoveType.Descend;
+ public int XOffset { get; }
+ public int ZOffset { get; }
+ public bool DynamicY => false;
+
+ public MoveDiagonalDescend(int xOffset, int zOffset)
+ {
+ XOffset = xOffset;
+ ZOffset = zOffset;
+ }
+
+ public void Calculate(CalculationContext ctx, int x, int y, int z, ref MoveResult result)
+ {
+ int destX = x + XOffset;
+ int destZ = z + ZOffset;
+ int destY = y - 1;
+
+ // Destination must have ground, body space, and head space
+ if (!ctx.CanWalkOn(destX, destY - 1, destZ))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ if (!ctx.CanWalkThrough(destX, destY, destZ) ||
+ !ctx.CanWalkThrough(destX, destY + 1, destZ))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ Material landOn = ctx.GetMaterial(destX, destY - 1, destZ);
+ if (MoveHelper.IsHazardous(landOn))
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ // Don't descend from climbable blocks
+ Material fromDown = ctx.GetMaterial(x, y - 1, z);
+ if (fromDown.CanBeClimbedOn())
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ // At least one of the two intermediate cardinal directions must be passable
+ // (player needs clearance to cut the corner).
+ bool pathViaX = ctx.CanWalkThrough(x + XOffset, y, z) &&
+ ctx.CanWalkThrough(x + XOffset, y + 1, z);
+ bool pathViaZ = ctx.CanWalkThrough(x, y, z + ZOffset) &&
+ ctx.CanWalkThrough(x, y + 1, z + ZOffset);
+
+ if (!pathViaX && !pathViaZ)
+ {
+ result.SetImpossible();
+ return;
+ }
+
+ double cost = ActionCosts.WalkOffBlock * ActionCosts.DiagonalMultiplier
+ + ActionCosts.FallCost(1);
+ result.Set(destX, destY, destZ, cost);
+ }
+ }
+}