Files
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

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
)
{
}