BT410 5.3.44: read the whole watcher list out of the resource; Torso MotionState

BTL4.RES stores each watcher as a (SUBSYSTEM, ATTRIBUTE) string pair, so the
complete list can be extracted directly instead of discovering one name per
6-minute run.  Banked the extraction method and the full normalised table.

Sixth rung climbed: Torso/MotionState (staged member, appended at the end of
Torso's own range so no downstream id moves).  LocalVelocity and
LocalAcceleration turn out to need nothing -- the engine's Mover already
publishes them.

Also banked, and deliberately NOT acted on: ReportLeak and
ConfigureActivePress are base-class attributes inside the pinned range, and
chasing them turned up what looks like the authentic attribute layout.  The
shipped pool gives MechSubsystem 1, HeatableSubsystem 3, HeatSink 6,
PoweredSubsystem 5 -- which counted from Subsystem::NextAttributeID=2 lands
MechWeapon's real ids on the binary-pinned 0x12 with EXACTLY ONE pad, where
our tree needs three.  That arithmetic is strong evidence for the original
layout, and reconciling to it would replace three guessed pads with the real
table.  But it moves ids underneath a cockpit currently verified at 98.9%
pixel-identical, so it wants a deliberate session with the A/B rig open
rather than a bulk edit at the end of a long one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-28 01:25:40 -05:00
co-authored by Claude Fable 5
parent 14da9489c2
commit 3ecd88aaef
3 changed files with 87 additions and 1 deletions
+3 -1
View File
@@ -37,7 +37,8 @@ const Torso::IndexEntry
ATTRIBUTE_ENTRY(Torso, HorizontalLimitRight, horizontalLimitRight),
ATTRIBUTE_ENTRY(Torso, HorizontalLimitLeft, horizontalLimitLeft),
ATTRIBUTE_ENTRY(Torso, SpeedOfTorsoVertical, baseElevationRate),
ATTRIBUTE_ENTRY(Torso, SpeedOfTorsoHorizontal, baseTwistRate)
ATTRIBUTE_ENTRY(Torso, SpeedOfTorsoHorizontal, baseTwistRate),
ATTRIBUTE_ENTRY(Torso, MotionState, motionState)
};
Torso::AttributeIndexSet
@@ -66,6 +67,7 @@ Torso::Torso(
Check(owner);
Check_Pointer(r);
motionState = 0; // staged: no gait state feed yet
isDamagedCopy = (owner->GetInstance() == Entity::ReplicantInstance);
statusFlags = 0;
+7
View File
@@ -123,6 +123,12 @@
HorizontalLimitLeftAttributeID,
SpeedOfTorsoVerticalAttributeID,
SpeedOfTorsoHorizontalAttributeID,
//
// Bound by an authored watcher (BTL4.RES pairs it as
// Torso/MotionState). Appended at the END of this leaf class's
// own range, so no downstream id moves.
//
MotionStateAttributeID,
NextAttributeID
};
@@ -130,6 +136,7 @@
static AttributeIndexSet AttributeIndex;
protected:
int motionState; // staged: no gait state feed yet
int isDamagedCopy;
int statusFlags;
Logical horizontalEnabled;
@@ -239,3 +239,80 @@ TYPES ARE PROVISIONAL on the staged members. AttributeWatcherOf<T> reads
*(T*)attributePointer (WATCHER.HPP:288) and the instantiation comes from the
resource, which the string pool does not reveal. Nothing drives these yet so
no watcher can fire -- settle the types WITH the models that write them.
--------------------------------------------------------------------------------
THE WATCHER LIST IS NOW FULLY KNOWN -- stop discovering it one run at a time
--------------------------------------------------------------------------------
BTL4.RES stores each watcher as a (SUBSYSTEM, ATTRIBUTE) string pair, so the
whole list can be read straight out of the resource instead of one 6-minute
run per name:
for each candidate name, find its NUL-terminated occurrences in BTL4.RES
and take the printable string immediately before it -- that is the owning
subsystem. Candidates = strings present in BOTH BTL4.RES and BTL4OPT.EXE
matching ^[A-Z][A-Za-z0-9]{3,}$ (59 of them).
Normalised across instance names (ERMLaser_1/2/3 -> the weapon class,
AmmoBinSRM6_1 -> AmmoBin, Condenser1..6 -> Condenser, GeneratorA..D ->
Generator), the COMPLETE set the engine will demand is:
Entity AnimationState(306) LocalVelocity(324) SimulationState(74)
CollisionSpeed(72) CollisionState(54) FootStep(36)
IncomingLock(36) UnstablePercentage(36) LocalAcceleration(18)
DistanceToMissile(18) ReduceButton(18) CollisionNormal(18)
weapons WeaponState PercentDone LaserOn ReportLeak
ConfigureActivePress SimulationState
AmmoBin AmmoState FireCountdownStarted SimulationState
Condenser CondenserState ReportLeak
Generator GeneratorOn GeneratorState ReportLeak
Reservoir ReservoirState
Myomers SpeedEffect ConfigureActivePress ReportLeak SimulationState
Avionics(Sensor) ConfigureActivePress ReportLeak SimulationState
ControlsMapper DisplayMode Look{Forward,Left,Right,Down,Behind}
TargetRangeExponent
Torso SpeedOfTorsoHorizontal MotionState
HeatSink CurrentTemperature
DONE so far (6 rungs, each run-verified): UnstablePercentage, SpeedEffect,
AnimationState, CollisionState/Normal, ReduceButton, SpeedOfTorsoHorizontal
(+ the whole Torso table), MotionState. LocalVelocity / LocalAcceleration
need nothing -- the ENGINE's Mover already publishes them (MOVER.CPP:104).
LEAF-SAFE APPENDS (do these next; each goes at the END of a class that
nothing chains off, so no id moves): ReservoirState, CondenserState,
GeneratorState (maps to the EXISTING stateAlarm), AmmoState +
FireCountdownStarted (AmmoBin has no table at all yet), LaserOn (Emitter),
TargetRangeExponent (ControlsMapper).
--------------------------------------------------------------------------------
!! ReportLeak AND ConfigureActivePress ARE NOT LEAF-SAFE -- and they reveal
the authentic attribute layout. DO NOT bulk-edit these without deciding.
--------------------------------------------------------------------------------
Both belong to BASE classes in the pinned range. The shipped string pool
gives each class's attribute names contiguously in id order:
MechSubsystem ConfigureActivePress (1)
HeatableSubsystem CurrentTemperature DegradationTemperature
FailureTemperature (3)
HeatSink NormalizedPressure DegradationPressure
CoolantCapacity CoolantMass
CoolantMassLeakRate ReportLeak (6)
PoweredSubsystem InputVoltage AuxScreenNumber AuxScreenPlacement
AuxScreenLabel EngScreenLabel (5)
Count them from Subsystem::NextAttributeID = 2: 2 | 3..5 | 6..0x0B |
0x0C..0x10 -> NextAttributeID = 0x11 -> MechWeapon needs EXACTLY ONE pad
to land its real ids on the binary-pinned 0x12. Our tree needs THREE pads,
because our tables differ: we publish HeatLoad (authentic does not),
CoolantAvailable (authentic does not), and a different PoweredSubsystem five
(OutputVoltage/RatedVoltage/VoltageState/ConnectMode instead of the four
AuxScreen* names). The one-pad arithmetic landing exactly on 0x12 is strong
evidence the four rows above ARE the original layout.
Reconciling to it is a real improvement -- it would replace three guessed
pads with the authentic table -- but it moves ids underneath the gauge
cockpit that is currently verified at 98.9% pixel-identical, so it wants a
deliberate session with the A/B rig open, not a bulk edit at the end of a
long one. Neither HeatLoad nor CoolantAvailable is bound by any gauge (0
hits in L4GAUGE.CFG), so the drop side looks cheap; the risk is the id shift,
not the names.