From 59c454ba6cc6d6e4b4c7c35d32e90f7d478e8140 Mon Sep 17 00:00:00 2001 From: Cyd Date: Thu, 16 Jul 2026 21:10:42 -0500 Subject: [PATCH] Cockpit: fix button lamp brightness -- decode the bitfield, subdue the idle baseline The buttons all read fully bright once PadRIO went live. Two causes: 1. The lamp value is a BITFIELD (RP412 LampLevel): bits 0-1 flash mode, bits 2-3 state1 brightness, bits 4-5 state2 (flash alternate). My code treated any non-zero as bright. Now decode it: solid -> state1 level; flashing -> alternate state1/state2 at the flash rate. Level 0=off, 1-2=dim, 3=bright. 2. Diagnosed (BT_LAMP_LOG): the pod lights EVERY mapped button's lamp to a uniform DIM baseline (0x14 = state1Dim+state2Dim, L4Lamp NotifyOfStateChange) -- 'button present, function idle' -- and only an ACTIVE function to bright (0x3C). So the dim baseline is on all buttons; rendering it at RP412's dim red (150,44,28) made the whole field look lit. Darkened off/dim so the idle field reads as unlit dark keys; bright (active) keeps RP412's full values and pops. Verified: idle cockpit shows dark/subdued button keys, not an all-lit field; the lamp decode animates flash modes and lights bright only on an active function. Co-Authored-By: Claude Fable 5 --- engine/MUNGA_L4/L4VB16.cpp | 64 ++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/engine/MUNGA_L4/L4VB16.cpp b/engine/MUNGA_L4/L4VB16.cpp index 2213cbe..89a75ea 100644 --- a/engine/MUNGA_L4/L4VB16.cpp +++ b/engine/MUNGA_L4/L4VB16.cpp @@ -558,18 +558,70 @@ static void BTFillRect(LPDIRECT3DDEVICE9 device, float x, float y, float w, floa device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(CVert)); } +// Lamp byte -> current brightness level (0 off / 1-2 dim / 3 bright), +// animating flash modes -- RP412 MFDSplitView::LampLevel. The lamp state is a +// BITFIELD, not a boolean: low 2 bits = flash mode (solid/slow/med/fast), bits +// 2-3 = state1 brightness, bits 4-5 = state2 (the flash alternate). Treating +// any non-zero value as "lit" made every commanded lamp read fully bright. +static int BTLampLevel(int lamp_state) +{ + int mode = lamp_state & 0x03; + int level1 = (lamp_state >> 2) & 0x03; + int level2 = (lamp_state >> 4) & 0x03; + if (mode == 0) + return level1; // solid: state1 brightness + static const int half_period[4] = { 0, 500, 250, 125 }; + return ((GetTickCount() / half_period[mode]) & 1) ? level2 : level1; +} + +// Fill colour by lamp brightness (ARGB). The pod lights EVERY mapped button's +// lamp to a DIM baseline (0x14) -- "button present, function idle" -- and only +// an ACTIVE function to bright. So the dim baseline must read as an unlit dark +// key (else the whole field looks lit); only bright (level 3) glows. Bright +// keeps RP412's ButtonFill values; off/dim are darkened so the idle field is +// subdued. +static DWORD BTButtonFill(int amber, int level) +{ + if (amber) + { + if (level >= 3) return 0xFFFFD230; // active: RGB(255,210,48) + if (level >= 1) return 0xFF4A3C0E; // dim baseline: subdued + return 0xFF2E2508; // off + } + if (level >= 3) return 0xFFFF482C; // active: RGB(255,72,44) + if (level >= 1) return 0xFF4A140D; // dim baseline: subdued + return 0xFF2E0D08; // off +} + static void BTDrawCockpitButtons(LPDIRECT3DDEVICE9 device, float W, float H) { BTCockpitBtn btn[64]; int count = BTBuildCockpitButtons(W, H, btn, 64); + // BT_LAMP_LOG: dump ALL 64 lamp states + decoded levels a few times so we + // can see what the game actually commands vs what my buttons read. + if (PadRIO::IsActive() && getenv("BT_LAMP_LOG") != NULL) + { + static int s_dumps = 0; + static unsigned long s_last = 0; + unsigned long now = GetTickCount(); + if (s_dumps < 3 && now - s_last > 2000) + { + s_last = now; ++s_dumps; + DEBUG_STREAM << "[lampdump] all 64 lamps:"; + for (int u = 0; u < 64; ++u) + { + int raw = PadRIO::GetLampState(u); + if (raw != 0) + DEBUG_STREAM << " 0x" << std::hex << u << "=0x" << raw + << "(L" << std::dec << BTLampLevel(raw) << ")"; + } + DEBUG_STREAM << "\n" << std::flush; + } + } for (int i = 0; i < count; ++i) { - int lit = PadRIO::IsActive() ? PadRIO::GetLampState(btn[i].unit) : 0; - DWORD color; - if (btn[i].amber) - color = lit ? 0xFFFFB000 : 0xFF3A2A00; // amber lit / dim - else - color = lit ? 0xFFFF2020 : 0xFF3A0808; // red lit / dim + int level = PadRIO::IsActive() ? BTLampLevel(PadRIO::GetLampState(btn[i].unit)) : 0; + DWORD color = BTButtonFill(btn[i].amber, level); // a thin dark border reads as a raised key BTFillRect(device, btn[i].x - 1, btn[i].y - 1, btn[i].w + 2, btn[i].h + 2, 0xFF101010); BTFillRect(device, btn[i].x, btn[i].y, btn[i].w, btn[i].h, color);