Ran the reconstructed exe and the shipped ALPHA_1 binary against the SAME GAUGE content, same mount, gauges on, and diffed the 640x480x16 framebuffers. OUR EXE DRAWS THE COCKPIT: 90.1% pixel-identical on the first run, and every difference traced to a single root cause. The authored aux-screen block (screen number / background placement / label) lives in the subsystem resource -- which is freed with the Mech ctor's stream buffer (the 5.3.24 use-after-free landmine). Both this tree and the BT411 port had stubbed it, which is why BT411's displays show the same artifacting. PoweredSubsystem now caches the block at ctor time with accessors, and btl4gau2 uses the AUTHORED screen instead of a roster-order stand-in and builds the placement strip art. Panels snapped onto their authored screens -- weapon dials now land on the same panels as the shipped exe, mech icons appear on the sensor/myomer panels, generator letters correct -- taking the match to 93.2% identical / 85% coverage. Remaining differences are donor-inherited stubs: the panel title art (now proven to come from the q-strip statusImage path, not auxScreenLabel -- the label blit is wired and guarded regardless), the pilot name bitmap, and some lamp frames. The A/B rig is banked at emulator/render-bridge/gauge-ab/ (both confs + the window-grab script) so the comparison is repeatable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
484 lines
15 KiB
C++
484 lines
15 KiB
C++
//===========================================================================//
|
|
// File: powersub.hpp //
|
|
// Project: BattleTech //
|
|
// Contents: Implementation details for powered subsystems //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#if !defined(POWERSUB_HPP)
|
|
# define POWERSUB_HPP
|
|
|
|
# if !defined(HEAT_HPP)
|
|
# include <heat.hpp>
|
|
# endif
|
|
|
|
//##################### Forward Class Declarations #######################
|
|
class Mech;
|
|
|
|
//###########################################################################
|
|
//################### PoweredSubsystem Model Resource ###################
|
|
//###########################################################################
|
|
|
|
struct PoweredSubsystem__SubsystemResource:
|
|
public HeatSink::SubsystemResource
|
|
{
|
|
int voltageSourceIndex;
|
|
Scalar thermalResistivityCoefficient;
|
|
int auxScreenNumber;
|
|
int auxScreenPlacement;
|
|
char auxScreenLabel[64];
|
|
char engScreenLabel[64];
|
|
Scalar startTime;
|
|
};
|
|
|
|
//###########################################################################
|
|
//######################### PoweredSubsystem ############################
|
|
//###########################################################################
|
|
|
|
class PoweredSubsystem:
|
|
public HeatSink
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data Support
|
|
//
|
|
public:
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Attribute Support. The electrical family; NextAttributeID lands on
|
|
// the AUTHENTIC 0x0F -- the surviving SENSOR.HPP numbers RadarPercent
|
|
// off it and MechWeapon's pinned table starts its real IDs at 0x12
|
|
// (three pads bridge 0x0F..0x11). Do not renumber without re-pinning
|
|
// both.
|
|
//
|
|
public:
|
|
enum {
|
|
InputVoltageAttributeID = HeatSink::NextAttributeID, // 0x0A
|
|
OutputVoltageAttributeID, // 0x0B
|
|
RatedVoltageAttributeID, // 0x0C
|
|
VoltageStateAttributeID, // 0x0D
|
|
ConnectModeAttributeID, // 0x0E
|
|
NextAttributeID // 0x0F
|
|
};
|
|
|
|
static const IndexEntry AttributePointers[];
|
|
static AttributeIndexSet AttributeIndex;
|
|
|
|
static Derivation ClassDerivations;
|
|
static SharedData DefaultData;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Test Class Support
|
|
//
|
|
public:
|
|
static Logical
|
|
TestClass(Mech &);
|
|
Logical
|
|
TestInstance() const;
|
|
void
|
|
ResetToInitialState(Logical powered);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Electrical state machine (carried in electricalStateAlarm, 5 levels) and
|
|
// the connection-mode indicator (modeAlarm, 3 levels).
|
|
//
|
|
public:
|
|
enum ElectricalState {
|
|
Starting = 0, // powering up; startTimer counts toward startTime
|
|
NoVoltage = 1, // voltage source missing / unresolvable
|
|
Shorted = 2, // source generator shorted
|
|
GeneratorOff = 3, // source generator off / not ready
|
|
Ready = 4 // powered and operating
|
|
};
|
|
|
|
enum ConnectMode {
|
|
ManualConnect = 0,
|
|
Connected = 1,
|
|
AutoConnect = 2
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Power support
|
|
//
|
|
public:
|
|
//
|
|
// The cockpit generator-select buttons (binary handler table
|
|
// @0x50F4EC, ids 4-8, chained onto the HeatSink set so id 3
|
|
// ToggleCooling stays reachable through every powered subsystem).
|
|
//
|
|
enum {
|
|
SelectGeneratorAMessageID = HeatSink::NextMessageID, // 4
|
|
SelectGeneratorBMessageID, // 5
|
|
SelectGeneratorCMessageID, // 6
|
|
SelectGeneratorDMessageID, // 7
|
|
ToggleGeneratorModeMessageID, // 8
|
|
NextMessageID // 9
|
|
};
|
|
|
|
static const HandlerEntry MessageHandlerEntries[];
|
|
static MessageHandlerSet MessageHandlers;
|
|
|
|
void
|
|
SelectGeneratorAMessageHandler(ReceiverDataMessageOf<int> *message);
|
|
void
|
|
SelectGeneratorBMessageHandler(ReceiverDataMessageOf<int> *message);
|
|
void
|
|
SelectGeneratorCMessageHandler(ReceiverDataMessageOf<int> *message);
|
|
void
|
|
SelectGeneratorDMessageHandler(ReceiverDataMessageOf<int> *message);
|
|
void
|
|
ToggleGeneratorModeMessageHandler(ReceiverDataMessageOf<int> *message);
|
|
|
|
//
|
|
// The manual generator re-tap (binary @004b0b18/@004b0dd8 family):
|
|
// find generator N in the owner's roster, release the current tap,
|
|
// tap the new source, drop the connect mode to Connected.
|
|
//
|
|
void
|
|
SelectGenerator(int generator_number);
|
|
Subsystem*
|
|
FindGeneratorByNumber(int generator_number);
|
|
void
|
|
DetachFromVoltageSource();
|
|
|
|
//
|
|
// Respawn restore: thermal + electrical re-init (the FSM restarts
|
|
// from GeneratorOff/Starting exactly like spawn).
|
|
//
|
|
virtual void
|
|
DeathReset(Logical full_reset);
|
|
|
|
//
|
|
// The AUTHORED aux-screen assignment (streamed). CACHED at ctor
|
|
// time because MechSubsystem::resource points into the Mech ctor's
|
|
// padded stream copy, which is FREED before the ctor returns -- the
|
|
// 5.3.24 use-after-free landmine. The cockpit's engineering panels
|
|
// key their screen number, background placement and title label off
|
|
// these.
|
|
//
|
|
int
|
|
GetAuxScreenNumber() const { Check(this); return auxScreenNumber; }
|
|
int
|
|
GetAuxScreenPlacement() const { Check(this); return auxScreenPlacement; }
|
|
const char*
|
|
GetAuxScreenLabel() const { Check(this); return auxScreenLabel; }
|
|
const char*
|
|
GetEngScreenLabel() const { Check(this); return engScreenLabel; }
|
|
|
|
Subsystem*
|
|
ResolveVoltageSource() { return voltageSource.Resolve(); }
|
|
int
|
|
AttachToVoltageSource(Subsystem *source);
|
|
unsigned
|
|
GetVoltageState() { Check(this); return electricalStateAlarm.GetLevel(); }
|
|
|
|
//
|
|
// The charge time-scale (binary @004b0d50) -- the heat/firepower
|
|
// feedback: the base voltageScale (the ctor-calibrated exponential
|
|
// charge constant) stretched by the powering generator's temperature
|
|
// rise above its start point.
|
|
//
|
|
Scalar
|
|
ChargeTimeScale();
|
|
|
|
//
|
|
// Short event (binary @004b11bc): drive the powering generator into
|
|
// the Shorted state and zero its output -- the electrical FSM then
|
|
// runs its short-recovery cycle. Gated on the not-novice experience
|
|
// predicate (both hops share the mech's player). Called by the
|
|
// special-damage path (a type-4 Energy hit on a zone with attached
|
|
// generator criticals).
|
|
//
|
|
void
|
|
ForceShortRecovery();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Per-frame simulation (heat step + the electrical state machine).
|
|
//
|
|
public:
|
|
typedef void
|
|
(PoweredSubsystem::*Performance)(Scalar time_slice);
|
|
void
|
|
SetPerformance(Performance performance)
|
|
{
|
|
Check(this);
|
|
activePerformance = (Simulation::Performance)performance;
|
|
}
|
|
|
|
void
|
|
PoweredSubsystemSimulation(Scalar time_slice);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction
|
|
//
|
|
public:
|
|
typedef PoweredSubsystem__SubsystemResource SubsystemResource;
|
|
|
|
PoweredSubsystem(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data = DefaultData
|
|
);
|
|
|
|
~PoweredSubsystem();
|
|
|
|
static int
|
|
CreateStreamedSubsystem(
|
|
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
|
|
//
|
|
protected:
|
|
Scalar inputVoltage;
|
|
Scalar outputVoltage;
|
|
Scalar ratedVoltage;
|
|
SubsystemConnection voltageSource;
|
|
AlarmIndicator electricalStateAlarm;
|
|
AlarmIndicator modeAlarm;
|
|
int auxScreenNumber;
|
|
int auxScreenPlacement;
|
|
char auxScreenLabel[64];
|
|
char engScreenLabel[64];
|
|
Scalar thermalResistivityCoefficient;
|
|
Scalar startTime;
|
|
Scalar startTimer;
|
|
Scalar voltageScale;
|
|
};
|
|
|
|
//###########################################################################
|
|
//################### PowerWatcher Model Resource ######################
|
|
//###########################################################################
|
|
|
|
struct PowerWatcher__SubsystemResource:
|
|
public HeatWatcher::SubsystemResource
|
|
{
|
|
Scalar minVoltagePercent;
|
|
};
|
|
|
|
//###########################################################################
|
|
//############################ PowerWatcher ############################
|
|
//###########################################################################
|
|
//
|
|
// A HeatWatcher that also watches its subject's supply voltage. Base of the
|
|
// Torso / HUD / Gyroscope cockpit subsystems.
|
|
//
|
|
class PowerWatcher:
|
|
public HeatWatcher
|
|
{
|
|
public:
|
|
static Derivation ClassDerivations;
|
|
static SharedData DefaultData;
|
|
|
|
static Logical
|
|
TestClass(Mech &);
|
|
Logical
|
|
TestInstance() const;
|
|
void
|
|
ResetToInitialState(Logical powered);
|
|
//
|
|
// Respawn restore: base heal + the watchdog re-baseline (the
|
|
// HeatWatcher::DeathReset it would otherwise inherit binds the
|
|
// NON-virtual ResetToInitialState statically and skips this one).
|
|
//
|
|
virtual void
|
|
DeathReset(Logical full_reset);
|
|
void
|
|
Simulation(Scalar time_slice);
|
|
|
|
public:
|
|
typedef PowerWatcher__SubsystemResource SubsystemResource;
|
|
|
|
PowerWatcher(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data = DefaultData
|
|
);
|
|
~PowerWatcher();
|
|
|
|
protected:
|
|
Scalar minVoltage;
|
|
AlarmIndicator watchdogAlarm;
|
|
};
|
|
|
|
//###########################################################################
|
|
//#################### Generator Model Resource ########################
|
|
//###########################################################################
|
|
|
|
struct Generator__SubsystemResource:
|
|
public HeatSink::SubsystemResource
|
|
{
|
|
Scalar ratedVoltage;
|
|
int maxTapCount;
|
|
Scalar startTime;
|
|
Scalar shortRecoveryTime;
|
|
};
|
|
|
|
//###########################################################################
|
|
//############################## Generator #############################
|
|
//###########################################################################
|
|
//
|
|
// The voltage SOURCE a PoweredSubsystem attaches to (currentTapCount tracks
|
|
// attached loads). A HeatSink that produces power.
|
|
//
|
|
class Generator:
|
|
public HeatSink
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data Support
|
|
//
|
|
public:
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Attribute Support (the generator panel reads these by name). A
|
|
// SIBLING branch of PoweredSubsystem off HeatSink -- the shared 0x0A
|
|
// base is correct, each branch owns its own index set.
|
|
//
|
|
public:
|
|
enum {
|
|
OutputVoltageAttributeID = HeatSink::NextAttributeID, // 0x0A
|
|
RatedVoltageAttributeID, // 0x0B
|
|
GeneratorNumberAttributeID, // 0x0C
|
|
NextAttributeID
|
|
};
|
|
|
|
static const IndexEntry AttributePointers[];
|
|
static AttributeIndexSet AttributeIndex;
|
|
|
|
static Derivation ClassDerivations;
|
|
static SharedData DefaultData;
|
|
|
|
enum {
|
|
GeneratorOff = 0,
|
|
GeneratorStarting,
|
|
GeneratorReady,
|
|
GeneratorShorted,
|
|
GeneratorFailed,
|
|
GeneratorStateCount
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Test / reset
|
|
//
|
|
public:
|
|
static Logical
|
|
TestClass(Mech &);
|
|
Logical
|
|
TestInstance() const;
|
|
void
|
|
ResetToInitialState();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Power interface
|
|
//
|
|
public:
|
|
Scalar MeasuredVoltage() { Check(this); return outputVoltage; }
|
|
Scalar RatedVoltageOf() { Check(this); return ratedVoltage; }
|
|
int GetGeneratorNumber(){ Check(this); return generatorNumber; }
|
|
unsigned
|
|
GeneratorStateOf() { Check(this); return stateAlarm.GetLevel(); }
|
|
|
|
//
|
|
// The short event landing ON this generator (the tail of binary
|
|
// @004b11bc): Shorted state + output killed; GeneratorSimulation
|
|
// runs the short-recovery timer back to Ready. Callers gate on the
|
|
// novice experience predicate.
|
|
//
|
|
void
|
|
ForceShort()
|
|
{
|
|
Check(this);
|
|
stateAlarm.SetLevel(GeneratorShorted);
|
|
outputVoltage = 0.0f;
|
|
}
|
|
|
|
//
|
|
// Tap the generator for one load (a PoweredSubsystem attaching): -1 when
|
|
// every tap is taken, else 0.
|
|
//
|
|
int
|
|
TapVoltageSource()
|
|
{
|
|
Check(this);
|
|
if (currentTapCount >= maxTapCount)
|
|
{
|
|
return -1;
|
|
}
|
|
++currentTapCount;
|
|
return 0;
|
|
}
|
|
virtual void
|
|
DeathReset(Logical full_reset);
|
|
|
|
void
|
|
UntapVoltageSource()
|
|
{
|
|
Check(this);
|
|
if (currentTapCount > 0)
|
|
{
|
|
--currentTapCount;
|
|
}
|
|
}
|
|
|
|
typedef void
|
|
(Generator::*Performance)(Scalar time_slice);
|
|
void
|
|
SetPerformance(Performance performance)
|
|
{
|
|
Check(this);
|
|
activePerformance = (Simulation::Performance)performance;
|
|
}
|
|
|
|
void
|
|
GeneratorSimulation(Scalar time_slice);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction
|
|
//
|
|
public:
|
|
typedef Generator__SubsystemResource SubsystemResource;
|
|
|
|
Generator(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data = DefaultData
|
|
);
|
|
|
|
~Generator();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Local Data
|
|
//
|
|
public:
|
|
Scalar percentVoltageAvailable;
|
|
int generatorOn;
|
|
Scalar ratedVoltage;
|
|
Scalar outputVoltage;
|
|
int generatorNumber;
|
|
int maxTapCount;
|
|
int currentTapCount;
|
|
Scalar startTime;
|
|
Scalar startTimer;
|
|
Scalar shortRecoveryTime;
|
|
Scalar shortTimer;
|
|
AlarmIndicator stateAlarm;
|
|
};
|
|
|
|
#endif
|