//============================================================================// // File: projtile.cpp // // Project: BattleTech // // Contents: In-flight projectile entity base (Projectile : Entity) // //----------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved // // PROPRIETARY AND CONFIDENTIAL // //============================================================================// // // RECONSTRUCTED from the shipped binary. There is NO surviving projtile.cpp -- // only PROJTILE.TCP (Projectile::TestClass). Behaviour follows the Ghidra // pseudo-C in part_013.c (@004be1bc ctor, @004be358 dtor, @004be384 allocator, // @004be3b4 TestInstance). Each non-trivial method cites the originating @ADDR. // // Coverage: // confident : ctor @004be1bc (Entity base chain, world/segment registration, // launch-kinematics snapshot, Performance install), dtor thunk // @004be358, allocator @004be384, TestInstance @004be3b4 // best-effort: the names of the descriptor-sourced members (+0x300..+0x33C) // and the modelUpdate binding (+0x250/+0x254); MoveAndCollide // (the base Performance variants PTR_LAB_005129e8/f4 -- bodies sit // past the captured window; the live integrator that ships is the // Missile override @004bef78) // excluded : the 0x41xxxx/0x42xxxx Entity engine vtable slots and the base // Entity ctor @004234f0 // // Helper-function name mapping (engine internals referenced by the decomp): // FUN_004234f0 Entity base constructor (ctx, default record) // FUN_0041db7c init embedded connection/descriptor sub-object // FUN_0041bbd8 AlarmIndicator::SetLevel FUN_0041a1a4 IsDerivedFrom // FUN_00408440 Point3D / Vector3D copy FUN_00433ed4 motion-source lookup // FUN_00402298 operator new FUN_004022d0 operator delete // DAT_004efc94 global game/world context block // #include #pragma hdrstop #if !defined(PROJTILE_HPP) # include #endif #if !defined(TESTBT_HPP) # include #endif //########################################################################### //########################################################################### // Projectile //########################################################################### //########################################################################### //############################################################################# // Decompiler-artifact stand-ins (label/record operands). // static char DAT_005129b0[0x340] = { 0 }; // default streamed-entity record //############################################################################# // Shared Data Support // Derivation Projectile::ClassDerivations( Entity::GetClassDerivations(), "Projectile" ); Receiver::MessageHandlerSet Projectile::MessageHandlers; Simulation::AttributeIndexSet Projectile::AttributeIndex; Projectile::SharedData Projectile::DefaultData( &Projectile::ClassDerivations, Projectile::MessageHandlers, Projectile::AttributeIndex, Projectile::StateCount, &Projectile::Make ); // // Entity factory hook (Entity::SharedData requires a MakeHandler). Projectiles // are spawned through New(), not the generic Make() path, so this is a stub. // Entity *Projectile::Make(MakeMessage * /*creation_message*/) { return 0; } //############################################################################# // Allocation // // @004be384 Projectile::New(spawn_context) -- allocate 0x340 bytes and // construct with the default streamed-entity record &DAT_005129b0. // Projectile *Projectile::New(int spawn_context) { Projectile *p = (Projectile *)::operator new(0x340); // FUN_00402298 if (p != 0) p = new(p) Projectile(spawn_context, DAT_005129b0); // @004be1bc return p; } //############################################################################# // Construction // // @004be1bc Projectile::Projectile(int owner, const char *spawn_descriptor) // // 1. Chains the Entity base ctor and stamps the Projectile vtable. // 2. Initialises the embedded segment/connection sub-object (this+0x30C), // flags itself as a transient/guided entity (flags |= 0x10) and raises its // status alarm to level 2 (in-flight). // 3. Registers itself into the owning world's segment table (the segment is // resolved from descriptor+0x84 indexing world+0x128) and snapshots the // launch kinematics out of the spawn descriptor: // segmentIndex <- descriptor +0x84 // sourceEntity <- descriptor +0x88 // sourceWeapon <- descriptor +0x8C // launchPosition <- descriptor +0x90 (Point3D) // launchVelocity <- descriptor +0x9C (Vector3D) // aimDirection <- descriptor +0xA8 (Vector3D) // modelIndex <- descriptor +0xB4 // damageRecord <- descriptor +0xB8 // 4. Binds the per-frame model-update callback (this[0x94..0x97], // PTR_FUN_005129dc) and installs the active Performance: the authoritative // integrator (PTR_LAB_005129e8) when flags&0xC==4, else the ghost variant // (PTR_LAB_005129f4). Finally fires vtable slot 14 (initial placement). // Projectile::Projectile(int owner, const char *spawn_descriptor) : Entity((Entity::MakeMessage *)0, DefaultData) // @004234f0 { (void)owner; // vtable installed by the compiler (PTR_FUN_00512a5c) InitSegmentLink(&this->sourceEntity); // @0041db7c (this+0x30C) simulationFlags |= 0x10; // transient / projectile SetStatusLevel(2); // FUN_0041bbd8(this+0xb, 2) -- in flight // register into the world segment table world = ResolveWorld(DAT_004efc94); // this[0xC0] @0x300 segmentIndex = *(int *)(spawn_descriptor + 0x84); // this[0xC2] @0x308 currentSegment = *(Segment **)(*(int *)((int)world + 0x128) + segmentIndex * 4); // this[0xC1] @0x304 sourceEntity = *(int *)(spawn_descriptor + 0x88); // this[0xC3] @0x30C sourceWeapon = *(int *)(spawn_descriptor + 0x8C); // this[0xC4] @0x310 modelIndex = *(int *)(spawn_descriptor + 0xB4); // this[0xCE] @0x338 launchPosition = *(Point3D *)(spawn_descriptor + 0x90); // this[0xC5] @0x314 FUN_00408440 launchVelocity = *(Vector3D *)(spawn_descriptor + 0x9C); // this[0xC8] @0x320 FUN_00408440 aimDirection = *(Vector3D *)(spawn_descriptor + 0xA8); // this[0xCB] @0x32C FUN_00408440 damageRecord = *(int *)(spawn_descriptor + 0xB8); // this[0xCF] @0x33C // model-update callback context + bound member function pointer triple modelUpdateContext = ResolveMotionSource(DAT_004efc94); // this[0x94] @0x250 (FUN_00433ed4) BindModelUpdate(0); // this[0x95..0x97] @0x254 (PTR_FUN_005129dc) // select the per-frame integrator if ((simulationFlags & 0xC) == 4) SetPerformance(/*authoritative*/ &Projectile::MoveAndCollide); // PTR_LAB_005129e8 else SetPerformance(/*ghost*/ &Projectile::MoveAndCollide); // PTR_LAB_005129f4 // slot 14 (initial placement): a virtual call in the shipped ctor; the // concrete placement runs through the installed vtable at run time. } //############################################################################# // Destruction // // @004be358 ~Projectile() -- restores the Projectile vtable then chains the // Entity base destructor (which un-registers from the segment table). // Projectile::~Projectile() { // vtable reset + base ~Entity chaining handled by the compiler. } //############################################################################# // MoveAndCollide -- default per-frame integrator (best-effort) // // The base Projectile integrate-and-collide bodies (PTR_LAB_005129e8 / // 005129f4) lie past the captured decomp window. The concrete behaviour that // ships for the only instantiated subclass is the Missile override // (Missile::MoveAndCollide @004bef78): age the projectile, integrate motion // (FUN_00421b2c) and advance the model (FUN_00421bac), run the world collision // query (FUN_0042291c) and -- on a hit -- spawn the explosion/Damage entity and // retire (FUN_0042061c). Declared here to anchor the vtable slot; the body is // reconstructed structurally. // void Projectile::MoveAndCollide(Scalar time_slice) { IntegrateMotion(); // FUN_00421b2c AdvanceModel(time_slice); // FUN_00421bac Entity *hit = CollisionQuery(time_slice); // FUN_0042291c if (hit != 0) Retire(); // FUN_0042061c } //############################################################################# // TestInstance // // @004be3b4 IsDerivedFrom(ClassDerivations tag 0x512980). // Logical Projectile::TestInstance() const { return IsDerivedFrom(Projectile::ClassDerivations); } //############################################################################# // TestClass (PROJTILE.TCP) // // Surviving fragment: takes no argument and returns True. // Logical Projectile::TestClass() { return True; } //############################################################################# // Recovered internal helpers (engine-internal world/segment/motion plumbing). // These wrap Entity-engine operations the decompiler rendered as raw FUN_* // calls; reconstructed as structural stubs. // void Projectile::InitSegmentLink(void * /*link*/) {} void Projectile::SetStatusLevel(int /*level*/) {} void *Projectile::ResolveWorld(void * /*app_context*/) { return 0; } void *Projectile::ResolveMotionSource(void * /*app_context*/) { return 0; } void Projectile::BindModelUpdate(void * /*callback*/) {} void Projectile::IntegrateMotion() {} void Projectile::AdvanceModel(Scalar /*time_slice*/) {} Entity *Projectile::CollisionQuery(Scalar /*time_slice*/) { return 0; } void Projectile::Retire() {}