//============================================================================// // File: misthrst.cpp // // Project: BattleTech // // Contents: Missile Thruster -- provides thrust/burn to move a Missile // //----------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved // // PROPRIETARY AND CONFIDENTIAL // //============================================================================// // // RECONSTRUCTED from the shipped binary. The class declaration survives intact // (MISTHRST.HPP); behaviour follows the Ghidra pseudo-C in part_013.c // (@004be7c4 ctor, @004be8bc dtor, @004be8e8 TestInstance, @004bf8ec streamed // resource parser). Each non-trivial method cites the originating @ADDR. // // Coverage: // confident : ctor @004be7c4, dtor thunk @004be8bc, TestInstance @004be8e8, // CreateStreamedSubsystem @004bf8ec // best-effort: MissileThrusterSimulation -- the Performance method pointer is // installed by the ctor (PTR_LAB_00512b20) but its standalone body // is folded into the host Missile::MoveAndCollide @004bef78 (which // samples the thruster acceleration buffer at missile+0x234/+0x250). // excluded : the 0x41xxxx engine vtable slots (pure Subsystem base behaviour; // vtable @00512bbc overrides only slot0 = destructor) // // Decoded constant: // _DAT_004be8b8 = 0x3c8efa35 = 0.0174533f (PI/180, Degree -> Radian) // // Helper-function name mapping (engine internals referenced by the decomp): // FUN_0041c52c Subsystem base constructor (owner, id, name, sharedData) // FUN_00408440 Vector3D copy // FUN_0041bbd8 AlarmIndicator::SetLevel // FUN_0041a1a4 IsDerivedFrom(classDerivations) // FUN_004238bc Entity model-record base parser // FUN_00404118 read "
"/"" Scalar from notation file // FUN_00406db4 register parsed model record (type 0xf) // FUN_004dbb24/FUN_004d9c38 error-string accumulate / flush // #include #pragma hdrstop #if !defined(MISTHRST_HPP) # include #endif #if !defined(MISSILE_HPP) # include #endif #if !defined(TESTBT_HPP) # include #endif // // Degree -> Radian scale factor, read as a read-only global in the decomp. // static const Scalar DegreesToRadians = 0.0174533f; // _DAT_004be8b8 (0x3c8efa35) // // Unrecovered streamed-resource helper stand-ins (engine model-record parser). // static int ParseEntityModel(NotationFile*, const char*, NotationFile*, const ResourceDirectories*, float*) { return 0; } // FUN_004238bc static int ReadScalar(NotationFile*, const char*, const char*, float*) { return 1; } // FUN_00404118 static void ReportMissing(const char*, const char*) {} // FUN_004dbb24 static void SetStatusLevel(int) {} // FUN_0041bbd8 (status alarm) static int RegisterModelRecord(NotationFile*, const char*, int, int, int, float*, int, int) { return 0; } // FUN_00406db4 //########################################################################### //########################################################################### // MissileThruster //########################################################################### //########################################################################### //############################################################################# // Shared Data Support // Derivation MissileThruster::ClassDerivations( Subsystem::GetClassDerivations(), "MissileThruster" ); Receiver::MessageHandlerSet MissileThruster::MessageHandlers; const MissileThruster::IndexEntry MissileThruster::AttributePointers[] = { { 0, 0, 0 } }; // IndexEntry table @00512ad0 MissileThruster::AttributeIndexSet MissileThruster::AttributeIndex; MissileThruster::SharedData MissileThruster::DefaultData( // resolved as &DAT_00512a9c &MissileThruster::ClassDerivations, MissileThruster::MessageHandlers, MissileThruster::AttributeIndex, MissileThruster::StateCount ); //############################################################################# // Construction // // @004be7c4 MissileThruster::MissileThruster(Missile *owner, int id, // SubsystemResource *resource) // // Chains the Subsystem base ctor, stamps the MissileThruster vtable, copies the // four streamed tuning scalars (converting the rotation rate from degrees to // radians), then primes the base thrust acceleration vector to point "down the // tail" by thrusterAcceleration. The active Performance (the per-frame burn / // steer simulation) is installed unconditionally to PTR_LAB_00512b20; the // authoritative-vs-ghost guard (owner flags & 0xC) selects an alternate entry // that the final write overwrites in the shipped code. // MissileThruster::MissileThruster( Missile *owner, int subsystem_ID, SubsystemResource *subsystem_resource) : // Subsystem base ctor: owner, id, resource, shared default record. Subsystem(owner, subsystem_ID, subsystem_resource, DefaultData) { // vtable installed by the compiler (PTR_LAB_00512bbc) burnTimeRemaining = subsystem_resource->burnTime; // @0xE4 resource +0x30 thrusterAcceleration = subsystem_resource->thrusterAcceleration; // @0xEC resource +0x38 thrusterClimb = subsystem_resource->thrusterClimb; // @0xF0 resource +0x3C // install the burn/steer Performance (only the conditional guard differs for // remote ghosts; the unconditional store @004be7c4 is what actually runs). if ((owner->simulationFlags & 0xC) != 4) // owner+0x28 & 0xC SetPerformance(/*ghost*/ &MissileThruster::MissileThrusterSimulation); SetPerformance(&MissileThruster::MissileThrusterSimulation); // PTR_LAB_00512b20 // Degree -> Radian (Radian::operator=(const Degree&) performs the scaling) maxThrusterRotationRate = subsystem_resource->maxThrusterRotationRate; // @0xE8 SetStatusLevel(1); // FUN_0041bbd8(this+0xb, 1) // base acceleration = (0,0,-thrusterAcceleration), angular = (0,0,0) acceleration.linearMotion = Vector3D(0.0f, 0.0f, 0.0f); // @0xF4 FUN_00408440(.., &DAT_004e0f74) acceleration.linearMotion.z -= thrusterAcceleration; // @0xFC this[0x3f] -= this[0x3b] acceleration.angularMotion = Vector3D(0.0f, 0.0f, 0.0f); // @0x100 FUN_00408440(.., &DAT_004e0f74) } //############################################################################# // Destruction // // @004be8bc ~MissileThruster() -- restores the vtable then chains the Subsystem // base destructor. (Reached through the scalar-deleting thunk.) // MissileThruster::~MissileThruster() { // vtable reset + base ~Subsystem chaining handled by the compiler. } //############################################################################# // MissileThrusterSimulation -- the registered Performance (best-effort) // // The thruster's per-frame work (bleed burnTimeRemaining by the time slice and // contribute its acceleration to the missile's velocity) is, in the shipped // binary, executed from inside Missile::MoveAndCollide @004bef78 which reads // this->acceleration directly out of the roster. No distinct @ADDR body // survives for the standalone Performance; the reconstruction below mirrors // that folded behaviour and the MISTHRST.HPP signature. // void MissileThruster::MissileThrusterSimulation(Scalar time_slice) { if (burnTimeRemaining <= 0.0f) return; // fuel exhausted -- coast burnTimeRemaining -= time_slice; if (burnTimeRemaining < 0.0f) burnTimeRemaining = 0.0f; // thrust is applied to the host Missile's motion by the integrator // (Missile::MoveAndCollide), which scales this->acceleration by the slice. } //############################################################################# // Streamed resource parser // // @004bf8ec MissileThruster::CreateStreamedSubsystem(...) // // Parses the thruster tuning block out of the "gamedata" section of the // notation file into the (0x54-byte) shared model record and registers it as // a type-0xF entity-model resource. Field order in the record: // +0x40 MaxThrusterRotationRate +0x44 BurnTime +0x48 ThrusterAcceleration // +0x4C ThrusterClimb +0x50 SplashRadius // The Missile ctor later copies +0x40..+0x4C into the thruster's // SubsystemResource (+0x34/+0x30/+0x38/+0x3C). Any missing key is fatal. // int MissileThruster::CreateStreamedSubsystem( NotationFile *model_file, const char *model_name, NotationFile *subsystem_file, const ResourceDirectories *directories, SubsystemResource *subsystem_resource) { Logical owned_record = (subsystem_resource == 0); float *record = (float *)subsystem_resource; if (owned_record) record = (float *)::operator new(0x54); // FUN_00402298(0x54) // Entity model-record base parse. if (ParseEntityModel(model_file, model_name, subsystem_file, directories, record) == -1) goto fail; if (!ReadScalar(subsystem_file, "gamedata", "BurnTime", record + 0x11)) // +0x44 { ReportMissing(model_name, "Missing BurnTime"); goto fail; } if (!ReadScalar(subsystem_file, "gamedata", "MaxThrusterRotationRate", record + 0x10)) // +0x40 { ReportMissing(model_name, "MaxThrusterRotationRate Missing"); goto fail; } if (!ReadScalar(subsystem_file, "gamedata", "ThrusterAcceleration", record + 0x12)) // +0x48 { ReportMissing(model_name, "ThrusterAcceleration Missing"); goto fail; } if (!ReadScalar(subsystem_file, "gamedata", "ThrusterClimb", record + 0x13)) // +0x4C { ReportMissing(model_name, "ThrusterClimb missing"); goto fail; } if (!ReadScalar(subsystem_file, "gamedata", "SplashRadius", record + 0x14)) // +0x50 { ReportMissing(model_name, "Splash Radius missing"); goto fail; } if (owned_record) { int handle = RegisterModelRecord(model_file, model_name, 0xF, 1, 0, record, 0x54, -1); ::operator delete(record); return handle; } return 1; fail: if (owned_record) ::operator delete(record); return -1; } //############################################################################# // TestInstance // // @004be8e8 IsDerivedFrom(ClassDerivations tag 0x512aac). // Logical MissileThruster::TestInstance() const { return IsDerivedFrom(MissileThruster::ClassDerivations); } //############################################################################# // TestClass (MISTHRST.HPP / no surviving .TCP) // Logical MissileThruster::TestClass(Missile &) { return True; }