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

312 lines
6.9 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "boxlist.h"
//#############################################################################
//######################### BoundingBoxListNode #########################
//#############################################################################
MemoryBlock *BoundingBoxListNode::GetAllocatedMemory()
{
static MemoryBlock allocatedMemory(sizeof(BoundingBoxListNode), 500, 50, "BoundingBoxList Nodes");
return &allocatedMemory;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoundingBoxListNode::TestInstance() const
{
return True;
}
//#############################################################################
//########################### BoundingBoxList ###########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoundingBoxList::BoundingBoxList()
{
root = NULL;
nodeCount = 0;
scoreBoard = NULL;
boundingBoxIndex = NULL;
isXMajorAxis = True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoundingBoxList::~BoundingBoxList()
{
if (scoreBoard)
{
Unregister_Pointer(scoreBoard);
delete scoreBoard;
}
if (boundingBoxIndex)
{
Unregister_Pointer(boundingBoxIndex);
delete boundingBoxIndex;
}
EraseList();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::Add(
BoundingBox* volume,
const ExtentBox &slice
)
{
Check(this);
Check(volume);
++nodeCount;
BoundingBoxListNode *node = new BoundingBoxListNode(volume, slice);
Register_Object(node);
node->previousNode = root;
root = node;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::Remove(BoundingBox* volume)
{
Check(this);
Check(volume);
//
//--------------------------------------------------------------------------
// Spin through all the boxes until we find the one we are after or until we
// run off the list
//--------------------------------------------------------------------------
//
BoundingBoxListNode
*p = NULL,
*c = root;
while (c)
{
Check(c);
if (c->boundingBox == volume)
{
break;
}
p = c;
c = c->previousNode;
}
//
//----------------------------------------------
// Unlink the node from the list if it was found
//----------------------------------------------
//
if (!c)
{
return;
}
Check(c);
if (p)
{
Check(p);
p->previousNode = c->previousNode;
}
else
{
root = c->previousNode;
}
//
//-----------------
// Delete this node
//-----------------
//
--nodeCount;
Unregister_Object(c);
delete c;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::EraseList()
{
Check(this);
//
//-----------------------------
// Delete each node in the list
//-----------------------------
//
BoundingBoxListNode *p = root;
while (p)
{
Check(p);
BoundingBoxListNode *q = p;
p = p->previousNode;
Unregister_Object(q);
delete q;
}
root = NULL;
nodeCount = 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoundingBox*
BoundingBoxList::FindBoundingBoxContaining(const Point3D &point)
{
Check(this);
Check(&point);
//
//--------------------------------------------------------------------------
// Spin through each of the boxes in reverse order until we find a box which
// contains the specified point. It will be default have priority over any
// boxes put in before it
//--------------------------------------------------------------------------
//
for (BoundingBoxListNode *p=root; p; p = p->previousNode)
{
Check(p);
BoundingBox *box = p->boundingBox;
Check(box);
if (box->Contains(point))
{
return box;
}
}
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BoundingBoxList::FindBoundingBoxesContaining(
BoundingBox *volume,
BoundingBoxCollisionList &list
)
{
Check(this);
Check(volume);
Check(&list);
//
//-------------------------------------------------------------
// Spin through each of the boxes in reverse order, looking for
// intesections between the test volume and the box list
//-------------------------------------------------------------
//
for (BoundingBoxListNode *p=root; p; p = p->previousNode)
{
Check(p);
BoundingBox *box = p->boundingBox;
Check(box);
if (box->ExtentBox::Intersects(*volume))
{
//
//----------------------------------------------------------------
// Build the intersection slice, and make sure that it really does
// intersect the list box
//----------------------------------------------------------------
//
ExtentBox slice;
slice.Intersect(*volume, *box);
if (box->IntersectsBounded(slice))
{
//
//------------------------------------------------------------
// It intersects, so add this to the list. If we are out of
// collisions in the list, we are finished detecting, and just
// return
//------------------------------------------------------------
//
Verify(list.collisionsLeft);
list.AddCollisionToList(box, slice);
if (!list.collisionsLeft)
{
return;
}
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoundingBox*
BoundingBoxList::FindBoundingBoxUnder(
const Point3D &point,
Scalar *height
)
{
Check(this);
Check(&point);
Check_Pointer(height);
//
//-----------------------------------------------------------------------
// Spin through each of the bounding boxes, keeping track of which box is
// hit after the shortest distance
//-----------------------------------------------------------------------
//
BoundingBox *best_box = NULL;
*height = -1.0f;
for (BoundingBoxListNode *p=root; p; p = p->previousNode)
{
Check(p);
BoundingBox *box = p->boundingBox;
Check(box);
Scalar h = box->FindDistanceBelow(point);
if (h != -1.0f)
{
if (!best_box || *height > h)
{
best_box = box;
*height = h;
}
}
}
return best_box;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoundingBox*
BoundingBoxList::FindBoundingBoxHitBy(Line *line)
{
//
//-----------------------------------------------------------------------
// Spin through each of the boxes, letting each box in turn clip the line
// lengths
//-----------------------------------------------------------------------
//
BoundingBox *best_box = NULL;
for (BoundingBoxListNode *p=root; p; p = p->previousNode)
{
Check(p);
BoundingBox *box = p->boundingBox;
Check(box);
Logical h = box->HitBy(line);
if (h)
{
best_box = box;
}
}
return best_box;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoundingBoxList::TestInstance() const
{
return True;
}