#142 crouch: refuse-and-snap, not queue -- and honour the must-be-stopped rule

Fixes a regression I introduced in 59f53da.  That revision retried the request
when the posture gate was not ready, which is worse than the drop it replaced:
benched, a crouch tapped at a walk QUEUED for 41 seconds (41 [duck] WAITING
lines) and would fire the instant the pilot stopped -- while duckState stayed
1, so the cockpit symbol read "crouched" for the whole time a STANDING mech
walked around.  duckState is what the gauge strip draws; it has to tell the
truth.

Now: if the gate refuses, snap desired back to actual (duckState = duckActual)
and say so once, throttled.

This also confirms the authentic rule rather than assuming it.  Benched, a
crouch pressed while driving gives posture=0 and no squat -- exactly Lynx:
"When a mech STOPS, crouch button lowers its stance."  Immobilization while
crouched looks EMERGENT rather than gated: the leg channel parked in 'sqd'
produces no root motion to travel on, and the operator's read ("i think you
cant walk when you crouch") matches.  No [skate] in the driving case either.

Also guards re-issue: while 'sqd'/'squ' is playing (legAnimationState 2 or 3)
the transition owns the channel, so want != actual no longer re-fires
SetLegAnimation every frame.

Benched both cases:
  stopped  duckState -> 1 (crouch) -> SQUAT -> holds -> -> 0 (rise) -> RISE
  moving   REFUSED (not stopped): posture=0 ... duckState 1 -> 0
           0 squats, 0 queued waits

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCJQkvq6G2JNrpVbA75tVZ
This commit is contained in:
Joe DiPrima
2026-08-07 16:39:05 -05:00
co-authored by Claude Opus 5
parent 59f53da07b
commit fcd1a0ca8d
+32 -15
View File
@@ -4450,9 +4450,13 @@ void
// where mapPosture is not ready RETRIES next frame instead of
// silently dropping the request, which also retires the old
// "request consumed, posture=N" miss.
const int duckWant = (duckState != 0);
const int duckActual = ((int)legStateAlarm.GetLevel() == 1); // parked in 'sqd'
if (squatCapable != 0 && duckWant != duckActual)
const int duckWant = (duckState != 0);
const int duckActual = ((int)legStateAlarm.GetLevel() == 1); // parked in 'sqd'
// Already playing 'sqd'/'squ': the transition owns the channel, so
// do not re-issue it every frame while want != actual.
const int duckInTransit =
(legAnimationState == 2 || legAnimationState == 3);
if (squatCapable != 0 && duckWant != duckActual && !duckInTransit)
{
if (duckWant && mapPosture == 1)
{
@@ -4474,18 +4478,31 @@ void
}
else
{
// Gates not ready THIS frame (posture selector still
// settling). Retry next frame -- do NOT clear duckState:
// it is the gauge's posture source now, and dropping it
// here is what made the request vanish silently.
static int s_duckWait = 0;
if ((s_duckWait++ % 60) == 0)
DEBUG_STREAM << "[duck] WAITING: want=" << duckWant
<< " actual=" << duckActual
<< " posture=" << mapPosture
<< " mode=" << MovementMode()
<< " myo=" << myomerEffectiveness
<< "\n" << std::flush;
// GATE NOT READY -- and the posture selector reads 0 for a
// MOVING mech, which is the authentic rule: Lynx, "when a
// mech STOPS, crouch button lowers its stance". Benched:
// pressing crouch at a walk gives posture=0 and no squat.
//
// So snap the desired posture back to the actual one. An
// earlier revision retried instead, and that was worse than
// what it replaced: a crouch tapped while running QUEUED for
// 41 seconds and would fire the moment the pilot stopped,
// while the cockpit symbol read "crouched" the whole time a
// standing mech walked around. duckState is what the gauge
// strip draws -- it has to tell the truth, and the truth is
// that this mech did not crouch.
if (duckState != duckActual)
{
static int s_duckRefuse = 0;
if ((s_duckRefuse++ % 30) == 0)
DEBUG_STREAM << "[duck] REFUSED (not stopped): posture="
<< mapPosture << " mode=" << MovementMode()
<< " legLvl=" << (int)legStateAlarm.GetLevel()
<< " myo=" << myomerEffectiveness
<< " -- duckState " << duckState << " -> "
<< duckActual << "\n" << std::flush;
duckState = duckActual;
}
}
}