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

693 lines
16 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "boxsolid.h"
#include "collorgn.h"
#include "app.h"
#include "notation.h"
//#############################################################################
//########################### LatticeZone ###############################
//#############################################################################
LatticeZone::LatticeZone(const ExtentBox &extents):
boundingBox(extents, this)
{
}
LatticeZone::~LatticeZone()
{
}
//#############################################################################
//######################### EnvironmentZone #############################
//#############################################################################
EnvironmentZone::EnvironmentZone(
const ExtentBox &extents,
const Environment &the_environment
):
LatticeZone(extents)
{
environment = the_environment;
}
EnvironmentZone::~EnvironmentZone()
{
}
Logical
EnvironmentZone::TestInstance() const
{
LatticeZone::TestInstance();
Check(&environment);
return True;
}
//#############################################################################
//########################### InterestZone ##############################
//#############################################################################
InterestZone::InterestZone(
const ExtentBox &extents,
InterestZoneID interest_zone_id,
BoxedSolidTree *collision_root,
EnvironmentZone *environment_zone,
BoundingBoxTree *existance_root
):
LatticeZone(extents)
{
Verify(interest_zone_id != NullInterestZoneID);
Check(collision_root);
Check(environment_zone);
Check(existance_root);
interestZoneID = interest_zone_id;
collisionRoot = collision_root;
environmentZone = environment_zone;
collisionOrigin = NULL;
existanceRoot = existance_root;
}
InterestZone::~InterestZone()
{
//
// HACK - delete collision tree
//
Unregister_Object(collisionRoot);
delete collisionRoot;
collisionRoot = NULL;
//
// HACK - delete environment zone
//
Unregister_Object(environmentZone);
delete environmentZone;
environmentZone = NULL;
//
// HACK - delete existance tree
//
Unregister_Object(existanceRoot);
delete existanceRoot;
existanceRoot = NULL;
//
// HACK - delete collision origin
//
if (collisionOrigin != NULL)
{
Check(application);
Check(application->GetInterestManager());
application->GetInterestManager()->OrphanInterestOrigin(collisionOrigin);
CollisionOrigin *col_origin = collisionOrigin;
collisionOrigin = NULL;
Unregister_Object(col_origin);
delete col_origin;
}
}
Logical
InterestZone::TestInstance() const
{
LatticeZone::TestInstance();
Verify(interestZoneID != NullInterestZoneID);
if (collisionOrigin != NULL)
{
Check(collisionOrigin);
}
if (collisionRoot != NULL)
{
Check(collisionRoot);
}
if (environmentZone != NULL)
{
Check(environmentZone);
}
if (existanceRoot != NULL)
{
Check(existanceRoot);
}
return True;
}
//#############################################################################
//########################### InterestArena #############################
//#############################################################################
InterestArena::InterestArena(InterestZone *interest_zone):
interestZoneSocket(NULL)
{
Check(interest_zone);
Check(&interestZoneSocket);
interestZoneSocket.Add(interest_zone);
}
InterestArena::~InterestArena()
{
//
// deconnect from interest zone
//
Check(&interestZoneSocket);
#if DEBUG_LEVEL>0
InterestZone *interest_zone = interestZoneSocket.GetCurrent();
Check(interest_zone);
#endif
interestZoneSocket.Remove();
#if 0
//
// If the interest zone is not in any interest arenas then delete
//
PlugIterator iterator(interest_zone);
Check(&iterator);
if (iterator.GetSize() == 0)
{
Unregister_Object(interest_zone);
delete interest_zone;
}
#endif
}
Logical
InterestArena::TestInstance() const
{
Node::TestInstance();
Check(&interestZoneSocket);
return True;
}
//#############################################################################
//########################### LatticeManager ############################
//#############################################################################
LatticeManager::LatticeManager()
{
}
LatticeManager::~LatticeManager()
{
}
//#############################################################################
//####################### InterestLatticeManager ########################
//#############################################################################
//
//#############################################################################
//#############################################################################
//
InterestLatticeManager::InterestLatticeManager():
interestZoneSocket(NULL)
{
}
//
//#############################################################################
//#############################################################################
//
InterestLatticeManager::~InterestLatticeManager()
{
InterestZone *interest_zone;
Check(&interestZoneSocket);
if ((interest_zone = interestZoneSocket.GetCurrent()) != NULL)
{
Unregister_Object(interest_zone);
delete interest_zone;
}
}
//
//#############################################################################
//#############################################################################
//
Logical
InterestLatticeManager::TestInstance() const
{
Node::TestInstance();
Check(&interestZoneSocket);
return True;
}
//
//#############################################################################
//#############################################################################
//
InterestArena*
InterestLatticeManager::MakeInterestArena(
InterestZoneID,
InterestType,
InterestDepth
)
{
Check(this);
//
//---------------------------------------------------------------------
// HACK - See if the one interest zone has been created
//---------------------------------------------------------------------
//
InterestZone *interest_zone;
if ((interest_zone = interestZoneSocket.GetCurrent()) == NULL)
{
//
//---------------------------------------------------------------------
// Create the one environment
//---------------------------------------------------------------------
//
EnvironmentZone *environment_zone;
//
// Make the memory stream
//
Check(application);
ResourceFile *res_file = application->GetResourceFile();
Check(res_file);
ResourceDescription *resource_description =
res_file->FindResourceDescription(
"EnvironmentZone",
ResourceDescription::EnvironmentZoneResourceType
);
if (resource_description)
{
Check(resource_description);
resource_description->Lock();
MemoryStream
memory_stream(
resource_description->resourceAddress,
resource_description->resourceSize
);
//
// Get the construction arguments
//
ExtentBox extent_box;
Environment environment;
memory_stream >> extent_box >> environment;
resource_description->Unlock();
//
// Make the environment zone
//
environment_zone = new EnvironmentZone(extent_box, environment);
Register_Object(environment_zone);
}
//
//---------------------------------------------------------------------
// Create the one interest zone
//---------------------------------------------------------------------
//
resource_description =
res_file->FindResourceDescription(
"InterestZone",
ResourceDescription::InterestZoneResourceType
);
if (resource_description)
{
Check(resource_description);
resource_description->Lock();
MemoryStream
memory_stream(
resource_description->resourceAddress,
resource_description->resourceSize
);
//
// Get the construction arguments
//
ExtentBox extent_box;
InterestZoneID interest_zone_ID;
CString map_resource_name;
memory_stream >> extent_box >> interest_zone_ID >> map_resource_name;
resource_description->Unlock();
//
// Make the collision tree root
//
BoxedSolidTree *collision_root = new BoxedSolidTree;
Register_Object(collision_root);
//
// Make the existance tree root
//
BoundingBoxTree *existance_root = new BoundingBoxTree;
Register_Object(existance_root);
//
// Make the interest zone
//
interest_zone =
new InterestZone(
extent_box,
interest_zone_ID,
collision_root,
environment_zone,
existance_root
);
Register_Object(interest_zone);
}
interestZoneSocket.Add(interest_zone);
Verify(interest_zone == interestZoneSocket.GetCurrent());
}
Check(interest_zone);
//
//---------------------------------------------------------------------
// Create an interest arena pointing at this zone
//---------------------------------------------------------------------
//
return new InterestArena(interest_zone);
}
#if 0
InterestArena*
InterestLatticeManager::MakeInterestArena(
InterestZoneID,
InterestType,
InterestDepth
)
{
Check(this);
//
//---------------------------------------------------------------------
// HACK - Create one interest zone
//---------------------------------------------------------------------
//
InterestZone *interest_zone;
if ((interest_zone = interestZoneSocket.GetCurrent()) == NULL)
{
const Scalar min_dimension = -100000.0f;
const Scalar max_dimension = 100000.0f;
Vector3D min_vector(min_dimension, min_dimension, min_dimension);
Vector3D max_vector(max_dimension, max_dimension, max_dimension);
ExtentBox big_extent(min_vector, max_vector);
//
// Pick one interest zone id
//
InterestZoneID interest_zone_ID = NullInterestZoneID + 1;
//
// Set up one environment
//
Environment environment;
EnvironmentZone *environment_zone;
environment.gravityConstant = 6.5f;
environment.airDensity = 1.0f;
environment.windVelocity = Vector3D::Identity;
environment.ambientTemperature = 294.26f; // Seventy degrees farenheit
environment_zone = new EnvironmentZone(big_extent, environment);
Register_Object(environment_zone);
//
// Load entire collision tree
//
BoxedSolidTree *collision_root = new BoxedSolidTree;
Register_Object(collision_root);
//
// Make one interest zone for everything
//
interest_zone =
new InterestZone(
big_extent,
interest_zone_ID,
collision_root,
environment_zone
);
Register_Object(interest_zone);
interestZoneSocket.Add(interest_zone);
Verify(interest_zone == interestZoneSocket.GetCurrent());
}
Check(interest_zone);
//
//---------------------------------------------------------------------
// Create an interest arena pointing at this zone
//---------------------------------------------------------------------
//
return new InterestArena(interest_zone);
}
#endif
#if 0
//
//#############################################################################
//#############################################################################
//
void
InterestLatticeManager::DestroyInterestArena(InterestArena *interest_arena)
{
Check(this);
Check(interest_arena);
//
//---------------------------------------------------------------------
// Delete the interest arena
//---------------------------------------------------------------------
//
Unregister_Object(interest_arena);
delete interest_arena;
}
#endif
//
//#############################################################################
//#############################################################################
//
InterestZoneID
InterestLatticeManager::SearchForEntityInterestZone(
InterestZoneID,
const EntityID&
)
{
Check(this);
Check(interestZoneSocket.GetCurrent());
return interestZoneSocket.GetCurrent()->GetInterestZoneID();
}
//
//#############################################################################
//#############################################################################
//
InterestZone*
InterestLatticeManager::GetInterestZone(InterestZoneID)
{
Check(this);
return interestZoneSocket.GetCurrent();
}
//
//#############################################################################
//#############################################################################
//
void
InterestLatticeManager::CreateZoneResources(
ResourceFile *resource_file,
NotationFile *zone_notation_file
)
{
Check(zone_notation_file);
//
//---------------------------------------------------------------------
// HACK - Create the one environment zone resource
//---------------------------------------------------------------------
//
{
//
// Environment zone construction arguments
//
ExtentBox extent_box;
Environment environment;
//
// Get environment zone construction arguments
//
const char *contents;
#if DEBUG_LEVEL>0
int ret = zone_notation_file->GetEntry(
"EnvironmentZone",
"extent_box",
&contents
);
Verify(ret);
#else
zone_notation_file->GetEntry(
"EnvironmentZone",
"extent_box",
&contents
);
#endif
Convert_From_Ascii(contents, &extent_box);
#if DEBUG_LEVEL>0
ret = zone_notation_file->GetEntry(
"EnvironmentZone",
"environment",
&contents
);
Verify(ret);
#else
zone_notation_file->GetEntry(
"EnvironmentZone",
"environment",
&contents
);
#endif
Convert_From_Ascii(contents, &environment);
//
// Create environment zone memory stream
//
DynamicMemoryStream environment_zone_memory_stream;
environment_zone_memory_stream << extent_box << environment;
//
// Add environment zone resource
//
CString environment_zone_resource_name("EnvironmentZone");
#if DEBUG_LEVEL>0
ResourceDescription *res_description =
resource_file->AddResourceMemoryStream(
environment_zone_resource_name,
ResourceDescription::EnvironmentZoneResourceType,
1,
ResourceDescription::Preload,
&environment_zone_memory_stream
);
Check(res_description);
#else
resource_file->AddResourceMemoryStream(
environment_zone_resource_name,
ResourceDescription::EnvironmentZoneResourceType,
1,
ResourceDescription::Preload,
&environment_zone_memory_stream
);
#endif
}
//
//---------------------------------------------------------------------
// HACK - Create the one interest zone resource
//---------------------------------------------------------------------
//
{
//
// Interest zone construction arguments
//
ExtentBox extent_box;
InterestZoneID interest_zone_ID;
CString map_resource_name;
//
// Get interest zone construction arguments
//
const char *contents;
#if DEBUG_LEVEL>0
int ret = zone_notation_file->GetEntry(
"InterestZone",
"extent_box",
&contents
);
Verify(ret);
#else
zone_notation_file->GetEntry(
"InterestZone",
"extent_box",
&contents
);
#endif
Convert_From_Ascii(contents, &extent_box);
#if DEBUG_LEVEL>0
ret = zone_notation_file->GetEntry(
"InterestZone",
"interest_zone_ID",
&contents
);
Verify(ret);
#else
zone_notation_file->GetEntry(
"InterestZone",
"interest_zone_ID",
&contents
);
#endif
Convert_From_Ascii(contents, &interest_zone_ID);
#if DEBUG_LEVEL>0
ret = zone_notation_file->GetEntry(
"InterestZone",
"map_resource_name",
&contents
);
Verify(ret);
#else
zone_notation_file->GetEntry(
"InterestZone",
"map_resource_name",
&contents
);
#endif
map_resource_name = contents;
//
// Create interest zone memory stream
//
DynamicMemoryStream interest_zone_memory_stream;
interest_zone_memory_stream << extent_box << interest_zone_ID;
interest_zone_memory_stream << map_resource_name;
//
// Add interest zone resource
//
CString interest_zone_resource_name("InterestZone");
#if DEBUG_LEVEL>0
ResourceDescription *res_description =
resource_file->AddResourceMemoryStream(
interest_zone_resource_name,
ResourceDescription::InterestZoneResourceType,
1,
ResourceDescription::Preload,
&interest_zone_memory_stream
);
Check(res_description);
#else
resource_file->AddResourceMemoryStream(
interest_zone_resource_name,
ResourceDescription::InterestZoneResourceType,
1,
ResourceDescription::Preload,
&interest_zone_memory_stream
);
#endif
}
}