From fc7f3117712cd8e667769b85c44b1d850cfb314b Mon Sep 17 00:00:00 2001 From: arcattack Date: Thu, 16 Jul 2026 10:43:38 -0500 Subject: [PATCH] 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) --- engine/MUNGA/AUDIO.cpp | 11 +++++-- engine/MUNGA_L4/L4AUDIO.cpp | 52 +++++++++++++++++++++------------ engine/MUNGA_L4/L4AUDIO.h | 4 +++ game/reconstructed/ammobin.cpp | 3 +- game/reconstructed/ammobin.hpp | 4 +++ game/reconstructed/btl4app.cpp | 32 +++++++++----------- game/reconstructed/btl4mppr.cpp | 22 +++++++++++++- game/reconstructed/btl4mppr.hpp | 14 +++++++++ game/reconstructed/mechsub.cpp | 24 +++++++++++++-- game/reconstructed/mechsub.hpp | 20 ++++++++++++- game/reconstructed/mechweap.cpp | 16 +++++----- game/reconstructed/mechweap.hpp | 8 +++-- game/reconstructed/myomers.cpp | 9 ++---- game/reconstructed/myomers.hpp | 8 +---- game/reconstructed/sensor.cpp | 9 ++---- game/reconstructed/sensor.hpp | 7 +---- 16 files changed, 162 insertions(+), 81 deletions(-) diff --git a/engine/MUNGA/AUDIO.cpp b/engine/MUNGA/AUDIO.cpp index 9afc689..1d0edc5 100644 --- a/engine/MUNGA/AUDIO.cpp +++ b/engine/MUNGA/AUDIO.cpp @@ -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 // diff --git a/engine/MUNGA_L4/L4AUDIO.cpp b/engine/MUNGA_L4/L4AUDIO.cpp index 552e50d..538f1e5 100644 --- a/engine/MUNGA_L4/L4AUDIO.cpp +++ b/engine/MUNGA_L4/L4AUDIO.cpp @@ -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)); } // diff --git a/engine/MUNGA_L4/L4AUDIO.h b/engine/MUNGA_L4/L4AUDIO.h index b0eaa1e..67a634b 100644 --- a/engine/MUNGA_L4/L4AUDIO.h +++ b/engine/MUNGA_L4/L4AUDIO.h @@ -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 diff --git a/game/reconstructed/ammobin.cpp b/game/reconstructed/ammobin.cpp index caa9577..0125e4f 100644 --- a/game/reconstructed/ammobin.cpp +++ b/game/reconstructed/ammobin.cpp @@ -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( diff --git a/game/reconstructed/ammobin.hpp b/game/reconstructed/ammobin.hpp index 06d8846..e37c3d7 100644 --- a/game/reconstructed/ammobin.hpp +++ b/game/reconstructed/ammobin.hpp @@ -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[]; diff --git a/game/reconstructed/btl4app.cpp b/game/reconstructed/btl4app.cpp index 08c5e04..2f4a5c2 100644 --- a/game/reconstructed/btl4app.cpp +++ b/game/reconstructed/btl4app.cpp @@ -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(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( diff --git a/game/reconstructed/btl4mppr.cpp b/game/reconstructed/btl4mppr.cpp index bc881d8..85c9051 100644 --- a/game/reconstructed/btl4mppr.cpp +++ b/game/reconstructed/btl4mppr.cpp @@ -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 ); diff --git a/game/reconstructed/btl4mppr.hpp b/game/reconstructed/btl4mppr.hpp index ebf81b4..e6a15cf 100644 --- a/game/reconstructed/btl4mppr.hpp +++ b/game/reconstructed/btl4mppr.hpp @@ -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) // diff --git a/game/reconstructed/mechsub.cpp b/game/reconstructed/mechsub.cpp index cbb452d..0328282 100644 --- a/game/reconstructed/mechsub.cpp +++ b/game/reconstructed/mechsub.cpp @@ -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 diff --git a/game/reconstructed/mechsub.hpp b/game/reconstructed/mechsub.hpp index c1c5bee..cd7333b 100644 --- a/game/reconstructed/mechsub.hpp +++ b/game/reconstructed/mechsub.hpp @@ -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 diff --git a/game/reconstructed/mechweap.cpp b/game/reconstructed/mechweap.cpp index 647278b..f19b041 100644 --- a/game/reconstructed/mechweap.cpp +++ b/game/reconstructed/mechweap.cpp @@ -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) diff --git a/game/reconstructed/mechweap.hpp b/game/reconstructed/mechweap.hpp index 446fccd..468a899 100644 --- a/game/reconstructed/mechweap.hpp +++ b/game/reconstructed/mechweap.hpp @@ -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 diff --git a/game/reconstructed/myomers.cpp b/game/reconstructed/myomers.cpp index a2e1054..5eced4c 100644 --- a/game/reconstructed/myomers.cpp +++ b/game/reconstructed/myomers.cpp @@ -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); } diff --git a/game/reconstructed/myomers.hpp b/game/reconstructed/myomers.hpp index 3c90a29..60ca9cd 100644 --- a/game/reconstructed/myomers.hpp +++ b/game/reconstructed/myomers.hpp @@ -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; }; diff --git a/game/reconstructed/sensor.cpp b/game/reconstructed/sensor.cpp index 644586d..7910fc8 100644 --- a/game/reconstructed/sensor.cpp +++ b/game/reconstructed/sensor.cpp @@ -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); } diff --git a/game/reconstructed/sensor.hpp b/game/reconstructed/sensor.hpp index 62092b6..8d69969 100644 --- a/game/reconstructed/sensor.hpp +++ b/game/reconstructed/sensor.hpp @@ -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). + //##########################################################################