Files
TeslaRel410/restoration/source410/BT/MECHWEAP.HPP
T
CydandClaude Fable 5 b499c88cb7 BT410 5.3.137: the ammo cooks off -- a mech can finally be gutted by its own magazine
The blocking identity is settled: DamageZoneClassID == 78 == 0x4e,
computed from our own VDATA enum, so the binary's collector filter
selects DAMAGE ZONES and the blast is shared across the mech's zones.

Landed: AmmoBin::AmmoBinSimulation -- a Performance the class never had,
installed under the same master+dynamic gate the binary's ctor uses --
and AmmoBin::CookOff.  A bin whose fed weapon reaches heat failure
latches a randomised fuse; when it burns down the bin is destroyed and
its remaining rounds go off inside the mech, damage scaled by rounds
remaining and SHARED across the zones, every victim announced.

Correcting my own 5.3.136 write-up: heat clearing does NOT cancel the
countdown.  Both the arm gate and the cancel read the same cell -- the
ammo alarm settled on spent -- so once armed, only spending or destroying
the bin calls it off.  Letting the gun cool does not save you.

Live on the rig: three bins arm, the fuse spread sends them off on
different frames rather than together, 20 rounds x 25 = 500 damage over
22 zones from one bin and 16 x 50 = 800 from another, feeding straight
into the existing destruction cascade -- Searchlight and ThermalSight
destroyed as a consequence.

[T1] recorded: the per-round charge is a stand-in.  The binary reads it
from the bin's streamed explosion record, in the block our cookOffState
reserves and nothing parses yet; we use the fed weapon's authored
per-shot damage until that record is read.

And a mistake worth keeping in the notes: the first attempt sourced the
charge from heatPerRound, a heat figure of about 3e-9, so the detonation
computed 4.8e-08 of damage and did precisely nothing -- a mechanic that
fires, announces itself, and is silently inert.  Only printing the
arithmetic caught it.  Log the numbers, not just the event.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 23:27:09 -05:00

287 lines
9.7 KiB
C++

