Files
BT412/engine/MUNGA/EXTNTBOX.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

252 lines
5.5 KiB
C++

#include "munga.h"
#include "windows.h"
#pragma hdrstop
#include "extntbox.h"
#include "cstr.h"
#include "vector3d.h"
#include "notation.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(USE_SIGNATURE)
int
Is_Signature_Bad(const volatile ExtentBox *)
{
return False;
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExtentBox::ExtentBox(
const Vector3D &min,
const Vector3D &max
)
{
Check_Pointer(this);
Check(&min);
Check(&max);
if (min.x <= max.x)
{
minX = min.x;
maxX = max.x;
}
else
{
minX = max.x;
maxX = min.x;
}
if (min.y <= max.y)
{
minY = min.y;
maxY = max.y;
}
else
{
minY = max.y;
maxY = min.y;
}
if (min.z <= max.z)
{
minZ = min.z;
maxZ = max.z;
}
else
{
minZ = max.z;
maxZ = min.z;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExtentBox::ExtentBox(const ExtentBox &box)
{
Check_Pointer(this);
Check(&box);
minX = box.minX;
maxX = box.maxX;
minY = box.minY;
maxY = box.maxY;
minZ = box.minZ;
maxZ = box.maxZ;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExtentBox&
ExtentBox::Intersect(
const ExtentBox &box_1,
const ExtentBox &box_2
)
{
Check_Pointer(this);
Check(&box_1);
Check(&box_2);
Verify(box_1.minX <= box_2.maxX);
Verify(box_1.maxX >= box_2.minX);
Verify(box_1.minY <= box_2.maxY);
Verify(box_1.maxY >= box_2.minY);
Verify(box_1.minZ <= box_2.maxZ);
Verify(box_1.maxZ >= box_2.minZ);
minX = Max(box_1.minX, box_2.minX);
maxX = Min(box_1.maxX, box_2.maxX);
minY = Max(box_1.minY, box_2.minY);
maxY = Min(box_1.maxY, box_2.maxY);
minZ = Max(box_1.minZ, box_2.minZ);
maxZ = Min(box_1.maxZ, box_2.maxZ);
return *this;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Vector3D*
ExtentBox::Constrain(Vector3D *point) const
{
Check(this);
Check(point);
Clamp(point->x,minX,maxX);
Clamp(point->y,minY,maxY);
Clamp(point->z,minZ,maxZ);
return point;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExtentBox::Contains(const Vector3D &point) const
{
return
minX <= point.x && maxX >= point.x
&& minY <= point.y && maxY >= point.y
&& minZ <= point.z && maxZ >= point.z;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExtentBox::Contains(const ExtentBox &box) const
{
return
minX <= box.minX && maxX >= box.maxX
&& minY <= box.minY && maxY >= box.maxY
&& minZ <= box.minZ && maxZ >= box.maxZ;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExtentBox::Intersects(const ExtentBox &box) const
{
return
minX <= box.maxX && maxX >= box.minX
&& minY <= box.maxY && maxY >= box.minY
&& minZ <= box.maxZ && maxZ >= box.minZ;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
ExtentBox::TestInstance() const
{
return minX<=maxX && minY<=maxY && minZ<=maxZ;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
std::ostream& operator<<(std::ostream& stream, const ExtentBox& e)
{
return stream << '[' << e.minX << ".." << e.maxX << "],[" << e.minY << ".." << e.maxY << "],[" << e.minZ << ".." << e.maxZ << ']';
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~ ExtentBox functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Convert_From_Ascii(
const char *str,
ExtentBox *extent_box
)
{
Check_Pointer(str);
Check_Signature(extent_box);
CString parse_string(str);
Check(&parse_string);
Check_Pointer(parse_string.GetNthToken(0));
extent_box->minX = atof(parse_string.GetNthToken(0));
Check_Pointer(parse_string.GetNthToken(1));
extent_box->minY = atof(parse_string.GetNthToken(1));
Check_Pointer(parse_string.GetNthToken(2));
extent_box->minZ = atof(parse_string.GetNthToken(2));
Check_Pointer(parse_string.GetNthToken(3));
extent_box->maxX = atof(parse_string.GetNthToken(3));
Check_Pointer(parse_string.GetNthToken(4));
extent_box->maxY = atof(parse_string.GetNthToken(4));
Check_Pointer(parse_string.GetNthToken(5));
extent_box->maxZ = atof(parse_string.GetNthToken(5));
Check(extent_box);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ExtentBox *
MakeExistanceBoxStream(
NotationFile *notation_file,
int *size
)
{
char page_name[50];
if (!notation_file->GetEntry("COLLISION", "extentcount", size))
{
std::cerr << "No 'COLLISION' entry!!!!!" << std::endl << std::flush;
PostQuitMessage(AbortExitCodeID);
}
ExtentBox *extents = new ExtentBox[*size];
for (int j=0; j < *size; ++j)
{
sprintf(page_name, "extent %d", j);
int errorvalue = 0;
errorvalue |= !notation_file->GetEntry(page_name, "extminX", &extents[j].minX);
errorvalue |= !notation_file->GetEntry(page_name, "extmaxX", &extents[j].maxX);
errorvalue |= !notation_file->GetEntry(page_name, "extminY", &extents[j].minY);
errorvalue |= !notation_file->GetEntry(page_name, "extmaxY", &extents[j].maxY);
errorvalue |= !notation_file->GetEntry(page_name, "extminZ", &extents[j].minZ);
errorvalue |= !notation_file->GetEntry(page_name, "extmaxZ", &extents[j].maxZ);
if ( errorvalue )
{
Tell("Extent number : " << j << " is does not have valid slice extents " << endl << std::flush);
PostQuitMessage(AbortExitCodeID);
}
}
return extents;
}