Files
BT411/game/reconstructed/btinput.hpp
T
Joe DiPrimaandClaude Fable 5 e996be249d EJECT/PANIC wired: Mech::EjectPilot (id 0x19 @0049f854) + the crippled-mech gate
The button died twice before reaching game logic: no handler (0x19 was
unregistered) and no sender (the pod's panic was a control bit, not a
mappable). Both halves reconstructed from raw disasm -- the handler AND
its permission evaluator sat in export gaps.

@0049f854 EjectPilot: press-only; gated on ejectPermitted (@0x414) and
!IsDisabled; console eject notice (relay wire = tracked tail; the
suppressConsole@0x258 latch that prevents the death double-notify IS
wired); graphicAlarm -> 10, which kills via the >=9 predicate; then a
self TakeDamage {inflicting=SELF, zone -1, Explosive, amount =
ScenarioRole::killBonus}. Role layout byte-settled via the role reader
@00429bec dest offsets + the ctor record copy: +0x1c IS killBonus, +0x20
is the 4.10-only SpecialCaseDeathPenalty, +0x28 returnFromDeath (all
prior citations reconciled). KillBonus authors NOWHERE in shipped
content -> the charge is 0 and the ALARM does the killing: our bench
outcome is the pod outcome.

@0049fa1c EvaluateEjectPermission: eject only from a CRIPPLED mech --
bank coolant fraction < 0.05 | zero live generators | live weapons
below mech+0x448 (no exported writer: zero, clause inert) | leg-gimped
novice. A healthy mech REFUSES the button; no free resets.

Input: binding-engine "Eject" action -- Backspace / pad LeftThumb
(default profile + shipped CONTROLS.MAP). Bridges per the databinding
rule: weapon/generator/bank/player reads land in their complete TUs;
MechSubsystem gains the both-cells destroyed accessors (gotcha #22).
Bench scalpels: BT_EJECT_AT=<frame> (path-identical synthetic press),
BT_KILL_SUBSYS now takes a comma list.

Verified single-node: healthy press REFUSED (x2), four generators
killed, next press PUNCH-OUT -> death, wreck smoke, respawn; the
respawned mech refuses again (permission re-evaluates after Reset).
Death rides the normal damage/death chain, so MP replication is the
proven path. Tails tracked: console relay notice, RIO 0x38 panic
control, alarm-10 eject audio/canopy, SpecialCaseDeathPenalty consumer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 16:45:54 -05:00

78 lines
3.5 KiB
C++

//#############################################################################
// btinput.hpp -- the desktop input binding engine (CONTROLS.MAP + XInput).
//
// The pod's controls are a fixed physical device set (stick, throttle lever,
// pedals, ~44 lamp buttons, keypads) reaching the game through the
// LBE4ControlsManager push model. On a desktop there is no RIO, so this
// engine maps PC keys and an XInput gamepad onto the SAME channels:
//
// axes -> the virtual-controls integrators in mech4.cpp (lever / turn
// stick / torso twist), exactly where the BT_KEY_BRIDGE writes
// the analog values the RIO would have produced
// buttons -> real buttonGroup[addr] ForceUpdates (aux banks, hotbox,
// panic, throttle-head...) so the AUTHENTIC handlers fire;
// the four joystick fire buttons (0x40/45/46/47) drive the
// bring-up fire channels instead (see btinput.cpp note)
// keypads -> keyboardGroup[KeyboardPilot/KeyboardExternal] key values
// pckey -> keyboardGroup[KeyboardPC] (any authentic '+'-style hotkey)
// actions -> the port-side dev controls (view toggle, all-stop, ...)
//
// Bindings load from content/CONTROLS.MAP at first poll; if the file is
// absent the compiled-in WASD-classic profile (identical text) applies.
// Keys claimed by a binding are SUPPRESSED from the legacy WM_CHAR/-KEYUP
// keyboard feed (BTInputSuppressKey), ending the historic double-dispatch
// (W = drive AND pilot-select, F5 keyup aliasing to 't' = pilot 3, ...).
//#############################################################################
#ifndef BTINPUT_HPP
#define BTINPUT_HPP
struct BTInputState
{
// analog demands (consumed by the mech4 virtual-controls block)
float leverRate; // throttle lever sweep, per second (+fwd/-back)
float turnTarget; // turn-stick deflection target [-1,1]
int turnActive; // any turn input held (else spring-center)
float twistTarget; // torso-twist deflection target [-1,1]
int twistActive;
float elevTarget; // torso-elevation (pitch) target [-1,1]
int elevActive;
int turnAbsolute; // pad stick overrides the integrator outright
int twistAbsolute;
int elevAbsolute;
// the four pod joystick fire buttons (levels)
int fireTrigger; // 0x40 Main
int firePinky; // 0x45
int fireThumbLow; // 0x46 Middle
int fireThumbHigh; // 0x47 Upper
// port-side actions (levels; consumers keep their own edge detectors)
int viewToggle;
int lookBehind;
int allStop;
int modeCycle;
int displayCycle; // Gitea #6: secondary schematic cycle (Damage/Critical/Heat)
int valve;
int flush; // Gitea #7: coolant flush HELD (InjectCoolant)
int configHold;
int eject; // PANIC/EJECT punch-out (level; mech4 edge-dispatches Mech 0x19)
int genSel; // 0 = none, 4..7 = Generator A..D, 8 = reconnect
// Gitea #9: per-MFD preset-page cycle (desktop senders for SetPresetMode;
// index 0/1/2 = Mfd1 lower-left / Mfd2 upper-center / Mfd3 lower-right)
int mfdCycle[3];
};
extern BTInputState gBTInput;
// Poll everything (keys + pad), evaluate the binding table, fire button /
// keypad edges into the controls manager. Call ONCE per sim frame from the
// player drive block. Handles the foreground-focus guard (BT_KEY_NOFOCUS)
// internally.
void BTInputPoll(float dt);
// The legacy keyboard feed asks before dispatching a typed character /
// key-up value: nonzero = this key is claimed by a binding, swallow it.
extern "C" int BTInputSuppressKey(unsigned int key_value, int is_char);
#endif // BTINPUT_HPP