Issue #36: direct-axis RELEASE EDGE -- centering an axis now releases its channel
Night-2 (RajelAran): 'the game is not seeing axis release for turn -- the mech may keep turning when control is released.' Root cause: the direct-mode axis write was gated on raw != 0, so an axis returning INSIDE the deadzone just stopped writing and the channel LATCHED at the last deflection -- fatal on channels with no keyboard spring (the Turn composite in the twin-stick profile). Fix: per-binding previous-value tracking (pad + joystick paths); the outside->inside transition writes 0 exactly once, then the centered axis leaves the channel to other inputs (composition preserved; slew bindings excluded by design -- a lever stays where slewed). Plus the #24 rule, axis edition: a pad/stick that DIES while deflected releases its direct channels (throttle keeps the last lever position -- safer than an all-stop mid-fight). Verified: build + 45s glass mission with an idle XInput pad (no spurious writes, no regression). The deflect-release cycle itself needs a human hand -- awaiting RajelAran's next session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0d1507027c
commit
390d4ddce7
@@ -130,6 +130,8 @@ PadRIO::PadRIO():
|
||||
memset(previousKeyHeld, 0, sizeof(previousKeyHeld));
|
||||
memset(previousJoyButtonHeld, 0, sizeof(previousJoyButtonHeld));
|
||||
memset(previousJoyHatHeld, 0, sizeof(previousJoyHatHeld));
|
||||
memset(previousPadAxisRaw, 0, sizeof(previousPadAxisRaw));
|
||||
memset(previousJoyAxisRaw, 0, sizeof(previousJoyAxisRaw));
|
||||
memset(channelValue, 0, sizeof(channelValue));
|
||||
memset(lampState, 0, sizeof(lampState));
|
||||
|
||||
@@ -439,6 +441,19 @@ void
|
||||
}
|
||||
}
|
||||
previousPadButtons = 0;
|
||||
//
|
||||
// Issue #36 (the #24 rule, axis edition): a pad that dies while
|
||||
// DEFLECTED must release its direct channels too -- otherwise
|
||||
// the mech keeps turning/aiming on a dead controller.
|
||||
//
|
||||
for (int a = 0; a < bindings.padAxisBindingCount; ++a)
|
||||
{
|
||||
if (previousPadAxisRaw[a] != 0.0f)
|
||||
{
|
||||
channelValue[bindings.padAxisBindings[a].channel] = 0.0f;
|
||||
previousPadAxisRaw[a] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (padIndex < 0 && (lastPadProbeMilliseconds == 0 ||
|
||||
@@ -672,6 +687,18 @@ void
|
||||
//
|
||||
channelValue[binding.channel] = raw;
|
||||
}
|
||||
else if (previousPadAxisRaw[a] != 0.0f)
|
||||
{
|
||||
//
|
||||
// RELEASE EDGE (issue #36): the axis just came back inside
|
||||
// the deadzone -- write 0 ONCE so its deflection lets go
|
||||
// (channels without a keyboard spring, e.g. Turn, latched
|
||||
// at the last value forever). After this single write the
|
||||
// centered axis leaves the channel to other inputs again.
|
||||
//
|
||||
channelValue[binding.channel] = 0.0f;
|
||||
}
|
||||
previousPadAxisRaw[a] = binding.slew ? 0.0f : raw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,7 +783,21 @@ void
|
||||
const BTJoyDeviceState *state = slotState[binding.device];
|
||||
if (state == 0)
|
||||
{
|
||||
continue; // detached: leave the channel alone
|
||||
//
|
||||
// Issue #36 (the #24 rule): a stick that detaches while
|
||||
// deflected releases its direct channel once. (Throttle
|
||||
// keeps its last lever position -- freezing the lever is
|
||||
// safer than an all-stop on an unplug mid-fight.)
|
||||
//
|
||||
if (previousJoyAxisRaw[a] != 0.0f)
|
||||
{
|
||||
if (binding.channel != PadBindingProfile::ChannelThrottle)
|
||||
{
|
||||
channelValue[binding.channel] = 0.0f;
|
||||
}
|
||||
previousJoyAxisRaw[a] = 0.0f;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
float raw = state->axis[binding.axis];
|
||||
if (binding.deadzone > 0.0f)
|
||||
@@ -797,6 +838,15 @@ void
|
||||
{
|
||||
channelValue[binding.channel] = raw;
|
||||
}
|
||||
else if (previousJoyAxisRaw[a] != 0.0f)
|
||||
{
|
||||
//
|
||||
// RELEASE EDGE (issue #36): outside -> inside the deadzone
|
||||
// writes 0 once -- see the pad-axis twin above.
|
||||
//
|
||||
channelValue[binding.channel] = 0.0f;
|
||||
}
|
||||
previousJoyAxisRaw[a] = binding.slew ? 0.0f : raw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,20 @@ protected:
|
||||
previousJoyButtonHeld[48];
|
||||
unsigned char
|
||||
previousJoyHatHeld[16];
|
||||
//
|
||||
// Direct-axis RELEASE EDGES (issue #36, night-2: "the mech may keep
|
||||
// turning when control is released"). A direct-mode axis write was
|
||||
// gated on raw != 0, so an axis returning INSIDE the deadzone simply
|
||||
// stopped writing -- the channel LATCHED at the last deflection (fatal
|
||||
// on channels with no keyboard spring, e.g. the Turn composite). Track
|
||||
// the previous post-deadzone value per binding: the outside->inside
|
||||
// transition writes 0 exactly once, then leaves the channel to other
|
||||
// inputs (the composition model is preserved).
|
||||
//
|
||||
float
|
||||
previousPadAxisRaw[12]; // parallel to padAxisBindings
|
||||
float
|
||||
previousJoyAxisRaw[24]; // parallel to joyAxisBindings
|
||||
float
|
||||
channelValue[PadBindingProfile::ChannelCount];
|
||||
float
|
||||
|
||||
Reference in New Issue
Block a user