diff --git a/MinecraftClient/Pathing/Execution/Templates/GroundedSegmentController.cs b/MinecraftClient/Pathing/Execution/Templates/GroundedSegmentController.cs index 13e67da4..87c03403 100644 --- a/MinecraftClient/Pathing/Execution/Templates/GroundedSegmentController.cs +++ b/MinecraftClient/Pathing/Execution/Templates/GroundedSegmentController.cs @@ -123,12 +123,14 @@ namespace MinecraftClient.Pathing.Execution.Templates return TemplateHelper.RemainingDistanceAlongSegment(pos, segment) <= handoffDistance; } - if (exitSpeed < segment.ExitHints.MinExitSpeed) - return false; - - if (exitSpeed > segment.ExitHints.MaxExitSpeed) - return false; - + // LandingRecovery accepts a fully-decelerated handoff: once the bot + // has reached the target block on the ground, the segment has done + // its job. Apply this before the MinExitSpeed gate so a Descend + // that lands and naturally settles to zero speed (e.g. when the + // following segment is a fresh Traverse rather than a chained + // Parkour) can hand off cleanly. Without this early-out the bot + // would idle inside the destination block until the segment timed + // out, triggering an unnecessary replan. if (segment.ExitTransition == PathTransitionType.LandingRecovery && physics.OnGround && !segment.ExitHints.RequireStableFooting @@ -137,6 +139,12 @@ namespace MinecraftClient.Pathing.Execution.Templates return true; } + if (exitSpeed < segment.ExitHints.MinExitSpeed) + return false; + + if (exitSpeed > segment.ExitHints.MaxExitSpeed) + return false; + if (segment.ExitHints.RequireStableFooting) { return physics.OnGround diff --git a/MinecraftClient/Pathing/Moves/ParkourFeasibility.cs b/MinecraftClient/Pathing/Moves/ParkourFeasibility.cs index a1ef655f..480a92fd 100644 --- a/MinecraftClient/Pathing/Moves/ParkourFeasibility.cs +++ b/MinecraftClient/Pathing/Moves/ParkourFeasibility.cs @@ -62,11 +62,31 @@ internal static class ParkourFeasibility if (carriedEntry && yDelta < 0) return true; - int backX = x - Math.Sign(xOffset); - int backZ = z - Math.Sign(zOffset); - if (!ctx.CanWalkOn(backX, y - 1, backZ)) - return false; - return IsColumnPassable(ctx, backX, y, backZ); + int xSign = Math.Sign(xOffset); + int zSign = Math.Sign(zOffset); + + // 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. + // 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; + + for (int i = 1; i <= requiredBackBlocks; i++) + { + int backX = x - xSign * i; + int backZ = z - zSign * i; + if (!ctx.CanWalkOn(backX, y - 1, backZ)) + return false; + if (!IsColumnPassable(ctx, backX, y, backZ)) + return false; + } + + return true; } public static bool TryGetRequiredStaticEntryRunupSteps(