Files
TeslaRel410/restoration/source410/BT/TORSO.HPP
T
CydandClaude Fable 5 8231bee58c BT410 5.3.68: THE LIVE-RIO FAULT IS FIXED -- it and the TM crash were one defect
The [map] audit named it in one run: subsystem 17 = Torso, attribute ids 12/13
unresolved.  The donor's decompiled torso.hpp carries the authentic enum with
binary offsets -- ids 3..15, including StickPosition(9), TorsoUp/Down/
Left/Right(10-13), TorsoCenter(14), MotionState(15).  Our table stopped at
seven entries with MotionState at id 9, on the strength of a comment claiming
the rest were messages.

The streamed control mappings bind BY ID as direct WRITE destinations, so the
truncation produced two different crashes from one cause: in TM mode the
button ids 12/13 resolved NULL (the boot write-fault the moment the joystick
polled), and in RIO mode the analog id 9 resolved to our motionState
StateIndicator -- every analog packet from a live vRIO wrote raw floats over a
watcher-socketed object, and the corrupted chains walked into unmapped memory
~30-40s later.  That was the 'intermittent' pod fault at the fixed DPMI-host
address.  vRIO down = no analog = no corruption, which is why the norio conf
launched: every observation from the whole hunt drops out of this mechanism.

Fix: the full 13-entry donor table; five int command members carved from the
dynamicsState reserve (class size unchanged); ctor zeros them.

Verified, two agreeing runs each way: TM mode launches with a clean audit and
the Thrustmaster joystick driving the torso (stickY=0.907 -> torsoElev 0.349);
RIO mode with vRIO STREAMING launches, zero faults, weapons cycling.
pod_render_rec is no longer poisoned and the norio workaround is obsolete.

The audit-before-the-archive-call pattern (BT_MAP_LOG walks the same streamed
table CreateStreamedMappings consumes, naming what will not resolve) turned a
two-day intermittent-corruption hunt into a one-run lookup.  The streamed RES
tables are binding contracts on our attribute enums.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 14:41:52 -05:00

205 lines
7.1 KiB
C++

