//===========================================================================// // File: mechsub.hpp // // Project: BattleTech Brick: Entity Manager // // Contents: MechSubsystem -- BT damage-coupled base for all mech subsystems // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // --/--/95 ?? Initial coding. // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved // // PROPRIETARY AND CONFIDENTIAL // //===========================================================================// // // RECONSTRUCTED from the shipped binary. No mechsub.hpp survived, but the // surviving MECHTECH.HPP `#include ` and references // `MechSubsystem::TechStatusTypeCount`, `MechSubsystem *monitoredSubsystem`, // confirming a distinct BT class named MechSubsystem. // // RECONCILIATION VERDICT: MechSubsystem IS a distinct base class -- it is NOT // an alias for the MUNGA `Subsystem` (RP/MUNGA/SUBSYSTM.HPP, which has no // "Tech" status and no DamageZone coupling). It sits between MUNGA Subsystem // and HeatableSubsystem: // // Simulation (MUNGA) // Subsystem (MUNGA, SUBSYSTM.HPP -- we have it) // MechSubsystem <-- THIS FILE (classID 0xBBB, vtable 0x50e210) // HeatableSubsystem (heat.cpp -- string @0x50e700, classID 0xBBC?) // HeatSink ... (heat.cpp / heatfamily_reslice.cpp) // MechTech (mechtech.cpp -- : MechSubsystem? see note) // // // *** CLASSMAP CORRECTION (for the next reconciler) *** // The heat.cpp / heat.hpp reconstruction attributed the cluster // @0x4ac07c..@0x4ac9ec (vtable 0x50e210, dtor 0x4ac868) to its // "Subsystem/HeatableSubsystem base". The string and behavioural evidence say // that cluster is actually MechSubsystem: // * CreateStreamedSubsystem @0x4ac9ec stamps classID 0xBBB and model size // 0xE4, and parses ONLY damage/critical fields (PrintSimulationState, // CollisionCriticalHitWeight, VideoObjectName, VitalSubsystem) -- NO // thermal fields. (Thermal init is HeatSink's @0x4ae150.) // * The ClassDerivations name string is "MechSubsystem" (@0x50def5, // @0x50e2fd), distinct from "HeatableSubsystem" (@0x50e700). // * The class couples the subsystem to a DamageZone (this+0xE0) and exposes // the subsystem status/alarm with state names @0x50df17: // Destroyed / Damaged / CoolantLeaking / Overheating / AmmoBurning / // Jammed / BadPower (TechStatusTypeCount of these), plus the // PrintSimulationState names DefaultState / Destroyed / Exploding. // heat.cpp's `HeatableSubsystem(currentTemperature@0x114)` ctor @0x4ac644 does // NOT write 0x114 -- it is in fact a MechSubsystem ctor. HeatableSubsystem is // a (near-empty) layer above MechSubsystem; the temperature/heat fields belong // to HeatSink (@0x4adda0). // // This reconstruction is BEST-EFFORT and overlaps heat.cpp; names of the // MechSubsystem data members are inferred from usage. See mechsub.cpp for the // per-method @ADDR evidence. // #if !defined(MECHSUB_HPP) # define MECHSUB_HPP #if !defined(SUBSYSTM_HPP) # include #endif #if !defined(DAMAGE_HPP) # include #endif #include // AffineMatrix #include // AverageOf #include // Scalar #include // GaugeAlarm #include "mechrecon.hpp" // reconstruction shim (proxies, artifact globals, type aliases) //##################### Reconstruction type aliases ##################### #if !defined(BT_RECON_TYPE_ALIASES) # define BT_RECON_TYPE_ALIASES typedef AffineMatrix Matrix34; // 3x4 affine (AFFNMTRX.h) typedef GaugeAlarm AlarmIndicator; // GAUGALRM.h typedef AverageOf FilteredScalar; // 15-sample running average (AVERAGE.h) #endif //##################### Forward Class Declarations ####################### class Mech; class NotationFile; class Damage; //########################################################################### //##################### MechSubsystem Model Resource #################### //########################################################################### // // Extends the base Subsystem resource. The mech-specific fields begin at +0x30 // in the parsed resource record (see MechSubsystem::CreateStreamedSubsystem // @0x4ac9ec). Resource record size = 0xE4. // struct MechSubsystem__SubsystemResource: public Subsystem::SubsystemResource { Scalar armorByFacing[5]; // +0x30 per-facing armour (default -1.0f) Scalar structureReference; // +0x44 health/structure divisor (default -1.0f) int vitalSubsystemIndex; // +0x48 "VitalSubsystem" segment (default -1) char videoObjectName[128]; // +0x4C "VideoObjectName" (default "None") ResourceDescription::ResourceID alarmModel; // +0xCC (resolved id; the RECORD reserves a // 12-byte field here -- @004ac9ec parses // it via FUN_00408944 as 3 floats. The // reconstruction only consumes the leading // id, but the 12-byte footprint MUST be // preserved or every field below slides // 8 bytes low and the heat resource -- // which begins at +0xE4 -- misaligns.) int _alarmModelReserved[2]; // +0xD0 remainder of the 12-byte 0xCC field int printSimulationState; // +0xD8 "PrintSimulationState" Scalar collisionCriticalHitWeight; // +0xDC "CollisionCriticalHitWeight" Scalar criticalReference; // +0xE0 (default -1.0f) }; //########################################################################### //########################## MechSubsystem ############################# //########################################################################### // // Abstract BT base for every subsystem mounted in a mech. Owns a DamageZone // (this+0xE0) representing its armour facings and structural integrity, a // 3+-level status alarm (this+0x2C) reported by the cockpit MechTech, and the // critical-hit / damage-distribution machinery. // (vtable @0x50e210, ctors @0x4ac530 / @0x4ac644, dtor @0x4ac868, // classID 0xBBB, resource model size 0xE4.) // class MechSubsystem: public Subsystem { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Shared Data Support // public: static Derivation ClassDerivations; // name string "MechSubsystem" static SharedData DefaultData; static Recon MessageHandlers; // SharedData ctor operand enum { StateCount = 7 }; // TechStatusTypeCount // alarm-changed callback (vtable slot +0x34) + host back-pointer void OnAlarmChanged(); Mech *owner; // owning mech (== hostEntity) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Status model // public: // Damage/condition status states (alarm @this+0x2C). Parsed from the // state-name table @0x50df17 by ConfigureActivePress (@0x4ac194). enum TechStatusType { Destroyed = 0, Damaged = 1, CoolantLeaking = 2, Overheating = 3, AmmoBurning = 4, Jammed = 5, BadPower = 6, TechStatusTypeCount // = 7 (referenced by MECHTECH.HPP) }; // Simulation-state values stored at this+0x40 (printed by PrintState). enum SimulationState { DefaultState = 0, DestroyedState = 1, ExplodingState = 2 }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Test Class Support // public: static Logical TestClass(Mech&); Logical TestInstance() const; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Construction and Destruction // public: typedef MechSubsystem__SubsystemResource SubsystemResource; // @0x4ac530 -- light ctor (name + classID); DamageZone named "None". MechSubsystem( Mech *owner, int subsystem_ID, const char *subsystem_name, RegisteredClass::ClassID class_id, SharedData &shared_data = DefaultData ); // @0x4ac644 -- resource ctor; copies armorByFacing[5] -> DamageZone+0x144, // structureReference -> DamageZone+0x140, and pre-inverts the armour // scalars (1.0 / (armour * reference)) where above epsilon. // (heat.cpp mislabels this as "HeatableSubsystem".) MechSubsystem( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data = DefaultData ); ~MechSubsystem(); // @0x4ac868 static int CreateStreamedSubsystem( // @0x4ac9ec NotationFile *model_file, const char *model_name, const char *subsystem_name, SubsystemResource *subsystem_resource, NotationFile *subsystem_file, const ResourceDirectories *directories, int passes = 1 ); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Subsystem virtual overrides (slots on vtable @0x50e210) // public: LWord GetStatusFlags(); // slot 12, @0x4ac144 void ResetToInitialState(Logical powered); // slot 8, @0x4ac1d4 void PrintState(); // slot 13, @0x4ac8c0 Logical HandleMessage(int message); // @0x4ac0bc virtual void TakeDamage(Damage &damage); // slot via this+0x24 (see DamageDelta) Logical IsDamaged(); // @0x4ac9c8 (bus-state query) // Critical-state readouts for ColorMapperCritical (CriticalConnection): // the raw simulation state (DefaultState=0 / Destroyed=1 / Exploding=2) and // the subsystem's own damage zone (a real DamageZone behind the proxy type; // set in the ctor via `new DamageZone(...)`, mechsub.cpp). int GetSimulationState() const { return simulationState; } ReconDamageZone * GetDamageZoneProxy() const { return damageZone; } // gauge-complete wave: this subsystem's OWN structural damage [0,1] (0=intact, // 1=destroyed) -- the engine DamageZone::damageLevel of the zone the ctor built // (new DamageZone(this,0) @0xE0). Read via the ENGINE type's NAMED member (not // the ReconDamageZone proxy, whose offset-0 structureLevel aliases the vtable // ptr as a float = garbage). The binary reads *(this[0x38]+0x158) here (== the // same anchor UpdateCoolant/SensorSimulation use). Defined in mechsub.cpp. Scalar GetSubsystemDamageLevel() const; void SetSubsystemDamageLevel(Scalar level); // task #2 (crit propagation): the zone-side crit paths // (Mech__DamageZone::CriticalHit @0049ccc4 / SendSubsystemDamage // @0049c9a8) reach these subsystem facets. int IsVitalSubsystem() const { return vitalSubsystem; } // @0x4ac07c -- run the virtual TakeDamage and return the resulting // rise in the subsystem's own damageLevel (the crit allotment used). Scalar ApplyDamageAndMeasure(Damage &damage); // The destroyed-side effects SendSubsystemDamage applies when the // subsystem's own zone hits 1.0: statusAlarm Destroyed(1) + the // PrintState debug gate + the zone's state valve. [T1 @0049c9a8] void ForceCriticalFailure(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Internal helpers // protected: // @0x4ac22c -- clear structure & both alarms to the inactive level. void ClearStatus(); // @0x4ac274 -- distribute a critical hit across the contained critical // subsystems, scaling damage by 1/criticalCount and logging // "ammo explosion damaging ". void DistributeCriticalHit(Damage &damage); // @0x4ac194 -- map a status-state name string -> TechStatusType value // using the name table @0x50de74 (ConfigureActivePress support). static Logical LookupStatusType(const char *name, int *out_value); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Local data (byte offsets into the shipped object) // protected: AlarmIndicator statusAlarm; // @0x2C this+0xb (condition/status alarm) int simulationState; // @0x40 DefaultState/Destroyed/Exploding ReconDamageZone *damageZone; // @0xE0 this[0x38] (0x160-byte zone) int vitalSubsystem; // @0xE4 this[0x39] -- destroying this // subsystem KILLS the mech (ctor: res+0x48 // "VitalSubsystem"==1; was misnamed // videoObjectFlag) [T1 part_012.c:15776] // task #6 CORRECTION: these two are the CONTROLS feed, not identity -- // the binary ctor chain stores its destination/message ctor pass-throughs // here (FUN_004ac644 part_012.c:15772-15773), and ConfigMapGauge::Execute // @004c6f1c reads BOTH for buttonGroup GetMapState. MechWeapon's ctor // defaults the destination to &fireImpulse (@004b99a8 part_013.c:6886-9). // The old names hostEntity/subsystemId2 (populated with owner/subsystem_ID) // were a mislabel -- any reader would have classified every weapon as // "mappedByOthers". (No port reader existed; verified before the rename.) void *controlDestination; // @0xE8 this[0x3a] (ControlsButton* the direct maps write) int controlMessageID; // @0xEC this[0x3b] (event-mapping message id; weapons 0) friend void BTSubsystemControlFeed(void *, void **, int *, void **); // gauge bridge (task #6) SubsystemResource *resource; // @0xF0 this[0x3c] void *refCount; // @0xF4 this[0x3d] ResourceDescription::ResourceID alarmModel; // @0xF8 this[0x3e] int printSimulationState; // @0x104 this[0x41] Scalar criticalReference; // @0x108 this[0x42] Scalar collisionCriticalHitWeight; // @0x10C this[0x43] int vitalSubsystemIndex; // @0x110 this[0x44] (default -1) }; #endif