fd055281a8a24be6541b3d4a2486afa37042e325
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7fc4acb89f |
gauge-complete P4g: MessageBoard reconstructed + registered -> LAST parse-skip cleared (0 unregistered gauges)
The "messageBoard" cockpit primitive (the secondary-MFD comm/status message ticker, L4GAUGE.CFG:4913) was PROSE-ONLY (no bodies/methodDescription) -> parse-skipped. This was the last unregistered gauge primitive. Reconstructed byte-verified (Make @4cb678, ctor @4cb704, dtor @4cb788, BecameActive @4cb7fc, Execute @4cb82c; vtable 0051bddc; sizeof 0xA4). THREE header mislabels fixed (decode + adversarial verify): int enabled -> Entity* trackedMech (a pointer deref'd at +0x190, set by SetSource not SetEnable); previousMessageId/previousNameId were SWAPPED (BecameActive writes 0x9c=-1/0xa0=-2; Execute compares messageId vs 0xa0, name vs 0x9c). Execute blits the strip cell (id -> (id&3)<<7,(id>>2)<<5) + sender name; methodDescription = 4 params (rate, mode, btsmsgs.pcx, color). Registered in BTL4MethodDescription[]. DEFERRED / EMPTY by design (authentic for bring-up): the source is never bound (SetSource has no recovered caller) AND the per-player status queue (StatusMessagePool, btstubs.cpp:62) is a NULL stub -> no status messages exist -> Execute early-returns on the NULL source (safe no-op == empty board). Data read via a BTResolveMessageBoard bridge in btplayer.cpp (a real /FORCE-safe stub returning False; the raw mech+0x190/+0x1dc reads stay in the complete-BTPlayer TU, dodging the databinding trap). The name-cell path is a marked structural simplification to restore when the feed lands. VERIFIED: the gauge parse-skip list is now EMPTY ([gskip]=0 -- every config gauge primitive registered + built); BTResolveMessageBoard resolves (no /FORCE __ImageBase AV); combat TARGET DESTROYED, no crash; gauge composite renders identically (300/77, clusters, radar un-regressed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4a4ec6855c |
gauge-complete P4e: SectorDisplay reconstructed + registered -> radar SECTOR X/Z read-out LIVE
The "sectorDisplay" cockpit primitive (Secondary overlay, the radar SECTOR X/Z coordinate read-out) was PROSE-ONLY in btl4gau3 (placeholder Make, no ctor/ methodDescription/registration) -> the config line was parse-SKIPPED and never built. Reconstructed byte-verified from the disassembly (ctor @4c9e10, Execute @4ca07c, methodDescription PE-parse): SectorDisplay : GraphicGauge, sizeof 0xC4. Its Execute reads the linked mech's world position and shows two 100-unit sector numerics: numericA = Round(-localOrigin.z * 0.01) + 500 numericB = Round( localOrigin.x * 0.01) + 500 (rounding = round-to-nearest == FUN_004dcd94, corrected from the reviewer's wrong "truncate" claim; 0.01 const PE-verified; -Z/+X axis + fchs confirmed from asm). Overridden slots: LinkToEntity(9) caches the subject, BecameActive(3, non-inactivating), Execute(16) -> satisfies the container-Execute rule. Layout overflow-locked (static_assert sizeof<=0xC4). Make/ctor/dtor mirror the registered PilotList sibling; the config image name is copied (nameCopy) since Execute reads it per-frame. Registered in BTL4MethodDescription[]. VERIFIED LIVE (BT_SECTOR_LOG): Make port=1 pos=(125,579) image=helv15.pcc gridCached=1; Execute -Z=960.4 X=361.6 -> sectorA=510 sectorB=504 (Round(9.6)+500=510, Round(3.6)+500=504 -- authentic 100-unit sectors from live mech position). Gauge composite renders full, no crash. The skip list is now exactly the two remaining widgets (prepEngr x12, messageBoard). Also: a permanent BT_GAUGE_SKIP_LOG diagnostic (GAUGREND.cpp, gated) that logs each unregistered gauge primitive the dev-parse skips -- the tool that pinned this down (earlier "not built" runs were killed before the lazy gauge-renderer init). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
34aaa7dda4 |
gauge wave P3: wire the combat scoring feed (KILLS tracks combat)
The Comm roster + score gauges read 0 for 4 independent reasons (none was handler logic -- the BTPlayer ScoreMessage/VehicleDead/ScoreInflicted handlers were already reconstructed). Mapped by the scoring-feed-decode workflow; fixed all 4: - NO PRODUCER: combat only dispatched Entity::TakeDamageMessage, never a scoring message. Added producers (btplayer.cpp bridges, called from mech4.cpp): per-hit ScoreInflicted at the beam (:2171) + projectile (:831) damage dispatch -> SCORE; KillScore at the TARGET-DESTROYED edge (:2198) -> KILLS. senderMechID = the VICTIM (so the local player is credited via the !=our-mech branch, not the suicide-negate branch); dispatched to application->GetMissionPlayer(). - CROSS-FAMILY MECH OFFSETS: MECH_OWNING_PLAYER/TONNAGE/DAMAGE_BIAS read raw binary offsets (garbage in our 0x638 Mech). MECH_OWNING_PLAYER -> Entity::GetPlayerLink() (NULL for the ownerless dummy); tonnage/bias stubbed 1.0/0.0 (bring-up). - NULL scenarioRole: the BTMission role registry has no WinTesla analog, so every award path NULL-deref'd. CalcInflictedScore returns the neutral (damage+bias) when scenarioRole==0; DamageReceived guarded. - NULL-owner dummy: the KillScore sender-owner increment + StatusMessage now guard GetPlayerLink() (the dummy has none) so a solo kill credits only the local player. - DATABINDING: the PilotList read KILLS/DEATHS at raw offsets (pilot+0x27c/+0x200) that don't match our compiled layout -> silent 0. Repointed to the compiled members via bridges (BTPilotKills/BTPilotDeaths -> BTPlayer::GetKillCount/GetDeaths). ⚠ ROOT-CAUSE of the empty scoreboard: the PilotList SELECT-TARGET highlight did a raw-offset deref (*(local+0x284) objectiveMech, then *(tgt+0x190)) that AV'd on our layout -- caught silently by the SEH GuardedExecute, aborting Execute BEFORE the KILLS/DEATHS draw. Replaced with BTPilotIsSelected (accessors + GetPlayerLink). - SCORE persistence: PlayerSimulation's 10s flush zeroed currentScore (the binary's console delta); now only flushes/zeros when a console host exists (solo keeps the running score for the gauge + CalcRanking). Verified DBASE+dev gauges (BT_SCORE_LOG): SCORE climbs +5.83/hit -> 164; on the kill KILLS 0->1, and the Comm roster RENDERS KILLS=1 (screenshot). No crash. Follow-ups (noted): DEATHS=1 is a pre-existing spawn/respawn-handshake artifact (not from the producers -- the dummy's GetPlayerLink is NULL so no VehicleDead is posted; confirmed killCount=1 not 2); RANK=-1 is CalcRanking's non-scoring mark (Plasma serial surface, invisible on dev); MP VehicleDead/tonnage/role registry remain per the plan. Diagnostics kept: BT_SCORE_LOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0060a3e1ca |
gauge wave P2b: build the Comm KILLS/DEATHS pilot roster (PilotList)
PilotList (keyword "pilotList") was PROSE-ONLY, so the Comm surface showed only
the baked btcomm.pcx labels. Reconstructed all 7 functions from part_014.c:
3156-3434 (Make/ctor/dtor/BecameActive/TestInstance/Execute/DrawMechIcon), with
the 8x(x,y,layoutMode) layout table DAT_0051af88 PE-parsed exact from BTL4OPT.EXE.
Draws ONE roster slot/frame (round-robin) from the viewpoint mech's cockpit-mapper
pilot roster.
- Roster FEED: new BTResolveRosterPilot(slot) bridge in mechmppr.cpp (a complete-Mech
TU) resolves the viewpoint mech's ControlsMapper (subsystemArray[0]) -> GetPilot;
btl4gau3.cpp reads the returned pilot at raw BTPlayer offsets. The mapper's
FillPilotArray already fills the roster (pilotArray[0]=GetMissionPlayer,
[1..]=FindGroup("Players")).
- KILLS = killCount@0x27c (real; ScoreMessageHandler increments it in combat).
- DEATHS: the binary reads pad_0x280 which has NO writer anywhere (a shipped dead
field -> perpetual 0). FIX per "if it doesn't work, fix it": read the real
deaths counter Player::deathCount@0x200 (VehicleDeadMessageHandler increments it)
so DEATHS is meaningful in MP.
- DrawMechIcon: App+0xC8 name-bitmap cache is unwired (same deferral PlayerStatus
uses) -> LookupPlayerNameBitmap returns NULL -> the tinted name box (never an AV).
Dropped the bogus x,y from the header ctor (positions come from DAT_0051af88).
/FORCE-safe (all vtable slots real; link log clean, no unresolved PilotList/
BTResolveRosterPilot). Verified DBASE+dev gauges: the Comm surface now renders the
live local pilot row (KILLS 0 DEATHS 0 + name box; 0/0 authentic in solo -- the
combat scoring feed that moves them is Phase 3), combat un-regressed (DESTROYED),
0 crashes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
b6918632fc |
gauges: reconstruct + register PlayerStatus (mission-review scoreboard)
PlayerStatus (btl4gau3) had only a BUGGY Make defined (it read raw DAT_ pools, passed (int)entity/(int)gauge_renderer as x/y, and DROPPED the graphics_port_number param -- the header ctor was one int short); the ctor/Execute/dtor/BecameActive and the PlayerStatusMappingGroup + CreateMutantPixelmap8 helpers were comment-only stubs. Reconstructed the whole family (mapped by the playerstatus-decomp-map workflow, 6 agents): * methodDescription (8 params: rate,modeMask,integer player#,string font,4x color) + rewired Make reading parameterList[] with the port/x/y bug fixed. * ctor (@004cb1a8): GraphicGauge base (owner folded to 0), store colours+port, SetOrigin, playerIndex=player_number-1, build a score NumericDisplay (fmt 2 = signedBlankedZeros). Fixed the header layout: nameImage is a BitMap* (not a Pixmap), and two missing members (dirty@0x2F, previousStatus@0x30). * dtor / BecameActive / TestInstance. * PlayerStatusMappingGroup (@004c9bd0): 28 ColorMapperArmor zone tints over the mech-outline schematic, one per dz_* damage zone (mech->GetDamageZoneIndex), using the EXISTING 10-arg ColorMapperArmor ctor (per the workflow's correction -- changing it would break cmArmor); added ColorMapper/Armor::SetColor (FUN_004c3c38, stores @0x6C). * Execute (@004cb358): resolve the player (WinTesla-clean via GetMissionPlayer for the local player, instead of the binary's raw App+0x24 "Players"-node dictionary walk), then draw the score + name box + alive/dead status box; null-guarded. KEY FINDING: PlayerStatus lives in the config's `cameraInit` block (the MISSION- REVIEW / spectator camera cockpit), NOT `MechInit` -- so it is NOT part of the mech cockpit and does not build during normal mech play (which is why the mech test shows it un-regressed and its Execute never runs). It is the post-mission scoreboard (all 8 players' name/score/mech-status), rendered by the BTCameraDirector game model. Registered in BTL4MethodDescription[] so `cameraInit` builds it instead of parse-skipping. STATE: complete reconstruction; compiles, links (no new /FORCE unresolved), and the mech cockpit is un-regressed (TARGET DESTROYED, 0 crashes). NOT runtime-verified -- cameraInit is only built by the mission-review camera model, which a normal `vehicle=<mech>` egg does not trigger. BRING-UP STUB (marked): CreateMutantPixelmap8 returns NULL (the mech-outline recolor needs the DynamicMemoryStream read API + Pixmap pixel-copy mapped) -> the score/name-box/status-box render but not the recoloured mech schematic. BT_PS_LOG traces the resolve. 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>
|