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>
665 lines
12 KiB
C++
665 lines
12 KiB
C++
#pragma once
|
|
|
|
#include "style.h"
|
|
#include "audio.h"
|
|
#include "audloc.h"
|
|
#include "audlvl.h"
|
|
#include "audwgt.h"
|
|
#include "audtime.h"
|
|
|
|
//##########################################################################
|
|
//##################### AudioSourceStartState ########################
|
|
//##########################################################################
|
|
|
|
class AudioSourceStartState SIGNATURED
|
|
{
|
|
public:
|
|
AudioSourceStartState();
|
|
~AudioSourceStartState();
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
|
|
void
|
|
CalculateDuration(AudioControlValue control_value);
|
|
Logical
|
|
IsFinishedPlaying();
|
|
AudioTime
|
|
GetCurrentRunningTime();
|
|
|
|
void
|
|
SetNoteValue(AudioControlValue note_value)
|
|
{noteValue = note_value;}
|
|
AudioControlValue
|
|
GetNoteValue()
|
|
{return noteValue;}
|
|
void
|
|
SetDurationValue(AudioControlValue duration_value);
|
|
void
|
|
Start()
|
|
{startTime = AudioTime::Now();}
|
|
|
|
private:
|
|
AudioControlValue
|
|
noteValue;
|
|
|
|
Logical
|
|
isDurationSet;
|
|
Logical
|
|
hasDuration;
|
|
Scalar
|
|
duration;
|
|
AudioTime
|
|
startTime;
|
|
AudioTime
|
|
endTime;
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~ AudioSourceStartState inlines ~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
inline Logical
|
|
AudioSourceStartState::IsFinishedPlaying()
|
|
{
|
|
Check(this);
|
|
if (hasDuration)
|
|
{
|
|
return (AudioTime::Now() > endTime);
|
|
}
|
|
return False;
|
|
}
|
|
|
|
inline void
|
|
AudioSourceStartState::SetDurationValue(AudioControlValue duration_value)
|
|
{
|
|
Check(this);
|
|
isDurationSet = True;
|
|
duration = duration_value;
|
|
}
|
|
|
|
inline AudioTime
|
|
AudioSourceStartState::GetCurrentRunningTime()
|
|
{
|
|
Check(this);
|
|
return (AudioTime::Now() - startTime);
|
|
}
|
|
|
|
//##########################################################################
|
|
//######################### AudioSource ##############################
|
|
//##########################################################################
|
|
|
|
class AudioSource__RequestMessage;
|
|
class AudioSource__StartMessage;
|
|
class AudioSource__StopMessage;
|
|
|
|
class AudioSource:
|
|
public AudioComponent
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction, Destruction, and Testing
|
|
//
|
|
public:
|
|
AudioSource(
|
|
PlugStream *stream,
|
|
Entity *entity
|
|
);
|
|
void
|
|
AudioSource::AudioSourceX(
|
|
AudioResource *audio_resource,
|
|
AudioLocation *audio_location,
|
|
Entity *entity,
|
|
AudioSourcePriority priority,
|
|
AudioSourceMixPresence mix_presence,
|
|
AudioControlValue volume_mix_level,
|
|
AudioPitchCents pitch_mix_offset,
|
|
AudioControlValue brightness_mix_level,
|
|
Logical has_compression_curve,
|
|
const AudioTime &compression_duration,
|
|
Logical use_brightness_scale,
|
|
Logical use_attack_time_scale
|
|
);
|
|
~AudioSource();
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
|
|
static void
|
|
BuildFromPage(
|
|
PlugStream *stream,
|
|
NameList *name_list,
|
|
ClassID class_ID,
|
|
ObjectID object_ID
|
|
);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Location methods
|
|
//
|
|
public:
|
|
AudioLocation*
|
|
GetAudioLocation();
|
|
void
|
|
UpdateSpatialModel(AudioHead *audio_head);
|
|
Scalar
|
|
GetDistanceToSource();
|
|
virtual Logical
|
|
IsAudioSourceClipped(AudioHead *audio_head);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Resource methods
|
|
//
|
|
public:
|
|
void
|
|
SetAudioResource(AudioResource *audio_resource);
|
|
AudioResource*
|
|
GetAudioResource();
|
|
|
|
AudioVoiceCount
|
|
GetAudioVoiceCount();
|
|
AudioRenderType
|
|
GetAudioRenderType();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Current State/Status Accessors
|
|
//
|
|
public:
|
|
void
|
|
AssignAudioSourceState(AudioSourceState audio_source_state);
|
|
AudioSourceState
|
|
GetAudioSourceState();
|
|
|
|
Logical
|
|
IsFinishedPlaying();
|
|
AudioTime
|
|
GetCurrentRunningTime();
|
|
|
|
AudioControlValue
|
|
GetCurrentNoteValue();
|
|
AudioControlValue
|
|
CalculateSourceAttackVolumeScale();
|
|
Logical
|
|
UseSourceAttackTime();
|
|
AudioControlValue
|
|
CalculateSourceAttackTime();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Priority Accessors
|
|
//
|
|
public:
|
|
void
|
|
SetAudioSourcePriority(AudioSourcePriority audio_source_priority);
|
|
AudioSourcePriority
|
|
GetAudioSourcePriority();
|
|
|
|
AudioWeighting
|
|
CalculateAudioWeighting();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Mixing & Logical compression methods
|
|
//
|
|
public:
|
|
AudioSourceMixPresence
|
|
GetAudioSourceMixPresence();
|
|
|
|
void
|
|
SetVolumeCompressionScale(AudioControlValue compression_scale);
|
|
AudioControlValue
|
|
CalculateSourceCompressionEffect();
|
|
|
|
virtual AudioControlValue
|
|
CalculateSourceVolumeScale();
|
|
virtual AudioPitchCents
|
|
CalculateSourcePitchOffset();
|
|
Logical
|
|
UseSourceBrightnessScale();
|
|
virtual AudioControlValue
|
|
CalculateSourceBrightnessScale();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Controller methods
|
|
//
|
|
public:
|
|
void
|
|
ReceiveControl(
|
|
AudioControlID control_ID,
|
|
AudioControlValue control_value
|
|
);
|
|
|
|
virtual void
|
|
VolumeAudioHandler(AudioControlValue);
|
|
virtual void
|
|
PitchAudioHandler(AudioControlValue);
|
|
virtual void
|
|
BrightnessAudioHandler(AudioControlValue);
|
|
virtual void
|
|
AttackVolumeAudioHandler(AudioControlValue);
|
|
virtual void
|
|
AttackTimeAudioHandler(AudioControlValue);
|
|
virtual void
|
|
NoteAudioHandler(AudioControlValue);
|
|
virtual void
|
|
DurationAudioHandler(AudioControlValue);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// State implementations
|
|
//
|
|
public:
|
|
virtual void
|
|
StartImplementation();
|
|
virtual void
|
|
StopImplementation();
|
|
|
|
virtual void
|
|
SuspendImplementation();
|
|
virtual void
|
|
ResumeImplementation();
|
|
|
|
protected:
|
|
virtual void
|
|
StartMessageImplementation() {}
|
|
virtual void
|
|
StopMessageImplementation() {}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Execution
|
|
//
|
|
public:
|
|
Logical
|
|
RequiresMaintenance(AudioHead *audio_head);
|
|
Logical
|
|
CanResume(AudioHead *audio_head);
|
|
void
|
|
Execute();
|
|
|
|
protected:
|
|
virtual void
|
|
ExecuteModel(Logical force_update) {}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Message Support
|
|
//
|
|
public:
|
|
enum {
|
|
StartMessageID = AudioComponent::NextMessageID,
|
|
StopMessageID,
|
|
NextMessageID
|
|
};
|
|
|
|
typedef AudioSource__RequestMessage
|
|
RequestMessage;
|
|
typedef AudioSource__StartMessage
|
|
StartMessage;
|
|
typedef AudioSource__StopMessage
|
|
StopMessage;
|
|
|
|
static const HandlerEntry
|
|
MessageHandlerEntries[];
|
|
//static MessageHandlerSet MessageHandlers;
|
|
static MessageHandlerSet& GetMessageHandlers();
|
|
|
|
void
|
|
StartMessageHandler(StartMessage *message);
|
|
void
|
|
StopMessageHandler(StopMessage *message);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data Support
|
|
//
|
|
public:
|
|
static Derivation *GetClassDerivations();
|
|
static SharedData
|
|
DefaultData;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Private data
|
|
//
|
|
private:
|
|
//
|
|
// Location data
|
|
//
|
|
AudioLocation
|
|
*audioLocation;
|
|
|
|
//
|
|
// Resource data
|
|
//
|
|
AudioResource
|
|
*audioResource;
|
|
|
|
//
|
|
// State/Status data
|
|
//
|
|
AudioSourceState
|
|
audioSourceState;
|
|
|
|
AudioSourceStartState
|
|
nextStartState,
|
|
currentStartState;
|
|
|
|
AudioControlValue
|
|
attackVolumeScale;
|
|
Logical
|
|
useAttackTimeScale;
|
|
AudioControlValue
|
|
attackTimeScale;
|
|
|
|
//
|
|
// Priority data
|
|
//
|
|
AudioSourcePriority
|
|
audioSourcePriority;
|
|
|
|
//
|
|
// Mixing and compression
|
|
//
|
|
AudioSourceMixPresence
|
|
audioSourceMixPresence;
|
|
AudioControlValue
|
|
volumeCompressionScale;
|
|
|
|
Logical
|
|
hasCompressionCurve;
|
|
AudioTime
|
|
compressionDuration;
|
|
|
|
AudioControlValue
|
|
volumeMixScale;
|
|
AudioPitchCents
|
|
pitchMixOffset;
|
|
Logical
|
|
useBrightnessScale;
|
|
AudioControlValue
|
|
brightnessMixScale;
|
|
|
|
AudioControlValue
|
|
volumeScale;
|
|
AudioPitchCents
|
|
pitchOffset;
|
|
AudioControlValue
|
|
brightnessScale;
|
|
|
|
//
|
|
// Execution
|
|
//
|
|
AudioFrameCount
|
|
nextExecuteFrame;
|
|
AudioFrameCount
|
|
suspendFinishedFrame;
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSource inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
//
|
|
// Location methods
|
|
//
|
|
|
|
inline AudioLocation*
|
|
AudioSource::GetAudioLocation()
|
|
{
|
|
Check(this);
|
|
return audioLocation;
|
|
}
|
|
|
|
inline void
|
|
AudioSource::UpdateSpatialModel(AudioHead *audio_head)
|
|
{
|
|
Check(this);
|
|
Check(audioLocation);
|
|
audioLocation->UpdateSpatialModel(audio_head);
|
|
}
|
|
|
|
inline Scalar
|
|
AudioSource::GetDistanceToSource()
|
|
{
|
|
Check(this);
|
|
Check(audioLocation);
|
|
return audioLocation->GetDistanceToSource();
|
|
}
|
|
|
|
inline Logical
|
|
AudioSource::IsAudioSourceClipped(AudioHead *audio_head)
|
|
{
|
|
Check(this);
|
|
Check(audioLocation);
|
|
return audioLocation->IsAudioLocationClipped(audio_head);
|
|
}
|
|
|
|
//
|
|
// Resource methods
|
|
//
|
|
|
|
inline AudioResource*
|
|
AudioSource::GetAudioResource()
|
|
{
|
|
Check(this);
|
|
return audioResource;
|
|
}
|
|
|
|
inline AudioVoiceCount
|
|
AudioSource::GetAudioVoiceCount()
|
|
{
|
|
Check(this);
|
|
Check(audioResource);
|
|
return audioResource->GetVoiceCount();
|
|
}
|
|
|
|
inline AudioRenderType
|
|
AudioSource::GetAudioRenderType()
|
|
{
|
|
Check(this);
|
|
Check(audioResource);
|
|
return audioResource->GetAudioRenderType();
|
|
}
|
|
|
|
//
|
|
// Current State/Status Accessors
|
|
//
|
|
|
|
inline AudioSourceState
|
|
AudioSource::GetAudioSourceState()
|
|
{
|
|
Check(this);
|
|
return audioSourceState;
|
|
}
|
|
|
|
inline Logical
|
|
AudioSource::IsFinishedPlaying()
|
|
{
|
|
Check(this);
|
|
return currentStartState.IsFinishedPlaying();
|
|
}
|
|
|
|
inline AudioTime
|
|
AudioSource::GetCurrentRunningTime()
|
|
{
|
|
Check(this);
|
|
Verify(audioSourceState == RunningAudioSourceState);
|
|
return currentStartState.GetCurrentRunningTime();
|
|
}
|
|
|
|
inline AudioControlValue
|
|
AudioSource::GetCurrentNoteValue()
|
|
{
|
|
Check(this);
|
|
return currentStartState.GetNoteValue();
|
|
}
|
|
|
|
inline AudioControlValue
|
|
AudioSource::CalculateSourceAttackVolumeScale()
|
|
{
|
|
Check(this);
|
|
return attackVolumeScale;
|
|
}
|
|
|
|
inline Logical
|
|
AudioSource::UseSourceAttackTime()
|
|
{
|
|
Check(this);
|
|
return useAttackTimeScale;
|
|
}
|
|
|
|
inline AudioControlValue
|
|
AudioSource::CalculateSourceAttackTime()
|
|
{
|
|
Check(this);
|
|
return attackTimeScale;
|
|
}
|
|
|
|
//
|
|
// Priority Accessors
|
|
//
|
|
|
|
inline void
|
|
AudioSource::SetAudioSourcePriority(
|
|
AudioSourcePriority audio_source_priority
|
|
)
|
|
{
|
|
Check(this);
|
|
audioSourcePriority = audio_source_priority;
|
|
}
|
|
|
|
inline AudioSourcePriority
|
|
AudioSource::GetAudioSourcePriority()
|
|
{
|
|
Check(this);
|
|
return audioSourcePriority;
|
|
}
|
|
|
|
inline AudioWeighting
|
|
AudioSource::CalculateAudioWeighting()
|
|
{
|
|
Check(this);
|
|
AudioWeighting
|
|
audio_weight(
|
|
GetAudioSourcePriority(),
|
|
CalculateSourceVolumeScale()
|
|
);
|
|
return audio_weight;
|
|
}
|
|
|
|
//
|
|
// Mixing & Logical compression methods
|
|
//
|
|
|
|
inline AudioSourceMixPresence
|
|
AudioSource::GetAudioSourceMixPresence()
|
|
{
|
|
Check(this);
|
|
return audioSourceMixPresence;
|
|
}
|
|
|
|
inline void
|
|
AudioSource::SetVolumeCompressionScale(
|
|
AudioControlValue compression_level
|
|
)
|
|
{
|
|
Check(this);
|
|
volumeCompressionScale = compression_level;
|
|
}
|
|
|
|
inline AudioPitchCents
|
|
AudioSource::CalculateSourcePitchOffset()
|
|
{
|
|
Check(this);
|
|
return pitchMixOffset + pitchOffset;
|
|
}
|
|
|
|
inline Logical
|
|
AudioSource::UseSourceBrightnessScale()
|
|
{
|
|
Check(this);
|
|
return useBrightnessScale;
|
|
}
|
|
|
|
//
|
|
// Execute
|
|
//
|
|
|
|
inline Logical
|
|
AudioSource::RequiresMaintenance(AudioHead *audio_head)
|
|
{
|
|
Check(this);
|
|
Check(audio_head);
|
|
return (nextExecuteFrame <= audio_head->GetAudioFrameCount());
|
|
}
|
|
|
|
inline Logical
|
|
AudioSource::CanResume(AudioHead *audio_head)
|
|
{
|
|
Check(this);
|
|
Check(audio_head);
|
|
return (suspendFinishedFrame <= audio_head->GetAudioFrameCount());
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~ AudioSource__RequestMessage ~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
class AudioSource__RequestMessage:
|
|
public Receiver::Message
|
|
{
|
|
public:
|
|
AudioSource__RequestMessage(
|
|
Receiver::MessageID id,
|
|
size_t message_length,
|
|
AudioControlID control_ID,
|
|
AudioControlValue control_value
|
|
);
|
|
|
|
AudioControlID
|
|
controlID;
|
|
AudioControlValue
|
|
controlValue;
|
|
};
|
|
|
|
inline AudioSource__RequestMessage::AudioSource__RequestMessage(
|
|
Receiver::MessageID message_ID,
|
|
size_t message_length,
|
|
AudioControlID control_ID,
|
|
AudioControlValue control_value
|
|
):
|
|
Receiver::Message(
|
|
message_ID,
|
|
message_length
|
|
)
|
|
{
|
|
controlID = control_ID;
|
|
controlValue = control_value;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~ AudioSource__StartMessage ~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
class AudioSource__StartMessage:
|
|
public AudioSource::RequestMessage
|
|
{
|
|
public:
|
|
AudioSource__StartMessage(AudioControlValue control_value);
|
|
};
|
|
|
|
inline AudioSource__StartMessage::AudioSource__StartMessage(
|
|
AudioControlValue control_value
|
|
):
|
|
AudioSource::RequestMessage(
|
|
AudioSource::StartMessageID,
|
|
sizeof(AudioSource__StartMessage),
|
|
StartAudioControlID,
|
|
control_value
|
|
)
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~ AudioSource__StopMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
class AudioSource__StopMessage:
|
|
public AudioSource::RequestMessage
|
|
{
|
|
public:
|
|
AudioSource__StopMessage();
|
|
};
|
|
|
|
inline AudioSource__StopMessage::AudioSource__StopMessage():
|
|
AudioSource::RequestMessage(
|
|
AudioSource::StopMessageID,
|
|
sizeof(AudioSource__StopMessage),
|
|
StopAudioControlID,
|
|
0.0f
|
|
)
|
|
{
|
|
}
|