Files
TeslaRel410/restoration/source410/BT/MECH.HPP
T
CydandClaude Fable 5 14da9489c2 BT410 5.3.43: full pod rig -- geometry loads, ladder advances to the audio watchers
Under the documented rig (launch_pod.ps1 with the GL bridge up) the 'couldn't
load object' count went to ZERO.  .BGF loading was never a reconstruction
problem, only a missing bridge.

What blocks now is a series of authored AttributeWatchers: BTL4.RES binds
them BY NAME and the engine Fails outright on any that does not resolve, so
each is a name our subsystems must publish.  Five rungs climbed, each
run-verified: UnstablePercentage, SpeedEffect (Myomers table), AnimationState
+ CollisionState/CollisionNormal + ReduceButton (Mech), and the full Torso
table (all six authentic names mapped onto existing members).

The method that makes this cheap is banked: the shipped binary's string pool
carries each class's attribute names CONTIGUOUSLY in ID ORDER after the class
name, and in every case so far our member declarations sit in the same order
-- which confirms the layout and lets ids be pinned rather than guessed.
Intersecting those names with BTL4.RES gives the exact work list.

A THIRD miswired SharedData found on the way: Myomers carried Subsystem's
tables where it derives from PoweredSubsystem, the same defect as
MissileLauncher (5.3.33).  Worth a sweep across every subsystem.

Staged member TYPES are provisional and documented as such: AttributeWatcherOf
reads *(T*)attributePointer and the instantiation comes from the resource,
which the string pool does not reveal.  Nothing drives them yet, so settle the
types with the models that write them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 01:03:36 -05:00

577 lines
19 KiB
C++

