Compare commits

..

No commits in common. "0ecd4f34ae852d6270ffe4e57a94e091a47d89b2" and "94e9cbcf76902644f229f8444b11a6c90dc5ed00" have entirely different histories.

3 changed files with 11 additions and 39 deletions

3
.gitignore vendored
View file

@ -38,6 +38,3 @@ __pycache__/
# Per-session scratch (Claude commit message drafts, ad-hoc temp files)
tmp/
# Git worktrees for isolated feature work
.worktrees/

View file

@ -52,13 +52,6 @@ public readonly record struct MovementResult(
float? TurnSpeed,
bool IsRunning = false,
uint? LocalAnimationCommand = null, // which cycle to play on the local player (RunForward when running)
// K-fix5 (2026-04-26): cycle-pace multiplier for the LOCAL animation
// sequencer. Decoupled from ForwardSpeed so the wire can keep sending
// 1.0 for WalkBackward (ACE-compatible) while the animation plays at
// runRate × so the cycle visually matches the run-speed velocity.
// Forward+Run = runRate (same as ForwardSpeed); Backward+Run, Strafe+Run
// = runRate (where ForwardSpeed is 1.0 / null); everything else = 1.0.
float LocalAnimationSpeed = 1f,
bool JustLanded = false, // true on the single frame we transitioned airborne → grounded
float? JumpExtent = null, // non-null when a jump was triggered this frame
Vector3? JumpVelocity = null); // world-space launch velocity (sent in jump packet)
@ -563,18 +556,6 @@ public sealed class PlayerMovementController
HeartbeatDue = false;
}
// K-fix5 (2026-04-26): local-animation-cycle pacing. Visual rate
// should match the actual movement speed. For Forward+Run this is
// already runRate (it equals ForwardSpeed). For Backward+Run and
// Strafe+Run it must be runRate too even though the wire keeps
// those at 1.0. Picking runMul (already computed above) keeps the
// math in one place.
bool anyDirectional = input.Forward || input.Backward
|| input.StrafeLeft || input.StrafeRight;
float localAnimSpeed = (input.Run && anyDirectional)
? (_weenie.InqRunRate(out float vrrAnim) ? vrrAnim : 1f)
: 1f;
return new MovementResult(
Position: Position,
CellId: CellId,
@ -588,7 +569,6 @@ public sealed class PlayerMovementController
TurnSpeed: outTurnSpeed,
IsRunning: input.Run && input.Forward,
LocalAnimationCommand: localAnimCmd,
LocalAnimationSpeed: localAnimSpeed,
JustLanded: justLanded,
JumpExtent: outJumpExtent,
JumpVelocity: outJumpVelocity);

View file

@ -4726,12 +4726,7 @@ public sealed class GameWindow : IDisposable
// command is unchanged but speed changed, we must still propagate
// so the sequencer can MultiplyCyclicFramerate — keeping the run
// loop smooth without restart.
// K-fix5 (2026-04-26): use LocalAnimationSpeed (cycle pace) NOT
// ForwardSpeed (wire field) — backward+run + strafe+run keep
// ForwardSpeed/SidestepSpeed at 1.0 for ACE compatibility but
// need the local cycle to play at runRate × so the animation
// matches the actual movement velocity.
float newSpeed = result.LocalAnimationSpeed;
float newSpeed = result.ForwardSpeed ?? 1f;
bool sameCmd = animCommand == _playerCurrentAnimCommand;
bool sameSpeed = MathF.Abs(newSpeed - _playerCurrentAnimSpeed) < 1e-3f;
if (sameCmd && sameSpeed) return;
@ -4786,19 +4781,19 @@ public sealed class GameWindow : IDisposable
// Sequencer path: SetCycle handles adjust_motion internally
// (TurnLeft→TurnRight with negative speed, etc.)
//
// Speed scaling: K-fix5 (2026-04-26) — use LocalAnimationSpeed
// (the PlayerMovementController-computed cycle pace) instead of
// the wire ForwardSpeed. Forward+Run = runRate; Backward+Run =
// runRate (where ForwardSpeed is the ACE-compatible 1.0);
// Strafe+Run = runRate (where SidestepSpeed is 1.0). Anything
// not in run = 1.0. The animation cycle now visually matches
// the movement velocity in every direction.
// Speed scaling: use the MovementResult's ForwardSpeed for
// locomotion cycles. This mirrors what the server broadcasts for
// remote observers, and keeps our own character's animation rate
// in sync with movement velocity (a 1.5× RunRate player's anim
// plays 1.5× as fast — matching retail).
if (ae.Sequencer is not null)
{
uint fullStyle = 0x80000000u | (uint)NonCombatStance;
float animSpeed = result.LocalAnimationSpeed > 0f
? result.LocalAnimationSpeed
: 1f;
float animSpeed = 1f;
if (result.ForwardSpeed is { } fs && fs > 0f)
{
animSpeed = fs;
}
// ACDREAM_ANIM_SPEED_SCALE: optional visual-pacing knob. Retail's
// animation framerate scales linearly with speedMod (r03 §8.3),
// and our speedMod = runRate. If the visual feel doesn't match