fix(movement): jump works locally (airborne velocity preserved)

Two fixes for jump physics:
- Skip ground-snap when velocity Z > 0 (prevents immediate re-landing
  at high framerates where per-frame Z delta < 0.05 snap threshold)
- Guard apply_current_movement velocity write behind OnWalkable check
  (prevents MotionInterpreter.DoMotion from zeroing jump velocity on
  every frame while airborne)
- Guard PlayerMovementController velocity replacement behind OnWalkable
  (preserves momentum during airborne flight)

Also fix run speed: compute RunRate locally via PlayerWeenie.InqRunRate
instead of waiting for server UpdateMotion echo (server doesn't echo
to sender). With Run skill 200, run speed is now 9.5 m/s instead of
4.0 m/s.

Strip all diagnostic logging from previous debug sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-14 10:17:59 +02:00
parent 31cd5480dc
commit bb7eced168
2 changed files with 9 additions and 23 deletions

View file

@ -97,8 +97,6 @@ public sealed class PlayerMovementController
// Jump charge state. // Jump charge state.
private bool _jumpCharging; private bool _jumpCharging;
private int _debugSpeedLogCounter;
private int _debugJumpFrames;
private float _jumpExtent; private float _jumpExtent;
private const float JumpChargeRate = 1.0f; // 0→1 over 1 second private const float JumpChargeRate = 1.0f; // 0→1 over 1 second
@ -196,8 +194,15 @@ public sealed class PlayerMovementController
float forwardCmdSpeed; float forwardCmdSpeed;
if (input.Forward) if (input.Forward)
{ {
forwardCmd = input.Run ? MotionCommand.RunForward : MotionCommand.WalkForward; forwardCmd = input.Run ? MotionCommand.RunForward : MotionCommand.WalkForward;
forwardCmdSpeed = 1.0f; // When running, use the PlayerWeenie's RunRate as ForwardSpeed.
// The retail server computes this from Run skill + encumbrance and
// broadcasts it in UpdateMotion, but it doesn't echo to the sender.
// We compute locally using the same formula.
if (input.Run && _weenie.InqRunRate(out float runRate))
forwardCmdSpeed = runRate;
else
forwardCmdSpeed = 1.0f;
} }
else if (input.Backward) else if (input.Backward)
{ {
@ -246,8 +251,6 @@ public sealed class PlayerMovementController
else if (input.StrafeLeft) else if (input.StrafeLeft)
localX = -MotionInterpreter.SidestepAnimSpeed * 0.5f; localX = -MotionInterpreter.SidestepAnimSpeed * 0.5f;
if (input.Forward && _debugSpeedLogCounter++ % 60 == 0)
Console.WriteLine($"DEBUG speed: stateVel.Y={stateVel.Y:F2} MyRunRate={_motion.MyRunRate:F3} localY={localY:F2}");
_body.set_local_velocity(new Vector3(localX, localY, savedWorldVz)); _body.set_local_velocity(new Vector3(localX, localY, savedWorldVz));
} }
@ -275,12 +278,6 @@ public sealed class PlayerMovementController
{ {
_motion.LeaveGround(); _motion.LeaveGround();
outJumpExtent = _jumpExtent; outJumpExtent = _jumpExtent;
_debugJumpFrames = 500; // log until landing
Console.WriteLine($"DEBUG jump FIRED: extent={_jumpExtent:F2} vel={_body.Velocity} onWalk={_body.OnWalkable}");
}
else
{
Console.WriteLine($"DEBUG jump FAILED: extent={_jumpExtent:F2} result={jumpResult} onWalk={_body.OnWalkable} contact={_body.InContact}");
} }
_jumpCharging = false; _jumpCharging = false;
_jumpExtent = 0f; _jumpExtent = 0f;
@ -295,12 +292,6 @@ public sealed class PlayerMovementController
_body.calc_acceleration(); _body.calc_acceleration();
_body.UpdatePhysicsInternal(dt); _body.UpdatePhysicsInternal(dt);
if (_debugJumpFrames > 0)
{
_debugJumpFrames--;
Console.WriteLine($"DEBUG jump frame: pos.Z={_body.Position.Z:F3} vel.Z={_body.Velocity.Z:F3} onWalk={_body.OnWalkable} accel.Z={_body.Acceleration.Z:F1} dt={dt:F4}");
}
// ── 5. Terrain/cell Z snap and ground-contact detection ─────────────── // ── 5. Terrain/cell Z snap and ground-contact detection ───────────────
// Use PhysicsEngine.Resolve to find the ground surface Z under the player. // Use PhysicsEngine.Resolve to find the ground surface Z under the player.
// We pass a zero delta because PhysicsBody already moved the position. // We pass a zero delta because PhysicsBody already moved the position.
@ -326,11 +317,7 @@ public sealed class PlayerMovementController
_body.Velocity = new Vector3(_body.Velocity.X, _body.Velocity.Y, 0f); _body.Velocity = new Vector3(_body.Velocity.X, _body.Velocity.Y, 0f);
if (wasAirborne) if (wasAirborne)
{
Console.WriteLine($"DEBUG LANDED: bodyZ={bodyZ:F3} groundZ={groundZ:F3} vel.Z={_body.Velocity.Z:F3}");
_motion.HitGround(); _motion.HitGround();
_debugJumpFrames = 0;
}
} }
else else
{ {

View file

@ -967,7 +967,6 @@ public sealed class GameWindow : IDisposable
&& update.MotionState.ForwardSpeed.HasValue && update.MotionState.ForwardSpeed.HasValue
&& update.MotionState.ForwardSpeed.Value > 0f) && update.MotionState.ForwardSpeed.Value > 0f)
{ {
Console.WriteLine($"DEBUG RunRate: guid={update.Guid:X8} fwd={update.MotionState.ForwardSpeed.Value:F3}");
_playerController.ApplyServerRunRate(update.MotionState.ForwardSpeed.Value); _playerController.ApplyServerRunRate(update.MotionState.ForwardSpeed.Value);
} }