Files
TeslaRel410/restoration/source410/BT/POWERSUB.HPP
T
CydandClaude Fable 5 24ea06574c BT410 Phase 5.3.28: the visual A/B pass -- 93% pixel-identical to the shipped cockpit
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>
2026-07-26 22:29:01 -05:00

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