diff --git a/src/AcDream.App/Input/PlayerMovementController.cs b/src/AcDream.App/Input/PlayerMovementController.cs index fbc1425..4326599 100644 --- a/src/AcDream.App/Input/PlayerMovementController.cs +++ b/src/AcDream.App/Input/PlayerMovementController.cs @@ -130,6 +130,12 @@ public sealed class PlayerMovementController // Jump charge state. private bool _jumpCharging; private float _jumpExtent; + // K-fix10 diag (2026-04-26): track airborne arc height for the + // jump-too-low investigation. Strip after fix. + private bool _jumpDiagSampling; + private float _jumpDiagStartZ; + private float _jumpDiagPeakZ; + private float _jumpDiagSentVz; // K-fix6 (2026-04-26): retail's PowerBar charge constant for jump is // not legible in the named decomp (the divisor was clobbered in // GetPowerBarLevel's FPU stack reordering at FUN_0056ade0). 2.0/s @@ -391,6 +397,16 @@ public sealed class PlayerMovementController _motion.LeaveGround(); outJumpExtent = _jumpExtent; outJumpVelocity = _body.Velocity; // capture after LeaveGround applies it + + _jumpDiagSampling = true; + _jumpDiagStartZ = _body.Position.Z; + _jumpDiagPeakZ = _body.Position.Z; + _jumpDiagSentVz = _body.Velocity.Z; + Console.WriteLine( + $"[jump.send] extent={_jumpExtent:F3} sentVz={_body.Velocity.Z:F3} " + + $"sentVel=({_body.Velocity.X:F2},{_body.Velocity.Y:F2},{_body.Velocity.Z:F2}) " + + $"formulaPeak={_body.Velocity.Z * _body.Velocity.Z / 19.6f:F2}m " + + $"startZ={_body.Position.Z:F2}"); } _jumpCharging = false; _jumpExtent = 0f; @@ -435,6 +451,14 @@ public sealed class PlayerMovementController { _motion.HitGround(); justLanded = true; + if (_jumpDiagSampling) + { + Console.WriteLine( + $"[jump.peak] sentVz={_jumpDiagSentVz:F3} formulaPeak={_jumpDiagSentVz * _jumpDiagSentVz / 19.6f:F2}m " + + $"actualPeakDz={(_jumpDiagPeakZ - _jumpDiagStartZ):F2}m " + + $"startZ={_jumpDiagStartZ:F2} peakZ={_jumpDiagPeakZ:F2} landZ={_body.Position.Z:F2}"); + _jumpDiagSampling = false; + } } } else @@ -451,6 +475,11 @@ public sealed class PlayerMovementController _body.calc_acceleration(); } + // K-fix10 diag: peak Z tracking — placed AFTER the resolve branch + // so it doesn't disrupt control flow. + if (_jumpDiagSampling && _body.Position.Z > _jumpDiagPeakZ) + _jumpDiagPeakZ = _body.Position.Z; + _wasAirborneLastFrame = !_body.OnWalkable; CellId = resolveResult.CellId; diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 308da9e..1f6e726 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -2380,6 +2380,25 @@ public sealed class GameWindow : IDisposable rm.Body.TransientState &= ~(AcDream.Core.Physics.TransientStateFlags.Contact | AcDream.Core.Physics.TransientStateFlags.OnWalkable); rm.Body.State |= AcDream.Core.Physics.PhysicsStateFlags.Gravity; + + // K-fix10 (2026-04-26): force the Falling animation cycle on + // the remote so the legs match the arc. Mirrors the local + // player's UpdatePlayerAnimation path which sets + // animCommand = Falling whenever !IsOnGround. Without this, + // the remote's existing locomotion cycle (RunForward, + // Ready, etc.) keeps playing through the jump — body goes + // up but legs stay running. Style is the sequencer's + // current style (NonCombat 0x8000003D for humanoids); cycle + // pace = 1.0 (Falling animation has its own baked rate). + if (_entitiesByServerGuid.TryGetValue(update.Guid, out var ent) + && _animatedEntities.TryGetValue(ent.Id, out var ae) + && ae.Sequencer is not null) + { + uint style = ae.Sequencer.CurrentStyle != 0 + ? ae.Sequencer.CurrentStyle + : 0x8000003Du; // NonCombat default + ae.Sequencer.SetCycle(style, AcDream.Core.Physics.MotionCommand.Falling, 1.0f); + } } if (Environment.GetEnvironmentVariable("ACDREAM_DUMP_MOTION") == "1")