Merge remote-tracking branch 'origin/master'
# Conflicts: # game/reconstructed/btl4gau2.cpp
This commit is contained in:
+2
-2
@@ -183,9 +183,9 @@ badge=VGL
|
||||
patch=Yellow
|
||||
role=Role::Default
|
||||
dropzone=one
|
||||
vehicle=ava1
|
||||
vehicle=madcat
|
||||
vehicleValue=1000
|
||||
color=Red
|
||||
color=Crimson
|
||||
[largebitmap]
|
||||
bitmap=BitMap::Large::Aeolus
|
||||
[BitMap::Large::Aeolus]
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
[mission]
|
||||
adventure=BattleTech
|
||||
map=cavern
|
||||
scenario=freeforall
|
||||
time=night
|
||||
weather=clear
|
||||
temperature=27
|
||||
length=600
|
||||
[ordinals]
|
||||
bitmap=Ordinal::BitMap::1
|
||||
bitmap=Ordinal::BitMap::2
|
||||
bitmap=Ordinal::BitMap::3
|
||||
bitmap=Ordinal::BitMap::4
|
||||
[Ordinal::BitMap::1]
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=000038000003C000001FF00000000F00
|
||||
bitmap=0000F8000003C000003FF80000000F00
|
||||
bitmap=0001F8000003C00000707C0000000F00
|
||||
bitmap=0001F8000003C00000603C0000000F00
|
||||
bitmap=00007801FC0FF00000003C3DF807FF00
|
||||
bitmap=00007803FE0FF00000003C3FFC0FFF00
|
||||
bitmap=00007803C703C00000003C3E3C1F0F00
|
||||
bitmap=000078078303C0000000783C1E1E0F00
|
||||
bitmap=000078078003C0000000783C1E1E0F00
|
||||
bitmap=00007807C003C0000000F03C1E1E0F00
|
||||
bitmap=00007807F003C0000001E03C1E1E0F00
|
||||
bitmap=00007803FC03C0000003C03C1E1E0F00
|
||||
bitmap=00007801FE03C0000007803C1E1E0F00
|
||||
bitmap=000078007F03C000000F003C1E1E0F00
|
||||
bitmap=000078001F03C000001E003C1E1E0F00
|
||||
bitmap=000078000F03C000003C003C1E1E0F00
|
||||
bitmap=000078060F03C0000078003C1E1E0F00
|
||||
bitmap=000078071E03E0000078003C1E1F1F00
|
||||
bitmap=00007803FE01F000007FFC3C1E0FFF00
|
||||
bitmap=00007801FC00F000007FFC3C1E07EF00
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
x=128
|
||||
y=32
|
||||
width=8
|
||||
[Ordinal::BitMap::2]
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=001FFF0000003C0000003C03C0780000
|
||||
bitmap=001FFF0000003C0000007C03C0780000
|
||||
bitmap=00000E0000003C000000FC03C0780000
|
||||
bitmap=00003C0000003C000001BC03C0780000
|
||||
bitmap=0000700F3E1FFC000003BC0FF07BF000
|
||||
bitmap=0001E00F7E3FFC0000073C0FF07FF800
|
||||
bitmap=0003800FFE7C3C0000063C03C07C7800
|
||||
bitmap=0007F80FFE783C00000C3C03C0783C00
|
||||
bitmap=0007FE0F80783C0000183C03C0783C00
|
||||
bitmap=00001E0F00783C0000383C03C0783C00
|
||||
bitmap=00000F0F00783C0000703C03C0783C00
|
||||
bitmap=00000F0F00783C00007FFF03C0783C00
|
||||
bitmap=00000F0F00783C00007FFF03C0783C00
|
||||
bitmap=00000F0F00783C0000003C03C0783C00
|
||||
bitmap=00000F0F00783C0000003C03C0783C00
|
||||
bitmap=00000F0F00783C0000003C03C0783C00
|
||||
bitmap=00180F0F00783C0000003C03C0783C00
|
||||
bitmap=001C1F0F007C7C0000003C03E0783C00
|
||||
bitmap=000FFE0F003FFC0000003C01F0783C00
|
||||
bitmap=0007FC0F001FBC0000003C00F0783C00
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
x=128
|
||||
y=32
|
||||
width=8
|
||||
[Ordinal::BitMap::3]
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=001FFF03C07800000000FC03C0780000
|
||||
bitmap=001FFF03C07800000003FC03C0780000
|
||||
bitmap=001E0003C07800000007C003C0780000
|
||||
bitmap=001E0003C0780000000F0003C0780000
|
||||
bitmap=001E000FF07BF000000F000FF07BF000
|
||||
bitmap=001E000FF07FF800001E000FF07FF800
|
||||
bitmap=001E0003C07C7800001E0003C07C7800
|
||||
bitmap=001FFC03C0783C00001EFC03C0783C00
|
||||
bitmap=001FFE03C0783C00001FFE03C0783C00
|
||||
bitmap=00001F03C0783C00001F1F03C0783C00
|
||||
bitmap=00000F03C0783C00001E0F03C0783C00
|
||||
bitmap=00000F03C0783C00001E0F03C0783C00
|
||||
bitmap=00000F03C0783C00001E0F03C0783C00
|
||||
bitmap=00000F03C0783C00001E0F03C0783C00
|
||||
bitmap=00000F03C0783C00001E0F03C0783C00
|
||||
bitmap=00000F03C0783C00001E0F03C0783C00
|
||||
bitmap=00180F03C0783C00001E0F03C0783C00
|
||||
bitmap=001C1F03E0783C00001F1F03E0783C00
|
||||
bitmap=000FFE01F0783C00000FFE01F0783C00
|
||||
bitmap=0007FC00F0783C000007FC00F0783C00
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
x=128
|
||||
y=32
|
||||
width=8
|
||||
[Ordinal::BitMap::4]
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=001FFF03C07800000007FC03C0780000
|
||||
bitmap=001FFF03C0780000000FFE03C0780000
|
||||
bitmap=00000F03C0780000001F1F03C0780000
|
||||
bitmap=00000F03C0780000001E0F03C0780000
|
||||
bitmap=00000F0FF07BF000001E0F0FF07BF000
|
||||
bitmap=00001F0FF07FF800001E0F0FF07FF800
|
||||
bitmap=00001E03C07C7800001E0F03C07C7800
|
||||
bitmap=00003E03C0783C00001E0F03C0783C00
|
||||
bitmap=00003C03C0783C00000F1E03C0783C00
|
||||
bitmap=00003C03C0783C000007FC03C0783C00
|
||||
bitmap=00007803C0783C000007FC03C0783C00
|
||||
bitmap=00007803C0783C00000F1E03C0783C00
|
||||
bitmap=00007803C0783C00001E0F03C0783C00
|
||||
bitmap=0000F003C0783C00001E0F03C0783C00
|
||||
bitmap=0000F003C0783C00001E0F03C0783C00
|
||||
bitmap=0000F003C0783C00001E0F03C0783C00
|
||||
bitmap=0000F003C0783C00001E0F03C0783C00
|
||||
bitmap=0000F003E0783C00000F1E03E0783C00
|
||||
bitmap=0000F001F0783C00000FFE01F0783C00
|
||||
bitmap=0000F000F0783C000007FC00F0783C00
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
x=128
|
||||
y=32
|
||||
width=8
|
||||
[pilots]
|
||||
pilot=127.0.0.1:1502
|
||||
pilot=127.0.0.1:1602
|
||||
[127.0.0.1:1502]
|
||||
hostType=0
|
||||
advancedDamage=1
|
||||
loadzones=1
|
||||
name=Aeolus
|
||||
bitmapindex=1
|
||||
experience=expert
|
||||
badge=VGL
|
||||
patch=Yellow
|
||||
role=Role::Default
|
||||
dropzone=one
|
||||
vehicle=bhk1
|
||||
vehicleValue=1000
|
||||
color=White
|
||||
[127.0.0.1:1602]
|
||||
hostType=0
|
||||
advancedDamage=1
|
||||
loadzones=1
|
||||
name=Boreas
|
||||
bitmapindex=2
|
||||
experience=expert
|
||||
badge=VGL
|
||||
patch=Yellow
|
||||
role=Role::Default
|
||||
dropzone=one
|
||||
vehicle=madcat
|
||||
vehicleValue=1000
|
||||
color=Crimson
|
||||
[largebitmap]
|
||||
bitmap=BitMap::Large::Aeolus
|
||||
[BitMap::Large::Aeolus]
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=000003FE0000000001E0000000000000
|
||||
bitmap=000007FF0000000001E0000000000000
|
||||
bitmap=00000F8F8000000001E0000000000000
|
||||
bitmap=00000F078000000001E0000000000000
|
||||
bitmap=00000F0781FF00FF81E0F0781FC00000
|
||||
bitmap=00000F0783FF81FFC1E0F0783FE00000
|
||||
bitmap=00000F0787C7C3E3E1E0F0783C700000
|
||||
bitmap=00000F078783C3C1E1E0F07878300000
|
||||
bitmap=00000F078783C3C1E1E0F07878000000
|
||||
bitmap=00000FFF8783C3C1E1E0F0787C000000
|
||||
bitmap=00000FFF8783C3C1E1E0F0787F000000
|
||||
bitmap=00000F0787FFC3C1E1E0F0783FC00000
|
||||
bitmap=00000F0787FFC3C1E1E0F0781FE00000
|
||||
bitmap=00000F07878003C1E1E0F07807F00000
|
||||
bitmap=00000F07878003C1E1E0F07801F00000
|
||||
bitmap=00000F07878003C1E1E0F07800F00000
|
||||
bitmap=00000F078780C3C1E1E0F07860F00000
|
||||
bitmap=00000F0787C1C3E3E1E078F871E00000
|
||||
bitmap=00000F0783FF81FFC1E07FF83FE00000
|
||||
bitmap=00000F0781FF00FF81E03F781FC00000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
x=128
|
||||
y=32
|
||||
width=8
|
||||
[smallbitmap]
|
||||
bitmap=BitMap::Small::Aeolus
|
||||
[BitMap::Small::Aeolus]
|
||||
bitmap=00000000000000000000000000000000
|
||||
bitmap=0000000000000000003E00000C000000
|
||||
bitmap=006300000C00000000631F0F8CC63C00
|
||||
bitmap=00633198CCC66200007F3198CCC66000
|
||||
bitmap=00633F98CCC6780000633018CCC61E00
|
||||
bitmap=00633018CCC6060000633098CCC64600
|
||||
bitmap=00631F0F8C7E3C000000000000000000
|
||||
bitmap=00000000000000000000000000000000
|
||||
x=64
|
||||
y=16
|
||||
width=4
|
||||
[Role::Default]
|
||||
model=dfltrole
|
||||
[Role::NoReturn]
|
||||
model=noretun
|
||||
@@ -142,6 +142,22 @@ At the authentic 1e7-unit economy (tasks #9-#10) the Heat MFD is fully dynamic:
|
||||
77→2000 (the authored failure threshold), condensers/generators run 100-1400 under sustained
|
||||
fire, and the bank plateaus ~600 — temperatures, heatLoad and the heat alarms all animate. [T2]
|
||||
|
||||
## Radar view-wedge tracks the torso twist (Gitea issue #1 — FIXED 2026-07-17) [T2]
|
||||
The SECTOR radar's view cone (`MapDisplay::DrawViewWedge` @004c2484, btl4rdr.cpp) reads
|
||||
`viewHorizontalRotation@0x31C`, fed by a per-frame `GaugeConnectionDirectOf<Radian>` the ctor
|
||||
wires from the mech's Torso. The two helpers were NULL stubs, so the connection was never
|
||||
created and the wedge sat at heading 0 (the reported "cone does not track the twist").
|
||||
Reconstructed from the binary: `FindSubObject` (FUN_0041f98c) = a SUBSYSTEM-ROSTER walk
|
||||
(count @+0x124 / array @+0x128) matching the streamed name (sub+0xd4) case-insensitively
|
||||
(FUN_004d4b58 = tolower-strcmp) — the "Torso" sub-object IS the roster Torso subsystem;
|
||||
`GetHorizontalRotation` = torso+0x1D8 = `Torso::currentTwist` (layout-locked), reached via the
|
||||
existing task-#56 bridge `BTGetTorsoTwistAddr` (Radian ≡ {Scalar angle}, exact reinterpret).
|
||||
VERIFIED live (MadCat, Standard mode Q/E): wedge rot tracks twist 0→−2.21 rad in lockstep, and
|
||||
the SEMANTIC test passes — body turned away, torso twisted back onto the enemy → reticle goes
|
||||
green AND the wedge points at the enemy's blip while the scope stays body-fixed. ⚠ Test with a
|
||||
TWISTING mech: the Blackhawk's torso is FIXED (limits ±0.01°) — its wedge authentically never
|
||||
moves. Diag: `BT_RADAR_LOG` ([radar] ctor probe + 1 Hz [radar-wedge] rot trace). [T2]
|
||||
|
||||
## Remaining = DATA FEEDS, not widgets (deferred systems)
|
||||
**✅ The condenser valve CONTROL is LIVE (task #13, 2026-07-11) [T2]:** `MoveValve` (id 4, the
|
||||
Condenser handler table @0x50E52C — exactly one entry) registered + guarded by the REAL
|
||||
|
||||
@@ -29,6 +29,32 @@ authentic path scoped.
|
||||
Remaining: bind the side/down look views to keys (only 'V' look-back is bound); pod look-button
|
||||
mappings for Phase 8.
|
||||
|
||||
## Pod-hardware readiness gaps (Phase 8 assessment, 2026-07-17) [[pod-hardware]]
|
||||
Everything below EXISTS in the tree but has NEVER run against real pod hardware. Mechanism
|
||||
status vs the pod's 7-display / RGB-splitter / dual-sound-card rig:
|
||||
- **CARRIED + plausibly functional (untested on hardware):**
|
||||
- RIO serial controls: real Win32 COM (`L4SERIAL` CreateFileA/SetCommState), the full chain
|
||||
RIO AnalogReply → Ranger → ControlsManager → streamed `.CTL` mappings → mapper traced [T1];
|
||||
keyboard bridge disable = `BT_KEY_BRIDGE=0`. Pod look/config buttons are `.CTL` data-driven.
|
||||
- RGB-splitter channel packing: `L4GraphicsPort::ChannelEnableID` (Red/Green/Blue/All +
|
||||
transparent-zero variants) — each gauge draws into ONE color channel of the shared
|
||||
640x480x16 surface, feeding one monochrome CRT per channel through the splitter. The gauge
|
||||
CONTENT is complete (same `L4GaugeRenderer` verified daily in the `BT_DEV_GAUGES` window).
|
||||
- Multi-output selection: `SVGA16::BuildWindows` (a fullscreen D3D9 device per gauge/MFD
|
||||
surface), adapter env `PRIMGAUGE`/`SECGAUGE`/`MFDGAUGE`/`MFDGAUGE2`/`SPANDISABLE`, MFD span
|
||||
= width x2 across two displays; `content/SETENV.BAT` = the authentic pod env preset.
|
||||
- The `L4PLASMA.cpp` serial plasma-display driver (`L4PLASMA=com2`).
|
||||
- **EXPECTED TO NEED REAL WORK on modern hardware:**
|
||||
1. **Multi-head display management**: the original used TWO video cards; simultaneous
|
||||
exclusive-fullscreen D3D9 devices on multiple heads of one modern GPU is fragile — likely
|
||||
needs a borderless-window-per-head modernization of `BuildWindows`.
|
||||
2. **The REAR sound card is folded away**: the pod ran `AWE_FRONT`/`AWE_REAR` as two physical
|
||||
cards for the 4-speaker front/rear split; our OpenAL opens ONE device (front/rear collapses
|
||||
to stereo). Feasible fix: OpenAL Soft quad output (surround config) or a second device.
|
||||
3. Serial protocol timing/handshake vs a real RIO board (and the intercom path) — unverified.
|
||||
4. `-platform pod` deliberately does NOT auto-enable `L4GAUGE` (each surface needs its own
|
||||
device); the pod bring-up procedure itself is undocumented → the Nick items above.
|
||||
|
||||
## BINARY-COVERAGE AUDIT discoveries (2026-07-13, 6-agent decomp census) [T1 verified-uncited]
|
||||
New unaccounted functionality no prior list knew (addresses verified absent from game/+context/+docs):
|
||||
- **✅ `BTL4VideoRenderer::StartEntityEffectImplementation` @004d097c — DONE (2026-07-13) [T2].**
|
||||
@@ -618,6 +644,10 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
|
||||
LIVENESS audit (does a live call path reach each reconstructed fn?) would catch this class.
|
||||
|
||||
## Rendering follow-ups (non-blocking)
|
||||
- ~~Per-pilot mech PAINT (color/badge/patch)~~ — **DONE 2026-07-17, verified live** (crimson MadCat +
|
||||
yellow VGL emblems + hip hazard stripes). Mechanics + the vehicletable color/badge/patch name
|
||||
tables in [[rendering]] §Per-pilot mech PAINT. Residual nit: eggs saying `color=Red` mean
|
||||
**Crimson** (table has no Red; the binary Fail()ed on it, the port warns + drops).
|
||||
- **.PFX effect-layer polish (deferred 2026-07-08; the layer itself is LIVE + verified).** The
|
||||
reconstructed BT particle layer (L4VIDEO.cpp, see its banner) renders the authentic .PFX content
|
||||
(fire + smoke, premultiplied blend, impact-frame oriented — weapon hits AND damage bands via
|
||||
|
||||
@@ -263,6 +263,11 @@ state transitions: charge/seek loops, snap comparisons, timers compared with `==
|
||||
ucrtbased **abort() dialog** ("Debug Error!"), not an AV — `sxe av` won't break there; the box
|
||||
blocks the event loop (a headless node just "stops logging"). cdb: run with a config that does
|
||||
`g` then `kb 40` — the int3 lands ON the aborting thread. [T2]
|
||||
- **Verify under the USER'S launch flags, not a bare run:** a "30 s regression: stable" check that
|
||||
omits `BT_DEV_GAUGES=1` / `BT_START_INSIDE=1` (the `tools/mp_launch.sh` set) misses any bug on the
|
||||
gauge/cockpit paths those flags gate. The task-#7 gauge crash was DORMANT for a day because every
|
||||
post-commit check ran flagless; it surfaced the instant the pod launch config was used. Reproduce
|
||||
with the real launcher's env, and drive (`-net` keyboard) — not just spawn-and-look. [T2]
|
||||
|
||||
---
|
||||
|
||||
@@ -358,6 +363,36 @@ hundreds of pixels off-scope (the radar looked simply "empty"; nothing crashed,
|
||||
decompile) before assigning it an engine method — a wrong-but-plausible identity survives
|
||||
every compile and every "it runs" test.
|
||||
|
||||
## 18. Uninitialized stack-built resource → garbage `simulationFlags` (the "worked for weeks then broke" trap)
|
||||
|
||||
**Symptom:** a subsystem installed from a HAND-BUILT (stack) `*__SubsystemResource` silently stops
|
||||
ticking — `IsNonReplicantExecutable()` returns false because a stray `DontExecuteFlag` (0x2) bit is
|
||||
set. Config-dependent and nondeterministic: it "works for weeks" then an UNRELATED commit that shifts
|
||||
the stack layout before the install flips the garbage bit. Archetype (task #7, 2026-07-17): the RIO
|
||||
controls mapper built on the stack in `btl4app.cpp MakeViewpointEntity` — `Subsystem::Subsystem`
|
||||
copies `model->subsystemFlags` into `simulationFlags`, but the stack struct only set
|
||||
`subsystemName`/`classID`/`subsystemModelSize`, leaving `subsystemFlags`+`segmentIndex` = stack
|
||||
garbage. When the garbage carried 0x2 the mapper froze → `speedDemand` stuck at 0 → no forward motion
|
||||
(MASKED in single-player when the garbage happened to be clean; surfaced in `-net` where the stack
|
||||
differed; the July-16 audio commits shifted the stack and flipped it). **Cause:** partial init of a
|
||||
struct the engine reads in full. **Fix:** `memset(&res, 0, sizeof(res))` before filling ANY
|
||||
stack-built resource — flags 0 = `AlwaysExecute`, the faithful value for a mapper (it MUST tick every
|
||||
frame). **Tell:** a subsystem present in the roster but absent from the executed set; instrument the
|
||||
tick's `!IsNonReplicantExecutable()` branch to name the slot + dump `simulationFlags` (bit 0x2 =
|
||||
DontExecute). **Rule:** zero every hand-built resource struct; never rely on a partially-filled one.
|
||||
[T2]
|
||||
|
||||
**Related — raw numeric attribute index into a GROWING table (same root as gotcha 11):** the dev
|
||||
gauges' `CoolingLoopConnection` reached the cooling master with a RAW `GetAttributePointer(3)` +
|
||||
`*(master+0x1d4)` walk. The July-16 audio work inserted attribute rows (`ReportLeak` etc.), shifting
|
||||
the chained ids — index 3 landed on a scalar, the resolve walked garbage, and the gauge background
|
||||
pass AV'd (only with `BT_DEV_GAUGES=1`, the pod launch flag, ~15 s in when the gauge builds lazily —
|
||||
the cdb stack pinned it to `CoolingLoopConnection::Update`). **Fix:** route through a complete-type
|
||||
bridge reading NAMED members (`heat.cpp BTCoolingLoopFrame`: `linkedSinks.Resolve()` +
|
||||
`Condenser::condenserNumber`) — the databinding rule (gotcha 8), never a raw numeric attribute index.
|
||||
The name-keyed samplers (`PowerSourceConnection`/`GeneratorVoltageConnection`, which look up
|
||||
`"InputVoltage"` by NAME) were unaffected — name lookup survives table growth. [T2]
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic recipe (the standard loop)
|
||||
|
||||
+37
-2
@@ -105,8 +105,43 @@ models is deferred (needs the board's hot-spot semantics). [T2]
|
||||
`.BSL` is a bit-sliced container (6 grayscale sub-images per file); the BMF `0x18 BITSLICE` tag picks
|
||||
the slice. Reading it as RGB overlaid 2-3 gray sheets = the "rainbow/graffiti" bug. Fixed:
|
||||
`image.cpp decodeBSL` slice-decodes; `bgfload` threads the 0x18 channel; a cross-library RAMP registry
|
||||
resolves the mech-skin `softer` ramp (defined in OTHER libs). Every mech renders authentic gray-metal
|
||||
paint now. See [[asset-formats]]. [T2]
|
||||
resolves the mech-skin `softer` ramp (defined in OTHER libs). Every mech renders the authentic BASE
|
||||
gray-metal skin now. See [[asset-formats]]. [T2]
|
||||
|
||||
## Per-pilot mech PAINT (color/badge/patch substitution) — NOT WIRED (2026-07 audit)
|
||||
The "missing warning stripes / coloration" report is the per-pilot paint layer, fully authored but
|
||||
never engaged by the port. Mechanics (all [T1] from the BMF/BGF/vehicletable content + FUN_004d0cc0):
|
||||
- Mech BGFs reference the BASE lib only (`blhskin:gen2_dz_rfoot_mtl` …). The binary rewrites lib
|
||||
prefixes at load via the `vehicletable` resource `[substitute]` templates: `skin:` → `_%color%%serno%:`
|
||||
(`blhskin:` → `blh_wht0:`) and `skin:lgo` → `<<<emblem%serno%:%badge%_%patch%>` (emblem pages →
|
||||
`EMBLEM0.BMF` `lgoN_<patch>_dz_*` materials). Pilot egg values (color/badge/patch) map through the
|
||||
`[color]/[badge]/[patch]` sections to the codes.
|
||||
- The COLOR libs (e.g. `BLH_WHT0.BMF`) are byte-identical to the base lib EXCEPT they add tags
|
||||
`0x23`(diffuse RGB) + `0x24`(ambient RGB) = the pilot color to the ~35 paintable `gen*/mech*_dz_*`
|
||||
materials (base leaves them untinted). 7 colors: wht(.99,.99,.99) red(.90,.11,.10) grn(.63,.83,.52)
|
||||
gry(.65,.60,.65) brn(.60,.44,.28) tan(.65,.55,.43) blk(0,0,0). All 8 sernos are byte-identical
|
||||
(verified: 0 of 193 groups differ) — `%serno%` was future variety, never used. `<mech>SKIN.BMF`
|
||||
(bare) == `<mech>SKIN0.BMF`.
|
||||
- WARNING STRIPES are grayscale hazard-stripe art in the shared `GEN.BSL` pages (slice 1 = `gen2_tex`,
|
||||
top-left), shown through the paintable-zone materials and tinted by the pilot color. `EMBLEM0.BMF`
|
||||
carries `lgoN_<patchcolor>_dz_ld/rdleg` materials (8 patch tints incl. yellow .89,.73,0) over
|
||||
`LGO.BSL` slices 0-5 (slice 5 = VGL winged logo).
|
||||
- **WIRED 2026-07-17 (same-day fix), verified live**: MP MadCat renders crimson with hip hazard
|
||||
stripes + yellow VGL leg emblems; Blackhawk white + emblems; replicants painted on both nodes.
|
||||
The reconstruction: (a) `Mech::resourceNameA/B/C` are real `CString` members (the binary's exact
|
||||
16-byte `CStringRepresentation` {size,len,text@+8,refs@+0xc}; ctor = part_012.c:9841-71, deep-copy
|
||||
bind from the MakeMessage = `CString::operator=(const char*)` == FUN_00402a98 @10427-41, implicit
|
||||
member dtors = 11388-403) with paint-name accessors; (b) `SetupMaterialSubstitutionList` reads them
|
||||
(real egg names; `[paint]` log line); (c) `dpl_SetMaterialNameCallback` is REAL now
|
||||
(L4VIDEO.cpp registry, cleared first in TearDown as FUN_004d11e8 does) and
|
||||
`bgfload MaterialResolver::resolve()` runs every name through `dpl_ApplyMaterialNameCallback`;
|
||||
no mesh-cache issue (LoadObject/LoadBgfFile don't cache — every load resolves fresh). [T2]
|
||||
- DEVIATION (documented, btl4vid.cpp): on a vehicletable miss the binary Fail()ed ("Exiting"
|
||||
@BTL4VID.CPP:0xbeb); the port warns + drops that placeholder (RP analogue). Real case: dev eggs
|
||||
ship `color=Red` but the `[color]` table only has **Crimson** — MP_BHMC.EGG fixed to Crimson;
|
||||
LAST.EGG pilot-2 still says Red (unused slot). The 7 valid egg colors:
|
||||
Black/Brown/Crimson/Green/Grey/Tan/White; badges Davion/Kurita/Liao/Marik/None/Steiner/VGL;
|
||||
patches Black/Blue/Green/Grey/Red/Violet/White/Yellow. [T1]
|
||||
|
||||
## The OLD-STYLE SPECIALFX layer (the weapon-impact action) — RECONSTRUCTED 2026-07-12 (firmware-decoded)
|
||||
The per-weapon impact visuals are fully AUTHORED in `content/BTDPL.INI` `[effects_to_load]`:
|
||||
|
||||
@@ -2631,6 +2631,39 @@ char*
|
||||
return source;
|
||||
}
|
||||
|
||||
//#############################################################################
|
||||
// dpl material-name callback registry (FUN_0049664c / DAT_00527f24).
|
||||
//-----------------------------------------------------------------------------
|
||||
// The 1995 dpl board applied the installed callback to every material name as
|
||||
// a model loaded (dpl_LoadObject). The port analogue: the BGF loader
|
||||
// (bgfload.cpp MaterialResolver::resolve) runs every "lib:material" name
|
||||
// through dpl_ApplyMaterialNameCallback before resolving it, so the per-pilot
|
||||
// paint substitutions (skin: -> _<color><serno>:, skin:lgo -> emblem<serno>:)
|
||||
// installed by SetupMaterialSubstitutionList take effect on the mech model
|
||||
// loads that happen between the Setup/TearDown bracket.
|
||||
//#############################################################################
|
||||
static char *(*gMaterialNameCallback)(char *source) = NULL;
|
||||
|
||||
void dpl_SetMaterialNameCallback(char *(*callback)(char *source))
|
||||
{
|
||||
gMaterialNameCallback = callback;
|
||||
}
|
||||
|
||||
// C++-friendly wrapper honouring the callback's legacy protocol (the callback
|
||||
// may delete[] the input and return a new[] buffer). Returns the (possibly
|
||||
// substituted) name.
|
||||
std::string dpl_ApplyMaterialNameCallback(const std::string &name)
|
||||
{
|
||||
if (gMaterialNameCallback == NULL)
|
||||
return name;
|
||||
char *buf = new char[name.size() + 1];
|
||||
strcpy(buf, name.c_str());
|
||||
buf = gMaterialNameCallback(buf);
|
||||
std::string result(buf);
|
||||
delete [] buf;
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<MONITORINFO> DPLRenderer::MonitorsCreateAll(int &monitorCount)
|
||||
{
|
||||
monitorCount = gD3D->GetAdapterCount();
|
||||
|
||||
@@ -40,6 +40,13 @@ class NotationFile;
|
||||
char*
|
||||
substituteMaterial(char *source);
|
||||
|
||||
// dpl material-name callback registry (FUN_0049664c): install/clear the
|
||||
// per-load substitution hook; dpl_ApplyMaterialNameCallback runs the installed
|
||||
// hook over a name (identity when none installed). Consumed by the BGF
|
||||
// loader's material resolver.
|
||||
void dpl_SetMaterialNameCallback(char *(*callback)(char *source));
|
||||
std::string dpl_ApplyMaterialNameCallback(const std::string &name);
|
||||
|
||||
void loadTables();
|
||||
const char* opMaterialName(const char *fileName, int opId);
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#include <set>
|
||||
#include <array>
|
||||
|
||||
// L4VIDEO.cpp: the dpl material-name callback registry. While a mech loads
|
||||
// (the SetupMaterialSubstitutionList / TearDown bracket) this rewrites
|
||||
// "blhskin:..." -> "blh_wht0:..." / "emblem0:..." -- the per-pilot paint.
|
||||
std::string dpl_ApplyMaterialNameCallback(const std::string &name);
|
||||
|
||||
namespace {
|
||||
|
||||
// ---- DIV-BIZ2 tag ids (PFBIZTAG.H), masked form (tagword & 0x2fff) ----
|
||||
@@ -503,7 +508,11 @@ struct MaterialResolver {
|
||||
return libs.emplace(lib, std::move(table)).first->second;
|
||||
}
|
||||
|
||||
MatInfo resolve(const std::string& full) {
|
||||
MatInfo resolve(const std::string& fullIn) {
|
||||
// Per-pilot paint: run the name through the installed dpl material-name
|
||||
// callback first (identity when none) -- the board rewrote names at
|
||||
// load time, before any material lookup.
|
||||
std::string full = dpl_ApplyMaterialNameCallback(fullIn);
|
||||
auto cached = resolvedCache.find(full);
|
||||
if (cached != resolvedCache.end()) return cached->second;
|
||||
MatInfo info;
|
||||
|
||||
@@ -445,6 +445,8 @@ BTL4Application::SharedData
|
||||
//------------------------------------------------------------
|
||||
CameraControlsMapper::SubsystemResource
|
||||
control_subsystem_resource;
|
||||
// task #7: zero the stack resource (see the mech mapper note below).
|
||||
memset(&control_subsystem_resource, 0, sizeof(control_subsystem_resource));
|
||||
|
||||
Str_Copy(
|
||||
control_subsystem_resource.subsystemName,
|
||||
@@ -530,6 +532,14 @@ BTL4Application::SharedData
|
||||
//
|
||||
MechControlsMapper::SubsystemResource
|
||||
control_subsystem_resource;
|
||||
// task #7 ROOT-CAUSE FIX: Subsystem::Subsystem copies
|
||||
// model->subsystemFlags into simulationFlags -- an
|
||||
// uninitialized stack resource left GARBAGE there, and a
|
||||
// stray DontExecuteFlag bit froze the installed controls
|
||||
// mapper for the whole session (the -net "can't drive"
|
||||
// freeze; config-dependent because stack contents are).
|
||||
// Zero the whole struct before filling it.
|
||||
memset(&control_subsystem_resource, 0, sizeof(control_subsystem_resource));
|
||||
|
||||
Str_Copy(
|
||||
control_subsystem_resource.subsystemName,
|
||||
@@ -555,6 +565,14 @@ BTL4Application::SharedData
|
||||
{
|
||||
MechControlsMapper::SubsystemResource
|
||||
control_subsystem_resource;
|
||||
// task #7 ROOT-CAUSE FIX: Subsystem::Subsystem copies
|
||||
// model->subsystemFlags into simulationFlags -- an
|
||||
// uninitialized stack resource left GARBAGE there, and a
|
||||
// stray DontExecuteFlag bit froze the installed controls
|
||||
// mapper for the whole session (the -net "can't drive"
|
||||
// freeze; config-dependent because stack contents are).
|
||||
// Zero the whole struct before filling it.
|
||||
memset(&control_subsystem_resource, 0, sizeof(control_subsystem_resource));
|
||||
|
||||
Str_Copy(
|
||||
control_subsystem_resource.subsystemName,
|
||||
@@ -589,6 +607,14 @@ BTL4Application::SharedData
|
||||
{
|
||||
MechControlsMapper::SubsystemResource
|
||||
control_subsystem_resource;
|
||||
// task #7 ROOT-CAUSE FIX: Subsystem::Subsystem copies
|
||||
// model->subsystemFlags into simulationFlags -- an
|
||||
// uninitialized stack resource left GARBAGE there, and a
|
||||
// stray DontExecuteFlag bit froze the installed controls
|
||||
// mapper for the whole session (the -net "can't drive"
|
||||
// freeze; config-dependent because stack contents are).
|
||||
// Zero the whole struct before filling it.
|
||||
memset(&control_subsystem_resource, 0, sizeof(control_subsystem_resource));
|
||||
|
||||
Str_Copy(
|
||||
control_subsystem_resource.subsystemName,
|
||||
|
||||
@@ -181,22 +181,11 @@ static void *ResolveLink(void *plug)
|
||||
//
|
||||
extern bool BTGetSubsystemAuxScreen(Subsystem *sub, int *screen, int *placement, char *label64);
|
||||
|
||||
//
|
||||
// FUN_0041bf44 -- GetAttributePointer(index): a pointer to the subsystem's Nth
|
||||
// attribute member (the cooling-loop lamp reaches its cooling-master link at
|
||||
// attribute slot 3), or 0 if the index is out of range. The engine exposes this
|
||||
// via Simulation::GetAttributePointer(AttributeID); a miss returns &NullAttribute,
|
||||
// normalised to 0.
|
||||
//
|
||||
static void *FindLinkedSubsystem(void *subsystem, int slot)
|
||||
{
|
||||
if (subsystem == 0)
|
||||
return 0;
|
||||
void *p = ((Simulation *)subsystem)->GetAttributePointer((Simulation::AttributeID)slot);
|
||||
if (p == (void *)&Simulation::NullAttribute)
|
||||
return 0;
|
||||
return p;
|
||||
}
|
||||
// (FUN_0041bf44 GetAttributePointer-by-index helper removed with the task #7
|
||||
// fix: its only caller was CoolingLoopConnection, which now resolves the
|
||||
// cooling master through the named-member bridge BTCoolingLoopFrame -- reaching
|
||||
// an attribute by RAW numeric index is exactly what broke when the audio
|
||||
// attribute rows grew the chained tables.)
|
||||
|
||||
//
|
||||
// FUN_0041a1a4 IsDerivedFrom(0x50f4bc == PoweredSubsystem's ClassDerivations).
|
||||
@@ -218,10 +207,16 @@ static Logical IsGeneratorDerived(Subsystem *sub) { return BTIsPoweredSubsystem(
|
||||
|
||||
//
|
||||
// CoolingLoopConnection -- @004c3134 ctor / @004c31a0 sampler (vt 0x518ea8).
|
||||
// When the source subsystem's coolant loop is active (source+0x134 == 1) it
|
||||
// follows the linked cooling master (FindLinkedSubsystem(source,3) then Resolve)
|
||||
// and copies its loop-lamp state (+0x1d4); otherwise 0.
|
||||
// When the source subsystem's coolant loop is available (coolantAvailable == 1,
|
||||
// the binary's source+0x134 gate) it follows the linked cooling master (binary
|
||||
// attr id 3 == "HeatSink" == linkedSinks) and shows its Condenser loop number
|
||||
// (+0x1d4 == condenserNumber, the image-strip frame); otherwise 0.
|
||||
// task #7 REGRESSION FIX (2026-07-17): the old raw GetAttributePointer(3) +
|
||||
// *(+0x1d4) walk AV'd after the audio attribute-table rows shifted the chained
|
||||
// ids (BT_DEV_GAUGES crash). The sampler now routes through a complete-type
|
||||
// bridge (heat.cpp) reading NAMED members -- the databinding rule (gotcha 8).
|
||||
//
|
||||
extern int BTCoolingLoopFrame(void *subsystem);
|
||||
class CoolingLoopConnection : public GaugeConnection
|
||||
{
|
||||
public:
|
||||
@@ -230,18 +225,7 @@ public:
|
||||
source(source), destination(destination) {}
|
||||
void Update() // @004c31a0
|
||||
{
|
||||
void *resolved = 0;
|
||||
// [T3] guard the raw +0x134 / +0x1d4 reads against the layout mismatch
|
||||
// (see BTPtrReadable above) -- an invalid chain reads "no source" (0).
|
||||
if (BTPtrReadable((char *)source + 0x134) &&
|
||||
*(int *)((char *)source + 0x134) == 1)
|
||||
{
|
||||
void *linked = FindLinkedSubsystem(source, 3);
|
||||
if (linked != 0)
|
||||
resolved = ResolveLink(linked);
|
||||
}
|
||||
*destination = (resolved != 0 && BTPtrReadable((char *)resolved + 0x1d4))
|
||||
? *(int *)((char *)resolved + 0x1d4) : 0;
|
||||
*destination = BTCoolingLoopFrame(source);
|
||||
}
|
||||
protected:
|
||||
void *source; // @0x10 this[4]
|
||||
|
||||
@@ -201,11 +201,13 @@ static Entity *
|
||||
|
||||
//
|
||||
// Mech sub-object lookup ("Torso") + its horizontal rotation feed.
|
||||
// (The "sub-object" IS a roster SUBSYSTEM -- FUN_0041f98c walks the entity's
|
||||
// subsystem roster by NAME; see the bodies at the end of this file.)
|
||||
//
|
||||
static Entity *
|
||||
static Subsystem *
|
||||
FindSubObject(Entity *entity, const char *name);
|
||||
static Radian *
|
||||
GetHorizontalRotation(Entity *entity);
|
||||
GetHorizontalRotation(Subsystem *torso);
|
||||
|
||||
|
||||
//###########################################################################
|
||||
@@ -471,7 +473,11 @@ MapDisplay::MapDisplay(
|
||||
//
|
||||
if (entity->GetClassID() == 0xBB9)
|
||||
{
|
||||
Entity *torso = FindSubObject(entity, "Torso"); // FUN_0041f98c
|
||||
Subsystem *torso = FindSubObject(entity, "Torso"); // FUN_0041f98c (roster walk)
|
||||
if (getenv("BT_RADAR_LOG"))
|
||||
DEBUG_STREAM << "[radar] ctor: torso sub "
|
||||
<< (torso ? "FOUND" : "NOT FOUND (roster names?)")
|
||||
<< " on mech " << (void *)entity << "\n" << std::flush;
|
||||
if (torso != NULL)
|
||||
{
|
||||
AddConnection(
|
||||
@@ -791,6 +797,19 @@ void
|
||||
Radian leftEdge = viewHorizontalRotation - viewHalfHorizontalWidth;
|
||||
Radian rightEdge = viewHorizontalRotation + viewHalfHorizontalWidth;
|
||||
|
||||
// DIAG (issue #1): 1 Hz wedge-heading trace.
|
||||
if (getenv("BT_RADAR_LOG"))
|
||||
{
|
||||
static unsigned long sLast = 0;
|
||||
unsigned long now = GetTickCount();
|
||||
if (now - sLast >= 1000)
|
||||
{
|
||||
sLast = now;
|
||||
DEBUG_STREAM << "[radar-wedge] rot=" << (Scalar)viewHorizontalRotation
|
||||
<< "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
localView.SetColor(staticColor); // (this+0x48 vtbl+0x18)
|
||||
|
||||
Radian edges[2];
|
||||
@@ -1176,20 +1195,40 @@ static Entity *
|
||||
|
||||
//
|
||||
// FUN_0041f98c -- Entity::FindSubObject(name) (locates "Torso").
|
||||
// The binary walks the entity's SUBSYSTEM ROSTER (count @+0x124, array
|
||||
// @+0x128) and returns the first subsystem whose streamed NAME (sub+0xd4 ==
|
||||
// Subsystem::subsystemName) matches case-insensitively (FUN_004d4b58 is a
|
||||
// tolower-strcmp). Gitea issue #1 fix (2026-07-17): this and the feed below
|
||||
// were NULL stubs, so the torso-twist -> radar view-wedge connection was
|
||||
// never created and the wedge sat at heading 0 forever.
|
||||
//
|
||||
static Entity *
|
||||
FindSubObject(Entity * /*entity*/, const char * /*name*/)
|
||||
static Subsystem *
|
||||
FindSubObject(Entity *entity, const char *name)
|
||||
{
|
||||
if (entity == NULL || name == NULL)
|
||||
return NULL;
|
||||
for (int i = 0; i < entity->GetSubsystemCount(); ++i)
|
||||
{
|
||||
Subsystem *sub = entity->GetSubsystem(i);
|
||||
if (sub != NULL && sub->GetName() != NULL
|
||||
&& _stricmp(sub->GetName(), name) == 0)
|
||||
return sub;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// torso+0x1D8 -- the sub-object's horizontal (yaw) rotation feed.
|
||||
// torso+0x1D8 -- the sub-object's horizontal (yaw) rotation feed:
|
||||
// Torso::currentTwist (layout-locked @0x1D8, torso.hpp). Reached via the
|
||||
// torso.cpp complete-type bridge (task #56's BTGetTorsoTwistAddr -- the same
|
||||
// pointer the gyro's external-pitch link uses). Radian is layout-identical
|
||||
// to Scalar ({ Scalar angle }, ANGLE.h), so the reinterpret is exact.
|
||||
//
|
||||
static Radian *
|
||||
GetHorizontalRotation(Entity * /*entity*/)
|
||||
GetHorizontalRotation(Subsystem *torso)
|
||||
{
|
||||
return NULL;
|
||||
extern Scalar *BTGetTorsoTwistAddr(Subsystem *torso);
|
||||
return (Radian *)BTGetTorsoTwistAddr(torso);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2128,23 +2128,25 @@ void
|
||||
//
|
||||
// Look up this mech's colour / badge / patch codes from the table, using
|
||||
// the egg-supplied names carried on the entity (badge=resourceNameA @0x844,
|
||||
// color=resourceNameB @0x848, patch=resourceNameC @0x84c).
|
||||
// color=resourceNameB @0x848, patch=resourceNameC @0x84c). The binary
|
||||
// reads the backing text straight off the ref-counted name objects
|
||||
// (*(char**)(*(int*)(mech+0x848)+8) etc.); the reconstructed Mech now
|
||||
// carries them as real CString members deep-copied from the MakeMessage,
|
||||
// exposed via the paint-name accessors.
|
||||
//
|
||||
// The recovered code read these directly off the BattleTech mech egg
|
||||
// (Mech::vehicleColor / vehicleBadge / vehiclePatch) and Fail()ed on a miss.
|
||||
// TODO(bring-up): the reconstructed Mech carries those names as ref-counted
|
||||
// creation-name objects (resourceNameA/B/C) whose backing string is the
|
||||
// transient MakeMessage buffer -- not safely readable here yet, and the
|
||||
// attribute-index path ("VehicleColor"/...) is not wired, so it returns
|
||||
// garbage that FindNote then deref-crashed on (btl4vid.cpp:808). Follow the
|
||||
// RP analogue (RPL4VID.cpp:1562) which simply tolerates a missing colour/
|
||||
// badge: a NULL egg name or a table miss leaves veh_* == NULL and the
|
||||
// placeholder substitution below drops to default materials. Re-wire the
|
||||
// real egg names (a named Mech accessor) once the Mech layout is mapped.
|
||||
// DEVIATION (documented): on a table miss the binary Fail()ed hard
|
||||
// ("Exiting" @BTL4VID.CPP:0xbeb/0xbf2/0xbf9). We follow the RP analogue
|
||||
// (RPL4VID.cpp:1562) and tolerate it -- a NULL/missing name leaves veh_* ==
|
||||
// NULL and that placeholder expands empty (base materials) -- so a sparse
|
||||
// test egg degrades to the old gray-metal look instead of killing the pod.
|
||||
//
|
||||
const char *egg_color = NULL; // [0x848] resourceNameB (color)
|
||||
const char *egg_badge = NULL; // [0x844] resourceNameA (badge)
|
||||
const char *egg_patch = NULL; // [0x84c] resourceNameC (patch)
|
||||
Mech *mech = (Mech *)entity; // caller dispatched on MechClassID
|
||||
const char *egg_color = mech->GetVehicleColorName(); // [0x848] resourceNameB
|
||||
const char *egg_badge = mech->GetVehicleBadgeName(); // [0x844] resourceNameA
|
||||
const char *egg_patch = mech->GetVehiclePatchName(); // [0x84c] resourceNameC
|
||||
if (egg_color && !*egg_color) egg_color = NULL;
|
||||
if (egg_badge && !*egg_badge) egg_badge = NULL;
|
||||
if (egg_patch && !*egg_patch) egg_patch = NULL;
|
||||
|
||||
const char *veh_color = NULL, *veh_badge = NULL, *veh_patch = NULL;
|
||||
if (egg_color && !veh_tbl->GetEntry("color", egg_color, &veh_color)) // @0051d94e
|
||||
@@ -2166,6 +2168,14 @@ void
|
||||
veh_patch = NULL;
|
||||
}
|
||||
|
||||
DEBUG_STREAM << "[paint] mech egg color='" << (egg_color ? egg_color : "<none>")
|
||||
<< "' badge='" << (egg_badge ? egg_badge : "<none>")
|
||||
<< "' patch='" << (egg_patch ? egg_patch : "<none>")
|
||||
<< "' -> codes color=" << (veh_color ? veh_color : "-")
|
||||
<< " badge=" << (veh_badge ? veh_badge : "-")
|
||||
<< " patch=" << (veh_patch ? veh_patch : "-")
|
||||
<< " serno=" << gSerno << "\n" << std::flush;
|
||||
|
||||
//
|
||||
// Generic substitution list, then expand placeholders per entry.
|
||||
//
|
||||
@@ -2257,6 +2267,7 @@ void
|
||||
void
|
||||
BTL4VideoRenderer::TearDownMaterialSubstitutionList()
|
||||
{
|
||||
dpl_SetMaterialNameCallback(NULL); // FUN_0049664c(0) -- first, as the binary does
|
||||
if (materialSubstitutionList != NULL)
|
||||
{
|
||||
for (NameList::Entry *entry = materialSubstitutionList->GetFirstEntry();
|
||||
@@ -2269,7 +2280,6 @@ void
|
||||
delete materialSubstitutionList;
|
||||
materialSubstitutionList = NULL;
|
||||
}
|
||||
// dpl_SetMaterialNameCallback(NULL);
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
|
||||
@@ -247,17 +247,11 @@ LWord BT_GetSegmentFlags(Mech *owner)
|
||||
return owner ? owner->simulationFlags : 0;
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// Legacy libDPL material-name callback.
|
||||
//---------------------------------------------------------------------------//
|
||||
// The dpl2d_ 2D display-list API (NewDisplayList/Begin/SetColor/Circle/
|
||||
// PushMatrix/PopMatrix/MoveTo/End/Compile) was MOVED OUT of this file and
|
||||
// reimplemented over Direct3D 9 in decomp/reconstructed/dpl2d.cpp (the HUD/
|
||||
// reticle 2D layer). Only the 3D material-name-substitution hook -- which is
|
||||
// part of the world renderer, not the 2D HUD -- remains stubbed here.
|
||||
// TODO(bring-up): wire the material substitution table (FUN_00459eb8).
|
||||
//===========================================================================//
|
||||
void dpl_SetMaterialNameCallback(char *(*)(char *)) {}
|
||||
// dpl_SetMaterialNameCallback is now REAL (per-pilot mech paint): it lives in
|
||||
// engine/MUNGA_L4/L4VIDEO.cpp next to substituteMaterial, and the BGF loader
|
||||
// (bgfload.cpp MaterialResolver::resolve) applies the installed callback to
|
||||
// every material name -- the port analogue of the dpl board rewriting names
|
||||
// at dpl_LoadObject time (FUN_0049664c / FUN_00459eb8).
|
||||
|
||||
//===========================================================================//
|
||||
// BTL4VideoRenderer helper methods whose bodies were not reconstructed.
|
||||
|
||||
@@ -1123,3 +1123,31 @@ Subsystem *CreateCondenserSubsystem(Mech *owner, int id, void *seg)
|
||||
|
||||
// CreateHeatSinkBankSubsystem (0xBBE) now lives in heatfamily_reslice.cpp -- it
|
||||
// builds the real AggregateHeatSink (which needs that TU's class definition).
|
||||
|
||||
//===========================================================================//
|
||||
// Gauge cooling-loop bridge (task #7 regression fix, 2026-07-17).
|
||||
//
|
||||
// The dev-gauge CoolingLoopConnection (btl4gau2.cpp @004c31a0) reached the
|
||||
// cooling master with a RAW attribute index (GetAttributePointer(3)) and then
|
||||
// raw-read master+0x1d4. The binary heat attribute table (@0x50e438..0x50e4c8)
|
||||
// shows id 3 == "HeatSink" == the linkedSinks plug, and +0x1d4 on the resolved
|
||||
// master is Condenser::condenserNumber (the loop number that selects the
|
||||
// image-strip frame). The numeric-id read crashed when the AUDIO_FIDELITY rows
|
||||
// (ReportLeak et al., cc2b109) grew the chained tables: index 3 landed on a
|
||||
// scalar, the resolve walked garbage, and the gauge background pass AV'd
|
||||
// (BT_DEV_GAUGES=1, the pod launch flag). House rule (gotcha 8): named members
|
||||
// via a complete-type TU -- never a raw numeric attribute index.
|
||||
//===========================================================================//
|
||||
int BTCoolingLoopFrame(void *subsystem_v)
|
||||
{
|
||||
Subsystem *sub = (Subsystem *)subsystem_v;
|
||||
if (sub == 0 || !sub->IsDerivedFrom(*HeatSink::GetClassDerivations()))
|
||||
return 0;
|
||||
HeatSink *sink = (HeatSink *)sub;
|
||||
if (sink->coolantAvailable != 1) // the binary's src+0x134 gate
|
||||
return 0;
|
||||
Subsystem *master = sink->linkedSinks.Resolve(); // attr id 3 "HeatSink" + FUN_00417ab4
|
||||
if (master == 0 || !master->IsDerivedFrom(*Condenser::GetClassDerivations()))
|
||||
return 0;
|
||||
return ((Condenser *)master)->condenserNumber; // master+0x1d4 (loop/frame number)
|
||||
}
|
||||
|
||||
+15
-10
@@ -888,10 +888,11 @@ Mech::Mech(
|
||||
|
||||
//
|
||||
// Three ref-counted creation-name holders (badge/color/insignia).
|
||||
// CString members now: their default ctors run in the member-init phase and
|
||||
// allocate the empty CStringRepresentation + addref -- exactly the binary's
|
||||
// new(0x10)/FUN_004023f4/refs++ triple @part_012.c:9841-9871. Nothing to
|
||||
// do here.
|
||||
//
|
||||
resourceNameA = NewRefCounted(); // Wword(0x211)
|
||||
resourceNameB = NewRefCounted(); // Wword(0x212)
|
||||
resourceNameC = NewRefCounted(); // Wword(0x213)
|
||||
|
||||
instanceFlags |= 0x10; // this[10] |= 0x10
|
||||
// Zero the optional-subsystem back-pointers up front (binary @9873-9875).
|
||||
@@ -1710,11 +1711,14 @@ Mech::Mech(
|
||||
}
|
||||
|
||||
//
|
||||
// Bind the three creation-name strings from the MakeMessage.
|
||||
// Bind the three creation-name strings from the MakeMessage. CString::
|
||||
// operator=(const char*) releases the empty ctor rep and allocates a fresh
|
||||
// rep COPYING the message's inline char[20] -- byte-for-byte FUN_00402a98
|
||||
// (part_012.c:10427-10441); the names survive the transient message.
|
||||
//
|
||||
resourceNameA = creation_message->resourceNameA; // param_2+0x7c -> Wword(0x211)
|
||||
resourceNameB = creation_message->resourceNameB; // param_2+0x90 -> Wword(0x212)
|
||||
resourceNameC = creation_message->resourceNameC; // param_2+0xa4 -> Wword(0x213)
|
||||
resourceNameA = creation_message->resourceNameA; // badge param_2+0x7c -> Wword(0x211)
|
||||
resourceNameB = creation_message->resourceNameB; // color param_2+0x90 -> Wword(0x212)
|
||||
resourceNameC = creation_message->resourceNameC; // patch param_2+0xa4 -> Wword(0x213)
|
||||
|
||||
//
|
||||
// Register this Mech in the global "Mechs" directory.
|
||||
@@ -1810,9 +1814,10 @@ Mech::~Mech()
|
||||
delete (MechDeathHandler *)deathHandler; // real class -> plain delete
|
||||
}
|
||||
|
||||
ReleaseRefCounted(resourceNameC); // Wword(0x213)
|
||||
ReleaseRefCounted(resourceNameB); // Wword(0x212)
|
||||
ReleaseRefCounted(resourceNameA); // Wword(0x211)
|
||||
// resourceNameC/B/A: the binary's inline refcount-decrement/delete triple
|
||||
// (part_012.c:11388-11403) is the CString member dtors -- implicit at the
|
||||
// closing brace now that the members are real CStrings (dtor-epilogue rule:
|
||||
// do NOT also write them out here).
|
||||
|
||||
//
|
||||
// ⚠ RECONSTRUCTION RULE (root cause of the P5 death-row crash AND the app-exit
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
# include <alarm.hpp> // GaugeAlarm
|
||||
#endif
|
||||
#include <tool.hpp> // PlatformTool
|
||||
#include <cstr.hpp> // CString (the vehicle badge/color/patch creation names)
|
||||
#include <affnmtrx.hpp> // AffineMatrix
|
||||
#include <average.hpp> // AverageOf<T>
|
||||
#include <scalar.hpp> // Scalar
|
||||
@@ -764,10 +765,27 @@ protected:
|
||||
// eye-slew system (HUD freeAimSlew / gyro eye chain) writes it.
|
||||
EulerAngles eyepointRotation;
|
||||
|
||||
// Three ref-counted creation-name objects (badge/color/insignia).
|
||||
void *resourceNameA; // @0x844 this[0x211]
|
||||
void *resourceNameB; // @0x848 this[0x212]
|
||||
void *resourceNameC; // @0x84c this[0x213]
|
||||
// Three ref-counted creation-name strings (badge/color/insignia).
|
||||
// CString members: each slot is a CStringRepresentation* -- the exact
|
||||
// 16-byte object {size,len,text@+8,refs@+0xc} the binary's Mech ctor
|
||||
// allocates (part_012.c:9841-9871) and the make handler re-binds from
|
||||
// the MakeMessage via CString::operator=(const char*) == FUN_00402a98
|
||||
// (part_012.c:10427-10441). SetupMaterialSubstitutionList reads the
|
||||
// backing text (*(char**)(this+0x844..84c)+8) for the vehicle-table
|
||||
// color/badge/patch lookups.
|
||||
CString resourceNameA; // @0x844 this[0x211] (vehicle badge)
|
||||
CString resourceNameB; // @0x848 this[0x212] (vehicle color)
|
||||
CString resourceNameC; // @0x84c this[0x213] (vehicle patch)
|
||||
|
||||
public:
|
||||
// Paint-name accessors (SetupMaterialSubstitutionList). CString's
|
||||
// operator char*() returns the rep's stringText -- NULL for a
|
||||
// default-constructed (never-bound) name; callers must tolerate NULL.
|
||||
const char *GetVehicleBadgeName() const { return (const char *)resourceNameA; }
|
||||
const char *GetVehicleColorName() const { return (const char *)resourceNameB; }
|
||||
const char *GetVehiclePatchName() const { return (const char *)resourceNameC; }
|
||||
|
||||
protected:
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Reconstruction additions -- members & methods whose bodies live in the
|
||||
|
||||
Reference in New Issue
Block a user