Author SHA1 Message Date
CydandClaude Opus 4.8 1f54592595 docs: the maps page (docs/MAPS.html + generator)
Companion page to MECHS.html: every map in BTL4.RES drawn the way the
cockpit draws it (tools/build_maps.py + tools/maps.tpl -> docs/MAPS.html).
Each plan is rendered from the map's own Entity::MakeMessage stream --
every placement, the rtype-18 GaugeImage outline its Model List carries,
in the game's own btspal.pcc palette.  Nothing is traced or redrawn.
The eight playable maps lead; supporting streams (arena geometry, drop
points, wind emitters) are labeled and filterable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 21:06:15 -05:00
CydandClaude Opus 4.8 150492054a docs: the mech roster page (docs/MECHS.html + generator)
An HTML roster of all 18 selectable mechs in the VTV-PRESETS layout style:
per-mech portrait, loadout chips, manual stat sheet, and the authored
weapons/ammo table, with filter (all/base/variant), search and live count.

Nothing is transcribed by hand where the game can be read directly
(tools/build_mechs.py + tools/mechs.tpl -> docs/MECHS.html):

  - ROSTER: kVehicles[] parsed from game/glass/btl4fe.cpp -- the exact
    18-entry catalog the menu offers, in menu order (8 base chassis + 10
    variants).
  - WEAPONS + AMMO BINS: each mech's cmCrit(...) lines in
    content/GAUGE/L4GAUGE.CFG (the critical-schematic's authored subsystem
    roster; independently cross-checked against the rtype-17 subsystem
    records in BTL4.RES, 2026-08-12 -- all 18 agree).
  - PORTRAITS: the game's own mission-review images (GAUGE/<stem>_MR.PCC,
    PCC=PCX) resolved through mrpal.pcc with the zone slots (32..63) held
    at adpal entry 0 -- the review screen's undamaged green.  Variants
    without their own portrait share the base's, as the RES does.
  - MECH STATS: the original player's manual stat sheets, transcribed [T1]
    from page renders (pp. 25 Loki/Thor, 26 Vulture/MadCat, 29
    Owens/Blackhawk).  The manual covers the six 4.0 hulls only; Avatar,
    Sunder and the variants show a dash, never an invented number.
  - FIXED vs TWISTING torso: derived from the shipped RES joint tables
    (jointtorso/jointtshadow presence; torso.cpp:270 gate) -- blkhawk,
    bhk1, owens, own1 are fixed, matching the manual's Torso 0/0 rows.

