efa7aeb6833ee0321b833a8801930bbc77832fec
12
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
29035028fd |
Cross-pod beams: replicate emitter discharge via subsystem update records
User report: lasers only visible on the window firing them. The peer's replicant emitters never learned the master fired. THE AUTHENTIC PIPE (decomp-verified): - FUN_0041c350 (the "beam keepalive" ServiceDischarge/ContinueDischarge call) does TWO things: queue the LOCAL deferred beam-effect callback (@0x4bac0c) on the app+0x34 manager -- our per-weapon render walk already plays that role -- and set the subsystem DIRTY bit, which maps to the 2007 engine's updateModel / ForceUpdate(). - Replication rides SUBSYSTEM UPDATE RECORDS inside the mech's update message: the roster walk already hands the entity's stream to every subsystem's PerformAndWatch; Simulation::WriteSimulationUpdate serializes each requested updateModel bit; Entity::UpdateMessageHandler routes received records by subsystemID to the subsystem's ReadUpdateRecord. All engine machinery -- the missing pieces were the Emitter's serialize/apply pair + the triggers. CORRECTIONS to the dormant task-33-era transcriptions (never exercised -- nothing ever set updateModel -- so the latent misreads never surfaced): - The weapon-family VTABLE SLOT MAP was swapped: slot 6 = ReadUpdateRecord, 7 = WriteUpdateRecord, 9 = TakeDamage (evidence: Mech hierarchy symmetry + body semantics; @004ba568 resolves an EntityID at rec+0x30 through the entity index -- record semantics, not Damage). Renamed across MechWeapon / Emitter / ProjectileWeapon; the real Emitter::TakeDamage @004bafc8 is undecoded (inherits MechWeapon for now). - Emitter/MechWeapon Write: `*record = 0x38/0x18` is the record LENGTH, not recordID; rec+0x30 is the TARGET's EntityID (GetEntityID()), not a colour -- the old `CopyColor(targetEntity+0x184)` was also a databinding trap. - OVERRIDE-SIGNATURE TRAP: the decls used each class's own shadowing UpdateRecord typedef as the param type, silently NOT overriding the engine virtual (the base ran instead; nothing would ever have serialized). Base-typed params (Simulation__UpdateRecord*), casts inside. - Emitter::ReadUpdateRecord reconstructed (@004ba568): target EntityID resolve (drop unknown non-null targets), MechWeapon alarm apply chain, beam fields. - ServiceDischarge/ContinueDischarge: ForceUpdate() per keepalive tick + one final record at beam end (turns the peer's beam off). - Mech::DrawWeaponBeams extracted from the player-only drive block so the walk runs for REPLICANTS (+ per-mech gun-port cache -- the process-wide statics would have served the player's segment pointers as the replicant's muzzles). VERIFIED 2-node: A fires 57 volleys -> 225 emitter records -> B applies all 225 -> B draws 414 beams (PPC blue / laser red, from A's replicant's own gun ports). Solo un-regressed (150 beams, kill chain, no crash). Also preserved: the full Mech::WriteUpdateRecord @0x4a0c2c recovery (reference/decomp/mech_writeupdate_004a0c2c.disasm.txt) with all 9 record types decoded (pose/alarm/leg-state+heat with the body-channel write-through re-sync, knockdown, death, impact, movementMode) -- transcription deferred; it replicates remote knockdown/death/heat and was not needed for beams. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d78bde066d |
Authentic target acquisition LIVE: reticle slew + pick-ray lock + aimed zone damage (task #36)
The engine Reticle model (MUNGA/RETICLE.h [T0]) reconstructed end to end: - Mech::targetReticle is a real Reticle member bound to the TargetReticle attribute (0x1d), per the RP VTV analog (VTV.h targetReticle). - Crosshair slew: mouse -> client rect -> reticle coords (the pod stick free-aim channel's dev-box stand-in); BT_AIM="x y" pins it headless. LMB fires lasers / RMB missiles (alongside SPACE/CTRL). - Pick ray: the ACTIVE eye publishes pos + LookAtRH basis (BTSetAimCamera, L4VIDRND view-write site) + the render loop publishes proj._22; BTGetAimRay builds the world ray, Mech::PickRayHit slab-tests it against the collision template's ExtentBox via the engine's BoundingBox::HitBy (local frame; clips the Line at entry) -> world hull point. - Designation: the mech under the crosshair designates (sticky; re-hover refreshes; cleared when the target leaves the roster at burial); the entity target slots 0x37c/0x388/0x38c feed the whole weapon path. - Aimed fire: while HOT the impact point is the PICKED hull point -> the STEP-6 cylinder lookup resolves the zone under the crosshair (verified: center-aim -> head-band zone 13 dominant). Off-crosshair the sticky designation converges on center mass. - HUD: the aim group draws at the slewed position ([0x9a] translate, contained by push/pop); the designator ring tracks the target's projected point (subB9 hot / subB8 designated, BTProjectToReticle); edge arrows when off-screen/behind. - AUTHENTIC gating: no fire arc exists in the binary (FireWeapon fires whenever HasActiveTarget, part_013.c:7758) -> BT_FIRE_ARC is now an explicit OPT-IN presentation clamp; the hardwired gEnemyMech lock and the projectile path's gEnemyMech fallback are removed. - Fixed en route: every renderable rebuild stomped mCamera back to the chase eye (start-inside silently lost the cockpit camera; the aim feed exposed it). BTL4VideoRenderer::mViewInside persists the chosen view. Verified headless: BT_AIM="0 0" -> HOT lock, pick hits the hull face at exact range, aimed zones resolve; BT_AIM="0.8 0.3" -> no lock, zero damage, zero missile launches; kill chain completes to wreck + smoke. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
48b17750e5 |
HUD reticle + weapon pips LIVE: dpl2d 2D display-list port (task #35)
- dpl2d API fully recovered from the binary recorders (@487f34-488630): opcode model (points/lines/polyline/circle/color/width/matrix/push-pop), CallList = INLINE include (state persists to caller), centered coordinate frame (unit = half viewport height). game/reconstructed/dpl2d.cpp rework. - BTReticleRenderable ctor @004cc40c transcribed with the authentic calibration (originX .35, originY .25, scaleY .5, 0..1200m right range ladder, bottom heading tape, FUN_004cd938 tick ladders, lock rings, turn arrows); range caret slides from the live target range fed by the mech4 targeting step (BTSetHudTargetRange). - Weapon pips: the binary gate is IsDerivedFrom(0x511830 = MechWeapon::ClassDerivations) [T1: part_014.c:5386 hard-aborts on missing weapon attrs; part_012 counts + roster ORs capabilityFlags@+0x334] so ALL 7 BLH weapons register (3 lasers + 2 PPCs + 2 MissileLaunchers). Pip A (lit, authored PipColor) on TargetWithinRange, else dark ring B. - AddWeapon @004cdac0 store map corrected to the verified order (part_014.c:4827-4837); both state attrs are literally named "SimulationState" (strings @51d526/51d577) -> weapon simulationState. - Mech roster this[0x1ef] renamed poweredSubsystems -> weaponRoster (0x511830 is MechWeapon, not PoweredSubsystem=0x50f4bc); derivation-tag table added to context/decomp-reference.md. - Draw hook BTDrawReticle after the 3D scene, cockpit view only. Binary Execute @004cdcf0 is an un-exported gap -> Draw dynamics [T3], tracked in context/open-questions.md with the blx_cop canopy + PNAME pip meshes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
56f02b2176 |
Cockpit view live: V toggle, camera gate, real EyepointRotation, inside skeleton
- V toggles the authentic cockpit eyepoint <-> the chase camera. Both eyes coexist; DPLEyeRenderable::Execute now writes the VIEW only when it IS mCamera (unconditional writes let the last-executed eye stomp the toggle). - EyepointRotation is a real zeroed EulerAngles member on Mech (was bound to the shared junk attrPad -- the eye composes this attribute into the view EVERY frame, so the cockpit camera was rotated by garbage: the canted horizon, then the black screen). The eye-slew systems write it later. - The cockpit eye mounts at the eyepoint's REST position with a clean upright forward basis on the tree root (the live joint chain fed it the site tilt + torso pose; authentic pitch/yaw is the deferred gyro eye chain). - The inside view swaps the player to the INSIDE skeleton mesh set (SkeletonType_A): 19 body segments hide, exactly one mesh remains -- blx_cop.bgf, the authentic cockpit canopy shell around the eyepoint. It currently renders as a black enclosure (the black-screen report), so it is HIDDEN pending its interior/punch-material rendering (BT_INSIDE_COCKPIT=1 shows it for that work). Damage gstates respected in both directions; RemakeEntity keys off the DISPLAYED skeleton. - Dev: BT_START_INSIDE=1 starts in the cockpit view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b44c0f98f6 |
PFX continuous-rate emission (data-verified) + persistent wreck smoke
The one-burst emitter batching was wrong: in EVERY shipped .PFX, maxIssue/rate == release_period (DAFC 25/100~=0.2, DNBOOM 35/150~=0.2, DDAM2 35/16~=2, DDTHSMK 30/3=10) -- release_period is the EMISSION WINDOW and rate is particles/second emitted continuously across it. The death smoke plume is authored as a 3/sec trickle for 10 seconds (+6s particle life), not one same-frame puff. Emitters now integrate rate*dt until maximum_issue. Wreck smoke (port addition, [T3]): a destroyed mech re-arms the death/rubble smoke plume (psfx 1, DDTHSMK) every 10s -- its own authored window -- so the dead hulk visibly keeps smoking instead of standing there looking alive (the freeze-death is invisible on an already-stationary target). New member Mech::wreckSmokeTimer; UpdateDeathState takes dt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a3d67cc639 |
Combat visible + killable: Wword root-cause fix, .PFX effect layer, RemakeEntity swap
The 'can't kill the enemy / no visible damage' cluster, root-caused and fixed faithfully: - STEP-6 unaimed path was INERT: the cylinder table was 'cached' at Wword(0x111) -- the recon ABSORBER bank (stores nothing, reads 0) -- so every unaimed hit silently no-op'd. Promoted to the named member Mech::damageLookupTable (binary this[0x111], was mislabeled ammoExpended). New gotcha class recorded (reconstruction-gotchas §2) + sweep; 2 dead multiplayer branches logged. - Fire path migrated off the stale vital-zone aim onto the completed STEP-6 unaimed dispatch (zone=-1 + beam entry point -> cylinder resolves the exterior zone). No more invisible 1-shot kills; death via the authentic cascade (~14 center-mass hits). Wreck stays TARGETED on kill (beams stop on it); scoring latches off. - SendSubsystemDamage AV fixed: unbound critical-subsystem plug guard (43 unbound plugs/mech logged as an open question -- the binding itself is a gap). - RemakeEntity (render damage swap): the 1996 render state machine's missing Remake state, reconstructed as an in-place SetDrawObj mesh swap keyed by each segment's damage-zone graphic state (tree dtor doesn't cascade -> never rebuild). Destroyed arms/guns visibly wreck (the only variants the RES registers). - BT .PFX particle layer (L4VIDEO.cpp): the 1995 explosion/damage effect layer, unported since 2007 (DPLIndependantEffect/ReadPSFX/ExplosionScripts all stubs). Parses the authentic VIDEO/*.PFX definitions via the [pfx_day] psfxN mapping; premultiplied blending renders BOTH families from the same data (additive-style fire + occluding smoke -- DDAM2 is 30% grey, DDTHSMK ramps negative: impossible additively); depth-sorted billboards with a radial-masked grit sprite; impact-frame orientation (.PFX offsets are authored mech-local, -Z = out of the struck armor toward the shooter) for weapon hits AND damage bands (via lastInflictingID, now maintained -- was declared but never written). Both effect-number encodings route (raw dpl <100 + WinTesla 1000+slot carried by the band resources). Death fires the authentic dnboom (7) + ddthsmk smoke plume (1). - Effects anchor at the impact point / damaged zone's segment, not the mech origin (no more fire at the feet). - Dev force-input gates BT_AUTOFIRE / BT_AUTODRIVE for headless fire-chain verification; BT_PFX_ADD=1 flips the particle blend for A/B. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a9467481c5 |
Death-state sequence: collapse + subsystem shutdown + freeze (wreck stays)
Reconstructs the mech DEATH state machine — the un-exported master-perf death
branch (region 0x4a9770-0x4ab188) — from its EXPORTED consumers + the RP
VTV::DeathShutdown analog, wired into the active path (the bring-up drive
override bypasses the authentic Simulate where this normally lives).
Mech::UpdateDeathState() + IsMechDestroyed() (mech4.cpp), called for every mech
early in PerformAndWatch: on a vital kill (graphicAlarm >= 9, raised by the
damage side) it
1. sets movementMode = 5 -> the collapse clip's one-shot latch in
AdvanceBodyAnimation (fall direction 5-8 is un-exported -> 5 [T3]);
2. loops the roster calling Subsystem::DeathShutdown(1) (RP VTV::DeathShutdown
analog; the base is a no-op virtual, overrides act -- a SHUTDOWN not a
teardown, so it frees nothing and never removes the entity: the wreck STAYS);
3. next frame settles to movementMode = 9 -> IsDisabled -> locomotion frozen.
The drive's lone `movementMode = 1` write (mech4.cpp ~1309) is guarded on
!IsMechDestroyed so the death state is not clobbered back to a live gait.
Runtime-verified (forced kill, BT_DEATH_LOG=1):
[death] mech destroyed -> collapse + subsystem shutdown (wreck stays)
[death] mech settled -> disabled (IsDisabled=1, frozen wreck)
mech frozen in place, subsystem tick + renderer keep running, no crash, wreck
stays.
Also records the full death-sequence decomp map in combat-damage.md (the
exported consumers: AdvanceLeg/BodyAnimation collapse latch, IsDestroyed, the
MechDeathHandler effect engine; and the un-exported orchestration gap).
Deferred (honest): the visible collapse ANIMATION latch through the active path
(gait SM shows state=0 post-death; spawned mechs don't advance body anim);
MechDeathHandler (FUN_0042a984/FUN_0042aa2c -- the exported per-subsystem
destroyed-skin + explosion engine, still a stub) for death explosions; the
whole-mech DeathSplash radius damage (un-exported).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
d07ac7dd49 |
STEP 6 COMPLETE: cylinder hit-location LIVE — unaimed hits resolve to zones
The Mech per-impact hit-location resolver (the cylinder damage table) is now functional, wired, and runtime-verified [T2]. Unaimed (zone==-1) hits — the collision-damage path — now resolve an impact point to a damage zone via the authentic height x angle grid + weighted dice roll, instead of dropping. dmgtable.cpp/.hpp was a non-functional skeleton on no-op ReconTable/stream shims; backed it with real std::vector storage and fixed 5 latent runtime bugs: - ReadEntries now consumes the leading cell name-string ([i32 len][len+1]) - PieSlice ctor reads rotateWithTorso into the correct member - SelectSlice direct-indexes (was int lookup on a float-keyed table) - ResolveHit returns the zone (chains SelectSlice -> SelectZone) - real MemoryStream::ReadBytes (was a variadic no-op) mech.cpp ctor: replaced the empty-name StandingAnimation stub with the real load — FindResourceDescription(dzRes->resourceName, type 0x1d) -> stream -> new DamageLookupTable, cached at mech[0x111]; ~Mech deletes it. Mech::TakeDamageMessageHandler override registered (MESSAGE_ENTRY overlays Entity's by ID): on invalidDamageZone, resolve via the table then base-route; aimed reticle hits pass through unchanged. Three named accessors (no databinding-trap raw reads): WorldToLocal (localToWorld.MultiplyByInverse), CylinderReferenceHeight (standingTemplateMaxY == collisionTemplate->maxY == binary mech+0x2ec[+0xc]), TorsoHeading via a BTGetTorsoTwist bridge in torso.cpp (Torso::CurrentTwist == torso+0x1d8; torso.hpp cannot be included into mech.cpp — subsystem-stub collision). Stream format + geometry + roll + handler were all byte-verified against the shipped BTL4.RES type-29 resources (18 tables, exact consumption) and the disassembly (FUN_0049eb54/e678/de14, glue 0x49ed0c, handler @0x4a037a). Runtime: boots clean, "[cyl] table 'bhk1' layers=7" (exact byte-verified layer count, found by name), mech spawns + walks, no asserts/AV/0xCDCDCDCD. Env gate BT_CYL_LOG=1. Unblocks collision-damage application. KB updated (combat-damage.md STEP 6 COMPLETE, open-questions.md marked done). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f52bf057e6 |
WAVE 7 Phase B: the Mad Cat's LRMs LAUNCH flying missiles that fly + damage (autocannon too)
The byte-exact WORLD-ENTITY reconstruction (Projectile @4be1bc 0x340 / Missile @4bf5b4 0x368)
is infeasible on the 2007 engine: measured sizeof(engine Entity)=0x1BC vs the 1995 binary's
0x300, so the reconstruction's raw base-offset reads (velocity@0x1dc, roster@0x124, motion@0x250)
read GARBAGE on the engine (the Mech 0x638-vs-0x854 gap, but the entity integrator DEPENDS on
those offsets). So -- like the mech drive and the beam renderer -- flying projectiles are a PORT
reconstruction (BTPushProjectile/BTUpdateProjectiles in mech4.cpp, a static array + stack
messages, ZERO heap ops): seeded from the launcher's fire with the decomp's real muzzle
(GetMuzzlePoint) / launch speed (|launchVelocity|) / per-shot damage (damageData, split across
missileCount), they fly to the target (tracer via BTPushBeam) and deliver the weapon's damage on
impact through the SAME Entity::TakeDamage path as the beam (aim Mech::FirstVitalZone()).
THREE bring-up fixes were needed to make a projectile weapon fire at all (found by tracing):
1. TRIGGER: fireImpulse was only driven for the Emitter; ProjectileWeaponSimulation now sets
fireImpulse = gBTWeaponTrigger too (else CheckFireEdge never sees an edge).
2. AMMO BIN: OwnerSubsystemCount/OwnerSubsystem were stubbed ->0, so ammoBinLink never resolved
and ConsumeRound always failed; redirected to the real roster (owner->GetSubsystemCount()/
GetSubsystem(i) -- the AmmoBin 0xBCB constructs before the weapons 0xBCD/0xBD0).
3. JAM ROLL: UniformRandom() was stubbed `return 0.0f`, so CheckForJam's `0 < jamChance` ALWAYS
jammed (a projectile weapon could NEVER fire); replaced with a real LCG [0,1) (fires
~1-jamChance of the time -- authentic occasional jams).
Also: the mech's own target slot (owner+0x388) isn't populated in bring-up (the visible fire
targets the gEnemyMech global), so BTPushProjectile falls back to gEnemyMech.
Verified: Mad Cat PUSH=62 / IMPACT=31 (LRM missiles 3.33 dmg each split across the salvo + AFC100
autocannon 25 dmg), TARGET DESTROYED, 0 crashes, construction heapcheck-clean; BLH un-regressed
(also fires its ballistic weapon now). Diagnostics BT_PROJ_LOG ([projectile] PUSH/IMPACT).
REMAINING (deferred): the byte-exact world-entity Missile (Projectile : Mover, MP-replicable via
Registry::MakeEntity) -- the port projectile is master-local only (no MP replication); the real
per-weapon fire-rate/heat wiring off the subsystem sim (mech4 beam path is still the bring-up harness).
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> |
||
|
|
9e31408ba2 |
gauges: publish Mech attribute table (dense prefix 0x15..0x21 -> LinearSpeed live)
mech.cpp shipped an EMPTY, unchained Mech::AttributeIndex (a default-constructed AttributeIndexSet passed to DefaultData), so Simulation::GetAttributePointer severed the whole parent chain -> EVERY bare Mech gauge binding (LinearSpeed / MaxRunSpeed / DuckState / RadarRange / ...) resolved to NullAttribute. Add the Mech AttributeID enum (full binary set 0x15..0x38, VA 0x50be84) + a Mech::AttributePointers[] table + Mech::GetAttributeIndex() chained to JointedMover::GetAttributeIndex(), and change DefaultData to pass it. DENSE-TABLE HAZARD (systemic): AttributeIndexSet::Build (SIMULATE.cpp:565) sizes the built index to max(id) and Find (SIMULATE.cpp:663) strcmps EVERY slot, and Build does NOT zero gap slots -> an unpublished id between the parent's NextAttributeID and the max published id holds a garbage entryName -> AV on the next name lookup. So the table is a DENSE PREFIX 0x15..0x21 (JointedMover's NextAttributeID through LinearSpeed): the ids the BLH cockpit doesn't bind (collision/eyepoint/reticle/footstep/anim-state) point at one shared read-only attrPad member; CurrentSpeed/MaxRunSpeed bind to the existing gait members legCycleSpeed/reverseStrideLength; LinearSpeed binds a new linearSpeed member populated each frame in PerformAndWatch (|adv|/dt = forward ground speed). Ids 0x22..0x38 stay declared in the enum for later extension (RadarRange/DuckState when the map/duck widgets land) but are NOT published yet. New members appended to Mech's own region (no locked base/gait offset shifts); inited in the ctor. Verified live (BT_DEV_GAUGES + drive): the radar-surface SPEED readout (numericSpeed(LinearSpeed), a base engine primitive already drawing NULL) now shows 225 (~63 u/s in the widget's display units) instead of 0, tracking the mech's motion. Heat surface CurrentTemperature=77 un-regressed. Combat un- regressed (TARGET DESTROYED after 8 hits), 0 attribute-not-found, and Mech construction + attribute-index build is heap-clean under BT_HEAPCHECK. 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>
|