arcattackandClaude Fable 5 6f5a264835 Gitea #47 COMPLETE: the ENG-button attention FLASH is live (jam / bay fire) -- MechTech status scan + BTL4GaugeAlarmManager + lamp chain, all from the binary
The pod behaviour Cyd described -- "on a jam or bay fire the display eng button
for the system flashes" -- is authored data + a five-stage chain, now running:

  MechTech::TechnicalAssistance (@004ad33c, per frame)
    edge-scans every monitored subsystem's GetStatusFlags() 7-bit condition
    mask (TechStatusType: Destroyed 0, Damaged 1, CoolantLeaking 2,
    Overheating 3, AmmoBurning 4, Jammed 5, BadPower 6)
  -> Start/StopEntityAlarmMessage (@00436688 id 7 size 0x20 / @004366b8 id 8
     size 0x1C; broadcast @004364e4; port shape: direct
     Start/StopEntityAlarmImplementation calls on the gauge renderer)
  -> GaugeAlarmManager::Activate(alarmModel) -- alarmModel = MechTech+0x100 =
     the 'mechalrm' ModelList (id 83) -> SearchList(83, type 31) -> the baked
     GaugeAlarmStream (id 331, 11 items {condition, lampCode}), decoded:
         Destroyed      -> gotoEngineering + engCooling + engBusMode
         CoolantLeaking -> gotoEngineering + engCooling
         AmmoBurning    -> gotoEngineering + engEject
         Jammed         -> gotoEngineering + engEject
         BadPower       -> gotoEngineering + engBusMode
  -> BTL4GaugeAlarmManager::ReadGaugeAlarmStreamItem -- THE REAL BODY, from
     @004cc2fc + helpers @004cc108/148/1a0/264/27c + tables @0051cf1c..0x51d084
     (gotoEngineering 0x80 = the subsystem's QUAD-SELECT bezel button via
     lamp[aux]/mode[aux] tables; 0x81+ descend the eng-page bank; Condenser /
     Generator specials on CoolantLeaking; <0x80 = the heat-bank fixed map).
     btl4galm.cpp's old bodies were admitted fabrications and its provenance
     note ("no override body exists in the image") was wrong -- corrected.
  -> LampManager::FindLamp (@00444c80) -> Lamp::SetAlertState (@00444e64, a
     COUNTER so stacked alarms hold the flash) -> L4Lamp::NotifyOfStateChange
     emits RIO flashFast states 0x37/0x13 (== T0 L4LAMP.cpp:234-239) -> the
     pod's physical lamps AND the glass panels (PadRIO IS rioPointer there).