//===========================================================================//
// File: torso.hpp //
// Project: BattleTech Brick: Mech subsystems //
// Contents: Torso -- the torso twist / weapon-elevation subsystem //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#if !defined(TORSO_HPP)
# define TORSO_HPP
# if !defined(POWERSUB_HPP)
# include <powersub.hpp>
# endif
//##################### Forward Class Declarations #######################
class Mech;
class Joint;
//###########################################################################
//##################### Torso Model Resource ###########################
//###########################################################################
struct Torso__SubsystemResource:
public PowerWatcher::SubsystemResource
{
Scalar horizontalRotationPerSecond;
Scalar verticalRotationPerSecond;
Scalar horizontalLimitRight;
Scalar horizontalLimitLeft;
Scalar verticalLimitTop;
Scalar verticalLimitBottom;
char torsoHorizontalJoint[32];
char torsoHorizontalShadowJoint[32];
Logical torsoHorizontalEnabled;
Scalar buttonAccelerationPerSecond;
Scalar buttonAccelerationStartValue;
};
//###########################################################################
//############################### Torso ################################
//###########################################################################
//
// Drives torso twist (horizontal) and weapon elevation (vertical). The
// tuning rates/limits come from the resource; the live twist/elevation state
// and the resolved skeleton joints are advanced by the per-frame
// TorsoSimulation (staged) once the Mech skeleton link is live.
//
class Torso:
public PowerWatcher
{
public:
static Derivation ClassDerivations;
static SharedData DefaultData;
typedef void
(Torso::*Performance)(Scalar time_slice);
void
SetPerformance(Performance performance)
{
Check(this);
activePerformance = (Simulation::Performance)performance;
}
static Logical
TestClass(Mech &);
Logical
TestInstance() const;
Scalar
CurrentTwist() const { Check(this); return currentTwist; }
Scalar
CurrentElevation() const { Check(this); return currentElevation; }
Logical
GetHorizontalEnabled() const{ Check(this); return horizontalEnabled; }
//
// Analog aim inputs (written by MechControlsMapper::InterpretControls;
// consumed by TorsoSimulation to slew the twist/elevation each frame).
//
void
SetAnalogElevationAxis(Scalar axis) { Check(this); analogElevationAxis = axis; }
void
SetAnalogTwistAxis(Scalar axis) { Check(this); analogTwistAxis = axis; }
void
TorsoSimulation(Scalar time_slice);
void
TorsoCopySimulation(Scalar time_slice);
public:
typedef Torso__SubsystemResource SubsystemResource;
Torso(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data = DefaultData
);
~Torso();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Attribute publication. The shipped string pool lists this class's
// attribute names contiguously after the class name "Torso":
//
// RotationOfTorsoVertical RotationOfTorsoHorizontal
// HorizontalLimitRight HorizontalLimitLeft
// SpeedOfTorsoVertical SpeedOfTorsoHorizontal
//
// The FULL authentic table, ids 3..15, confirmed twice over: the BT411
// donor's decompiled enum carries these names WITH binary member offsets
// (torso.hpp 111-124, @0x1E4..@0x20C), and BTL4.RES's own streamed
// control mappings bind ids 12/13 as DIRECT button destinations on
// subsystem 17 = Torso (the [map] audit).
//
// An earlier revision declared "TorsoUp/Down/Left/Right/Center are
// MESSAGES, not attributes" and stopped at 7 entries with MotionState at
// id 9. That was WRONG in a specifically nasty way: the ids are the
// binding key for the streamed mappings, so Thrustmaster's buttons
// (12/13) resolved NULL -- the boot write-fault at
// ControlsInstanceDirectOf<int>::Update+0xE -- and the RIO's ANALOG
// StickPosition (9) resolved to our motionState StateIndicator, letting
// every analog update from a live RIO smash a watcher-socketed object
// with raw floats.
//
// The chain -- PowerWatcher -> HeatWatcher -> MechSubsystem -- publishes
// nothing of its own, so these start at MechSubsystem::NextAttributeID.
//
public:
enum {
RotationOfTorsoVerticalAttributeID = MechSubsystem::NextAttributeID,
RotationOfTorsoHorizontalAttributeID,
HorizontalLimitRightAttributeID,
HorizontalLimitLeftAttributeID,
SpeedOfTorsoVerticalAttributeID,
SpeedOfTorsoHorizontalAttributeID,
StickPositionAttributeID,
TorsoUpAttributeID,
TorsoDownAttributeID,
TorsoLeftAttributeID,
TorsoRightAttributeID,
TorsoCenterAttributeID,
MotionStateAttributeID,
NextAttributeID
};
static const IndexEntry AttributePointers[];
static AttributeIndexSet AttributeIndex;
protected:
int isDamagedCopy;
int statusFlags;
Logical horizontalEnabled;
Scalar baseTwistRate;
Scalar baseElevationRate;
Scalar horizontalLimitRight;
Scalar horizontalLimitLeft;
Scalar verticalLimitTop;
Scalar verticalLimitBottom;
Scalar twistCenterHigh;
Scalar twistCenterLow;
Scalar elevationCenter;
Scalar elevationHalfBottom;
Scalar buttonAccelerationPerSecond;
Scalar buttonAccelerationStart;
int buttonRampActive;
Scalar buttonRamp;
Joint *horizontalJointNode;
Joint *horizontalShadowJointNode;
Scalar currentTwist;
Scalar currentElevation;
Scalar analogElevationAxis;
Scalar analogTwistAxis;
//
// MUST BE A StateIndicator. The authored watcher for a *State
// name is an AudioStateWatcher, which is AudioWatcherOf<StateIndicator>
// and whose ctor immediately runs
// Cast_Object(StateIndicator*, attributePointer)->AddAudioWatcher(this)
// (AUDWTHR.CPP:891). Point it at a plain int and that call goes
// through garbage -- the pod dies on the extender.
//
StateIndicator motionState; // authored watcher
//
// The five BUTTON COMMAND destinations (donor torso.hpp @0x1F8-0x208).
// The streamed control mappings bind these BY ID as DIRECT WRITE
// targets -- LBE4ControlsManager::CreateStreamedMappings registers
// GetAttributePointer(attributeID) as the destination a device poll
// writes through (L4CTRL.CPP:1936). Carved from the front of the
// dynamicsState reserve so the class size is unchanged.
//
int elevateUpCommand;
int elevateDownCommand;
int twistLeftCommand;
int twistRightCommand;
int centerCommand;
//
// Twist/elevation dynamics accumulators advanced by TorsoSimulation.
// Reserved until the per-frame sim is reconstructed (phase 5).
//
Scalar dynamicsState[17];
};
#endif