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>
476 lines
9.6 KiB
C++
476 lines
9.6 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "icom.h"
|
|
#include "app.h"
|
|
|
|
// #define LOCAL_TEST
|
|
|
|
#if defined(LOCAL_TEST)
|
|
# define Test_Tell(n) Tell(n << flush);
|
|
#else
|
|
# define Test_Tell(n)
|
|
#endif
|
|
|
|
//##########################################################################
|
|
//############################# Icom #################################
|
|
//##########################################################################
|
|
Icom::Icom(
|
|
HostID host_id,
|
|
int unit_number
|
|
):
|
|
Plug(IcomClassID)
|
|
{
|
|
Test_Tell("Icom::Icom(" << host_id << ", " << unit_number << ")\n");
|
|
Check_Pointer(this);
|
|
//--------------------------------------
|
|
// Initialize values
|
|
//--------------------------------------
|
|
hostID = host_id;
|
|
unitNumber = unit_number;
|
|
channel = undefinedChannel;
|
|
channelBeforePA = undefinedChannel;
|
|
PTTStatus = False;
|
|
PACaptive = False;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
Icom::~Icom()
|
|
{
|
|
Test_Tell("Icom::~Icom()\n");
|
|
Check(this);
|
|
//--------------------------------------
|
|
// Cleanup before deletion
|
|
//--------------------------------------
|
|
SetPTTStatusValue(False);
|
|
SetChannelValue(undefinedChannel);
|
|
//--------------------------------------
|
|
// The plug automatically unlinks
|
|
// from the Icom Manager upon deletion.
|
|
//--------------------------------------
|
|
Check_Fpu();
|
|
}
|
|
|
|
Logical
|
|
Icom::TestInstance() const
|
|
{
|
|
Plug::TestInstance();
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
Icom::SetChannel(int new_channel)
|
|
{
|
|
Test_Tell("Icom::SetChannel(" << new_channel << ")\n");
|
|
Check(this);
|
|
//--------------------------------------
|
|
// Broadcast the change
|
|
// (nonexclusive broadcast: we receive
|
|
// and process this msg locally)
|
|
//--------------------------------------
|
|
IcomManager::IMMessage
|
|
message;
|
|
|
|
message.BuildChannelMessage(
|
|
IcomManager::ChannelMessageID,
|
|
hostID,
|
|
unitNumber,
|
|
new_channel
|
|
);
|
|
Check(application);
|
|
application->BroadcastEvent(
|
|
DefaultEventPriority,
|
|
NetworkClient::IcomManagerClientID,
|
|
&message
|
|
);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Icom::SetChannelValue(int new_channel)
|
|
{
|
|
Test_Tell("Icom::SetChannelValue(" << new_channel << ")\n");
|
|
Check(this);
|
|
//--------------------------------------
|
|
// Set local value
|
|
//--------------------------------------
|
|
channel = new_channel;
|
|
Check_Fpu();
|
|
}
|
|
|
|
int
|
|
Icom::GetChannel()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
return channel;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
Icom::SetPTTStatus(Logical new_ptt)
|
|
{
|
|
Test_Tell("Icom::SetPTTStatus(" << new_ptt << ")\n");
|
|
Check(this);
|
|
|
|
//---------------------------------------
|
|
// Disallow talking during public address
|
|
//---------------------------------------
|
|
if (!PACaptive)
|
|
{
|
|
//--------------------------------------
|
|
// Broadcast change
|
|
// (nonexclusive broadcast: we receive
|
|
// and process this msg locally)
|
|
//--------------------------------------
|
|
IcomManager::IMMessage
|
|
message;
|
|
|
|
message.BuildPTTMessage(
|
|
IcomManager::PTTMessageID,
|
|
hostID,
|
|
unitNumber,
|
|
new_ptt
|
|
);
|
|
Check(application);
|
|
application->BroadcastEvent(
|
|
DefaultEventPriority,
|
|
NetworkClient::IcomManagerClientID,
|
|
&message
|
|
);
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Icom::SetPTTStatusValue(Logical new_ptt)
|
|
{
|
|
Test_Tell("Icom::SetPTTStatusValue(" << new_ptt << ")\n");
|
|
Check(this);
|
|
//---------------------------------------
|
|
// Disallow talking during public address
|
|
//---------------------------------------
|
|
if (!PACaptive)
|
|
{
|
|
PTTStatus = new_ptt;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
Logical
|
|
Icom::GetPTTStatus()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
return PTTStatus;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
Icom::SetPACaptive(Logical new_pa)
|
|
{
|
|
Test_Tell("Icom::SetPACaptive(" << new_pa << ")\n");
|
|
Check(this);
|
|
|
|
//--------------------------------------
|
|
// Broadcast the change
|
|
// (nonexclusive broadcast: we receive
|
|
// and process this msg locally)
|
|
//--------------------------------------
|
|
IcomManager::IMMessage
|
|
message;
|
|
|
|
message.BuildPAMessage(
|
|
IcomManager::PAMessageID,
|
|
hostID,
|
|
unitNumber,
|
|
new_pa
|
|
);
|
|
Check(application);
|
|
application->BroadcastEvent(
|
|
DefaultEventPriority,
|
|
NetworkClient::IcomManagerClientID,
|
|
&message
|
|
);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Icom::SetPACaptiveValue(Logical new_pa)
|
|
{
|
|
Test_Tell("Icom::SetPACaptiveValue(" << new_pa << ")\n");
|
|
Check(this);
|
|
|
|
if (new_pa == True)
|
|
{
|
|
if (PACaptive == False)
|
|
{
|
|
channelBeforePA = channel;
|
|
}
|
|
//--------------------------------------
|
|
// Force PTT off during public address
|
|
//--------------------------------------
|
|
if (PTTStatus == True)
|
|
{
|
|
SetPTTStatusValue(False);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--------------------------------------
|
|
// Return to previous channel
|
|
//--------------------------------------
|
|
SetChannelValue(channelBeforePA);
|
|
}
|
|
PACaptive = new_pa;
|
|
Check_Fpu();
|
|
}
|
|
|
|
Logical
|
|
Icom::GetPACaptive()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
return PACaptive;
|
|
}
|
|
|
|
//##########################################################################
|
|
//######################### IcomManager ##############################
|
|
//##########################################################################
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Virtual Data support
|
|
//
|
|
|
|
Derivation* IcomManager::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(NetworkClient::GetClassDerivations(), "IcomManager");
|
|
return &classDerivations;
|
|
}
|
|
|
|
IcomManager::SharedData
|
|
IcomManager::DefaultData(
|
|
IcomManager::GetClassDerivations(),
|
|
IcomManager::GetMessageHandlers()
|
|
);
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Creator
|
|
//
|
|
IcomManager::IcomManager(
|
|
SharedData &shared_data
|
|
):
|
|
NetworkClient(IcomManagerClassID, shared_data, IcomManagerClientID),
|
|
instanceList(this)
|
|
{
|
|
Test_Tell("IcomManager::IcomManager()\n");
|
|
Check_Pointer(this);
|
|
|
|
//--------------------------------
|
|
// Initialize unit number service
|
|
//--------------------------------
|
|
nextUnitNumber = 0;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Destructor
|
|
//
|
|
IcomManager::~IcomManager()
|
|
{
|
|
Test_Tell("IcomManager::~IcomManager()\n");
|
|
Check(this);
|
|
|
|
Shutdown();
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
Logical
|
|
IcomManager::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
IcomManager::LoadMission(Mission */*mission*/)
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
IcomManager::Shutdown()
|
|
{
|
|
Check(this);
|
|
|
|
Icom
|
|
*icom;
|
|
ChainIteratorOf<Icom*>
|
|
i(instanceList);
|
|
|
|
//------------------------------------------
|
|
// Delete all icom objects
|
|
//------------------------------------------
|
|
while ((icom=i.ReadAndNext()) != NULL)
|
|
{
|
|
Check(icom);
|
|
Unregister_Object(icom);
|
|
delete icom;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
IcomManager::Execute()
|
|
{
|
|
Check(this);
|
|
Fail("IcomManager::Execute() should not be called directly!\n");
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
Icom *
|
|
IcomManager::MakeIcom(HostID host_id, int unit_number)
|
|
{
|
|
Test_Tell(
|
|
"IcomManager::MakeIcom(" << host_id <<
|
|
", " << unit_number <<
|
|
")\n"
|
|
);
|
|
Check(this);
|
|
|
|
//------------------------------------------
|
|
// Create, register icom object
|
|
//------------------------------------------
|
|
Icom
|
|
*icom = new Icom(host_id, unit_number);
|
|
Check(icom);
|
|
Register_Object(icom);
|
|
//------------------------------------------
|
|
// Add to global list
|
|
//------------------------------------------
|
|
instanceList.Add(icom);
|
|
|
|
Check_Fpu();
|
|
return icom;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void
|
|
IcomManager::SetPAState(int pa_channel)
|
|
{
|
|
Test_Tell("IcomManager::SetPAState(" << pa_channel << ")\n");
|
|
Check(this);
|
|
|
|
Icom
|
|
*icom;
|
|
|
|
ChainIteratorOf<Icom*>
|
|
i(instanceList);
|
|
|
|
if (pa_channel == Icom::undefinedChannel)
|
|
{
|
|
//------------------------------------------
|
|
// Release all icom objects from PA state
|
|
//------------------------------------------
|
|
while ((icom=i.ReadAndNext()) != NULL)
|
|
{
|
|
Check(icom);
|
|
icom->SetPACaptive(False);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//------------------------------------------
|
|
// Force all icom objects to PA state
|
|
//------------------------------------------
|
|
while ((icom=i.ReadAndNext()) != NULL)
|
|
{
|
|
Check(icom);
|
|
icom->SetPACaptive(True);
|
|
icom->SetChannel(pa_channel);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
Icom*
|
|
IcomManager::FindIcom(HostID host_id, int unit_number)
|
|
{
|
|
Check(this);
|
|
|
|
ChainIteratorOf<Icom*>
|
|
i(instanceList);
|
|
Icom
|
|
*icom;
|
|
|
|
while ((icom=i.ReadAndNext()) != NULL)
|
|
{
|
|
Check(icom);
|
|
if (icom->hostID == host_id)
|
|
{
|
|
if (icom->unitNumber == unit_number)
|
|
{
|
|
Check_Fpu();
|
|
return icom;
|
|
}
|
|
}
|
|
}
|
|
|
|
Check_Fpu();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
IcomManager::ReceiveNetworkPacket(
|
|
NetworkPacket */*packet*/,
|
|
Receiver::Message *packet_message
|
|
)
|
|
{
|
|
Test_Tell("IcomManager::ReceiveNetworkPacket(...)\n");
|
|
Check(this);
|
|
Check(packet_message);
|
|
Verify(packet_message->messageID >= 0);
|
|
Verify(packet_message->messageID < NextMessageID);
|
|
|
|
IMMessage
|
|
*message = (IMMessage *) packet_message;
|
|
|
|
Icom
|
|
*icom = FindIcom(message->hostID, message->unitNumber);
|
|
|
|
if (icom != NULL)
|
|
{
|
|
Check(icom);
|
|
switch (message->messageID)
|
|
{
|
|
case ChannelMessageID:
|
|
icom->SetChannelValue(message->data.channelNumber);
|
|
break;
|
|
|
|
case PTTMessageID:
|
|
icom->SetPTTStatusValue(message->data.PTTStatus);
|
|
break;
|
|
|
|
case PAMessageID:
|
|
icom->SetPACaptiveValue(message->data.PAStatus);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|