Files
TeslaRel410/restoration/source410/BT/EMITTER.HPP
T
CydandClaude Fable 5 0d97d8c38b BT410 5.3.45: six more watcher names published; the Reservoir one CRASHES (bisected)
Published and verified green on the pod rig: Condenser/CondenserState,
Generator/GeneratorState, Emitter/LaserOn, ControlsMapper/TargetRangeExponent,
plus the Torso and Myomers tables from the previous pass.

Reservoir/ReservoirState CRASHES the pod on the DOS extender, and a bisect
pins it to exactly that change: revert it and the run returns to a clean
Fail, re-apply it alone and the crash returns.  It is reverted; the tree is
green and the ladder is blocked there.

The important lesson is general: AttributeWatcherOf<T> does
currentValue = *(T*)attributePointer AT CONSTRUCTION, so a published name is
read the moment its watcher is built.  My earlier staging note claimed the
provisional types could not matter because nothing drives the values yet --
that is wrong, and this is the counterexample.

Ruled out by measurement and recorded so they are not retried: the
AlarmIndicator-vs-int type (a plain int got further, 232 -> 749 bytes of log,
but still crashed), static-init order (reservr.obj sorts last, after heat),
an id gap (contiguous at HeatSink::NextAttributeID), and the gauge rig (same
binary runs clean there -- only the pod/arena context faults).

Crash signature for whoever picks it up: 0044B4AD, mov eax,[edx+0x18] then
call [eax+4], EAX=0x15, fault at 0x19 -- a small integer called through as an
object, i.e. the AttributeIndexSet::Build uninitialised-slot pattern.
Condenser is the control: same base class, plain int, no crash.

Also fixed on the way: staged members must be appended at the END of a class
(offset-sensitive readers exist -- condenserNumber is reached as
master+0x1d4) and never added to a resource struct, which is a wire format.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 02:42:43 -05:00

180 lines
5.8 KiB
C++

//===========================================================================//
// File: emitter.hpp //
// Project: BattleTech //
// Contents: Implementation details for energy weapon emitters //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#if !defined(EMITTER_HPP)
# define EMITTER_HPP
# if !defined(MECHWEAP_HPP)
# include <mechweap.hpp>
# endif
//##################### Forward Class Declarations #######################
class Mech;
//###########################################################################
//####################### Emitter Model Resource ########################
//###########################################################################
//
// WIRE-VERIFIED layout: graphicLength, dischargeTime, then the SeekVoltage
// curve -- five FRACTIONS of the generator's rated voltage (-1 sentinel
// ends the list) -- and the recommended (default) gear index. (The earlier
// two-field guess read seekVoltage[3] = 0.99 as "dischargeTime 0.99 s".)
//
struct Emitter__SubsystemResource:
public MechWeapon::SubsystemResource
{
Scalar graphicLength;
Scalar dischargeTime;
Scalar seekVoltage[5];
int seekVoltageRecommendedIndex;
};
//###########################################################################
//############################## Emitter ################################
//###########################################################################
class Emitter:
public MechWeapon
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation ClassDerivations;
static SharedData DefaultData;
static const HandlerEntry MessageHandlerEntries[];
static MessageHandlerSet MessageHandlers;
//
// The energy-weapon seek-voltage button (binary table @0x511DB8,
// id 0xb, chained after MechWeapon's 9/10; the ammo-weapon branch
// binds 0xb to EjectAmmo in ITS OWN table instead).
//
enum {
ToggleSeekVoltageMessageID = MechWeapon::NextMessageID, // 0xb
NextMessageID
};
void
ToggleSeekVoltageMessageHandler(ReceiverDataMessageOf<int> *message);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Attribute Support. The energy-weapon family publishes past the MechWeapon
// table end (0x1C) -- the binary's emitter IDs run 0x1D+.
//
public:
enum {
ChargeLevelAttributeID = MechWeapon::NextAttributeID, // 0x1D
LaserOnAttributeID, // authored watcher: <weapon>/LaserOn
NextAttributeID
};
static const IndexEntry AttributePointers[];
static AttributeIndexSet AttributeIndex;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Class Support
//
public:
static Logical
TestClass(Mech &);
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
public:
typedef Emitter__SubsystemResource SubsystemResource;
Emitter(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data = DefaultData
);
~Emitter();
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:
void
FireWeapon();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Per-frame simulation (the beam-weapon fire state machine) and the
// electrical charge model: the Loading state integrates currentLevel
// toward the powering generator's voltage (TrackSeekVoltage); Loaded snaps
// when the recharge dial reaches 1.0 (level ~= the selected seek voltage).
//
public:
typedef void
(Emitter::*Performance)(Scalar time_slice);
void
SetPerformance(Performance performance)
{
Check(this);
activePerformance = (Simulation::Performance)performance;
}
void
EmitterSimulation(Scalar time_slice);
void
ResetFiringState();
void
TrackSeekVoltage(Scalar time_slice);
virtual void
ComputeOutputVoltage(); // the charge-voltage form (level/seekV)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local Data. chargeLevel is the ChargeLevel attribute alias of the raw
// charge voltage (currentLevel in the binary's naming).
//
protected:
Scalar chargeLevel; // the raw charge voltage (binary currentLevel @0x414)
Scalar dischargeTime;
Scalar dischargeTimer;
Scalar graphicLength;
Scalar seekRate;
Scalar energyCoefficient;
Scalar energyTotal;
Scalar damageFraction;
int laserOn; // staged: authored watcher <weapon>/LaserOn
Scalar damagePortion;
Scalar heatPortion;
int firingActive;
int seekVoltageIndex;
int seekVoltageRecommendedIndex;
int minSeekVoltageIndex;
int maxSeekVoltageIndex;
Scalar seekVoltage[5];
};
#endif