four ENG-page lamps were invisible, not missing: the ctors dropped their colour parameters
Playtest report: "generators don't seem to have an Auto setting" / "no auto mode is ever displayed", plus the operator's own memory that the BUS MODE button once stepped through three states. The state machine was NEVER wrong: @004b0abc has exactly two branches (<2 -> Auto(2); ==2 -> detach + Off(0)), Manual(1) is set only by the four SelectGenerator buttons, and the auto-hunt gates on ==2 -- all byte-verified, and the #62 re-attach fires live. Manual is a one-way door out of BUS MODE by design: a single cycle button cannot know WHICH generator manual should mean. Verified on-screen by the operator this session: A-D returns to manual, BUS MODE toggles auto/off thereafter. What was actually broken: the connect-mode lamp -- and three siblings -- never drew a pixel. OneOfSeveralStates (@004c5470) and OneOfSeveralInt (@004c5148) forward caller-supplied background/foreground colours in the binary; the recon dropped both parameters and hardcoded 0,0 into the base. A BitMap-strip lamp draws SetColor(bg) + DrawBitMapOpaque(fg,...), so 0/0 painted colour-0 on colour-0: invisible. Affected: btemode (connect mode, 1x3 -- THE report), btecmode (coolant on/off, 1x2), and both bteseek gear-step lamps (1x4). The cluster call sites pass 0xff/0, byte-verified (@004c866c disasm; part_014.c:1479-1481, :2146-2147). Same dropped-element family as issue #42's MoveToAbsolute. THE TRAP THAT WAS NOT LANDED, recorded so it stays unlanded: with the lamp first made visible, the frames appeared inverted against the levels (art reads AUTO/MANUAL/OFF top-down; levels run OFF/MANUAL/AUTO), and a row=(rows-1)-selected "fix" was proposed. An adversarial workflow proved it wrong: the gauge blit addresses SOURCE rows BOTTOM-UP (Video16BitBuffered::DrawBitMapOpaque, L4VB16.cpp:3846-3850 -- sTop = map_max_y - sTop, rows walked upward), the 1995 blit @0046bdfc performs the identical flip, and the vertical strips are AUTHORED bottom-up to match. Identity level->row therefore draws the pod-correct display; the inversion would have created the very bug it claimed to cure. Two of five investigators (and the first human pass) assumed top-down; the engine source overruled all three. Convention + warning now recorded in context/gauges-hud.md. btecmode doubles as the standing tripwire: a second vertical strip that must show ON when coolant is available -- if it ever reads inverted, the bottom-up verdict is falsified. Also corrected: @004c552c is OneOfSeveralStates' EXECUTE override (clamp >= 0, chain the base draw) -- the port had the body on BecameActive under a wrong label; vtable-diffed against OneOfSeveral (0x518b24 vs 0x518bf0). BecameActive is inherited. Behaviorally inert today (the state source never goes negative), byte-faithful now. README: the "KNOWN ISSUE -- automatic re-attach is not working yet" text is replaced with how power routing actually works (A-D = manual, BUS MODE = auto then off, two presses off / one back to auto). That text shipped in 600, which already contained the #62 fix -- players were being TOLD auto was broken while it worked, which is half of how a painted-out lamp became "no auto mode". Diagnostic kept: BT_GENSEL_TEST=<id> now drives any of the five power-routing message ids (4..8, default 7) and pulses four times, so the whole mode cycle is observable headlessly under BT_FIRE_LOG ([gensel] lines). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c32d02b3cc
commit
901cf1b459
@@ -1556,8 +1556,8 @@ void
|
||||
//
|
||||
// @004c5390 Make / @004c5470 ctor -- OneOfSeveralStates (vtable PTR_FUN_00518b24):
|
||||
// OneOfSeveral(fromImageStrip=1) + a StateConnection (FUN_004c3324) reading the
|
||||
// subsystem state word @0x14. BecameActive @004c552c clamps the state >= 0
|
||||
// before chaining to OneOfSeveral::Execute @004c4f28. dtor @004c5500.
|
||||
// subsystem state word @0x14. @004c552c is the EXECUTE override (clamp >= 0,
|
||||
// chain OneOfSeveral::Execute @004c4f28); BecameActive is inherited. dtor @004c5500.
|
||||
//
|
||||
|
||||
//
|
||||
@@ -1917,13 +1917,20 @@ void HorizTwoPartBar::Execute()
|
||||
// @004c5148 -- OneOfSeveralInt ctor (vtable 0x518bac): OneOfSeveral(fromStrip=1) +
|
||||
// a GaugeConnectionDirectOf<int> feeding the selected frame. dtor @004c51d8.
|
||||
//
|
||||
// Colours are CALLER-SUPPLIED (fixed 2026-07-28, same dropped-parameter bug as
|
||||
// OneOfSeveralStates): hardcoding 0/0 into the base drew every OneOfSeveralInt
|
||||
// lamp in colour 0 -- btecmode (coolant on/off) and both bteseek gear-step
|
||||
// lamps were invisible. Binary: FUN_004c5148 forwards its args; call sites
|
||||
// pass 0xff/0 (part_014.c:1479-1481 and :2146-2147).
|
||||
OneOfSeveralInt::OneOfSeveralInt(
|
||||
GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in,
|
||||
int graphics_port_number, int x, int y, const char *image,
|
||||
int background_color, int foreground_color,
|
||||
int columns, int rows, int *value_pointer, const char *identification_string
|
||||
):
|
||||
OneOfSeveral(rate, mode_mask, renderer_in, graphics_port_number, x, y,
|
||||
True, image, 0, 0, columns, rows, identification_string)
|
||||
True, image, background_color, foreground_color, columns, rows,
|
||||
identification_string)
|
||||
{
|
||||
selected = 0;
|
||||
AddConnection(new GaugeConnectionDirectOf<int>(0, &selected, value_pointer)); // FUN_004749de
|
||||
@@ -1933,16 +1940,28 @@ OneOfSeveralInt::~OneOfSeveralInt() {} // @004c51d8
|
||||
|
||||
//
|
||||
// @004c5470 -- OneOfSeveralStates ctor (vtable 0x518b24): OneOfSeveral + a
|
||||
// StateConnection reading the subsystem state word @0x14. BecameActive @004c552c
|
||||
// clamps the state >= 0 then chains OneOfSeveral::Execute. dtor @004c5500.
|
||||
// StateConnection reading the subsystem state word @0x14. @004c552c is the
|
||||
// EXECUTE override (clamp >= 0, chain the base); BecameActive is inherited.
|
||||
// dtor @004c5500.
|
||||
//
|
||||
// THE COLOURS ARE CALLER-SUPPLIED (fixed 2026-07-28). This ctor used to drop
|
||||
// them and hardcode 0/0 into the base. For a BitMap strip Execute does
|
||||
// `SetColor(backgroundColor)` then `DrawBitMapOpaque(foregroundColor, ...)`, so
|
||||
// 0/0 draws the lamp in colour 0 and it is invisible -- which is why the ENG
|
||||
// page's connect-mode lamp (btemode.pcc) never appeared and pilots reported
|
||||
// "there is no Auto setting": the mode was cycling correctly underneath an
|
||||
// unpainted lamp. The binary's ctor @004c5470 forwards its own arguments to
|
||||
// the base (only fromImageStrip is a literal 1), and its eng-page caller
|
||||
// @004c866c pushes `0xff` then `0` -- background 0xFF, foreground 0.
|
||||
OneOfSeveralStates::OneOfSeveralStates(
|
||||
GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in,
|
||||
int graphics_port_number, int x, int y, const char *image,
|
||||
int columns, int rows, Entity *subsystem_source, const char *identification_string
|
||||
int background_color, int foreground_color, int columns, int rows,
|
||||
Entity *subsystem_source, const char *identification_string
|
||||
):
|
||||
OneOfSeveral(rate, mode_mask, renderer_in, graphics_port_number, x, y,
|
||||
True, image, 0, 0, columns, rows, identification_string)
|
||||
True, image, background_color, foreground_color, columns, rows,
|
||||
identification_string)
|
||||
{
|
||||
selected = 0;
|
||||
AddConnection(new StateConnection(&selected, subsystem_source)); // FUN_004c3324
|
||||
@@ -1950,11 +1969,16 @@ OneOfSeveralStates::OneOfSeveralStates(
|
||||
|
||||
OneOfSeveralStates::~OneOfSeveralStates() {} // @004c5500
|
||||
|
||||
void OneOfSeveralStates::BecameActive() // @004c552c
|
||||
// @004c552c -- the EXECUTE override (corrected 2026-07-28; the port had this
|
||||
// body on BecameActive). The binary's OneOfSeveralStates vtable was dumped and
|
||||
// diffed against OneOfSeveral's: the one differing slot is Execute, whose body
|
||||
// clamps a negative state to 0 and chains the base draw. On the base class
|
||||
// BecameActive just forces a redraw (previousSelected = -1) -- inherited.
|
||||
void OneOfSeveralStates::Execute() // @004c552c
|
||||
{
|
||||
if (selected < 0)
|
||||
selected = 0;
|
||||
OneOfSeveral::BecameActive();
|
||||
OneOfSeveral::Execute();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user