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:
co-authored by
Claude Opus 4.8
parent
05cb3f5549
commit
fc7f311771
@@ -95,8 +95,15 @@ void
|
||||
}
|
||||
headEntitySocket.Add(entity);
|
||||
|
||||
alDistanceModel(AL_LINEAR_DISTANCE);
|
||||
alDopplerFactor(0.3f);
|
||||
// FIDELITY (AUDIO_FIDELITY.md F3/F10): the engine computes the AUTHORED
|
||||
// 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
|
||||
//
|
||||
|
||||
+34
-18
@@ -1106,8 +1106,7 @@ void
|
||||
<< " vol=" << volume_scale << "\n" << std::flush; }
|
||||
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);
|
||||
alSourcef(channelSet.sources[i], AL_GAIN, volume_scale * volume_scale); // FIDELITY F4: CC7 squared law (linear was ~+6 dB at mid volumes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1439,6 +1438,13 @@ void
|
||||
|
||||
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);
|
||||
Clamp(relativePitch,0.5,2.0);
|
||||
{ // apply the combined control-chain + note pitch per frame
|
||||
@@ -1456,9 +1462,8 @@ void
|
||||
for (int i=0; i < channelSet.count; i++)
|
||||
{
|
||||
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);
|
||||
alSourcef(channelSet.sources[i],AL_MAX_DISTANCE,audio_location->getMaxDistance(audio_head));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1477,22 +1482,18 @@ AudioControlValue
|
||||
//
|
||||
Scalar
|
||||
volume_scale = L4AudioSource::CalculateSourceVolumeScale();
|
||||
return volume_scale;
|
||||
|
||||
//
|
||||
// Update the spatial model that will result in the value
|
||||
// for distance related volume attenuation
|
||||
//
|
||||
/*Check(application);
|
||||
Check(application->GetAudioRenderer());
|
||||
UpdateSpatialModel(application->GetAudioRenderer()->GetAudioHead());
|
||||
|
||||
//
|
||||
// Apply distance attenuation to the volume scale
|
||||
//
|
||||
// FIDELITY (AUDIO_FIDELITY.md F3): apply the AUTHORED distance attenuation
|
||||
// (AUDIO.INI knee/rolloff curve, computed into distanceVolumeScale on every
|
||||
// spatial update). This multiply was commented out and AL_LINEAR_DISTANCE
|
||||
// substituted -- far sounds faded on a straight line to zero instead of the
|
||||
// authored 1/(1+(k(d-knee))^2) curve, and the volume-based cull / voice
|
||||
// steal / ducking chains were distance-blind. AL's model is now AL_NONE.
|
||||
//
|
||||
Check(GetAudioLocation());
|
||||
volume_scale *= GetAudioLocation()->GetDistanceVolumeScale();
|
||||
return volume_scale;*/
|
||||
return volume_scale;
|
||||
}
|
||||
|
||||
//#############################################################################
|
||||
@@ -1682,6 +1683,22 @@ void
|
||||
// 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
|
||||
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);
|
||||
alSourcef(channelSet.sources[i],AL_MAX_DISTANCE,audio_location->getMaxDistance(audio_head));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -552,6 +552,10 @@ public:
|
||||
// State implementations
|
||||
//
|
||||
public:
|
||||
// FIDELITY (AUDIO_FIDELITY.md F3): statics get the authored distance curve too
|
||||
AudioControlValue
|
||||
CalculateSourceVolumeScale();
|
||||
|
||||
void
|
||||
StartImplementation();
|
||||
void
|
||||
|
||||
@@ -101,7 +101,8 @@ Receiver::MessageHandlerSet AmmoBin::MessageHandlers;
|
||||
const AmmoBin::IndexEntry
|
||||
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(
|
||||
|
||||
@@ -120,6 +120,10 @@
|
||||
// was a bare, unchained index, so even SimulationState resolved to NULL.
|
||||
enum {
|
||||
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
|
||||
};
|
||||
static const IndexEntry AttributePointers[];
|
||||
|
||||
@@ -280,27 +280,21 @@ BTL4Application::SharedData
|
||||
}
|
||||
|
||||
//
|
||||
// FUN_0044e19c read the sample rate as a float out of the division
|
||||
// parameter block (@+0x10). In WinTesla that block is the public
|
||||
// L4Application::divisionParameters char*; fall back to 1000.0f (the
|
||||
// RP default) when it is absent. Second arg is the mission-review
|
||||
// flag (the 1995 binary hard-coded it off; DAT_004fd550 == 0).
|
||||
// AUDIO CLOCK RATE (AUDIO_FIDELITY.md KB correction): FUN_0044e19c is
|
||||
// ApplicationManager::GetFrameRate() -- the ORIGINAL audio "frames" were
|
||||
// system CLOCK TICKS (the DOS 18.2/28 Hz timer), not a sample rate. The
|
||||
// old port read a float from divisionParameters+0x10 (a raw 1995-layout
|
||||
// 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 =
|
||||
divisionParameters
|
||||
? (RendererRate)*reinterpret_cast<float*>(divisionParameters + 0x10)
|
||||
: (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;
|
||||
long ticks_per_second = SystemClock::GetTicksPerSecond();
|
||||
if (ticks_per_second <= 0) ticks_per_second = 1000; // GetTickCount ms (static not yet measured)
|
||||
RendererRate sample_rate = (RendererRate)ticks_per_second;
|
||||
if (getenv("BT_AUDIO_LOG"))
|
||||
DEBUG_STREAM << "[audio] renderer sample_rate=" << sample_rate
|
||||
<< " (divisionParameters=" << (void*)divisionParameters << ")" << "\n" << std::flush;
|
||||
DEBUG_STREAM << "[audio] renderer calibration rate=" << sample_rate
|
||||
<< " (SystemClock ticks/sec)" << "\n" << std::flush;
|
||||
|
||||
return
|
||||
new BTL4AudioRenderer(
|
||||
|
||||
@@ -244,11 +244,31 @@ static void
|
||||
//#############################################################################
|
||||
// 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::DefaultData(
|
||||
L4MechControlsMapper::GetClassDerivations(),
|
||||
L4MechControlsMapper::GetMessageHandlers(),
|
||||
L4MechControlsMapper::GetAttributeIndex(), // inherited from MechControlsMapper
|
||||
L4MechControlsMapper::GetAttributeIndex(), // own table -> chained to MechControlsMapper
|
||||
L4MechControlsMapper::StateCount
|
||||
);
|
||||
|
||||
|
||||
@@ -159,6 +159,20 @@
|
||||
);
|
||||
~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)
|
||||
//
|
||||
|
||||
@@ -89,11 +89,29 @@ static const StatusNameEntry StatusNameTable[] =
|
||||
//#############################################################################
|
||||
// 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::DefaultData(
|
||||
&MechSubsystem::ClassDerivations,
|
||||
ReconMessageHandlers,
|
||||
ReconAttributeIndex,
|
||||
MechSubsystem::GetAttributeIndex(),
|
||||
MechSubsystem::StateCount
|
||||
);
|
||||
|
||||
@@ -132,7 +150,7 @@ MechSubsystem::MechSubsystem(
|
||||
criticalReference = 0; // this[0x42]
|
||||
collisionCriticalHitWeight = 0; // this[0x43]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -162,7 +180,7 @@ MechSubsystem::MechSubsystem(
|
||||
controlDestination = 0; // this[0x3a]=param_6 (ctor pass-through;
|
||||
controlMessageID = 0; // this[0x3b]=param_7 weapons override -- task #6)
|
||||
resource = subsystem_resource; // this[0x3c]
|
||||
vitalSubsystemIndex = -1; // this[0x44]
|
||||
configureActivePress = -1; // this[0x44] configure-session idle
|
||||
vitalSubsystem = (subsystem_resource->vitalSubsystemIndex == 1); // res+0x48==1
|
||||
|
||||
BindName(refCount, subsystem_resource->videoObjectName); // res+0x4c
|
||||
|
||||
@@ -137,6 +137,21 @@ class Damage;
|
||||
static Recon MessageHandlers; // SharedData ctor operand
|
||||
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
|
||||
void OnAlarmChanged();
|
||||
Mech *owner; // owning mech (== hostEntity)
|
||||
@@ -307,7 +322,10 @@ class Damage;
|
||||
int printSimulationState; // @0x104 this[0x41]
|
||||
Scalar criticalReference; // @0x108 this[0x42]
|
||||
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
|
||||
|
||||
@@ -126,15 +126,13 @@ const MechWeapon::IndexEntry
|
||||
// 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/
|
||||
// aux-screen family we don't publish); target = rechargeLevel, harmless.
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 0, "MechWeaponPad0D",
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 0, "MechWeaponPad0E",
|
||||
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 1, "MechWeaponPad0E",
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 1, "MechWeaponPad0F",
|
||||
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 2, "MechWeaponPad0F",
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 2, "MechWeaponPad10",
|
||||
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 3, "MechWeaponPad10",
|
||||
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 4, "MechWeaponPad11",
|
||||
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 3, "MechWeaponPad11",
|
||||
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
|
||||
ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel), // 0x12 @0x320
|
||||
ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // 0x13 @0x31C
|
||||
@@ -183,7 +181,7 @@ MechWeapon::SharedData
|
||||
// Release: close the session (mode restored, temp layer stripped).
|
||||
// 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
|
||||
// vitalSubsystemIndex -- no confirmed reader yet (read-site survey pending).
|
||||
// configureActivePress -- the reader is the authored audio configure ticker (F15).
|
||||
//
|
||||
void
|
||||
MechWeapon::ConfigureMappablesMessageHandler(
|
||||
@@ -205,11 +203,11 @@ void
|
||||
if (value < 1)
|
||||
{
|
||||
mapper->ExitConfiguration(element); // vtbl+0x3C
|
||||
vitalSubsystemIndex = -1; // this+0x110 -> idle
|
||||
configureActivePress = -1; // this+0x110 -> idle (audio ticker OFF)
|
||||
}
|
||||
else
|
||||
{
|
||||
vitalSubsystemIndex = 0; // this+0x110 -> session open
|
||||
configureActivePress = 0; // this+0x110 -> session open (audio ticker ON)
|
||||
mapper->EnterConfiguration( // vtbl+0x38
|
||||
element,
|
||||
(ControlsButton *)&fireImpulse, // this+0x31C (TriggerState)
|
||||
|
||||
@@ -184,8 +184,12 @@ class CockpitHud;
|
||||
// is a latent AV (gotcha #11; the old pinned table survived on
|
||||
// heap luck -- the renumber reshuffled allocations and it fired
|
||||
// live in WeaponCluster's "PercentDone" resolve). Pads below fill
|
||||
// ids 0x0D..0x11 (the mech.cpp attrPad idiom).
|
||||
MechWeaponPadFirstAttributeID = 0x0D, // == PoweredSubsystem::NextAttributeID (locked below)
|
||||
// ids 0x0E..0x11 (the mech.cpp attrPad idiom). F15: the base
|
||||
// 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
|
||||
TriggerStateAttributeID, // 0x13 @0x31C (PINNED -- the fire-button binding)
|
||||
DistanceToTargetAttributeID, // 0x14 @0x324 rangeToTarget
|
||||
|
||||
@@ -102,8 +102,7 @@ Myomers::AttributeIndexSet&
|
||||
ATTRIBUTE_ENTRY(Myomers, MinSeekVoltageIndex, minSeekVoltageIndex), // 0x15 @0x328
|
||||
ATTRIBUTE_ENTRY(Myomers, MaxSeekVoltageIndex, maxSeekVoltageIndex), // 0x16 @0x32C
|
||||
ATTRIBUTE_ENTRY(Myomers, SeekVoltage, seekVoltage), // 0x17 @0x330 (array base)
|
||||
ATTRIBUTE_ENTRY(Myomers, OutputVoltage, outputVoltage), // 0x18 @0x344
|
||||
ATTRIBUTE_ENTRY(Myomers, ConfigureActivePress, configureActivePress) // 0x19 audio configure-ticker gate
|
||||
ATTRIBUTE_ENTRY(Myomers, OutputVoltage, outputVoltage) // 0x18 @0x344
|
||||
};
|
||||
static Myomers::AttributeIndexSet attributeIndex(
|
||||
ELEMENTS(entries), entries,
|
||||
@@ -165,7 +164,6 @@ Myomers::Myomers(
|
||||
// vtable @005117dc installed by the compiler here.
|
||||
|
||||
speedEffect = 1.0f; // @0x31C
|
||||
configureActivePress = -1; // APPENDED: no configure button held
|
||||
heatRange = failureTemperature - degradationTemperature; // @0x348 (this[0x11C]-this[0x118])
|
||||
heatRangeSquared = heatRange * heatRange; // @0x34C
|
||||
velocityEfficiency = resource->velocityEfficiency; // @0x350 res+0x190
|
||||
@@ -571,7 +569,7 @@ struct MyomersLayoutCheck
|
||||
// .. accelerationEfficiency@0x354) land exactly at 0x31C..0x358.
|
||||
static_assert(offsetof(Myomers, speedEffect) == 0x31C, "Myomers::speedEffect @0x31C (attr 0x12)");
|
||||
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)
|
||||
{
|
||||
// 0x35C = the 0x358 binary object + 4 appended (configureActivePress; sizeof lock bumped too)
|
||||
return (Subsystem *) new (Memory::Allocate(0x35C))
|
||||
return (Subsystem *) new (Memory::Allocate(0x358))
|
||||
Myomers(owner, id, (Myomers::SubsystemResource *)seg);
|
||||
}
|
||||
|
||||
@@ -162,8 +162,7 @@ class Mech;
|
||||
MaxSeekVoltageIndexAttributeID, // 0x16
|
||||
SeekVoltageAttributeID, // 0x17
|
||||
OutputVoltageAttributeID, // 0x18
|
||||
ConfigureActivePressAttributeID, // 0x19 audio configure-ticker gate (idle -1)
|
||||
NextAttributeID // 0x1a
|
||||
NextAttributeID // 0x19
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -341,11 +340,6 @@ class Mech;
|
||||
Scalar heatRangeSquared; // @0x34C ctor = heatRange*heatRange (degradation-curve denominator)
|
||||
Scalar velocityEfficiency; // @0x350 resource +0x190
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -117,8 +117,7 @@ const Sensor::IndexEntry
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Sensor, RadarPercent, radarPercent), // @0x31C (map capabilities ratio)
|
||||
ATTRIBUTE_ENTRY(Sensor, SelfTest, selfTest),
|
||||
ATTRIBUTE_ENTRY(Sensor, BadVoltage, badVoltage),
|
||||
ATTRIBUTE_ENTRY(Sensor, ConfigureActivePress, configureActivePress) // audio configure-ticker gate
|
||||
ATTRIBUTE_ENTRY(Sensor, BadVoltage, badVoltage)
|
||||
};
|
||||
|
||||
Sensor::AttributeIndexSet
|
||||
@@ -163,7 +162,6 @@ Sensor::Sensor(
|
||||
radarPercent = RadarBaseline; // @0x31C = 1.0f
|
||||
selfTest = False; // @0x320 = 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".
|
||||
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, selfTest) == 0x320, "Sensor::selfTest @0x320");
|
||||
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)
|
||||
{
|
||||
// 0x32C = the 0x328 binary object + 4 appended (configureActivePress; sizeof lock bumped too)
|
||||
return (Subsystem *) new (Memory::Allocate(0x32C))
|
||||
return (Subsystem *) new (Memory::Allocate(0x328))
|
||||
Sensor(owner, id, (Sensor::SubsystemResource *)seg);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,6 @@
|
||||
RadarPercentAttributeID = PoweredSubsystem::NextAttributeID,
|
||||
SelfTestAttributeID,
|
||||
BadVoltageAttributeID,
|
||||
ConfigureActivePressAttributeID, // audio configure-ticker gate (idle -1)
|
||||
NextAttributeID
|
||||
};
|
||||
|
||||
@@ -167,11 +166,7 @@
|
||||
Logical
|
||||
selfTest, // @0x320 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).
|
||||
|
||||
|
||||
|
||||
//##########################################################################
|
||||
|
||||
Reference in New Issue
Block a user