//===========================================================================//
// File: mech.hpp //
// Project: BattleTech Brick: Entity Manager //
// Contents: Implementation details for the Mech entity //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#if !defined(MECH_HPP)
# define MECH_HPP
# if !defined(JMOVER_HPP)
# include <jmover.hpp>
# endif
# if !defined(RETICLE_HPP)
# include <reticle.hpp>
# endif
# if !defined(ALARM_HPP)
# include <alarm.hpp>
# endif
# if !defined(STATE_HPP)
# include <state.hpp>
# endif
# if !defined(CHAIN_HPP)
# include <chain.hpp>
# endif
# if !defined(AVERAGE_HPP)
# include <average.hpp>
# endif
# if !defined(CSTR_HPP)
# include <cstr.hpp>
# endif
# if !defined(SEQCTL_HPP)
# include <seqctl.hpp>
# endif
# if !defined(NAMEFILT_HPP)
# include <namefilt.hpp>
# endif
# if !defined(ROTATION_HPP)
# include <rotation.hpp>
# endif
//##################### Forward Class Declarations #######################
class Mech;
class Subsystem;
class SubsystemMessageManager;
class ControlsMapping;
class PlatformTool;
class MechControlsMapper;
class Mech__DamageZone;
class Joint;
class DamageLookupTable;
//###########################################################################
//######################### Mech Model Resource #########################
//###########################################################################
struct Mech__ModelResource:
public JointedMover::ModelResource
{
char animationPrefix[4];
Scalar maxAcceleration;
Scalar superStopAcceleration;
Scalar throttleAdjustment;
Scalar lookLeftAngle;
Scalar lookRightAngle;
Scalar lookFrontAngle;
Scalar lookBackAngle;
Scalar walkingTurnRate;
Scalar runningTurnRate;
Scalar reticleX;
Scalar reticleY;
Scalar relativeMechValue;
int deathEffectResourceID;
Scalar deathSplashDamage;
Scalar deathSplashRadius;
Scalar maxUnstableAcceleration;
Scalar unstableAccelerationEffect;
Scalar unstableGunTheEngineEffect;
Scalar unstableSuperStopEffect;
Scalar unstableHighVelocityEffect;
Scalar unstableStopedTurnEffect;
Scalar updatePositionDiffrence;
Scalar updateTurnVelocityDiffrence;
Scalar updateTurnDegreeDiffrence;
Scalar timeDelay;
Vector3D cameraOffset;
char shadowJointName[20];
};
//###########################################################################
//########################## Mech Make Message ##########################
//###########################################################################
class Mech__MakeMessage:
public JointedMover::MakeMessage
{
public:
char resourceNameA[20];
char resourceNameB[20];
char resourceNameC[20];
Mech__MakeMessage(
Receiver::MessageID message_ID,
size_t length,
const EntityID &entity_ID,
Entity::ClassID class_ID,
const EntityID &owner_ID,
ResourceDescription::ResourceID resource_ID,
LWord instance_flags,
const Origin &origin,
const Motion &velocity,
const Motion &acceleration,
const char *badge,
const char *color,
const char *patch
):
JointedMover::MakeMessage(
message_ID, length, entity_ID, class_ID, owner_ID,
resource_ID, instance_flags, origin, velocity, acceleration
)
{
Str_Copy(resourceNameA, badge, sizeof(resourceNameA));
Str_Copy(resourceNameB, color, sizeof(resourceNameB));
Str_Copy(resourceNameC, patch, sizeof(resourceNameC));
}
};
//###########################################################################
//############################## Mech ###############################
//###########################################################################
class Mech:
public JointedMover
{
//
// The per-zone damage code walks the roster / segment table / alarms
// directly (the 1995 arrangement, mirrored by the reconstruction).
//
friend class Mech__DamageZone;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation ClassDerivations;
static SharedData DefaultData;
static const HandlerEntry
MessageHandlerEntries[];
static MessageHandlerSet
MessageHandlers;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Attribute Support -- the ENTITY-level rows the cockpit binds without a
// subsystem prefix (the radar trio the map() primitive reads, the speed
// pair the speedometer arc reads, and the duck/crouch state selector).
// Chained onto JointedMover's index.
//
public:
enum {
RadarRangeAttributeID = JointedMover::NextAttributeID,
RadarLinearPositionAttributeID,
RadarAngularPositionAttributeID,
LinearSpeedAttributeID,
MaxRunSpeedAttributeID,
DuckStateAttributeID,
//
// The 3-D renderer reads this BY NAME: DPLRenderer::SetupCull
// (CODE/RP/MUNGA_L4/L4VIDEO.CPP:4990) fetches
// "EyepointRotation" off the viewpoint entity and, for a
// JointedMover, composes it with the siteeyepoint segment to
// build worldToEyeMatrix. Without it the engine Fails every
// frame. The member already existed -- only the publication
// was missing.
//
EyepointRotationAttributeID,
//
// Bound BY NAME by an authored AttributeWatcher (BTL4.RES); the
// shipped binary carries the same string in its pool.
//
UnstablePercentageAttributeID,
CollisionSpeedAttributeID,
DistanceToMissileAttributeID,
FootStepAttributeID,
IncomingLockAttributeID,
//
// The remaining names BTL4.RES binds watchers to. The two
// state indicators already existed as members -- only the
// publication was missing, same as EyepointRotation.
// AnimationState is bound 307 times in the resource: it is how
// the audio system follows the gait.
//
CollisionStateAttributeID,
CollisionNormalAttributeID,
AnimationStateAttributeID,
ReduceButtonAttributeID,
NextAttributeID
};
static const IndexEntry AttributePointers[];
static AttributeIndexSet AttributeIndex;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Class Support
//
public:
static Logical
TestClass(Mech &);
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
public:
typedef Mech__ModelResource ModelResource;
typedef Mech__MakeMessage MakeMessage;
typedef Mech__DamageZone DamageZone;
static Mech*
Make(MakeMessage *creation_message);
Mech(
MakeMessage *creation_message,
SharedData &shared_data = DefaultData
);
~Mech();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// App interface
//
public:
void
SetMappingSubsystem(Subsystem *mapper);
//
// Resolve a skeleton joint by name (the shared resolver the subsystems
// use to bind their animated joints -- Torso twist, etc.):
// GetSegment(name) -> segment jointIndex -> JointSubsystem::GetJoint.
//
Joint*
ResolveJoint(const char *joint_name);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Per-frame simulation (the mech's body Performance; installed by the ctor
// and dispatched each frame from Simulation::PerformAndWatch).
//
public:
typedef void
(Mech::*Performance)(Scalar time_slice);
void
SetPerformance(Performance performance)
{
Check(this);
activePerformance = (Simulation::Performance)performance;
}
void
Simulate(Scalar time_slice);
//
// The respawn heal-and-move (binary @0049fb74): reposition the SAME
// entity at the drop-zone origin, kill all motion, clear the death
// latch, heal every hull zone and sweep the roster through
// DeathReset. Shared by the initial drop-in and the respawn.
//
void
Reset(const Origin &origin, Logical full_reset);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Resource creation
//
public:
static void
CreateMakeMessage(
MakeMessage *creation_message,
NotationFile *model_file,
const ResourceDirectories *directories
);
static ResourceDescription::ResourceID
CreateModelResource(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories,
ModelResource *model = 0
);
static ResourceDescription::ResourceID
CreateSubsystemStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
);
static ResourceDescription::ResourceID
CreateDamageZoneStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
);
static ResourceDescription::ResourceID
CreateSkeletonStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
);
static ResourceDescription::ResourceID
CreateExplosionTableStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
);
typedef Logical (*FindNameFunction)(
const char *control_name, ControlsMapping *mapping);
static ResourceDescription::ResourceID
CreateControlMappingStream(
const char *mapping_name,
NotationFile *mapping_file,
FindNameFunction find_name,
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories,
PlatformTool *current_tool
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Subsystem roster accessors (the roster itself -- subsystemArray /
// subsystemCount -- lives in the base Entity)
//
public:
Subsystem*
GetGyroSubsystem() { Check(this); return gyroSubsystem; }
Subsystem*
GetTorsoSubsystem() { Check(this); return sinkSourceSubsystem; }
Subsystem*
GetHudSubsystem() { Check(this); return hudSubsystem; }
Subsystem*
GetSensorSubsystem() { Check(this); return sensorSubsystem; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Locomotion parameters (read by MechControlsMapper::InterpretControls to
// shape the demands, and by the drive in Simulate). reverseStrideLength is
// the top/run cycle speed the throttle scales (the naming is the 1995
// field's; LoadLocomotionClips measures it from the run clips), walkStride
// Length the walk speed. Turn rates are radians/sec.
//
public:
Scalar GetReverseStrideLength() const { Check(this); return reverseStrideLength; }
Scalar GetWalkStrideLength() const { Check(this); return walkStrideLength; }
Scalar GetForwardThrottleScale() const { Check(this); return forwardThrottleScale; }
void SetForwardThrottleScale(Scalar s){ Check(this); forwardThrottleScale = s; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Eyepoint / aim-ray composition. The pilot's torso-elevation aim (stick
// pitch) does NOT tilt any skeleton joint on this mech family -- it pitches
// the COCKPIT EYE and the weapon boresight (pixel-calibrated in the BT411
// reverse-engineering: "pitch does not work" turned out to mean nothing
// consumed Torso's currentElevation, not that a joint was un-animated).
// Composed each frame in Simulate; DPLEyeRenderable / the aim ray read it.
//
public:
const EulerAngles&
GetEyepointRotation() const { Check(this); return eyepointRotation; }
//
// The current target (the binary's mech target slot @0x388): read by
// the weapons' HasActiveTarget / UpdateTargeting and the fire path.
// The authentic writer is the reticle targeting step (mech4, the
// render wave); the dev harness sets it directly.
//
Entity*
GetTargetEntity() const { Check(this); return targetEntity; }
void
SetTargetEntity(Entity *target) { Check(this); targetEntity = target; }
//
// Damage entry (overrides the Entity handler): latches the attacker
// (mech+0x43c, read by the zone LOD router) and feeds the gyro
// cockpit bounce before chaining to the base zone routing. The
// cylinder unaimed-hit resolver (type-0x1d table) is a later wave.
//
void
TakeDamageMessageHandler(TakeDamageMessage *message);
//
// Damage-side death flag: the body graphic alarm (statusAlarm,
// binary @0x714) at level >= 9 = the death/fall state.
//
Logical
IsMechDestroyed() { Check(this); return statusAlarm.GetLevel() >= 9; }
//
// The live torso twist (the cylinder table's rotate-with-torso rows
// follow it). NULL-safe: 0 with no torso subsystem.
//
Scalar
CurrentTorsoTwist();
DamageLookupTable*
GetDamageLookupTable() const { Check(this); return damageLookupTable; }
//
// Look-state commit (called by the controls mapper on a look-button
// state change): re-aim the eyepoint from the model's authored look
// angles. look_state = MechControlsMapper::LookState.
//
void
CommitLookState(int look_state);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local Data
//
protected:
//
// Cached subsystem back-pointers (also held in the base roster).
//
Subsystem *sensorSubsystem;
Subsystem *gyroSubsystem;
Subsystem *sinkSourceSubsystem; // the real Torso
Subsystem *hudSubsystem;
SubsystemMessageManager *messageManager;
int weaponCount;
//
// Capability sub-rosters (Socket views onto the subsystem roster;
// populated per-frame -- phase 5).
//
ChainOf<Subsystem*> controllableSubsystems;
ChainOf<Subsystem*> watchedSubsystems;
ChainOf<Subsystem*> heatableSubsystems;
ChainOf<Subsystem*> weaponRoster;
ChainOf<Subsystem*> damageableSubsystems;
//
// Embedded status / animation / naming state.
//
NameFilter mechNameFilter;
AlarmIndicator masterAlarm;
AlarmIndicator heatAlarm;
AlarmIndicator stabilityAlarm;
AlarmIndicator statusAlarm;
Reticle targetReticle;
StateIndicator animationState;
StateIndicator replicantAnimationState;
StateIndicator collisionState;
SequenceController legAnimation;
SequenceController bodyAnimation;
AverageOf<Scalar> telemetryFilter[5];
CString resourceNameA;
CString resourceNameB;
CString resourceNameC;
//
// Locomotion state (Phase 5.3). Turn rates in rad/s, speeds/strides in
// world-units/s. reverseStrideLength = top (run) speed; walkStrideLength
// = walk speed; reverseSpeedMax = the low-speed turn-rate gate; forward
// ThrottleScale multiplies the forward demand; bodyTargetSpeed = the
// demanded speed; currentBodySpeed = the accel-tracked actual speed.
//
Scalar walkingTurnRate;
Scalar runningTurnRate;
Scalar reverseStrideLength;
Scalar walkStrideLength;
Scalar reverseSpeedMax;
Scalar forwardThrottleScale;
Scalar maxBodyAcceleration;
Scalar bodyTargetSpeed;
Scalar currentBodySpeed;
//
// Composed each frame in Simulate: the look-state eye component
// (lookPitch/lookYaw, set by CommitLookState from the authored look
// angles) plus the Torso elevation.
//
EulerAngles eyepointRotation;
Scalar lookPitch;
Scalar lookYaw;
Entity *targetEntity;
EntityID lastInflictingID; // binary mech+0x43c -- last
// attacker (zone LOD router)
Scalar lastInflictingDamage; // the killing-blow
// magnitude (kill score)
int deathTransitionDone; // the once-per-death latch
// (binary movementMode 2||9)
DamageLookupTable *damageLookupTable; // binary mech+0x444 -- the
// cylinder hit-location table
//
// Cockpit-published state (bound by name from L4GAUGE.CFG): the radar
// scale + the pointers the map gauge follows, and the duck selector.
// The position/angle pointers are the 1995 shape -- the gauge holds a
// pointer TO the pointer and re-reads the live transform every frame.
//
Scalar radarRange;
Point3D *radarLinearPosition;
Quaternion *radarAngularPosition;
int duckState;
//
// STAGED VALUE (the instability model is not reconstructed).
// The mech streams the authored unstable* effect constants in its
// resource (maxUnstableAcceleration and friends) but nothing yet
// computes how close the mech is to going over. It is PUBLISHED
// because an authored AttributeWatcher in BTL4.RES binds
// "UnstablePercentage" by name and the engine Fails outright when
// the name does not resolve (WATCHER.CPP:141). Holds 0 = stable
// until the model lands.
//
Scalar unstablePercentage;
//
// The AUDIO watcher set, staged alongside unstablePercentage.
// BTL4.RES binds exactly six attribute watchers by name --
// UnstablePercentage, CollisionSpeed, FootStep, IncomingLock,
// DistanceToMissile (Mech) and SpeedEffect (Myomers) -- and the
// engine Fails outright on any that does not resolve.
// These are the sound triggers: impact volume, footfalls, the
// missile-lock warning, the myomer whine.
//
// TYPES ARE PROVISIONAL. AttributeWatcherOf<T> reads
// *(T*)attributePointer (WATCHER.HPP:288) and the instantiation
// comes from the resource, which we cannot read off the string
// pool. Nothing drives these yet, so nothing ever changes and no
// watcher can fire -- settle the types WITH the models that write
// them (collision response, the gait FSM, the missile threat
// track), not before.
//
Scalar collisionSpeed;
Scalar distanceToMissile;
int footStep;
int incomingLock;
UnitVector collisionNormal; // staged with collisionSpeed
int reduceButton; // staged: cockpit button feed
//
// The model's authored look-view angles (rad; deg in the resource).
//
Scalar lookLeftAngle;
Scalar lookRightAngle;
Scalar lookFrontAngle;
Scalar lookBackAngle;
//
// Remaining per-frame targeting / animation state, advanced by the
// mech2/mech3/mech4 simulation. Reserved until that path is
// reconstructed with named fields.
//
int reservedState[191];
};
#endif