The audio subsystem binds AudioStateWatchers (class 28) to per-subsystem state attrs (GeneratorState/CondenserState/ReservoirState) BY NAME. Recovered the watcher TYPE per attribute via a MakeObjectImplementation ClassID trace. Root cause of the subsystem-state crash: the binary's 0x54-byte subsystem alarm (FUN_0041b9ec, reconstructed as GaugeAlarm54) IS a StateIndicator -- its "three sub-indicators" @0x18/0x2c/0x40 are the audio/video/gauge watcher SChains, and level@0x14 is currentState. The reconstruction had modeled that tail as an opaque `_tail`, so the sockets were never constructed -> AddAudioWatcher AV'd on 0xCDCDCDCD. - GaugeAlarm54: give it the three REAL, constructed SChainOf<Component*> sockets + AddAudioWatcher/Video/Gauge; SetLevel now fires the watchers on a level CHANGE (empty sockets = no-op, so the ~all other alarms are unaffected). levelB (weapon LevelCountB "reset source") is left untouched -- weapons unchanged. sizeof stays 0x54 (static_assert holds -> SChainOf<Component*> is 0x14, layout exact). - Register the state attrs -> the subsystem's own alarm (own AttributePointers[] chained to the parent): Generator.GeneratorState->stateAlarm, Condenser. CondenserState->condenserAlarm, Reservoir.ReservoirState->reservoirAlarm. Those alarms are already SetLevel'd by the sim, so the state audio fires on transition. - AudioStateWatcher guard now validates the +0x18 SOCKET (what AddAudioWatcher touches), not the +0 vtable -- GaugeAlarm54 is non-polymorphic at +0 (raw header) but has a real socket at +0x18. audiostate skips 85 -> 10 (only AmmoBin AmmoState/SimulationState left). The Logical/Scalar/Enum subsystem attrs (ReportLeak/GeneratorOn/ConfigureActivePress/ MotionState/SpeedOfTorsoHorizontal/TargetRangeExponent) stay inert (read 0, silent, non-crashing) -- they need backing members in size-locked layouts (a polish wave). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1075 lines
29 KiB
C++
1075 lines
29 KiB
C++
#include <cstdlib>
|
|
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "audwthr.h"
|
|
#include "objstrm.h"
|
|
#include "namelist.h"
|
|
|
|
//#############################################################################
|
|
//########################### AudioIdleWatcher ##########################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioIdleWatcher::AudioIdleWatcher(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
Component(stream),
|
|
audioComponentSocket(this)
|
|
{
|
|
AudioComponent *audio_component;
|
|
PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
|
|
AudioIdleWatcherX(audio_component, entity);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioIdleWatcher::AudioIdleWatcherX(
|
|
AudioComponent *audio_component,
|
|
Entity *entity
|
|
)
|
|
{
|
|
Check(audio_component);
|
|
Check(entity);
|
|
audioComponentSocket.Add(audio_component);
|
|
entity->AddAudioWatcher(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioIdleWatcher::~AudioIdleWatcher()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioIdleWatcher::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
Component::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
|
|
CString audio_component_name("audio_component");
|
|
PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
AudioIdleWatcher::TestInstance() const
|
|
{
|
|
Component::TestInstance();
|
|
Check(&audioComponentSocket);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioIdleWatcher::ReleaseLinkHandler(
|
|
Socket *socket,
|
|
Plug *plug
|
|
)
|
|
{
|
|
if (socket == &audioComponentSocket)
|
|
{
|
|
Check(&audioComponentSocket);
|
|
Check(socket);
|
|
Unregister_Object(this);
|
|
delete this;
|
|
}
|
|
else
|
|
{
|
|
Component::ReleaseLinkHandler(socket, plug);
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioIdleWatcher::Execute()
|
|
{
|
|
Check(this);
|
|
Check(audioComponentSocket.GetCurrent());
|
|
audioComponentSocket.GetCurrent()->ReceiveControl(
|
|
IdleAudioControlID,
|
|
0.0f
|
|
);
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################## AudioMotionTrigger #####################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioMotionTrigger::AudioMotionTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioTriggerOf<Motion>(stream, entity)
|
|
{
|
|
MemoryStream_Read(stream, &motionType);
|
|
MemoryStream_Read(stream, &motionValue);
|
|
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioMotionTrigger::~AudioMotionTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioMotionTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioTriggerOf<Motion>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
|
|
//
|
|
// Store fields
|
|
//
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_type);
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_value);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioMotionTrigger::ExtractInterestingValue(const Motion *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check(attribute_ptr);
|
|
|
|
switch (motionType)
|
|
{
|
|
case LinearMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
return attribute_ptr->linearMotion.x;
|
|
case YMotionValue:
|
|
return attribute_ptr->linearMotion.y;
|
|
case ZMotionValue:
|
|
return attribute_ptr->linearMotion.z;
|
|
case LengthMotionValue:
|
|
return attribute_ptr->linearMotion.Length();
|
|
}
|
|
break;
|
|
|
|
case AngularMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
return attribute_ptr->angularMotion.x;
|
|
case YMotionValue:
|
|
return attribute_ptr->angularMotion.y;
|
|
case ZMotionValue:
|
|
return attribute_ptr->angularMotion.z;
|
|
case LengthMotionValue:
|
|
return attribute_ptr->angularMotion.Length();
|
|
}
|
|
break;
|
|
}
|
|
|
|
Fail("AudioMotionTrigger::ExtractInterestingValue - Should never reach here");
|
|
return 0.0f;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
void
|
|
AudioMotionTrigger::DumpValue(const Motion *attribute_ptr)
|
|
#else
|
|
void
|
|
AudioMotionTrigger::DumpValue(const Motion*)
|
|
#endif
|
|
{
|
|
Check(this);
|
|
Check(attribute_ptr);
|
|
|
|
switch (motionType)
|
|
{
|
|
case LinearMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
Dump(attribute_ptr->linearMotion.x);
|
|
break;
|
|
case YMotionValue:
|
|
Dump(attribute_ptr->linearMotion.y);
|
|
break;
|
|
case ZMotionValue:
|
|
Dump(attribute_ptr->linearMotion.z);
|
|
break;
|
|
case LengthMotionValue:
|
|
Dump(attribute_ptr->linearMotion.Length());
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AngularMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
Dump(attribute_ptr->angularMotion.x);
|
|
break;
|
|
case YMotionValue:
|
|
Dump(attribute_ptr->angularMotion.y);
|
|
break;
|
|
case ZMotionValue:
|
|
Dump(attribute_ptr->angularMotion.z);
|
|
break;
|
|
case LengthMotionValue:
|
|
Dump(attribute_ptr->angularMotion.Length());
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################## AudioMotionScale #######################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioMotionScale::AudioMotionScale(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioScaleOf<Motion>(stream, entity)
|
|
{
|
|
MemoryStream_Read(stream, &motionType);
|
|
MemoryStream_Read(stream, &motionValue);
|
|
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioMotionScale::~AudioMotionScale()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioMotionScale::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioScaleOf<Motion>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
|
|
//
|
|
// Store fields
|
|
//
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_type);
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, motion_value);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioMotionScale::ExtractInterestingValue(const Motion *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check(attribute_ptr);
|
|
|
|
switch (motionType)
|
|
{
|
|
case LinearMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
return attribute_ptr->linearMotion.x;
|
|
case YMotionValue:
|
|
return attribute_ptr->linearMotion.y;
|
|
case ZMotionValue:
|
|
return attribute_ptr->linearMotion.z;
|
|
case LengthMotionValue:
|
|
return attribute_ptr->linearMotion.Length();
|
|
}
|
|
break;
|
|
|
|
case AngularMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
return attribute_ptr->angularMotion.x;
|
|
case YMotionValue:
|
|
return attribute_ptr->angularMotion.y;
|
|
case ZMotionValue:
|
|
return attribute_ptr->angularMotion.z;
|
|
case LengthMotionValue:
|
|
return attribute_ptr->angularMotion.Length();
|
|
}
|
|
break;
|
|
}
|
|
|
|
Fail("AudioMotionScale::ExtractInterestingValue - Should never reach here");
|
|
return 0.0f;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
void
|
|
AudioMotionScale::DumpValue(const Motion *attribute_ptr)
|
|
#else
|
|
void
|
|
AudioMotionScale::DumpValue(const Motion*)
|
|
#endif
|
|
{
|
|
Check(this);
|
|
Check(attribute_ptr);
|
|
|
|
switch (motionType)
|
|
{
|
|
case LinearMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
Dump(attribute_ptr->linearMotion.x);
|
|
break;
|
|
case YMotionValue:
|
|
Dump(attribute_ptr->linearMotion.y);
|
|
break;
|
|
case ZMotionValue:
|
|
Dump(attribute_ptr->linearMotion.z);
|
|
break;
|
|
case LengthMotionValue:
|
|
Dump(attribute_ptr->linearMotion.Length());
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AngularMotionType:
|
|
switch (motionValue)
|
|
{
|
|
case XMotionValue:
|
|
Dump(attribute_ptr->angularMotion.x);
|
|
break;
|
|
case YMotionValue:
|
|
Dump(attribute_ptr->angularMotion.y);
|
|
break;
|
|
case ZMotionValue:
|
|
Dump(attribute_ptr->angularMotion.z);
|
|
break;
|
|
case LengthMotionValue:
|
|
Dump(attribute_ptr->angularMotion.Length());
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################## AudioHingeScale ########################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioHingeScale::AudioHingeScale(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioScaleOf<Hinge>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioHingeScale::~AudioHingeScale()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioHingeScale::ExtractInterestingValue(const Hinge *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check(attribute_ptr);
|
|
return attribute_ptr->rotationAmount;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if DEBUG_LEVEL>0
|
|
void
|
|
AudioHingeScale::DumpValue(const Hinge *attribute_ptr)
|
|
#else
|
|
void
|
|
AudioHingeScale::DumpValue(const Hinge*)
|
|
#endif
|
|
{
|
|
Check(this);
|
|
Check(attribute_ptr);
|
|
Dump(attribute_ptr->rotationAmount);
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################## AudioScalarTrigger #####################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioScalarTrigger::AudioScalarTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioTriggerOf<Scalar>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioScalarTrigger::~AudioScalarTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioScalarTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioTriggerOf<Scalar>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioScalarTrigger::ExtractInterestingValue(const Scalar *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(attribute_ptr);
|
|
return *attribute_ptr;
|
|
}
|
|
|
|
//#############################################################################
|
|
//####################### AudioScalarDeltaTrigger #######################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioScalarDeltaTrigger::AudioScalarDeltaTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioDeltaOf<Scalar>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioScalarDeltaTrigger::~AudioScalarDeltaTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioScalarDeltaTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioDeltaOf<Scalar>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################## AudioScalarScale #######################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioScalarScale::AudioScalarScale(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioScaleOf<Scalar>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioScalarScale::~AudioScalarScale()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioScalarScale::ExtractInterestingValue(const Scalar *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(attribute_ptr);
|
|
return *attribute_ptr;
|
|
}
|
|
|
|
//#############################################################################
|
|
//######################### AudioLogicalTrigger #########################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioLogicalTrigger::AudioLogicalTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioMatchOf<Logical>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioLogicalTrigger::~AudioLogicalTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioLogicalTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioMatchOf<Logical>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
int
|
|
AudioLogicalTrigger::ExtractInterestingValue(const Logical *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(attribute_ptr);
|
|
return *attribute_ptr;
|
|
}
|
|
|
|
//#############################################################################
|
|
//######################### AudioEnumerationTrigger #####################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioEnumerationTrigger::AudioEnumerationTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioMatchOf<Enumeration>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioEnumerationTrigger::~AudioEnumerationTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioEnumerationTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioMatchOf<Enumeration>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
int
|
|
AudioEnumerationTrigger::ExtractInterestingValue(const Enumeration *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(attribute_ptr);
|
|
return *attribute_ptr;
|
|
}
|
|
|
|
//#############################################################################
|
|
//###################### AudioEnumerationDeltaTrigger ###################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioEnumerationDeltaTrigger::AudioEnumerationDeltaTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioDeltaOf<Enumeration>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioEnumerationDeltaTrigger::~AudioEnumerationDeltaTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioEnumerationDeltaTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioDeltaOf<Enumeration>::BuildFromPage(
|
|
stream,
|
|
name_list,
|
|
class_ID,
|
|
object_ID
|
|
);
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################# AudioIntegerTrigger #####################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioIntegerTrigger::AudioIntegerTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioTriggerOf<int>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioIntegerTrigger::~AudioIntegerTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioIntegerTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioTriggerOf<int>::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioIntegerTrigger::ExtractInterestingValue(const int *attribute_ptr)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(attribute_ptr);
|
|
return *attribute_ptr;
|
|
}
|
|
|
|
//#############################################################################
|
|
//###################### AudioControlsButtonTrigger #####################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlsButtonTrigger::AudioControlsButtonTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioTriggerOf<ControlsButton>(stream, entity)
|
|
{
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlsButtonTrigger::~AudioControlsButtonTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioControlsButtonTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioTriggerOf<ControlsButton>::BuildFromPage(
|
|
stream,
|
|
name_list,
|
|
class_ID,
|
|
object_ID
|
|
);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioControlValue
|
|
AudioControlsButtonTrigger::ExtractInterestingValue(
|
|
const ControlsButton *attribute_ptr
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(attribute_ptr);
|
|
return *attribute_ptr;
|
|
}
|
|
|
|
//#############################################################################
|
|
//########################### AudioStateWatcher #########################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioStateWatcher::AudioStateWatcher(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioWatcherOf<StateIndicator>(stream, entity)
|
|
{
|
|
Check_Pointer(attributePointer);
|
|
// BRING-UP GUARD [T3, temporary]: an AudioStateWatcher binds to a StateIndicator
|
|
// attribute BY NAME. The Mech's own state indicators (SimulationState, Animation/
|
|
// ReplicantAnimationState, CollisionState) are real; but audio also binds state
|
|
// attrs on subsystems that are not fully reconstructed yet (GeneratorState,
|
|
// CondenserState, Torso MotionState, ...). Those resolve to an unconstructed
|
|
// object -- a null/garbage vtable at +0 or a debug-fill (0xCDCDCDCD) watcher chain
|
|
// at +0x18 -- and AddAudioWatcher would AV in SChainOf::Add. Skip the register
|
|
// (that subsystem's state audio stays silent) until the subsystem is built; this
|
|
// is SELF-CLEARING (a real StateIndicator passes). See docs: audio subsystem wave.
|
|
// Validate the AUDIO SOCKET at +0x18 (what AddAudioWatcher touches), NOT the
|
|
// object's +0 vtable: a real StateIndicator has a vtable at +0, but the binary's
|
|
// 0x54 subsystem alarm (GaugeAlarm54) is non-polymorphic there (a raw header) yet
|
|
// has a real, constructed SChainOf socket at +0x18. A skip means the socket is
|
|
// unconstructed (null / debug-fill 0xCDCDCDCD) -- the inert pad or a subsystem not
|
|
// yet reconstructed. Registering there would AV in SChainOf::Add.
|
|
{
|
|
unsigned chain = *(unsigned*)((char*)attributePointer + 0x18);
|
|
if (chain == 0 || chain == 0xCDCDCDCD)
|
|
{
|
|
if (getenv("BT_AUDIO_LOG"))
|
|
DEBUG_STREAM << "[audiostate] skip watcher on unbuilt StateIndicator "
|
|
<< attributePointer << " (chain=" << (void*)chain << ")\n" << std::flush;
|
|
return;
|
|
}
|
|
}
|
|
Cast_Object(StateIndicator*, attributePointer)->AddAudioWatcher(this);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioStateWatcher::~AudioStateWatcher()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioStateWatcher::SendNotificationOfChange()
|
|
{
|
|
Check(this);
|
|
|
|
StateIndicator *state_indicator = GetCurrentPointer();
|
|
|
|
Check(state_indicator);
|
|
StateChanged(
|
|
state_indicator->GetOldState(),
|
|
state_indicator->GetState()
|
|
);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioStateWatcher::StateChanged(
|
|
Enumeration,
|
|
Enumeration
|
|
)
|
|
{
|
|
Fail("AudioStateWatcher::StateChanged - Should never reach here");
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################## AudioStateTrigger ######################
|
|
//#############################################################################
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioStateTrigger::AudioStateTrigger(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
):
|
|
AudioStateWatcher(stream, entity)
|
|
{
|
|
MemoryStream_Read(stream, &triggerState);
|
|
MemoryStream_Read(stream, &inverseTrigger);
|
|
MemoryStream_Read(stream, &controlID);
|
|
MemoryStream_Read(stream, &controlValue);
|
|
MemoryStream_Read(stream, &excludeTransition);
|
|
MemoryStream_Read(stream, &excludeState);
|
|
|
|
PrimeWatcher();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
AudioStateTrigger::~AudioStateTrigger()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioStateTrigger::BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
)
|
|
{
|
|
AudioStateWatcher::BuildFromPage(stream, name_list, class_ID, object_ID);
|
|
|
|
//
|
|
// Write standard fields
|
|
//
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, trigger_state);
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, inverse_trigger);
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
|
|
MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, control_value);
|
|
|
|
//
|
|
// Write optional fields
|
|
//
|
|
Logical exclude_transition = False;
|
|
Enumeration exclude_state = 0;
|
|
|
|
if ((name_list)->FindData("exclude_state") != NULL)
|
|
{
|
|
exclude_transition = True;
|
|
Check_Pointer((name_list)->FindData("exclude_state"));
|
|
Convert_From_Ascii(
|
|
(const char *)(name_list)->FindData("exclude_state"),
|
|
&exclude_state
|
|
);
|
|
}
|
|
MemoryStream_Write(stream, &exclude_transition);
|
|
MemoryStream_Write(stream, &exclude_state);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
AudioStateTrigger::StateChanged(
|
|
Enumeration old_state,
|
|
Enumeration new_state
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// If the exclude switch is on then check for the transition
|
|
//
|
|
if (excludeTransition)
|
|
{
|
|
if (
|
|
!inverseTrigger &&
|
|
new_state == triggerState &&
|
|
old_state == excludeState
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
if (
|
|
inverseTrigger &&
|
|
old_state == triggerState &&
|
|
new_state == excludeState
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for the trigger state
|
|
//
|
|
if (
|
|
(!inverseTrigger && new_state == triggerState) ||
|
|
(inverseTrigger && old_state == triggerState)
|
|
)
|
|
{
|
|
Check(&audioComponentSocket);
|
|
Check(audioComponentSocket.GetCurrent());
|
|
#if 1
|
|
audioComponentSocket.GetCurrent()->ReceiveControl(
|
|
controlID,
|
|
controlValue
|
|
);
|
|
#else
|
|
audioComponentSocket.GetCurrent()->PostReceiveControl(
|
|
controlID,
|
|
controlValue
|
|
);
|
|
#endif
|
|
}
|
|
}
|