//===========================================================================//
// File: mechweap.hpp //
// Project: BattleTech //
// Contents: Implementation details for mech weapons //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#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
//##################### Forward Class Declarations #######################
class Mech;
//###########################################################################
//###################### MechWeapon Model Resource ######################
//###########################################################################
//
// WIRE-VERIFIED layout (raw-stream dump vs the BT411 verified overlay):
// eleven fields; the pip tail is pipPosition(int) + pipColor(3 floats) +
// pipExtendedRange(int). bhk1 PPC reads: recharge 5.0s, range 900,
// damage 12, type 4, heatCost 11, pipColor (0,0,1).
//
struct MechWeapon__SubsystemResource:
public PoweredSubsystem::SubsystemResource
{
Scalar rechargeRate;
Scalar weaponRange;
ResourceDescription::ResourceID
explosionModelFile;
Scalar damageAmount;
int damageType;
Scalar heatCostToFire;
int pipPosition;
Scalar pipColor[3];
int pipExtendedRange;
};
//###########################################################################
//############################ MechWeapon ###############################
//###########################################################################
class MechWeapon:
public PoweredSubsystem
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation ClassDerivations;
static SharedData DefaultData;
static const HandlerEntry MessageHandlerEntries[];
static MessageHandlerSet MessageHandlers;
//
// The weapon-group config buttons (binary ids 9/10, chained after
// the PoweredSubsystem generator panel 4-8).
//
enum {
ConfigureMappablesMessageID = PoweredSubsystem::NextMessageID, // 9
ChooseButtonMessageID, // 10
NextMessageID // 11
};
void
ConfigureMappablesMessageHandler(ReceiverDataMessageOf<int> *message);
void
ChooseButtonMessageHandler(ReceiverDataMessageOf<int> *message);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Attribute Support. The real IDs are PINNED to the 1995 binary numbering
// (table @0x511890, ids 0x12..0x1C): the streamed per-mech control mappings
// reference them by NUMBER -- TriggerState (0x13) is the fire-button binding.
// Our parent chain publishes nothing past Subsystem::NextAttributeID (2)
// yet, so pads bridge the gap 2..0x11 (they shrink to the authentic
// 0x0F..0x11 once the mechsub/heat/powersub attribute waves publish --
// SENSOR.HPP pins PoweredSubsystem::NextAttributeID at 0x0F). The pads are
// LOAD-BEARING: AttributeIndexSet::Build leaves unlisted gap slots as
// uninitialized garbage, so every ID up to the max must be covered.
//
public:
enum {
MechWeaponPadFirstAttributeID = PoweredSubsystem::NextAttributeID, // 0x0F
PercentDoneAttributeID = 0x12,
TriggerStateAttributeID, // 0x13 -- the streamed fire-button binding
DistanceToTargetAttributeID, // 0x14
TargetWithinRangeAttributeID, // 0x15
WeaponRangeAttributeID, // 0x16
PipPositionAttributeID, // 0x17
PipColorAttributeID, // 0x18
PipExtendedRangeAttributeID, // 0x19
EstimatedReadyTimeAttributeID, // 0x1A
RearFiringAttributeID, // 0x1B
WeaponStateAttributeID, // 0x1C (binary table end)
NextAttributeID // 0x1D (the Emitter family starts here)
};
static const IndexEntry AttributePointers[];
static AttributeIndexSet AttributeIndex;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Class Support
//
public:
static Logical
TestClass(Mech &);
Logical
TestInstance() const;
//
// The projectile/explosion GameModel this weapon spawns (binary
// @0x3e4) -- read by SubsystemMessageManager's consolidator to
// bundle the firing weapon's explosion with the damage it caused.
//
ResourceDescription::ResourceID
ExplosionResourceIDOf()
{ Check(this); return explosionResourceID; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 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(
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
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Weapon interface
//
public:
virtual void
FireWeapon();
void
SendDamage(
Entity *target,
Damage &damage
);
//
// View-fire gating (the look-state commit re-arms these): a weapon
// mounted on a back gun port (rearFiring) fires only into the LOOK-BACK
// view; the rest fire only in the forward view.
//
Logical
IsRearFiring() const { Check(this); return rearFiring; }
Logical
GetViewFireEnable() const { Check(this); return viewFireEnable; }
void
SetViewFireEnable(Logical enable){ Check(this); viewFireEnable = enable; }
//
// Fire state machine. The weapon state is carried in the weapon alarm
// level: 0 = Firing, 2 = Loaded (ready), 3 = Loading, 4 = the
// trigger-during-load blip. CheckFireEdge is the rising-edge detector
// on fireImpulse (binary @004b9608): TriggerState carries the raw
// ControlsButton INT bit-copied by the streamed direct mapping, so the
// compare works on the BIT PATTERNS as signed ints (sign-correct for
// the button ints AND genuine floats), reproducing the binary's x87
// semantics. ComputeOutputVoltage (@004b9c9c) is the recharge-dial
// writer.
//
enum {
FiringState = 0,
DryTriggerState = 1,
LoadedState = 2,
LoadingState = 3,
TriggerDuringLoadState = 4,
JammedState = 5,
TriggerDuringJamState = 6,
NoAmmoState = 7,
WeaponStateCount = 8
};
unsigned
GetWeaponState() { Check(this); return weaponAlarm.GetLevel(); }
//
// The authored per-shot damage. Read by the ammo cook-off as its
// [T1] stand-in for the per-round charge (see AMMOBIN.CPP).
//
Scalar
DamageAmountOf() const { Check(this); return damageAmount; }
Logical
CheckFireEdge();
virtual void
ComputeOutputVoltage(); // vtable slot 17; the Emitter overrides
// with its charge-voltage form
//
// Targeting: the owner mech's target slot gates the Loaded->Firing
// transition (no target = the denial blip, no shot); UpdateTargeting
// (binary @004b9bdc) refreshes rangeToTarget / targetWithinRange from
// the target's position each frame.
//
Logical
HasActiveTarget();
Logical
UpdateTargeting();
//
// Respawn restore: base heal + the fire machine back to a fresh
// Loading cycle (charge/recoil/trigger cleared; jams and the NoAmmo
// roach-motel release -- the bins restock in their own DeathReset).
//
virtual void
DeathReset(Logical full_reset);
//
// The muzzle world position (binary @004b9948): the MOUNT segment's
// world frame -- rounds and beams leave FROM THE GUN, following the
// torso twist. Falls back to the mech origin when the mount
// segment doesn't resolve.
//
void
GetMuzzlePoint(Point3D &point);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local Data
//
protected:
Scalar rechargeRate;
Scalar weaponRange;
Scalar damageAmount;
int damageType;
Scalar heatCostToFire;
ResourceDescription::ResourceID
explosionResourceID; // binary @0x3e4 -- the projectile /
// explosion GameModel this weapon spawns
int targetWithinRange;
Damage damageData;
Logical rearFiring;
Logical viewFireEnable;
//
// Fire-machine state (binary @0x31C/0x320/0x324/0x328/0x330/0x350/
// 0x3A4/0x3E8): the trigger sample pair, the recharge dial, targeting
// ranges, and the weapon-state alarm.
//
Scalar fireImpulse;
Scalar previousFireImpulse;
Scalar rechargeLevel;
Scalar rangeToTarget;
Scalar effectiveRange;
int estimatedReadyTime;
Scalar recoil;
AlarmIndicator weaponAlarm;
};
#endif