feat(R3-W4): ground transitions + lifecycle verbatim; K-fix18 DELETED (closes J8, J10, J11-shape, J12, J13, J18, J19)

Core (dedicated agent, independently reviewed): HitGround 0x00528ac0 /
LeaveGround 0x00528b00 verbatim (creature+gravity gates, the
RemoveLinkAnimations seam — K-fix18's retail mechanism — velocity via
GetLeaveGroundVelocity with the autonomous flag, jump-state resets,
apply_current_movement re-sync); enter_default_state 0x00528c80 per A8
(fresh states, InitializeMotionTables seam, sentinel APPENDED without
draining pending_motions — pinned, Initted=1, LeaveGround tail);
Initted gates; the A3 IsThePlayer dual dispatch in
apply_current_movement / ReportExhaustion / SetWeenieObject /
SetPhysicsObject (a remote player routes INTERPRETED — the
ACE-divergence pin); set_hold_run 0x00528b70 + SetHoldKey 0x00528bb0
(XOR guard, None-only-from-Run); adjust_motion creature guard wired
(TS-34 retired); PhysicsBody.LastMoveWasAutonomous +
set_local_velocity(autonomous). Port discovery: retail's
apply_raw_movement 0x005287e0 / apply_interpreted_movement 0x00528600
ARE the already-shipped D6.2a/funnel functions — the dual dispatch
composes them instead of duplicating.

App cutover (orchestrator): the skipTransitionLink flag + both K-fix18
call sites DELETED (AP-74 retired). MotionInterpreter.DefaultSink routes
apply_current_movement's interpreted branch through the REAL funnel
dispatch when a sink is bound — so a remote's LeaveGround engages
Falling via the contact-gated funnel, replacing the forced SetCycle
(J19); the per-remote MotionTableDispatchSink is now PERSISTENT
(EnsureRemoteMotionBindings: DefaultSink + RemoveLinkAnimations +
InitializeMotionTables seams, idempotent from both the UM and
VectorUpdate paths; wire velocity re-applied after LeaveGround so it
stays authoritative). Player: seams bound to the player sequencer; the
controller's grounded→airborne EDGE now fires LeaveGround (jump()
clears OnWalkable and the same frame's transition detection fires it —
retail's order; walk-off-a-ledge gets the momentum fallback + link
strip it never had); the manual jump-block LeaveGround deleted;
LastMoveWasAutonomous set at the controller chokepoint (W6 refines).

Trace S8 re-expressed as the retail mechanism (Falling dispatch +
RemoveAllLinkAnimations = same final state the flag produced). 43 new
lifecycle tests. Registers: TS-34 + AP-74 retired; TS-38, AP-77, AP-78
added. Full suite: 3,665 passed. Live smoke: in-world clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-02 23:01:31 +02:00
parent af4764443f
commit e214acdf23
8 changed files with 1575 additions and 124 deletions

View file

@ -40,7 +40,7 @@ public sealed class DatCollectionLoader : IAnimationLoader
// see CSequence.cs/AnimSequenceNode.cs/FrameOps.cs). This adapter keeps every
// public member's signature (the API-migration table in the gap map is the
// contract) and re-expresses each one over the core. Invented mechanisms that
// are still R2/R3 scope (K-fix18 skipTransitionLink, Fix B locomotion
// were R2/R3 scope (K-fix18 skipTransitionLink DELETED in R3-W4, Fix B locomotion
// link-skip, stop-anim fallback, GetLink's reversed branch, velocity/omega
// synthesis constants) SURVIVE unchanged at the adapter level per the gap
// map's "Invented behaviors NOT in the gap list" note.
@ -339,15 +339,7 @@ public sealed class AnimationSequencer
/// <param name="style">MotionCommand style / stance (e.g. NonCombat 0x003D0000).</param>
/// <param name="motion">Target motion command (e.g. WalkForward 0x45000005).</param>
/// <param name="speedMod">Speed multiplier applied to framerates (1.0 = normal).</param>
/// <param name="skipTransitionLink">K-fix18 (2026-04-26): when true, do
/// NOT enqueue the transition-link frames between the previous and
/// new cycle. Used when the caller wants the new cycle to engage
/// instantly — e.g. swapping to Falling on a jump start, where the
/// RunForward→Falling link is a short "stop running" pose that
/// makes the jump look delayed (legs stand still for ~100 ms while
/// the link drains, then fold into Falling). Defaults to false to
/// preserve normal smooth transitions for everything else.</param>
public void SetCycle(uint style, uint motion, float speedMod = 1f, bool skipTransitionLink = false)
public void SetCycle(uint style, uint motion, float speedMod = 1f)
{
EnsureInitialized();
@ -394,17 +386,11 @@ public sealed class AnimationSequencer
uint dispatchResult = PerformMovement(
MotionTableMovement.Interpreted(adjustedMotion, adjustedSpeed));
// K-fix18 (2026-04-26, register row survives → R3): instant-engage
// cases (Falling on jump start) strip the just-selected transition
// links so the new cycle starts immediately. Post-Q4 expression:
// the retail primitive CPhysicsObj::RemoveLinkAnimations
// (0x0050fe20 → CSequence::remove_all_link_animations) — exactly
// what R3-W4's LeaveGround will call, at which point this flag and
// its GameWindow call sites are deleted. The pending-queue entry's
// tick count intentionally stays (retail's RemoveLinkAnimations
// does not touch the manager queue; the countdown chain absorbs it).
if (skipTransitionLink)
_core.RemoveAllLinkAnimations();
// R3-W4: the K-fix18 skipTransitionLink flag is DELETED — the
// instant-Falling engage is now retail's own mechanism: the entity's
// MotionInterpreter.LeaveGround (0x00528b00) fires the
// RemoveLinkAnimations seam (bound to this sequencer's
// RemoveAllLinkAnimations) when the body leaves the ground.
// Failed dispatch (missing cycle for this style, is_allowed reject):
// retail leaves sequence AND state untouched — no synthesis either.
@ -446,6 +432,18 @@ public sealed class AnimationSequencer
}
}
/// <summary>
/// R3-W4: the retail link-strip primitive
/// (<c>CPhysicsObj::RemoveLinkAnimations</c> 0x0050fe20 →
/// <c>CSequence::remove_all_link_animations</c> 0x00524ca0). The App
/// binds the entity's <c>MotionInterpreter.RemoveLinkAnimations</c> seam
/// here so HitGround/LeaveGround strip pending transition links exactly
/// where retail does. The pending-queue tick counts intentionally stay
/// (retail's primitive does not touch the manager queue; the countdown
/// chain absorbs it).
/// </summary>
public void RemoveAllLinkAnimations() => _core.RemoveAllLinkAnimations();
/// <summary>
/// R2-Q5: run retail's <c>enter_default_state</c> analog now if it
/// hasn't run yet (idempotent). Spawn paths call this so an entity with