diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index 9c3f9751..c343f16e 100644
--- a/MinecraftClient/McClient.cs
+++ b/MinecraftClient/McClient.cs
@@ -1730,11 +1730,20 @@ namespace MinecraftClient
int sx = (int)Math.Floor(location.X);
int sy = (int)Math.Floor(location.Y);
int sz = (int)Math.Floor(location.Z);
+
+ // If floored Y lands inside a solid block (e.g. player on top of it), step up
+ if (!ctx.CanWalkThrough(sx, sy, sz) && ctx.CanWalkThrough(sx, sy + 1, sz))
+ sy++;
+
int gx = (int)Math.Floor(goal.X);
int gy = (int)Math.Floor(goal.Y);
int gz = (int)Math.Floor(goal.Z);
- Log.Info($"[Goto] A* search from ({sx},{sy},{sz}) to ({gx},{gy},{gz})...");
+ if (!ctx.CanWalkThrough(gx, gy, gz) && ctx.CanWalkThrough(gx, gy + 1, gz))
+ gy++;
+
+ Log.Info($"[Goto] A* search from ({sx},{sy},{sz}) to ({gx},{gy},{gz}) " +
+ $"[raw pos=({location.X:F2},{location.Y:F2},{location.Z:F2})]");
using var cts = new CancellationTokenSource();
var result = finder.Calculate(ctx, sx, sy, sz,
@@ -3293,12 +3302,20 @@ namespace MinecraftClient
///
/// Check if the player has approximately reached a waypoint.
+ /// Uses both horizontal and vertical distance for climb/descend waypoints.
///
private bool ReachedWaypoint(Location target)
{
double dx = target.X - location.X;
double dz = target.Z - location.Z;
- return dx * dx + dz * dz < 0.25; // within ~0.5 blocks horizontally
+ double dy = target.Y - location.Y;
+ double horizDistSq = dx * dx + dz * dz;
+
+ // Vertical waypoint (climbing/falling): require reaching target Y level
+ if (horizDistSq < 0.5 && Math.Abs(dy) > 0.8)
+ return false;
+
+ return horizDistSq < 0.25 && Math.Abs(dy) < 0.8;
}
///
@@ -3312,7 +3329,41 @@ namespace MinecraftClient
double dy = target.Y - location.Y;
double distSqr = dx * dx + dz * dz;
- if (distSqr < 0.01) return; // Close enough horizontally
+ // Climbing: target is above/below with small horizontal offset
+ if (playerPhysics.OnClimbable && Math.Abs(dy) > 0.5 && distSqr < 1.0)
+ {
+ if (dy > 0)
+ {
+ physicsInput.Jump = true;
+ // Push against the wall for HorizontalCollision-triggered climbing
+ if (distSqr > 0.01)
+ {
+ float yaw = (float)(-Math.Atan2(dx, dz) / Math.PI * 180.0);
+ if (yaw < 0) yaw += 360;
+ playerPhysics.Yaw = yaw;
+ playerYaw = yaw;
+ physicsInput.Forward = true;
+ }
+ else
+ {
+ physicsInput.Forward = true;
+ }
+ }
+ else
+ {
+ physicsInput.Sneak = false;
+ }
+ return;
+ }
+
+ // Non-climbing vertical jump
+ if (distSqr < 0.1 && dy > 0.5 && playerPhysics.OnGround)
+ {
+ physicsInput.Jump = true;
+ return;
+ }
+
+ if (distSqr < 0.01) return;
// Calculate yaw to face target
float targetYaw = (float)(-Math.Atan2(dx, dz) / Math.PI * 180.0);
diff --git a/MinecraftClient/Pathing/Moves/MoveHelper.cs b/MinecraftClient/Pathing/Moves/MoveHelper.cs
index 8bb6108f..e925bc6f 100644
--- a/MinecraftClient/Pathing/Moves/MoveHelper.cs
+++ b/MinecraftClient/Pathing/Moves/MoveHelper.cs
@@ -19,6 +19,10 @@ namespace MinecraftClient.Pathing.Moves
return true;
if (mat.IsLiquid())
return false;
+ if (mat.CanBeClimbedOn())
+ return true;
+ if (IsOpenGate(mat))
+ return true;
if (mat.IsSolid())
return false;
if (mat.CanHarmPlayers())
@@ -38,6 +42,10 @@ namespace MinecraftClient.Pathing.Moves
return false;
if (mat.CanHarmPlayers())
return false;
+ if (mat.CanBeClimbedOn())
+ return false;
+ if (IsOpenGate(mat))
+ return false;
return mat.IsSolid();
}
@@ -65,5 +73,19 @@ namespace MinecraftClient.Pathing.Moves
{
return mat == Material.Water;
}
+
+ ///
+ /// Conservative check for gate-type blocks. Since we cannot read block state
+ /// (open/closed) during planning, treat all fence gates as passable.
+ ///
+ private static bool IsOpenGate(Material mat)
+ {
+ return mat is Material.AcaciaFenceGate or Material.BirchFenceGate
+ or Material.CrimsonFenceGate or Material.DarkOakFenceGate
+ or Material.JungleFenceGate or Material.MangroveWood
+ or Material.OakFenceGate or Material.SpruceFenceGate
+ or Material.WarpedFenceGate or Material.CherryFenceGate
+ or Material.BambooFenceGate or Material.PaleOakFenceGate;
+ }
}
}