- dpl2d API fully recovered from the binary recorders (@487f34-488630): opcode model (points/lines/polyline/circle/color/width/matrix/push-pop), CallList = INLINE include (state persists to caller), centered coordinate frame (unit = half viewport height). game/reconstructed/dpl2d.cpp rework. - BTReticleRenderable ctor @004cc40c transcribed with the authentic calibration (originX .35, originY .25, scaleY .5, 0..1200m right range ladder, bottom heading tape, FUN_004cd938 tick ladders, lock rings, turn arrows); range caret slides from the live target range fed by the mech4 targeting step (BTSetHudTargetRange). - Weapon pips: the binary gate is IsDerivedFrom(0x511830 = MechWeapon::ClassDerivations) [T1: part_014.c:5386 hard-aborts on missing weapon attrs; part_012 counts + roster ORs capabilityFlags@+0x334] so ALL 7 BLH weapons register (3 lasers + 2 PPCs + 2 MissileLaunchers). Pip A (lit, authored PipColor) on TargetWithinRange, else dark ring B. - AddWeapon @004cdac0 store map corrected to the verified order (part_014.c:4827-4837); both state attrs are literally named "SimulationState" (strings @51d526/51d577) -> weapon simulationState. - Mech roster this[0x1ef] renamed poweredSubsystems -> weaponRoster (0x511830 is MechWeapon, not PoweredSubsystem=0x50f4bc); derivation-tag table added to context/decomp-reference.md. - Draw hook BTDrawReticle after the 3D scene, cockpit view only. Binary Execute @004cdcf0 is an un-exported gap -> Draw dynamics [T3], tracked in context/open-questions.md with the blx_cop canopy + PNAME pip meshes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
821 lines
40 KiB
C++
821 lines
40 KiB
C++
//===========================================================================//
|
|
// File: mech.hpp //
|
|
// Project: BattleTech Brick: Entity Manager //
|
|
// Contents: Mech -- the BattleMech player/AI entity (declaration) //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// --/--/95 ?? Initial coding. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
|
|
// PROPRIETARY AND CONFIDENTIAL //
|
|
//===========================================================================//
|
|
//
|
|
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C, BTL4OPT.EXE).
|
|
//
|
|
// The Mech class is the heart of the game and its implementation is split
|
|
// across mech.cpp .. mech4.cpp. This header declares the *whole* class so
|
|
// the recovered mech.cpp slice compiles; methods whose bodies live in the
|
|
// sibling translation units are marked [mech2] / [mech3] / [mech4].
|
|
//
|
|
// mech.cpp slice recovered here: 0x004a1232 .. 0x004a5027
|
|
// (bounded below by the inter-module gap 0x0049ffc8..0x004a1232 that
|
|
// separates it from btmssn.cpp, and above by mech2.cpp @0x004a5028.)
|
|
//
|
|
// Structural template: Red Planet's player vehicle entity VTV (RP/VTV.h),
|
|
// which -- like Mech -- derives from JointedMover and hosts a roster of
|
|
// streamed Subsystem objects. Surviving BT headers MECHDMG.HPP / MECHTECH.HPP
|
|
// supplied the DamageZone / MechTech shapes.
|
|
//
|
|
// Field offsets in comments are BYTE offsets into the shipped object
|
|
// (sizeof(Mech) == 0x854, see Mech::Make @004a2d48). The decomp addresses
|
|
// the object as int words, so this[0xNN] == byte offset 0xNN*4. Names are
|
|
// taken from VTV / the surviving BT headers where an analog exists and are
|
|
// inferred from usage otherwise (flagged TODO / "best-effort").
|
|
//
|
|
|
|
#if !defined(MECH_HPP)
|
|
# define MECH_HPP
|
|
|
|
#if !defined(JMOVER_HPP)
|
|
# include <jmover.hpp> // JointedMover -- engine base (MUNGA)
|
|
#endif
|
|
#if !defined(CONTROLS_HPP)
|
|
# include <controls.hpp>
|
|
#endif
|
|
#if !defined(DAMAGE_HPP)
|
|
# include <damage.hpp>
|
|
#endif
|
|
#if !defined(ALARM_HPP)
|
|
# include <alarm.hpp> // GaugeAlarm
|
|
#endif
|
|
#include <tool.hpp> // PlatformTool
|
|
#include <affnmtrx.hpp> // AffineMatrix
|
|
#include <average.hpp> // AverageOf<T>
|
|
#include <scalar.hpp> // Scalar
|
|
|
|
#include "mechrecon.hpp" // reconstruction shim (proxies, artifact globals, type aliases)
|
|
|
|
//##################### Reconstruction type aliases #####################
|
|
// Names inferred by the decompiler mapped to the real engine types.
|
|
// (The actual aliases are now defined in mechrecon.hpp under the same guard.)
|
|
#if !defined(BT_RECON_TYPE_ALIASES)
|
|
# define BT_RECON_TYPE_ALIASES
|
|
typedef ReconMatrix Matrix34;
|
|
typedef ReconAlarm AlarmIndicator;
|
|
typedef ReconFiltered FilteredScalar;
|
|
#endif
|
|
|
|
//##################### Forward Class Declarations #######################
|
|
class Mech;
|
|
class Subsystem;
|
|
class MechSubsystem;
|
|
class MechWeapon;
|
|
class MechControlsMapper;
|
|
class MechTech;
|
|
class Mech__DamageZone; // see MECHDMG.HPP
|
|
class HitQuery; // weapon sweep query (mech4)
|
|
class Joint; // engine skeleton node (JOINT.h); ResolveJoint result
|
|
|
|
//
|
|
// Streamed-subsystem resource blob handed to each <Class>::CreateStreamedSubsystem.
|
|
// The recovered factory only passes it through, so an opaque alias suffices.
|
|
//
|
|
typedef void SubsystemResourceBlob;
|
|
|
|
//
|
|
// Per-shot impact descriptor consumed by Mech::ResolveWeaponImpact (mech4).
|
|
// Fields are those the recovered body reads.
|
|
//
|
|
struct ShotDescriptor
|
|
{
|
|
Vector3D dir; // +0x14 shot direction
|
|
Scalar damage; // computed impact damage
|
|
Scalar fieldAt(int) const { return Scalar(0); } // raw +0x240/0x244/0x248 reads
|
|
};
|
|
|
|
//###########################################################################
|
|
//######################### Mech::ModelResource #########################
|
|
//###########################################################################
|
|
//
|
|
// Parsed by Mech::CreateModelResource @004a2da8 from the "gamedata" notation
|
|
// block. Layout mirrors the float[] the decomp fills (pfVar3, a 200-byte /
|
|
// 0x32-word record). Indices in comments are the word index used by the
|
|
// parser (e.g. pfVar3+0x11 == MaxAcceleration).
|
|
//
|
|
struct Mech__ModelResource:
|
|
public JointedMover::ModelResource
|
|
{
|
|
char animationPrefix[12]; // +0x10 "AnimationPrefix" (3-letter)
|
|
Scalar maxAcceleration; // +0x11 "MaxAcceleration"
|
|
Scalar superStopAcceleration; // +0x12 "SuperStopAcceleration"
|
|
Scalar throttleAdjustment; // +0x13 "ThrottleAdjustment"
|
|
Scalar lookLeftAngle; // +0x14 "LookLeftAngle"
|
|
Scalar lookRightAngle; // +0x15 "LookRightAngle"
|
|
Scalar lookFrontAngle; // +0x16 "LookFrontAngle"
|
|
Scalar lookBackAngle; // +0x17 "LookBackAngle"
|
|
Scalar walkingTurnRate; // +0x18 "WalkingTurnRate"
|
|
Scalar runningTurnRate; // +0x19 "RunningTurnRate"
|
|
char skeletonName[64]; // +0x1a (.skl spec, 0x10 words)
|
|
Scalar deathEffectResourceID; // +0x1d resolved "DeathEffect" id
|
|
Scalar deathSplashDamage; // +0x1e "DeathSplashDamage"
|
|
Scalar deathSplashRadius; // +0x1f "DeathSplashRadius"
|
|
Scalar maxUnstableAcceleration; // +0x20 "MaxUnstableAcceleration"
|
|
Scalar unstableAccelerationEffect; // +0x21 "UnstableAccelerationEffect"
|
|
Scalar unstableGunTheEngineEffect; // +0x22 "UnstableGunTheEngineEffect"
|
|
Scalar unstableSuperStopEffect; // +0x23 "UnstableSuperStopEffect"
|
|
Scalar unstableHighVelocityEffect; // +0x24 "UnstableHighVelocityEffect"
|
|
Scalar unstableStopedTurnEffect; // +0x25 "UnstableStopedTurnEffect"
|
|
Scalar updatePositionDiffrence; // +0x26 "UpdatePositionDiffrence"
|
|
Scalar updateTurnVelocityDiffrence;// +0x27 "UpdateTurnVelocityDiffrence"
|
|
Scalar updateTurnDegreeDiffrence; // +0x28 "UpdateTurnDegreeDiffrence"
|
|
Scalar timeDelay; // +0x29 "TimeDelay"
|
|
Vector3D cameraOffset; // +0x2a "CameraOffset"
|
|
char shadowJointName[20]; // +0x2d "ShadowJointName" (<20 chars)
|
|
Scalar relativeMechValue; // +0x1c "RelativeMechValue"
|
|
|
|
// --- raw recovered fields read by the ctor (Pass 2, model record) -----
|
|
// Offsets are the decomp's record offsets; names are best-effort.
|
|
char *base; // record base pointer (model->base + 0xa8)
|
|
int deathEffectField; // resolved death-effect handle source
|
|
Scalar field48, field4c, field60, field64;
|
|
int field68, field6c;
|
|
Scalar field70, field74, field78, field7c;
|
|
Scalar field80, field84, field88, field8c, field90, field94, field98, field9c;
|
|
Scalar fieldA0;
|
|
int fieldA4;
|
|
int classID; // stamped by CreateModelResourceStub (0xBB9)
|
|
};
|
|
|
|
//###########################################################################
|
|
//######################### Mech::UpdateRecord ##########################
|
|
//###########################################################################
|
|
//
|
|
// The network/replay update packet applied by Mech::ReadUpdateRecord
|
|
// @004a1232. The switch is keyed on a 16-bit record-type field at +6 of the
|
|
// message (cases 0,2..8). Field layout is best-effort -- only the offsets
|
|
// touched by the decomp are named.
|
|
//
|
|
struct Mech__UpdateRecord:
|
|
public JointedMover::UpdateRecord
|
|
{
|
|
// case 0 (full pose) : position (+0x10), orientation (+0x74)
|
|
// case 2/3 (alarm/state) : alarm levels, score/heat state
|
|
// case 4 (snap+time) : pose + server timestamp re-sync
|
|
// case 5/6/7 (subsystem) : per-subsystem alarm + flags
|
|
// case 8 (single scalar) : misc field [0xfd]
|
|
int recordType; // *(u16*)(msg+6) -- switch selector
|
|
int sequence; // trailing replication sequence word
|
|
int field4, field5, field6, field7, field9, fieldA; // per-case payload words
|
|
Vector3D position; // case 0/4 world position
|
|
EulerAngles orientation; // case 0/4 net orientation
|
|
};
|
|
|
|
//###########################################################################
|
|
//########################## Mech::MakeMessage ##########################
|
|
//###########################################################################
|
|
//
|
|
// Creation message. Carries the three resource-name strings copied into the
|
|
// badge/color/insignia slots (this[0x211..0x213]) at +0x7c / +0x90 / +0xa4.
|
|
//
|
|
class Mech__MakeMessage:
|
|
public JointedMover::MakeMessage
|
|
{
|
|
public:
|
|
char resourceNameA[20]; // +0x7c -> this[0x211] (vehicle badge)
|
|
char resourceNameB[20]; // +0x90 -> this[0x212] (vehicle color)
|
|
char resourceNameC[20]; // +0xa4 -> this[0x213] (vehicle patch / insignia)
|
|
|
|
//
|
|
// Construction message, mirrored on VTV__MakeMessage (RP/VTV.h): the
|
|
// entity_ID form carrying the three resource-name strings copied into
|
|
// the badge/color/patch slots. Built by BTPlayer::CreatePlayerVehicle.
|
|
//
|
|
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 ###############################
|
|
//###########################################################################
|
|
//
|
|
// The BattleMech entity. vtable @0050cfa8 (16 slots); slot 0 == ~Mech
|
|
// @004a452c. Engine-base slots (0x41xxxx / 0x42xxxx) are inherited from
|
|
// JointedMover/Mover/Entity and are NOT redefined here. In-module overrides:
|
|
// slot 0 (0050cfa8) ~Mech @004a452c [mech.cpp]
|
|
// slot 6 (0050cfc0) ReadUpdateRecord @004a122c [mech.cpp]
|
|
// slot 7 (0050cfc4) (pose write/simulate)@004a0c2c [mech.cpp gap]
|
|
// slot 15 (0050cfe4) (state/print) @004abb40 [mech3/4]
|
|
//
|
|
class Mech:
|
|
public JointedMover
|
|
{
|
|
friend class Mech__DamageZone; // per-zone damage code reads the roster / segment table
|
|
friend struct MechBaseLayoutCheck; // base-region layout locks (mech4.cpp; P3 STEP-6 audit)
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data Support
|
|
//
|
|
public:
|
|
static Derivation ClassDerivations; // @0050bdb4
|
|
static SharedData DefaultData; // @0050bde4
|
|
|
|
enum { MechClassID = 0xBB9 }; // stamped by CreateModelResource
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Subsystem roster -- streamed-class registry
|
|
//
|
|
// The constructor (@004a1674) walks the model's segment table and
|
|
// instantiates one Subsystem per segment, dispatching on the segment's
|
|
// streamed ClassID. ClassIDs confirmed against CLASSMAP.md are named;
|
|
// the rest are best-effort from the factory address + object size.
|
|
//
|
|
public:
|
|
enum SubsystemClassID {
|
|
CockpitClassID = 0xBBD, // ctor @004ae568 (size 0x230)
|
|
SensorClassID = 0xBBE, // ctor @004ae8d0 -> this[0x1f7]
|
|
CondenserClassID = 0xBC0, // ctor @004af408 (size 0x230)
|
|
GeneratorClassID = 0xBC1, // ctor @004b225c (CLASSMAP)
|
|
PoweredSubsystemClassID = 0xBC2, // ctor @004b0f74 (CLASSMAP)
|
|
MyomersClassID = 0xBC3, // ctor @004b1d18 (CLASSMAP)
|
|
GyroClassID = 0xBC4, // ctor @004b3778 -> this[0x14a]
|
|
SinkSourceClassID = 0xBC5, // ctor @004b6b0c -> this[0x10e]
|
|
ActuatorClassID = 0xBC6, // ctor @004b8fec
|
|
WeaponEmitterClassID = 0xBC8, // ctor @004bb120 (+weapon count)
|
|
JumpJetClassID = 0xBCB, // ctor @004bd5c4
|
|
MechWeaponClassID = 0xBCD, // ctor @004bc3fc (CLASSMAP)
|
|
MissileWeaponClassID = 0xBCE, // ctor @004bdcb4 (+weapon count)
|
|
BallisticWeaponClassID = 0xBD0, // ctor @004bcff0 (+weapon count)
|
|
MechControlsMapperID = 0xBD3, // ctor @0049bca4 -> this[0x10d]
|
|
GaussWeaponClassID = 0xBD4, // ctor @004bb888 (+weapon count)
|
|
MechTechClassID = 0xBD6, // ctor @004b7f94 -> this[0x16d]
|
|
LegSubsystemClassID = 0xBD8, // ctor @004b84dc
|
|
HeatableClassID = 0xBDC, // ctor @004ad228 (heat.cpp family; exact class TBD)
|
|
DisplayClassID = 0xBDE // ctor @004b8718
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Test Class Support
|
|
//
|
|
public:
|
|
Logical
|
|
TestInstance() const; // @004a4c7c
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// App / cockpit interface (called by the BT_L4 layer: btl4app/mppr/pb).
|
|
// Declarations only -- definitions live in mech.cpp (factory pass / bring-up).
|
|
//
|
|
public:
|
|
enum ResetMode { MissionReviewReset = 0 }; // reset-mode selector (Reset arg, value 0)
|
|
|
|
void SetMappingSubsystem(Subsystem *mapper); // btl4app.cpp:567
|
|
Logical GetMissionReviewMode(); // reads this+0x414 (btl4mppr.cpp:366)
|
|
void SetTargetRange(Scalar range); // writes this+0x404 (btl4mppr.cpp:407)
|
|
void Reset(const Origin &origin, int mode); // btl4pb.cpp:555 (FUN_0049fb74)
|
|
|
|
//~~~ Skeleton joint access (shared by Torso, Gyroscope) ~~~~~~~~~~~~~~~~
|
|
// Resolve a named skeleton node to its live Joint* via the PUBLIC segment
|
|
// path (GetSegment -> segment joint index -> JointSubsystem::GetJoint);
|
|
// the protected EntitySegment::jointPointer is never read. The Torso ctor
|
|
// (@004b6b0c) and Gyroscope ctor (@004b3778) inline this exact sequence
|
|
// (binary FUN_00424b60); hoisted here as the single reuse point.
|
|
Joint* ResolveJoint(const char *joint_name);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction
|
|
//
|
|
public:
|
|
typedef Mech__ModelResource ModelResource;
|
|
typedef Mech__UpdateRecord UpdateRecord;
|
|
typedef Mech__MakeMessage MakeMessage;
|
|
typedef Mech__DamageZone DamageZone;
|
|
|
|
static Mech*
|
|
Make(MakeMessage *creation_message); // @004a2d48
|
|
|
|
Mech(
|
|
MakeMessage *creation_message,
|
|
SharedData &shared_data = DefaultData
|
|
); // @004a1674
|
|
~Mech(); // @004a452c (vtable slot 0)
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Resource authoring (offline tools)
|
|
//
|
|
public:
|
|
static ResourceDescription::ResourceID
|
|
CreateModelResource( // @004a2da8
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories,
|
|
ModelResource *model = 0
|
|
);
|
|
|
|
static Logical
|
|
CreateModelResourceStub( // @004a2d78 (best-effort)
|
|
ModelResource *model
|
|
); // stamps model->classID = MechClassID
|
|
|
|
typedef Logical (*FindNameFunction)(
|
|
const char *control_name, ControlsMapping *mapping);
|
|
|
|
static ResourceDescription::ResourceID
|
|
CreateControlMappingStream( // @004a3794
|
|
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
|
|
);
|
|
|
|
static ResourceDescription::ResourceID
|
|
CreateDamageZoneStream( // @004a474c
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Simulation / replication
|
|
//
|
|
protected:
|
|
void
|
|
ReadUpdateRecord( // @004a1232 (vtable slot 6)
|
|
Simulation::UpdateRecord *message
|
|
);
|
|
// WriteUpdateRecord / per-frame Simulate live in the uncaptured
|
|
// mech.cpp gap (slot 7 @004a0c2c) and mech2.cpp. [mech2]
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Instance-flag helper
|
|
//
|
|
protected:
|
|
void
|
|
SetInstanceFlags(Word flags); // @004a4c54
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Subsystem roster (host pattern -- cf. VTV / CLASSMAP shared base)
|
|
//
|
|
// this[0x47] segmentCount / this[0x48] segment[] (JointedMover base)
|
|
// this[0x49] subsystemCount / this[0x4a] subsystem[] (Mech-owned table)
|
|
// The capability sub-rosters below are ChainOf<> views built in the ctor
|
|
// by IsDerivedFrom() tests against the streamed subsystem classes.
|
|
//
|
|
protected:
|
|
// NOTE: subsystemCount (this[0x49]) and the subsystem table (this[0x4a])
|
|
// are the INHERITED Entity base members `subsystemCount` / `subsystemArray`
|
|
// (Entity reads them in GetSimulation/GetSubsystem). They are NOT
|
|
// re-declared here -- doing so shadowed the base fields, so the engine's
|
|
// control-mapping / per-frame code never saw the streamed roster. The
|
|
// ctor streams into the inherited subsystemArray/subsystemCount directly.
|
|
|
|
// NOTE: damageZoneCount (this[0x47]) and the zone table (this[0x48]) are the
|
|
// INHERITED Entity base members `damageZoneCount` / `damageZones` -- the engine
|
|
// Entity ctor (ENTITY.cpp:961-987 == binary FUN_0041ff38) reads the count from
|
|
// the type-0x14 resource and allocates damageZones[count] (entries left
|
|
// uninitialised); the Mech ctor populates THAT inherited array. They are NOT
|
|
// re-declared here -- doing so shadowed the base fields (count read 0xCDCDCDCD,
|
|
// the populate loop never ran, and the engine damage router read the empty
|
|
// base array). Same lesson as subsystemCount/subsystemArray above. Use Zone(i)
|
|
// for typed (Mech__DamageZone*) access to the engine-stored DamageZone* entries.
|
|
|
|
ReconChain<Subsystem*> controllableSubsystems; // @0x65c this[0x197] (FUN_00427768)
|
|
ReconChain<Subsystem*> watchedSubsystems; // @0x6bc this[0x1af] (FUN_00427768)
|
|
ReconChain<Subsystem*> heatableSubsystems; // @0x7ac this[0x1eb] (class 0x51155c)
|
|
ReconChain<Subsystem*> weaponRoster; // @0x7bc this[0x1ef] (class 0x511830 =
|
|
// MechWeapon::ClassDerivations -- the
|
|
// reticle AddWeapon loop @part_014.c:5386
|
|
// asserts weapon attrs on every member.
|
|
// Was mislabeled "poweredSubsystems";
|
|
// PoweredSubsystem is 0x50f4bc.)
|
|
ReconChain<Subsystem*> damageableSubsystems; // @0x7cc this[499] (class 0x50e4fc)
|
|
|
|
int weaponCount; // @0x448 this[0x112]
|
|
Subsystem *sensorSubsystem; // @0x7dc this[0x1f7] (0xBBE)
|
|
Subsystem *gyroSubsystem; // @0x528 this[0x14a] (0xBC4)
|
|
public:
|
|
// Cache accessors for the controls mapper (@004afd10 reads mech+0x438/+0x5b4).
|
|
Subsystem *GetTorsoSubsystem() { return sinkSourceSubsystem; }
|
|
Subsystem *GetHudSubsystem() { return hudSubsystem; }
|
|
// Reachable horizontal firing half-arc (radians) the mech's torso can bring
|
|
// its guns to bear -- the wider torso twist limit, or 0 for a fixed torso.
|
|
// Defined in mechmppr.cpp (which knows the full Torso type). Used by the
|
|
// weapon fire path (mech4.cpp) to derive the real, per-mech firing arc.
|
|
Scalar GetHorizontalFiringReach();
|
|
protected:
|
|
Subsystem *sinkSourceSubsystem; // @0x438 this[0x10e] (0xBC5 -> real Torso)
|
|
// The @0x5b4 cache holds the HUD, NOT the MechTech: raw factory (part_012.c:
|
|
// 10155-10164) case 0xbd6 (alloc 0x2a4, HUD ctor @004b7f94) writes param_1[0x16d];
|
|
// case 0xbdc (MechTech, alloc 0x104) stores NO cache. Also required by the
|
|
// controls mapper (@004afd10 writes cockpit+0x28c -- past MechTech's alloc,
|
|
// inside the HUD's). Was misnamed mechTechSubsystem.
|
|
Subsystem *hudSubsystem; // @0x5b4 this[0x16d] (0xBD6 -> real HUD)
|
|
MechControlsMapper *controlsMapper; // @0x434 this[0x10d] (0xBD3)
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Pose / kinematics (subset touched by the recovered slice)
|
|
//
|
|
protected:
|
|
Matrix34 orientation; // @0xd0 this[0x34] (FUN_0040ab44)
|
|
Quaternion bodyRotation; // @0x100 this[0x40]
|
|
BTVal torsoRotation; // @0x130 this[0x4b] (proxy: .Mul)
|
|
Vector3D worldPosition; // @0x138 this[0x4e]
|
|
BTVal turretBase; // @0x1c4 this[0x71] (proxy: .SetIdentity)
|
|
Quaternion headPitch; // @0x1cc this[0x73] (look)
|
|
Quaternion torsoTwist; // @0x1dc this[0x77]
|
|
Vector3D legAngle; // @0x1f4 this[0x7d]
|
|
Vector3D hipAngle; // @0x200 this[0x80]
|
|
BTVal torsoAimCurrent; // @0x298 this[0xa6] (proxy: .Mul/.SetIdentity)
|
|
BTVal torsoAimTarget; // @0x2c8 this[0xb2] (proxy: .SetIdentity)
|
|
EulerAngles netOrientation; // @0x2d4 this[0xb5] (from update record)
|
|
// SHADOW JOINT (task #20): the model record's ShadowJointName (record+0xB4,
|
|
// 'jointshadow' on the BLH) resolved to the live skeleton Joint that tilts
|
|
// the flat *_tshd shadow proxy to the terrain angle. Binary: part_012.c:
|
|
// 10285 (record+0xb4 -> CString -> GetSegment -> GetJointIndex -> GetJoint
|
|
// -> this[0x10b] @0x42c). Accessed BY NAME only; declared after the locked
|
|
// fields so it does not shift them.
|
|
public:
|
|
Joint *shadowJointNode; // binary @0x42c this[0x10b]
|
|
void UpdateShadowJoint(const Vector3D &ground_normal);
|
|
// AUTHENTIC GROUND MODEL ctor products (task #15, ground-model-decode;
|
|
// binary part_012.c:9938-9940 + 9974-9975). By-name access only; declared
|
|
// after the layout-locked fields so nothing shifts.
|
|
Scalar standingTemplateMaxY; // binary @0x518 collisionTemplate->maxY at ctor
|
|
Scalar duckedTemplateMaxY; // binary @0x51c 0.6 x standing (duck preset)
|
|
Scalar templateBottomLift; // binary @0x4b8 0.05 x (volume maxX-minX)
|
|
protected:
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Alarms / indicators
|
|
//
|
|
protected:
|
|
AlarmIndicator masterAlarm; // @0x39c this[0xe7] (33 levels)
|
|
AlarmIndicator heatAlarm; // @0x450 this[0x114] (3 levels)
|
|
AlarmIndicator stabilityAlarm; // @0x4c4 this[0x131] (2 levels)
|
|
AlarmIndicator statusAlarm; // @0x714 this[0x1c5] (33 levels)
|
|
|
|
FilteredScalar telemetryFilter[5]; // @0x7e0 this[0x1f8..0x204] (15-sample)
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Misc recovered state (names best-effort)
|
|
//
|
|
protected:
|
|
BTVal mechName; // @0x360 this[0xd8] (proxy: .Copy)
|
|
Scalar maxSpeed; // @0x400 this[0x100] = FLT_MAX
|
|
int stateFlags; // @0x410 this[0x104]
|
|
int throttleState; // @0x4a4 this[0x129] = 2 (initial)
|
|
int heatLevel; // @0x518 this[0x146]
|
|
int heatCapacity; // @0x51c this[0x147] = 0.6 * heatLevel
|
|
Time creationTime; // @0x778 this[0x1de]
|
|
// Cylinder hit-location table (STEP 6) -- DamageLookupTable* cached by the
|
|
// ctor from the type-0x1d resource (FUN_0049ea48 = the table ctor), read by
|
|
// Mech::TakeDamageMessageHandler to resolve an unaimed (-1) hit's zone from
|
|
// its impact point. MUST be a real named member: the old home Wword(0x111)
|
|
// is the recon ABSORBER bank (BTVal stores nothing, reads 0) -> the cached
|
|
// table silently vanished and every unaimed hit no-op'd. (Was mislabeled
|
|
// "ammoExpended"; binary slot this[0x111] is this pointer.)
|
|
int damageLookupTable; // @0x444 this[0x111] (FUN_0049ea48)
|
|
int deathHandler; // @0x850 this[0x214] (FUN_0042a984)
|
|
// Wreck-smoke cadence (port addition, [T3]): re-fires the death/rubble
|
|
// smoke plume (psfx 1, DDTHSMK) every 10s (its authored emission window)
|
|
// while the mech is a destroyed wreck, so a dead mech keeps smoking.
|
|
Scalar wreckSmokeTimer;
|
|
// The cockpit eye-slew angles (the "EyepointRotation" attribute the
|
|
// binary's DPLEyeRenderable composes into the view every frame). Was
|
|
// bound to the shared junk attrPad -> the cockpit camera got rotated by
|
|
// garbage (the canted-horizon / black-screen views). Zero until the
|
|
// eye-slew system (HUD freeAimSlew / gyro eye chain) writes it.
|
|
EulerAngles eyepointRotation;
|
|
|
|
// Three ref-counted creation-name objects (badge/color/insignia).
|
|
void *resourceNameA; // @0x844 this[0x211]
|
|
void *resourceNameB; // @0x848 this[0x212]
|
|
void *resourceNameC; // @0x84c this[0x213]
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Reconstruction additions -- members & methods whose bodies live in the
|
|
// mech2 / mech3 / mech4 slices (declared here so the whole class is visible).
|
|
// Offsets cite the mech2/mech3 offset maps; types inferred from usage.
|
|
//
|
|
public:
|
|
static Receiver::MessageHandlerSet MessageHandlers; // SharedData ctor operand
|
|
// Mech's OWN handler table (overrides Entity's TakeDamage with the
|
|
// cylinder hit-location resolver); chained to Entity::GetMessageHandlers().
|
|
static const Receiver::HandlerEntry MessageHandlerEntries[];
|
|
enum { StateCount = 0x21 };
|
|
|
|
//
|
|
// Attribute Support -- the named attributes the cockpit gauges bind to.
|
|
// The config (content/GAUGE/L4GAUGE.CFG) binds the bare names LinearSpeed /
|
|
// MaxRunSpeed / DuckState / RadarRange / RadarLinearPosition /
|
|
// RadarAngularPosition, resolved by Simulation::GetAttributePointer ->
|
|
// activeAttributeIndex->Find(name). The reconstruction shipped an EMPTY,
|
|
// unchained Mech::AttributeIndex, so EVERY bare Mech gauge (and even the
|
|
// inherited attributes) resolved to NullAttribute.
|
|
//
|
|
// The full binary id set (0x15..0x38, table VA 0x50be84) is declared so the
|
|
// published table can be extended without renumbering. The chain starts at
|
|
// JointedMover::NextAttributeID (0x15); ids MUST stay contiguous, because the
|
|
// built index is a dense array sized to max(id) and AttributeIndexSet::Find
|
|
// strcmps EVERY slot -- an unfilled slot holds a garbage entryName -> AV.
|
|
//
|
|
enum {
|
|
MaxAccelerationAttributeID = JointedMover::NextAttributeID, // 0x15
|
|
CollisionStateAttributeID, // 0x16
|
|
CollisionNormalAttributeID, // 0x17
|
|
CollisionSpeedAttributeID, // 0x18
|
|
CollisionMaterialTypeAttributeID, // 0x19
|
|
CurrentSpeedAttributeID, // 0x1a
|
|
MaxRunSpeedAttributeID, // 0x1b
|
|
EyepointRotationAttributeID, // 0x1c
|
|
TargetReticleAttributeID, // 0x1d
|
|
FootStepAttributeID, // 0x1e
|
|
AnimationStateAttributeID, // 0x1f
|
|
ReplicantAnimationStateAttributeID, // 0x20
|
|
LinearSpeedAttributeID, // 0x21
|
|
ClimbRateAttributeID, // 0x22
|
|
AccelerationLastFrameAttributeID, // 0x23
|
|
AngularSpeedAttributeID, // 0x24
|
|
ArmorDamageLevelAttributeID, // 0x25
|
|
SubsystemDamageLevelAttributeID, // 0x26
|
|
MyomerDamageLevelAttributeID, // 0x27
|
|
TestButton1AttributeID, // 0x28
|
|
TestButton2AttributeID, // 0x29
|
|
TestButton3AttributeID, // 0x2a
|
|
TestButton4AttributeID, // 0x2b
|
|
TestButton5AttributeID, // 0x2c
|
|
TestButton6AttributeID, // 0x2d
|
|
ReduceButtonAttributeID, // 0x2e
|
|
RadarRangeAttributeID, // 0x2f
|
|
RadarLinearPositionAttributeID, // 0x30
|
|
RadarAngularPositionAttributeID, // 0x31
|
|
RearFiringAttributeID, // 0x32
|
|
RequestDuckAnimationAttributeID, // 0x33
|
|
UnstablePercentageAttributeID, // 0x34
|
|
SuperStopAttributeID, // 0x35
|
|
IncomingLockAttributeID, // 0x36
|
|
DuckStateAttributeID, // 0x37
|
|
DistanceToMissileAttributeID, // 0x38
|
|
NextAttributeID // 0x39
|
|
};
|
|
static const IndexEntry AttributePointers[];
|
|
static AttributeIndexSet& GetAttributeIndex();
|
|
|
|
// Cockpit-gauge attribute backing members (APPENDED to Mech's own region --
|
|
// never at a locked base/gait offset). attrPad is a shared read-only 0 for
|
|
// the dense-prefix ids the BLH cockpit does not bind; linearSpeed is the live
|
|
// forward ground speed the speed readout / arc / radar consume (binary @0x81c).
|
|
Scalar attrPad;
|
|
Scalar linearSpeed;
|
|
// Radar/map gauge attributes (binary @0x404/0x408/0x40c/0x3f8). The map
|
|
// widget reads position/angle as POINTERS into the mech's live origin, so
|
|
// radarLinearPosition/radarAngularPosition point at localOrigin.{linear,
|
|
// angular}Position (set in the ctor); radarRange is the radar scale.
|
|
Scalar radarRange; // 0x2f RadarRange (scale/max)
|
|
Point3D *radarLinearPosition; // 0x30 RadarLinearPosition
|
|
Quaternion *radarAngularPosition; // 0x31 RadarAngularPosition
|
|
int duckState; // 0x37 DuckState (crouch posture)
|
|
|
|
// --- raw / engine-shim members touched by the ctor slice -------------
|
|
void *vtable; // installed Mech vtable ptr
|
|
Word instanceFlags; // entity instance flags (this+0x18 region)
|
|
ReconFiltered mechNameFilter; // @0xdb name filter
|
|
ReconSlot voltageBus; // resolve-able power-bus slot
|
|
void *controlSource; // @0x128 controls subsystem handle
|
|
|
|
// --- locomotion / gait animation state (mech2/mech3 maps) ------------
|
|
Word actionRequestFlags; // @0x18 pending action bits
|
|
int movementMode; // @0x40 gait/death selector
|
|
int movementFlags;
|
|
int motionEventArmed; // @0x5a4
|
|
int motionEventPending;
|
|
int airborneSelect;
|
|
Scalar forwardCycleRate; // @0x344
|
|
Scalar legCycleSpeed; // @0x348
|
|
Scalar reverseStrideLength; // @0x34c
|
|
Scalar gimpStrideLength; // @0x350
|
|
AlarmIndicator legStateAlarm; // @0x39c
|
|
int legAnimationState; // @0x3b0
|
|
Scalar gimpSpeedMax; // @0x52c
|
|
Scalar standSpeed; // @0x530
|
|
Scalar walkStrideLength; // @0x534
|
|
Scalar reverseSpeedMax; // @0x538
|
|
Scalar jumpRunSpeedMax; // @0x53c
|
|
Scalar jumpWalkSpeedMax; // @0x540
|
|
Scalar jumpRunStrideLength; // @0x544
|
|
Scalar jumpWalkStrideLength; // @0x548
|
|
Scalar groundCycleRate; // @0x5b8
|
|
Scalar airborneCycleRate; // @0x5bc
|
|
// Forward-throttle scale read by the controls mapper (@004afd10:
|
|
// speedDemand = topSpeed@0x34c * throttle * THIS). No writer found in the
|
|
// decomp windows (likely model/status-driven); ctor inits 1.0 (neutral).
|
|
Scalar forwardThrottleScale; // @0x5c0
|
|
int clipLoadGuard; // @0x5c4
|
|
Scalar globalTimeScale; // @0x5a8
|
|
Scalar idleStrideScale; // @0x5ac
|
|
Scalar gimpCycleRate; // @0x5b0
|
|
int jumpCapable; // @0x580
|
|
int hasReverseGimpSet; // @0x584
|
|
int hasCrashSet; // @0x588
|
|
int deathAnimationLatched; // @0x650
|
|
int legResetLatch; // @0x654
|
|
int bodyResetLatch; // @0x658
|
|
ReconSeq legAnimation; // @0x65c channel-A controller
|
|
ReconSeq bodyAnimation; // @0x6bc channel-B controller
|
|
AlarmIndicator bodyStateAlarm; // @0x714
|
|
int bodyAnimationState; // @0x728
|
|
Scalar bodyTargetSpeed; // @0x6b4
|
|
Scalar bodyCycleSpeed; // @0x6b8
|
|
Scalar reverseSpeedMax2; // @0x7a0
|
|
Scalar arrivalTime;
|
|
Scalar simTime;
|
|
Scalar spinRate;
|
|
ResourceDescription::ResourceID animationClips[40]; // @0x5cc gait clips
|
|
// namedClip is NOT a separate array in the binary: namedClip@0x5e0 == &animationClips[5]
|
|
// (0x5cc + 5*4). LoadLocomotionClips writes namedClip[i]; SetBodyAnimation/MeasureClipStride
|
|
// read animationClips[i+5] -- SAME storage. Aliased via this pointer (set in the ctor) so the
|
|
// array syntax in mech3.cpp keeps working AND the writes land where the state machine reads.
|
|
ResourceDescription::ResourceID *namedClip; // -> &animationClips[5]
|
|
ResourceDescription::ResourceID crashClipA, crashClipB, crashClipC; // @0x5d8/0x5d4/0x5dc
|
|
ResourceDescription::ResourceID gimpBaseClip; // @0x64c
|
|
CString motionEventName; // @0x598 cleared to "" on fall/reset
|
|
|
|
// --- gait helpers (mech2) -------------------------------------------
|
|
void SetLegAnimation(int state); // @004a7fc4
|
|
void SetBodyAnimation(int state); // @004a800c
|
|
void RequestActionFlags(Word bits); // @004a4c54
|
|
// P3 STEP-7 body finished-callback (the real PTR_LAB_0050d6fc == FUN_004a6d8c):
|
|
// called by SequenceController::Advance at end-of-clip -- picks the next gait state
|
|
// from commanded speed vs the loaded caps, re-arms via SetBodyAnimation, recursively
|
|
// advances the carryover. Static (the cb is a plain fn ptr, owner Mech is arg1).
|
|
// BodyTransition = the shared tail (SetBodyAnimation(next) + bodyAnimation.Advance).
|
|
// LoopBodyClip = bring-up loop for the inline cutover path.
|
|
static Scalar BodyClipFinished(Mech *m, unsigned a2, Scalar carryover, int move_joints); // FUN_004a6d8c
|
|
static Scalar LoopBodyClip(Mech *m, unsigned a2, Scalar carryover, int move_joints);
|
|
Scalar BodyTransition(int next_state, Scalar adv_time, int move_joints);
|
|
// LEG-channel finished-callback (the real PTR_LAB_0050d6f0 == FUN_004a6928):
|
|
// same transition machine keyed on legAnimationState@0x3b0, but compares the
|
|
// LIVE mapper speedDemand (subsystemArray[0]+0x128) and slews legCycleSpeed.
|
|
static Scalar LegClipFinished(Mech *m, unsigned a2, Scalar carryover, int move_joints); // FUN_004a6928
|
|
Scalar LegTransition(int next_state, Scalar adv_time, int move_joints);
|
|
Scalar AdvanceLegAnimation(Scalar time_slice); // @004a5028
|
|
Scalar AdvanceBodyAnimation(Scalar time_slice, int loop);
|
|
Scalar AdvanceBodyAnimationAirborne(Scalar time_slice, int loop);
|
|
Scalar AdvanceLegAnimationAirborne(Scalar time_slice);
|
|
Logical IsDisabled(); // FUN_0049fb54
|
|
// --- death sequence (the un-exported master-perf death branch, rebuilt
|
|
// from its exported consumers + the RP VTV::DeathShutdown analog) ---
|
|
Logical IsMechDestroyed(); // damage-side death flag: graphicAlarm level >= 9
|
|
void UpdateDeathState(Scalar dt); // death freeze + subsystem DeathShutdown + wreck smoke
|
|
|
|
// --- clip loaders (mech3) -------------------------------------------
|
|
ResourceDescription::ResourceID *
|
|
ResolveAnimationClip(const char *prefix, const char *suffix); // @004a7f50
|
|
|
|
// Body-animation looping callback (bring-up gait). Non-virtual so adding
|
|
// it does NOT change object layout; passed to AnimationInstance::SetAnimation
|
|
// (reinterpret_cast to JointedMover::AnimationCallback) so the clip re-arms at
|
|
// frame 0 when it ends instead of dereferencing a NULL finishedCallback.
|
|
Scalar OnBodyAnimFinished(ResourceDescription::ResourceID animation_number,
|
|
Scalar carryover, Logical animate_joints);
|
|
void MeasureClipStride(int slot, Scalar *total, Scalar *lastKey); // @004a8054
|
|
void LoadLocomotionClips(Mech__ModelResource *model); // @004a80d4
|
|
void LoadLocomotionClipsExt(Mech__ModelResource *model); // @004a86c8
|
|
|
|
static int
|
|
CreateSubsystemStream(
|
|
int subsystem_index, const char *type_name, int pass,
|
|
NotationFile *model_file, const char *model_name,
|
|
const char *subsystem_name, SubsystemResourceBlob *out_blob,
|
|
NotationFile *subsystem_file,
|
|
const ResourceDirectories *directories);
|
|
static ResourceDescription::ResourceID
|
|
CreateSubsystemsStream(
|
|
NotationFile *model_file, const char *model_name,
|
|
NotationFile *model_notation,
|
|
const ResourceDirectories *directories);
|
|
static SharedData *
|
|
SubsystemDefaultData(const char *type_name);
|
|
|
|
// --- simulation / damage (mech4) ------------------------------------
|
|
void DeadReckonPose(Scalar fraction);
|
|
Logical IntegrateMotion(Scalar time_slice, int loop);
|
|
void Simulate(Scalar time_slice);
|
|
|
|
// --- BRING-UP locomotion (Tier 2: drivable player mech) -------------
|
|
// Overrides the engine per-frame Simulation::PerformAndWatch (virtual;
|
|
// the simulation director calls it every frame on every entity). The
|
|
// shipped game's per-frame tick (Simulate via activePerformance) is the
|
|
// uncaptured/reconstructed-but-unsafe chain (raw this+0xNN offset bugs,
|
|
// stubbed subsystems), so for the drivable bring-up we override
|
|
// PerformAndWatch directly and integrate the player's localOrigin /
|
|
// localToWorld from throttle+turn input. localToWorld is the matrix the
|
|
// render tree + chase camera (btl4vid.cpp) are bound to, so the mech
|
|
// walks and the camera follows. See mech4.cpp. MUST match the base
|
|
// signature exactly or it hides instead of overriding.
|
|
void PerformAndWatch(const Time& till, MemoryStream *update_stream);
|
|
// THE REAL per-contact collision responder (task #15, ground-model-decode):
|
|
// binary FUN_004abb40, Mech vtable slot +0x3c -- the override of the engine
|
|
// protected virtual Mover::ProcessCollision (MOVER.h:359-365, signature
|
|
// exact). The earlier draft misread this function as a weapon sweep
|
|
// ("ResolveWeaponImpact") -- it is the mech-vs-world collision policy:
|
|
// BoxedSolid resolver -> StaticBounce -> owner classification (Mover /
|
|
// CulturalIcon crush sentinel 0.00123f). Gated: falls through to the
|
|
// engine base when GroundReal() is off.
|
|
virtual void ProcessCollision(Scalar time_slice, BoxedSolidCollision &collision,
|
|
const Point3D &old_position, Damage *damage);
|
|
// The authentic per-frame ground block (binary FUN_004a9b5c @4aa630-4aab5f):
|
|
// MoveCollisionVolume -> ground probe/snap -> collisions -> response policy.
|
|
// old_position = the START-OF-FRAME position (the blocking-hit restore target);
|
|
// must NOT alias localOrigin.linearPosition.
|
|
void AuthenticGroundAndCollide(Scalar dt, const Point3D &old_position);
|
|
void FeedHeatCapacityGauge();
|
|
void FeedHeatLevelGauge();
|
|
static Logical
|
|
LookupDamageState(const char *keyword, int *out);
|
|
void RaiseStatusAlarm(int level); // subsystem -> mech status alarm
|
|
|
|
// --- damage-table support (dmgtable) --------------------------------
|
|
Point3D WorldToLocal(const Point3D &world); // FUN_00408bf8 (owner+0xd0)
|
|
void *TorsoOrientationSource(); // *(this+0x438)
|
|
// Height reference the cylinder table normalises impact height against --
|
|
// the binary's *(mech+0x2ec)+0xc, a stance height set from mech+0x518
|
|
// (standing) / +0x51c (ducked). We return the standing value
|
|
// (collisionTemplate->maxY captured at ctor).
|
|
Scalar CylinderReferenceHeight();
|
|
// Live torso twist (yaw) in radians -- torso+0x1d8, via the roster torso
|
|
// (sinkSourceSubsystem @0x438). 0 for a fixed-torso mech.
|
|
Scalar TorsoHeading();
|
|
// Mech override of Entity::TakeDamageMessageHandler: resolve an unaimed
|
|
// (invalidDamageZone) hit's zone via the cylinder table, then base-route.
|
|
void TakeDamageMessageHandler(TakeDamageMessage *message);
|
|
|
|
// --- damage-routing support (mechdmg / mech4) -----------------------
|
|
// Typed access to the inherited Entity::damageZones[] (engine stores DamageZone*;
|
|
// our entries are Mech__DamageZone, populated by the Mech ctor). Defined in
|
|
// mech.cpp where Mech__DamageZone is a complete type.
|
|
Mech__DamageZone *Zone(int i) const;
|
|
// First VITAL damage zone index (0 if none) -- for concentrated fire that kills.
|
|
// Defined in mech4.cpp (Mech__DamageZone::vitalDamageZone is protected; Mech has access).
|
|
int FirstVitalZone() const;
|
|
int flags; // entity flags word (this+0x28 region)
|
|
int stance; // posture state
|
|
int ammoState; // @0x44c 0 none / 1 leaking / 2 dry
|
|
AlarmIndicator graphicAlarm; // body graphic-state alarm
|
|
EntityID lastInflictingID; // @0x43c last attacker (read by DamageZone routing)
|
|
Logical IsAirborne();
|
|
|
|
// --- gait world-step accumulators (P3 STEP-6 base-region reconciliation) ---
|
|
// RELOCATED out of the engine base: IntegrateMotion used raw 1995 binary
|
|
// offsets that, in the 2007 engine layout, stomp live engine fields
|
|
// (0x260->projectedOrigin, 0x26c->previousOrigin, 0x298->projectedVelocity,
|
|
// 0x100->localOrigin, 0x12c->updateOrigin). Declared here in the Mech's OWN
|
|
// region (appended -- shifts no locked offset) and accessed BY NAME. See
|
|
// btbuild/P3_LOCOMOTION.md "BASE-REGION RECONCILIATION".
|
|
Quaternion motionDelta; // was raw @0x260 (orientation delta; FromQuat src)
|
|
Quaternion worldPose; // was raw @0x26c (integrated world pose)
|
|
Quaternion worldPoseBase; // was raw @0x138 (dead-reckon base pose; DeadReckonPose)
|
|
Quaternion angularAccum; // was raw @0x298 (per-frame angular accumulator)
|
|
Vector3D motionSourceA; // was raw @0x100 (motion-event source A)
|
|
Vector3D motionSourceB; // was raw @0x12c (motion-event source B)
|
|
Vector3D motionEventVector; // was raw @0x598 (motion-event delta = B - A)
|
|
Quaternion aimRate; // was raw @0x1dc (aim/torso angular-rate telemetry,
|
|
// cleared each frame; stomped localAcceleration)
|
|
|
|
//
|
|
// --- Everything past here is owned by mech2.cpp .. mech4.cpp ---
|
|
// [mech2] MoveAndCollide / Simulate / WriteUpdateRecord / damage routing
|
|
// [mech3] PrintState / status reporting / cockpit display feed
|
|
// [mech4] AI / pathing / scoring hooks
|
|
// The remaining members up to sizeof==0x854 are declared by those slices.
|
|
//
|
|
};
|
|
|
|
#endif
|