BT410 Phase 5.3.22: the cockpit weapon-button column -- generator panel LIVE
The full per-receiver message chain, contiguous ids 3..0xb: PoweredSubsystem 4-8 (SelectGeneratorA-D + ToggleGeneratorMode, binary table @0x50F4EC) chained on HeatSink's ToggleCooling(3); MechWeapon 9/10 (config buttons, staged latch bodies); Emitter 0xb ToggleSeekVoltage (@0x511DB8). SelectGenerator = release the old tap -> roster walk by generatorNumber -> AttachToVoltageSource -> Connected; mode toggle cycles Manual->Auto-> (detach)->Manual; the seek dial steps the authored voltage ladder with wrap (a step up re-enters Loading, charge persists). All novice-locked. Generator::UntapVoltageSource + PoweredSubsystem::DetachFromVoltageSource added. Dev harness: BT_PRESS_GEN=1..4 / BT_PRESS_SEEK. Verified: PPC_1 retaps GeneratorB (tapped); seek index 2->3, target 9900V (authored fraction x rated); novice presses INERT; missile fight 14/14 and smoke green, zero faults. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,21 @@
|
||||
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
|
||||
|
||||
@@ -951,6 +951,51 @@ void
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// BT_PRESS_GEN=1..4: SelectGenerator<N> at the first Emitter
|
||||
// (the generator panel re-tap); BT_PRESS_SEEK: ToggleSeekVoltage
|
||||
// at the first Emitter (the seek dial).
|
||||
//
|
||||
{
|
||||
const char *press_gen = getenv("BT_PRESS_GEN");
|
||||
const char *press_seek = getenv("BT_PRESS_SEEK");
|
||||
if (press_gen != NULL || press_seek != NULL)
|
||||
{
|
||||
for (int s = 2; s < subsystemCount; ++s)
|
||||
{
|
||||
Subsystem *sub = subsystemArray[s];
|
||||
if (sub != NULL
|
||||
&& sub->IsDerivedFrom(Emitter::ClassDerivations))
|
||||
{
|
||||
if (press_gen != NULL)
|
||||
{
|
||||
int n = atoi(press_gen);
|
||||
if (n >= 1 && n <= 4)
|
||||
{
|
||||
ReceiverDataMessageOf<int> press(
|
||||
PoweredSubsystem::
|
||||
SelectGeneratorAMessageID
|
||||
+ (n - 1),
|
||||
sizeof(ReceiverDataMessageOf<int>),
|
||||
1
|
||||
);
|
||||
sub->Dispatch(&press);
|
||||
}
|
||||
}
|
||||
if (press_seek != NULL)
|
||||
{
|
||||
ReceiverDataMessageOf<int> press(
|
||||
Emitter::ToggleSeekVoltageMessageID,
|
||||
sizeof(ReceiverDataMessageOf<int>),
|
||||
1
|
||||
);
|
||||
sub->Dispatch(&press);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,19 +38,26 @@ Derivation
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Message handlers. MechWeapon publishes no handlers of its own yet (the
|
||||
// weapon fire/damage handler wave), but the set MUST be a real defined object:
|
||||
// the surviving CODE PPC.CPP binds the inherited name (`PPC::MessageHandlers`)
|
||||
// into PPC::DefaultData, and a declared-but-undefined static links as a
|
||||
// zero-filled common block -- the same silent-NULL trap as the
|
||||
// Emitter::AttributeIndex crash (see MECHWEAP.NOTES.md).
|
||||
// Message handlers (binary ids 9/10 @004b9550/@004b95b8) -- the weapon-group
|
||||
// config buttons, chained onto the PoweredSubsystem generator panel (4-8),
|
||||
// which chains HeatSink (3): a weapon's dispatch reaches the whole cockpit
|
||||
// column. (The set MUST be a real defined object: the surviving CODE
|
||||
// PPC.CPP binds the inherited name into PPC::DefaultData, and a
|
||||
// declared-but-undefined static links as a zero-filled common block.)
|
||||
//#############################################################################
|
||||
//
|
||||
const MechWeapon::HandlerEntry
|
||||
MechWeapon::MessageHandlerEntries[] =
|
||||
{
|
||||
MESSAGE_ENTRY(MechWeapon, ConfigureMappables), // id 9 @004b9550
|
||||
MESSAGE_ENTRY(MechWeapon, ChooseButton) // id 10 @004b95b8
|
||||
};
|
||||
|
||||
MechWeapon::MessageHandlerSet
|
||||
MechWeapon::MessageHandlers(
|
||||
0,
|
||||
NULL,
|
||||
Subsystem::MessageHandlers
|
||||
ELEMENTS(MechWeapon::MessageHandlerEntries),
|
||||
MechWeapon::MessageHandlerEntries,
|
||||
PoweredSubsystem::MessageHandlers
|
||||
);
|
||||
|
||||
//
|
||||
@@ -196,6 +203,41 @@ MechWeapon::MechWeapon(
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The weapon-group config buttons (ids 9/10). STAGED: the binary bodies
|
||||
// (@004b9550/@004b95b8) drive the group-assign flow with the HUD config
|
||||
// page; until that page exists the press is latched (configureActivePress)
|
||||
// and logged -- the dispatch path itself is the live, authentic part.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
MechWeapon::ConfigureMappablesMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
configureActivePress = message->dataContents;
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[btn] '" << GetName()
|
||||
<< "' ConfigureMappables press=" << message->dataContents
|
||||
<< " (staged latch)" << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MechWeapon::ChooseButtonMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[btn] '" << GetName()
|
||||
<< "' ChooseButton press=" << message->dataContents
|
||||
<< " (staged)" << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
MechWeapon::~MechWeapon()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -68,8 +68,24 @@
|
||||
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
|
||||
|
||||
@@ -160,3 +160,22 @@ with burstCount=missileCount (burstCount feeds only gyro bounce + splash
|
||||
falloff — never the zone formula). MISLANCH now dispatches a single
|
||||
SendDamage per salvo (verified 1:1 FIRED:[dmg]). The cluster SPLASH component
|
||||
arrives with the Missile entity wave (proximity-fuse detonation).
|
||||
|
||||
## 5.3.22 — the cockpit weapon-button column
|
||||
|
||||
The full per-receiver message chain is registered (contiguous, no pads):
|
||||
HeatSink 3 ToggleCooling → PoweredSubsystem 4-8 (SelectGeneratorA-D @004b099c+,
|
||||
ToggleGeneratorMode @004b0abc — binary table @0x50F4EC) → MechWeapon 9/10
|
||||
(ConfigureMappables/ChooseButton @004b9550/@004b95b8 — STAGED press-latch
|
||||
bodies, the group-config flow needs the HUD page) → Emitter 0xb
|
||||
ToggleSeekVoltage (@004ba478, table @0x511DB8; the ammo branch's 0xb
|
||||
EjectAmmo is deferred — its absence is safe, no gap slot below a registered
|
||||
max). SelectGenerator = detach old tap → FindGeneratorByNumber roster walk →
|
||||
AttachToVoltageSource → modeAlarm Connected; ToggleGeneratorMode cycles
|
||||
Manual→AutoConnect→(detach)→Manual (the auto-hunt itself = a
|
||||
PoweredSubsystemSimulation brick). ToggleSeekVoltage steps the seek ladder
|
||||
with wrap; a step UP re-enters Loading (charge persists and climbs).
|
||||
All novice-locked. Dev: BT_PRESS_GEN=1..4 / BT_PRESS_SEEK (~6s dispatch at
|
||||
the first Emitter). Verified: PPC_1 → GeneratorB (tapped); seek 2→3 target
|
||||
9900V; novice presses inert; fight/smoke green. Static-init order is safe
|
||||
under the authentic .MAK lib order (heat→powersub→mechweap→emitter).
|
||||
|
||||
@@ -30,10 +30,35 @@ Derivation
|
||||
"PoweredSubsystem"
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The cockpit generator buttons (binary handler table @0x50F4EC, ids 4-8),
|
||||
// chained onto the HeatSink set (id 3 ToggleCooling) so every powered
|
||||
// subsystem's dispatch reaches the Eng-page Coolant button too. Static-init
|
||||
// order is safe under the authentic .MAK lib order (heat precedes powersub).
|
||||
//#############################################################################
|
||||
//
|
||||
const PoweredSubsystem::HandlerEntry
|
||||
PoweredSubsystem::MessageHandlerEntries[] =
|
||||
{
|
||||
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorA), // id 4 @004b099c
|
||||
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorB), // id 5 @004b09e4
|
||||
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorC), // id 6 @004b0a2c
|
||||
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorD), // id 7 @004b0a74
|
||||
MESSAGE_ENTRY(PoweredSubsystem, ToggleGeneratorMode) // id 8 @004b0abc
|
||||
};
|
||||
|
||||
PoweredSubsystem::MessageHandlerSet
|
||||
PoweredSubsystem::MessageHandlers(
|
||||
ELEMENTS(PoweredSubsystem::MessageHandlerEntries),
|
||||
PoweredSubsystem::MessageHandlerEntries,
|
||||
HeatSink::MessageHandlers
|
||||
);
|
||||
|
||||
PoweredSubsystem::SharedData
|
||||
PoweredSubsystem::DefaultData(
|
||||
PoweredSubsystem::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
PoweredSubsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
@@ -179,6 +204,171 @@ int
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DetachFromVoltageSource (binary @004b0e30): release the tap on the current
|
||||
// source and clear the connection.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
PoweredSubsystem::DetachFromVoltageSource()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Generator *source = (Generator *)voltageSource.Resolve();
|
||||
if (source != NULL)
|
||||
{
|
||||
source->UntapVoltageSource();
|
||||
voltageSource.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// FindGeneratorByNumber (binary @004b0b18): walk the owner's roster for the
|
||||
// Generator whose authored generatorNumber matches (1=A .. 4=D).
|
||||
//#############################################################################
|
||||
//
|
||||
Subsystem*
|
||||
PoweredSubsystem::FindGeneratorByNumber(int generator_number)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
for (int slot = 2; slot < owner->GetSubsystemCount(); ++slot)
|
||||
{
|
||||
Subsystem *sub = owner->GetSubsystem(slot);
|
||||
if (sub != NULL
|
||||
&& sub->IsDerivedFrom(Generator::ClassDerivations)
|
||||
&& ((Generator *)sub)->GetGeneratorNumber() == generator_number)
|
||||
{
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// SelectGenerator -- the manual re-tap: release the current source, tap
|
||||
// generator N, drop the connect mode back to Connected (a manual selection
|
||||
// ends any auto-hunt). The binary dereferences the find unguarded (authored
|
||||
// mechs always carry A-D); we skip loud instead.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
PoweredSubsystem::SelectGenerator(int generator_number)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Subsystem *generator = FindGeneratorByNumber(generator_number);
|
||||
if (generator == NULL)
|
||||
{
|
||||
if (getenv("BT_MECH_LOG") || getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[gensel] '" << GetName()
|
||||
<< "' -> generator " << generator_number
|
||||
<< " NOT FOUND" << endl << flush;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DetachFromVoltageSource();
|
||||
int tap = AttachToVoltageSource(generator);
|
||||
modeAlarm.SetLevel(Connected);
|
||||
|
||||
if (getenv("BT_MECH_LOG") || getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[gensel] '" << GetName()
|
||||
<< "' -> '" << generator->GetName() << "'"
|
||||
<< ((tap >= 0) ? " (tapped)" : " (REFUSED: no spare tap)")
|
||||
<< endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The cockpit button handlers (ids 4-8). Press-only (dataContents > 0),
|
||||
// novice-locked -- a novice cockpit's generator panel is inert.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
PoweredSubsystem::SelectGeneratorAMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
if (!NoviceLockout() && message->dataContents > 0)
|
||||
{
|
||||
SelectGenerator(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PoweredSubsystem::SelectGeneratorBMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
if (!NoviceLockout() && message->dataContents > 0)
|
||||
{
|
||||
SelectGenerator(2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PoweredSubsystem::SelectGeneratorCMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
if (!NoviceLockout() && message->dataContents > 0)
|
||||
{
|
||||
SelectGenerator(3);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PoweredSubsystem::SelectGeneratorDMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
if (!NoviceLockout() && message->dataContents > 0)
|
||||
{
|
||||
SelectGenerator(4);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ToggleGeneratorMode (id 8, binary @004b0abc): cycle Manual -> AutoConnect
|
||||
// -> (detach +) Manual. The auto-hunt itself (a shorted/dead source makes
|
||||
// the subsystem walk for a live generator) lives in
|
||||
// PoweredSubsystemSimulation -- a later brick.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
PoweredSubsystem::ToggleGeneratorModeMessageHandler(
|
||||
ReceiverDataMessageOf<int> *message)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (NoviceLockout() || message->dataContents <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ((unsigned)modeAlarm.GetLevel() < (unsigned)AutoConnect)
|
||||
{
|
||||
modeAlarm.SetLevel(AutoConnect);
|
||||
}
|
||||
else if (modeAlarm.GetLevel() == AutoConnect)
|
||||
{
|
||||
DetachFromVoltageSource();
|
||||
modeAlarm.SetLevel(ManualConnect);
|
||||
}
|
||||
if (getenv("BT_MECH_LOG") || getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[gensel] '" << GetName()
|
||||
<< "' mode -> " << modeAlarm.GetLevel() << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ChargeTimeScale -- the heat/firepower feedback (binary @004b0d50):
|
||||
|
||||
@@ -86,6 +86,46 @@
|
||||
// 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();
|
||||
|
||||
Subsystem*
|
||||
ResolveVoltageSource() { return voltageSource.Resolve(); }
|
||||
int
|
||||
@@ -309,6 +349,15 @@
|
||||
++currentTapCount;
|
||||
return 0;
|
||||
}
|
||||
void
|
||||
UntapVoltageSource()
|
||||
{
|
||||
Check(this);
|
||||
if (currentTapCount > 0)
|
||||
{
|
||||
--currentTapCount;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void
|
||||
(Generator::*Performance)(Scalar time_slice);
|
||||
|
||||
Reference in New Issue
Block a user