mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-29 13:04:59 +00:00
feat: tighten parkour reliability checks
This commit is contained in:
parent
6b449cc72a
commit
0e0fc06b72
7 changed files with 987 additions and 25 deletions
|
|
@ -29,6 +29,7 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
private readonly double _horizDist;
|
||||
private int _tickCount;
|
||||
private Phase _phase = Phase.Approach;
|
||||
private bool _airReleaseCommitted;
|
||||
private bool _leftGround;
|
||||
|
||||
private const float YawToleranceDeg = 5f;
|
||||
|
|
@ -103,7 +104,11 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
_leftGround = true;
|
||||
|
||||
bool pastTarget = IsPastTarget(pos);
|
||||
bool releaseInAir = TransitionBrakingPlanner.ShouldReleaseForwardInAir(_segment, _nextSegment, pos, physics);
|
||||
bool releaseInAir = ShouldReleaseInAir(pos, physics, world);
|
||||
if (_segment.ExitTransition == PathTransitionType.LandingRecovery && releaseInAir)
|
||||
_airReleaseCommitted = true;
|
||||
if (_airReleaseCommitted)
|
||||
releaseInAir = true;
|
||||
|
||||
if (releaseInAir || pastTarget)
|
||||
{
|
||||
|
|
@ -138,7 +143,8 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
return TemplateState.Complete;
|
||||
|
||||
if (_segment.ExitTransition != PathTransitionType.ContinueStraight
|
||||
&& TemplateHelper.IsSettledAtEnd(pos, ExpectedEnd, physics, horizThresholdSq: 0.0025))
|
||||
&& physics.OnGround
|
||||
&& TemplateHelper.IsSettledOnTargetBlock(pos, ExpectedEnd, physics))
|
||||
{
|
||||
return TemplateState.Complete;
|
||||
}
|
||||
|
|
@ -169,6 +175,80 @@ namespace MinecraftClient.Pathing.Execution.Templates
|
|||
return dot > 0.0;
|
||||
}
|
||||
|
||||
private bool ShouldReleaseInAir(Location pos, PlayerPhysics physics, World world)
|
||||
{
|
||||
if (TransitionBrakingPlanner.ShouldReleaseForwardInAir(_segment, _nextSegment, pos, physics))
|
||||
return true;
|
||||
|
||||
if (_segment.ExitTransition == PathTransitionType.ContinueStraight || physics.OnGround)
|
||||
return false;
|
||||
|
||||
Location? landingIfHolding = PredictLandingPosition(physics, world, holdForward: true, holdSprint: true);
|
||||
Location? landingIfReleased = PredictLandingPosition(physics, world, holdForward: false, holdSprint: false);
|
||||
if (landingIfHolding is null || landingIfReleased is null)
|
||||
return false;
|
||||
|
||||
bool holdingStaysInside = TemplateFootingHelper.IsFootprintInsideTargetBlock(landingIfHolding.Value, ExpectedEnd);
|
||||
bool releasingStaysInside = TemplateFootingHelper.IsFootprintInsideTargetBlock(landingIfReleased.Value, ExpectedEnd);
|
||||
|
||||
if (_segment.ExitTransition == PathTransitionType.LandingRecovery && !holdingStaysInside)
|
||||
return true;
|
||||
|
||||
return !holdingStaysInside && releasingStaysInside;
|
||||
}
|
||||
|
||||
private Location? PredictLandingPosition(PlayerPhysics physics, World world, bool holdForward, bool holdSprint)
|
||||
{
|
||||
PlayerPhysics sim = ClonePhysics(physics);
|
||||
var input = new MovementInput
|
||||
{
|
||||
Forward = holdForward,
|
||||
Sprint = holdSprint
|
||||
};
|
||||
|
||||
for (int tick = 0; tick < 16; tick++)
|
||||
{
|
||||
sim.ApplyInput(input);
|
||||
sim.Tick(world);
|
||||
if (sim.OnGround)
|
||||
return new Location(sim.Position.X, sim.Position.Y, sim.Position.Z);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PlayerPhysics ClonePhysics(PlayerPhysics physics)
|
||||
{
|
||||
return new PlayerPhysics
|
||||
{
|
||||
Position = physics.Position,
|
||||
DeltaMovement = physics.DeltaMovement,
|
||||
Yaw = physics.Yaw,
|
||||
Pitch = physics.Pitch,
|
||||
OnGround = physics.OnGround,
|
||||
HorizontalCollision = physics.HorizontalCollision,
|
||||
VerticalCollision = physics.VerticalCollision,
|
||||
VerticalCollisionBelow = physics.VerticalCollisionBelow,
|
||||
FallDistance = physics.FallDistance,
|
||||
StuckSpeedMultiplier = physics.StuckSpeedMultiplier,
|
||||
Xxa = physics.Xxa,
|
||||
Zza = physics.Zza,
|
||||
Yya = physics.Yya,
|
||||
Jumping = physics.Jumping,
|
||||
Sprinting = physics.Sprinting,
|
||||
Sneaking = physics.Sneaking,
|
||||
CreativeFlying = physics.CreativeFlying,
|
||||
InWater = physics.InWater,
|
||||
IsUnderWater = physics.IsUnderWater,
|
||||
InLava = physics.InLava,
|
||||
OnClimbable = physics.OnClimbable,
|
||||
HasSlowFalling = physics.HasSlowFalling,
|
||||
HasLevitation = physics.HasLevitation,
|
||||
LevitationAmplifier = physics.LevitationAmplifier,
|
||||
MovementSpeed = physics.MovementSpeed
|
||||
};
|
||||
}
|
||||
|
||||
private static float YawDifference(float current, float target)
|
||||
{
|
||||
float delta = target - current;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using MinecraftClient.Mapping;
|
||||
using MinecraftClient.Pathing.Core;
|
||||
using MinecraftClient.Pathing.Moves;
|
||||
|
||||
namespace MinecraftClient.Pathing.Moves.Impl
|
||||
{
|
||||
|
|
@ -65,6 +66,12 @@ namespace MinecraftClient.Pathing.Moves.Impl
|
|||
return;
|
||||
}
|
||||
|
||||
if (!ParkourFeasibility.HasRunUp(ctx, x, y, z, XOffset, ZOffset, _yDelta))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
int destX = x + XOffset;
|
||||
int destZ = z + ZOffset;
|
||||
int destY = y + _yDelta;
|
||||
|
|
@ -141,33 +148,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)
|
||||
if (!ParkourFeasibility.HasDiagonalShoulderClearance(ctx, x, y, z, XOffset, ZOffset))
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
int overZ = destZ + zSign;
|
||||
if (!ctx.CanWalkThrough(overX, destY, overZ) ||
|
||||
!ctx.CanWalkThrough(overX, destY + 1, overZ))
|
||||
if (!ParkourFeasibility.HasCardinalSideClearance(ctx, x, y, z, XOffset, ZOffset))
|
||||
{
|
||||
// Wall right after landing - risk of collision. Still allow but add cost.
|
||||
// (Baritone rejects this, but we allow with penalty since the template
|
||||
// will decelerate anyway.)
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ParkourFeasibility.HasLandingOvershootClearance(
|
||||
ctx, destX, destY, destZ, xSign, zSign))
|
||||
{
|
||||
result.SetImpossible();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cost model following Baritone:
|
||||
|
|
|
|||
104
MinecraftClient/Pathing/Moves/ParkourFeasibility.cs
Normal file
104
MinecraftClient/Pathing/Moves/ParkourFeasibility.cs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using MinecraftClient.Pathing.Core;
|
||||
|
||||
namespace MinecraftClient.Pathing.Moves;
|
||||
|
||||
internal static class ParkourFeasibility
|
||||
{
|
||||
public static bool HasRunUp(
|
||||
CalculationContext ctx,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
int xOffset,
|
||||
int zOffset,
|
||||
int yDelta)
|
||||
{
|
||||
double horiz = Math.Sqrt(xOffset * xOffset + zOffset * zOffset);
|
||||
double threshold = yDelta > 0 ? 2.5 : 3.5;
|
||||
if (horiz < threshold)
|
||||
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);
|
||||
}
|
||||
|
||||
public static bool HasDiagonalShoulderClearance(
|
||||
CalculationContext ctx,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
int xOffset,
|
||||
int zOffset)
|
||||
{
|
||||
if (xOffset == 0 || zOffset == 0)
|
||||
return true;
|
||||
|
||||
return IsColumnPassable(ctx, x + Math.Sign(xOffset), y, z)
|
||||
&& IsColumnPassable(ctx, x, y, z + Math.Sign(zOffset));
|
||||
}
|
||||
|
||||
public static bool HasLandingOvershootClearance(
|
||||
CalculationContext ctx,
|
||||
int destX,
|
||||
int destY,
|
||||
int destZ,
|
||||
int xSign,
|
||||
int zSign)
|
||||
{
|
||||
if (xSign == 0 && zSign == 0)
|
||||
return true;
|
||||
|
||||
return IsColumnPassable(ctx, destX + xSign, destY, destZ + zSign);
|
||||
}
|
||||
|
||||
public static bool HasCardinalSideClearance(
|
||||
CalculationContext ctx,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
int xOffset,
|
||||
int zOffset)
|
||||
{
|
||||
if ((xOffset == 0) == (zOffset == 0))
|
||||
return true;
|
||||
|
||||
if (xOffset != 0)
|
||||
{
|
||||
int xSign = Math.Sign(xOffset);
|
||||
for (int step = 1; step <= Math.Abs(xOffset); step++)
|
||||
{
|
||||
int gx = x + xSign * step;
|
||||
if (!IsColumnPassable(ctx, gx, y, z - 1)
|
||||
|| !IsColumnPassable(ctx, gx, y, z + 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int zSign = Math.Sign(zOffset);
|
||||
for (int step = 1; step <= Math.Abs(zOffset); step++)
|
||||
{
|
||||
int gz = z + zSign * step;
|
||||
if (!IsColumnPassable(ctx, x - 1, y, gz)
|
||||
|| !IsColumnPassable(ctx, x + 1, y, gz))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsColumnPassable(CalculationContext ctx, int x, int y, int z)
|
||||
{
|
||||
return ctx.CanWalkThrough(x, y, z)
|
||||
&& ctx.CanWalkThrough(x, y + 1, z);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue