Audio Phase 1 (AUDIO_FIDELITY F3/F4/F10/F15/F18/F20 + KB): the quick wins

F3 distance: alDistanceModel(AL_NONE); restored the commented authored
  distance multiply in Dynamic3D::CalculateSourceVolumeScale, added the same
  override to Static3D, dropped the (now inert) AL_MAX_DISTANCE writes.  Far
  battle audio follows the authored knee/rolloff curve again, and the volume
  cull / voice steal / ducking chains are distance-aware.
F4 volume law: AL_GAIN = volume_scale^2 at all 3 per-frame sites (Direct/
  Dyn3D/Static3D) -- the GM CC7 squared curve; linear was ~+6 dB at mids.
F10 doppler: alDopplerFactor(0) (AL's model ran wrong constants + sign-
  inverted velocity: approaching sources pitched DOWN); the AUTHORED
  dopplerCents (AUDIO.INI range/speed-of-sound model, decomp-proven consumer
  part_008.c:7466) now adds into the Dynamic3D pitch chain.
F15 ConfigureActivePress: published at the MechSubsystem BASE (binary id 2,
  descriptor @0x50de5c -> +0x110); renamed the misnamed vitalSubsystemIndex
  member (the weapon ConfigureMappables handler already drives it 0/-1).
  Removed the invented Sensor/Myomers duplicates and RESTORED their byte-
  exact layouts (0x328/0x358 allocs + asserts).  MechWeapon's pinned-id pad
  absorbed the +1 chain shift -- which matches the binary's own numbering.
F18 cook-off warning: AmmoBin FireCountdownStarted -> the existing
  cookOffArmed @0x18C (binary table @0x512600); the countdown klaxon can fire.
F20 zoom blip: L4MechControlsMapper publishes TargetRangeExponent -> the live
  @0x1a4 zoom member (own table chained to MechControlsMapper).
KB: replaced the bogus divisionParameters+0x10 rate read with
  SystemClock::GetTicksPerSecond() (FUN_0044e19c is GetFrameRate -- original
  audio frames were CLOCK TICKS).

