CARD C06 - Q3D: split the invented debug_player_color aggregate into the genuine short hs global + update_teleporter's static locals (HCEX names), on top of c3
written: 2026-09-26 01:17:45 -0700 (BEFORE the compile)

Observed discrepancy (pick_game_engine Q3; rule 47 / 'no invented aggregates'): game_engine.c defines one anonymous
struct { short value; word pad; short teleporter_flash_type; word teleporter_flash_pad; real max_intensity;
real_argb_color color; real duration; } debug_player_color owning January's whole 32-byte .data.
Evidence:
- J: update_teleporter loads .data+4 as a WORD (mov dx,word ptr), +8/+0xC/+0x10/+0x14/+0x18/+0x1C as dwords, and
  builds a local screen_flash_definition; get_change_color reads .data+0 as a short.
- X: HCEX update_teleporter has STATIC LOCALS screen_flash_type, max_intensity, alpha, red, green, blue, duration
  (+ blocked_message_delay, fade_function in .bss) and a local `screen_flash`; HCEX's alpha/red/green/blue/duration
  are contiguous and in that order; HCEX game_engine.c has no debug_player_color at all.
- D: /Od 2020 update_teleporter 0x5afba0 reads word [0xa1dfec] (type), [0xa1dff0] max_intensity, [0xa1dff4..0xa1e000]
  alpha/red/green/blue, [0xa1e004] duration - the same contiguous 0x1C-byte block January has at .data+4 - and
  assigns the color COMPONENTS one statement each; debug_player_color is a separate global at 0xa1dfd0 with
  unrelated globals between it and the flash block.
- hs_globals_external.c registers debug_player_color as _hs_type_short_integer.
Proposed correction: `short debug_player_color = NONE;` (file scope, external) and, first in
game_engine_update_teleporter, `static short screen_flash_type = 6; static real max_intensity = 1.0f;
static real alpha = 0.5f; static real red = 0.35f; static real green = 1.0f; static real blue = 0.35f;
static real duration = 1.0f;` (January's word load keeps screen_flash_type short; HCEX later widened it to long);
color assigned per component (alpha, red, green, blue). The two .bss statics are NOT touched here (C07).
Compiler-state prediction: .data = debug_player_color(2)+pad(2)+the seven statics in declaration order = January's
32 bytes; update_teleporter's relocations name the static-local COFF symbols (?name@?1??game_engine_update_teleporter@@9@9)
but resolve to the same .data offsets, so coff_compare (hardened destination resolution) stays EXACT; the 16-byte
struct copy becomes 4 component stores (VC7 emits dword movs for both). C1: the struct tag-less aggregate's 7
members vanish, 7 static names appear in update_teleporter (after populate) -> populate unaffected.
PREDICTION: 180/180; object_audit PASS with 7 candidate-only static symbols; objdiff 3.3.1 .data: NOT 100% (its
symbol pairing sees _debug_player_color 4 bytes vs January's 32) unless symbols.json names the seven statics ->
this is a lead/owner symbols.json question, not a source blocker.
Decisive test: revgate + keyed_diff + object_audit + objdiff mini-project on c4q3d.
