Files
BT411/game/reconstructed/mechweap.hpp
T
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00

298 lines
12 KiB
C++

//===========================================================================//
// File: mechweap.hpp //
// Project: BattleTech Brick: Entity Manager //
// Contents: MechWeapon -- abstract base for every mounted Mech weapon //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// --/--/95 JM Initial coding. //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
// PROPRIETARY AND CONFIDENTIAL //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C, module cluster
// @004b95ec-@004b9d10, file=bt/mechweap.cpp; pure-virtual stub @004ba45c,
// file=bt/mechweap.hpp). Cross-referenced HEAVILY against the SURVIVING
// derived-class sources PPC.HPP/PPC.CPP and GAUSS.HPP/GAUSS.CPP (which derive
// from Emitter : MechWeapon) and MISLANCH.HPP (MissileLauncher : ProjectileWeapon
// : MechWeapon), plus the MUNGA SUBSYSTM.HPP base interface and the RP
// WEAPSYS.h analog. See mechweap.cpp for per-method @ADDR evidence.
//
// Inheritance chain established from the decomp:
// Simulation -> Subsystem -> PoweredSubsystem (powersub.cpp, ctor @004b0f74)
// -> MechWeapon (this file, vtable @00511d2c)
// -> Emitter / ProjectileWeapon -> PPC / GaussRifle / MissileLauncher
//
// The MechWeapon ctor (@004b99a8) installs vtable PTR_FUN_00511d2c and chains
// to the PoweredSubsystem ctor @004b0f74, confirming the base. PPC and Gauss
// FireWeapon() chain to Emitter::FireWeapon(); MechWeapon::FireWeapon() is the
// pure-virtual default that asserts "MechWeapon abstract class should..." (@004b95ec)
// -- the exact analog of RP GenericGun::Fire()'s "Should not be here!" stub.
//
// Field offsets in comments are byte offsets into the shipped object
// (e.g. "@0x34c" == this word index 0xd3). Names taken from PPC.CPP
// (targetWithinRange, damageData) and the resource-parser tag strings
// (WeaponRange, DamageAmount, HeatCostToFire, RechargeRate, DamageType,
// PipPosition/PipColor/PipExtendedRange, ExplosionModelFile); the remainder
// are inferred from usage and flagged best-effort where uncertain.
//
#if !defined(MECHWEAP_HPP)
# define MECHWEAP_HPP
#if !defined(POWERSUB_HPP)
# include <powersub.hpp>
#endif
#if !defined(DAMAGE_HPP)
# include <damage.hpp>
#endif
#if !defined(ALARM_HPP)
# include <alarm.hpp>
#endif
#include <color.hpp> // RGBColor
#include <linmtrx.hpp> // LinearMatrix
#include <vector3d.hpp> // Vector3D
#include <point3d.hpp> // Point3D
//##################### Forward Class Declarations #######################
class Mech;
class Entity;
class ResourceFile;
class NotationFile;
class CockpitHud;
//###########################################################################
//#################### MechWeapon Model Resource ########################
//###########################################################################
//
// Extends the PoweredSubsystem resource record. The MechWeapon-specific
// fields begin at +0x190 in the parsed record (see CreateStreamedSubsystem
// @004b9d10). Total record size is 0x1BC (444 bytes) -- the parser stamps
// subsystemModelSize = 0x1BC and classID = MechWeaponClassID (0xBCD).
//
struct MechWeapon__SubsystemResource:
public PoweredSubsystem::SubsystemResource
{
Scalar rechargeRate; // +0x190 "RechargeRate"
Scalar weaponRange; // +0x194 "WeaponRange"
ResourceDescription::ResourceID
explosionResourceID; // +0x198 "ExplosionModelFile"
Scalar damageAmount; // +0x19C "DamageAmount" (-> damageData)
Enumeration damageType; // +0x1A0 "DamageType" (Collision..Energy)
Scalar heatCostToFire; // +0x1A4 "HeatCostToFire"
int pipPosition; // +0x1A8 "PipPosition"
RGBColor pipColor; // +0x1AC "PipColor" (3 floats, init -1)
int pipExtendedRange; // +0x1B8 "PipExtendedRange"
}; // sizeof == 0x1BC
//###########################################################################
//######################### MechWeapon ############################
//###########################################################################
//
// Abstract base for any subsystem that can be aimed at a target and fired,
// dealing damage and (optionally) spawning an explosion model and a HUD pip.
// (vtable @00511d2c, ctor @004b99a8, dtor @004b9b9c.)
//
class MechWeapon:
public PoweredSubsystem
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation ClassDerivations;
static Receiver::MessageHandlerSet MessageHandlers;
static AttributeIndexSet AttributeIndex;
static SharedData DefaultData;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Update record (replication) -- extends the base Simulation record with
// the weapon-specific alarm/idle state appended by WriteUpdateRecord.
//
public:
struct MechWeapon__UpdateRecord:
public PoweredSubsystem::UpdateRecord
{
Enumeration alarmState; // weaponAlarm state
int weaponIdle; // 1 == idle (alarm level 0)
};
typedef MechWeapon__UpdateRecord UpdateRecord;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Firing -- the central abstract operation
//
// Pure-virtual in spirit: the base body @004b95ec just asserts
// "MechWeapon abstract class should [not be fired]". Emitter (PPC, Gauss)
// and ProjectileWeapon (MissileLauncher) provide the real implementations;
// PPC::FireWeapon / GaussRifle::FireWeapon chain up to Emitter::FireWeapon.
//
protected:
virtual void
FireWeapon(); // @004b95ec (abstract stub)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Subsystem virtual overrides (slots on vtable @00511d2c)
//
// Slot indices verified by diffing against the Subsystem vtable tail of
// @0050e170 and the PoweredSubsystem vtable @0050f9d8:
// slot 6 TakeDamage(Damage&) (Subsystem base @0041bd34)
// slot 7 WriteUpdateRecord(...) (Subsystem base @0041c500)
// slot 9 ReadUpdateRecord(...) (PoweredSubsystem base @004b0efc)
// slot 10 ResetToInitialState() (PoweredSubsystem base @004b0e6c)
// slot 13 PrintState() (PoweredSubsystem base @004b1224)
//
public:
void
TakeDamage(Damage &damage); // slot 6, @004b964c
void
ResetToInitialState(); // slot 10, @004b96ec
void
PrintState(); // slot 13, @004b9d00
protected:
void
WriteUpdateRecord( // slot 7, @004b9690
UpdateRecord *message,
int update_model
);
void
ReadUpdateRecord(UpdateRecord *message); // slot 9, @004b96d4
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Class Support
//
public:
static Logical
TestClass(Mech&);
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Targeting / aiming helpers (used by FireWeapon and the per-frame update)
//
protected:
// E3: read the owning Mech's CURRENT target slot (mech+0x388, the Entity* that
// mech4.cpp's targeting step writes) directly -- the old `hasTarget` member was
// never set (the Mech per-frame refresh isn't reconstructed) so the fire gate was
// permanently false. GetOwningSimulation is non-const, hence the cast.
Logical
HasActiveTarget() const
{
char *o = (char *)owner; // inherited MechSubsystem::owner (the Mech)
return (o != 0 && *(void **)(o + 0x388) != 0) ? True : False;
}
// @004b9bdc -- recompute rangeToTarget and the heat-degraded
// effectiveRange, then set targetWithinRange. No target => range -1.
// Returns the resulting targetWithinRange flag (consumed by Emitter /
// ProjectileWeapon simulation).
Logical
UpdateTargeting();
// @004b9608 -- rising-edge detector on fireImpulse (best-effort name);
// returns True the frame the weapon's discharge signal goes positive.
Logical
CheckFireEdge(); // TODO: confirm semantic (recharge/discharge edge)
// @004b9cbc -- copy the owning Mech's current target position.
void
GetTargetPosition(Point3D &position);
// @004b9cdc -- direction from a supplied point to the weapon mount.
void
GetVectorToWeapon(const Point3D &from, Vector3D &direction);
// @004b9948 -- resolve the weapon's muzzle/site world position from the
// owning Mech's segment table.
void
GetMuzzlePoint(Point3D &point);
// @004b9864 -- build an aim orientation (atan2 of a direction vector).
void
ComputeAimOrientation(LinearMatrix &orientation, const Vector3D &direction);
// @004b9728 -- register the targeting "pip" / fire marker with the
// owning Mech's cockpit HUD manager.
void
DrawWeaponPip(const LinearMatrix &transform);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
public:
typedef MechWeapon__SubsystemResource SubsystemResource;
MechWeapon(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data = DefaultData
);
~MechWeapon();
static int
CreateStreamedSubsystem( // @004b9d10
ResourceFile *resource_file,
NotationFile *model_file,
const char *model_name,
const char *subsystem_name,
SubsystemResource *subsystem_resource,
NotationFile *subsystem_file,
const ResourceDirectories *directories,
int passes = 1
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local data for the weapon base class.
// Offsets are byte offsets into the shipped object (PoweredSubsystem
// occupies everything below +0x31c).
//
protected:
// --- recharge / firing state ---
Scalar fireImpulse; // @0x31C reset 0 (edge-detected; best-effort name)
Scalar rechargeLevel; // @0x320 reset 1.0 (1.0 == fully charged/ready)
Scalar rangeToTarget; // @0x324 distance to current target (-1 if none)
Scalar effectiveRange; // @0x328 (1 - heatLoad) * weaponRange (heat-degraded)
Scalar weaponRange; // @0x32C resource WeaponRange (configured max)
int pipState; // @0x330 reset 0 (TODO: name -- pip/active state)
Logical usesExternalModel; // @0x334 resource model-name compare flag (best-effort)
int pipPosition; // @0x338 resource PipPosition
RGBColor pipColor; // @0x33C resource PipColor (3 floats, init -1,-1,-1)
int pipExtendedRange; // @0x348 resource PipExtendedRange
Logical targetWithinRange; // @0x34C PPC.CPP: "if (targetWithinRange)"
// --- damage / display alarm ---
AlarmIndicator weaponAlarm; // @0x350 alarm indicator (level count from ctor)
Scalar previousFireImpulse; // @0x3A4 reset 0 (fire-edge history, see @004b9608)
Damage damageData; // @0x3A8 damageType @0x3A8, damageAmount @0x3AC
// PPC.CPP: Damage energy_damage(damageData)
Scalar heatCostToFire; // @0x3D8 resource HeatCostToFire
Scalar rechargeRate; // @0x3DC resource RechargeRate
Logical useConfiguredPip; // @0x3E0 = (usesExternalModel == 0) (best-effort)
ResourceDescription::ResourceID
explosionResourceID; // @0x3E4 resource ExplosionModelFile
Scalar recoil; // @0x3E8 reset 0 (TODO: name)
int segmentPageIndex; // @0x3EC resource segmentIndex (+0x28)
// --- targeting/segment references (best-effort; used by GetMuzzlePoint /
// DrawWeaponPip). segmentReference indexes the owning Mech segment
// table; pipSegment is the HUD pip's mount segment (-1 == none). ---
int segmentReference; // muzzle segment lookup index
int pipSegment; // HUD pip mount segment (-1 == none)
// --- cached targeting kinematics (populated by the owning Mech's
// per-frame targeting/segment update; consumed by UpdateTargeting,
// GetTargetPosition, GetMuzzlePoint, GetVectorToWeapon). ---
Logical hasTarget; // owning mech has a locked target
Point3D targetPoint; // current target world position
Point3D muzzlePoint; // resolved weapon muzzle world position
};
#endif