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

680 lines
16 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "registry.h"
#include "player.h"
#include "mission.h"
#include "terrain.h"
#include "cultural.h"
#include "dropzone.h"
#include "explode.h"
#include "doorfram.h"
#include "eyecandy.h"
#include "director.h"
#include "camship.h"
#include "app.h"
#include "hostmgr.h"
//#############################################################################
//############################# Registry ################################
//#############################################################################
//#############################################################################
// Message Support
//
const Receiver::HandlerEntry
Registry::MessageHandlerEntries[]=
{
{
Registry::MakeEntityMessageID,
"MakeEntity",
(Registry::Handler)&Registry::MakeEntityMessageHandler
}
};
Registry::MessageHandlerSet& Registry::GetMessageHandlers()
{
static Registry::MessageHandlerSet messageHandlers(ELEMENTS(Registry::MessageHandlerEntries), Registry::MessageHandlerEntries, Receiver::GetMessageHandlers());
return messageHandlers;
}
//#############################################################################
// Shared Data support
//
Derivation* Registry::GetClassDerivations()
{
static Derivation classDerivations(Receiver::GetClassDerivations(), "Registry");
return &classDerivations;
}
Registry::SharedData
Registry::DefaultData(
Registry::GetClassDerivations(),
Registry::GetMessageHandlers()
);
//
//#############################################################################
// Registry
//#############################################################################
//
Registry::Registry(
ClassID class_ID,
SharedData &shared_data
):
Receiver(class_ID, shared_data)
{
}
//
//#############################################################################
// Registry
//#############################################################################
//
Registry::Registry():
Receiver(TrivialReceiverClassID, DefaultData)
{
}
//
//#############################################################################
// ~Registry
//#############################################################################
//
Registry::~Registry()
{
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
Registry::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}
//
//#############################################################################
// MakeEntity
//#############################################################################
//
Entity*
Registry::MakeEntity(Entity::MakeMessage *message)
{
Check(this);
Check(message);
Entity::SharedData *static_data = GetStaticData(message->classToCreate);
if (static_data != NULL)
{
Check(static_data);
Check_Pointer(static_data->makeHandler);
return (*static_data->makeHandler)(message);
}
return NULL;
}
//
//#############################################################################
// MakeTransferedEntity
//#############################################################################
//
Entity*
Registry::MakeTransferedEntity(
HostID,
EntityID,
void*
)
{
Fail("Registry::MakeTransferedEntity - under construction");
return NULL;
}
//
//#############################################################################
// MakeEntityReplacement
//#############################################################################
//
Entity*
Registry::MakeEntityReplacement(
HostID,
EntityID
)
{
Fail("Registry::MakeEntityReplacement - under construction");
return NULL;
}
//
//#############################################################################
// GetStaticData
//#############################################################################
//
Entity::SharedData*
Registry::GetStaticData(ClassID class_ID)
{
switch (class_ID)
{
case TrivialEntityClassID:
return &Entity::DefaultData;
case DropZoneClassID:
return &DropZone::DefaultData;
case TerrainClassID:
return &Terrain::DefaultData;
case CulturalIconClassID:
return &CulturalIcon::DefaultData;
case LandmarkClassID:
return &Landmark::DefaultData;
case TeamClassID:
return &Team::DefaultData;
case UnscalableTerrainClassID:
return &UnscalableTerrain::DefaultData;
case ExplosionClassID:
return &Explosion::DefaultData;
case PlayerClassID:
return &Player::DefaultData;
case DoorFrameClassID:
return &DoorFrame::DefaultData;
case EyeCandyClassID:
return &EyeCandy::DefaultData;
case CameraShipClassID:
return &CameraShip::DefaultData;
case CameraDirectorClassID:
return &CameraDirector::DefaultData;
default:
return NULL;
}
}
//
//#############################################################################
// GetStaticData
//#############################################################################
//
Player*
Registry::MakePlayer(Mission *mission)
{
Check(mission);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
// See if we are a CameraShip
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Enumeration player_flags = Player::DefaultFlags;
if (!strcmp(mission->GetGameModel(), "camera"))
{
player_flags |= Player::CameraShipPlayerFlag;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// See if we are a MissionReview, CameraShipHost
// GameMachine CameraShip or regular Player!
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Check(application);
HostManager *host_mgr = application->GetHostManager();
Host *host_ptr = host_mgr->GetLocalHost();
Check(host_ptr);
HostType host_type = host_ptr->GetHostType();
switch(host_type)
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Make A Player Which is a GameMachine Host
// i.e. VTV, Mech, CameraShip w/controls
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
case GameMachineHostType:
{
Player::MakeMessage make_player(
Player::MakeMessageID,
sizeof(Player::MakeMessage),
PlayerClassID,
EntityID::Null,
ResourceDescription::NullResourceID,
player_flags,
Origin::Identity,
mission->GetPlayerBitmapIndex()
);
return new Player(&make_player);
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Make A CameraShip Or Mission Review
// Not a CameraShip to Run on a POD!
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
case CameraShipHostType:
case MissionReviewHostType:
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Add any extra flags for a director
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
player_flags |= CameraDirector::DefaultFlags;
CameraDirector::MakeMessage create_director(
CameraDirector::MakeMessageID,
sizeof(CameraDirector::MakeMessage),
CameraDirectorClassID,
EntityID::Null,
ResourceDescription::NullResourceID,
player_flags,
Origin::Identity,
mission->GetPlayerBitmapIndex()
);
return new CameraDirector(&create_director);
}
}
return NULL;
}
//
//#############################################################################
// MakeEntityMessageHandler
//#############################################################################
//
void
Registry::MakeEntityMessageHandler(MakeEntityMessage *message)
{
Check(this);
Check(message);
Verify(message->messageID == MakeEntityMessageID);
//
// Extract the make message
//
MemoryStream
memory_stream(
message,
message->messageLength
);
Entity::MakeMessage
*make_message;
memory_stream.AdvancePointer(sizeof(MakeEntityMessage));
make_message = Cast_Object(Entity::MakeMessage*, memory_stream.GetPointer());
Check(make_message);
//
// Make the entity
//
#if DEBUG_LEVEL>0
Entity *entity = MakeEntity(make_message);
if (entity)
{
Register_Object(entity);
}
#else
MakeEntity(make_message);
#endif
}
//
//#############################################################################
// CreateStaticObjectStreamResource
//#############################################################################
//
ResourceDescription::ResourceID
Registry::CreateStaticObjectStreamResource(
NotationFile *notation_file,
ResourceFile *resource_file
)
{
Check(notation_file);
Check(resource_file);
//
//--------------------------------------------------------------------------
// Build the static object stream
//--------------------------------------------------------------------------
//
RegistryObjectStream static_object_stream;
Check(&static_object_stream);
static_object_stream.BuildFromNotationFile(
notation_file,
resource_file
);
//
//--------------------------------------------------------------------------
// Add the resource
//--------------------------------------------------------------------------
//
ResourceDescription *res_description;
res_description =
resource_file->AddResourceMemoryStream(
"RegistryStaticObjectStream",
ResourceDescription::RegistryStaticObjectStreamResourceType,
1,
ResourceDescription::Preload,
&static_object_stream
);
Check(res_description);
return res_description->resourceID;
}
//
//#############################################################################
// LoadStaticObjectStreamResource
//#############################################################################
//
void
Registry::LoadStaticObjectStreamResource()
{
Check(this);
//
//--------------------------------------------------------------------------
// Create static objects
//--------------------------------------------------------------------------
//
#if 0
//
// Get static object stream resource
//
ResourceDescription *resource_description;
Check(application);
Check(application->GetResourceFile());
resource_description =
application->GetResourceFile()->FindResourceDescription(
"RegistryStaticObjectStream",
ResourceDescription::RegistryStaticObjectStreamResourceType
);
if (resource_description != NULL)
{
Check(resource_description);
//
// Parse the static object stream
//
RegistryObjectStream static_object_stream(resource_description);
Check(&static_object_stream);
static_object_stream.CreateObjects();
}
#else
//
// Load all static object streams
//
ResourceFile *resource_file;
ResourceDescription::ResourceID index, max_resource_ID;
Check(application);
resource_file = application->GetResourceFile();
Check(resource_file);
max_resource_ID = resource_file->GetMaxResourceID();
for (index = 0; index <= max_resource_ID; index++)
{
ResourceDescription *resource_description =
resource_file->FindResourceDescription(index);
if (resource_description)
{
Check(resource_description);
//
// Load the static object streams
//
if (
resource_description->resourceType ==
ResourceDescription::RegistryStaticObjectStreamResourceType
)
{
resource_description->Lock();
//
// Parse the static object stream
//
RegistryObjectStream static_object_stream(resource_description);
Check(&static_object_stream);
static_object_stream.CreateObjects();
resource_description->Unlock();
}
}
}
resource_file->ReleaseUnlockedResources();
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~ Registry__MakeEntityMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//#############################################################################
// Registry__MakeEntityMessage
//#############################################################################
//
Registry__MakeEntityMessage::Registry__MakeEntityMessage(size_t total_length):
Receiver::Message(
Registry::MakeEntityMessageID,
total_length
)
{
}
//
//#############################################################################
// Make
//#############################################################################
//
Registry__MakeEntityMessage*
Registry__MakeEntityMessage::Make(Entity::MakeMessage *make_message)
{
Check(make_message);
//
// Allocate memory for the make message
//
size_t
total_length;
void
*buffer;
total_length =
sizeof (Registry__MakeEntityMessage) + make_message->messageLength;
buffer = new unsigned char[total_length];
Check_Pointer(buffer);
//
// Build the make message
//
MemoryStream
memory_stream(buffer, total_length);
Registry__MakeEntityMessage
*make_entity_message;
make_entity_message =
new (memory_stream.GetPointer()) Registry__MakeEntityMessage(total_length);
Check(make_entity_message);
memory_stream.AdvancePointer(sizeof(Registry__MakeEntityMessage));
Mem_Copy(
memory_stream.GetPointer(),
make_message,
make_message->messageLength,
memory_stream.GetBytesRemaining()
);
return make_entity_message;
}
//#############################################################################
//####################### RegistryObjectStream ##########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
RegistryObjectStream::RegistryObjectStream(
ResourceDescription *resource_description
):
PlugStream(resource_description)
{
}
//
//#############################################################################
//#############################################################################
//
RegistryObjectStream::RegistryObjectStream()
{
}
//
//#############################################################################
//#############################################################################
//
RegistryObjectStream::~RegistryObjectStream()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
RegistryObjectStream::TestInstance() const
{
PlugStream::TestInstance();
return True;
}
//
//#############################################################################
//#############################################################################
//
RegisteredClass*
RegistryObjectStream::MakeObjectImplementation(Enumeration class_ID)
{
Check(this);
Verify(class_ID != RegisteredClass::NullClassID);
RegisteredClass *object;
switch (class_ID)
{
//
// Static resources ...
//
case RegisteredClass::ExplosionResourceTableClassID:
object = new ExplosionResourceTable(this);
break;
//
// Inherited behavior
//
default:
object = PlugStream::MakeObjectImplementation(class_ID);
break;
}
return object;
}
//
//#############################################################################
//#############################################################################
//
void
RegistryObjectStream::CreatedObjectImplementation(
RegisteredClass *object,
ObjectID object_ID
)
{
Check(this);
Check(object);
Verify(object_ID != NullObjectID);
//
// Decide which index to add object to
//
switch (object->GetClassID())
{
//
// Static resources ...
//
case RegisteredClass::ExplosionResourceTableClassID:
AddGlobalPlug(Cast_Object(Plug*, object), object_ID);
break;
default:
break;
}
}
//
//#############################################################################
//#############################################################################
//
void
RegistryObjectStream::BuildFromPageImplementation(
NameList *name_list,
Enumeration class_ID_enumeration,
ObjectID object_ID
)
{
Check(this);
Verify(class_ID_enumeration != RegisteredClass::NullClassID);
Verify(object_ID != NullObjectID);
RegisteredClass::ClassID
class_ID = (RegisteredClass::ClassID)class_ID_enumeration;
switch (class_ID)
{
//
// Static resources ...
//
case RegisteredClass::ExplosionResourceTableClassID:
ExplosionResourceTable::BuildFromPage(this, name_list, class_ID, object_ID);
break;
//
// Inherited behavior
//
default:
PlugStream::BuildFromPageImplementation(name_list, class_ID, object_ID);
break;
}
}
//
//#############################################################################
//#############################################################################
//
void
RegistryObjectStream::BuiltFromPageImplementation(
Enumeration class_ID,
ObjectID object_ID,
const CString &object_name
)
{
Check(this);
Verify(class_ID != RegisteredClass::NullClassID);
Verify(object_ID != NullObjectID);
Check(&object_name);
//
// Decide which index to add object to
//
switch (class_ID)
{
case RegisteredClass::ExplosionResourceTableClassID:
AddGlobalObjectID(object_ID, object_name);
break;
default:
break;
}
}