Completes the weapon-side half of the look commit:
- MECHWEAP: real rearFiring/viewFireEnable members (binary @0x334/@0x3E0) +
accessors. Ctor resolves REAR-FIRING authentically (@004b99a8 tail): the
mount SEGMENT site name is tested for the 'b' (back) marker -- only the back
gun ports (sitelbgunport/siterbgunport) carry it. Spawn = forward-armed.
- MECH CommitLookState: re-arms every roster weapon per view (forward =
non-rear, LOOK-BACK = rear-mounted, side/down = none) and flips the HUD
reticle pip group with the authentic Reticle::Front/RearFiringWeaponsOn flags
(BT411's raw "bits 1/2" resolved to their 1995 names).
- MECHWEAP: defined MechWeapon::MessageHandlers (0-entry set inheriting the
Subsystem chain) -- it was declared-but-undefined, and the surviving CODE
PPC.CPP binds the inherited name into PPC::DefaultData; the zero-filled
common block was a latent NULL deref (same trap as Emitter::AttributeIndex).
Discovery: the TEST.EGG mech genuinely mounts TWO REAR LASERS (ERMLaser_2/3 on
the b-ports). Verified on a full-clean baseline (engine 181 + bt 42, 0 fail):
look-behind arms exactly the rear lasers, disarms the other five,
pipMask=0xfffffffe; neutral run holds forward-armed; zero Fail.
build410.sh HARDENED against the stale-obj layout-skew trap this wave exposed:
cc() skipped recompiles on obj-vs-.cpp timestamps only, so the MECHWEAP.HPP
member additions left emitter.obj on the OLD MechWeapon layout -- its
chargeLevel=0.0f write landed exactly on the new rearFiring (a silent cross-TU
struct skew, heisenbug-grade). cc() now honors header stamps: a source410
engine-header edit invalidates every bucket, a BT/BT_L4 header edit
additionally invalidates the game buckets. CODE/ is immutable, no stamp needed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
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>
The Mech ctor now reads walkingTurnRate / runningTurnRate / maxAcceleration /
throttleAdjustment from the GameModel resource (SearchList(GameModelResourceType)
-> ModelResource), replacing the bring-up-default constants for those fields.
Each read is sanity-guarded (out-of-band -> keep default) since BT411 flags parts
of the ModelResource layout as mis-decoded -- but the live values came back clean
and authentic: walkTR=75deg/s, runTR=50deg/s, maxAcc=30 u/s^2, throttleAdj=1.0
(walking turns faster than running; maxAcc matches the madcat note), confirming
our reconstructed struct layout is correct for these fields.
Verified: at walk speed the authentic 75deg/s tightens the turn circle to r~6.9
(=speed/turnRate); zero Fail. Still on defaults (pending LoadLocomotionClips):
reverseStrideLength (top speed) / walkStrideLength / reverseSpeedMax -- measured
from the walk/run animation clips.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Phase-5.1 crash solved + two BTPlayer bricks reconstructed. The tree now
builds AND boots end-to-end with no staged Fail() reached, into the running
per-frame game loop.
- EMITTER.CPP: define Emitter::AttributePointers[]/AttributeIndex (ChargeLevel,
chained to Subsystem::AttributeIndex) and point Emitter::DefaultData at it.
EMITTER.HPP declared the index but the .cpp never defined it, so the surviving
PPC.CPP (PPC::DefaultData binds inherited Emitter::AttributeIndex) and
GAUSS.CPP (chains it) captured a NULL activeAttributeIndex -> GetAttributePointer
faulted on the first PPC (roster slot 22). Fix clears all 33 roster slots and
the control-mapping binding block.
- BTPLAYER.CPP InitializePlayerLink: real body -- dispatch PlayerLinkMessage
(our EntityID) to playerVehicle + replicants (mirrors engine
CameraDirector::CreateCameraShip). No staging.
- BTPLAYER.CPP VehicleDeadMessageHandler: non-death flavours (-2 drop-zone probe,
>=0 respawn re-post) delegate to the authentic Player base handler; only the
-1 death cycle stays staged (needs mech4 + scoring roles).
Verified: 516-line smoke run, zero Fail/Exception/abort; boot reaches
LBE4ControlsManager::Execute per-frame, waiting only on absent RIO hardware.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Mech ctor fully constructs (33 subsystems / 7 weapons on real TEST.EGG data,
trace-confirmed). The crash is the control-mapping resource binding in
MakeViewpointEntity resolving subsystem attribute IDs via GetAttributePointer --
the subsystems publish none (reuse base AttributeIndex). Documented the precise
phase-5 step-1 scope (per-subsystem AttributeIndex + the ID chain) in MECH.NOTES.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records that the Mech constructor works live (segment walk instantiates the full
roster, mech links as viewpoint, mapper installs) and the precise phase-5
frontier: Simulation::GetAttributePointer via PerformAndWatch, because the
reconstructed subsystems reuse the base AttributeIndex and publish none of their
real attributes. Phase-5 plan (per-subsystem AttributeIndex -> watcher/chain/plug
wiring -> mech2/3/4 per-frame sim -> rendering) in MECH.NOTES.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BTPlayer::CreatePlayerVehicle reconstructed (faithful; BT411's BT_SPAWN_XZ/
BT_SPAWN_ENEMY bring-up scaffolding dropped): builds the Mech::MakeMessage from
the mission game-model resource and hands it to MakeAndLinkViewpointEntity.
Mech::Make = new Mech(creation_message); a staged Mech ctor chains the real
JointedMover base (so the model skeleton/segments stream from BTL4.RES) then
Fails.
This validates the entire spawn factory chain live: CreatePlayerVehicle ->
MakeAndLinkViewpointEntity -> registry -> Mech::Make -> new Mech -> JointedMover
base ctor (streams the mech model) -> halts at the Mech ctor (mech.cpp:66).
The Mech ctor (@004a1674, 5690 bytes -- the largest function in the game) plus
the subsystem roster is the next MAJOR milestone, not a brick: it needs the full
1995 Mech member layout derived first (MECH.HPP is still reserved[331]; BT411's
decomp uses raw offset pokes that can't compile fresh), the subsystem class
hierarchy (only GAUSS/PPC/SENSOR survive in the 4.10 archive; the rest are the
measured "917 missing functions"), and the segment-table walk. Plan + scope in
MECH.NOTES.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>