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

656 lines
14 KiB
C++

#pragma once
#include "style.h"
#include "entity.h"
#include "sphere.h"
#include "slot.h"
#include "table.h"
class PlugStream;
//
//--------------------------------------------------------------------------
// Support types
//--------------------------------------------------------------------------
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Time types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef long AudioTick;
typedef long AudioDivisionsPerBeat;
typedef long AudioTempo;
typedef long AudioFrameCount;
const AudioFrameCount NullAudioFrameCount = -1;
const AudioFrameCount DefaultAudioFrameDelay = 2;
extern const Scalar AudioDoubleTriggerCutoff;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Resource types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef int AudioResourceID;
typedef int AudioVoiceCount;
typedef Scalar AudioRadiationProfile;
typedef Scalar AudioPitchCents;
enum AudioRenderType
{
TransientAudioRenderType = 0,
SustainedAudioRenderType = 1
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Source types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum AudioRepresentation
{
InternalAudioRepresentation = 0,
ExternalAudioRepresentation = 1
};
#define AUDIO_SOURCE_PRIORITY_COUNT (5)
enum AudioSourcePriority
{
MinAudioSourcePriority = 0,
LowAudioSourcePriority = 1,
MedAudioSourcePriority = 2,
HighAudioSourcePriority = 3,
MaxAudioSourcePriority = 4
};
enum AudioSourceState
{
StoppedAudioSourceState = 0,
DormantAudioSourceState = 1,
RunningAudioSourceState = 2,
SuspendedAudioSourceState = 3
};
enum AudioSourceMixPresence
{
ManualAudioSourceMixPresence = 0,
MaxAudioSourceMixPresence = 1,
HighAudioSourceMixPresence = 2,
MedAudioSourceMixPresence = 3,
LowAudioSourceMixPresence = 4,
MinAudioSourceMixPresence = 5
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Control types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum AudioControlID
{
NullAudioControlID = 0,
StartAudioControlID,
StopAudioControlID,
VolumeAudioControlID,
PitchAudioControlID,
BrightnessAudioControlID,
AttackVolumeAudioControlID,
AttackTimeAudioControlID,
NoteAudioControlID,
IdleAudioControlID,
DurationAudioControlID,
FlushMessagesAudioControlID,
TempoAudioControlID,
AudioControlIDCount
};
typedef Scalar AudioControlValue;
extern const AudioControlValue MaxAudioVolume;
extern const AudioControlValue MinAudioVolume;
extern const AudioControlValue LowAudioVolumeThreshold;
extern const AudioControlValue MaxAudioBrightness;
extern const AudioControlValue MinAudioBrightness;
extern const AudioControlValue MaxAudioAttack;
extern const AudioControlValue MinAudioAttack;
#if 0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioClippingSphere ~~~~~~~~~~~~~~~~~~~~~~~~~~
class AudioClippingSphere SIGNATURED
{
public:
AudioClippingSphere();
~AudioClippingSphere();
void
SetReferenceRadius(Scalar radius);
void
SetCurrentOrigin(const Point3D &origin);
void
SetCurrentScale(Scalar scale);
Logical
Contains(const Point3D &point) const;
private:
Scalar
referenceRadius;
Sphere
currentSphere;
};
//~~~~~~~~~~~~~~~~~~~~~~~ AudioClippingSphere inlines ~~~~~~~~~~~~~~~~~~~~~~
inline AudioClippingSphere::AudioClippingSphere():
referenceRadius(100.0f),
currentSphere(0.0f, 0.0f, 0.0f, 100.0f)
{
}
inline AudioClippingSphere::~AudioClippingSphere()
{
}
inline void
AudioClippingSphere::SetReferenceRadius(Scalar radius)
{
Check(this);
referenceRadius = radius;
currentSphere.radius = radius;
}
inline void
AudioClippingSphere::SetCurrentOrigin(const Point3D &origin)
{
Check(this);
currentSphere.center = origin;
}
inline void
AudioClippingSphere::SetCurrentScale(Scalar scale)
{
Check(this);
currentSphere.radius = scale * referenceRadius;
}
inline Logical
AudioClippingSphere::Contains(const Point3D &point) const
{
Check(this);
return currentSphere.Contains(point);
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHead ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AudioSource;
class AudioHead SIGNATURED
{
public:
//
//-----------------------------------------------------------------------
// Construction, Destruction, Testing
//-----------------------------------------------------------------------
//
AudioHead();
~AudioHead();
Logical
TestInstance() const;
//
//-----------------------------------------------------------------------
// Get current frame of audio simulation
//-----------------------------------------------------------------------
//
AudioFrameCount
GetAudioFrameCount()
{return audioFrameCount;}
//
//-----------------------------------------------------------------------
// Sets the location and orientation of the head
//-----------------------------------------------------------------------
//
virtual void
LinkToEntity(Entity *entity);
Entity*
GetHeadEntity();
//
//-----------------------------------------------------------------------
// Get the ear to world linear matrix
//-----------------------------------------------------------------------
//
virtual LinearMatrix
GetEarToWorld();
//
//-----------------------------------------------------------------------
// Execute
//-----------------------------------------------------------------------
//
virtual void
Execute();
//
//-----------------------------------------------------------------------
// Sets the distance between ears. Expressed in meters.
//-----------------------------------------------------------------------
//
void
SetDistanceBetweenEars(Scalar distance)
{distanceBetweenEars = distance;}
Scalar
GetDistanceBetweenEars()
{return distanceBetweenEars;}
//
//-----------------------------------------------------------------------
// Controls Doppler effects
//-----------------------------------------------------------------------
//
void
ControlDopplerEffect(
Scalar doppler_constant,
Scalar sound_speed
);
Scalar
GetAudioSoundSpeed()
{return audioSoundSpeed;}
AudioPitchCents
GetAudioDopplerConstant()
{return audioDopplerConstant;}
//
//-----------------------------------------------------------------------
// Reverb scaling
//-----------------------------------------------------------------------
//
void
SetReverbToDryRatio(Scalar ratio)
{reverbToDryRatio = ratio;}
Scalar
GetReverbToDryRatio()
{return reverbToDryRatio;}
// HACK
void
SetGlobalReverbScale(Scalar global_reverb_scale)
{globalReverbScale = global_reverb_scale;}
Scalar
GetGlobalReverbScale()
{return globalReverbScale;}
//
//-----------------------------------------------------------------------
// Controls the attenuation of an audio source with respect to its
// distance from the head.
//-----------------------------------------------------------------------
//
void
ControlAmplitudeRollOff(
Scalar exponent,
Scalar amplitude_rolloff_knee,
Scalar distance_scale
);
Scalar
GetAmplitudeRollOffExponent()
{return amplitudeRollOffExponent;}
Scalar
GetAmplitudeRollOffKnee()
{return amplitudeRollOffKnee;}
Scalar
GetAmplitudeRollOffDistanceScale()
{return amplitudeRollOffDistanceScale;}
//
//-----------------------------------------------------------------------
// Controls the attenuation of the high frequencies
// of an audio source with respect to its distance from
// the head.
//-----------------------------------------------------------------------
//
void
ControlHighFrequencyRollOff(
Scalar exponent,
Scalar high_frequency_rolloff_knee,
Scalar distance_scale
);
Scalar
GetHighFrequencyRollOffExponent()
{return highFrequencyRollOffExponent;}
Scalar
GetHighFrequencyRollOffKnee()
{return highFrequencyRollOffKnee;}
Scalar
GetHighFrequencyRollOffDistanceScale()
{return highFrequencyRollOffDistanceScale;}
//
//-----------------------------------------------------------------------
// Controls source compression
//-----------------------------------------------------------------------
//
void
ControlSourceCompression(
Scalar exponent,
Scalar scale
);
Scalar
GetSourceCompressionExponent()
{return sourceCompressionExponent;}
Scalar
GetSourceCompressionScale()
{return sourceCompressionScale;}
//
//-----------------------------------------------------------------------
// Defines the audio clipping sphere
//-----------------------------------------------------------------------
//
void
DefineClippingSphere(Scalar radius);
Logical
IsPointClipped(const Point3D &point);
Scalar
GetClippingRadius()
{return clippingRadius;}
//
//-----------------------------------------------------------------------
// ITD Difference
//-----------------------------------------------------------------------
//
void
SetITDDifference(Scalar itd_difference)
{itdDifference = itd_difference;}
Scalar
GetITDDifference()
{return itdDifference;}
//
//-----------------------------------------------------------------------
// The SetPositionalCulling method allows the rendering process to
// not play audio locations whose positions are masked by other
// audio sources. This has found to be useful for controlling
// audio when resources are limited, but must be
// selectable globally or on a per audio effect basis.
//-----------------------------------------------------------------------
//
void
SetPositionalCulling(Logical turn_on);
private:
//
//-----------------------------------------------------------------------
// Private data
//-----------------------------------------------------------------------
//
AudioFrameCount
audioFrameCount;
SlotOf<Entity*>
headEntitySocket;
Scalar
clippingRadius;
Scalar
distanceBetweenEars;
Scalar
amplitudeRollOffExponent,
amplitudeRollOffKnee,
amplitudeRollOffDistanceScale;
Scalar
highFrequencyRollOffExponent,
highFrequencyRollOffKnee,
highFrequencyRollOffDistanceScale;
Scalar
reverbToDryRatio;
Scalar
audioSoundSpeed;
Scalar
audioDopplerConstant;
Scalar
sourceCompressionExponent,
sourceCompressionScale;
Scalar
itdDifference;
Scalar
globalReverbScale;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHead inlines ~~~~~~~~~~~~~~~~~~~~~~~~~
inline Entity*
AudioHead::GetHeadEntity()
{
Check(this);
return headEntitySocket.GetCurrent();
}
//##########################################################################
//######################### AudioComponent ###########################
//##########################################################################
class AudioComponent__ReceiveControlMessage;
class AudioComponent:
public Component
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction, Testing
//
public:
AudioComponent(
PlugStream *stream,
SharedData &shared_data = DefaultData
);
~AudioComponent();
Logical
TestInstance() const;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Audio Component Methods
//
public:
void
AddWatcher(Component *component);
void
ExecuteWatchers();
void
Execute();
virtual void
ReceiveControl(
AudioControlID control_ID,
AudioControlValue control_value
);
void
PostReceiveControl(
AudioControlID control_ID,
AudioControlValue control_value
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Message Support
//
public:
enum {
ReceiveControlMessageID = Component::NextMessageID,
NextMessageID
};
typedef AudioComponent__ReceiveControlMessage
ReceiveControlMessage;
static const HandlerEntry
MessageHandlerEntries[];
//static MessageHandlerSet MessageHandlers;
static MessageHandlerSet& GetMessageHandlers();
void
ReceiveControlMessageHandler(
ReceiveControlMessage *message
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation *GetClassDerivations();
static SharedData
DefaultData;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private Data
//
private:
AudioFrameCount
nextExecuteWatcherFrame;
ChainOf<Component*>
audioWatcherSocket;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioComponent inlines ~~~~~~~~~~~~~~~~~~~~~~~~
inline void
AudioComponent::AddWatcher(Component *component)
{
Check(component);
Check(&audioWatcherSocket);
audioWatcherSocket.Add(component);
}
//~~~~~~~~~~~~~~~~~~ AudioComponent__ReceiveControlMessage ~~~~~~~~~~~~~~~~~
class AudioComponent__ReceiveControlMessage:
public Receiver::Message
{
friend class AudioComponent;
public:
AudioComponent__ReceiveControlMessage(
AudioControlID control_ID,
AudioControlValue control_value
);
private:
AudioControlID
controlID;
AudioControlValue
controlValue;
};
//~~~~~~~~~~~~~~~~~~~~~~~~ Resource type inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
AudioRenderType *output
)
{
return stream->ReadBytes(output, sizeof(*output));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const AudioRenderType *input
)
{
return stream->WriteBytes(input, sizeof(*input));
}
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
AudioRepresentation *output
)
{
return stream->ReadBytes(output, sizeof(*output));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const AudioRepresentation *input
)
{
return stream->WriteBytes(input, sizeof(*input));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ Source type inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
AudioSourceState *output
)
{
return stream->ReadBytes(output, sizeof(*output));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const AudioSourceState *input
)
{
return stream->WriteBytes(input, sizeof(*input));
}
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
AudioSourcePriority *output
)
{
return stream->ReadBytes(output, sizeof(*output));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const AudioSourcePriority *input
)
{
return stream->WriteBytes(input, sizeof(*input));
}
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
AudioSourceMixPresence *output
)
{
return stream->ReadBytes(output, sizeof(*output));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const AudioSourceMixPresence *input
)
{
return stream->WriteBytes(input, sizeof(*input));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~ Control type inlines ~~~~~~~~~~~~~~~~~~~~~~~~~
inline MemoryStream&
MemoryStream_Read(
MemoryStream* stream,
AudioControlID *output
)
{
return stream->ReadBytes(output, sizeof(*output));
}
inline MemoryStream&
MemoryStream_Write(
MemoryStream* stream,
const AudioControlID *input
)
{
return stream->WriteBytes(input, sizeof(*input));
}