Rendered + verified headless (top and bottom, all 18 cards, filters wired).
Not yet shipped by mkdist (MAPS.html is not either); add a block modelled
on the CONTROLS.html one if testers should get it in the zip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 21:00:11 -05:00
30 changed files with 1934 additions and 717 deletions
-16
View File
@@ -1328,19 +1328,3 @@ record shipped penalty-off for entire steam nights, #162); (3) the bench for ANY
here is scratchpad/night15/kd_bench.sh -- five HARD invariants (deaths==increments,
cost-per-death, credit-per-kill, zero swallowed, arithmetic witness); its predecessor's
assertions were greps that matched nothing, which is how both halves shipped broken.
## Weapon destroyed-state: the ONE-cell contract (#165 root cause, night-16) [T1]
Binary: subsystem `+0x40` (statusAlarm level) is THE destroyed cell -- all writers (base
TakeDamage @0x4ac0bc zone-saturation tail; cascade descent @0049c9a8; slot-8 pump watchdog),
all readers (Emitter gate @004baa88 `==1` strict; small-dial X @004c7844; techstat; the wire
header). Four alarm cells exist per weapon and are NOT interchangeable: +0x40 status/health,
+0x184 temperature (gate clause `==2`), +0x278 power feed (eng-page X only!), +0x364 weaponAlarm
FSM (level 7 "unavailable" = AMMO weapons only, @004bb9b8/@004bbd04 -- the #30 answer). The
eng-page energy X (@004c8990) draws on `+0x40==1 OR power!=4`: an X over a LOADED emitter that
legally fires its one loaded shot is AUTHENTIC binary behavior (power-fault case). Destroyed
emitters park at weaponAlarm=3 (Loading) via the gate's per-frame refusal -- "recharging under
the X" is authentic. Replication: weapon statusAlarm NEVER crosses the wire; destroyed-ness
reaches peers via ZONE records only; replicant emitters run a gateless Simulate (@004ba8d0) --
fire legality is master-only. Port fix + the four-store split it repaired: [[reconstruction-gotchas]] SS30;
bench: `scratchpad/night16/weaponx_bench.sh` (crit-path kill + partial-crit freeze regression).
-38
View File
@@ -826,41 +826,3 @@ classes (PlayerStatus/pilotList/...) with the config interpreter, which let
more of the authored page furniture parse + draw than before -- the leaking
high-index pixmaps rode in with that. [T3 for this correlation detail; the
leak itself and its fix are T2 live-verified.]
## Coolant-leak LAMP ROUTING (#172, night-16 hunt 2026-08-13) [T1 resolver + T2 field]
The leak lamp chain is INTACT -- every staged leak all night (35 events, 4 machines, incl. the
"fatal" case) asserted its lamp the SAME FRAME as the [techstat] cond-2 SET. The field reports
were three different presentation facts:
- **Generator leak -> the radar-rail generator button ONLY** (kBTGeneratorLamp @0051d070; both
authored item codes 0x80+0x87 collapse to that one lamp, mode 0xffffffff = the ALWAYS-ACTIVE
sentinel, never mode-gated). NO MFD carries any generator-leak indicator -- binary-authentic
(@004cc2fc). In glass surround that rail cell renders at the minimumStrip floor (~6px,
L4RIOBANK.cpp) -- Elengil's fatal lamp FLASHED for 28.4s. ~~"effectively invisible"~~
**CORRECTED SAME DAY (user challenge upheld, bench-verified):** the strip PAINTS AND BLINKS
correctly -- [lampblink] receipts show the painted shade alternating at a metronomic 125ms,
full frame rate, in a loop code-identical to 913's; Elengil's layout receipts show the stock
surround with the lamp on screen (~126px wide x ~6px strip @4Hz). The failure is
EXPECTATION, not visibility: testers watch the MFDs, and no MFD carries a generator-leak
indicator (authentic). Resolution: the handout's WHERE-TO-LOOK entry; the ALERT RING
deviation exists but is GATED OFF (BT_ALERT_RING=1) pending Friday evidence that testers
miss the lamp even knowing its location. Receipts: [lamppx] alert edges, [lampblink] shade
transitions (BT_LAMP_LOG), [seqloop] voice Start/Stop. Era question for Oracle: how
prominent was the pod's physical generator button light?
- **Condenser (loop) leak -> the per-loop button** (@0051d058, 1-based '2F 2E 2D 2B 2A 29';
the 0x2c gap is AUTHENTIC -- 0x2c belongs to the DISTINCT coolingLoop set of the code<0x80
path). Also always-active. No gate, immediate flash.
- **Weapon leak (AFC50 family) -> PAGE-SCOPED lamps only** (quad-select + its eng-page button):
the mode gate (@00444e64 == LAMP.cpp) skips the notify while the page is hidden and the
page-entry force-notify delivers it when the player pages there -- "flash appeared at the
second voice line" = the player paging to engineering after voice loop #1. AUTHENTIC;
documented for testers, not fixed. Coolant-cooled guns leak in PAIRS (weapon bit + serving
condenser bit) -- one page-gated lamp, one always-active loop lamp.
- **The VOICE is a LOOPING sequence** (0E4ACE50, tempo 160): Start on the coolantActive 0->1
edge, self-repeats, Stop on 1->0 (the #136 model REFINED: edge-started, level-looped,
edge-stopped; hysteresis flap = restart). Voice repetitions carry NO edge information --
the field testers used the voice as a clock and mis-dated the lamp. Receipt: [seqloop]
START/STOP, always-on for looped sequences.
- Authored no-lamp facts (from the mechalrm item census, [galm-item]): conditions 1 (Damaged)
and 3 (Overheating) have NO lamp items for ANY subsystem; cond 0 (Destroyed) resolves lamps
only for weapon-slot subsystems. A heat alarm never flashes a cockpit button -- authored.
-51
View File
@@ -1194,54 +1194,3 @@ DATAPOINT (2026-08-11, the #162 retest wave): the kd4/kd8 benches audit this exp
and saw **0 inst=R DMG rows across three 4-node runs and one 8-node melee** (35 deaths total)
on build ~900 -- the behavior did not reproduce on the bench rig; whatever produced the
night-15 field rows needs field conditions (steam wire seam?) the LAN relay path lacks.
## Night-16 playtest intelligence (build 4.11.913, 2026-08-12) [T2 player testimony + log receipts]
Field-verified CLOSED this night: #162 (K/D exact all night, 3 independent confirms), #156
(fade), #154 (gen voice at trip), #135 (leak flashes the right lamp), #169 (lobby layout),
#128 (no ram leaks; ram crits land on gyro/torso progressively, myomer crits unobtainable --
consistent with internals-only, see #103). Logs: `scratchpad/night16/` + Oracle notes.
- **#170 -- post-mission menu-relaunch crash (TOP defect).** `[fe] mission over -- relaunching
the menu` -> `[crash] UNHANDLED EXCEPTION code=0xc0000005 addr=0x0 access=8 target=0x0`.
7 hits / 3 machines / identical module-relative stack, all frames in a Steam-family DLL;
a NULL function-pointer call dispatched by a Steam callback across the mission-teardown ->
menu-relaunch boundary. Intermittent (10 relaunches -> 3 crashes in one log). [T4 hypothesis:
callback left registered across teardown; possibly a #163/#156 teardown-ORDER regression.]
Players report it as "crash while joining lobby" -- it is not the join.
**ROOT-CAUSED + FIXED same night [T2]:** the first #163 fix's `SteamAPI_Shutdown()` inside
`BTSteamNet_ShutdownAll` raced steamclient's service thread (round-end = a burst of peer
closes always in flight). ShutdownAll is now a QUIESCE (no Shutdown; linger closes; seam
gated first) and every exit path dies via TerminateProcess -- see [[steam-networking]]
§THE EXIT CONTRACT. Field verify Friday; the crash filter now names foreign DLL frames.
- **#171 -- ballistic/missile hitscan era question (HOLD until group consensus).** Era witnesses
(Oracle, Lynx, Sauron; Dplotta softer; Draco DISSENTS) recall AFC/Gauss as instant-hit,
no lead, no flight animation, single-panel damage, no splash. 913 field observations: locked
AFC50 misses a perpendicular mover unless led; ballistics AND SRM/Streak/Narc hit beyond the
rangefinder max without limit; energy weapons DO stop damaging at rangefinder max (=> at least
one unmapped range-gating mechanism exists). The projectile flight clocks are [T1] from the
decomp (#168) -- reconcile: hit-resolved-at-trigger with cosmetic projectile? speeds/ranges
off? undiscovered range gate? Pod-hardware ground truth possible end of month.
- **#172 -- leak lamp first-flash delayed/absent.** Elengil: flash only on the SECOND voice line
(x2 spawns, AFC50/Zanin Neko); once ABSENT for an entire fatal leak (5-6 voice repetitions,
no flash on any MFD, died ~9:38PM CT -- the match's only death, receipt findable in night16).
Distinct from the closed #135: this is the SET-edge / first-flash arming path.
- **#173 -- chicken-walker seek-4 myomer overheat** (see [[subsystems]] night-16 note).
- **#165 ROOT-CAUSED + FIXED + BENCHED (2026-08-13, the 4-agent hunt):** gotcha SS30 -- the
binary's ONE destroyed cell was split into four port stores and the crit-lottery kill path
(MechWeapon::TakeDamage) wrote none the gate reads. Fixed (chain to MechSubsystem::TakeDamage,
FSM tail deleted, phantom simulationState initialized + bridged); weaponx_bench PASS both
phases. RESIDUAL UNKNOWNS from the hunt: (i) Elengil's 20:17 ET no-X facet needs ALIA's
PRE-ROTATION steam_20260812.log (ask Elengil -- staged file starts 22:20 ET); (ii) cascade
re-descend re-entrancy (zone 18 x7 re-rolls, own ticket); (iii) incident-(b) "no damage on
target": shooter log computed FULL damage (11.76) -- wire/victim-side loss, needs paired
matchlogs (fleet-wide BT_DMG_LOG + matchlog next night would make this attributable).
- **#172 RESOLVED (2026-08-13, 4-agent hunt): the lamp chain was NEVER broken** -- all 35
staged leak events flashed same-frame. Three presentation facts: generator lamp = the ~6px
radar-rail strip in glass (fatal case: flashed 28.4s unseen -> ALERT BLOOM deviation);
weapon-leak lamps are page-gated AUTHENTICALLY (the "delayed" flash = the player paging to
engineering); the leak voice LOOPS off one edge (testers' clock was wrong). See
[[gauges-hud]] SSCoolant-leak LAMP ROUTING. Receipts armed: [seqloop], [lamppx]. Residual:
Lynx's loop-6 report unstaged (his machine, no log); Elengil's AFC50 pre-rotation log still
wanted for the pair-leak confirmation.
- Nanook lost the coolant MFD glass window, recovered only by config edit -> #76 comment
(in-game layout-reset recovery path).
-50
View File
@@ -1014,53 +1014,3 @@ sampled a cell whose write timing differs from the binary's". Fix shape: run the
transition inline at the destruction edge (mech.cpp death_tail → `UpdateDeathState(0)`), keep the
per-frame caller for maintenance. Verified: kd4_bench 9/9/9/9 + kd8_stress 26/26/26/26, both
SWALLOWED=0 (the receiver dedup tripwire now never fires — no duplicate is ever GENERATED).
## 30. ONE binary cell, FOUR port stores: the destroyed-state split that survived three "fixes" (#165, night-16 2026-08-13)
The binary keeps ONE destroyed cell per subsystem: `+0x40` = the statusAlarm level -- written by
EVERY destruction path (base TakeDamage @0x4ac0bc at private-zone saturation; zone-cascade
descent @0049c9a8; the per-frame watchdog in the slot-8 message pump), read by EVERY consumer
(the Emitter fire gate @004baa88, the small weapon-dial X @004c7844, the techstat scan, the wire
via the Simulation record header). The port split that cell into FOUR stores that never talked:
the private DamageZone.damageLevel (drives the X), the ReconAlarm statusAlarm (gate clause 2 --
written ONLY by the cascade + bench hook), a phantom `int simulationState` (gate clause 1 --
ZERO writers, NEVER ctor-initialized: uninitialized pool memory, MEMBLOCK recycles unzeroed),
and the weaponAlarm FSM cell. `MechWeapon::TakeDamage` then bypassed the reconstructed
`MechSubsystem::TakeDamage` (which HAS the destroyed tail) for the raw engine base -- so the
everyday CRIT-LOTTERY kill saturated the zone (X lit) without ever writing a gate cell: X'd
weapons kept firing. Three prior "fixes" all aimed at the GATE, and the bench (BT_KILL_SUBSYS)
wrote both cells directly -- 4,213 refusals proving a gate that was never broken.
**Sibling defect in the same function:** the port-authored tail drove the weapon FSM from zone
state -- saturation -> level 0 (=Firing!), partial damage -> level 1 (NO case in either FSM:
silent freeze; with beamFlag armed = the 883 hanging beam, #164's upstream writer).
**Rules:** (1) when the binary reads/writes ONE offset, the port must have ONE authority --
alias, don't duplicate; if layout forces a twin field, every writer writes BOTH (grep-audit the
writer census, not the reader census). (2) Never let an override "simplify away" the
reconstructed base -- `MechWeapon::TakeDamage` calling `Subsystem::TakeDamage` instead of
`MechSubsystem::TakeDamage` dropped the invariant silently. (3) A bench hook that writes state
directly validates READERS only; destruction/state benches must drive the REAL writer path
(BT_CRIT_SUBSYS: Damage -> ApplyDamageAndMeasure -> virtual TakeDamage). (4) Every ctor
initializes every reconstructed field -- an unwritten int in pooled memory is a time bomb.
Verified: weaponx_bench P1 (crit kill: 0 FIRED after, 5,664 refusals, X+gate agree) + P2
(partial crit keeps firing -- the freeze is gone). Full mechanism: the night-16 workflow dossier
(4 agents, cross-examined) + docs/GEN_WARNING_AUDIT-style lanes in the #165 ticket thread.
## 31. GUARD-SCOPE NARROWING: a binary guard wrapping a whole block, transcribed onto its first statement (#174, 2026-08-13)
The binary's burst loop guards the ENTIRE per-burst application on zone-not-destroyed
(`if (piVar1[9] != 1) { crit-roll | zone TakeDamage; score tally; destroyed-flag }`,
part_012.c:14661-14677 @0x4a0446) -- a burst landing on a dead zone is a structural no-op:
no re-application, no cascade re-fire, NO SCORE. The port transcribed that guard onto the
crit roll ONLY; the unguarded TakeDamage re-saturated the zone every burst, whose >=1.0 tail
re-ran RecurseSegmentTable (night-16 field: zone 18 x7, zone 9 x4 per mission), each pass
re-pushing crit allotment into surviving subsystems (the ram "crits worsened with each
collision") -- and hits on wreckage kept SCORING (the binary awards zero). Neither
RecurseSegmentTable nor SendSubsystemDamage has any internal re-entry protection in the
binary (verified @0049cad4/@0049c9a8: no `used` write, no top gate) -- the delivery-layer
guard is the ONLY protection, so its scope is load-bearing.
**Rule:** when transcribing a decomp `if` whose body spans multiple statements, diff the
BRACE SCOPE against the port -- a guard attached to the wrong statement compiles, runs, and
passes every reader-side bench. **Bench rule (the #165 lesson, second application in one
week):** prove the bench detects the bug BEFORE trusting its pass -- the negative control
here (fix stashed) scored 147 cascades where the fixed build scores 1.
Bench: BT_ZONE_HAMMER=<zone>[,<amt>] (mech4.cpp) + scratchpad/night16/cascade_bench.sh.
-26
View File
@@ -60,32 +60,6 @@ reset on death), #69 (Sunder V1 → Denkou naming).
token / transport layer. Everything above the seam is the same arcade mesh — the three replication
symptoms this night produced would all reproduce on the relay.
## THE EXIT CONTRACT (#163 -> #170, settled 2026-08-12) [T2 field-proven]
Process-exit topology for every glass/steam process: **flush -> quiesce -> TerminateProcess.**
`BTSteamNet_ShutdownAll` (L4STEAMNET.cpp) is a QUIESCE: gate `steamActive` FIRST (stops
marshal/game-thread seam re-entry), `CloseConnection(..., linger=true)` on every connection
("round over", not a peer-side timeout), close listeners, one `sockets->RunCallbacks()` drain,
`Sleep(60)` grace. **It deliberately never calls `SteamAPI_Shutdown()`, and no exit path may
return through the CRT** (menu-quit included -- it dies via `BTOrderlyDie`, btl4console.cpp).
The two-failure history, because each fix's reason constrains the other:
- **883 (no teardown at all):** bare `ExitProcess` ran DLL_PROCESS_DETACH with live SNS threads
-> intermittent loader-lock deadlock -> "frozen view of how the game ended" + the menu child
hung in `SteamAPI_Init` against the wedged half-dead sibling (#163, night15).
- **913 (full `SteamAPI_Shutdown` before TerminateProcess):** at round end every peer closes
simultaneously, so status events are ALWAYS in flight; Shutdown tears client state down while
steamclient's own service thread dispatches them -> that thread calls literal NULL
(0xc0000005 access=8 target=0x0, BaseThreadInitThunk root, ZERO btl4 frames) in the window
before TerminateProcess (#170, night16: 7 hits / 3 machines / one module-relative stack).
- **The stable point:** TerminateProcess kills all threads atomically with every pointer still
valid (no detach, no dispatch window); the Steam client GCs the session on IPC pipe break --
the standard crashed-game path (883 relaunched all night with no Shutdown ever called). The
child-hang needed a WEDGED sibling, which TerminateProcess makes structurally impossible.
Forensics rider: the crash filter (btl4main.cpp `BTCrashFilter`) now prints foreign frames as
`<module.dll>+0xOFFSET` (VirtualQuery AllocationBase + GetModuleFileName), so a future
foreign-thread crash names its DLL instead of an unresolvable ASLR address.
## Key Relationships
- Rides: [[glass-cockpit]] miniconsole (marshal has a Steam-wire branch) · Extends:
[[multiplayer]] (the arcade mesh, unchanged above the seam)
-15
View File
@@ -338,18 +338,3 @@ forward at the respawn (physical lever / HOTAS — exactly who reported it) had
speed in the spike frames. The calibration facts above (constants byte-exact, dt-normalised
kinetic term) all STAND; the in-life governor (running hot at sustained top speed derates the
myomers) is authentic and remains.
## Spawn-discharge loop heat + per-chassis generator bias (night-16 field, 2026-08-12) [T3 field-observed, mechanism plausible-authentic]
Oracle's explanation for the long-standing "missiles cook off on one side first" reports
(Lynx): at spawn ALL weapons start discharged, and the initial CHARGE of the energy weapons
dumps heat into their loops; generator assignments are biased per chassis (Avatar: BOTH large
lasers on GeneratorA), so that side's loops start hot and a missile launcher sharing the side
cooks off first even if the player fires only missiles. Not yet verified against the authored
data -- verify via BTL4.RES generator/loop assignments before treating as established. [T3]
Related open defect/question: **#173** -- Vulture (and per Oracle ALL large chicken walkers)
cannot run seek-4 myomers without immediate overheat, loop 5 pegged, heat only sheds near
standstill (Elengil; Oracle repro). Investigate myomer heat rate + loop-5 capacity vs authored
data for those chassis; authored-balance vs port-artifact undecided. The myomer drive-heat
FORMULA is verified faithful (#137, above) -- if the rate is right, the question moves to the
authored loop capacity / condenser assignments.
-8
View File
@@ -226,11 +226,3 @@ wrote); affinity masks 2-cores-each up to 4 nodes, 1-each at 8.
- Uses: [[build-and-run]] (parity, env gates, BT_SHOT capture) · [[experience-levels]] (expert vs novice gating)
- Informs: [[reconstruction-method]] (step 4 "verify honestly" — this file is the how)
- Incident sources: [[reconstruction-gotchas]] §23 (pixels), §25 (same-pass latch timing — caught by the 4-node bench), §26 (silent stubs); [[multiplayer]] (replication layers)
## weaponx_bench (night16) -- #165 crit-path destruction
`scratchpad/night16/weaponx_bench.sh`: single node, vulture, BT_AUTOFIRE; P1 kills ERSLaser_1
via BT_CRIT_SUBSYS (the REAL writer path: Damage -> ApplyDamageAndMeasure -> virtual TakeDamage)
and asserts [crit] DESTROYED + [techstat] X + zero named FIRED after + REFUSED>0 + survivor
fires; P2 partial-crits to 0.5 and asserts the weapon KEEPS firing (freeze regression, the #164
upstream writer). LESSON (gotcha SS30): BT_KILL_SUBSYS writes the cells directly and can only
validate READERS -- state benches must drive the real writer path.
+2 -3
View File
@@ -629,8 +629,7 @@ attributes published on Emitter+Myomers — see the AUDIT finding A addendum bel
Byte-exact re-base of the PoweredSubsystem:HeatSink LEAF (Sensor/Emitter/Myomers) → radarPercent heat penalty
(delete the sensor.cpp:287-288 guard — ✅ LANDED: the guard is deleted and the leaf is byte-exact,
sensor.cpp:407-413) + the raw-offset cluster reads (partially locked since — MechWeapon byte-exact);
radar pip/name symbology + staticEntities (task #17: pips landed the real L4GaugeImage path;
✅ staticEntities RESOLVED 2026-08-13, branch radar-static-geometry — see row 24);
radar pip/name symbology + staticEntities (task #17, under reconstruction 2026-07-12);
CycleDisplayMode→ModeManager mask (✅ RESOLVED 2026-07-19, Gitea #6 [T2]: @4d1ae4 relabeled the
NotifyOfDisplayModeChange override (vtbl+0x4C) and made virtual; desktop 'N'/pad RightThumb →
CycleDisplayModeNow; secondary schematic swap dama→crit→heat pixel-verified via
@@ -688,7 +687,7 @@ reconfigure/externalConfigure); parse-skip list EMPTY ([gskip]=0), all 50 attr b
| 21 | Eng myomer page: seek-step lamp | Myomers@0x320 INFERRED (ctor @004c8df4 not in the decomp export) | marked in code | T3 | CORRECT (best-effort; flagged) |
| 22 | Eng page: linked heat-sink number (hs+0x1d4 guarded raw) | PrepEngr numeric #2 | marked BEST-EFFORT (guarded) | T3 | CORRECT (guarded; flagged) |
| 23 | sec: radar map (wedge/blips/scale) | Mech Radar* attrs; wedge ← Torso::currentTwist@0x1D8 (Gitea #1) | #1 [T2]; LIVE: blips + wedge render, zoom via SetTargetRange | T2 | CORRECT |
| 24 | sec: radar pip/name symbology + MAP GEOMETRY | GetVideoObject/LookUpPip/GetNameID; MapDisplay::DrawStatic ← staticEntities | pips: the real L4GaugeImage path landed (task #17). **Map geometry RESOLVED 2026-08-13** (branch radar-static-geometry, playtester DOS comparison): DrawStatic was always faithful but staticEntities was EMPTY — (1) the per-frame RebuildEntityGrid Clear()+dynamic-only refill structurally excluded the 528 static-flagged UnscalableTerrain placements (arenall: 59 static vs 3 dynamic), and (2) the notify gate was transcribed as Terrain, which matches ZERO shipped records (1008 UnscalableTerrain + 789 CulturalIcon derive from Entity — @0x4e6ef8 was almost certainly UnscalableTerrain's derivation). Fix: staticEntities is now persistent, filled once per mission (AllEntityIterator, UnscalableTerrain-derived + rtype-18 pip; `[radar] static grid filled: N` under BT_RADAR_LOG), reset at LinkToEntity, pruned by the gate-corrected remove notify. Verified live: 326 entities filled, arena outlines draw | T2 | CORRECT |
| 24 | sec: radar pip/name symbology | GetVideoObject/LookUpPip/GetNameID | task #17 under reconstruction — cross-blip stand-in | T3 | DEFERRED-FEED |
| 25 | sec: headingPointer needle + numeric | YawPitchRoll decomposition (engine-convention port of euler[0]) | LIVE: 089→120 while turning under BT_GOTO | T2 | CORRECT |
| 26 | sec: numericSpeed | LinearSpeed (abs(adv)/dt) | LIVE: 0 parked → 176-182 walking | T2 | CORRECT (units question stays open) |
| 27 | sec: digitalClock MISSION TIME | engine mission clock (format enum arg) | LIVE: 09:19→06:04 counting | T0/T2 | CORRECT |
+290
View File
File diff suppressed because one or more lines are too long
+638
View File
@@ -0,0 +1,638 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BattleTech 4.11 &mdash; the mech roster</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
:root {
--ground: #0e0f11; --panel: #16181b; --rule: #2a2e33;
--ink: #e8e6e2; --ink-mid: #a3a8ae; --ink-low: #71777e;
--hot: #f39313; /* palette 6, the display's own amber */
--nrg: #ff5340; /* energy weapons */
--bal: #f39313; /* ballistic */
--msl: #58c470; /* missiles -- the armor doll's green family */
--mono: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
--sans: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
@media (prefers-color-scheme: light) {
:root {
--ground: #f4f2ee; --panel: #fbfaf8; --rule: #ddd8d0;
--ink: #14161a; --ink-mid: #565c63; --ink-low: #7d838a;
--hot: #a35f00; --nrg: #b3260f; --bal: #a35f00; --msl: #1c7a3f;
}
}
body {
margin: 0; background: var(--ground); color: var(--ink);
font: 400 15px/1.6 var(--sans);
}
.wrap { max-width: 78rem; margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2.5rem); }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
:focus-visible { outline: 2px solid var(--hot); outline-offset: 2px; }
header.top { border-bottom: 1px solid var(--rule); padding: clamp(2.5rem,6vw,4rem) 0 1.75rem; }
.eyebrow {
font: 500 12px/1 var(--mono); letter-spacing: .2em; text-transform: uppercase;
color: var(--ink-low); margin: 0 0 1rem;
}
h1 {
font: 700 clamp(1.9rem,1.4rem+2.2vw,3rem)/1.05 var(--mono);
letter-spacing: -.02em; text-transform: uppercase; margin: 0 0 .9rem;
}
h1 em { font-style: normal; color: var(--hot); }
.lede { max-width: 62ch; color: var(--ink-mid); margin: 0 0 1.4rem; font-size: 1.05rem; }
.lede strong { color: var(--ink); font-weight: 600; }
.rules { display: flex; flex-wrap: wrap; gap: .6rem 2rem; margin: 0; padding: 0; list-style: none; }
.rules li {
font: 500 12px/1.5 var(--mono); color: var(--ink-mid);
padding-left: 1.1rem; position: relative;
}
.rules li::before {
content: ""; position: absolute; left: 0; top: .45em;
width: .45rem; height: .45rem; background: var(--hot);
}
.rules b { color: var(--ink); font-weight: 700; }
.bar {
position: sticky; top: 0; z-index: 5;
background: var(--ground);
border-bottom: 1px solid var(--rule); padding: .75rem 0;
}
.bar__in { display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: center; }
.seg { display: inline-flex; border: 1px solid var(--rule); }
.seg button {
font: 500 12px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
background: none; border: 0; color: var(--ink-mid); padding: .55rem .9rem; cursor: pointer;
}
.seg button[aria-pressed="true"] { background: var(--hot); color: var(--ground); }
.search {
flex: 1 1 12rem; min-width: 9rem;
font: 400 12px var(--mono);
background: var(--panel); color: var(--ink);
border: 1px solid var(--rule); padding: .55rem .7rem;
}
.search::placeholder { color: var(--ink-low); }
.count { font: 500 12px var(--mono); color: var(--ink-low); letter-spacing: .1em; }
.key { display: flex; flex-wrap: wrap; gap: .4rem .9rem; padding: 1.25rem 0 0; }
.key span { display: inline-flex; align-items: center; gap: .4rem;
font: 500 12px var(--mono); letter-spacing: .08em; color: var(--ink-low); }
.key i { width: .7rem; height: .7rem; display: inline-block; }
.roster { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule);
margin: 1.75rem 0 3rem; }
.mech { background: var(--panel); display: grid; gap: 0;
grid-template-columns: minmax(15rem, 19rem) 1fr; }
@media (max-width: 46rem) { .mech { grid-template-columns: 1fr; } }
.mech[hidden] { display: none; }
.mech__id { padding: 1.1rem 1.25rem 1.35rem; border-right: 1px solid var(--rule); }
@media (max-width: 46rem) { .mech__id { border-right: 0; border-bottom: 1px solid var(--rule); } }
.hull { margin: 0 0 .9rem; display: flex; flex-direction: column; gap: .45rem;
align-items: flex-start; }
/* The game's own review image on the display's own black, either theme. */
.hull__img {
display: block; width: 7rem; height: auto;
background: #000; border: 1px solid var(--rule); image-rendering: pixelated;
}
.hull figcaption {
font: 500 11px/1.35 var(--mono); letter-spacing: .1em;
text-transform: uppercase; color: var(--ink-low);
}
.mech__id h3 {
font: 700 1.5rem/1 var(--mono); letter-spacing: .01em;
margin: 0 0 .55rem; text-transform: uppercase;
}
.mech__key {
font: 500 12px/1 var(--mono); letter-spacing: .12em;
color: var(--ink-low); margin: -.3rem 0 .7rem;
}
.mech__load { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 .7rem; }
.chip {
font: 500 11px/1 var(--mono); letter-spacing: .08em;
padding: .3rem .45rem; border: 1px solid currentColor;
}
.chip--e { color: var(--nrg); }
.chip--b { color: var(--bal); }
.chip--m { color: var(--msl); }
.mech__meta { display: flex; flex-wrap: wrap; gap: .8rem; margin: 0 0 .2rem;
font: 400 12px var(--mono); color: var(--ink-low); font-variant-numeric: tabular-nums; }
.spec {
margin: .75rem 0 0; display: grid; grid-template-columns: 1fr auto;
gap: .2rem .8rem; font: 500 12px var(--mono);
}
.spec div { display: contents; }
.spec dt { color: var(--ink-low); letter-spacing: .06em; }
.spec dd { margin: 0; text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; }
.mech__sheet { margin: .6rem 0 0; font: 400 11px/1.5 var(--mono); color: var(--ink-low); }
.mech__tables { min-width: 0; }
.tablewrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
thead th {
font: 500 12px/1 var(--mono); letter-spacing: .14em;
color: var(--ink-low); text-align: left; font-weight: 500;
padding: .9rem .9rem .55rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}
tbody th {
font: 500 13px/1.3 var(--mono); text-align: left; color: var(--ink);
padding: .5rem .9rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}
tbody td {
font: 400 13px/1.3 var(--mono); color: var(--ink-mid);
padding: .5rem .9rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}
tbody tr:last-child th, tbody tr:last-child td { border-bottom: 0; }
th.num, td.num { text-align: right; }
.empty-state {
margin: 2rem 0; font: 500 13px var(--mono); color: var(--ink-low);
}
.foot {
border-top: 1px solid var(--rule); padding: 1.5rem 0 0; max-width: 76ch;
color: var(--ink-low); font-size: 13px;
}
.foot:last-child { padding-bottom: 4rem; }
.foot code { font-family: var(--mono); color: var(--ink-mid); }
</style>
</head>
<body>
<header class="top">
<div class="wrap">
<p class="eyebrow">BattleTech 4.11 &middot; from btl4fe.cpp, L4GAUGE.CFG and the player's manual</p>
<h1>Every mech,<br><em>and what it carries</em></h1>
<p class="lede">All <strong>18 mechs the menu offers</strong> &mdash; 8 base chassis
and 10 variants &mdash; each with its authored weapons roster, ammo bins and the
stat sheet from the original player's manual. The portraits are the game's own
mission-review images, shown in their undamaged state.</p>
<ul class="rules">
<li><b>Variants share the chassis</b>, not the loadout</li>
<li><b>Blackhawk, Nova, Owens, Kotori</b> have a fixed torso</li>
<li>The manual sheets cover the <b>six 4.0 hulls</b> only</li>
</ul>
</div>
</header>
<div class="bar">
<div class="wrap bar__in">
<div class="seg" role="group" aria-label="Filter">
<button type="button" data-filter="all" aria-pressed="true">All 18</button>
<button type="button" data-filter="base" aria-pressed="false">Base 8</button>
<button type="button" data-filter="variant" aria-pressed="false">Variants 10</button>
</div>
<input id="search" class="search" type="search" placeholder="Search mech or weapon&hellip;"
aria-label="Search mech or weapon">
<span class="count" id="count">18 / 18 mechs</span>
</div>
</div>
<main class="wrap">
<div class="key">
<span><i style="background:var(--nrg)"></i>Energy</span>
<span><i style="background:var(--bal)"></i>Ballistic</span>
<span><i style="background:var(--msl)"></i>Missile</span>
</div>
<div class="roster">
<article class="mech" data-tags="base" data-search="blkhawk black hawk er large laser er medium laser er small laser srm 6 erllaser ermlaser erslaser srm6">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Black Hawk mission-review portrait" loading="lazy">
<figcaption>Black Hawk chassis &middot; with Nova</figcaption>
</figure>
<h3>Black Hawk</h3>
<p class="mech__key">blkhawk &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">ER LARGE LASER</span><span class="chip chip--e">5&times; ER MEDIUM LASER</span><span class="chip chip--e">2&times; ER SMALL LASER</span><span class="chip chip--m">2&times; SRM 6</span></p>
<p class="mech__meta"><span>10 weapons</span><span>2 ammo bins</span><span>torso FIXED</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>50 t</dd></div><div><dt>Armor points</dt><dd>1362</dd></div><div><dt>Reservoir</dt><dd>1600 L</dd></div><div><dt>Heat sinks</dt><dd>16</dd></div><div><dt>Torso</dt><dd>FIXED</dd></div><div><dt>0&ndash;100 kph</dt><dd>5.1 s</dd></div><div><dt>Top speed</dt><dd>190 kph</dd></div><div><dt>Super charged</dt><dd>280 kph</dd></div><div><dt>Gimped</dt><dd>60 kph</dd></div></dl>
<p class="mech__sheet">manual p. 29</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Black Hawk</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Large Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">5</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">SRM 6</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="loki loki er medium laser er small laser afc 50 srm 4 ermlaser erslaser afc50 srm4">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Loki mission-review portrait" loading="lazy">
<figcaption>Loki chassis &middot; with Hellbringer, Mischief</figcaption>
</figure>
<h3>Loki</h3>
<p class="mech__key">loki &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER MEDIUM LASER</span><span class="chip chip--e">2&times; ER SMALL LASER</span><span class="chip chip--b">2&times; AFC 50</span><span class="chip chip--m">SRM 4</span></p>
<p class="mech__meta"><span>7 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>75 t</dd></div><div><dt>Armor points</dt><dd>2110</dd></div><div><dt>Reservoir</dt><dd>3800 L</dd></div><div><dt>Heat sinks</dt><dd>38</dd></div><div><dt>Torso</dt><dd>60&deg;/s &middot; 110&deg;</dd></div><div><dt>0&ndash;100 kph</dt><dd>4.5 s</dd></div><div><dt>Top speed</dt><dd>143 kph</dd></div><div><dt>Super charged</dt><dd>182 kph</dd></div><div><dt>Gimped</dt><dd>40 kph</dd></div></dl>
<p class="mech__sheet">manual p. 25</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Loki</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 50</th><td>Ballistic</td><td class="num">2</td><td class="num">2&times;</td></tr><tr><th scope="row">SRM 4</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="bhk1 nova er medium laser ppc srm 6 ermlaser ppc srm6">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Nova mission-review portrait" loading="lazy">
<figcaption>Black Hawk chassis &middot; with Black Hawk</figcaption>
</figure>
<h3>Nova</h3>
<p class="mech__key">bhk1 &middot; variant of Black Hawk</p>
<p class="mech__load"><span class="chip chip--e">3&times; ER MEDIUM LASER</span><span class="chip chip--e">2&times; PPC</span><span class="chip chip--m">2&times; SRM 6</span></p>
<p class="mech__meta"><span>7 weapons</span><span>2 ammo bins</span><span>torso FIXED</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>FIXED</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Black Hawk</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Nova</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">3</td><td class="num">&mdash;</td></tr><tr><th scope="row">PPC</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">SRM 6</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="madcat mad cat er large laser er small laser afc 100 lrm 15 erllaser erslaser afc100 lrm15">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Mad Cat mission-review portrait" loading="lazy">
<figcaption>Mad Cat chassis &middot; with Timber Wolf, Zanin Neko</figcaption>
</figure>
<h3>Mad Cat</h3>
<p class="mech__key">madcat &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">ER LARGE LASER</span><span class="chip chip--e">2&times; ER SMALL LASER</span><span class="chip chip--b">AFC 100</span><span class="chip chip--m">2&times; LRM 15</span></p>
<p class="mech__meta"><span>6 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>75 t</dd></div><div><dt>Armor points</dt><dd>2195</dd></div><div><dt>Reservoir</dt><dd>2800 L</dd></div><div><dt>Heat sinks</dt><dd>28</dd></div><div><dt>Torso</dt><dd>80&deg;/s &middot; 90&deg;</dd></div><div><dt>0&ndash;100 kph</dt><dd>4.2 s</dd></div><div><dt>Top speed</dt><dd>175 kph</dd></div><div><dt>Super charged</dt><dd>220 kph</dd></div><div><dt>Gimped</dt><dd>62 kph</dd></div></dl>
<p class="mech__sheet">manual p. 26</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Mad Cat</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Large Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 100</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">LRM 15</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="thor thor er medium laser er ppc er small laser large laser lrm 15 ermlaser erppc erslaser llaser lrm15">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="204"
alt="Thor mission-review portrait" loading="lazy">
<figcaption>Thor chassis &middot; with Summoner</figcaption>
</figure>
<h3>Thor</h3>
<p class="mech__key">thor &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">ER MEDIUM LASER</span><span class="chip chip--e">2&times; ER PPC</span><span class="chip chip--e">2&times; ER SMALL LASER</span><span class="chip chip--e">LARGE LASER</span><span class="chip chip--m">LRM 15</span></p>
<p class="mech__meta"><span>7 weapons</span><span>1 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>75 t</dd></div><div><dt>Armor points</dt><dd>2110</dd></div><div><dt>Reservoir</dt><dd>3800 L</dd></div><div><dt>Heat sinks</dt><dd>38</dd></div><div><dt>Torso</dt><dd>60&deg;/s &middot; 110&deg;</dd></div><div><dt>0&ndash;100 kph</dt><dd>4.5 s</dd></div><div><dt>Top speed</dt><dd>143 kph</dd></div><div><dt>Super charged</dt><dd>182 kph</dd></div><div><dt>Gimped</dt><dd>40 kph</dd></div></dl>
<p class="mech__sheet">manual p. 25</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Thor</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER PPC</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">Large Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">LRM 15</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="owens owens er medium laser er small laser srm 6 ermlaser erslaser srm6">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ4AAADLCAMAAACoGNoNAAADAFBMVEUAAAD/AAAP/wdjY2NTU1PDYxOfn58A4wD/AAD/05f///9jY9fzgwCDUwDzwwBTMwAAAAAARwAzMwAAAABbWwB/fwDLywAPRwCDf/8fdxcAAIs7OzsPMwt7ewCHUwCTAAAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwCTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6v///+68zF9AAAEV0lEQVR42u3cgW6bMBAGYEv3BHuESZ0WqVW6bEn7/m+22BiwDx+cyRlo+p/UqWpD+HbmDmNonEMgEAgEAoFAIBAIBAKBQCAQCARi2yAxjq07hI/ojw+iC4/j8Ghj3vIg/btH5HnhAu96j3X7URxQgi74qEvfPO96FXyrDlaijzRI0nmfz17nk0vjepV89JmGyke9LuxCw/Nfl2koeWE3vY80Azvq/PdLvFC5q3lxNz2PanJXzbvdYxVPzN/kkFHw0tJIebdb4uOloeSxw5cXwoeGlzaWkXe7pT5NY+G8T1Ysa3n8rDHPE7dezZuWRrFrqHnlrfPSUPIiizWWNry+sUSjlkeTtkx0GoObZ0sjvPB9jIyX1ISOl1VwFa/cWBS8YYiNef2v5tryHjzWnARe8qpteb99+H87njAlIDqfz/7rbMfjpTGEwCMheyHKPNbtqkrjI9MpePQ4r6axsAnVQCrzgrDIi7Qpb/gmn1CtO2s05a04a7Bz7hLPA0s879LyqBkv6AqFe/Gw/XleeClcCvnRpb15Xe6KPD++/QBb85Sl0R13Eq/zWZcGm1DN8Cj2PWFwQ98jVWNR9z12TpN5FI1jD85Lo8te310kXn5Ws+JRxBV7Stpdgo9NCTbh1cW2PBeTp8remdx34bH51Da8z8r53lJjMePVX6mxs0ZxvufqCsNJ8z2zVYIJL2bv51z0jUW8FDLkUXal1nflZV64HMmSV8MrLVCVs0c5LwCXeZTzaDZ70xUq5QIa50XfIi9es6l4y7dGxMXbCS8K53n9PiRe5eJt4pte55K4iLF4c0jkZa3PuWrfLM+x9P29R5a86SKGxKu4czD573d9r8BzPS+84m+IccglXvquj9536X9ymufF3HX5m+W9l3kP3cbKT2uP8ZLObHWX7avzhkMvHHxH4w2Fm5buwXi6xrItL4sfQrPYkTdzSnN785zMoyPwnOphjP14jhaH9pA8B95X4g3Xk6R5UmnCG1dbmvDGy3HGS1I2kz0qXEva806TzCQ+Kheuk26r2T22lAEVD6KxV7BlAvunqmSe5g3YIkbr5/Q23Lw6e6da3nu77E3Wp3zx1vGysjUWHp7ni8GSR2TYW8IFuC0vXSv4FrwsVvDyuxzWPB61PBbgHYl35GOP7CvX8LwWeVbZi7y2f1ew6Rto3j2fMFe+wfSmlXMt53wbbarfx8sQa2bLr0OABx544IEH3s48suPRLn/6127rZ8geeOCBBx544IEHHnjggQceeOCBBx54WKHaMXtkyWtxN/IliXre62vmo2Yj+zjPfHzBe2oeWfa9Jg9nWmWvQWe2zp47znMYboNnMfLRfWRwwQPv2Urj1xBreG9DgAceeOCB99y87kyU8uqfvM14ts98P/RR7S0/6p3GxK3Yh7DJmEJqcbwx75KNH29Wx2Gqy8ph/MUSb2SUy+StLW+milk5gAceeOB9bZ7itCZ/fIcZT9zBYzybicH+vP8Wh7Z72j2/ewAAAABJRU5ErkJggg==" width="158" height="203"
alt="Owens mission-review portrait" loading="lazy">
<figcaption>Owens chassis &middot; with Kotori</figcaption>
</figure>
<h3>Owens</h3>
<p class="mech__key">owens &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER MEDIUM LASER</span><span class="chip chip--e">4&times; ER SMALL LASER</span><span class="chip chip--m">2&times; SRM 6</span></p>
<p class="mech__meta"><span>8 weapons</span><span>2 ammo bins</span><span>torso FIXED</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>25 t</dd></div><div><dt>Armor points</dt><dd>760</dd></div><div><dt>Reservoir</dt><dd>1500 L</dd></div><div><dt>Heat sinks</dt><dd>15</dd></div><div><dt>Torso</dt><dd>FIXED</dd></div><div><dt>0&ndash;100 kph</dt><dd>4.2 s</dd></div><div><dt>Top speed</dt><dd>154 kph</dd></div><div><dt>Super charged</dt><dd>265 kph</dd></div><div><dt>Gimped</dt><dd>63 kph</dd></div></dl>
<p class="mech__sheet">manual p. 29</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Owens</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">4</td><td class="num">&mdash;</td></tr><tr><th scope="row">SRM 6</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="own1 kotori er medium laser er small laser afc 25 nrk 5 ermlaser erslaser afc25 nrk5">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Kotori mission-review portrait" loading="lazy">
<figcaption>Owens chassis &middot; with Owens</figcaption>
</figure>
<h3>Kotori</h3>
<p class="mech__key">own1 &middot; variant of Owens</p>
<p class="mech__load"><span class="chip chip--e">ER MEDIUM LASER</span><span class="chip chip--e">ER SMALL LASER</span><span class="chip chip--b">AFC 25</span><span class="chip chip--b">2&times; NRK 5</span></p>
<p class="mech__meta"><span>5 weapons</span><span>3 ammo bins</span><span>torso FIXED</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>FIXED</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Owens</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Kotori</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 25</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">NRK 5</th><td>Ballistic</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="thr1 summoner er small laser large laser afc 25 lrm 10 erslaser llaser afc25 lrm10">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="204"
alt="Summoner mission-review portrait" loading="lazy">
<figcaption>Thor chassis &middot; with Thor</figcaption>
</figure>
<h3>Summoner</h3>
<p class="mech__key">thr1 &middot; variant of Thor</p>
<p class="mech__load"><span class="chip chip--e">4&times; ER SMALL LASER</span><span class="chip chip--e">2&times; LARGE LASER</span><span class="chip chip--b">2&times; AFC 25</span><span class="chip chip--m">LRM 10</span></p>
<p class="mech__meta"><span>9 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Thor</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Summoner</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">4</td><td class="num">&mdash;</td></tr><tr><th scope="row">Large Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 25</th><td>Ballistic</td><td class="num">2</td><td class="num">2&times;</td></tr><tr><th scope="row">LRM 10</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="lok1 hellbringer large laser medium laser ppc small laser srm 4 llaser mlaser ppc slaser srm4">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Hellbringer mission-review portrait" loading="lazy">
<figcaption>Loki chassis &middot; with Loki, Mischief</figcaption>
</figure>
<h3>Hellbringer</h3>
<p class="mech__key">lok1 &middot; variant of Loki</p>
<p class="mech__load"><span class="chip chip--e">2&times; LARGE LASER</span><span class="chip chip--e">2&times; MEDIUM LASER</span><span class="chip chip--e">2&times; PPC</span><span class="chip chip--e">2&times; SMALL LASER</span><span class="chip chip--m">SRM 4</span></p>
<p class="mech__meta"><span>9 weapons</span><span>1 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Loki</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Hellbringer</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">Large Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">PPC</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">SRM 4</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="mad1 timber wolf medium laser afc 25 lrm 10 mlaser afc25 lrm10">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Timber Wolf mission-review portrait" loading="lazy">
<figcaption>Mad Cat chassis &middot; with Mad Cat, Zanin Neko</figcaption>
</figure>
<h3>Timber Wolf</h3>
<p class="mech__key">mad1 &middot; variant of Mad Cat</p>
<p class="mech__load"><span class="chip chip--e">4&times; MEDIUM LASER</span><span class="chip chip--b">2&times; AFC 25</span><span class="chip chip--m">2&times; LRM 10</span></p>
<p class="mech__meta"><span>8 weapons</span><span>4 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Mad Cat</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Timber Wolf</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">Medium Laser</th><td>Energy</td><td class="num">4</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 25</th><td>Ballistic</td><td class="num">2</td><td class="num">2&times;</td></tr><tr><th scope="row">LRM 10</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="avatar avatar er large laser er medium laser afc 50 lrm 10 erllaser ermlaser afc50 lrm10">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ4AAADLCAMAAACoGNoNAAADAFBMVEUAAAD/AAAP/wdjY2NTU1PDYxOfn58A4wD/AAD/05f///9jY9fzgwCDUwDzwwBTMwAAAAAARwAzMwAAAABbWwB/fwDLywAPRwCDf/8fdxcAAIs7OzsPMwt7ewCHUwCTAAAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwAAZwCTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6uTh6v///+68zF9AAAGtUlEQVR42u3c23bjKgwAUK+lL0mbTrrSnnR6e+n/f9jYIIHEzYDByWnQQy5unOwKhLFpM00jRowYMWLEiBEjRoy4x4BkDN4a710F3okng5fJcxr15njv7376bovHUnd7PM3CmxvhmX4G75g4fWc75NXHvDTvWkD92acT5/3MwXmXy5WEhBO8nyV7P4J3DaC1ubw5eS5vZyEIXBZvP6Bjy2jc/YS+jXhYsn5p+EDoiSOTc3w1I0pgYNHBiZ102qV44eyRCY9qLHtUxPq2S+q0jXiolTwAyUMP8bSwdQKBVyvycEzmPJM5yVNNCyaFl7YzaXyzx8cQjzWumLHwxvV553MrIdkeXd6JygQC0z3Z93jjIu+Mwga8RwzBcyvXi1DlCp4WbuY9+rzPObzKjWdvjq85fN65C+/zk/s473uOEO9LxT68z0/hY7zvb+MTvC+M6/K+v61vHx4Ing7Lc858fB4WheFhnBvVBggepot4dHwzE3rBs+UKQDw6wHEebMudz3NLA0LZ08cRpzR83pb86WOFz3MGFhCFy0oX3IElwKv36YZlw7IVifneAcywZ3XL/PTA53t89mLbtr7/2WMZZPPEuLzCMzVS5+NHM5wShONwKNtOU4Jt5ctqloynQCjGIRCKdwmEHFtUeWwc8lAX8M0bD0Gf2nwJ+PR0VFZHXfacIfnk+5Zth3ArKp7vA5k+ONfzjG/pHnpy7MYpnb2Lv8eybTJlUXtow2HF6KZk9uK8YPYm9FWXLk2SgXTEozMgfZPDM6dDNG3Gt4Nz+bTZNgLZUIc8OmOjm3Ue0MmZOemg9wPW+/LWGkDy+B6teRObv2Suhcw//INheh69vnHjqnc02fvPRMK36Bafuo/wIqXhDCmrpRHlxX1aB2leJHsAf/8yWH32Fl+Mp3MneCB5sb4362bfMkC7vGjfY4NL1koc8vBG86AvD8qyZ32mcu1Pk42LvJLGtYNfNs/4vESvlEacFy0NNrhgTcAqj3xeL8jKHviNm8qe8S0oTUuXBisP76cZfQ9K+h576xmls7fOU4ML7MzTOnyQ5gV1XUqjlEdHtMBL1o4a0WE5VRplvNTIyFcNQkeN6EHNZi+2dlDMS1yZx+sWtu8lQvY9e32+qjRyLs7rq921EyrM3xTirQ4smT5VvxUTKrM+OYV468NyfgLd0jjg6drB3OmHvDRiqTO8tYNa/sKVXbyyPH5lwOfRAgfk/CXR1uUhA8zkQSJ1LbPntnAWbwXXmMcTmNP34iXRoXElkHhu7eJGtggJ6bdrmD1ZIijxr7AgL2cNF/JO1Wp6YPDqGebwkpE6N32NlioBDxspHlwyF28tr91aLx7bkrzMnm7T13Il2nYzHR9zCF52LrrwnCHhQ0XVIEG81n9oAPCgYr77wFBP9EYo/01bpayAl/PJLXh+s5Xwuv+VjY7nOQp5b3P0NDLZs+ZRGN4D6RiP4o2ik4/JBM4r3dh5xxuLXjzbvJQgk6lQ2BfZpu3FE21bzpOt27dxN/B6Nu7gDd41eWw0hpxgL9yFh9kzB4yVWF6nYXvyMnEI3J/38FDgG7zb5oHb90pC8Pocc/lstI5HT/Zo3KKdd2hcmsZv4/WdzdvGreB1Ph9qwpumvv+0sYE3Tf3/36WYB4M3eIN3PV71sOwec++UVzklkBOqu+TZZt3M6zph2d732vvAn8zXZq9D/hZcQ17jsw19PbQhr61PNWxT3tvgDd7gDd7g/WIeNJ6xjIPa4P2KCdW07evX+n97m1wMr+L1XQ6Xq34VO/e77j14DdeFnsv7jux7+3yF4h47b/x6x477FuiOx6O6wcANmbyXlxd1g4EbGvmQBUfOwycZPC164Tx80o6nEih5oLdn8FQCJQ/09oaN62evoHH97N1T4w7e4A3e4A3e4A3enjwW5TwW95U9kTAnlZk7+9PRMd8bvMG7b97/YGDxr5AUZM/fuUPjMuOxuHFfuKxL3zua5q3imaa9Mx4VBgheQWkIHtjimNqWruHlX0GU1aB5PRb9TLUaXuHOhtfv2q2XvYKLt5LXyLj+vw69ds7FPamwj+bHT3wrpHZ+VTHf46Plsd269RuDLUOpDE/BkkCL0/eWp7lbgRZn7jmPzGEgx1kVe8DslV/saT7dUfEH+of+96Xhp5t29HgWXgEM4UI88wL+GQz3asPnVQPBZuXpaYXnAsO4MM+kt3QcdROX4tGvAfq7Z3UhOLgYz/wa5cO8i4jzMHfEAx+X4L0Wj4GQNvhwfLXiBQkWEZM34D0l0rfCg9do9n4drzo27PpreDcU/wDwAOAHKra7nQAAAABJRU5ErkJggg==" width="158" height="203"
alt="Avatar mission-review portrait" loading="lazy">
<figcaption>Avatar chassis &middot; with Satyr</figcaption>
</figure>
<h3>Avatar</h3>
<p class="mech__key">avatar &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER LARGE LASER</span><span class="chip chip--e">4&times; ER MEDIUM LASER</span><span class="chip chip--b">AFC 50</span><span class="chip chip--m">2&times; LRM 10</span></p>
<p class="mech__meta"><span>9 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">no manual sheet (4.10 addition)</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Avatar</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Large Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">4</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 50</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">LRM 10</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="ava1 satyr er medium laser ppc afc 100 lrm 5 srm 2 ermlaser ppc afc100 lrm5 srm2">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Satyr mission-review portrait" loading="lazy">
<figcaption>Avatar chassis &middot; with Avatar</figcaption>
</figure>
<h3>Satyr</h3>
<p class="mech__key">ava1 &middot; variant of Avatar</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER MEDIUM LASER</span><span class="chip chip--e">PPC</span><span class="chip chip--b">AFC 100</span><span class="chip chip--m">LRM 5</span><span class="chip chip--m">SRM 2</span></p>
<p class="mech__meta"><span>6 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">no manual sheet (4.10 addition)</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Satyr</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">PPC</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 100</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">LRM 5</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">SRM 2</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="sunder sunder er medium laser er small laser medium laser afc 100 afc 25 lrm 10 ermlaser erslaser mlaser afc100 afc25 lrm10">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Sunder mission-review portrait" loading="lazy">
<figcaption>Sunder chassis &middot; with Denkou</figcaption>
</figure>
<h3>Sunder</h3>
<p class="mech__key">sunder &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER MEDIUM LASER</span><span class="chip chip--e">2&times; ER SMALL LASER</span><span class="chip chip--e">2&times; MEDIUM LASER</span><span class="chip chip--b">AFC 100</span><span class="chip chip--b">AFC 25</span><span class="chip chip--m">LRM 10</span></p>
<p class="mech__meta"><span>9 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">no manual sheet (4.10 addition)</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Sunder</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 100</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">AFC 25</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">LRM 10</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="snd1 denkou er small laser ppc afc 25 lrm 5 erslaser ppc afc25 lrm5">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Denkou mission-review portrait" loading="lazy">
<figcaption>Sunder chassis &middot; with Sunder</figcaption>
</figure>
<h3>Denkou</h3>
<p class="mech__key">snd1 &middot; variant of Sunder</p>
<p class="mech__load"><span class="chip chip--e">ER SMALL LASER</span><span class="chip chip--e">3&times; PPC</span><span class="chip chip--b">AFC 25</span><span class="chip chip--m">LRM 5</span></p>
<p class="mech__meta"><span>6 weapons</span><span>2 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">no manual sheet (4.10 addition)</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Denkou</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">PPC</th><td>Energy</td><td class="num">3</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 25</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">LRM 5</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="base" data-search="vulture vulture er medium laser er small laser large laser lrm 20 ermlaser erslaser llaser lrm20">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Vulture mission-review portrait" loading="lazy">
<figcaption>Vulture chassis &middot; with Mad Dog</figcaption>
</figure>
<h3>Vulture</h3>
<p class="mech__key">vulture &middot; base chassis</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER MEDIUM LASER</span><span class="chip chip--e">2&times; ER SMALL LASER</span><span class="chip chip--e">2&times; LARGE LASER</span><span class="chip chip--m">2&times; LRM 20</span></p>
<p class="mech__meta"><span>8 weapons</span><span>2 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>75 t</dd></div><div><dt>Armor points</dt><dd>2025</dd></div><div><dt>Reservoir</dt><dd>2400 L</dd></div><div><dt>Heat sinks</dt><dd>24</dd></div><div><dt>Torso</dt><dd>50&deg;/s &middot; 130&deg;</dd></div><div><dt>0&ndash;100 kph</dt><dd>4.7 s</dd></div><div><dt>Top speed</dt><dd>175 kph</dd></div><div><dt>Super charged</dt><dd>235 kph</dd></div><div><dt>Gimped</dt><dd>62 kph</dd></div></dl>
<p class="mech__sheet">manual p. 26</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Vulture</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">Large Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">LRM 20</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="vul1 mad dog er small laser ppc lrm 15 erslaser ppc lrm15">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Mad Dog mission-review portrait" loading="lazy">
<figcaption>Vulture chassis &middot; with Vulture</figcaption>
</figure>
<h3>Mad Dog</h3>
<p class="mech__key">vul1 &middot; variant of Vulture</p>
<p class="mech__load"><span class="chip chip--e">4&times; ER SMALL LASER</span><span class="chip chip--e">2&times; PPC</span><span class="chip chip--m">2&times; LRM 15</span></p>
<p class="mech__meta"><span>8 weapons</span><span>2 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Vulture</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Mad Dog</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Small Laser</th><td>Energy</td><td class="num">4</td><td class="num">&mdash;</td></tr><tr><th scope="row">PPC</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">LRM 15</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="lok2 mischief medium laser ppc small laser afc 25 gauss srm 4 mlaser ppc slaser afc25 gauss srm4">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Mischief mission-review portrait" loading="lazy">
<figcaption>Loki chassis &middot; with Loki, Hellbringer</figcaption>
</figure>
<h3>Mischief</h3>
<p class="mech__key">lok2 &middot; variant of Loki</p>
<p class="mech__load"><span class="chip chip--e">3&times; MEDIUM LASER</span><span class="chip chip--e">PPC</span><span class="chip chip--e">2&times; SMALL LASER</span><span class="chip chip--b">AFC 25</span><span class="chip chip--b">GAUSS</span><span class="chip chip--m">SRM 4</span></p>
<p class="mech__meta"><span>9 weapons</span><span>3 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Loki</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Mischief</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">Medium Laser</th><td>Energy</td><td class="num">3</td><td class="num">&mdash;</td></tr><tr><th scope="row">PPC</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">Small Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 25</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">Gauss</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">SRM 4</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr></tbody>
</table></div></div>
</article>
<article class="mech" data-tags="variant" data-search="mad2 zanin neko er medium laser er ppc afc 50 lrm 15 srm 6 ermlaser erppc afc50 lrm15 srm6">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="data:image/png;base64,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" width="158" height="203"
alt="Zanin Neko mission-review portrait" loading="lazy">
<figcaption>Mad Cat chassis &middot; with Mad Cat, Timber Wolf</figcaption>
</figure>
<h3>Zanin Neko</h3>
<p class="mech__key">mad2 &middot; variant of Mad Cat</p>
<p class="mech__load"><span class="chip chip--e">2&times; ER MEDIUM LASER</span><span class="chip chip--e">ER PPC</span><span class="chip chip--b">AFC 50</span><span class="chip chip--m">LRM 15</span><span class="chip chip--m">2&times; SRM 6</span></p>
<p class="mech__meta"><span>7 weapons</span><span>4 ammo bins</span><span>torso TWISTS</span></p>
<dl class="spec"><div><dt>Tonnage</dt><dd>&mdash;</dd></div><div><dt>Armor points</dt><dd>&mdash;</dd></div><div><dt>Reservoir</dt><dd>&mdash;</dd></div><div><dt>Heat sinks</dt><dd>&mdash;</dd></div><div><dt>Torso</dt><dd>twists</dd></div><div><dt>0&ndash;100 kph</dt><dd>&mdash;</dd></div><div><dt>Top speed</dt><dd>&mdash;</dd></div><div><dt>Super charged</dt><dd>&mdash;</dd></div><div><dt>Gimped</dt><dd>&mdash;</dd></div></dl>
<p class="mech__sheet">chassis sheet: see Mad Cat</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the Zanin Neko</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody><tr><th scope="row">ER Medium Laser</th><td>Energy</td><td class="num">2</td><td class="num">&mdash;</td></tr><tr><th scope="row">ER PPC</th><td>Energy</td><td class="num">1</td><td class="num">&mdash;</td></tr><tr><th scope="row">AFC 50</th><td>Ballistic</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">LRM 15</th><td>Missile</td><td class="num">1</td><td class="num">1&times;</td></tr><tr><th scope="row">SRM 6</th><td>Missile</td><td class="num">2</td><td class="num">2&times;</td></tr></tbody>
</table></div></div>
</article></div>
<p class="empty-state" id="nomatch" hidden>No mech matches that.</p>
<p class="foot">The roster is <code>kVehicles[]</code> in <code>game/glass/btl4fe.cpp</code>
&mdash; the exact catalog the in-game menu offers, in menu order. Each mech's weapons and
ammo bins are its <code>cmCrit(...)</code> lines in <code>content/GAUGE/L4GAUGE.CFG</code>,
the critical-damage schematic's authored subsystem roster (independently confirmed against
the subsystem records in <code>BTL4.RES</code>). Nothing is transcribed by hand.</p>
<p class="foot">The portraits are each mech's mission-review image
(<code>GAUGE/&lt;stem&gt;_MR.PCC</code>) resolved through the review screen's own palette.
In play, its zone indexes are live damage slots painted green&rarr;orange&rarr;red by the
ColorMapper gauges; this page shows every zone at the undamaged ramp entry &mdash; the
same green the armor doll idles at. Variants without their own portrait share the base's,
exactly as the resource file does.</p>
<p class="foot">Mech stats are the original player's manual stat sheets (pp. 25, 26, 29).
The manual documents the six 4.0 hulls; Avatar and Sunder arrived with 4.10 and have no
sheet, so their rows show a dash rather than an invented number. FIXED vs twisting torso
is read from the shipped resource file: a mech twists only if its subsystem record carries
the <code>jointtorso</code>/<code>jointtshadow</code> joints &mdash; the four fixed mechs
match the manual's Torso&nbsp;0/0 rows exactly.</p>
<p class="foot">Weapon notes: ER lasers fire red beams, PPCs blue, standard lasers yellow.
Authored ranges where the game states them: PPCs 900&nbsp;m, missiles 800&nbsp;m, the
Blackhawk's lasers 500&nbsp;m, the AFC&nbsp;50 750&nbsp;m. The Blackhawk's SRM&nbsp;6
racks fire Streak ammunition. LRM tube counts are the salvo size; each launcher's ammo
bin is listed beside it.</p>
</main>
<script>
const cards = [...document.querySelectorAll('.mech')];
const count = document.getElementById('count');
const search = document.getElementById('search');
let filter = 'all';
function apply() {
const q = search.value.trim().toLowerCase();
let shown = 0;
for (const c of cards) {
const okTag = filter === 'all' || c.dataset.tags.split(' ').includes(filter);
const okQ = !q || c.dataset.search.includes(q);
const on = okTag && okQ;
c.hidden = !on;
if (on) shown++;
}
count.textContent = shown + ' / ' + cards.length + ' mechs';
document.getElementById('nomatch').hidden = shown > 0;
}
for (const b of document.querySelectorAll('[data-filter]')) {
b.addEventListener('click', () => {
filter = b.dataset.filter;
for (const o of document.querySelectorAll('[data-filter]'))
o.setAttribute('aria-pressed', String(o === b));
apply();
});
}
search.addEventListener('input', apply);
apply();
</script>
</body>
</html>
+5 -18
View File
@@ -5,11 +5,8 @@
build. Section A is the payoff -- please verify.
=============================================================================
HOW TO REPORT: drop a line in Discord with the issue number and PASS /
FAIL / what you saw. Screenshots and rough times help.
LOGS -- NEW, ONE CLICK: double-click SENDLOGS.bat in the game folder after
the session. It puts ONE small zip on your Desktop with every log from the
night (session logs, matchlogs, launch breadcrumbs) -- attach that one file.
No more hunting for the right .log, no more missing parts.
FAIL / what you saw. Screenshots and rough times help; logs get collected
as usual.
=============================================================================
-----------------------------------------------------------------------------
@@ -37,19 +34,9 @@ A. FIXED -- PLEASE VERIFY (headliners first: new fixes fielded THIS build)
TEST: take a leak (ram someone) -- the generator lamp for THAT loop
flashes, and stops when it should.
#136 Leaking SHUT loop responds to component toggles
EXPLAINED AS AUTHENTIC (refined): the leak voice STARTS at the leak,
LOOPS on its own while the leak persists, STOPS when it seals -- so
hearing it again does NOT mean a new leak. The lamp FLASH is
continuous while leaking. Confirm and we close it.
#172 "Coolant leak but no flashing indicator" -- WHERE TO LOOK
The lamp always flashed; it was in a place nobody watches. A
GENERATOR leak flashes the generator button BY THE RADAR (bottom
centre) -- NOT any MFD. That is authentic: no MFD carries a
generator-leak indicator. A WEAPON's leak lamp lives on its
engineering page and flashes when you page there (also authentic).
Loop leaks flash the loop button immediately. TEST Friday: take a
leak, look AT THE RADAR RAIL -- tell us if it still reads as missing
even knowing where to look.
EXPLAINED AS AUTHENTIC: the alarm SOUND is one-shot per new leak,
the lamp FLASH is continuous while leaking. Confirm the description
matches what you see and we close it.
#128 Ramming terrain = instant coolant leak
Ram damage eligibility fixed -- walls and rocks no longer puncture
loops. TEST: bump terrain hard; leaks come from combat, not curbs.
-15
View File
@@ -401,17 +401,6 @@ void
<< " tempo=" << (int)tempo
<< " divPerBeat=" << (int)divisionsPerBeat << std::endl; } }
// #172 field receipt, ALWAYS-ON for LOOPED sequences (the alarm family --
// leak warning, lock beeper; a handful per mission): night-16 proved the
// voice loop is the testers' CLOCK ("it said coolant leak 5-6 times") yet
// no field receipt tied voice to leak edges -- the fatal-case timeline had
// to be reconstructed from matchlog wallclocks. One line per Start/Stop
// EDGE makes voice-vs-lamp timing readable straight from any field log.
// (Looped only: one-shot effects fire constantly and would spam.)
if (isLooped && !isRunning)
DEBUG_STREAM << "[seqloop] START seq=" << (void *)this
<< " tempo=" << (int)tempo << "\n" << std::flush;
Check(this);
//
@@ -449,10 +438,6 @@ void
if (!isRunning)
return;
if (isLooped) // #172 receipt (see StartSequence)
DEBUG_STREAM << "[seqloop] STOP seq=" << (void *)this
<< "\n" << std::flush;
//
// Find next on or off event
//
+12 -55
View File
@@ -2798,7 +2798,6 @@ GaugeRenderer::GaugeRenderer():
{
Test_Tell("GaugeRenderer::GaugeRenderer()\n");
Check_Pointer(this);
staticEntitiesFilled = False; // radar map geometry: filled per mission
int
i;
@@ -3016,13 +3015,6 @@ void
Test_Tell("GaugeRenderer::LinkToEntity(" << entity << ")\n");
// RADAR MAP GEOMETRY (2026-08-13): a new mission link invalidates the
// persistent static grid -- clear it and let the next RebuildEntityGrid
// refill from the new mission's entities (the pod process hosts many
// missions back to back; stale pointers must not cross rounds).
staticEntities.Clear();
staticEntitiesFilled = False;
//
//--------------------------------------------------------------------
// Inform all gauges
@@ -3621,6 +3613,7 @@ void
Check(this);
movingEntities.Clear();
staticEntities.Clear();
if (application == NULL)
{
@@ -3636,18 +3629,13 @@ void
// projectiles that AllEntityIterator would flood the grid with -- iterate the
// dynamic masters (the local + AI/dummy vehicles) plus the dynamic replicants
// (peer vehicles in multiplayer).
//
// STATIC GRID SPLIT (2026-08-13, the missing-radar-map-geometry fix): the
// per-frame rebuild used to Clear() + refill staticEntities from the SAME
// dynamic iterators -- which structurally excluded the map's STATIC-flagged
// UnscalableTerrain placements (528 of the 1071 outlined map records; on the
// arena maps essentially every wall/building), so MapDisplay::DrawStatic had
// nothing to draw and the radar showed no map. staticEntities is now
// PERSISTENT: filled ONCE per mission below (every UnscalableTerrain-derived
// entity carrying a rtype-18 GaugeImage -- static AND dynamic, so the
// CulturalIcon props the 2026-07-12 phantom-red-pip fix routed here still
// arrive), pruned by NotifyOfBecomingUninterestingEntity, reset at
// LinkToEntity. The per-frame loops below rebuild only the MOVING grid.
// CLASSIFICATION (the phantom-red-pip fix, 2026-07-12): the dynamic
// iterators ALSO surface non-Mover world entities (the map's CulturalIcon
// props, class 0x5E, register dynamic on this port) -- unsorted, they drew
// through the radar's MOVING loop as RED contacts ("multiple pips with one
// player"). Sort by Mover derivation: vehicles into the moving grid (red
// pips), everything else into the static grid (the dim silhouette layer
// DrawStatic draws) -- the follow-up the old note deferred.
{
HostManager::DynamicMasterEntityIterator master_iterator(host_manager);
master_iterator.First();
@@ -3667,6 +3655,8 @@ void
if (st != 2 && st != 9)
movingEntities.Add(entity);
}
else
staticEntities.Add(entity);
}
}
{
@@ -3681,43 +3671,10 @@ void
if (st != 2 && st != 9)
movingEntities.Add(entity);
}
else
staticEntities.Add(entity);
}
}
//
// THE ONE-TIME STATIC FILL. Authoritative regardless of the interest
// feed's health: walk EVERY entity once per mission and keep the
// UnscalableTerrain-derived ones that carry a gauge representation (the
// rtype-18 GaugeImage in their model list -- the same records
// docs/MAPS.html renders). The class gate is UnscalableTerrain, NOT
// Terrain: the shipped content has ZERO Terrain records -- its map
// geometry is 1008x UnscalableTerrain + 789x CulturalIcon (a subclass),
// and our headers derive UnscalableTerrain from Entity, so the old
// Terrain gate matched nothing (see the btl4grnd.cpp note on @0x4e6ef8).
// Clear() first so any pre-fill interest-feed adds can't duplicate.
//
if (!staticEntitiesFilled)
{
staticEntities.Clear();
HostManager::AllEntityIterator all_iterator(host_manager);
Entity *entity;
int kept = 0;
while ((entity = all_iterator.ReadAndNext()) != NULL)
{
if (!entity->IsDerivedFrom(*UnscalableTerrain::GetClassDerivations()))
continue;
if (application->GetResourceFile()->SearchList(
entity->GetResourceID(),
ResourceDescription::GaugeImageStreamResourceType) == NULL)
continue;
staticEntities.Add(entity);
++kept;
}
staticEntitiesFilled = True;
if (getenv("BT_RADAR_LOG"))
DEBUG_STREAM << "[radar] static grid filled: " << kept
<< " map entities with gauge outlines\n" << std::flush;
}
Check_Fpu();
}
-5
View File
@@ -673,11 +673,6 @@ protected:
GraphicsPort *graphicsPort[maximumGraphicsPorts];
GaugeEntityList movingEntities;
GaugeEntityArray staticEntities;
// RADAR MAP GEOMETRY (2026-08-13): staticEntities is PERSISTENT, filled
// ONCE per mission by RebuildEntityGrid's authoritative walk (and pruned by
// NotifyOfBecomingUninterestingEntity). Reset at LinkToEntity so a pod
// process that hosts many missions refills per round.
Logical staticEntitiesFilled;
LampManager *lampManager;
GaugeAlarmManager *gaugeAlarmManager;
+5 -38
View File
@@ -155,10 +155,6 @@ static void
static void __cdecl
OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t *status)
{
if (!steamActive)
{
return; // #170: the quiesce window -- the process is dying;
} // no accepts, no table writes, just survive the call
ISteamNetworkingSockets *sockets = SteamNetworkingSockets();
switch (status->m_info.m_eState)
@@ -638,8 +634,7 @@ unsigned long long
}
//
// #163/#170 -- the ORDERED transport QUIESCE for process exit
// (2026-08-11, corrected 2026-08-12 -- see the #170 block at the tail).
// #163 -- the ORDERED transport shutdown for process exit (2026-08-11).
//
// The port never called SteamAPI_Shutdown(): every round-end died via bare
// ExitProcess with live SNS connections and Steam callback threads, and the
@@ -660,10 +655,6 @@ void
{
return;
}
steamActive = 0; // #170 FIRST: gate every seam entry
// (Pump/Send/Recv/Accept) against
// re-entry from the marshal or game
// thread while the table is torn down
ISteamNetworkingSockets *sockets = SteamNetworkingSockets();
for (int i = 0; i < MaxConnections; ++i)
{
@@ -671,9 +662,7 @@ void
&& connections[i].connection != k_HSteamNetConnection_Invalid)
{
sockets->CloseConnection(connections[i].connection, 0,
"round over", true); // #170 linger: let the close packet
// actually transmit -- peers should
// read "round over", not a timeout
"round over", false);
}
connections[i].inUse = 0;
}
@@ -685,29 +674,7 @@ void
listenSockets[p] = k_HSteamListenSocket_Invalid;
}
}
sockets->RunCallbacks(); // one SNS drain; our status callback
// stays REGISTERED and this module
// stays LOADED, so any late dispatch
// lands in live (guarded) code
Sleep(60); // bounded grace for the lingered closes
//
// #170 -- DELIBERATELY NO SteamAPI_Shutdown(). The first #163 fix called
// it here, and that OVER-CORRECTED into tonight's field crash: at round
// end every peer closes simultaneously, so ClosedByPeer/ProblemDetected
// status events are ALWAYS in flight when this runs -- and Shutdown tears
// the client state down while steamclient's own service thread is still
// dispatching them. Field signature (night16, 7 hits / 3 machines): a
// steamclient-internal thread (BaseThreadInitThunk root, ZERO btl4
// frames) calling literal NULL (0xc0000005 access=8 target=0x0) in the
// window between Shutdown and TerminateProcess. The contract now
// [T2 field-proven]: quiesce only; EVERY caller must die via
// TerminateProcess (never a CRT return -- that re-opens the DLL-detach
// deadlock this function no longer prevents). TerminateProcess kills
// all threads atomically with every pointer still valid; the Steam
// client GCs the session on IPC pipe break (the standard crashed-game
// path -- build 883 relaunched all night without a single Shutdown
// call). The 883 child-hang-in-SteamAPI_Init needed a WEDGED
// (detach-deadlocked, half-dead) sibling; TerminateProcess makes wedges
// structurally impossible, so the child boots clean without Shutdown.
//
SteamAPI_RunCallbacks(); // let the closes flush client-side
steamActive = 0;
SteamAPI_Shutdown();
}
+3 -5
View File
@@ -33,11 +33,9 @@ int
void
BTSteamNet_Pump(); // callbacks + rx drain (game thread)
void
BTSteamNet_ShutdownAll(); // #163/#170: transport QUIESCE for
// process exit -- gate the seam, close
// every connection (linger), drain.
// NO SteamAPI_Shutdown (#170: NULL-call
// crash); caller MUST TerminateProcess
BTSteamNet_ShutdownAll(); // #163: ordered transport death for
// process exit -- close every
// connection, then SteamAPI_Shutdown
//
// The wire-seam surface (BTNet* wrappers in L4NET.CPP + the marshal).
+2 -100
View File
@@ -1180,115 +1180,17 @@ void BTDrawCockpitPanels(LPDIRECT3DDEVICE9 device)
CkFill(device, 0, L.viewY, L.viewX, L.viewH, band); // left
CkFill(device, L.viewX + L.viewW, L.viewY, L.canvasW - (L.viewX + L.viewW), L.viewH, band); // right
// 2a) #172 ALERT RING (glass-only deviation, #154-class, user-approved
// 2026-08-13 -- the first cut, a +8px grown-face bloom, read as "the
// button got fat" on the bench and was rejected): an ALERTING lamp in
// a surround strip bank is a ~6px sliver (minimumStrip) -- Elengil's
// fatal GeneratorB leak FLASHED lamp 0x1b for 28.4s and nobody saw it
// (night-16; the pod's physical backlit buttons don't have this
// problem). Draw a thin PULSING OUTLINE 3px outside the lamp's
// border, UNDER everything: the lamp keeps its exact size, neighbors
// repaint their own faces, the surfaces cover the inward arc, and
// only the outward ring survives -- respecting the "no on-top
// overlay" rule (twice field-broken, see the note below step 3).
// The ring blinks WITH the lamp (same brightness sample) and only
// while it genuinely alternates (flash bits set AND levels differ);
// it vanishes on the lamp's own dark half-cycle, so it reads as
// "alarm light" rather than geometry.
// GATED OFF BY DEFAULT (BT_ALERT_RING=1 to enable), 2026-08-13: the
// bench + Elengil's layout receipts proved the lamp paints and blinks
// correctly on screen (see [lampblink]; code-identical loop in 913) --
// the field issue is EXPECTATION (generator leak lights the button by
// the RADAR, authentically; testers watch the MFDs), which the
// handout now states. The ring stays available in case Friday shows
// testers still miss it KNOWING where to look.
// 2) Button lamp faces (UNDER the surfaces -- only the protruding edge shows).
unsigned long tick = GetTickCount();
static int s_alertRing = -1;
if (s_alertRing < 0)
{ const char *ar = getenv("BT_ALERT_RING"); s_alertRing = (ar && *ar == '1') ? 1 : 0; }
const int kRingGap = 3; // lamp border -> ring inner edge
const int kRingWidth = 2;
for (int i = 0; s_alertRing && i < L.buttonCount; i++)
{
const BTCockpitBtn &b = L.buttons[i];
int state = CkLampState(b.address);
if ((state & 0x3) != 0
&& (((state >> 2) & 0x3) != ((state >> 4) & 0x3)))
{
int shade = BTLampBrightnessOf(state, tick);
if (shade < 2) continue; // dark half-cycle: no ring
D3DCOLOR fill, border;
CkLampColors(b.colorClass, 3 /*bright*/, b.inert, &fill, &border);
int rx = b.x - kRingGap - kRingWidth;
int ry = b.y - kRingGap - kRingWidth;
int rw = b.w + 2 * (kRingGap + kRingWidth);
int rh = b.h + 2 * (kRingGap + kRingWidth);
// four bars = the ring; the gap between ring and lamp stays band-dark
CkFill(device, rx, ry, rw, kRingWidth, fill); // top
CkFill(device, rx, ry + rh - kRingWidth, rw, kRingWidth, fill); // bottom
CkFill(device, rx, ry, kRingWidth, rh, fill); // left
CkFill(device, rx + rw - kRingWidth, ry, kRingWidth, rh, fill); // right
}
}
// 2b) Button lamp faces (UNDER the surfaces -- only the protruding edge shows).
for (int i = 0; i < L.buttonCount; i++)
{
const BTCockpitBtn &b = L.buttons[i];
int state = CkLampState(b.address);
int shade = BTLampBrightnessOf(state, tick);
int shade = BTLampBrightnessOf(CkLampState(b.address), tick);
if (gCkPressed == b.address || gCkLatched[b.address & 0x7F]) shade = 3;
D3DCOLOR fill, border;
CkLampColors(b.colorClass, shade, b.inert, &fill, &border);
CkFill(device, b.x, b.y, b.w, b.h, border);
CkFill(device, b.x + 1, b.y + 1, b.w - 2, b.h - 2, fill);
// #172 SHADE-TRANSITION receipt (BT_LAMP_LOG only, first 24 blinks):
// proves the surround paint loop actually COMPUTES the alternation --
// the "did the pixels flash" question, answered without a screenshot
// (the PNG-capture rig cratered the frame rate to ~1fps and was
// scrapped). This base fill loop is code-identical to 913's, so its
// verdict applies to the field build.
if (getenv("BT_LAMP_LOG"))
{
static unsigned char s_lastShade[128];
static int s_blinkLog = 0;
int slot2 = b.address & 0x7F;
if ((state & 0x3) != 0
&& (((state >> 2) & 0x3) != ((state >> 4) & 0x3))
&& (unsigned char)shade != s_lastShade[slot2]
&& s_blinkLog < 24)
{
++s_blinkLog;
DEBUG_STREAM << "[lampblink] 0x" << std::hex << b.address
<< std::dec << " shade " << (int)s_lastShade[slot2]
<< "->" << shade << " tick=" << tick
<< "\n" << std::flush;
}
s_lastShade[slot2] = (unsigned char)shade;
}
// #172 paint receipt: one line per ALERT edge per lamp -- the night-16
// forensics could prove the lamp STATE but had zero visibility into
// pixels ("the seam-to-pixels observability hole"). Cheap: fires only
// when a lamp's alternating-status flips.
{
static unsigned char s_wasAlert[128];
int alerting = ((state & 0x3) != 0
&& (((state >> 2) & 0x3) != ((state >> 4) & 0x3))) ? 1 : 0;
int slot = b.address & 0x7F;
if (alerting != s_wasAlert[slot])
{
s_wasAlert[slot] = (unsigned char)alerting;
DEBUG_STREAM << "[lamppx] 0x" << std::hex << b.address
<< (alerting ? " alert ON" : " alert OFF")
<< " cell=" << std::dec << b.w << "x" << b.h
<< (alerting ? "+ring" : "")
<< " at " << b.x << "," << b.y
<< " state=0x" << std::hex << state << std::dec
<< "\n" << std::flush;
}
}
}
// 3) Gauge surfaces on top (green-tinted mono / palette sec). Reuses the
+33 -46
View File
@@ -216,28 +216,7 @@ static LONG WINAPI
if (eip >= base && eip < base + 0x2000000UL)
std::cout << "btl4+0x" << std::hex << (eip - base);
else
{
//
// #170: name the owning MODULE -- a foreign-DLL stack is
// unresolvable after the fact without its (ASLR'd) base; the
// steam-worker NULL-call needed this and didn't have it.
//
MEMORY_BASIC_INFORMATION fmbi;
char dll_path[MAX_PATH];
if (VirtualQuery((void *)eip, &fmbi, sizeof(fmbi)) != 0
&& fmbi.AllocationBase != NULL
&& GetModuleFileNameA((HMODULE)fmbi.AllocationBase,
dll_path, sizeof(dll_path)) != 0)
{
const char *dll_name = dll_path;
for (const char *s = dll_path; *s; ++s)
if (*s == '\\' || *s == '/') dll_name = s + 1;
std::cout << dll_name << "+0x" << std::hex
<< (eip - (unsigned long)fmbi.AllocationBase);
}
else
std::cout << "0x" << std::hex << eip;
}
std::cout << "0x" << std::hex << eip;
MEMORY_BASIC_INFORMATION mbi;
if (ebp == 0 || (ebp & 3) != 0
|| VirtualQuery((void *)ebp, &mbi, sizeof(mbi)) == 0
@@ -731,18 +710,29 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
lt = shifted;
}
}
// ONE NIGHT = ONE FILE (the 8 MB size roll-over RETIRED 2026-08-13).
// The roll existed because the evidence channel is a Discord
// attachment (free-tier cap: 10 MB today; 8 MB when this shipped) --
// but multi-part files cost us field evidence the moment they
// existed: testers attach "the log" (the newest part) and part 0
// stays home (night16: Elengil's 20:17 ET incident window was in the
// unsent pre-rotation part). Transport is now SENDLOGS.bat (ships
// in the dist root): zips every log/matchlog/lastrun to one ~10:1
// compressed attachment, so raw file size no longer matters and the
// whole night reads as one uninterrupted file.
wsprintfA(resolvedLog, "%s_%04d%02d%02d.log",
logStem, (int)lt.wYear, (int)lt.wMonth, (int)lt.wDay);
// SIZE ROLL-OVER -- never deletes anything, but keeps each FILE small
// enough to actually send. A playtest evening concatenates into one
// file (4.3 MB from a single session in scratchpad/night5/), and the
// channel this evidence travels through is a chat attachment. Part 0
// is <stem>_YYYYMMDD.log; once it passes the cap the NEXT launch opens
// <stem>_YYYYMMDD.1.log, and so on. Checked only at open, so one very
// long session can still overrun -- that is deliberate: never split a
// live session across files.
const DWORD part_max = 8UL * 1024UL * 1024UL;
for (int part = 0; part < 1000; ++part)
{
if (part == 0)
wsprintfA(resolvedLog, "%s_%04d%02d%02d.log",
logStem, (int)lt.wYear, (int)lt.wMonth, (int)lt.wDay);
else
wsprintfA(resolvedLog, "%s_%04d%02d%02d.%d.log",
logStem, (int)lt.wYear, (int)lt.wMonth, (int)lt.wDay, part);
WIN32_FILE_ATTRIBUTE_DATA fad;
if (!GetFileAttributesExA(resolvedLog, GetFileExInfoStandard, &fad))
break; // does not exist yet -- use it
if (fad.nFileSizeHigh == 0 && fad.nFileSizeLow < part_max)
break; // still room to append
}
logSelfNamed = 1;
}
}
@@ -1251,20 +1241,17 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
if (BTFrontEnd_Run(&fe_spec) != 0 || fe_spec.mode == BTFeLaunchNone)
{
BTLastrunNote("clean exit: player quit from the menu");
//
// #163/#170: the menu keeps the Steam transport up for the
// lobby, and a CRT return would run DLL detach with live
// Steam threads (the 883 wedge). ShutdownAll is now a
// QUIESCE -- it deliberately does NOT stop those threads
// (SteamAPI_Shutdown mid-dispatch was the #170 NULL-call
// crash) -- so this path must die exactly like every other
// exit: flush, quiesce, TerminateProcess. One topology.
//
#ifdef BT_STEAM
// #163: the menu keeps the Steam transport up for the lobby;
// returning through the CRT would run DLL detach with live
// Steam threads -- the same intermittent deadlock the
// round-end relaunch hit. Tear it down deliberately first.
{
extern void BTOrderlyDie(void);
BTOrderlyDie(); // never returns
extern void BTSteamNet_ShutdownAll();
BTSteamNet_ShutdownAll();
}
return 0; // unreachable (TerminateProcess)
#endif
return 0; // quit from the menu
}
char fe_arguments[192];
char fe_value[64];
+3 -3
View File
@@ -181,9 +181,9 @@ static BOOL CALLBACK
return TRUE;
}
void
BTOrderlyDie(void) // #170: non-static -- the menu-quit
{ // path in btl4main dies through here
static void
BTOrderlyDie(void)
{
{
extern void BTMatchLogClose();
BTMatchLogClose(); // complete receipts on disk (the
+2 -19
View File
@@ -362,21 +362,7 @@ void
{
movingEntities.Add(entity); // FUN_004434f4, this+0x94
}
//
// GATE CORRECTED 2026-08-13 (the missing-radar-map-geometry fix): this
// was transcribed as Terrain::GetClassDerivations() -- but the shipped
// content has ZERO Terrain records (census: 1008x UnscalableTerrain +
// 789x CulturalIcon, both deriving from Entity in our headers), so the
// Terrain predicate was dead code and rejected 100% of map geometry.
// The binary's @0x4e6ef8 must have been UnscalableTerrain's derivation
// (or the shipped-machine MUNGA derived UnscalableTerrain from
// Terrain). [T3 on which; the corrected gate matches the content.]
// The add is guarded on the one-time fill (GAUGREND RebuildEntityGrid)
// not having run -- post-fill notifications would double-add, and the
// fill's Clear()+walk supersedes any pre-fill adds.
//
else if (!staticEntitiesFilled
&& entity->IsDerivedFrom(*UnscalableTerrain::GetClassDerivations()))
else if (entity->IsDerivedFrom(*Terrain::GetClassDerivations())) // 0x4e6ef8
{
staticEntities.Add(entity); // FUN_00443864, this+0xb0
}
@@ -423,10 +409,7 @@ void
{
movingEntities.Remove(entity); // FUN_00443510, this+0x94
}
// Gate corrected 2026-08-13 to match the ADD side (see the note there):
// UnscalableTerrain, not Terrain. Post-fill this is the prune path for
// a destroyed/expiring static -- it leaves the persistent radar grid.
else if (entity->IsDerivedFrom(*UnscalableTerrain::GetClassDerivations()))
else if (entity->IsDerivedFrom(*Terrain::GetClassDerivations())) // 0x4e6ef8
{
staticEntities.Remove(entity); // FUN_00443930, this+0xb0
}
+18 -36
View File
@@ -1233,47 +1233,29 @@ void
{
Mech__DamageZone *zone =
(Mech__DamageZone *)damageZones[zoneIndex]; // this[0x120][idx]
Subsystem *critted = 0;
//
// #174 (2026-08-13): the binary's zone-state guard wraps the WHOLE
// per-burst application -- `if (piVar1[9] != 1) { crit-roll |
// TakeDamage; tally; destroyed-flag }` (part_012.c:14661-14677,
// @0x4a0446) -- a burst landing on an already-destroyed zone is a
// structural NO-OP: no re-application, no cascade re-fire, NO SCORE.
// The port had narrowed the guard to the crit roll only, so
// continued fire into a dead zone re-saturated it every burst ->
// Mech__DamageZone::TakeDamage's >=1.0 tail re-ran
// RecurseSegmentTable (night-16: zone 18 x7, zone 9 x4 in single
// missions), each pass re-pushing the crit allotment into the
// surviving subsystems (the ram "crits worsened with each
// collision") -- and the shooter's damageTally kept scoring hits
// on wreckage the binary scores as ZERO.
//
if (zone->GetDamageZoneState() != DamageZone::BurningState) // zone state != 1
if (zone->GetDamageZoneState() != DamageZone::BurningState // zone state != 1
&& BTMechCriticalChance(this, zoneIndex)) // @0x4a0164 (roll below)
{
Subsystem *critted = 0;
if (BTMechCriticalChance(this, zoneIndex)) // @0x4a0164 (roll below)
critted = zone->CriticalHit(message->damageData); // @0049ccc4
if (critted != 0)
{
critted = zone->CriticalHit(message->damageData); // @0049ccc4
if (critted != 0)
{
damageTally +=
((MechSubsystem *)critted)->CriticalScoreBonus(); // +0x108
if (BTEnvOn("BT_CRIT_LOG", 0))
DEBUG_STREAM << "[critroll] zone=" << zoneIndex
<< " -> " << (critted->GetName() ? critted->GetName() : "?")
<< " subLvl=" << ((MechSubsystem *)critted)->GetSubsystemDamageLevel()
<< "\n" << std::flush;
}
damageTally +=
((MechSubsystem *)critted)->CriticalScoreBonus(); // +0x108
if (BTEnvOn("BT_CRIT_LOG", 0))
DEBUG_STREAM << "[critroll] zone=" << zoneIndex
<< " -> " << (critted->GetName() ? critted->GetName() : "?")
<< " subLvl=" << ((MechSubsystem *)critted)->GetSubsystemDamageLevel()
<< "\n" << std::flush;
}
if (critted == 0)
zone->TakeDamage(message->damageData); // zone vtbl+0x18 @0x4a0488
}
if (critted == 0)
zone->TakeDamage(message->damageData); // zone vtbl+0x18 @0x4a0488
damageTally += message->damageData.damageAmount; // +0x30
if (zone->GetDamageZoneState() == DamageZone::BurningState)
zoneDestroyed = 1; // set only by the burst that
} // DESTROYS it (binary: inside
// the guard, part_012.c:14674)
damageTally += message->damageData.damageAmount; // +0x30
if (zone->GetDamageZoneState() == DamageZone::BurningState)
zoneDestroyed = 1;
if (--burstsLeft == 0)
break;
-98
View File
@@ -7494,104 +7494,6 @@ void
}
}
// #165 BENCH (BT_CRIT_SUBSYS=<name>[,<name>...]): kill named subsystems
// through the REAL crit path -- Damage record -> ApplyDamageAndMeasure ->
// virtual TakeDamage -- NOT the direct cell writes BT_KILL_SUBSYS does.
// This is the path the field takes (the crit lottery), and the path the
// old 4,213-refusal bench never exercised: BT_KILL_SUBSYS wrote
// statusAlarm AND the zone directly, so it validated the fire gate while
// the field's writer (MechWeapon::TakeDamage) silently dropped the
// destroyed-side write. Applies one energy-damage chunk per frame from
// frame 900 until the subsystem's own zone saturates ([crit] receipt
// fires from MechSubsystem::TakeDamage on the edge), then stops.
// Env-gated, viewpoint mech only.
if ((Entity *)this == application->GetViewpointEntity()
&& getenv("BT_CRIT_SUBSYS"))
{
static int s_csFrame = 0;
if (++s_csFrame >= 900) // same settle window as killsub
{
char want_list[256];
strncpy(want_list, getenv("BT_CRIT_SUBSYS"), sizeof(want_list) - 1);
want_list[sizeof(want_list) - 1] = 0;
for (char *want = strtok(want_list, ","); want != 0;
want = strtok(0, ","))
{
// PARTIAL form ("Name=0.5"): stop at that zone level -- the
// freeze-regression phase (a partially-critted weapon must
// keep recycling; the old port tail froze it at alarm 1).
float target = 1.0f;
char *eq = strchr(want, '=');
if (eq != 0)
{
*eq = 0;
target = (float)atof(eq + 1);
}
for (int i = 2; i < GetSubsystemCount(); ++i)
{
Subsystem *s = GetSubsystem(i);
if (s == 0 || s->GetName() == 0) continue;
if (stricmp(s->GetName(), want) != 0) continue;
if (!s->IsDerivedFrom(MechSubsystem::ClassDerivations)) continue;
MechSubsystem *ms = (MechSubsystem *)s;
if (ms->GetSubsystemDamageLevel() >= target) break; // done
Damage d;
d.damageType = Damage::EnergyDamageType;
d.damageAmount = 0.75f; // FINE-GRAINED: a small-laser
// private zone saturates on a
// single 20.0 chunk (first run);
// ~0.04 level/frame ramps let
// the PARTIAL stop actually stop
d.burstCount = 1;
ms->ApplyDamageAndMeasure(d); // FUN_004ac07c -> virtual TakeDamage
if ((s_csFrame % 30) == 0)
DEBUG_STREAM << "[critsub] '" << s->GetName()
<< "' zone=" << (float)ms->GetSubsystemDamageLevel()
<< std::endl << std::flush;
break;
}
}
}
}
// #174 BENCH (BT_ZONE_HAMMER=<zoneIdx>[,<amt>]): 4x/sec, dispatch a
// TakeDamage with an EXPLICIT damageZone at the viewpoint mech itself.
// Pointed at a descend=1 non-vital zone (dz_larm=2 on most chassis),
// the zone saturates, cascades ONCE, the mech survives, and the hammer
// keeps landing on the DEAD zone -- the exact repro of the night-16
// re-descend (zone 18 x7). With the burst-loop guard at binary scope:
// ONE [cascade] for the life and the [dmghit] stream for that zone
// STOPS at saturation (the guard skips application entirely).
if ((Entity *)this == application->GetViewpointEntity()
&& getenv("BT_ZONE_HAMMER"))
{
static int s_zhFrame = 0;
++s_zhFrame;
if (s_zhFrame >= 900 && (s_zhFrame % 15) == 0) // settle, then ~4 Hz
{
int zidx = 2;
float amt = 8.0f;
{
char spec[64];
strncpy(spec, getenv("BT_ZONE_HAMMER"), sizeof(spec) - 1);
spec[sizeof(spec) - 1] = 0;
char *comma = strchr(spec, ',');
if (comma != 0) { *comma = 0; amt = (float)atof(comma + 1); }
if (spec[0]) zidx = atoi(spec);
}
Damage dmg;
dmg.damageType = Damage::ExplosiveDamageType;
dmg.damageAmount = amt;
dmg.burstCount = 1;
dmg.impactPoint = localOrigin.linearPosition;
Entity::TakeDamageMessage td(
Entity::TakeDamageMessageID,
sizeof(Entity::TakeDamageMessage),
GetEntityID(), zidx, dmg);
Dispatch(&td);
}
}
// task #13 scripted verify (BT_VALVE_TEST=1): one MoveValve press at
// frame ~600 -- Condenser1's valveState cycles 1 -> 5, so the flow
// redistribution gives it 5/10 of the total coolant flow ([valve] log
-23
View File
@@ -142,11 +142,6 @@ MechSubsystem::MechSubsystem(
{
refCount = NewRefCount(0x10); // this[0x3d]
this->owner = owner; // this[0x34] -- the owning Mech (canonical)
simulationState = 0; // #165: was NEVER initialized anywhere --
// MemoryBlock pools recycle unzeroed
// (MEMBLOCK.cpp), so stale garbage ==1
// could brick a weapon from spawn and
// draw the seek-graph destroyed bitmap
controlDestination = 0; // this[0x3a] = param_7 (ctor pass-through;
controlMessageID = 0; // this[0x3b] = param_8 weapons override -- task #6)
vitalSubsystem = 0; // this[0x39]
@@ -192,8 +187,6 @@ MechSubsystem::MechSubsystem(
// ctor only set hostEntity; subclasses read
// `owner` widely -> must be set or it is garbage.)
statusAlarm.SetLevel(0); // FUN_0041bbd8(this+0xb, 0)
simulationState = 0; // #165: uninitialized-landmine fix (see
// the sibling ctor)
controlDestination = 0; // this[0x3a]=param_6 (ctor pass-through;
controlMessageID = 0; // this[0x3b]=param_7 weapons override -- task #6)
resource = subsystem_resource; // this[0x3c]
@@ -286,7 +279,6 @@ Scalar MechSubsystem::GetSubsystemDamageLevel() const
void MechSubsystem::ForceCriticalFailure()
{
statusAlarm.SetLevel(1); // +0x2C: 1 = Destroyed
simulationState = 1; // #165 twin (see TakeDamage)
if (printSimulationState) // +0x104 gate
{
PrintState(); // vtable slot 0x34 @4ac8c0
@@ -311,7 +303,6 @@ void MechSubsystem::ForceCriticalFailure()
void MechSubsystem::RespawnRepair()
{
statusAlarm.SetLevel(0); // operational
simulationState = 0; // #165 twin (see TakeDamage)
if (damageZone != 0)
{
((DamageZone *)damageZone)->damageLevel = 0.0f;
@@ -367,7 +358,6 @@ Logical
if (StatusThreshold <= ((DamageZone *)damageZone)->damageLevel) // _DAT_004ac140 (engine view)
{
statusAlarm.SetLevel(1); // FUN_0041bbd8(this+0xb, 1)
simulationState = 1; // #165 twin (see TakeDamage)
if (printSimulationState != 0) // this[0x41]
{
OnAlarmChanged(); // (*this.vtable+0x34)(this)
@@ -396,7 +386,6 @@ void
((DamageZone *)damageZone)->damageLevel = 1.0f; // dz+0x158 = 1.0 (engine view)
}
statusAlarm.SetLevel(1); // FUN_0041bbd8(this+0xb, 1)
simulationState = 1; // #165 twin (see TakeDamage)
if (printSimulationState != 0)
{
OnAlarmChanged(); // (*this.vtable+0x34)(this)
@@ -418,7 +407,6 @@ void
((DamageZone *)damageZone)->damageLevel = 0.0f; // dz+0x158 = 0 (engine view)
damageZone->alarm.SetLevel(0); // FUN_0041bbd8(dz+0x10, 0)
statusAlarm.SetLevel(0); // FUN_0041bbd8(this+0xb, 0)
simulationState = 0; // #165 twin (see TakeDamage)
if (printSimulationState != 0)
{
OnAlarmChanged();
@@ -532,18 +520,7 @@ void MechSubsystem::TakeDamage(Damage &damage)
if (dz->damageLevel >= 1.0f) // _DAT_004ac140
{
// #165: named destruction receipt on the EDGE only (damage keeps
// landing on dead subsystems; the write below is idempotent) -- the
// crit-path kill had NO field receipt, which is how an X'd-but-firing
// weapon hid for a month. Always-on: once per subsystem per life.
if (statusAlarm.GetLevel() != 1)
DEBUG_STREAM << "[crit] '" << (GetName() ? GetName() : "?")
<< "' DESTROYED (zone saturated)\n" << std::flush;
statusAlarm.SetLevel(1); // Destroyed
simulationState = 1; // #165: the binary's +0x40 is ONE
// cell; the port split it -- keep
// the twin in lockstep (gate clause
// 1, seek-graph destroyed bitmap)
if (printSimulationState) // +0x104
{
PrintState(); // vtable +0x34 @4ac8c0
+24 -29
View File
@@ -411,39 +411,34 @@ Logical
// @004b96d4 -- slot 9 (TASK #51 RENAME: this body was mislabeled slot-6
// ReadUpdateRecord; @004b96d4 chains @004b0efc = PoweredSubsystem::TakeDamage,
// which dispatches on *param==4 == Damage::damageType -- damage semantics).
//
// #165 ROOT CAUSE (night-16 hunt, 2026-08-13). The old body here called the
// raw ENGINE Subsystem::TakeDamage (zone accumulation ONLY) and then drove
// weaponAlarm from the zone state -- two defects in one:
// (1) it DROPPED the destroyed-side invariant: the binary keeps ONE
// destroyed cell per subsystem (+0x40 = statusAlarm level) that every
// destruction path writes and the fire gate reads. A weapon killed by
// the CRIT LOTTERY (the everyday field path) saturated its private
// zone (-> the MFD X, [techstat] condition 0) but never wrote the gate
// cell -- the X'd weapon kept firing (Oracle 20260812: ER PPC firing
// indefinitely; the PPC+SL pair firing until the arm CASCADE finally
// wrote the cell). The 4,213-refusal bench passed because
// BT_KILL_SUBSYS writes both cells -- it validated the gate, not the
// writers.
// (2) the weaponAlarm tail wrote the FSM cell BACKWARDS: saturation ->
// level 0 (Firing!), partial damage -> level 1 -- a state with NO case
// in either weapon FSM: the silent freeze (berserker's 20x [beam]
// STUCK-ON, and the state-1+beamFlag combination that drew Conn Man's
// 883 hanging beam -- #164's upstream cause). The binary never drives
// the FSM from damage: a destroyed weapon parks at Loading(3) via the
// fire gate's per-frame refusal (ResetFiringState, emitter.cpp).
// FIX: chain to MechSubsystem::TakeDamage -- the correctly-reconstructed
// binary base (@0x4ac0bc: zone TakeDamage, then damageLevel>=1.0 ->
// statusAlarm=1 + pin + vital-kill) -- and do NOT touch the FSM. The
// binary's electrical-short middle layer (@004b0efc) remains unreconstructed
// (a marked gap, unchanged); what is restored is the destruction invariant.
// MechSubsystem::TakeDamage guards damageZone==0 itself (the 2026-07-29
// Owens NULL-zone field crash stays guarded).
// The PoweredSubsystem::TakeDamage body (electrical-short handling) is not yet
// reconstructed; chain to the engine Subsystem base, and keep the damage-zone
// alarm tracking this port has shipped with (best-effort; the binary's alarm
// display on weapon damage flows through the unreconstructed @004b0efc chain).
//
void
MechWeapon::TakeDamage(Damage &damage)
{
MechSubsystem::TakeDamage(damage); // @0x4ac0bc semantics
// FIELD CRASH GUARD (2026-07-29, build 641): the engine base derefs
// damageZone unguarded; an Owens weapon reached here with zone == NULL
// the first night crits could land (#80). See ApplyDamageAndMeasure.
if (this->Subsystem::damageZone != 0)
Subsystem::TakeDamage(damage); // engine base (real chain: @004b0efc)
// Port behavior (kept): a destroyed weapon clears its display alarm;
// otherwise the alarm tracks the resolved damage-zone state.
::DamageZone *dz = this->Subsystem::damageZone;
if (dz != 0
&& dz->GetGraphicState() == DamageZone::DestroyedGraphicState)
{
weaponAlarm.SetLevel(0);
}
else
{
weaponAlarm.SetLevel(
(dz != 0 && dz->damageLevel > 0.0f) ? 1 : 0
);
}
}
//
-19
View File
@@ -1,19 +0,0 @@
@echo off
rem ==========================================================================
rem BT411 -- package tonight's logs into ONE Discord-attachable zip.
rem Double-click me after a playtest. The zip lands on your DESKTOP as
rem BTLOGS_<machine>_<date>.zip -- attach that one file in Discord.
rem Collects: every session log, matchlog, and launch breadcrumb. Nothing
rem is deleted or modified; this only makes a copy.
rem ==========================================================================
cd /d "%~dp0"
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$d = Get-Date -Format yyyyMMdd_HHmm;" ^
"$out = Join-Path ([Environment]::GetFolderPath('Desktop')) (\"BTLOGS_{0}_{1}.zip\" -f $env:COMPUTERNAME, $d);" ^
"$files = Get-ChildItem -Path 'content\*.log','content\lastrun_*.txt','content\matchlog_*.txt','content\cdb_*.txt' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt (Get-Date).AddHours(-36) };" ^
"if (-not $files) { Write-Host 'No logs found -- run me from the BT411 folder (next to the content directory).'; exit 1 }" ^
"Compress-Archive -Force -Path $files.FullName -DestinationPath $out;" ^
"Write-Host ('Wrote ' + $out + ' (' + $files.Count + ' files, ' + [math]::Round((Get-Item $out).Length/1MB,1) + ' MB)');"
echo.
echo Attach the BTLOGS zip from your Desktop in Discord. Thanks!
pause
+229
View File
@@ -0,0 +1,229 @@
"""Render every map in BTL4.RES the way the cockpit draws them.
The technique is the one worked out for Red Planet (RP412
tools/pages/navmap.py) and it transfers because both games are MUNGA:
a map stream is a run of Entity::MakeMessage records, each carrying its
own length, and each naming a Model List whose GaugeImage is the outline
the display draws.
Where BT differs from RP:
* The resource file has a real directory. Header is
(labOnly, maxID) at +4 and an offset table at +12; each descriptor
holds rid/rtype at +0, a 32-byte name at +8, and priority, flags,
offset and length at +40. RP412's file inlines the data after each
descriptor instead, so its walk does not apply here.
* rtype 1 is a Model List, 18 a GaugeImage, 14 a map stream. A model's
outline is the rtype-18 member of its list - a direct join, rather
than RP412's match on the model's name.
* Records are advanced by (length + 3) & ~3. RP's are not padded.
* Every primitive carries an explicit palette index; nothing falls back
to a display default the way RP's colour 0 does.
Colours are indices into btspal.pcc, the palette L4GAUGE.CFG configures
the secondary port with: configure(0,sec,270,0x003F,clut0,rgb,btspal.pcc).
PCC is PCX, so the palette is the last 769 bytes.
Usage: python tools/build_maps.py [out.html]
"""
import base64, collections, html, io, os, struct, sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
RES = open(os.path.join(ROOT, 'content', 'BTL4.RES'), 'rb').read()
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, 'docs', 'MAPS.html')
MODEL_LIST, SOLIDS, MAP_STREAM, GAUGE_IMAGE = 1, 9, 14, 18
# ------------------------------------------------------------- directory
labOnly, maxID = struct.unpack_from('<ii', RES, 4)
RESOURCES = {}
for off in struct.unpack_from('<%dI' % maxID, RES, 12):
if off == 0:
continue
rid, rtype = struct.unpack_from('<ii', RES, off)
name = RES[off + 8:off + 40].split(b'\0')[0].decode('ascii', 'replace')
prio, flags, addr, size = struct.unpack_from('<iIII', RES, off + 40)
RESOURCES[rid] = {'t': rtype, 'name': name, 'off': addr, 'len': size}
def members(rid):
r = RESOURCES.get(rid)
if not r or r['t'] != MODEL_LIST:
return ()
n = struct.unpack_from('<i', RES, r['off'])[0]
return struct.unpack_from('<%di' % n, RES, r['off'] + 4)
# A model's outline is simply the GaugeImage in its own Model List.
OUTLINE = {}
for rid, r in RESOURCES.items():
if r['t'] == MODEL_LIST:
g = [m for m in members(rid)
if RESOURCES.get(m, {}).get('t') == GAUGE_IMAGE]
if g:
OUTLINE[rid] = g[0]
def gauge_image(off):
p = off
vcount = struct.unpack_from('<i', RES, p)[0]; p += 4
verts = [struct.unpack_from('<3f', RES, p + 12 * i) for i in range(vcount)]
p += 12 * vcount
lods = struct.unpack_from('<i', RES, p)[0]; p += 4
scales = struct.unpack_from('<%df' % lods, RES, p); p += 4 * lods
out = []
for lod in range(lods):
pcount = struct.unpack_from('<i', RES, p)[0]; p += 4
prims = []
for _ in range(pcount):
_t, colour, attrs, n = struct.unpack_from('<4i', RES, p); p += 16
prims.append((colour, attrs, struct.unpack_from('<%di' % n, RES, p)))
p += 4 * n
out.append((scales[lod], prims))
return verts, out
def rotate(q, p):
qx, qy, qz, qw = q
x, y, z = p
tx = 2 * (qy * z - qz * y)
ty = 2 * (qz * x - qx * z)
tz = 2 * (qx * y - qy * x)
return (x + qw * tx + qy * tz - qz * ty,
y + qw * ty + qz * tx - qx * tz,
z + qw * tz + qx * ty - qy * tx)
def placements(rid):
"""-> ([(outline id, position, quaternion)], total records, model tally).
Each record is an Entity::MakeMessage: its own length at +0, flags at
+8, classToCreate at +28, the resource it names at +40, instanceFlags
at +44 and the origin at +48. Records shorter than 76 bytes cannot
carry an origin and are skipped - the arenas hold a couple of those."""
m = RESOURCES[rid]
count = struct.unpack_from('<i', RES, m['off'])[0]
p, end = m['off'] + 4, m['off'] + m['len']
drawn, total, tally = [], 0, collections.Counter()
for _ in range(count):
if p + 12 > end:
break
length = struct.unpack_from('<I', RES, p)[0]
if not (12 <= length <= 4096) or p + length > end:
break
total += 1
if length >= 76:
target = struct.unpack_from('<i', RES, p + 40)[0]
if target in OUTLINE:
drawn.append((OUTLINE[target],
struct.unpack_from('<3f', RES, p + 48),
struct.unpack_from('<4f', RES, p + 60)))
tally[RESOURCES[target]['name']] += 1
p += (length + 3) & ~3 # BT pads records; RP does not
return drawn, total, tally
# ---------------------------------------------------------------- palette
pal = open(os.path.join(ROOT, 'content', 'GAUGE', 'btspal.pcc'), 'rb').read()
if len(pal) < 769 or pal[-769] != 0x0C:
sys.exit('btspal.pcc has no VGA palette where PCX keeps one')
base = len(pal) - 768
PALETTE = [tuple(pal[base + i * 3:base + i * 3 + 3]) for i in range(256)]
def plan(drawn, box=520):
from PIL import Image, ImageDraw
xs = [d[1][0] for d in drawn]
zs = [d[1][2] for d in drawn]
mpp = max((max(xs) - min(xs)) / box, (max(zs) - min(zs)) / box, 1e-4) * 1.15
pad = 10
iw = max(24, int((max(xs) - min(xs)) / mpp) + pad * 2)
ih = max(24, int((max(zs) - min(zs)) / mpp) + pad * 2)
cx, cz = (min(xs) + max(xs)) / 2, (min(zs) + max(zs)) / 2
img = Image.new('RGB', (iw, ih), PALETTE[0])
dr = ImageDraw.Draw(img)
cache = {}
for gid, pos, q in drawn:
if gid not in cache:
cache[gid] = gauge_image(RESOURCES[gid]['off'])
verts, lods = cache[gid]
lod = 0
while lod < len(lods) and mpp > lods[lod][0]:
lod += 1
if lod >= len(lods):
continue
for colour, attrs, idx in lods[lod][1]:
pts = []
for k in idx:
if not (0 <= k < len(verts)):
continue
v = verts[k]
if attrs & 1: # attributeUnscaled
v = (v[0] * mpp, v[1] * mpp, v[2] * mpp)
g = rotate(q, v)
pts.append((iw / 2 + ((g[0] + pos[0]) - cx) / mpp,
ih / 2 - ((g[2] + pos[2]) - cz) / mpp))
if len(pts) > 1:
dr.line(pts, fill=PALETTE[colour], width=1)
buf = io.BytesIO()
img.convert('P', palette=Image.ADAPTIVE, colors=64).save(
buf, 'PNG', optimize=True)
return ('data:image/png;base64,' + base64.b64encode(buf.getvalue()).decode(),
iw, ih, max(xs) - min(xs), max(zs) - min(zs))
# The eight the game offers, from tools/mapscan.py.
PLAYABLE = ['cavern', 'grass', 'rav', 'polar3', 'polar4', 'arena1', 'arena2',
'dbase']
maps = []
for rid, r in RESOURCES.items():
if r['t'] != MAP_STREAM:
continue
drawn, total, tally = placements(rid)
art = plan(drawn) if drawn else None
maps.append({'name': r['name'], 'rid': rid, 'total': total,
'drawn': len(drawn), 'tally': tally, 'art': art,
'playable': r['name'] in PLAYABLE})
print(' %-10s %4d records, %4d drawn' % (r['name'], total, len(drawn)),
file=sys.stderr)
maps.sort(key=lambda m: (not m['playable'], -m['drawn']))
cards = []
for m in maps:
if m['art']:
uri, w, h, ex, ez = m['art']
art = ('<img class="plan" src="%s" width="%d" height="%d" alt="%s from '
'above" loading="lazy">' % (uri, w, h, html.escape(m['name'])))
extent = '%d &times; %d' % (round(ex), round(ez))
else:
art = '<p class="plan plan--none">Nothing this stream places is drawn</p>'
extent = '&mdash;'
top = ', '.join('%s &times;%d' % (html.escape(n), c)
for n, c in m['tally'].most_common(5)) or '&mdash;'
cards.append("""
<article class="map" data-tags="{tags}" data-search="{search}">
<figure class="fig">{art}</figure>
<h2>{name}</h2>
<p class="tag">{label}</p>
<dl class="stat">
<div><dt>Placements</dt><dd>{total}</dd></div>
<div><dt>Drawn</dt><dd>{drawn}</dd></div>
<div><dt>Extent</dt><dd>{extent}</dd></div>
</dl>
<p class="models">{top}</p>
</article>""".format(
tags='playable' if m['playable'] else 'aux',
search=html.escape(m['name'].lower()), art=art,
name=html.escape(m['name']),
label='OFFERED IN THE GAME' if m['playable'] else 'SUPPORTING STREAM',
total=m['total'], drawn=m['drawn'], extent=extent, top=top))
PAGE = open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'maps.tpl'), encoding='utf-8').read()
os.makedirs(os.path.dirname(OUT), exist_ok=True)
open(OUT, 'w', encoding='utf-8').write(PAGE.format(
cards=''.join(cards), n=len(maps),
playable=sum(1 for m in maps if m['playable'])))
print('wrote %s (%d maps)' % (OUT, len(maps)), file=sys.stderr)
+260
View File
@@ -0,0 +1,260 @@
"""Render the mech roster the way the game knows it.
Data is drawn from the shipped files, not transcribed:
* The ROSTER is `kVehicles[]` in game/glass/btl4fe.cpp -- the exact
18-entry catalog the in-game menu offers (key + display name, menu
order). Nothing is filtered, so a menu change flows through.
* Each mech's WEAPONS and AMMO BINS are its `cmCrit(...)` lines in
content/GAUGE/L4GAUGE.CFG -- the critical-damage schematic's authored
subsystem roster. (Independently confirmed against the rtype-17
subsystem records in BTL4.RES, 2026-08-12.)
* The PORTRAIT is the mech's own mission-review image
(GAUGE/<stem>_MR.PCC, shown by the cameraInit review context through
mrpal.pcc). Its zone indexes 32..63 are runtime ColorMapper slots --
the review screen paints damage per zone -- so the page shows the
HEALTHY state: every zone slot at adpal.pcc entry 0, the same green
the armor doll idles at. PCC is PCX; the palette is the file tail.
* MECH STATS come from the original player's manual stat sheets
(reference/manual/Tesla40_BT_manual.pdf pp. 25/26/29, transcribed
from page renders 2026-08-12). The manual documents the six 4.0
hulls only; the 4.10 additions (Avatar, Sunder) and all variants have
no sheet, and this page shows a dash rather than an invented number.
* FIXED vs TWISTING torso is derived from the shipped BTL4.RES: a mech
twists only if its subsystem record carries the jointtorso +
jointtshadow skeleton joints (Torso resolves them only when
torsoHorizontalEnabled, game/reconstructed/torso.cpp:270). The four
fixed mechs match the manual's Torso 0/0 rows exactly.
Usage: python tools/build_mechs.py [out.html]
"""
import base64, html, io, os, re, sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
GAUGE = os.path.join(ROOT, 'content', 'GAUGE')
OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, 'docs', 'MECHS.html')
# --------------------------------------------------------------- roster
src = open(os.path.join(ROOT, 'game', 'glass', 'btl4fe.cpp'),
encoding='utf-8', errors='replace').read()
block = re.search(r'kVehicles\[\]\s*=\s*\{(.*?)\};', src, re.S).group(1)
ROSTER = re.findall(r'\{\s*"(\w+)"\s*,\s*"([^"]+)"\s*\}', block)
if len(ROSTER) < 18:
sys.exit('roster parse found only %d entries' % len(ROSTER))
# Variant -> base chassis (kVehicles order groups them; the stems below agree).
VARIANT_OF = {'bhk1': 'blkhawk', 'thr1': 'thor', 'lok1': 'loki',
'lok2': 'loki', 'mad1': 'madcat', 'mad2': 'madcat',
'own1': 'owens', 'ava1': 'avatar', 'snd1': 'sunder',
'vul1': 'vulture'}
# Mission-review portrait stem per key (GAUGE/<stem>_MR.PCC; variants with no
# portrait of their own share the base's, as the RES does).
MR_STEM = {'blkhawk': 'BLH', 'bhk1': 'BLH', 'loki': 'LOKI', 'lok1': 'LOK1',
'lok2': 'LOKI', 'madcat': 'MAD', 'mad1': 'MAD1', 'mad2': 'MAD2',
'thor': 'THOR', 'thr1': 'THR1', 'owens': 'OWEN', 'own1': 'OWEN',
'avatar': 'AVA', 'ava1': 'AVA', 'sunder': 'SUN', 'snd1': 'SUN',
'vulture': 'VULT', 'vul1': 'VUL1'}
# Fixed-torso set, derived from BTL4.RES joint tables (see module docstring).
FIXED_TORSO = {'blkhawk', 'bhk1', 'owens', 'own1'}
# The manual stat sheets (pp. 25 Loki/Thor, 26 Vulture/MadCat, 29 Owens/
# Blackhawk). Keys: t tons, ap armor points, res reservoir liters, hs heat
# sinks, ts torso deg/s, tl torso limit deg, a100 0-100s, b100 100-0s,
# vn/vs/vg top speed normal / super charged / gimped kph.
MANUAL = {
'loki': dict(t=75, ap=2110, res=3800, hs=38, ts=60, tl=110,
a100=4.5, b100=4.5, vn=143, vs=182, vg=40, page=25),
'thor': dict(t=75, ap=2110, res=3800, hs=38, ts=60, tl=110,
a100=4.5, b100=4.5, vn=143, vs=182, vg=40, page=25),
'vulture': dict(t=75, ap=2025, res=2400, hs=24, ts=50, tl=130,
a100=4.7, b100=3, vn=175, vs=235, vg=62, page=26),
'madcat': dict(t=75, ap=2195, res=2800, hs=28, ts=80, tl=90,
a100=4.2, b100=4, vn=175, vs=220, vg=62, page=26),
'owens': dict(t=25, ap=760, res=1500, hs=15, ts=0, tl=0,
a100=4.2, b100=5, vn=154, vs=265, vg=63, page=29),
'blkhawk': dict(t=50, ap=1362, res=1600, hs=16, ts=0, tl=0,
a100=5.1, b100=4.6, vn=190, vs=280, vg=60, page=29),
}
# ------------------------------------------------------------- weapons
CFG = open(os.path.join(GAUGE, 'L4GAUGE.CFG'),
encoding='utf-8', errors='replace').read()
INFRA = {'HUD', 'Avionics', 'Gyroscope', 'Torso', 'Myomers'}
def init_block(key):
name = key.capitalize() + 'Init'
m = re.search(r'^%s\b' % re.escape(name), CFG, re.M)
if not m:
sys.exit('no %s block in L4GAUGE.CFG' % name)
n = re.search(r'^\w+Init\b', CFG[m.end():], re.M)
return CFG[m.start(): m.end() + n.start() if n else len(CFG)]
def family(sub):
"""ERPPC_2 -> ERPPC, MLaser1 -> MLaser; AFC100/LRM15/NRK5 keep their digits
(they are family names, and they are in FAM already)."""
fam = re.sub(r'_\d+$', '', sub)
if fam not in FAM:
stripped = re.sub(r'\d+$', '', fam)
if stripped in FAM:
fam = stripped
return fam
def loadout(key):
weapons, bins = {}, {}
for sub in re.findall(r'cmCrit\([^,]+,[^,]+,\d+,[^,]+,[^,]+,(\w+)\)',
init_block(key)):
if sub.startswith('AmmoBin'):
fam = family(sub[len('AmmoBin'):])
bins[fam] = bins.get(fam, 0) + 1
elif not (sub in INFRA
or sub.startswith(('Generator', 'Condenser', 'Myomers'))):
fam = family(sub)
weapons[fam] = weapons.get(fam, 0) + 1
return weapons, bins
# Family -> (display name, class). Classes: e energy, b ballistic, m missile.
FAM = {
'PPC': ('PPC', 'e'), 'ERPPC': ('ER PPC', 'e'),
'LLaser': ('Large Laser', 'e'), 'MLaser': ('Medium Laser', 'e'),
'SLaser': ('Small Laser', 'e'), 'ERLLaser': ('ER Large Laser', 'e'),
'ERMLaser': ('ER Medium Laser', 'e'), 'ERSLaser': ('ER Small Laser', 'e'),
'AFC25': ('AFC 25', 'b'), 'AFC50': ('AFC 50', 'b'),
'AFC100': ('AFC 100', 'b'), 'GAUSS': ('Gauss', 'b'), 'NRK5': ('NRK 5', 'b'),
'LRM5': ('LRM 5', 'm'), 'LRM10': ('LRM 10', 'm'), 'LRM15': ('LRM 15', 'm'),
'LRM20': ('LRM 20', 'm'), 'SRM2': ('SRM 2', 'm'), 'SRM4': ('SRM 4', 'm'),
'SRM6': ('SRM 6', 'm'),
}
CLASS_NAME = {'e': 'Energy', 'b': 'Ballistic', 'm': 'Missile'}
CLASS_ORDER = {'e': 0, 'b': 1, 'm': 2}
# ------------------------------------------------------------- portraits
from PIL import Image
mrpal = open(os.path.join(GAUGE, 'MRPAL.PCC'), 'rb').read()
if mrpal[-769] != 0x0C:
sys.exit('MRPAL.PCC has no VGA palette where PCX keeps one')
BASEPAL = list(mrpal[-768:])
adpal = open(os.path.join(GAUGE, 'ADPAL.PCC'), 'rb').read()
HEALTHY = list(adpal[-768:][0:3]) # ramp entry 0 = undamaged green
_portraits = {}
def portrait(stem):
if stem not in _portraits:
im = Image.open(os.path.join(GAUGE, stem + '_MR.PCC'))
im.load()
flat = BASEPAL[:]
for s in range(32, 64): # zone slots -> healthy state
flat[s * 3:s * 3 + 3] = HEALTHY
im.putpalette(flat)
buf = io.BytesIO()
im.save(buf, 'PNG', optimize=True)
_portraits[stem] = ('data:image/png;base64,'
+ base64.b64encode(buf.getvalue()).decode(),
im.width, im.height)
return _portraits[stem]
# ----------------------------------------------------------------- page
def dash(v, unit=''):
return '&mdash;' if v is None else '%s%s' % (v, unit)
NAMES = dict(ROSTER)
cards = []
for key, name in ROSTER:
weapons, bins = loadout(key)
base = VARIANT_OF.get(key)
man = MANUAL.get(key)
uri, w, h = portrait(MR_STEM[key])
fams = sorted(weapons, key=lambda f: (CLASS_ORDER[FAM[f][1]], FAM[f][0]))
chips = ''.join(
'<span class="chip chip--%s">%s%s</span>' % (
FAM[f][1],
'%d&times; ' % weapons[f] if weapons[f] > 1 else '',
html.escape(FAM[f][0].upper()))
for f in fams)
rows = ''.join(
'<tr><th scope="row">%s</th><td>%s</td>'
'<td class="num">%d</td><td class="num">%s</td></tr>' % (
html.escape(FAM[f][0]), CLASS_NAME[FAM[f][1]], weapons[f],
'%d&times;' % bins[f] if f in bins else '&mdash;')
for f in fams)
torso = ('FIXED' if key in FIXED_TORSO
else '%s&deg;/s &middot; %s&deg;' % (man['ts'], man['tl'])
if man else 'twists')
spec = ''.join('<div><dt>%s</dt><dd>%s</dd></div>' % kv for kv in (
('Tonnage', dash(man and man['t'], ' t')),
('Armor points', dash(man and man['ap'])),
('Reservoir', dash(man and man['res'], ' L')),
('Heat sinks', dash(man and man['hs'])),
('Torso', torso),
('0&ndash;100 kph', dash(man and man['a100'], ' s')),
('Top speed', dash(man and man['vn'], ' kph')),
('Super charged', dash(man and man['vs'], ' kph')),
('Gimped', dash(man and man['vg'], ' kph')),
))
sheet = ('manual p. %d' % man['page'] if man else
'chassis sheet: see %s' % html.escape(NAMES[base]) if base in MANUAL
else 'no manual sheet (4.10 addition)')
fam_base = base or key
kin = ([NAMES[fam_base]] if base else []) + \
[NAMES[v] for v, b in VARIANT_OF.items() if b == fam_base and v != key]
cap = ('%s chassis &middot; with %s' % (
html.escape(NAMES.get(base, name)), html.escape(', '.join(kin)))
if kin else '%s chassis' % html.escape(name))
search = ' '.join([key, name.lower()] +
[FAM[f][0].lower() for f in fams] + fams).lower()
cards.append("""
<article class="mech" data-tags="{tags}" data-search="{search}">
<header class="mech__id">
<figure class="hull">
<img class="hull__img" src="{uri}" width="{w}" height="{h}"
alt="{name} mission-review portrait" loading="lazy">
<figcaption>{cap}</figcaption>
</figure>
<h3>{name}</h3>
<p class="mech__key">{key} &middot; {kind}</p>
<p class="mech__load">{chips}</p>
<p class="mech__meta"><span>{nw} weapons</span><span>{nb} ammo bins</span><span>torso {torsoshort}</span></p>
<dl class="spec">{spec}</dl>
<p class="mech__sheet">{sheet}</p>
</header>
<div class="mech__tables"><div class="tablewrap"><table>
<caption class="sr-only">Weapons carried by the {name}</caption>
<thead><tr><th scope="col">WEAPON</th><th scope="col">TYPE</th>
<th scope="col" class="num">COUNT</th><th scope="col" class="num">AMMO BIN</th></tr></thead>
<tbody>{rows}</tbody>
</table></div></div>
</article>""".format(
tags='variant' if base else 'base', search=html.escape(search),
uri=uri, w=w, h=h, name=html.escape(name), key=key,
kind='variant of %s' % html.escape(NAMES[base]) if base else 'base chassis',
cap=cap, chips=chips, nw=sum(weapons.values()), nb=sum(bins.values()),
torsoshort='FIXED' if key in FIXED_TORSO else 'TWISTS',
spec=spec, sheet=sheet, rows=rows))
print(' %-8s %-12s %2d weapons, %d bins%s' % (
key, name, sum(weapons.values()), sum(bins.values()),
' [manual]' if man else ''), file=sys.stderr)
PAGE = open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'mechs.tpl'), encoding='utf-8').read()
os.makedirs(os.path.dirname(OUT), exist_ok=True)
open(OUT, 'w', encoding='utf-8').write(PAGE.format(
cards=''.join(cards), n=len(ROSTER),
nbase=sum(1 for k, _ in ROSTER if k not in VARIANT_OF),
nvar=sum(1 for k, _ in ROSTER if k in VARIANT_OF)))
print('wrote %s (%d mechs)' % (OUT, len(ROSTER)), file=sys.stderr)
+147
View File
@@ -0,0 +1,147 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BattleTech 4.11 &mdash; every map from above</title>
<style>
*, *::before, *::after {{ box-sizing: border-box; }}
:root {{
--ground: #0e0f11; --panel: #16181b; --rule: #2a2e33;
--ink: #e8e6e2; --ink-mid: #a3a8ae; --ink-low: #71777e;
--hot: #f39313; /* palette 6, the map's own amber */
--mono: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
--sans: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}}
@media (prefers-color-scheme: light) {{
:root {{
--ground: #f4f2ee; --panel: #fbfaf8; --rule: #ddd8d0;
--ink: #14161a; --ink-mid: #565c63; --ink-low: #7d838a; --hot: #a35f00;
}}
}}
body {{
margin: 0; background: var(--ground); color: var(--ink);
font: 400 15px/1.6 var(--sans);
}}
.wrap {{ max-width: 78rem; margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2.5rem); }}
header.top {{ border-bottom: 1px solid var(--rule); padding: clamp(2.5rem,6vw,4rem) 0 1.75rem; }}
.eyebrow {{
font: 500 12px/1 var(--mono); letter-spacing: .2em; text-transform: uppercase;
color: var(--ink-low); margin: 0 0 1rem;
}}
h1 {{
font: 700 clamp(1.9rem,1.4rem+2.2vw,3rem)/1.05 var(--mono);
letter-spacing: -.02em; text-transform: uppercase; margin: 0 0 .9rem;
}}
h1 em {{ font-style: normal; color: var(--hot); }}
.lede {{ max-width: 62ch; color: var(--ink-mid); margin: 0; font-size: 1.05rem; }}
.lede strong {{ color: var(--ink); font-weight: 600; }}
.bar {{ border-bottom: 1px solid var(--rule); padding: .8rem 0; }}
.seg {{ display: inline-flex; border: 1px solid var(--rule); }}
.seg button {{
font: 500 12px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
background: none; border: 0; color: var(--ink-mid); padding: .55rem .9rem; cursor: pointer;
}}
.seg button[aria-pressed="true"] {{ background: var(--hot); color: var(--ground); }}
.grid {{
display: grid; gap: 1px; background: var(--rule);
border: 1px solid var(--rule); margin: 1.75rem 0 3rem;
grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
}}
.map {{ background: var(--panel); padding: 1.1rem 1.25rem 1.35rem; }}
.map[hidden] {{ display: none; }}
.fig {{ margin: 0 0 .9rem; display: flex; justify-content: center; }}
/* The game's own palette on the display's own black, so it reads the same
in either theme. */
.plan {{
display: block; max-width: 100%; height: auto; max-height: 20rem;
background: #000; border: 1px solid var(--rule); image-rendering: pixelated;
}}
.plan--none {{
margin: 0; padding: 2rem 1rem; text-align: center; color: var(--ink-low);
font: 400 12px var(--mono); border: 1px dashed var(--rule); width: 100%;
}}
h2 {{ font: 700 1.3rem/1.1 var(--mono); margin: 0 0 .25rem; text-transform: uppercase; }}
.tag {{
font: 500 11px/1 var(--mono); letter-spacing: .14em; color: var(--ink-low);
margin: 0 0 .8rem;
}}
.stat {{ margin: 0; display: grid; grid-template-columns: 1fr auto; gap: .2rem .8rem; }}
.stat div {{ display: contents; }}
.stat dt {{ font: 500 12px var(--mono); letter-spacing: .08em; color: var(--ink-low); }}
.stat dd {{
margin: 0; text-align: right; font: 500 12px var(--mono); color: var(--ink);
font-variant-numeric: tabular-nums;
}}
.models {{
margin: .75rem 0 0; font: 400 11px/1.6 var(--mono); color: var(--ink-low);
word-break: break-word;
}}
.foot {{
border-top: 1px solid var(--rule); padding: 1.5rem 0 4rem; max-width: 76ch;
color: var(--ink-low); font-size: 13px;
}}
.foot code {{ font-family: var(--mono); color: var(--ink-mid); }}
</style>
</head>
<body>
<header class="top">
<div class="wrap">
<p class="eyebrow">BattleTech 4.11 &middot; drawn from BTL4.RES</p>
<h1>Every map,<br><em>seen from above</em></h1>
<p class="lede">Each plan is drawn the way the cockpit draws it: every placement in
the map's own stream, the outline its model carries, in the game's own palette.
<strong>Nothing here is traced or redrawn</strong> &mdash; the buttes are circles
because the game draws them as circles.</p>
</div>
</header>
<div class="bar">
<div class="wrap">
<div class="seg" role="group" aria-label="Filter">
<button type="button" data-filter="all" aria-pressed="true">All {n}</button>
<button type="button" data-filter="playable" aria-pressed="false">Playable {playable}</button>
<button type="button" data-filter="aux" aria-pressed="false">Supporting</button>
</div>
</div>
</div>
<main class="wrap">
<div class="grid">{cards}</div>
<p class="foot">A map stream is a run of <code>Entity::MakeMessage</code> records. Each
carries its own length at +0, its class at +28, the resource it names at +40, its
instance flags at +44 and its origin at +48 &mdash; and BT pads each record to the
next four bytes, which Red Planet does not. The resource named is a <em>Model List</em>;
the outline the display draws is the GaugeImage inside that list, so the join is direct
rather than by name. A model with no GaugeImage is not drawn, which is why several
streams place far more than they draw.</p>
<p class="foot">Colours are palette indices resolved through <code>btspal.pcc</code>, the
palette <code>L4GAUGE.CFG</code> configures the secondary port with:
<code>configure(0,sec,270,0x003F,clut0,rgb,btspal.pcc)</code>. Unlike Red Planet,
every primitive here carries an explicit colour &mdash; nothing falls back to a display
default. Seen from above, +X runs right and +Z up.</p>
<p class="foot">Two of the eight playable maps, <code>arena1</code> and
<code>arena2</code>, place almost nothing: a sky and a pair of records too short to
carry an origin. Their arena geometry is <code>arenall</code>, which is a stream of its
own. <code>adrop</code> and <code>pol4sfx</code> are likewise supporting streams
&mdash; drop points and wind emitters &mdash; rather than places you fight in.</p>
</main>
<script>
const cards = [...document.querySelectorAll('.map')];
for (const b of document.querySelectorAll('[data-filter]')) {{
b.addEventListener('click', () => {{
const f = b.dataset.filter;
for (const o of document.querySelectorAll('[data-filter]'))
o.setAttribute('aria-pressed', String(o === b));
for (const c of cards)
c.hidden = f !== 'all' && !c.dataset.tags.split(' ').includes(f);
}});
}}
</script>
</body>
</html>
+260
View File
@@ -0,0 +1,260 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BattleTech 4.11 &mdash; the mech roster</title>
<style>
*, *::before, *::after {{ box-sizing: border-box; }}
:root {{
--ground: #0e0f11; --panel: #16181b; --rule: #2a2e33;
--ink: #e8e6e2; --ink-mid: #a3a8ae; --ink-low: #71777e;
--hot: #f39313; /* palette 6, the display's own amber */
--nrg: #ff5340; /* energy weapons */
--bal: #f39313; /* ballistic */
--msl: #58c470; /* missiles -- the armor doll's green family */
--mono: ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace;
--sans: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}}
@media (prefers-color-scheme: light) {{
:root {{
--ground: #f4f2ee; --panel: #fbfaf8; --rule: #ddd8d0;
--ink: #14161a; --ink-mid: #565c63; --ink-low: #7d838a;
--hot: #a35f00; --nrg: #b3260f; --bal: #a35f00; --msl: #1c7a3f;
}}
}}
body {{
margin: 0; background: var(--ground); color: var(--ink);
font: 400 15px/1.6 var(--sans);
}}
.wrap {{ max-width: 78rem; margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2.5rem); }}
.sr-only {{
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}}
:focus-visible {{ outline: 2px solid var(--hot); outline-offset: 2px; }}
header.top {{ border-bottom: 1px solid var(--rule); padding: clamp(2.5rem,6vw,4rem) 0 1.75rem; }}
.eyebrow {{
font: 500 12px/1 var(--mono); letter-spacing: .2em; text-transform: uppercase;
color: var(--ink-low); margin: 0 0 1rem;
}}
h1 {{
font: 700 clamp(1.9rem,1.4rem+2.2vw,3rem)/1.05 var(--mono);
letter-spacing: -.02em; text-transform: uppercase; margin: 0 0 .9rem;
}}
h1 em {{ font-style: normal; color: var(--hot); }}
.lede {{ max-width: 62ch; color: var(--ink-mid); margin: 0 0 1.4rem; font-size: 1.05rem; }}
.lede strong {{ color: var(--ink); font-weight: 600; }}
.rules {{ display: flex; flex-wrap: wrap; gap: .6rem 2rem; margin: 0; padding: 0; list-style: none; }}
.rules li {{
font: 500 12px/1.5 var(--mono); color: var(--ink-mid);
padding-left: 1.1rem; position: relative;
}}
.rules li::before {{
content: ""; position: absolute; left: 0; top: .45em;
width: .45rem; height: .45rem; background: var(--hot);
}}
.rules b {{ color: var(--ink); font-weight: 700; }}
.bar {{
position: sticky; top: 0; z-index: 5;
background: var(--ground);
border-bottom: 1px solid var(--rule); padding: .75rem 0;
}}
.bar__in {{ display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: center; }}
.seg {{ display: inline-flex; border: 1px solid var(--rule); }}
.seg button {{
font: 500 12px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase;
background: none; border: 0; color: var(--ink-mid); padding: .55rem .9rem; cursor: pointer;
}}
.seg button[aria-pressed="true"] {{ background: var(--hot); color: var(--ground); }}
.search {{
flex: 1 1 12rem; min-width: 9rem;
font: 400 12px var(--mono);
background: var(--panel); color: var(--ink);
border: 1px solid var(--rule); padding: .55rem .7rem;
}}
.search::placeholder {{ color: var(--ink-low); }}
.count {{ font: 500 12px var(--mono); color: var(--ink-low); letter-spacing: .1em; }}
.key {{ display: flex; flex-wrap: wrap; gap: .4rem .9rem; padding: 1.25rem 0 0; }}
.key span {{ display: inline-flex; align-items: center; gap: .4rem;
font: 500 12px var(--mono); letter-spacing: .08em; color: var(--ink-low); }}
.key i {{ width: .7rem; height: .7rem; display: inline-block; }}
.roster {{ display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule);
margin: 1.75rem 0 3rem; }}
.mech {{ background: var(--panel); display: grid; gap: 0;
grid-template-columns: minmax(15rem, 19rem) 1fr; }}
@media (max-width: 46rem) {{ .mech {{ grid-template-columns: 1fr; }} }}
.mech[hidden] {{ display: none; }}
.mech__id {{ padding: 1.1rem 1.25rem 1.35rem; border-right: 1px solid var(--rule); }}
@media (max-width: 46rem) {{ .mech__id {{ border-right: 0; border-bottom: 1px solid var(--rule); }} }}
.hull {{ margin: 0 0 .9rem; display: flex; flex-direction: column; gap: .45rem;
align-items: flex-start; }}
/* The game's own review image on the display's own black, either theme. */
.hull__img {{
display: block; width: 7rem; height: auto;
background: #000; border: 1px solid var(--rule); image-rendering: pixelated;
}}
.hull figcaption {{
font: 500 11px/1.35 var(--mono); letter-spacing: .1em;
text-transform: uppercase; color: var(--ink-low);
}}
.mech__id h3 {{
font: 700 1.5rem/1 var(--mono); letter-spacing: .01em;
margin: 0 0 .55rem; text-transform: uppercase;
}}
.mech__key {{
font: 500 12px/1 var(--mono); letter-spacing: .12em;
color: var(--ink-low); margin: -.3rem 0 .7rem;
}}
.mech__load {{ display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 .7rem; }}
.chip {{
font: 500 11px/1 var(--mono); letter-spacing: .08em;
padding: .3rem .45rem; border: 1px solid currentColor;
}}
.chip--e {{ color: var(--nrg); }}
.chip--b {{ color: var(--bal); }}
.chip--m {{ color: var(--msl); }}
.mech__meta {{ display: flex; flex-wrap: wrap; gap: .8rem; margin: 0 0 .2rem;
font: 400 12px var(--mono); color: var(--ink-low); font-variant-numeric: tabular-nums; }}
.spec {{
margin: .75rem 0 0; display: grid; grid-template-columns: 1fr auto;
gap: .2rem .8rem; font: 500 12px var(--mono);
}}
.spec div {{ display: contents; }}
.spec dt {{ color: var(--ink-low); letter-spacing: .06em; }}
.spec dd {{ margin: 0; text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; }}
.mech__sheet {{ margin: .6rem 0 0; font: 400 11px/1.5 var(--mono); color: var(--ink-low); }}
.mech__tables {{ min-width: 0; }}
.tablewrap {{ overflow-x: auto; }}
table {{ border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }}
thead th {{
font: 500 12px/1 var(--mono); letter-spacing: .14em;
color: var(--ink-low); text-align: left; font-weight: 500;
padding: .9rem .9rem .55rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}}
tbody th {{
font: 500 13px/1.3 var(--mono); text-align: left; color: var(--ink);
padding: .5rem .9rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}}
tbody td {{
font: 400 13px/1.3 var(--mono); color: var(--ink-mid);
padding: .5rem .9rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}}
tbody tr:last-child th, tbody tr:last-child td {{ border-bottom: 0; }}
th.num, td.num {{ text-align: right; }}
.empty-state {{
margin: 2rem 0; font: 500 13px var(--mono); color: var(--ink-low);
}}
.foot {{
border-top: 1px solid var(--rule); padding: 1.5rem 0 0; max-width: 76ch;
color: var(--ink-low); font-size: 13px;
}}
.foot:last-child {{ padding-bottom: 4rem; }}
.foot code {{ font-family: var(--mono); color: var(--ink-mid); }}
</style>
</head>
<body>
<header class="top">
<div class="wrap">
<p class="eyebrow">BattleTech 4.11 &middot; from btl4fe.cpp, L4GAUGE.CFG and the player's manual</p>
<h1>Every mech,<br><em>and what it carries</em></h1>
<p class="lede">All <strong>{n} mechs the menu offers</strong> &mdash; {nbase} base chassis
and {nvar} variants &mdash; each with its authored weapons roster, ammo bins and the
stat sheet from the original player's manual. The portraits are the game's own
mission-review images, shown in their undamaged state.</p>
<ul class="rules">
<li><b>Variants share the chassis</b>, not the loadout</li>
<li><b>Blackhawk, Nova, Owens, Kotori</b> have a fixed torso</li>
<li>The manual sheets cover the <b>six 4.0 hulls</b> only</li>
</ul>
</div>
</header>
<div class="bar">
<div class="wrap bar__in">
<div class="seg" role="group" aria-label="Filter">
<button type="button" data-filter="all" aria-pressed="true">All {n}</button>
<button type="button" data-filter="base" aria-pressed="false">Base {nbase}</button>
<button type="button" data-filter="variant" aria-pressed="false">Variants {nvar}</button>
</div>
<input id="search" class="search" type="search" placeholder="Search mech or weapon&hellip;"
aria-label="Search mech or weapon">
<span class="count" id="count">{n} / {n} mechs</span>
</div>
</div>
<main class="wrap">
<div class="key">
<span><i style="background:var(--nrg)"></i>Energy</span>
<span><i style="background:var(--bal)"></i>Ballistic</span>
<span><i style="background:var(--msl)"></i>Missile</span>
</div>
<div class="roster">{cards}</div>
<p class="empty-state" id="nomatch" hidden>No mech matches that.</p>
<p class="foot">The roster is <code>kVehicles[]</code> in <code>game/glass/btl4fe.cpp</code>
&mdash; the exact catalog the in-game menu offers, in menu order. Each mech's weapons and
ammo bins are its <code>cmCrit(...)</code> lines in <code>content/GAUGE/L4GAUGE.CFG</code>,
the critical-damage schematic's authored subsystem roster (independently confirmed against
the subsystem records in <code>BTL4.RES</code>). Nothing is transcribed by hand.</p>
<p class="foot">The portraits are each mech's mission-review image
(<code>GAUGE/&lt;stem&gt;_MR.PCC</code>) resolved through the review screen's own palette.
In play, its zone indexes are live damage slots painted green&rarr;orange&rarr;red by the
ColorMapper gauges; this page shows every zone at the undamaged ramp entry &mdash; the
same green the armor doll idles at. Variants without their own portrait share the base's,
exactly as the resource file does.</p>
<p class="foot">Mech stats are the original player's manual stat sheets (pp. 25, 26, 29).
The manual documents the six 4.0 hulls; Avatar and Sunder arrived with 4.10 and have no
sheet, so their rows show a dash rather than an invented number. FIXED vs twisting torso
is read from the shipped resource file: a mech twists only if its subsystem record carries
the <code>jointtorso</code>/<code>jointtshadow</code> joints &mdash; the four fixed mechs
match the manual's Torso&nbsp;0/0 rows exactly.</p>
<p class="foot">Weapon notes: ER lasers fire red beams, PPCs blue, standard lasers yellow.
Authored ranges where the game states them: PPCs 900&nbsp;m, missiles 800&nbsp;m, the
Blackhawk's lasers 500&nbsp;m, the AFC&nbsp;50 750&nbsp;m. The Blackhawk's SRM&nbsp;6
racks fire Streak ammunition. LRM tube counts are the salvo size; each launcher's ammo
bin is listed beside it.</p>
</main>
<script>
const cards = [...document.querySelectorAll('.mech')];
const count = document.getElementById('count');
const search = document.getElementById('search');
let filter = 'all';
function apply() {{
const q = search.value.trim().toLowerCase();
let shown = 0;
for (const c of cards) {{
const okTag = filter === 'all' || c.dataset.tags.split(' ').includes(filter);
const okQ = !q || c.dataset.search.includes(q);
const on = okTag && okQ;
c.hidden = !on;
if (on) shown++;
}}
count.textContent = shown + ' / ' + cards.length + ' mechs';
document.getElementById('nomatch').hidden = shown > 0;
}}
for (const b of document.querySelectorAll('[data-filter]')) {{
b.addEventListener('click', () => {{
filter = b.dataset.filter;
for (const o of document.querySelectorAll('[data-filter]'))
o.setAttribute('aria-pressed', String(o === b));
apply();
}});
}}
search.addEventListener('input', apply);
apply();
</script>
</body>
</html>
+1 -1
View File
@@ -77,7 +77,7 @@ def main():
exes = ["build/Release/btl4.exe"]
bats = ["players/play_solo.bat", "players/join.bat", "players/join_lan.bat",
"players/joyconfig.bat", "players/SENDLOGS.bat"]
"players/joyconfig.bat"]
if steam_on:
bats.append("players/play_steam.bat")
for p in exes + bats: