Files
TeslaRel410/restoration/source410/BT/GYRO.HPP
T
CydandClaude Fable 5 79aba77309 BT410 5.3.127-128: the mech can stand on its heels, and it knows when it is about to fall over -- the super stop, the instability model and the telemetry rings
THE MODEL RESOURCE NAMED THESE BLOCKS.  The ctor loads (part_012.c:15798)
tie the master perf's anonymous offsets to field names our
Mech__ModelResource already carried, and the names confirm the arithmetic
as surely as the arithmetic confirms the names:

  rec+0x48 -> +0x5bc  superStopAcceleration
  rec+0x80 -> +0x784  maxUnstableAcceleration
  rec+0x84 -> +0x788  unstableAccelerationEffect
  rec+0x88 -> +0x78c  unstableGunTheEngineEffect
  rec+0x94 -> +0x798  unstableStopedTurnEffect

THE SUPER STOP (@0x4aa353): hauling the throttle negative while the mech
still rolls forward is a hard brake, not a reverse.  The edge swaps the
gait's acceleration rate to the authored superStopAcceleration, marks the
update, and throws ONE random-signed lurch through both gyro channels;
while it holds above a walk it repeats a pitch kick every 0.4s -- the
shudder of a hundred tons standing on its heels.  A chassis authoring
-1.1 disables the system entirely (the sentinel must survive the model
read unguarded).

THE INSTABILITY MODEL (tail @0x4aab8e): three weighted terms -- filtered
acceleration (squared), gunning the engine, and turning on the spot --
clamped to 1 into unstablePercentage, which was ALREADY a published
cockpit attribute holding a staged zero, and republished to the gyro
(whose tip/sway machinery @004b275c consumes it; that's the next gyro
brick).

THE TELEMETRY RINGS (@0x4aafba): the binary's five rings ARE the engine's
AverageOf<Scalar>, and our telemetryFilter[5] has been sized 15 and unfed
since the layout wave.  FUN_0043ae47 is CalculateOlympicAverage -- a
judge-style trimmed mean, min and max discarded.  Two oddities kept
verbatim and flagged: the vel.y ring is never reduced, and the derived
acceleration's components come from a mongrel basis (only its length is
consumed).

CORRECTION to 5.3.122: 'realMaxSpeed' was a duplicate member for
mech+0x7a0, which our tree already carried as runSpeedMax; merged.  And
the +0x7a4 'computed my own cap' flag is set by the ctor's MYOMERS sweep
-- a mech with myomers broadcasts its cap, one without adopts the
streamed value.

Soaks (pod_render_superstop / pod_render_telem, BT_SUPERSTOP_SOAK cycling
+0.8 for 6s then -0.5 for 4s): the super stop engages on the correct edge
(speed 35.6, demand -28.0) and releases cleanly, repeatedly; the rings
fill and the instability sweeps its whole range to the 1.0 clamp; zero
faults.  The new gauge immediately earned its keep as a diagnostic -- it
pins high while walking because the mech demands 35.9 u/s and the
reconstructed gait delivers 7.0, which is now a measurable open question
rather than an invisible one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 00:46:25 -05:00

213 lines
6.8 KiB
C++

