Files
BT411/engine/MUNGA/AUDREND.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

514 lines
13 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "audrend.h"
#include "audent.h"
#include "jmover.h"
#include "app.h"
//#############################################################################
//########################### AudioRenderer #############################
//#############################################################################
//
//#############################################################################
// AudioRenderer
//#############################################################################
//
AudioRenderer::AudioRenderer(RendererRate render_rate):
Renderer(
render_rate,
MaxRendererComplexity,
DefaultRendererPriority,
AudioInterestType,
DefaultInterestDepth,
AudioRendererClassID
),
audioEventSocket(NULL, False)
{
audioHead = NULL;
#ifdef LAB_ONLY
sourceClippedCount = 0;
sourceStartCount = 0;
sourceSuspendCount = 0;
sourceResumeCount = 0;
doubleTriggerCount = 0;
resourceStealCount = 0;
resourceStealPriorityCount = 0;
resourceStealVolumeCount = 0;
stealShortDurationCount = 0;
#endif
}
//
//#############################################################################
// ~AudioRenderer
//#############################################################################
//
AudioRenderer::~AudioRenderer()
{
Unregister_Object(audioHead);
delete audioHead;
#ifdef LAB_ONLY
cout << "AudioRenderer::~AudioRenderer - sourceClippedCount="
<< sourceClippedCount << "\n";
cout << "AudioRenderer::~AudioRenderer - sourceStartCount="
<< sourceStartCount << "\n";
cout << "AudioRenderer::~AudioRenderer - sourceSuspendCount="
<< sourceSuspendCount << "\n";
cout << "AudioRenderer::~AudioRenderer - sourceResumeCount="
<< sourceResumeCount << "\n";
cout << "AudioRenderer::~AudioRenderer - doubleTriggerCount="
<< doubleTriggerCount << "\n";
cout << "AudioRenderer::~AudioRenderer - resourceStealCount="
<< resourceStealCount << "\n";
cout << "AudioRenderer::~AudioRenderer - resourceStealPriorityCount="
<< resourceStealPriorityCount << "\n";
cout << "AudioRenderer::~AudioRenderer - resourceStealVolumeCount="
<< resourceStealVolumeCount << "\n";
cout << "AudioRenderer::~AudioRenderer - stealShortDurationCount="
<< stealShortDurationCount << "\n";
#endif
}
//
//#############################################################################
// Initialize
//#############################################################################
//
void
AudioRenderer::Initialize()
{
audioHead = MakeAudioHead();
Register_Object(audioHead);
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
AudioRenderer::TestInstance() const
{
Renderer::TestInstance();
if (audioHead != NULL)
{
Check(audioHead);
}
Check(&audioEventSocket);
return True;
}
//
//#############################################################################
// LinkToEntity
//#############################################################################
//
void
AudioRenderer::LinkToEntity(Entity *entity)
{
Check(this);
Check(entity);
//
//--------------------------------------------------------------------------
// Link the head to the entity
//--------------------------------------------------------------------------
//
Check(audioHead);
audioHead->LinkToEntity(entity);
//
//--------------------------------------------------------------------------
// Call inherited method
//--------------------------------------------------------------------------
//
Renderer::LinkToEntity(entity);
}
//
//#############################################################################
// ExecuteImplementation
//#############################################################################
//
void
AudioRenderer::ExecuteImplementation(
RendererComplexity,
RendererOrigin::InterestingEntityIterator*
)
{
Check(this);
Check(audioHead);
audioHead->Execute();
}
//
//#############################################################################
// PostAudioRequestMessage
//#############################################################################
//
void
AudioRenderer::PostAudioRequestMessage(
AudioSource *audio_source,
AudioSource::RequestMessage *message
)
{
Check(this);
Check(audio_source);
Check(message);
//
//--------------------------------------------------------------------------
// If audio source is clipped and the source is transient
// Then ignore
// Else set priority and volume
//--------------------------------------------------------------------------
//
AudioSourcePriority
audio_source_priority;
AudioControlValue
audio_source_volume_scale;
if (audio_source->IsAudioSourceClipped(GetAudioHead()))
{
//
// If it is a transient source then ignore request
//
if (audio_source->GetAudioRenderType() == TransientAudioRenderType)
{
#ifdef LAB_ONLY
sourceClippedCount++;
#endif
return;
}
audio_source_priority = audio_source->GetAudioSourcePriority();
audio_source_volume_scale = 0.0f;
}
else
{
audio_source_priority = audio_source->GetAudioSourcePriority();
audio_source_volume_scale = audio_source->CalculateSourceVolumeScale();
}
//
//--------------------------------------------------------------------------
// If this a transient source and volume is lower than threshold
// Then return
//--------------------------------------------------------------------------
//
if (
message->controlID == StartAudioControlID &&
audio_source->GetAudioRenderType() == TransientAudioRenderType &&
audio_source_volume_scale < LowAudioVolumeThreshold
)
{
#ifdef LAB_ONLY
sourceClippedCount++;
#endif
return;
}
//
//--------------------------------------------------------------------------
// Create audio weight based on priority and volume
// Add audio event
//--------------------------------------------------------------------------
//
AudioWeighting
audio_weight(audio_source_priority, audio_source_volume_scale);
AudioEvent
*audio_event;
audio_event = new AudioEvent(audio_source, message);
Register_Object(audio_event);
audioEventSocket.AddValue(audio_event, audio_weight);
}
//
//#############################################################################
// ProcessAudioRequestMessage
//#############################################################################
//
Logical
AudioRenderer::ProcessAudioRequestMessage()
{
//
// Process high priority, high volume event
//
AudioEventIterator
iterator(&audioEventSocket);
AudioEvent
*audio_event;
Logical stuff = False;
while ((audio_event = iterator.GetCurrent()) != NULL)
{
stuff = True;
Check(audio_event);
audio_event->Process();
//return True;
}
return stuff;
}
//
//#############################################################################
// FlushAudioMessages
//#############################################################################
//
void
AudioRenderer::FlushAudioMessages(AudioSource *audio_source)
{
Check(this);
Check(audio_source);
//
// Process matching events
//
AudioEventIterator
iterator(&audioEventSocket);
AudioEvent
*audio_event;
while ((audio_event = iterator.ReadAndNext()) != NULL)
{
Check(audio_event);
if (audio_event->targetReceiver.GetCurrent() == audio_source)
{
if (audio_event->messageToSend->controlID == StopAudioControlID)
{
audio_event->Process();
}
else
{
Unregister_Object(audio_event);
delete audio_event;
}
}
}
}
//
//#############################################################################
// MakeAudioHead
//#############################################################################
//
AudioHead*
AudioRenderer::MakeAudioHead()
{
return new AudioHead;
}
//
//#############################################################################
// StartEntityEffectImplementation
//#############################################################################
//
void
AudioRenderer::StartEntityEffectImplementation(
Entity *parent_entity,
DamageZone *damage_zone,
ResourceDescription::ResourceID resource_ID
)
{
SET_AUDIO_RENDERER();
Check(this);
Check(parent_entity);
Check(damage_zone);
Verify(resource_ID != ResourceDescription::NullResourceID);
//
//--------------------------------------------------------------------------
// Get the audio resource for this effect
//--------------------------------------------------------------------------
//
ResourceDescription *audio_resource_description;
Check(application);
Check(application->GetResourceFile());
audio_resource_description =
application->GetResourceFile()->SearchList(
resource_ID,
ResourceDescription::AudioStreamListResourceType
);
if (audio_resource_description == NULL)
{
CLEAR_AUDIO_RENDERER();
return;
}
Check(audio_resource_description);
//
//--------------------------------------------------------------------------
// Get the resource ID for the model resource for the audio effect entity
//--------------------------------------------------------------------------
//
ResourceDescription *model_resource_description;
Check(application);
Check(application->GetResourceFile());
Check(audio_resource_description);
model_resource_description =
application->GetResourceFile()->SearchList(
audio_resource_description->resourceID,
ResourceDescription::ModelListResourceType
);
if (model_resource_description == NULL)
{
CLEAR_AUDIO_RENDERER();
return;
}
Check(model_resource_description);
model_resource_description->Lock();
audio_resource_description->Lock();
//
//--------------------------------------------------------------------------
// Get the entity segment
//--------------------------------------------------------------------------
//
Entity *linked_entity;
AudioRepresentation audio_representation;
EntitySegment *parent_entity_segment;
linked_entity = GetLinkedEntity();
Check(linked_entity);
audio_representation =
(AudioRepresentation)parent_entity->GetAudioRepresentation(
linked_entity
);
parent_entity_segment = damage_zone->GetCurrentEffectSite(
(audio_representation == InternalAudioRepresentation) ?
DamageZone::InternalAudioEffectSite :
DamageZone::ExternalAudioEffectSite
);
Check(parent_entity_segment);
//
//--------------------------------------------------------------------------
// Create the audio effect entity with this resource
//--------------------------------------------------------------------------
//
JointedMover *jointed_mover;
Verify(parent_entity->IsDerivedFrom(JointedMover::GetClassDerivations()));
jointed_mover = Cast_Object(JointedMover*, parent_entity);
Check(jointed_mover);
AudioEntity::MakeMessage
make_message(
model_resource_description->resourceID,
jointed_mover,
parent_entity_segment
);
#if DEBUG_LEVEL>0
AudioEntity *audio_entity =
#endif
AudioEntity::Make(&make_message);
Register_Object(audio_entity);
model_resource_description->Unlock();
audio_resource_description->Unlock();
CLEAR_AUDIO_RENDERER();
}
//~~~~~~~~~~~~~~~~~~~~~~~~ AudioRenderer profile bits ~~~~~~~~~~~~~~~~~~~~~~~~~
#if defined(TRACE_AUDIO_RENDERER)
BitTrace Audio_Renderer("Audio Renderer");
#endif
#if defined(TRACE_AUDIO_RENDERER_CREATE_OBJECTS)
BitTrace Audio_Renderer_Create_Objects("Audio Renderer Create Objects");
#endif
#if defined(TRACE_AUDIO_RENDERER_DESTROY_OBJECTS)
BitTrace Audio_Renderer_Destroy_Objects("Audio Renderer Destroy Objects");
#endif
#if defined(TRACE_AUDIO_RENDERER_START_HANDLER)
BitTrace Audio_Renderer_Start_Handler("Audio Renderer Start Handler");
#endif
#if defined(TRACE_AUDIO_RENDERER_STOP_HANDLER)
BitTrace Audio_Renderer_Stop_Handler("Audio Renderer Stop Handler");
#endif
#if defined(TRACE_AUDIO_RENDERER_EXECUTE)
BitTrace Audio_Renderer_Execute("Audio Renderer Execute");
#endif
//#############################################################################
//############################## AudioEvent #############################
//#############################################################################
MemoryBlock *AudioEvent::GetAllocatedMemory()
{
static MemoryBlock allocatedMemory(sizeof(Event), AUDIOEVENT_MEMORYBLOCK_ALLOCATION, AUDIOEVENT_MEMORYBLOCK_ALLOCATION, "AudioEvents");
return &allocatedMemory;
}
//
//#############################################################################
//#############################################################################
//
AudioEvent::AudioEvent(
AudioSource *target,
AudioSource::RequestMessage *message
):
targetReceiver(this)
{
Check(target);
Check(message);
//
// Store the message
//
size_t long_size = (message->messageLength+3)>>2;
messageToSend = (AudioSource::RequestMessage*)new long[long_size];
Check_Pointer(messageToSend);
Register_Pointer(messageToSend);
Mem_Copy(
messageToSend,
message,
message->messageLength,
long_size*sizeof(long)
);
//
// Store the target receiver
//
targetReceiver.Add(target);
}
//
//#############################################################################
//#############################################################################
//
AudioEvent::~AudioEvent()
{
Unregister_Pointer(messageToSend);
delete messageToSend;
}
//
//#############################################################################
//#############################################################################
//
void
AudioEvent::ReleaseLinkHandler(
Socket*,
Plug*
)
{
Unregister_Object(this);
delete this;
}