Regression (35s drive+fire): stable; ConfigureActivePress binds real on all 9
subsystems (idle -1, zero pad redirects); FireCountdownStarted +
TargetRangeExponent bind live members; attrnull 53 -> 41; chirp still dead;
footfalls still fire (gain now correctly squared).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-16 10:43:38 -05:00
co-authored by Claude Opus 4.8
parent 05cb3f5549
commit fc7f311771
16 changed files with 162 additions and 81 deletions
+9 -2
View File
@@ -95,8 +95,15 @@ void
} }
headEntitySocket.Add(entity); headEntitySocket.Add(entity);
alDistanceModel(AL_LINEAR_DISTANCE); // FIDELITY (AUDIO_FIDELITY.md F3/F10): the engine computes the AUTHORED
alDopplerFactor(0.3f); // distance-attenuation curve (AUDIO.INI amplitude_rolloff knee/exponent ->
// AudioLocation::distanceVolumeScale) and the authored doppler-cents model.
// Disable OpenAL's own models so they can't double-apply / fight them:
// AL_LINEAR_DISTANCE made far battle audio fade to zero on a straight line
// (-6 dB vs authored at 300u), and AL doppler ran with the wrong constants
// AND a sign-inverted velocity feed (approaching sources pitched DOWN).
alDistanceModel(AL_NONE);
alDopplerFactor(0.0f);
#if 0 #if 0
// //
+33 -17
View File
@@ -1106,8 +1106,7 @@ void
<< " vol=" << volume_scale << "\n" << std::flush; } << " vol=" << volume_scale << "\n" << std::flush; }
for (int i=0; i < channelSet.count; i++) for (int i=0; i < channelSet.count; i++)
{ {
alSourcef(channelSet.sources[i],AL_MAX_DISTANCE,audio_location->getMaxDistance(audio_head)); alSourcef(channelSet.sources[i], AL_GAIN, volume_scale * volume_scale); // FIDELITY F4: CC7 squared law (linear was ~+6 dB at mid volumes)
alSourcef(channelSet.sources[i], AL_GAIN, volume_scale);
} }
} }
@@ -1439,6 +1438,13 @@ void
pitch_offset = CalculateSourcePitchOffset(); pitch_offset = CalculateSourcePitchOffset();
// FIDELITY (AUDIO_FIDELITY.md F10): the AUTHORED doppler (AUDIO.INI
// doppler_range/speed_of_sound -> AudioLocation::dopplerCents, computed each
// spatial update) -- the decomp proves the original added it to the pitch
// cents on THIS dynamic path (part_008.c:7466). AL's own doppler is now
// disabled (it ran with wrong constants and a sign-inverted velocity feed).
pitch_offset += GetAudioLocation()->GetDopplerCents();
double relativePitch = pow(2.0,pitch_offset/1200.0); double relativePitch = pow(2.0,pitch_offset/1200.0);
Clamp(relativePitch,0.5,2.0); Clamp(relativePitch,0.5,2.0);
{ // apply the combined control-chain + note pitch per frame { // apply the combined control-chain + note pitch per frame
@@ -1456,9 +1462,8 @@ void
for (int i=0; i < channelSet.count; i++) for (int i=0; i < channelSet.count; i++)
{ {
alSource3f(channelSet.sources[i],AL_POSITION,pos.x,pos.y,pos.z); alSource3f(channelSet.sources[i],AL_POSITION,pos.x,pos.y,pos.z);
alSourcef(channelSet.sources[i], AL_GAIN, volume_scale); alSourcef(channelSet.sources[i], AL_GAIN, volume_scale * volume_scale); // FIDELITY F4: CC7 squared law (linear was ~+6 dB at mid volumes)
alSource3f(channelSet.sources[i],AL_VELOCITY,-relative_velocity.x,-relative_velocity.y,-relative_velocity.z); alSource3f(channelSet.sources[i],AL_VELOCITY,-relative_velocity.x,-relative_velocity.y,-relative_velocity.z);
alSourcef(channelSet.sources[i],AL_MAX_DISTANCE,audio_location->getMaxDistance(audio_head));
} }
} }
@@ -1477,22 +1482,18 @@ AudioControlValue
// //
Scalar Scalar
volume_scale = L4AudioSource::CalculateSourceVolumeScale(); volume_scale = L4AudioSource::CalculateSourceVolumeScale();
return volume_scale;
// //
// Update the spatial model that will result in the value // FIDELITY (AUDIO_FIDELITY.md F3): apply the AUTHORED distance attenuation
// for distance related volume attenuation // (AUDIO.INI knee/rolloff curve, computed into distanceVolumeScale on every
// // spatial update). This multiply was commented out and AL_LINEAR_DISTANCE
/*Check(application); // substituted -- far sounds faded on a straight line to zero instead of the
Check(application->GetAudioRenderer()); // authored 1/(1+(k(d-knee))^2) curve, and the volume-based cull / voice
UpdateSpatialModel(application->GetAudioRenderer()->GetAudioHead()); // steal / ducking chains were distance-blind. AL's model is now AL_NONE.
//
// Apply distance attenuation to the volume scale
// //
Check(GetAudioLocation()); Check(GetAudioLocation());
volume_scale *= GetAudioLocation()->GetDistanceVolumeScale(); volume_scale *= GetAudioLocation()->GetDistanceVolumeScale();
return volume_scale;*/ return volume_scale;
} }
//############################################################################# //#############################################################################
@@ -1682,6 +1683,22 @@ void
// StartRequest(this); // StartRequest(this);
} }
//
//#############################################################################
//#############################################################################
//
AudioControlValue
Static3DPatchSource::CalculateSourceVolumeScale()
{
Check(this);
// FIDELITY (AUDIO_FIDELITY.md F3): same authored distance attenuation as the
// dynamic path -- the spatial model computes distanceVolumeScale per execute.
Scalar volume_scale = L4AudioSource::CalculateSourceVolumeScale();
Check(GetAudioLocation());
volume_scale *= GetAudioLocation()->GetDistanceVolumeScale();
return volume_scale;
}
// //
//############################################################################# //#############################################################################
//############################################################################# //#############################################################################
@@ -1992,9 +2009,8 @@ void
//Static models have their position freely available as relative positions and stand still //Static models have their position freely available as relative positions and stand still
for (int i=0; i < channelSet.count; i++) for (int i=0; i < channelSet.count; i++)
{ {
alSourcef(channelSet.sources[i], AL_GAIN, volume_scale); alSourcef(channelSet.sources[i], AL_GAIN, volume_scale * volume_scale); // FIDELITY F4: CC7 squared law (linear was ~+6 dB at mid volumes)
alSource3f(channelSet.sources[i],AL_POSITION,relative_position.x,relative_position.y,relative_position.z); alSource3f(channelSet.sources[i],AL_POSITION,relative_position.x,relative_position.y,relative_position.z);
alSourcef(channelSet.sources[i],AL_MAX_DISTANCE,audio_location->getMaxDistance(audio_head));
} }
// //
+4
View File
@@ -552,6 +552,10 @@ public:
// State implementations // State implementations
// //
public: public:
// FIDELITY (AUDIO_FIDELITY.md F3): statics get the authored distance curve too
AudioControlValue
CalculateSourceVolumeScale();
void void
StartImplementation(); StartImplementation();
void void
+2 -1
View File
@@ -101,7 +101,8 @@ Receiver::MessageHandlerSet AmmoBin::MessageHandlers;
const AmmoBin::IndexEntry const AmmoBin::IndexEntry
AmmoBin::AttributePointers[]= AmmoBin::AttributePointers[]=
{ {
ATTRIBUTE_ENTRY(AmmoBin, AmmoState, ammoAlarm) // @0x194 (0x54 StateIndicator-compatible feed alarm) ATTRIBUTE_ENTRY(AmmoBin, AmmoState, ammoAlarm), // @0x194 (0x54 StateIndicator-compatible feed alarm)
ATTRIBUTE_ENTRY(AmmoBin, FireCountdownStarted, cookOffArmed) // F18 @0x18C -- ammo cook-off warning gate
}; };
Simulation::AttributeIndexSet AmmoBin::AttributeIndex( Simulation::AttributeIndexSet AmmoBin::AttributeIndex(
+4
View File
@@ -120,6 +120,10 @@
// was a bare, unchained index, so even SimulationState resolved to NULL. // was a bare, unchained index, so even SimulationState resolved to NULL.
enum { enum {
AmmoStateAttributeID = HeatWatcher::NextAttributeID, AmmoStateAttributeID = HeatWatcher::NextAttributeID,
FireCountdownStartedAttributeID, // F18: the ammo COOK-OFF warning (binary
// AmmoBin table @0x512600 -> +0x18C); the 4
// authored start/stop match watchers gate the
// cook-off countdown klaxon on it
NextAttributeID NextAttributeID
}; };
static const IndexEntry AttributePointers[]; static const IndexEntry AttributePointers[];
+13 -19
View File
@@ -280,27 +280,21 @@ BTL4Application::SharedData
} }
// //
// FUN_0044e19c read the sample rate as a float out of the division // AUDIO CLOCK RATE (AUDIO_FIDELITY.md KB correction): FUN_0044e19c is
// parameter block (@+0x10). In WinTesla that block is the public // ApplicationManager::GetFrameRate() -- the ORIGINAL audio "frames" were
// L4Application::divisionParameters char*; fall back to 1000.0f (the // system CLOCK TICKS (the DOS 18.2/28 Hz timer), not a sample rate. The
// RP default) when it is absent. Second arg is the mission-review // old port read a float from divisionParameters+0x10 (a raw 1995-layout
// flag (the 1995 binary hard-coded it off; DAT_004fd550 == 0). // offset = garbage, -1.6e14 measured). The Windows equivalent of the
// original semantic is the system tick rate: AudioHead::Execute stamps
// audioFrameCount from SystemClock ticks, so calibrating the renderer to
// the same tick rate keeps every AudioTime conversion unit-consistent.
// //
RendererRate sample_rate = long ticks_per_second = SystemClock::GetTicksPerSecond();
divisionParameters if (ticks_per_second <= 0) ticks_per_second = 1000; // GetTickCount ms (static not yet measured)
? (RendererRate)*reinterpret_cast<float*>(divisionParameters + 0x10) RendererRate sample_rate = (RendererRate)ticks_per_second;
: (RendererRate)1000.0f;
// DATABINDING GUARD (task #50): divisionParameters is a raw engine block
// pointer and +0x10 is a 1995-layout offset; on the port it read GARBAGE
// (-1.6e14 measured), poisoning calibrationRate -> every AudioTime
// conversion (sequence event scheduling, compression durations) broke,
// which is why AudioControlSequence timeline events (footstep volumes!)
// never landed. Sanity-clamp to the authored 1000 frames/sec default.
if (!(sample_rate > 0.0f && sample_rate <= 100000.0f))
sample_rate = (RendererRate)1000.0f;
if (getenv("BT_AUDIO_LOG")) if (getenv("BT_AUDIO_LOG"))
DEBUG_STREAM << "[audio] renderer sample_rate=" << sample_rate DEBUG_STREAM << "[audio] renderer calibration rate=" << sample_rate
<< " (divisionParameters=" << (void*)divisionParameters << ")" << "\n" << std::flush; << " (SystemClock ticks/sec)" << "\n" << std::flush;
return return
new BTL4AudioRenderer( new BTL4AudioRenderer(
+21 -1
View File
@@ -244,11 +244,31 @@ static void
//############################################################################# //#############################################################################
// Shared Data Support // Shared Data Support
// //
// TargetRangeExponent -> the live @0x1a4 zoom member (AUDIO_FIDELITY.md F20):
// the authored AudioScalarDeltaTrigger blips per zoom step; it bound NULL until
// this publication. Chained to MechControlsMapper's dense index.
const L4MechControlsMapper::IndexEntry
L4MechControlsMapper::AttributePointers[]=
{
ATTRIBUTE_ENTRY(L4MechControlsMapper, TargetRangeExponent, targetRangeExponent) // @0x1a4
};
L4MechControlsMapper::AttributeIndexSet&
L4MechControlsMapper::GetAttributeIndex()
{
static L4MechControlsMapper::AttributeIndexSet attributeIndex(
ELEMENTS(L4MechControlsMapper::AttributePointers),
L4MechControlsMapper::AttributePointers,
MechControlsMapper::GetAttributeIndex()
);
return attributeIndex;
}
L4MechControlsMapper::SharedData L4MechControlsMapper::SharedData
L4MechControlsMapper::DefaultData( L4MechControlsMapper::DefaultData(
L4MechControlsMapper::GetClassDerivations(), L4MechControlsMapper::GetClassDerivations(),
L4MechControlsMapper::GetMessageHandlers(), L4MechControlsMapper::GetMessageHandlers(),
L4MechControlsMapper::GetAttributeIndex(), // inherited from MechControlsMapper L4MechControlsMapper::GetAttributeIndex(), // own table -> chained to MechControlsMapper
L4MechControlsMapper::StateCount L4MechControlsMapper::StateCount
); );
+14
View File
@@ -159,6 +159,20 @@
); );
~L4MechControlsMapper(); // @004d1814 ~L4MechControlsMapper(); // @004d1814
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Attribute Support (AUDIO_FIDELITY.md F20): the authored zoom-blip
// AudioScalarDeltaTrigger binds ControlsMapper/TargetRangeExponent -- the
// live @0x1a4 member that slews every frame. Chained to the parent index;
// Thrustmaster/RIO inherit this accessor.
//
public:
enum {
TargetRangeExponentAttributeID = MechControlsMapper::NextAttributeID,
L4NextAttributeID
};
static const IndexEntry AttributePointers[];
static AttributeIndexSet& GetAttributeIndex();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Protected data (object grows to >= 0x1bc) // Protected data (object grows to >= 0x1bc)
// //
+21 -3
View File
@@ -89,11 +89,29 @@ static const StatusNameEntry StatusNameTable[] =
//############################################################################# //#############################################################################
// Shared Data Support // Shared Data Support
// //
// ConfigureActivePress -> the +0x110 session flag (AUDIO_FIDELITY.md F15).
const MechSubsystem::IndexEntry
MechSubsystem::AttributePointers[]=
{
ATTRIBUTE_ENTRY(MechSubsystem, ConfigureActivePress, configureActivePress) // id 2 @0x110
};
MechSubsystem::AttributeIndexSet&
MechSubsystem::GetAttributeIndex()
{
static MechSubsystem::AttributeIndexSet attributeIndex(
ELEMENTS(MechSubsystem::AttributePointers),
MechSubsystem::AttributePointers,
Simulation::GetAttributeIndex()
);
return attributeIndex;
}
MechSubsystem::SharedData MechSubsystem::SharedData
MechSubsystem::DefaultData( MechSubsystem::DefaultData(
&MechSubsystem::ClassDerivations, &MechSubsystem::ClassDerivations,
ReconMessageHandlers, ReconMessageHandlers,
ReconAttributeIndex, MechSubsystem::GetAttributeIndex(),
MechSubsystem::StateCount MechSubsystem::StateCount
); );
@@ -132,7 +150,7 @@ MechSubsystem::MechSubsystem(
criticalReference = 0; // this[0x42] criticalReference = 0; // this[0x42]
collisionCriticalHitWeight = 0; // this[0x43] collisionCriticalHitWeight = 0; // this[0x43]
printSimulationState = 0; // this[0x41] printSimulationState = 0; // this[0x41]
vitalSubsystemIndex = -1; // this[0x44] configureActivePress = -1; // this[0x44] configure-session idle
damageZone = (ReconDamageZone *)new DamageZone(this, 0); // this[0x38] = FUN_0041de1c(new 0x160, this, 0) damageZone = (ReconDamageZone *)new DamageZone(this, 0); // this[0x38] = FUN_0041de1c(new 0x160, this, 0)
} }
@@ -162,7 +180,7 @@ MechSubsystem::MechSubsystem(
controlDestination = 0; // this[0x3a]=param_6 (ctor pass-through; controlDestination = 0; // this[0x3a]=param_6 (ctor pass-through;
controlMessageID = 0; // this[0x3b]=param_7 weapons override -- task #6) controlMessageID = 0; // this[0x3b]=param_7 weapons override -- task #6)
resource = subsystem_resource; // this[0x3c] resource = subsystem_resource; // this[0x3c]
vitalSubsystemIndex = -1; // this[0x44] configureActivePress = -1; // this[0x44] configure-session idle
vitalSubsystem = (subsystem_resource->vitalSubsystemIndex == 1); // res+0x48==1 vitalSubsystem = (subsystem_resource->vitalSubsystemIndex == 1); // res+0x48==1
BindName(refCount, subsystem_resource->videoObjectName); // res+0x4c BindName(refCount, subsystem_resource->videoObjectName); // res+0x4c
+19 -1
View File
@@ -137,6 +137,21 @@ class Damage;
static Recon MessageHandlers; // SharedData ctor operand static Recon MessageHandlers; // SharedData ctor operand
enum { StateCount = 7 }; // TechStatusTypeCount enum { StateCount = 7 }; // TechStatusTypeCount
// Attribute Support (AUDIO_FIDELITY.md F15): the binary registers
// ConfigureActivePress as a MechSubsystem-BASE attribute (id 2,
// descriptor @0x50de5c -> subsystem+0x110) -- the configure-session
// flag every weapon's ConfigureMappables handler drives (0 open /
// -1 idle) and the authored configure-ticker audio watches at
// thresh=-1. Publishing here makes it resolve on EVERY subsystem
// (each reads its own +0x110) and restores the binary-authentic
// dense id numbering (children start at 3, not 2).
enum {
ConfigureActivePressAttributeID = Simulation::NextAttributeID, // 2
NextAttributeID // 3
};
static const IndexEntry AttributePointers[];
static AttributeIndexSet& GetAttributeIndex();
// alarm-changed callback (vtable slot +0x34) + host back-pointer // alarm-changed callback (vtable slot +0x34) + host back-pointer
void OnAlarmChanged(); void OnAlarmChanged();
Mech *owner; // owning mech (== hostEntity) Mech *owner; // owning mech (== hostEntity)
@@ -307,7 +322,10 @@ class Damage;
int printSimulationState; // @0x104 this[0x41] int printSimulationState; // @0x104 this[0x41]
Scalar criticalReference; // @0x108 this[0x42] Scalar criticalReference; // @0x108 this[0x42]
Scalar collisionCriticalHitWeight; // @0x10C this[0x43] Scalar collisionCriticalHitWeight; // @0x10C this[0x43]
int vitalSubsystemIndex; // @0x110 this[0x44] (default -1) int configureActivePress; // @0x110 this[0x44] (default -1) -- the
// configure-session flag (was misnamed
// vitalSubsystemIndex; the reader is the
// authored audio configure-ticker, F15)
}; };
#endif #endif
+7 -9
View File
@@ -126,15 +126,13 @@ const MechWeapon::IndexEntry
// The five PADS fill our chain-vs-binary id gap (0x0D..0x11) with valid, // The five PADS fill our chain-vs-binary id gap (0x0D..0x11) with valid,
// named, never-bound entries (the binary's own ids here are the powered/ // named, never-bound entries (the binary's own ids here are the powered/
// aux-screen family we don't publish); target = rechargeLevel, harmless. // aux-screen family we don't publish); target = rechargeLevel, harmless.
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 0, "MechWeaponPad0D", { (int)MechWeapon::MechWeaponPadFirstAttributeID + 0, "MechWeaponPad0E",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel }, (Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 1, "MechWeaponPad0E", { (int)MechWeapon::MechWeaponPadFirstAttributeID + 1, "MechWeaponPad0F",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel }, (Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 2, "MechWeaponPad0F", { (int)MechWeapon::MechWeaponPadFirstAttributeID + 2, "MechWeaponPad10",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel }, (Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 3, "MechWeaponPad10", { (int)MechWeapon::MechWeaponPadFirstAttributeID + 3, "MechWeaponPad11",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 4, "MechWeaponPad11",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel }, (Simulation::AttributePointer)&MechWeapon::rechargeLevel },
ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel), // 0x12 @0x320 ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel), // 0x12 @0x320
ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // 0x13 @0x31C ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // 0x13 @0x31C
@@ -183,7 +181,7 @@ MechWeapon::SharedData
// Release: close the session (mode restored, temp layer stripped). // Release: close the session (mode restored, temp layer stripped).
// The +0x110 write (0 open / -1 idle) is the binary's config-session flag // The +0x110 write (0 open / -1 idle) is the binary's config-session flag
// (ctor default -1, part_012.c:15775); the port field carries the older name // (ctor default -1, part_012.c:15775); the port field carries the older name
// vitalSubsystemIndex -- no confirmed reader yet (read-site survey pending). // configureActivePress -- the reader is the authored audio configure ticker (F15).
// //
void void
MechWeapon::ConfigureMappablesMessageHandler( MechWeapon::ConfigureMappablesMessageHandler(
@@ -205,11 +203,11 @@ void
if (value < 1) if (value < 1)
{ {
mapper->ExitConfiguration(element); // vtbl+0x3C mapper->ExitConfiguration(element); // vtbl+0x3C
vitalSubsystemIndex = -1; // this+0x110 -> idle configureActivePress = -1; // this+0x110 -> idle (audio ticker OFF)
} }
else else
{ {
vitalSubsystemIndex = 0; // this+0x110 -> session open configureActivePress = 0; // this+0x110 -> session open (audio ticker ON)
mapper->EnterConfiguration( // vtbl+0x38 mapper->EnterConfiguration( // vtbl+0x38
element, element,
(ControlsButton *)&fireImpulse, // this+0x31C (TriggerState) (ControlsButton *)&fireImpulse, // this+0x31C (TriggerState)
+6 -2
View File
@@ -184,8 +184,12 @@ class CockpitHud;
// is a latent AV (gotcha #11; the old pinned table survived on // is a latent AV (gotcha #11; the old pinned table survived on
// heap luck -- the renumber reshuffled allocations and it fired // heap luck -- the renumber reshuffled allocations and it fired
// live in WeaponCluster's "PercentDone" resolve). Pads below fill // live in WeaponCluster's "PercentDone" resolve). Pads below fill
// ids 0x0D..0x11 (the mech.cpp attrPad idiom). // ids 0x0E..0x11 (the mech.cpp attrPad idiom). F15: the base
MechWeaponPadFirstAttributeID = 0x0D, // == PoweredSubsystem::NextAttributeID (locked below) // MechSubsystem now publishes ConfigureActivePress (binary id 2),
// shifting the parent chain end 0x0D -> 0x0E -- which matches the
// binary's own numbering (the old 0x0D chain was one LOW because
// the base attribute was missing). One pad absorbed.
MechWeaponPadFirstAttributeID = 0x0E, // == PoweredSubsystem::NextAttributeID (locked below)
PercentDoneAttributeID = 0x12, // @0x320 rechargeLevel PercentDoneAttributeID = 0x12, // @0x320 rechargeLevel
TriggerStateAttributeID, // 0x13 @0x31C (PINNED -- the fire-button binding) TriggerStateAttributeID, // 0x13 @0x31C (PINNED -- the fire-button binding)
DistanceToTargetAttributeID, // 0x14 @0x324 rangeToTarget DistanceToTargetAttributeID, // 0x14 @0x324 rangeToTarget
+3 -6
View File
@@ -102,8 +102,7 @@ Myomers::AttributeIndexSet&
ATTRIBUTE_ENTRY(Myomers, MinSeekVoltageIndex, minSeekVoltageIndex), // 0x15 @0x328 ATTRIBUTE_ENTRY(Myomers, MinSeekVoltageIndex, minSeekVoltageIndex), // 0x15 @0x328
ATTRIBUTE_ENTRY(Myomers, MaxSeekVoltageIndex, maxSeekVoltageIndex), // 0x16 @0x32C ATTRIBUTE_ENTRY(Myomers, MaxSeekVoltageIndex, maxSeekVoltageIndex), // 0x16 @0x32C
ATTRIBUTE_ENTRY(Myomers, SeekVoltage, seekVoltage), // 0x17 @0x330 (array base) ATTRIBUTE_ENTRY(Myomers, SeekVoltage, seekVoltage), // 0x17 @0x330 (array base)
ATTRIBUTE_ENTRY(Myomers, OutputVoltage, outputVoltage), // 0x18 @0x344 ATTRIBUTE_ENTRY(Myomers, OutputVoltage, outputVoltage) // 0x18 @0x344
ATTRIBUTE_ENTRY(Myomers, ConfigureActivePress, configureActivePress) // 0x19 audio configure-ticker gate
}; };
static Myomers::AttributeIndexSet attributeIndex( static Myomers::AttributeIndexSet attributeIndex(
ELEMENTS(entries), entries, ELEMENTS(entries), entries,
@@ -165,7 +164,6 @@ Myomers::Myomers(
// vtable @005117dc installed by the compiler here. // vtable @005117dc installed by the compiler here.
speedEffect = 1.0f; // @0x31C speedEffect = 1.0f; // @0x31C
configureActivePress = -1; // APPENDED: no configure button held
heatRange = failureTemperature - degradationTemperature; // @0x348 (this[0x11C]-this[0x118]) heatRange = failureTemperature - degradationTemperature; // @0x348 (this[0x11C]-this[0x118])
heatRangeSquared = heatRange * heatRange; // @0x34C heatRangeSquared = heatRange * heatRange; // @0x34C
velocityEfficiency = resource->velocityEfficiency; // @0x350 res+0x190 velocityEfficiency = resource->velocityEfficiency; // @0x350 res+0x190
@@ -571,7 +569,7 @@ struct MyomersLayoutCheck
// .. accelerationEfficiency@0x354) land exactly at 0x31C..0x358. // .. accelerationEfficiency@0x354) land exactly at 0x31C..0x358.
static_assert(offsetof(Myomers, speedEffect) == 0x31C, "Myomers::speedEffect @0x31C (attr 0x12)"); static_assert(offsetof(Myomers, speedEffect) == 0x31C, "Myomers::speedEffect @0x31C (attr 0x12)");
static_assert(offsetof(Myomers, seekVoltage) == 0x330, "Myomers::seekVoltage @0x330 (attr 0x17)"); static_assert(offsetof(Myomers, seekVoltage) == 0x330, "Myomers::seekVoltage @0x330 (attr 0x17)");
static_assert(sizeof(Myomers) == 0x35C, "sizeof(Myomers) 0x358 binary + 4 appended (alloc bumped in CreateMyomersSubsystem)"); static_assert(sizeof(Myomers) == 0x358, "sizeof(Myomers) 0x358 (factory alloc, byte-exact)");
}; };
@@ -586,7 +584,6 @@ struct MyomersLayoutCheck
//===========================================================================// //===========================================================================//
Subsystem *CreateMyomersSubsystem(Mech *owner, int id, void *seg) Subsystem *CreateMyomersSubsystem(Mech *owner, int id, void *seg)
{ {
// 0x35C = the 0x358 binary object + 4 appended (configureActivePress; sizeof lock bumped too) return (Subsystem *) new (Memory::Allocate(0x358))
return (Subsystem *) new (Memory::Allocate(0x35C))
Myomers(owner, id, (Myomers::SubsystemResource *)seg); Myomers(owner, id, (Myomers::SubsystemResource *)seg);
} }
+1 -7
View File
@@ -162,8 +162,7 @@ class Mech;
MaxSeekVoltageIndexAttributeID, // 0x16 MaxSeekVoltageIndexAttributeID, // 0x16
SeekVoltageAttributeID, // 0x17 SeekVoltageAttributeID, // 0x17
OutputVoltageAttributeID, // 0x18 OutputVoltageAttributeID, // 0x18
ConfigureActivePressAttributeID, // 0x19 audio configure-ticker gate (idle -1) NextAttributeID // 0x19
NextAttributeID // 0x1a
}; };
private: private:
@@ -341,11 +340,6 @@ class Mech;
Scalar heatRangeSquared; // @0x34C ctor = heatRange*heatRange (degradation-curve denominator) Scalar heatRangeSquared; // @0x34C ctor = heatRange*heatRange (degradation-curve denominator)
Scalar velocityEfficiency; // @0x350 resource +0x190 Scalar velocityEfficiency; // @0x350 resource +0x190
Scalar accelerationEfficiency; // @0x354 resource +0x194 Scalar accelerationEfficiency; // @0x354 resource +0x194
int configureActivePress; // @0x358 APPENDED (audio): configure held-button
// index; -1 = none. The audio configure-ticker gates
// on > -1; the inert-pad 0 ticked forever (the chirp).
// Alloc + sizeof lock bumped 0x358 -> 0x35C together.
friend struct MyomersLayoutCheck; friend struct MyomersLayoutCheck;
}; };
+3 -6
View File
@@ -117,8 +117,7 @@ const Sensor::IndexEntry
{ {
ATTRIBUTE_ENTRY(Sensor, RadarPercent, radarPercent), // @0x31C (map capabilities ratio) ATTRIBUTE_ENTRY(Sensor, RadarPercent, radarPercent), // @0x31C (map capabilities ratio)
ATTRIBUTE_ENTRY(Sensor, SelfTest, selfTest), ATTRIBUTE_ENTRY(Sensor, SelfTest, selfTest),
ATTRIBUTE_ENTRY(Sensor, BadVoltage, badVoltage), ATTRIBUTE_ENTRY(Sensor, BadVoltage, badVoltage)
ATTRIBUTE_ENTRY(Sensor, ConfigureActivePress, configureActivePress) // audio configure-ticker gate
}; };
Sensor::AttributeIndexSet Sensor::AttributeIndexSet
@@ -163,7 +162,6 @@ Sensor::Sensor(
radarPercent = RadarBaseline; // @0x31C = 1.0f radarPercent = RadarBaseline; // @0x31C = 1.0f
selfTest = False; // @0x320 = 0 selfTest = False; // @0x320 = 0
badVoltage = False; // @0x324 = 0 badVoltage = False; // @0x324 = 0
configureActivePress = -1; // APPENDED: no configure button held (audio ticker OFF)
// this[0x28] |= 0x8 -- flag "has an active per-frame Performance". // this[0x28] |= 0x8 -- flag "has an active per-frame Performance".
SetHasPerformanceFlag(); // param_1[10] |= 8 SetHasPerformanceFlag(); // param_1[10] |= 8
@@ -412,7 +410,7 @@ struct SensorLayoutCheck
static_assert(offsetof(Sensor, radarPercent) == 0x31C, "Sensor::radarPercent @0x31C (attr 0x12)"); static_assert(offsetof(Sensor, radarPercent) == 0x31C, "Sensor::radarPercent @0x31C (attr 0x12)");
static_assert(offsetof(Sensor, selfTest) == 0x320, "Sensor::selfTest @0x320"); static_assert(offsetof(Sensor, selfTest) == 0x320, "Sensor::selfTest @0x320");
static_assert(offsetof(Sensor, badVoltage) == 0x324, "Sensor::badVoltage @0x324"); static_assert(offsetof(Sensor, badVoltage) == 0x324, "Sensor::badVoltage @0x324");
static_assert(sizeof(Sensor) == 0x32C, "sizeof(Sensor) 0x328 binary + 4 appended (alloc bumped in CreateSensorSubsystem)"); static_assert(sizeof(Sensor) == 0x328, "sizeof(Sensor) 0x328 (factory alloc, byte-exact)");
}; };
@@ -426,7 +424,6 @@ struct SensorLayoutCheck
//===========================================================================// //===========================================================================//
Subsystem *CreateSensorSubsystem(Mech *owner, int id, void *seg) Subsystem *CreateSensorSubsystem(Mech *owner, int id, void *seg)
{ {
// 0x32C = the 0x328 binary object + 4 appended (configureActivePress; sizeof lock bumped too) return (Subsystem *) new (Memory::Allocate(0x328))
return (Subsystem *) new (Memory::Allocate(0x32C))
Sensor(owner, id, (Sensor::SubsystemResource *)seg); Sensor(owner, id, (Sensor::SubsystemResource *)seg);
} }
+1 -6
View File
@@ -149,7 +149,6 @@
RadarPercentAttributeID = PoweredSubsystem::NextAttributeID, RadarPercentAttributeID = PoweredSubsystem::NextAttributeID,
SelfTestAttributeID, SelfTestAttributeID,
BadVoltageAttributeID, BadVoltageAttributeID,
ConfigureActivePressAttributeID, // audio configure-ticker gate (idle -1)
NextAttributeID NextAttributeID
}; };
@@ -167,11 +166,7 @@
Logical Logical
selfTest, // @0x320 ctor init 0 selfTest, // @0x320 ctor init 0
badVoltage; // @0x324 ctor init 0 badVoltage; // @0x324 ctor init 0
int
configureActivePress; // APPENDED (audio): the cockpit-configure held-button
// index; -1 = none. The audio config gates the
// configure-mode ticker on this > -1 -- the inert-pad 0
// read as "button 0 held" and ticked FOREVER (the chirp).
//########################################################################## //##########################################################################