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 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-16 21:10:42 -05:00
co-authored by Claude Fable 5
parent 5b8c814029
commit 59c454ba6c
+58 -6
View File
@@ -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);