64f99ffcc8f48ce40a787f129561d2247ef92044
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3629755d90 |
BT410 5.3.92: the gait slot map -- the clip array is bigger than the name table, and the "gimp" members are the REVERSE figures
Went to source animationClips[] and found two things: a bug in what 5.3.91 committed, and a naming trap that has already cost BT411 a shipped defect. THE BUG, MINE. I sized animationClips[AnimationCount] -- 0x1d, from the enum. Wrong. The name table stops at 0x1d but the ARRAY does not: slot 0x20 (mech+0x64c) is the bump/crash clip the mech binds on a hard wall impact. So the array is [AnimationSlotCount] = 0x21 and Set*Animation's Verify bounds against that instead. Sizing a real array off a name table that stops earlier reads fine and corrupts whatever sits next door; caught by reading the clip loader, not by the compiler. THE SLOT MAP, recovered from LoadLocomotionClips and now written down in full (suffix, meaning, and which measured constant each clip yields): 5 swr stand -> walk standSpeed 6/7 wwr/wwl forward walk CYCLE walkStrideLength = (s6+s7)/(d6+d7) 8/9 wsr/wsl walk -> stand 10/11 wrr/wrl walk -> run reverseSpeedMax 12/13 rrr/rrl run CYCLE reverseStrideLength 14/15 rwr/rwl run -> walk 16/17 sbr/sbl stand -> back gimpSpeedMax 18/19 bbr/bbl reverse CYCLE gimpStrideLength (NEGATED here) 20/21 bsr/bsl back -> stand 22/23 wgl/wgr walk -> limp gimpLeft/RightSpeedMax 24/25 ggr/ggl limp CYCLE gimpLeft/RightStrideLength 26/27 gsl/gsr limp -> stand 0x20 bmp bump / crash stagger THE TRAP: THE "gimp*" MEMBERS ARE THE REVERSE FIGURES, NOT THE LIMP ONES. gimpSpeedMax and gimpStrideLength are measured from sbr and bbr/bbl -- the reverse gait. The real limp has its own gimpLeft*/gimpRight* pair. This is the same bad naming that produced the states-16-19 misreading recorded in 5.3.91, and it has now caused the same error twice from two directions. Also settled: gimpStrideLength's negative sign is applied AT MEASUREMENT, not authored into the data -- which is where the fold in the transition machines comes from. And the limp clips are OPTIONAL: the loader probes for wgl and leaves hasGimpClips 0 with slots 22-27 unfilled if the model lacks it, so the deferred gimp branch must check that before routing into the limp machine. THE ENUM IS NOT THE SLOT MAP, and MECH.HPP now says so at the enum itself. The names are verbatim from the binary and authoritative AS NAMES, but slot 0x0e takes the run-to-walk clip while the table calls it RightReverseAnimation, and the forward walk alternates 6/7 rather than the pair the WalkForward names suggest. Read the slot map for "what does this play"; read the enum for "what did the original call this index". ATTRIBUTION NOTE: the four clip helpers (ResolveAnimationClip @004a7f50, MeasureClipStride @004a8054, LoadLocomotionClips @004a80d4, LoadLocomotionClipsExt @004a86c8) are exactly the four addresses the manifest lists under mech2.cpp that BT411 files under mech3. The manifest's attribution comes from the binary's own file tagging, so they belong here -- which also accounts for all 12 of mech2's functions. BT 51/51. Still nothing calls the gait. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d8aea8d871 |
BT410 5.3.91: the gait transition machine -- two channels, one table, and a reverse cycle that has been misread twice
mech2.cpp exists. Six of its twelve functions are reconstructed: the two
Set*Animation binders, the two *Transition tails, and both *ClipFinished jump
tables (@0x4a69aa leg / @0x4a6e0a body). Compile-verified, BT 51/51, links
clean.
TWO CHANNELS, DELIBERATELY NEAR-DUPLICATE. A mech runs two clip channels over
the same states and the same clips; only the speed they consult differs:
LEG reads the LIVE mapper GetSpeedDemand() -- responds to the stick at once
BODY reads bodyTargetSpeed, a snapshot -- which is what lets a dead-reckoned
or networked mech walk with no controls mapper of its own
So LegClipFinished and BodyClipFinished are twins rather than one shared
routine, exactly as the binary has them. Kept that way on purpose: where the
two jump tables agree, a divergence in this file is a bug, and that mutual
check is worth more than the duplication costs.
EVERY CLIP IS ONE STRIDE, which is why every state is handed -- a walk is
Right, Left, Right, and each entry to and exit from a cycle has its own handed
pair so the mech always leaves on the correct foot. The 29-state enum is
VERBATIM from the 0x3c-stride name table at .data:0050cfe8, the table the
"Unsupported mech animation" assert indexes, so the names and their order are
the original's rather than inferred from behaviour.
THE COMMIT TEST. Both exits that leave a walk cycle test the demand AND the
current cycle speed slewed by one carryover:
if (demand < standSpeed && (cycle - rate*carryover) < standSpeed) -> stop
if (demand > walkStride && (cycle + rate*carryover) > walkStride) -> run
Requiring both is what stops a momentary flick of the stick yanking the mech
out of a stride it has already committed to. Drop either conjunct and you get
a mech that stutters between gaits on noisy input.
TWO THINGS THAT READ WRONG AND ARE NOT:
gimpStrideLength is authored NEGATIVE. The cycle time from it comes out
negative and is folded positive before being spent (@0x4a6c6e / @0x4a6d3d).
That fold is not defensive coding -- remove it and the limp plays backwards.
States 16-19 on the body channel are the REVERSE gait, not a limp, despite
sharing the gimp caps. BT411 records misreading these as "gimp, fall back to
standing" TWICE; that makes the body loop stand -> reverse-entry forever, a
slow reverse with a wrong-footed exit. Read here by structural symmetry with
the leg table, where every previously-decoded body case mirrors its leg twin.
DEFERRED, and named in the sidecar: the four Advance* per-frame entry points
and the two Gimp*ClipFinished limp machines -- with them the gimp-level branch
at the top of both *ClipFinished, so a limping mech currently runs the normal
machine. That branch needs a TU-safe read of the graphic alarm level (BT411
routes it through a mechdmg bridge to dodge an AlarmIndicator ODR split), worth
reproducing carefully rather than reaching for the alarm directly.
NOTHING CALLS ANY OF THIS YET. The Advance* functions are the entry points and
they are the deferred half, so no gait state is ever selected and a run behaves
exactly as before. Same caveat as 5.3.90: a blocker removed, not a behaviour
delivered.
HEADER: MECH.HPP gains the enum, six declarations, and the channel state --
legStateAlarm/bodyStateAlarm (read via GetLevel; the binary's +0x3b0/+0x728 are
mirrors of the alarm level, so no separate int is kept), legCycleSpeed,
bodyCycleSpeed, forwardCycleRate, gimpCycleRate, standSpeed, gimpSpeedMax,
gimpStrideLength, globalTimeScale, animationClips[0x1d]. 41 ints carved from
reservedState, 191 -> 150. walkStrideLength/reverseStrideLength/
reverseSpeedMax/bodyTargetSpeed already existed from the Phase 5.3 locomotion
work and are reused.
STILL UNSOURCED: animationClips[] is declared but nothing fills it. The clip
handles come from the mech's model resource and must be resolved before the
Advance* increment, or SetLegAnimation hands SelectSequence a garbage ID. It
fails soft (SelectSequence tolerates a missing resource with an inert
controller) but it is a hard prerequisite for the gait doing anything.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
14da9489c2 |
BT410 5.3.43: full pod rig -- geometry loads, ladder advances to the audio watchers
Under the documented rig (launch_pod.ps1 with the GL bridge up) the 'couldn't load object' count went to ZERO. .BGF loading was never a reconstruction problem, only a missing bridge. What blocks now is a series of authored AttributeWatchers: BTL4.RES binds them BY NAME and the engine Fails outright on any that does not resolve, so each is a name our subsystems must publish. Five rungs climbed, each run-verified: UnstablePercentage, SpeedEffect (Myomers table), AnimationState + CollisionState/CollisionNormal + ReduceButton (Mech), and the full Torso table (all six authentic names mapped onto existing members). The method that makes this cheap is banked: the shipped binary's string pool carries each class's attribute names CONTIGUOUSLY in ID ORDER after the class name, and in every case so far our member declarations sit in the same order -- which confirms the layout and lets ids be pinned rather than guessed. Intersecting those names with BTL4.RES gives the exact work list. A THIRD miswired SharedData found on the way: Myomers carried Subsystem's tables where it derives from PoweredSubsystem, the same defect as MissileLauncher (5.3.33). Worth a sweep across every subsystem. Staged member TYPES are provisional and documented as such: AttributeWatcherOf reads *(T*)attributePointer and the instantiation comes from the resource, which the string pool does not reveal. Nothing drives them yet, so settle the types with the models that write them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
91b043778e |
BT410 5.3.42: the 3-D pipeline runs to the geometry load -- two rungs climbed
RUNG 1: BTL4VideoRenderer::LoadMissionImplementation, Fail -> bring-up no-op. Legal, not a cheat: the engine's own VideoRenderer version (VIDREND.CPP:259) is a bare Tell and our working GaugeRenderer ships the same, so the renderer comes up and runs its frame loop with an empty scene -- exactly what we want to measure before writing content. The authentic shape for the real body is pinned by the surviving sibling header CODE/RP/RP_L4/RPL4VID.HPP (walk the mission entities -> MakeEntityRenderables -> ReadSKLFile/RecurseSKLFile). RUNG 2: Mech::EyepointRotation published. The engine then Failed at SetupCull, which fetches that attribute BY NAME off the viewpoint entity and composes it with the siteeyepoint segment to build worldToEyeMatrix. The mech already HAD EulerAngles eyepointRotation with a getter -- only the publication was missing. One enum id, one table row. The run now reaches the full mech build, constructs the renderer, boots the board (~907K VPX wire transactions), loads the mission, runs the per-frame cull and enters geometry loading with ZERO Fails and zero exceptions. It stops at 'couldn't load object buttee.bgf' / 'NULL instance' -- and that is not a defect: the models are present and BTDPL.INI points at them, but .BGF loading goes through the board to the external GL render bridge that the VPX HLE tees over VPX_FIFOSOCK, and that bridge was not running. 'NULL instance' comes from the prebuilt LIBDPL.LIB refusing to instance an object that never loaded. Next rung is the documented full rig (render-bridge/launch_pod.ps1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d67f8d5eda |
BT410 Phase 5.3.27: the attribute wave -- 48/50 cockpit bindings go LIVE
The gauges now read the real simulation: heat/power/sensor/mech attribute tables published under the 1995 L4GAUGE.CFG spellings the widgets already bind by name. Temperatures, coolant mass/capacity/leak rate, condenser valve settings, generator voltages and numbers, radar percent, and the mech's radar/speed rows all resolve to live members. THE NUMBERING IS PINNED: the chain publishes 2..0x0E so PoweredSubsystem::NextAttributeID lands on the authentic 0x0F -- the surviving SENSOR.HPP numbers RadarPercent off it and MechWeapon's binary-pinned table starts at 0x12, so its pads shrank 16 -> 3 (0x0F..0x11) exactly as the 5.3.x header comment predicted. Sensor's authentic enum finally has its table defined; MechWeapon/Sensor rechain to PoweredSubsystem and Reservoir/AggregateHeatSink to HeatSink so the whole family is visible where the cfg expects it. Verified with BT_GAUGE_ATTR_LOG: 48 OK / 2 NULL (was 33/17, and 0/50 at the block's birth). The two remaining have no member to bind -- HeatSink/AmbientTemperature (a sim constant) and Searchlight/LightOn (a memberless subclass) -- and fall to the documented zero cell. Gauge fight (15/15 rounds, 127 zone hits), smoke and novice all clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f65a3259ce |
BT410 Phase 5.3.25: scoring + the COMPLETE mission lifecycle -- egg to GAME-RC
Scoring roles bound (binary ctor tail @004c0bc8): the mission role dict -> Player::scenarioRole; TEST.EGG's Role::Default = the baked dfltrole (lives=1000, killBonus=500) -- authored data flowing. The three score handlers are live per the decomp derivations (@004c02e4 / @004c0200 / @004c02a8 + CalcInflictedScore @004c052c; tonnage/bias staged); the producers post damage scores per delivered hit and the kill credit from the death transition with the latched killing-blow magnitude. First live kill: award=3.43, kills=1, score=123.6. Out of lives -> the mission END: the binary's +10s review post sits in the one decomp gap, so the staged stand-in enters the engine's own cascade (StopMissionMessage +10s) -- EndingMission -> the 3s fade -> the second Stop -> Application::Stop -> RunMissions exits. The BTL4/L4 stop layers run authentically (plasma score display off, egress lamps). VERIFIED END-TO-END: a 2-lives egg (role-page return=2 override) -- death #1 debits and respawns healed; death #2 is OUT OF LIVES; +10s later the mission ends and BTL4OPT.EXE exits CLEANLY to DOS (GAME-RC prints). The full 1995 pod mission loop -- egg, boot, spawn, fight, score, die, respawn, die, mission end, exit -- now runs from reconstructed source. Zero faults; fight/smoke/novice regressions green. Dev knob: BT_PLAYER_PASSIVE=1 (piloted mechs hold force-fire). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
623c872c92 |
BT410 Phase 5.3.24: the death -> respawn cycle -- mechs DIE and COME BACK
The full circuit, workflow-researched (5 parallel dossiers over BT411 + the engine) and adversarially reviewed (3 lenses) before landing: Mech side: the once-per-death transition in Simulate (freeze, motion kill, DeathShutdown sweep, MASTER-only VehicleDead(-1) to the player link); Mech::Reset @0049fb74 (reposition + heal every hull zone + the roster DeathReset sweep + PreRun -- the reset-based respawn that REUSES the entity); dead-owner terms in both weapon hard gates (wrecks fall silent). Player side: the VehicleDead(-1) branch (deathPending dedup, deathCount bump+stamp, scenario-role life debit, +5s re-post -> the engine drop-zone hunt) and the DropZoneReply respawn branch (reset the dead mech at the replied drop zone; probes on a live mech are moot). Subsystem side: the engine base virtual DeathReset implemented across the family -- MechSubsystem (zone heal + DestroyedState clear), MechWeapon (full powered/thermal restore + fresh Loading cycle), AmmoBin (restock from the ctor-cached count), HeatSink/HeatWatcher/PowerWatcher/Generator/Sensor/ MissileLauncher. Review catches fixed before commit: AmmoBin restocked through the FREED padBuffer pointer (use-after-free -> initialAmmoCount; MechSubsystem:: resource is now documented as never-deref-post-ctor); died-hot weapons respawned at FailureHeat (now full thermal re-init); Generator tap counts desynced across reset (preserved); Sensor skipped the base heal; PowerWatcher inherited a statically-bound reset; cooling toggle + connect mode restored. Verified: kill -> wreck (0 shots while dead) -> +5s -> drop-zone hunt -> HEALED + placed at a real drop zone -> 134 shots after respawn incl. 12 SRM (restock proof); unowned enemy wreck settles silently; fight/smoke/novice regressions green, zero faults. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2f0d3b6239 |
BT410 Phase 5.3.23: the cylinder hit-location table -- unaimed hits land WHERE THEY STRIKE
DMGTABLE born (type-29 streams, BT411 byte-verified format): the height x angle grid -- DamageLookupTable rows by height, PieSlice cells by angle (rotate-with-torso rows add the live twist), DamageZonePercentTable = the cumulative hit-distribution roll (first threshold above the roll -- the BattleTech dice scatter). Loaded in the Mech ctor by the DamageZoneStream member's NAME (mech+0x444, resident); Mech::TakeDamageMessageHandler resolves invalidDamageZone hits through it (binary @0x4a0264 tail). The missile fuse becomes the authentic unaimed producer: zone -1 + the round's world position as the impact point (ENTITY3.HPP: "damage zones are only valid via reticle based weapons"). Fight-verified on the authentic 7-row bhk1 table: flat-flying SRMs strike low -> row 0 -> FEET AND LEG zones, side-correct by impact angle (+x right, -x left), identical geometry spread across the cell distribution by the roll. 17/17 missile lifecycle, zero faults; smoke + novice green. Also fixed: the ctor's reservedState zero-loop counted past the shrunken array. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d4f0aef6e1 |
BT410 Phase 5.3.17: the destruction cascade -- zone deaths DESTROY things
Mech::DamageZone::TakeDamage is the full binary cascade (@0049c690): LOD artifact router with same-attacker clustering (@0049c40c, hysteresis 0.33), artifact parent level = mean of children, Energy-hit generator shorts through the crit plug binding (novice-exempt), weighted CriticalHit (@0049ccc4), zone-death allotment push (SendSubsystemDamage @0049c9a8) and the segment-table destruction descent (RecurseSegmentTable @0049cad4 -- SIBS + DESCEND via the engine EntitySegment iterators). Supporting bricks: Mech's OWN handler table with the TakeDamage override (latches lastInflictingID @0x43c; gyro feed staged; cylinder table deferred); friend class Mech__DamageZone (authentic); subsystem private zones ARMED (structureReference + armorByFacing[5] normalized -- crits are measurable); ApplyDamageAndMeasure; ForceCriticalFailure sets DestroyedState so the weapon FSMs' GetSimulationState()==1 hard gate went live; Generator::ForceShort + PoweredSubsystem::ForceShortRecovery. Fight-verified (mutual BT_SPAWN_ENEMY + BT_FORCE_ZONE=8 concentrated fire): arm zone to 1.0 -> sibling searchlight zone dies (Searchlight2/ThermalSight crits destroyed, HUD degrading) -> DESCEND kills the gun zone -> PPC_2 + ERMLaser_2 + Condenser6 DESTROYED and FALL SILENT (0 shots after death; undamaged PPC_1 keeps firing); vital-zone hit = MECH KILL; PPC hit shorted GeneratorD. This art has zero artifact zones (redirect=0 across all 20 -- router verified dormant-correct). Smoke + novice regressions clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
76cfc64353 |
BT410 Phase 5.3.16: targeting + damage -- two-mech fights are LIVE
The simulation is now a FIGHT. Mech target slot (+0x388), authentic Loaded->Firing gate (viewFireEnable && HasActiveTarget), UpdateTargeting range refresh, and SendDamage via the engine TakeDamageMessage. MECHDMG.CPP is BORN against the AUTHENTIC surviving CODE/BT/BT/MECHDMG.HPP: the Mech ctor's Pass-3 hull zone fill constructs Mech__DamageZone per zone (the Entity base only allocates the raw pointer array -- unfilled slots were crash #1; a base-class fill parses each record short and skews the stream -- crash #2). Streamed ctor parses the full BT tail (flags / Scalar-weighted criticals with the Master+Dynamic plug gate / LOD redirect table) and normalizes the armor economy: scale[type]=1/(raw x armorPoints), legs halved -- BT411-verified @0049ce50. Live two-mech verification (BT_SPAWN_ENEMY harness in DropZoneReply): 20 named hull zones stream on both mechs; PPC lands 11.77/hit (12 x chargeRatio^2, type 4), ER-M laser 3.43 (type 3), SRM6 salvos 5.83 x 6 rounds on independent zones (type 2, one message per missile -- burstCount is NOT in the zone formula); repeat hits climb linearly, leg zones absorb at half scale, zones reach 1.0 Burning/Destroyed. No-target and novice regressions hold (weapons load but never fire without a target). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
eb8bba3195 |
BT410 Phase 5.3.6: look-button state machine -- full eyepoint composition
Reconstructs the binary's five-state LOOK machine (controls mapper tail, part_013.c:396-459) as proper members/methods: - MECHMPPR: LookState enum (None/Left/Right/Behind/Down) + lookState/ previousLookState (out of reserved[24] -> [22]). InterpretControls picks the state from the look buttons each frame and on a CHANGE calls Mech::CommitLookState. BT_FORCE_LOOK=<1..4> dev hook. - MECH: authored look angles lookLeft/Right/Front/BackAngle (deg->rad from the GameModel resource, guarded) + lookPitch/lookYaw + CommitLookState(int): side looks yaw by the authored angle, look-behind = yaw pi + lookBackAngle pitch, look-down = lookFrontAngle pitch, forward = identity. The per-frame eyepoint compose in Simulate adds the live Torso elevation on top. (reservedState [208] -> [202].) - Deferred inside the commit (needs MechWeapon viewFireEnable/rearFiring): per-view weapon fire re-arm + HUD pip group-mask flip. Verified headlessly: model reads clean authored angles (L=60 R=-60 F=-10 B=0 deg -- ModelResource layout confirmed again); BT_FORCE_LOOK=3 fires ONE commit ([look] state=3 yaw=3.14159 pitch=0) and the per-frame compose holds eyeYaw=pi with eyePitch=0.349066 (lookBackAngle + the 20deg-clamped elevation). Zero Fail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
87b1c048cf |
BT410 Phase 5.3.5: eyepointRotation -- torso elevation pitches the eye/aim-ray, not a joint
Investigated the next planned binding (elevation -> gun joints jointlgun/ jointrgun) before implementing it, and found it was the wrong target: BT411's own reverse-engineering history records that NO gun/arm elevation joint exists in the authentic engine. The pod's stick-Y aim pitches the COCKPIT EYE and the weapon boresight directly (mech4.cpp @~5219, pixel-calibrated against real screenshots) -- the torso geometry never tilts for elevation on this mech family. Reconstructing a gun-joint binding would have been invented behavior. - MECH.HPP: EulerAngles eyepointRotation member (out of reservedState, now [208]) + GetEyepointRotation() accessor. - MECH.CPP: composed each frame in Simulate -- eyepointRotation = EulerAngles(Radian(Normalize(lookPitch+elevation)), Radian(Normalize(lookYaw)), Radian(0)), reading Torso::CurrentElevation() via sinkSourceSubsystem. lookPitch/lookYaw are 0 until the look-button wave (BTCommitLookState) lands. Verified: BT_FORCE_ELEV=0.8 -> torsoElev and eyePitch both read 0.349066 (the authentic 20 degree resource limit) every frame, exact match; neutral -> 0. Zero Fail. Corrected TORSO.NOTES.md's prior (wrong) "next: gun joints" note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9fff0e969e |
BT410 Phase 5.3.4: Mech::ResolveJoint + skeleton-joint binding (torso twist)
Reconstructs the shared skeleton-joint resolver and binds the first subsystem (Torso) to the live skeleton. - Mech::ResolveJoint(name) (mech.cpp @00424b60): GetSegment(name) -> segment joint index -> GetJointSubsystem()->GetJoint(idx). - Verified the JointedMover base streams the full skeleton headlessly: jointCount=19, 40 named segments (gun joints jointlgun/jointrgun, shoulders, hip, leg joints jointlthigh..jointrankle). BT_MECH_LOG [skel] summary; BT_SKEL_DUMP lists every segment/jointIdx. - Torso resolves + binds its twist joint (ResolveJoint(torsoHorizontalJoint)) and pushes currentTwist onto it via Joint::SetRotation (hinge->Radian, ball->EulerAngles yaw). The TEST.EGG mech has a fixed torso (horizJoint='', enabled=0) so its twist path is inert -- correct + guarded, zero Fail. Next joint piece: elevation -> gun joints (jointlgun/jointrgun, the fixed-torso aim mechanism) via weapon-joint binding; then gait leg animation. Both are VISIBLE only under the renderer; headlessly the joint transforms are loggable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ebf0d8c23f |
BT410 Phase 5.3.2: DRIVABLE mech -- authentic control interpretation + locomotion
The mech now drives from the authentic control chain (faithful route). Verified headlessly: full throttle -> walks straight at top speed (30 u/s) along heading; throttle+turn -> walks a circle (radius = speed/turnRate); neutral -> holds pose; zero Fail. MechControlsMapper::InterpretControls (mechmppr.cpp @004afd10) -- installed as the mapper's per-frame Performance (roster slot 0, ticks before the mech): - speedDemand = topSpeed*throttle*fwdScale (reverse inverts); soft stick (square) / pedal (cube) response; Basic=stick turn, Std/Vet=pedal turn; speed clamped while turning hard. Reads owner stride via Mech accessors. - BT_FORCE_THROTTLE/BT_FORCE_TURN dev hooks for headless verification. Mech::Simulate drive -- consumes the mapper demands: - accelerate currentBodySpeed toward speedDemand (maxBodyAcceleration); - authTurnRate = lerp(walkingTurnRate, runningTurnRate) by ground speed + over-run falloff (mech4.cpp master-perf @0x4aa3d3); - integrate heading via Quaternion::Add(prevPose, (0,turn*rate*dt,0)); - facing = world -Z basis (GetFromAxis(Z_Axis)); worldLinearVelocity = facing*spd; - integrate position (increment-1 core). MECH.HPP: 9 named locomotion members out of reservedState (now [211]) -- walking/runningTurnRate, reverse/walkStrideLength, reverseSpeedMax, forwardThrottleScale, maxBodyAcceleration, body/currentBodySpeed. BRING-UP DEFAULTS (authentic values come from the model resource + LoadLocomotionClips -- next refinement). Deferred: gait-clip-exact advance + leg anim (needs animation subsystem/renderer), model-resource sourcing, terrain drop, torso/free-look aim, telemetry filters. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3364b65ae8 |
BT410 Phase 5.3.1: Mech::Simulate motion core -- mech integrates motion per-frame
Installs Mech::Simulate as the mech's per-frame Performance (was DoNothingOnce). Reconstructs the load-bearing spine of the 1995 Simulate (mech4.cpp @004ab430): integrate the body velocity into localOrigin (linearPosition.AddScaled(.., worldLinearVelocity, dt)) and commit the Origin to the world transform with the engine idiom (localToWorld = localOrigin, per ENTITY.cpp:988 / MOVER.cpp:850). Uses the NAMED engine base Mover fields (localOrigin/localToWorld/ worldLinearVelocity) -- BT411's MechBaseLayoutCheck proved the WinTesla decomp's raw this+0x100/0x260 offsets actually stomp those base fields; the clean 1995 build addresses them by name. - MECH.HPP: Mech Performance typedef + SetPerformance + Simulate() decl. - MECH.CPP: SetPerformance(&Mech::Simulate) at ctor tail; Simulate body. Verified headlessly (BT_MECH_LOG "[sim] mech pos=" 1Hz): BT_DRIVE="5,0,10" advances the mech +5.0/s x, +10.0/s z (y fixed) = exactly the injected world velocity; no BT_DRIVE -> worldLinearVelocity is 0, mech holds spawn pose. Zero Fail/Exception either way. BT_DRIVE is a retained dev hook for headless motion. Deferred (locomotion wave): gait-cycle self-propulsion feeding worldLinearVelocity (IntegrateMotion->AdvanceBodyAnimation, steered by mapper throttle/turn), heading integration, terrain-height drop, cockpit telemetry filters. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4d175c959a |
Mech phase 3: named member layout + embedded-member construction
Replaced MECH.HPP reserved[331] with the named Mech-own members: cached subsystem pointers (sensor/gyro/sinkSource/hud) + messageManager + weaponCount, the 5 capability chains (ChainOf<Subsystem*>), and the embedded status/animation state (4 AlarmIndicators, Reticle, 3 StateIndicators, 2 SequenceControllers, NameFilter, telemetryFilter[5]=AverageOf<Scalar>, 3 CStrings) + a reservedState pad for the phase-5 per-frame fields. The subsystem ROSTER (subsystemArray/subsystemCount) lives in the base Entity (GetSubsystem/GetSubsystemCount), so the Mech only caches back-pointers. The Mech ctor now CONSTRUCTS all embedded members (alarms Initialize'd, reticle armed, state indicators sized, telemetry filters sized, sequence controllers Init'd) -- compile-verified -- then halts at the segment walk (the remaining piece). BT: 42 ok; tree links clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d9b34ddc63 |
source410: THE WHOLE 4.10 TREE COMPILES - 11/11 under the fleet's BC++ 4.52
All 10 surviving original TUs plus the reconstructed BTL4APP.CPP pilot build clean with the authentic OPT.MAK flags (compile410.sh --sweep). First time the 4.10 BattleTech source has compiled since 1996. - layout_probe.cpp: the period compiler measures 1995 layouts from the surviving headers; validated 3/3 against binary alloc sizes (BTMission 0xFC, BTL4ModeManager 0xC, BTRegistry 0x10). Base boundary: Simulation 0xD0 / Entity 0x1C4 / Mover 0x300 / JointedMover 0x328 => Mech-own region 0x328-0x854 (MECH-LAYOUT.md staging worksheet). - 13 staged headers close the family ([T3] reserved[]-parked layouts; interfaces PROVEN by the surviving consumers): MECH, MECHSUB, HEAT, POWERSUB, MECHWEAP, EMITTER, MECHMPPR, BTPLAYER (BTPlayer__MakeMessage = Player's 8 args + roleName/teamName [T1 via BTREG]), PROJTILE, MISSILE, BTL4MPPR, BTL4VID + the round-2 BTCNSL/BTSCNRL. - compile410.sh: PCH gate retired (bt.hpp/mungal4.hpp full include sets), DPL SDK roots on the include line. - BTL4APP.CPP: appmgr include + GetApplicationManager()->GetFrameRate() + ResourceDescription:: scoping; Fail() held at its recorded line 400. - Evidence ledgers: STAGED-HEADERS.NOTES.md, MECH-LAYOUT.md, BACKFILLS, BTCNSL (binary-recovered console wire IDs), BTSCNRL. Remaining for a linkable BTL4OPT: the ~40 missing TU bodies (917-function manifest) - the header skeleton they grow into now exists and compiles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |