Files
TeslaRel410/restoration/source410/BT/TORSO.HPP
T
CydandClaude Fable 5 ea99e8987f BT410 5.3.114: the replicant torso aims -- TorsoCopySimulation + ComputeTargetTwist, decomp-verified, two donor drifts corrected
The damaged-copy twist path (@004b65f8 + @004b6510), reconstructed from the
raw decomp rather than the BT411 donor -- which drifted twice: its ease aged
by lastUpdate (+0x14) where the binary reads lastPerformance (+0x10), and it
calls the joint-write helper from the copy sim where 1995 does not (port-era
addition, dropped). The extrapolator predicts targetTwist = twistAtUpdate +
twistRate * elapsed on two time bases (settled = this frame's window, slewing
= the command window), and the copy sim snaps when settled or eases by the
remaining-time form dt/((stamp - now) + dt) -- landing exactly when the clock
reaches the command stamp. The decomp's DAT_0052140c divide is just
Time::operator- inlined; no hand conversion existed.

Members carved from the dynamicsState reserve (16->12): targetTwist @0x218,
twistAtUpdate @0x21C, twistRate @0x238, lastUpdateTime @0x254. The ctor now
registers the copy Performance on replicants (PTR @00510c1c); [T2] the gate
mirrors the binary's simulationFlags 0xC/0x100 pair with the instance test,
same as the whole watcher family, until the stream builders that seed those
bits are reconstructed. Feeders (Read/WriteUpdateRecord) stay staged --
zero-init eases a copy to centre, correct-by-vacuity in single-pod.

Stubs: 16 across 10 files. Regression: clean mission soak on the rig
(no faults, wheel turning, pacing steady).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 07:55:14 -05:00

233 lines
8.5 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);
//
// The extrapolation helper (binary @004b6510): predict targetTwist
// from the last update snapshot, clamp to the twist limits, return
// True while the copy is still slewing toward a future command time.
//
Logical
ComputeTargetTwist();
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;
//
// Recenter latch (donor @0x268-adjacent family): armed by the centre
// button, cleared by a twist button or on arrival at centre.
//
int recenterActive;
//
// TWIST REPLICATION (binary @0x218/@0x21C/@0x238/@0x254) -- the
// damaged-copy dead-reckoning state. A console update stamps a twist
// snapshot (twistAtUpdate) + rate (twistRate) + its command time
// (lastUpdateTime); ComputeTargetTwist extrapolates targetTwist from
// them and TorsoCopySimulation eases currentTwist onto it. The
// UPDATE-RECORD feeders (Read/WriteUpdateRecord, torso.cpp census
// remainder) are still staged -- zero-init means a copy eases to
// centre, which is correct-by-vacuity until MP updates arrive.
// Carved from the dynamicsState reserve; class size unchanged.
//
Scalar targetTwist;
Scalar twistAtUpdate;
Scalar twistRate;
Time lastUpdateTime;
//
// Twist/elevation dynamics accumulators advanced by TorsoSimulation.
// Reserved until the per-frame sim is reconstructed (phase 5).
//
Scalar dynamicsState[12];
};
#endif