fix(anim): walk-backward leg-twitch + jump-too-low — port ACE negative-speed link path + faster charge
Two animation/movement issues from live verification:
1. Walk-backward leg twitches forward two times before the cycle
reverses (X key glitch).
Root cause: AnimationSequencer.GetLink only implemented the
forward-direction lookup path. ACE's MotionTable.get_link
(MotionTable.cs:395-426) takes BOTH the substate and the new
motion's speeds, and switches lookup branches when EITHER speed
is negative:
* Forward path: Links[(style<<16) | substate][motion]
* Reversed path (any negative speed): Links[(style<<16) |
motion][substate]
For Ready → WalkBackward we adjust_motion to WalkForward at
speed -0.65 (negative). Our previous code looked up
Links[Ready][WalkForward] — the "start walking forward"
transition. Played in reverse, the cursor stranded at the
wrong cycle frame and produced the user-visible "left leg
twitches forward two times" before the cycle stabilized.
With the reversed key Links[WalkForward][Ready] (the "stop
walking → ready" anim) played at the cycle's negative speed,
the link smoothly transitions Ready → start-of-cycle, then
the cycle reverses cleanly.
GetLink signature changed from (style, fromMotion, toMotion)
to (style, substate, substateSpeed, motion, speed). Both
call sites updated: SetCycle passes CurrentSpeedMod +
adjustedSpeed; the Action-overlay path passes 1f, 1f
(action overlays are always forward).
2. Jump too low.
Two changes after deep investigation in named-retail decomp:
a) Charge rate sped up from 1.0/s → 2.0/s. Retail's PowerBar
charge constant is illegible in the named decomp (the
divisor was clobbered in GetPowerBarLevel's FPU stack
reordering at 0x0056ade0). 2.0/s (full charge in 0.5s)
matches retail muscle memory better — a tap gives a
noticeable hop, half-hold a meaningful jump, full-hold
the maximum.
b) Default jumpSkill bumped 200 → 300. Retail formula:
height = (skill / (skill + 1300)) × 22.2 + 0.05
At extent=1.0:
skill=200 → 3.01m max (felt too low)
skill=300 → 4.21m max (closer to retail mid-tier "I
can clear that fence" hop)
Override via ACDREAM_JUMP_SKILL env var.
Long-term fix is issue #7 — parsing PlayerDescription's
skill block to apply the server's authoritative skill
values. Until then, this default is the right baseline.
(Velocity formula sqrt(height × 19.6) is unchanged and
matches retail byte-for-byte; we only changed how much
extent-feeding skill we default to.)
Tests stay 1222 green. The walk-backward fix has no new test
because GetLink is private; the cycle-transition behavior
will be exercised live.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0ecd4f34ae
commit
32583cdfe4
2 changed files with 88 additions and 23 deletions
|
|
@ -130,7 +130,15 @@ public sealed class PlayerMovementController
|
|||
// Jump charge state.
|
||||
private bool _jumpCharging;
|
||||
private float _jumpExtent;
|
||||
private const float JumpChargeRate = 1.0f; // 0→1 over 1 second
|
||||
// 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
|
||||
// (full charge in 0.5s) feels matches retail muscle memory better
|
||||
// than the previous 1.0/s — a tap gives a noticeable hop, half-hold
|
||||
// a meaningful jump, full-hold the maximum extent. The vertical
|
||||
// velocity formula itself (height × 19.6 → vz) is unchanged and
|
||||
// matches retail byte-for-byte; only the time-to-fill is faster.
|
||||
private const float JumpChargeRate = 2.0f;
|
||||
|
||||
// Airborne → grounded transition detection. Flipped on every frame where
|
||||
// the body transitions from airborne to on-walkable; used by the GameWindow
|
||||
|
|
@ -159,12 +167,19 @@ public sealed class PlayerMovementController
|
|||
State = PhysicsStateFlags.Gravity | PhysicsStateFlags.ReportCollisions,
|
||||
};
|
||||
|
||||
// Default skills — tuned toward mid-retail feel (jump ≈ 3m at full charge,
|
||||
// run rate ≈ 2.4x). Real characters' skills come from PlayerDescription
|
||||
// (0xF7B0/0x0013) which we don't parse yet; override via env vars:
|
||||
// Default skills — tuned toward mid-retail feel. Real characters'
|
||||
// skills come from PlayerDescription (0xF7B0/0x0013) which we don't
|
||||
// parse yet; override via env vars:
|
||||
// ACDREAM_RUN_SKILL, ACDREAM_JUMP_SKILL
|
||||
// K-fix6 (2026-04-26): bumped default jump skill from 200 → 300.
|
||||
// Retail formula: height = (skill/(skill+1300))*22.2 + 0.05 (extent=1):
|
||||
// skill=200 → 3.01m max (felt too low — user complaint)
|
||||
// skill=300 → 4.21m max (closer to a typical retail mid-tier
|
||||
// character's "I can clear that fence" hop)
|
||||
// Until #7 ships and PlayerDescription gives us the server's real
|
||||
// skill, this default is the right "feels like retail" baseline.
|
||||
int runSkill = int.TryParse(Environment.GetEnvironmentVariable("ACDREAM_RUN_SKILL"), out var rs) ? rs : 200;
|
||||
int jumpSkill = int.TryParse(Environment.GetEnvironmentVariable("ACDREAM_JUMP_SKILL"), out var jsv) ? jsv : 200;
|
||||
int jumpSkill = int.TryParse(Environment.GetEnvironmentVariable("ACDREAM_JUMP_SKILL"), out var jsv) ? jsv : 300;
|
||||
_weenie = new PlayerWeenie(runSkill: runSkill, jumpSkill: jumpSkill);
|
||||
_motion = new MotionInterpreter(_body, _weenie);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue