mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
148 lines
6.6 KiB
C#
148 lines
6.6 KiB
C#
using System;
|
|
using MinecraftClient.Mapping;
|
|
using MinecraftClient.Pathing.Execution.Templates;
|
|
using MinecraftClient.Physics;
|
|
|
|
namespace MinecraftClient.Pathing.Execution
|
|
{
|
|
public static class TransitionBrakingPlanner
|
|
{
|
|
private const double GroundSpeedThreshold = 0.025;
|
|
private const int MaxSimulationTicks = 14;
|
|
private const double FinalStopLead = 0.06;
|
|
private const double FinalBrakeLead = 0.04;
|
|
private const double TurnBrakeLead = 0.10;
|
|
private const double AirReleaseLead = 0.14;
|
|
|
|
public static TransitionBrakingDecision Plan(PathSegment current, PathSegment? next, Location pos, PlayerPhysics physics, World world)
|
|
{
|
|
if (physics.OnGround
|
|
&& current.ExitTransition == PathTransitionType.LandingRecovery
|
|
&& next is not null
|
|
&& !HasSameHeading(current, next))
|
|
{
|
|
double remaining = RemainingDistanceAlongSegment(current, pos);
|
|
double forwardSpeed = Math.Max(0.0,
|
|
ProjectHorizontalSpeedAlongHeading(physics, current.HeadingX, current.HeadingZ));
|
|
double maxExitSpeed = !double.IsPositiveInfinity(current.ExitHints.MaxExitSpeed)
|
|
? current.ExitHints.MaxExitSpeed
|
|
: 0.035;
|
|
double coastStopDistance = EstimateGroundStopDistance(
|
|
physics, world, current.HeadingX, current.HeadingZ, applyBackBrake: false);
|
|
double hardBrakeDistance = EstimateGroundStopDistance(
|
|
physics, world, current.HeadingX, current.HeadingZ, applyBackBrake: true);
|
|
|
|
if (TemplateFootingHelper.IsFootprintInsideTargetBlock(pos, current.End)
|
|
&& forwardSpeed <= maxExitSpeed)
|
|
{
|
|
return TransitionBrakingDecision.Coast;
|
|
}
|
|
|
|
if (remaining < 0.0 && forwardSpeed > maxExitSpeed)
|
|
return TransitionBrakingDecision.Brake;
|
|
|
|
if (forwardSpeed > GroundSpeedThreshold && remaining <= hardBrakeDistance + TurnBrakeLead)
|
|
return TransitionBrakingDecision.Brake;
|
|
|
|
if (remaining <= coastStopDistance + FinalStopLead)
|
|
return TransitionBrakingDecision.Coast;
|
|
}
|
|
|
|
TransitionInputProfile profile;
|
|
if (physics.OnGround)
|
|
{
|
|
profile = TransitionLookaheadEvaluator.ChooseGroundProfile(current, pos, physics, world);
|
|
}
|
|
else
|
|
{
|
|
if (!current.ExitHints.AllowAirBrake)
|
|
return TransitionBrakingDecision.CarryMomentum(current.PreserveSprint);
|
|
|
|
profile = TransitionLookaheadEvaluator.ChooseAirProfile(current, pos, physics, world);
|
|
}
|
|
|
|
return profile switch
|
|
{
|
|
TransitionInputProfile.Carry => TransitionBrakingDecision.CarryMomentum(current.PreserveSprint),
|
|
TransitionInputProfile.Coast => TransitionBrakingDecision.Coast,
|
|
TransitionInputProfile.Brake => TransitionBrakingDecision.Brake,
|
|
TransitionInputProfile.AirHoldForward => TransitionBrakingDecision.CarryMomentum(current.PreserveSprint),
|
|
TransitionInputProfile.AirRelease => TransitionBrakingDecision.Coast,
|
|
TransitionInputProfile.AirBrake => TransitionBrakingDecision.Brake,
|
|
_ => TransitionBrakingDecision.Coast
|
|
};
|
|
}
|
|
|
|
public static bool ShouldReleaseForwardInAir(PathSegment current, PathSegment? next, Location pos, PlayerPhysics physics)
|
|
{
|
|
if (current.ExitTransition is not (PathTransitionType.FinalStop or PathTransitionType.Turn or PathTransitionType.LandingRecovery))
|
|
return false;
|
|
|
|
double remaining = RemainingDistanceAlongSegment(current, pos);
|
|
double forwardSpeed = Math.Max(0.0, ProjectHorizontalSpeedAlongHeading(physics, current.HeadingX, current.HeadingZ));
|
|
|
|
return remaining <= forwardSpeed + AirReleaseLead;
|
|
}
|
|
|
|
public static bool ShouldReleaseForwardInAir(PathSegment current, PathSegment? next, Location pos, PlayerPhysics physics, World world)
|
|
{
|
|
if (!current.ExitHints.AllowAirBrake)
|
|
return false;
|
|
|
|
TransitionInputProfile profile = TransitionLookaheadEvaluator.ChooseAirProfile(current, pos, physics, world);
|
|
return profile is TransitionInputProfile.AirRelease or TransitionInputProfile.AirBrake;
|
|
}
|
|
|
|
public static double EstimateGroundStopDistance(PlayerPhysics physics, World world, int headingX, int headingZ, bool applyBackBrake)
|
|
{
|
|
if (!physics.OnGround)
|
|
return 0.0;
|
|
|
|
double forwardSpeed = Math.Max(0.0, ProjectHorizontalSpeedAlongHeading(physics, headingX, headingZ));
|
|
if (forwardSpeed <= GroundSpeedThreshold)
|
|
return 0.0;
|
|
|
|
float blockFriction = PlayerPhysics.GetMaterialFriction(
|
|
world.GetBlock(new Location(physics.Position.X, physics.Position.Y - 0.5000010, physics.Position.Z)).Type);
|
|
double drag = blockFriction * PhysicsConsts.FrictionMultiplier;
|
|
double acceleration = physics.MovementSpeed
|
|
* (PhysicsConsts.GroundAccelerationFactor / (drag * drag * drag))
|
|
* PhysicsConsts.InputFriction;
|
|
|
|
if (applyBackBrake)
|
|
acceleration *= 0.98;
|
|
|
|
double distance = 0.0;
|
|
double speed = forwardSpeed;
|
|
for (int tick = 0; tick < MaxSimulationTicks; tick++)
|
|
{
|
|
distance += speed;
|
|
speed = applyBackBrake
|
|
? Math.Max(0.0, (speed - acceleration) * drag)
|
|
: speed * drag;
|
|
|
|
if (speed <= GroundSpeedThreshold)
|
|
break;
|
|
}
|
|
|
|
return distance;
|
|
}
|
|
|
|
private static double RemainingDistanceAlongSegment(PathSegment current, Location pos)
|
|
{
|
|
double dx = current.End.X - pos.X;
|
|
double dz = current.End.Z - pos.Z;
|
|
return dx * current.HeadingX + dz * current.HeadingZ;
|
|
}
|
|
|
|
private static double ProjectHorizontalSpeedAlongHeading(PlayerPhysics physics, int headingX, int headingZ)
|
|
{
|
|
return physics.DeltaMovement.X * headingX + physics.DeltaMovement.Z * headingZ;
|
|
}
|
|
|
|
private static bool HasSameHeading(PathSegment current, PathSegment next)
|
|
{
|
|
return current.HeadingX == next.HeadingX && current.HeadingZ == next.HeadingZ;
|
|
}
|
|
}
|
|
}
|