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>
180 lines
5.8 KiB
C++
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
|