User + old-timers were right; the earlier "no voice exists" verdict was wrong. The authored mech audio has state watchers on Entity.SimulationState==3/4 (the binary's one-cell mech+0x40) that start sequence notes 29,16,40 -- two klaxon hits then Warnings01 zone 8 (key 40-41) = the "reverse disabled" voice line the testers remember. The port's cell split (graphicAlarm vs engine simulationState, gotcha #23) meant the trigger value never arrived. - mechdmg: mirror gimp 3/4 into SetSimulationState at the leg-half crossing (guarded: never stomps disabled/fall/dead states). Voice verified playing end-to-end on the bench: SetupPatch bank2 patch113 note=40 -> Warnings01_z7.wav. - mech.cpp: BT_GIMP_SAFE_BASE_READ on all seven Simulation::ReadUpdateRecord sites -- gimp is monotonic per life; a record captured pre-gimp must not stomp the cell (the loopback otherwise perpetuates the stale value and restarts the warning on every damage event). - diagnostics (all env-gated): [statefire]/[startreq]/[animind]/[gimp-sim]/ [simstomp]/[indstomp] + StateIndicator::DebugAudioWatcherCount + raised spatial-log caps. These traced the whole chain and PROVED no SetState path stomps the cell. OPEN (follow-up): a RAW writer (bypasses SetState entirely; invisible to the indicator-level trap) resets the cell between damage events -- under the bench's 1 Hz metronome harness it restarted the sequence before the 1.8 s voice note; sporadic real-play damage is unaffected (one edge -> full sequence). Needs a cdb write-watchpoint session; candidates: a recon raw +0x2c-equivalent write or a struct copy spanning it. Also decoded en route: the AnimationState triggers on the limp states play EngineShiftRev01 (the downshift foley) -- working, and NOT the voice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
69 lines
1.4 KiB
C++
69 lines
1.4 KiB
C++
#pragma once
|
|
|
|
#include "cmpnnt.h"
|
|
#include "schain.h"
|
|
|
|
class StateIndicator : public Node
|
|
{
|
|
public:
|
|
StateIndicator();
|
|
StateIndicator(unsigned max_states);
|
|
StateIndicator(const StateIndicator &state_indicator);
|
|
~StateIndicator();
|
|
|
|
StateIndicator& operator=(const StateIndicator&);
|
|
|
|
Logical operator==(const StateIndicator&) const;
|
|
|
|
public:
|
|
unsigned GetState()
|
|
{
|
|
Check(this);
|
|
return currentState;
|
|
}
|
|
unsigned GetOldState()
|
|
{
|
|
Check(this);
|
|
return oldState;
|
|
}
|
|
void SetState(unsigned new_state);
|
|
|
|
protected:
|
|
unsigned stateCount, oldState, currentState;
|
|
|
|
public:
|
|
void AddAudioWatcher(Component *watcher)
|
|
{
|
|
Check(&audioWatcherSocket);
|
|
audioWatcherSocket.Add(watcher);
|
|
}
|
|
void AddVideoWatcher(Component *watcher)
|
|
{
|
|
Check(&videoWatcherSocket);
|
|
videoWatcherSocket.Add(watcher);
|
|
}
|
|
void AddGaugeWatcher(Component *watcher)
|
|
{
|
|
Check(&gaugeWatcherSocket);
|
|
gaugeWatcherSocket.Add(watcher);
|
|
}
|
|
int DebugAudioWatcherCount() // DEBUG (#78 audio flake): registered audio watchers
|
|
{
|
|
int n = 0;
|
|
SChainIteratorOf<Component*> it(audioWatcherSocket);
|
|
while (it.ReadAndNext() != NULL) ++n;
|
|
return n;
|
|
}
|
|
|
|
private:
|
|
SChainOf<Component*> audioWatcherSocket;
|
|
SChainOf<Component*> videoWatcherSocket;
|
|
SChainOf<Component*> gaugeWatcherSocket;
|
|
|
|
public:
|
|
friend std::ostream& operator << (std::ostream &strm, const StateIndicator &state_indicator);
|
|
|
|
public:
|
|
Logical TestInstance() const;
|
|
static Logical TestClass();
|
|
}; |