4c54f7ef0ccdce702d04f7cd287acbce7ff38607
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
dd27238ceb |
Gauges: the CLOSEOUT wave -- radar pips, Myomers dedup, attribute parity, ledger sweep (tasks #14-#17)
TASK #17 -- AUTHENTIC RADAR SYMBOLOGY (the cross-blip stand-in retired). The recon proved the 'missing pip raster set' never existed as BT game code: pips are the ENGINE's L4GaugeImage vector-stroke system (T0 source in tree, L4GAUIMA.cpp == FUN_0046f0c0 line-for-line), the 'pip table' is L4Warehouse:: gaugeImageBin keyed by Entity::resourceID, and BTL4.RES ships 110 type-0x12 shapes (every mech/vehicle/building/tree). The six btl4rdr stubs are wired to the real facilities: contacts draw their authentic model silhouettes with LOD selection, the target gets the binary's 4px-inflated box highlight, and player-name labels resolve via Mission::GetSmallNameBitmap (the prebuilt 64x16 egg rasters keyed by the player's bitmapindex). DECODED: the invented 'VideoObject' was Entity::owningPlayer all along (+0x190; nameID = playerBitmapIndex@0x1E0, target = BTPlayer::objectiveMech@0x284 -- new bridge BTPlayerObjectiveMechOf); the 'LabelledEntity' class is Landmark (cultural.h; label path dormant -- no landmark content ships, no runtime landmarkID writer). The L4GREND BT_DEV_GAUGES warehouse guard is removed (its AV had a different culprit, below); resource type 18 corrected to GaugeImageStream in decomp-reference (was mislabeled 'ModelList'). TASK #14 -- the Myomers ODR duplicate ELIMINATED: the powersub.cpp/hpp 'Myomers' (classID 0xBC3 -- actually Sensor) is retired whole; it duplicated ?DefaultData@Myomers@@ against the real class (dumpbin-verified) and /FORCE picked the winner by link order. The real Myomers (0xBC6) now chains PoweredSubsystem's handler set (ids 4-8) and publishes its SEVEN binary attributes (@00511588: SpeedEffect/Current/Recommended/Min/MaxSeekVoltage- Index/SeekVoltage/OutputVoltage) -- the old empty unchained index starved the Myomer engineering panel of every resolve. TASK #16 -- attribute parity: MechWeapon publishes the FULL binary table @0x511890 (11 entries; ids renumbered to binary truth -- the port aliases had squatted the binary's DistanceToTarget/TargetWithinRange ids; the streamed TriggerState 0x13 binding unchanged). Binary names resolved two TODO members: pipState -> estimatedReadyTime (attr 0x1A), and the EXT-model flag is the binary's RearFiring (0x1B). ThermalSight LightState published. HUD (offset conflicts) + missile-side tables (id encoding suspect) documented for a re-dump instead of publishing blind. THE CRASH THIS EXPOSED [T2, cdb-verified]: gotcha #11's dense-table gap is a LATENT AV, not a guaranteed one -- the old table's 0x0D..0x12 gap (task #5) survived on heap luck; the renumber reshuffled allocations and Find() AV'd on a garbage entryName in WeaponCluster's PercentDone resolve. Fixed with five named PAD entries (the mech.cpp attrPad idiom) + a static_assert locking the pad base to PoweredSubsystem::NextAttributeID. Gotcha #11 amended with the proof. TASK #15 -- stale-ledger sweep: GAUGE_COMPOSITE ('composite not yet built', Reservoir shadow, PlayerStatus/vehicleSubSystems 'remaining', valve-dormant- until-0xBD3, sensor guard, the superseded 'Heat MFD near-static' reframe -- all banner-corrected), gauges-hud frontmatter, L4VB16 + powersub comments. Verified live: 50/50 config attribute bindings resolve, 0 NULLs, 0 parse skips, mech spawns and simulates 31/31 subsystems, no cross-blip fallbacks, the pip cache fills through entity registration without the old guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fb3ed38fa7 |
map: fix contact-blip projection -- enemy now plots inside the radar FOV wedge
The previous commit found+drew contacts but they landed off-radar. Debugged with BT_MAP_LOG entity/blip tracing: the reads were CORRECT all along -- the enemy mech (cls 0xBB9, own=0) sits at dsq=14400 (exactly 120u) from the player (own=1, dsq=0), and the grid also carries ~311 cls-0x5E vehicle props per cycle (the map's scattered vehicles) which the 0xBB9 blip filter correctly skips. The bug was the PROJECTION: worldToView is a camera (view->world) matrix and Point3D::Multiply bakes a scale that is wrong for a point transform -- a 120u contact at ppm=0.366 projected to |167px| instead of |44px| (~3.8x), and with a spurious rotation. (The earlier "377-828m" reading was this wrong scale amplifying the varying player-drift distance, not a distance error.) Fixed by projecting delta = (entity - viewpoint) DIRECTLY: rotate by -heading (from the orientation quaternion's yaw, top-down heading-up) and scale by pixelsPerMeter. Now |screen| == |delta|*ppm exactly (blip @(0,44) for the 120u enemy), on-radar inside the view wedge. Blip is a cross + box so it reads as a distinct contact marker. Verified live (BT_DEV_GAUGES): the enemy contact renders as a box marker INSIDE the FOV wedge (render-confirmed via a 4x radar crop); combat un-regressed (TARGET DESTROYED after 8 hits), heap-clean under BT_HEAPCHECK. BT_MAP_LOG traces blip coords + scale + delta. Remaining map polish: blip-vs-wedge rotation convention (the enemy reads ahead/in-wedge, which is correct); the authentic pip symbol/name infrastructure (still stubbed); SetTargetRange (radar zoom, 1km default). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0bceb9a638 |
gauges/map: populate the radar entity grid + draw mech contact blips
Root-caused why the radar showed 0 contacts: the gauge renderer's moving/static entity grids (queried by GetMoving/StaticEntitiesWithinBounds) were NEVER populated -- the 1995 game fed them from ExecuteImplementation's InterestingEntity iterator, which the WinTesla port dropped. Fix (engine): add GaugeRenderer::RebuildEntityGrid() -- Clear + repopulate movingEntities from the world's DynamicMaster + DynamicReplicant entities (the vehicles/mechs; AllEntity would flood the grid with ~300 props/effects/terrain). Called from the map's Execute phase 0 (under the gauge's guarded Execute, so a fault disables only the map). Verified: contacts are now found, combat un- regressed (TARGET DESTROYED), heap-clean under BT_HEAPCHECK. Fix (map): DrawMoving now draws a cross blip for mech-class contacts (0xBB9) when the authentic pip raster set is absent (the BT pip table is stubbed in this engine build). KEY PROJECTION FIX: worldToView is a camera (view->world) matrix and Point3D::Multiply applies only rotation+scale (NOT translation), so projecting the absolute entity position left the viewpoint un-subtracted and the blip landed off-screen (verified: blip @(580,-1154)). Project the RELATIVE position (delta = entity - viewpoint, already computed for the range check) instead -> the blip projects viewpoint-relative + heading-rotated correctly (@(164,221)). radarRange default 500->1000 (1km zoom) so contacts within ~500m show on the radar (SetTargetRange, the real player zoom, is still stubbed). BT_MAP_LOG traces the blip coords. STATE: the grid population + viewpoint-relative blip projection are correct and combat-safe. A reliable on-screen contact demo still needs the display-scale vs spawn-distance reconciliation (the BT_SPAWN_ENEMY dummy sits at a varying, large distance -- 377-828m -- beyond the display radius across runs) + the authentic pip symbol/name/video-object infrastructure (still stubbed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
48ed3a8b73 |
gauges: register + wire the map (radar) -- the tactical display renders
MapDisplay (the radar/threat-map gauge, btl4rdr.cpp) was fully reconstructed (ctor + Execute + DrawViewWedge/DrawStatic/DrawMoving/DrawNames) but never registered and its data attributes never published. Wire it up end-to-end: DATA (the attributes the map binds): * Mech table extended to the full 0x15..0x38 (was the 0x21 prefix): add RadarRange@0x2f -> radarRange, RadarLinearPosition@0x30 -> radarLinearPosition (Point3D* -> &localOrigin.linearPosition), RadarAngularPosition@0x31 -> radarAngularPosition (Quaternion* -> &localOrigin.angularPosition), DuckState @0x37 -> duckState; the rest bind the shared attrPad (kept dense so Find can't strcmp a gap). New members set in the Mech ctor; radarRange defaults to 500 (SetTargetRange is still stubbed) so nearby contacts are on-scale. The map's position/angle attributes resolve to Point3D**/Quaternion** (AttributePointer is int Simulation::*, so the ATTRIBUTE_ENTRY reinterpret binds pointer members fine -- verified live: posPtr/anglePtr resolve). * Sensor (named "Avionics") publishes RadarPercent/SelfTest/BadVoltage: define Sensor::AttributePointers[] + build the previously-empty Sensor::AttributeIndex chained to PoweredSubsystem::GetAttributeIndex() (= HeatSink's dense index). WIDGET (the registration glue -- NOT in the assert-anchored decomp, so reconstructed from the config + ctor): * MapDisplay::methodDescription (config "map") + MapDisplay::Make. The offset_position keyword "center"/"bottom" is typed as a STRING and converted in Make, avoiding a ModeManager named-constant registration. Registered in BTL4MethodDescription[] (btl4grnd.cpp now includes btl4rdr.hpp). Three crash fixes exposed once the map ran (each a pre-existing stub the map is the first consumer of): * Sensor radarPercent went hugely negative (RadarBaseline - HeatMasterEnergy(), where the inherited heatEnergy is un-normalized in the not-byte-exact heat-leaf branch) -> the radar reported non-operational. Guard: treat an out-of-[0,1] heat term as no penalty (marked bring-up; real overheat penalty needs the heat normalization). * ResolveOperatorEntity was a stub returning NULL -> EntityPosition(NULL) crashed CalculateBounds. Fixed to the renderer linked-entity + application viewpoint fallback (same as HeadingPointer). * MapName::ExtractFromEntity didn't guard a NULL entity (the binary's Verify(False) iterator-mismatch path compiles out at DEBUG_LEVEL 0) -> EntityPosition(NULL). Guarded. Verified live (BT_DEV_GAUGES): the radar now draws the view wedge (the mech's 180-deg FOV cone) and completes the full wedge/static/moving/names cycle with operating=1, cap=1, scale=500, position/angle resolved, 0 crashes. Combat un- regressed (TARGET DESTROYED after 8 hits), heap-clean under BT_HEAPCHECK. Permanent gated diagnostic: BT_MAP_LOG traces the phase/draw pipeline. Contacts show 0 (the spatial-query entity classification is a follow-up); DuckState now resolves for the duck button too. 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>
|