Files
BT411/engine/MUNGA/AUDWTHR.cpp
T
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00

1049 lines
27 KiB
C++

#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);
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
}
}