FOUR load-bearing defects found and fixed en route -- each independently fatal
to the feature:

  1. HeatableSubsystem's Derivation chained Subsystem directly, SKIPPING
     MechSubsystem (written before the WAVE-1 re-basing) -- so EVERY subsystem
     on both family branches failed IsDerivedFrom(MechSubsystem), which is
     exactly MechTech's monitor filter: the status scan watched NOTHING.
  2. MechSubsystem::GetStatusFlags was non-virtual (binary: vtable slot 12) --
     the scan's MechSubsystem* call bound statically to the base tier and the
     weapon bits could never surface.
  3. ProjectileWeapon::GetStatusFlags sat in a Ghidra export gap -- raw-disasm
     @004bbf88: base | 0x20 (weaponAlarm==5 Jammed) | 0x10 (linked bin
     cookOffArmed@0x18C AmmoBurning).  The port had "defer to base".
  4. Mech::Reset's respawn sweep blanket-cast every roster entry to
     MechSubsystem and called RespawnRepair -- wrong for the Subsystem-level
     entries (MechTech 0xBDC, SubsystemMessageManager 0xBD3).  On MechTech the
     recon damageZone slot lands on its subsystemMonitors chain head: NULL
     while the scan was broken (fix #1's bug MASKED this one), but the moment
     the monitors populated, RespawnRepair virtual-called through a
     SubsystemMonitor as if it were a DamageZone -> load-time crash in
     StateIndicator::SetState (caught with cdb; call [edx+14h] on code bytes).
     The sweep now filters IsDerivedFrom(MechSubsystem) before the cast.

Also: GUID identities pinned -- 0x50f4bc = PoweredSubsystem::ClassDerivations
(via @004b1208 = its TestInstance; btl4gau2's two "Generator" comments were
wrong, swept), 0x50fb60 = Generator's.  A shadow-field instance documented for
the deferred de-shadow: MechSubsystem::damageZone re-declares the PUBLIC engine
Subsystem::damageZone (SUBSYSTM.h:159) -- mechtech's naive `sub->damageZone`
read the never-written engine member (0 monitors again); now reads the recon
member via GetDamageZoneProxy().  Logged in open-questions.

Wiring: BTL4GaugeRenderer now constructs + assigns the BTL4GaugeAlarmManager
(the base ctor NULLs it and Activate Check()s it); MechTech's Report*/
StatusMessageSink stubs are real; alarmModel confirmed baked (= 83) at runtime.

VERIFIED LIVE (scratchpad/lampflash.py, BT_LAMP_LOG chain trace):
    [techstat] MechTech id 32 monitors 29 subsystems, alarmModel 83
    [techstat] LRM15_1 condition 4 SET (alarmModel 83)      <- bay fire armed
    [galarm] condition 4 code 0x80 -> lamp 0xd mode 0x1 FLASH
    [lamp] 0xd <- 0x37  (FLASHING)                          <- the select button
    [galarm] condition 4 code 0x85 -> lamp 0xb mode 0x4 FLASH   <- engEject
    [techstat] LRM15_1 condition 4 CLEARED                  <- detonation clears
    [techstat] AmmoBinLRM15_1 condition 0 SET               <- bin Destroyed
Regressions: baytest PASS (bay fire kills), baypurge PASS (purge extinguishes),
sim3 3-pod brawl PASS with ZERO crashes (6 kills, organic heat-route bay fires,
respawns clean through the new sweep filter).

Env: BT_LAMP_LOG ([techstat]/[galarm]/[lamp]).  KB: gauges-hud (the flash
section), decomp-reference (the GaugeAlarm closure + tables + GUIDs),
open-questions (built-note + the de-shadow deferred item), btl4gau2 comment
sweep.  checkctx CLEAN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 16:04:34 -05:00

BattleTech 4.11 (bt411)

A standalone Windows port of Virtual World Entertainment's arcade BattleTech (Tesla platform, release 4.10, ~199596), reconstructed on the shared RP411 Windows engine. The game boots, renders, and runs a single-player drive → animate → target → fire → damage → destroy loop across all 8 maps, with two-instance multiplayer entity replication working.

This repo is a clean, self-contained extraction of the BattleTech-specific work from the larger reverse-engineering workspace — engine + game + content + build, with nothing from Red Planet or the raw archive dumps. It builds and runs out of the box.

License: the game content (content/) and the original binary are proprietary to Virtual World / the pod owner. This repository is private; do not redistribute.

Versioning

4.10 = the 1995 arcade release. 4.11 = this win32 reconstruction. Dev builds are 4.11.<build> where <build> is the git commit count — monotonic and zero-maintenance — plus the short commit hash (4.11.311 (980c9cd)); a trailing + on the hash means the exe was built from an uncommitted working tree. The stamp regenerates every build (tools/btversion.cmakebuild/btversion.h) and shows in the boot banner (btl4.log line 2) and the window title. To identify any player's build, ask for the title bar or the top of their btl4.log.


Layout

CMakeLists.txt      one build: munga_engine lib + bt410_l4 game lib + btl4.exe
engine/
  MUNGA/            shared 2007 sim/render engine (149 .cpp + headers)
  MUNGA_L4/         Win32/D3D9 HAL + renderer + asset loaders (44 .cpp), incl.
                    our BT work: bgfload / L4D3D / L4VIDEO + the image codec
  shim/             minimal ATL shim (USES_CONVERSION/W2A)
  lib/              OpenAL32 / libsndfile import libs + runtime DLLs
game/
  reconstructed/    the reconstructed BT game logic (mech, subsystems, HUD, app; ~47 .cpp)
  original/BT,BT_L4 surviving original BT source + all BT headers
  fwd/              header shims forwarding <NAME.hpp> -> the engine's NAME.h
  btl4main.cpp      WinMain launcher / entry point
content/            runtime data: BTL4.RES, VIDEO/, GAUGE/, AUDIO/, *.EGG, BTDPL.INI
context/            progressive knowledge graph — 18 on-demand topic files (routed by CLAUDE.md)
docs/               format specs + reconstruction ledgers + PROGRESS_LOG.md (full history)
reference/
  decomp/           raw Ghidra pseudocode — source-of-truth for ongoing recon
  ghidra_scripts/   the headless decomp exporter
  glossary.yaml     term / acronym definitions
phases/             restructuring / investigation logs
tools/              btconsole.py (MP console emulator), map/resource scanners
run/                run.cmd helper
CLAUDE.md           knowledge-base ROUTER — identity, protocols, quick-lookup, conventions

Prerequisites

  • Visual Studio 2019 BuildTools (MSVC v142, x86). The Community install on the original dev box was broken, hence the explicit BuildTools instance in the configure line below; adjust to your install.
  • CMake ≥ 3.20.
  • Legacy DirectX SDK (June 2010) — the engine uses d3dx9/dinput/dxerr, removed from the modern Windows SDK. Default path C:/Program Files (x86)/Microsoft DirectX SDK (June 2010); override with -DDXSDK=<path>. (The installer may throw a harmless S1023 error — dismiss it; the SDK headers/libs install before the failing redist step.)

OpenAL/libsndfile import libs + DLLs are vendored under engine/lib/; the DLLs are copied next to the exe automatically at build time.

Build (32-bit / Win32)

cmake -S . -B build -G "Visual Studio 16 2019" -A Win32 ^
      -DCMAKE_GENERATOR_INSTANCE="C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools"
cmake --build build --config Debug

Must be Win32 — the DirectX SDK link libs are Lib/x86. The link uses /FORCE: the 1995 headers define free functions/globals without inline/extern, so identical symbols appear in many translation units (~124 LNK2005); /FORCE:MULTIPLE keeps the first. UNRESOLVED tolerates a dead offline-tool factory in mech3.cpp that is never called at runtime. (Cleanup task: move those definitions to single TUs + neutralize the dead factory, then drop /FORCE.)

Run

run\run.cmd            REM boots DEV.EGG (grass / day)
run\run.cmd DBASE.EGG  REM any egg in content/

The working directory must be content/ (the engine resolves BTL4.RES, VIDEO\, BTDPL.INI, and eggs relative to cwd); run.cmd handles that. Maps available in BTL4.RES: cavern grass rav polar3 polar4 arena1 arena2 dbase — switch via a copied egg's map= field.

Useful env-var flags (default OFF unless noted)

The authentic stack (gait, collision, real controls) is default-on; set =0 to fall back. Debug/harness flags: BT_FORCE_THROTTLE=1 (auto-walk), BT_SPAWN_ENEMY=1 (spawn a target dummy), BT_FORCE_FIRE=1 (auto-fire), BT_HEAPCHECK=1 (whole-heap validation — slow), BT_BSL=0 (legacy texture decode), BT_DEV_GAUGES=1 (render the cockpit MFDs in a dev window), BT_LOG=<file>. Interactive: WASD drive, A/D turn, Q/E torso twist, R/F torso pitch aim, Space / 1-4 fire, X all-stop, V view, M control mode. An Xbox-type controller works out of the box. All bindings are user-editable in content/CONTROLS.MAP (delete it to restore the compiled-in WASD default; content/CONTROLS_NUMPAD.MAP is an alternate profile). The complete env-gate table is in context/decomp-reference.md §6 (routed from CLAUDE.md); controls/pad details in context/pod-hardware.md.

Multiplayer

Modern path (relay + operator console). Pods make ONE outbound connection to a relay/console, so internet play needs no per-player port forwarding and CGNAT-safe LAN discovery just works. Run the operator station:

python tools/btoperator.py    # PySide6 GUI: build the mission egg (validated dropdowns),
                              # start the relay, watch pods arrive, assign seats, LAUNCH,
                              # end the timed mission, export player join.bat scripts

Players run one universal join.bat (internet, seat assigned by the relay) or join_lan.bat (same LAN, auto-discovers the console) or play_solo.bat (offline practice) — see players/. The pod waits patiently if the session isn't up yet. Full architecture, wire format, and the D1 relay/UDP design: context/multiplayer.md.

Legacy mesh (two instances, one box), still supported:

instance A:  btl4.exe -egg MP.EGG -net 1501   (BT_LOG=mp_a.log)
instance B:  btl4.exe -net 1601               (BT_LOG=mp_b.log)
console:     python tools/btconsole.py MP.EGG 127.0.0.1:1501 127.0.0.1:1601

-net <port> enables networked mode. Verified end-to-end: full entity/movement replication, cross-pod combat + kills, per-pilot paint + callsigns, timed missions, 4-pod live sessions, and a spectator/broadcast camera seat (hostType=1 vehicle=camera) with auto-directed coverage and a live ranking window.

Status & continuing the work

The engine, renderer, audio, HAL, build, locomotion, collision, damage, render fidelity, the full cockpit gauge / MFD system (every config binding resolves + every widget builds), and the projectile / missile weapon families are done. Active fronts: per-subsystem polish (the gyroscope integrator; the 0xBD3 message manager that gates the valve / status-message control routes) and cross-pod MP combat. reference/decomp/ holds the raw pseudocode every reconstruction is verified against.

Start with CLAUDE.md — it is the router into the progressive knowledge base: a quick-lookup table pointing to the context/*.md topic files (loaded on demand), the evidence-tier and convention rules, and context/open-questions.md for what's deferred / next. The complete pre-restructure history is preserved verbatim in docs/PROGRESS_LOG.md; docs/*.md holds the detailed running ledgers.

S
Description
No description provided
Readme
151 MiB
Languages
C++ 52.1%
C 34%
1C Enterprise 9.4%
Python 3.7%
Batchfile 0.2%
Other 0.4%