//===========================================================================//
// File: gyro.hpp //
// Project: BattleTech Brick: Mech subsystems //
// Contents: Gyroscope -- the eye/body stabilisation subsystem //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#if !defined(GYRO_HPP)
# define GYRO_HPP
# if !defined(POWERSUB_HPP)
# include <powersub.hpp>
# endif
//##################### Forward Class Declarations #######################
class Mech;
//###########################################################################
//################### Gyroscope Model Resource #########################
//###########################################################################
//
// One per-damage-type response row: how hard a hit of that type kicks
// the four gyro channels.
//
struct Gyroscope__DamageResponse
{
Scalar trans;
Scalar pitchRoll;
Scalar yaw;
Scalar vibration;
};
struct Gyroscope__SubsystemResource:
public PowerWatcher::SubsystemResource
{
Scalar reservedF4;
Scalar exageration;
Scalar maxAnimationNoise;
Scalar minAnimationNoise;
Scalar rotationPerSecond;
Scalar percentageOnNormal;
Scalar percentageOnDestruction;
Scalar percentageOnDegradation;
Scalar percentageOnFailure;
Vector3D springConstant;
Vector3D dampingConstant;
Vector3D posSpring;
Vector3D negSpring;
Vector3D rotationSpringConstant;
Vector3D rotationDampingConstant;
Vector3D rotationPosSpring;
Vector3D rotationNegSpring;
char eyeJoint[32];
char mechJoint[32];
Scalar collisionDamageMultiplier;
Scalar ballisticDamageMultiplier;
Scalar explosiveDamageMultiplier;
Scalar laserDamageMultiplier;
Scalar energyDamageMultiplier;
//
// The five response quads follow the multipliers in the stream
// (record size 0x21C -- binary-verified layout).
//
Gyroscope__DamageResponse collisionDamageResponse;
Gyroscope__DamageResponse ballisticDamageResponse;
Gyroscope__DamageResponse explosiveDamageResponse;
Gyroscope__DamageResponse laserDamageResponse;
Gyroscope__DamageResponse energyDamageResponse;
};
//###########################################################################
//############################## Gyroscope #############################
//###########################################################################
//
// The gyro drives the cockpit eye joint (view stabilisation) and the body
// lean. The tuning constants are read from the resource; the eye/body
// dynamics state (springs, forces, velocities, work matrices) is advanced by
// the per-frame GyroscopeSimulation and held in dynamicsState until that
// (staged) method is reconstructed with the named fields.
//
class Gyroscope:
public PowerWatcher
{
public:
static Derivation ClassDerivations;
static SharedData DefaultData;
typedef void
(Gyroscope::*Performance)(Scalar time_slice);
void
SetPerformance(Performance performance)
{
Check(this);
activePerformance = (Simulation::Performance)performance;
}
static Logical
TestClass(Mech &);
Logical
TestInstance() const;
void
ResetToInitialState();
void
GyroscopeSimulation(Scalar time_slice);
//
// The two integrators (called by the sim) and the two joint writes
// (called by the MECH master performance tail, after the gait --
// NOT by the sim; binary @0x4aaf74/83).
//
void
IntegrateEyeJoint(Scalar time_slice);
void
IntegrateBody(Scalar time_slice);
void
WriteEyeJoint();
void
WriteMechJoint();
//
// The hit-bounce feeds. ApplyDamageResponse is the damage hub's
// fan-out (every non-collision hit shakes the cockpit); the three
// primitive kicks are also fed directly by the collision CRUNCH.
//
void
ApplyDamageResponse(const Damage &damage);
void
ApplyDamageImpulse(Scalar x, Scalar y, Scalar z, Scalar magnitude);
void
ApplyDamageTorque(Scalar x, Scalar y, Scalar z, Scalar magnitude);
void
ApplyVerticalImpulse(Scalar pitch, Scalar magnitude);
public:
typedef Gyroscope__SubsystemResource SubsystemResource;
Gyroscope(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data = DefaultData
);
~Gyroscope();
protected:
Scalar exageration;
Scalar maxAnimationNoise;
Scalar minAnimationNoise;
Scalar rotationPerSecond;
Scalar percentageOnNormal;
Scalar percentageOnDestruction;
Scalar percentageOnDegradation;
Scalar percentageOnFailure;
Vector3D springConstant;
Vector3D dampingConstant;
Vector3D posSpring;
Vector3D negSpring;
Vector3D rotationSpringConstant;
Vector3D rotationDampingConstant;
Vector3D rotationPosSpring;
Vector3D rotationNegSpring;
Scalar damageMultiplier[5];
Gyroscope__DamageResponse
damageResponse[5];
//
// The eye TRANSLATION spring state (the steady eye offset + hit
// bounce) and the body ROTATION spring state (the hull tip). The
// clamps are COPIES of the spring pairs, taken at ctor time (the
// body pair scaled by 2).
//
Vector3D eyePosition, eyeVelocity, eyeForce, eyeWork;
Vector3D eyeClampUpper, eyeClampLower;
Vector3D bodyOrientation, bodyVelocity, bodyForce, bodyWork;
Vector3D bodyClampUpper, bodyClampLower;
//
// Sway (the animation-noise wander the sim slews between the
// powered/impaired percentages) and the placement scratch the
// damage fan-out rotates hit directions through.
//
//
// The mech's instability, republished here every master frame
// (binary gyro+0x3a8, written by the master perf tail). The gyro's
// own tip/sway machinery (@004b275c) reads it summed with +0x3b0
// against the +0x3bc threshold -- that consumer is a later brick;
// the publish lands now so the value is where the binary puts it.
//
public:
Scalar mechInstability;
protected:
Scalar swayAngle, swayBias, swayVelocity;
int swayActive;
Vector3D placeRot, placePos;
Scalar placeQuat[4];
Scalar workMatrix[12];
Scalar spare0;
Scalar *externalPitchPtr; // -> the torso twist (yaw-only hit
// frame); self-points at spare0
// until a torso binds it
Vector3D vibrationDirection; // the fixed up axis
Joint *eyeJointNode;
Joint *mechJointNode;
};
#endif