4e0fcbf328e997efb294a3a0046bbd5ec453fd0b
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
89b4f53046 |
Gauges: trigger-config joystick is LIVE -- @004c6ee0 is LinkToEntity, not SetColor
The weapon panel's btjoy trigger-config display (ConfigMapGauge) never rendered because the reconstruction labelled @004c6ee0 as "SetColor(int)" and, finding no caller, concluded the gauge was authentically dormant (task #6), gating it behind a BT_CONFIGMAP dev env. The user challenged this (the PROGRAM/TRIGGER CONFIG button implies the display visualizes your config). Re-verification found the old analysis failed twice: the "no caller" search looked for direct calls to a VIRTUAL, and its slot math used the wrong vtable copy. The real ConfigMapGauge vtable @0051a1b8, matched against the T0 GaugeBase virtual roster (GAUGE.h), pins @004c6ee0 at slot 9 (+0x24) == GaugeBase::LinkToEntity, and @0x94 is the linkedEntity -- the Execute gate is "not yet linked", not "disabled". The engine broadcasts LinkToEntity(viewpointEntity) to every gauge at viewpoint bind (APP.cpp:1277 -> GaugeRenderer::LinkToEntity GAUGREND.cpp:3011), arming the gate -- the joystick DOES render in the shipped game, showing per-trigger mapping state (solid = mapped, matching the DOSBox reference). Port fix: SetColor(int color) -> LinkToEntity(Entity*), color -> linkedEntity, BT_CONFIGMAP dev enable deleted (the authentic path lights it). Render-verified with no env override: the joystick + mapped-trigger lamp draw on weapon panels. KB swept per the correction mandate: gauges-hud.md, decomp-reference.md, open-questions.md, GAUGE_COMPOSITE.md, VEHICLE_SUBSYSTEMS.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
66dcdf23db |
Gauges: ammo count stencils out of the fire-ready dot (BallisticWeaponCluster)
The base-page missile/round count sat on the fire-ready "dot" (the cluster image blitted green when charged) as green digits on an opaque black box -- a messy dark rectangle punched into the solid green dot. The binary swaps the ammo count's colours with the dot state. Verified from the decomp: BallisticWeaponCluster overrides DrawWarningLamp (@004c9b50) -- it chains the base (@004c932c, which blits the dot in on-colour 0xff / off-colour 0) and then calls the ammo NumericDisplayInteger's SetColors: dot ABSENT -> SetColors(0, 0xff) == green digits on black dot PRESENT -> SetColors(0xff, 0) == BLACK digits cut out of the green dot so the count is always legible (a stencil against the solid dot -- the same black-on-green look as the loop/generator squares). SetColors ForceUpdate()s the numeric so it repaints over the freshly drawn dot. The port had only the base WeaponCluster::DrawWarningLamp (non-virtual, no swap), so the count stayed green-on-black and clashed with the green dot. Made DrawWarningLamp virtual and added the BallisticWeaponCluster override. Render-verified (STREAK "0024" now black-cut-out of the solid dot); energy weapons (no ammo count) unaffected. Both builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e634709e5d |
Gauges: fix blank per-weapon loop/generator lamps (4A/1B/5D boxes)
The SubsystemCluster per-weapon MFD panels render two image-strip lamps in the TEMP/STATUS area: the cooling-loop number (btploop, 1..6) and the generator letter (btpbus, A..D). Both drew as completely empty boxes. Three stacked databinding bugs, all now fixed: 1. Color drop (btl4gau2.cpp): AnimatedSubsystemLamp/AnimatedSourceLamp ctors dropped the bg/fg color params the binary passes (bg=0xff,fg=0, same as the neighboring temp bar) and hardcoded 0,0 -> OneOfSeveral:: Execute did SetColor(0)+DrawBitMapOpaque(0), i.e. black-on-black. Restored 0xff,0 (verified vs @004c70a4 / caller SubsystemCluster @004c8140). 2. Shadow-field trap (btl4gau2.hpp, gotcha #2): both lamp classes re-declared `int selected;` while already inheriting it from OneOfSeveral (@0xAC). The CoolingLoop/PowerSource connection wrote the derived shadow copy while OneOfSeveral::Execute read the base @0xAC (always 0) -> every lamp stuck on frame 0 ("OFF"). Removed the redeclarations so &selected binds the inherited member. Loop numbers now render (verified: PPC->4, Myomers->5, ERMLaser->1/6, etc.). 3. Attribute-table shift (powersub.cpp + btl4gau2.cpp, gotcha #8): the generator-letter lamp resolved its source via ResolveLink(AttributePointerOf(subsystem,"InputVoltage")), but the BT_DEV_GAUGES audio attribute rows shifted the chained attribute ids so AttributePointerOf no longer landed on voltageSource@0x1D0 -> the link always resolved to 0 (OFF) even though the master PoweredSubsystem ctor DID bind voltageSource to its Generator. New BTPowerSourceFrame bridge reads the NAMED member via PoweredSubsystem::ResolveVoltageSource() and returns Generator::generatorNumber (@0x1E0), bypassing the attribute table -- the same pattern as the BTCoolingLoopFrame fix. Generator letters now render (verified: PPC_1->GeneratorA="A", Myomers-> GeneratorD="D", SRM6->GeneratorB="B"). Result: the "4 A / 1 B / 5 D" boxes render with both the loop number and generator letter, matching the reference. Kept BT_LOOP_LOG-gated [loopfeed]/[busfeed] feed diagnostics; both pod + glass builds clean. Awaiting live playtest verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
63b168cb92 |
SeekVoltageGraph: full reconstruction -- the eng-page POWER graph + top-box eraser (Gitea #11)
The #10 audit's one WRONG: the port Execute was a bring-up no-op, so the emitter/myomer engineering pages never erased their top data box -> stale sibling-page ghosts on the shared Eng bit-plane (SYSTEM 10 PPC showing the Streak ammo box, etc). Full faithful widget landed: - btl4gau2: ctor/BecameActive/Execute/clear/ticks/cursor recovered from the capstone disasm of @004c6798/@004c6920/@004c6934/@004c6be4/@004c6c30/ @004c6c6c (Ghidra dropped every x87 arg). Plot: v=0..12000 step 1200, x=Round(response(v)*230), y=Round(v*(1/12000)*187) (ld80 @004c6bd0/@004c6d74 = exactly 1/12000); change-test samples the response at 12000V vs the 9999 activation sentinel; XOR op for tick/cursor move-by-redraw; destroyed branch centres edestryd.pcc and revives via own vtbl+0xC (BecameActive, slot 3 of PTR_0051a1fc -- vtable-dump verified). - The vtbl+0x3C sampler identified from the binary vtables: Emitter slot 15 @004bb42c = sqrt(P(v)/2.0e8), P @004bb3f4 = damageFraction*v^2*0.5* energyCoefficient; Myomers slot 15 @004b8f94 = sqrt(AvailableOutput(v)*3.6/ 350). FUN_004dd138 == sqrt (part_015.c:4026): myomers' fabs reading corrected, GetSpeedReading renamed SeekVoltageResponse. Port dispatch via complete-type bridges BTSeekVoltageSample/BTMyomersSeekSample + BTSubsystemDestroyed (databinding rule). - Emitter's AUTHENTIC attribute table recovered (binary @0x511dd4, ids 0x1D-0x25) and published: Laser*/Seek*/OutputVoltage@0x414 (currentLevel, RAW volts -- the live-cursor feed). Field renames per the table: 0x3F8 minSeekVoltageIndex / 0x3FC maxSeekVoltageIndex (static_assert-locked). The MechWeapon 0x1D OutputVoltage PORT ALIAS retired (binary table ends 0x1C; Find walks lowest-id-first, the alias shadowed the authentic row). PPC::DefaultData -> Emitter::GetAttributeIndex(). - mech4: BT_PRESET_HOLD=<n> (freeze the #9 preset cycler after n pulses) for steady-state pixel verification; BT_SEEK_LOG diag. Verified live (BLH, autofire): both #10 repro pairs held ghost-free for minutes (SYS09->SYS10 PPC, SYS02->SYS04 ERMed, SYS05->SYS06 Myomers); curves draw with live cursors; one replot per activation; quad panels/J-K-L/sec panel un-regressed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
bb795e2805 |
MP live-play wave: collision economy, missiles, radar transform, panel polarity, comm ticker
The interactive 2-node playtest wave -- every fix decomp-grounded and live-verified: COLLISION ECONOMY (the ram one-shot): StaticBounce mutates worldLinearVelocity per contact and ProcessCollisionList walks EVERY touched solid per frame; with 2007 terrain-as-solids the reflections compounded x4-x40 within one frame and a walking bump one-shot a pristine mech for 112,375 pts (62-pt authentic economy). Fix: frameEntryWorldVelocity restore per contact (damage always priced at the real approach speed -- all the binary's physics ever saw); Mech::Reset zeroes the mover motion (respawn = teleport); [collide-tx]/[mp-hdlr] telemetry. Gotcha #16 (engine-facility drift class). MISSILES: peer-visible salvos (the launcher record extension carries a salvo counter + aim point; ForceUpdate actually enqueues it -- the dirty flag alone never serialized), the authentic arc (authored MuzzleVelocity vector + the Seeker's 200m/0.1/300 loft + gain-4 steering, decoded from @004beae4/@004bef78), world-impact bursts (rounds detonate on cave geometry instead of phasing through), contact-only damage (flight-cap expiry = fizzle, no more teleport damage), live re-lead, and ballistic (unguided) shells for autocannons. projweap's stale BTPushProjectile extern (the /FORCE signature trap, gotcha #6 corollary) crashed the Avatar's first AFC100 shot -- fixed + sweep rule recorded. RADAR: two transcription bugs made the scope permanently empty -- FUN_0040b244 is the affine INVERSE (not a copy) and FUN_0040adec writes ONLY the 3x3 rotation (never the translation); worldToView now Invert(view) built rotation-first. CulturalIcons sorted out of the moving grid (the phantom red pips were map props), visible-radius culls on all three draw passes, live pip verified at |delta| x ppm px. Gotcha #17 (verify the FUN_ body, not its call shape). WEAPON PANELS (the frozen-dial hunt): the binary's *(subsystem+0x40) means FAILED -- the recon's 'operating' name was backwards, inverting the destroyed-X lamps, the panel look, the children enable and the ready-lamp gate (which had NEVER executed). Polarity chain corrected end-to-end (failedState, fed by real damage saturation). Root cause of the freezes: MFD page-mode gating -- the dev composite shows ALL pages at once, so off-page dials legitimately stopped; under BT_DEV_GAUGES the 15 page-plane bits stay active (the exclusive secondary trio untouched). The SEH gauge guard now names its kills; repaint-heal resets the incremental arc after panel repaints; [panel]/[arc] probes added. COMM/SCORE: MessageBoard LIVE (the engine already shipped the whole Player__StatusMessage queue; wired the binary's one producer -- the kill branch, victim's name, 6s -- plus the consumer bridge and a lazy source bind); MP DEATHS counted via the observed-death tally (each node scores every pilot from locally observed events, the same model as the KILLS credit) and the -2/-1 engine seed clamped for display. DEV UX: node-tagged window titles (-net port), gauge panel reworked (1320x480, true 4:3 MFD cells, the portrait secondary UNROTATED upright, linear filtering, BT_GAUGE_SCALE), fixed close spawns via BT_SPAWN_XZ, Boreas flies an Avatar (first second-chassis live outing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
c0d2eb0aae |
gauge-complete P4f: PrepEngrScreen reconstructed + registered -> 12 engineering-screen label overlays build
The "prepEngr" cockpit primitive (the per-engineering-screen static label overlay,
12 CFG calls at L4GAUGE.CFG:4595-4759) was a MISLABELED stub (base Gauge, an Execute
override, mech/screenNumber SWAPPED) with no methodDescription -> parse-SKIPPED, none built.
Reconstructed byte-verified (ctor @4c7bf0, BecameActive @4c7e48, Make @4c7b30; vtable
0051a06c). THREE corrections vs the stub (decode workflow + vtables.tsv proof):
(1) base = GraphicGaugeBackground, not Gauge -- vtable-identical shape to the engine's
BackgroundBitmap (only dtor + BecameActive overridden); (2) the overridden slot is
BecameActive (a paint-on-activation), NOT Execute -- GraphicGaugeBackground has no Execute
virtual, so the Gauge::Execute->Fail->abort hazard doesn't apply; (3) screenNumber@0x6C /
mech@0x70 were swapped. sizeof 0x90.
BecameActive walks the mech roster (databinding-safe GetSubsystemCount/GetSubsystem +
BTGetSubsystemAuxScreen bridge, like VehicleSubSystems::Make), finds the subsystem whose
auxScreenNumber == this screen (1..12), and paints the screen-number numeric + subsystem
label + type-specific label cells dispatched on GetClassID (Sensor/Myomers/Emitter/PPC/
Projectile/Missile/GaussRifle). methodDescription = 9 params (mode, screen, 7 .pcc names).
Registered in BTL4MethodDescription[]. Two raw subsystem reads (heat-sink #, +0x224 label)
are guarded fail-soft best-efforts (marked; the aux-screen bridge label64 substitutes).
VERIFIED: the skip list drops from {prepEngr x12, messageBoard} to just {messageBoard} --
all 12 prepEngr gauges build; no screen-range/crash; boot+sim+combat run; gauge composite
renders identically (COOLANT 300/77, clusters, radar all un-regressed). The prepEngr labels
paint on the Eng (ModeMFD*Eng*) screens, not the default composite view.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
88cd68aaa1 |
gauge wave P2d: GeneratorCluster un-gated -- the 4 generator panels render
Root-caused the P2c abort with a cdb attach to the frozen abort dialog: the stack
was Gauge::Execute (GAUGE.cpp:598) -> abort, via GuardedExecute <- Update <-
ProcessOneActiveGauge. The engine base Gauge::Execute is Fail("not overridden")
-> abort, so an ACTIVE container that doesn't override Execute aborts by design;
GuardedExecute's SEH can't catch an abort(). The decode's "GeneratorCluster
overrides ONLY the dtor / renders via its children" was wrong -- the 1995 base
Gauge::Execute was a no-op, the 2007 WinTesla engine's aborts, so the container
MUST override Execute (the sibling SubsystemCluster does).
FIX: give GeneratorCluster the two container overrides (mirroring SubsystemCluster):
- BecameActive() -- blit the panel's static background pixmap (DrawPixelMap8) and,
by overriding the default, DON'T self-inactivate so the panel stays active.
- Execute() -- no-op (the 5 self-registered child gauges draw the dynamic content;
the override exists only to avoid the base Fail->abort).
Re-registered in BTL4MethodDescription[]. Verified DBASE+dev gauges: all 4
generator panels (A/B/C/D) render -- blue OutputVoltage bars (the @004c72ac
ScalarBarGauge Scalar* variant), labels, status lamps -- 0 "BecameActive not
defined" warnings, NO abort, combat un-regressed (TARGET DESTROYED). Phase 2 is
now COMPLETE (4/4 widgets: LeakGauge, VertNormalSlider, PilotList, GeneratorCluster).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
b44e908101 |
gauge wave P2c: GeneratorCluster reconstructed + infra (gated -- parent aborts)
Reconstructed the 4th unbuilt widget (the 4 generator engineering panels, buttons 9-12) + its companions, but left it UNREGISTERED pending a runtime-abort fix: - Generator OutputVoltage attribute table (powersub) -> outputVoltage@0x1DC, chained to HeatSink's dense index (temps already reach it). Correct + reusable. - ScalarBarGauge @004c72ac plain-Scalar variant (btl4gau2) -- the DIRECT-Scalar* bar the GeneratorCluster voltage bar needs (the existing @004c721c takes a resolved source object). Correct + harmless. - GeneratorCluster Make/ctor/dtor + 15-param methodDescription reconstructed from part_014.c:891-1013 (replacing the prose + placeholder Make + the bogus DAT pool); builds the 5 children (temp bar / voltage bar / leak wipe / 2 lamps). Header fixed to the binary ctor param order (secondaryColor@0x94, +_reserved0xB0 -> sizeof 0xB4). ChildRate forward-declared. ⚠ REGISTRATION GATED (commented out in BTL4MethodDescription[]): registering it aborts at runtime -- the 4 panels build (the 4 "BecameActive not defined" warnings fire) then abort(). ISOLATED to the PARENT lifecycle: the abort PERSISTS with all 5 children nulled; there are no pure virtuals; Execute is SEH-guarded under BT_DEV_GAUGES so the fault is in the unguarded ctor/lifecycle path. The sibling SubsystemCluster overrides BecameActive/Execute/TestInstance whereas the decode has GeneratorCluster overriding ONLY the dtor -- that "inherits only the dtor" claim is the prime suspect. Deferred; the 3 other P2 widgets (LeakGauge/VertNormalSlider/PilotList) ship clean. Verified: unregistered -> parse-skips -> no abort; combat un-regressed (DESTROYED), 0 crashes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
674c172cf8 |
vehicleSubSystems Phase 1: reconstruct + register the cluster-panel family
Reconstruct the whole engineering-screen (MFD) subsystem cluster-panel system in btl4gau2 (from the Ghidra decomp of BTL4OPT.EXE) and register the vehicleSubSystems config primitive: - Sub-gauges: AnimatedSubsystemLamp/AnimatedSourceLamp (OneOfSeveral + cooling/ power connections), ScalarBarGauge (BarGraphBitMapScalar + generator-voltage connection), ConfigMapGauge, plus the 3 file-private GaugeConnection classes (@004c3134/31ec/3288 samplers). - SubsystemCluster base (ctor builds ~11 child gauges across the MFD + engineering ports; Execute/dtor/frame-draw/enable helpers) + the 5 subclasses HeatSink/ Myomer/Weapon/Energy/Ballistic (ctors + Executes). - VehicleSubSystems::Make dispatcher (@004cbaf0): per-subsystem factory keyed on classID (HeatSink 0xBC3 / Myomer 0xBC6 / Energy 0xBC8,0xBD4 / Ballistic 0xBCD,0xBD0), placed via the 12-row aux-screen geometry table, + registered in BTL4MethodDescription[]. SAFE + gated: gauges only build under BT_DEV_GAUGES/pod, and until Phase 2 populates the subsystem aux-screen field (subsystem[0x1dc]) the Make hits its "aux screen = zero" skip -> combat untouched. Two minor children deferred as marked NULLs (SegmentArc270 recharge dial + BitMapInverseWipeScalar eject wipe -- their ctors are btl4gaug placeholders). Raw-offset subsystem reads (0x1dc/0x1e0/ 0x1e4/0x31c/0x334/0x364/0x43c...) marked BEST-EFFORT pending Phase 2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7b7d465e5e |
Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|