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>
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
#include "mungal4.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#define PRELOAD_ART
|
||||
|
||||
#include "..\munga\vdata.h"
|
||||
#include "l4lamp.h"
|
||||
#include "l4wrhous.h"
|
||||
|
||||
// #define LOCAL_TEST
|
||||
|
||||
#if defined(LOCAL_TEST)
|
||||
# define Test_Tell(n) cout << n
|
||||
#else
|
||||
# define Test_Tell(n)
|
||||
#endif
|
||||
|
||||
//#########################################################################
|
||||
//############################# L4LampManager #############################
|
||||
//#########################################################################
|
||||
L4LampManager::L4LampManager(LBE4ControlsManager *controls_manager):
|
||||
LampManager()
|
||||
{
|
||||
Check(controls_manager);
|
||||
controlsManager = controls_manager;
|
||||
|
||||
previousLampState = new int[LBE4ControlsManager::LampCount];
|
||||
Register_Pointer(previousLampState);
|
||||
lampActiveFlag = new Logical[LBE4ControlsManager::LampCount];
|
||||
Register_Pointer(lampActiveFlag);
|
||||
//-----------------------------------------
|
||||
// Clear L4 lamp state tables
|
||||
//-----------------------------------------
|
||||
int
|
||||
i;
|
||||
|
||||
Check_Pointer(previousLampState);
|
||||
for(i=0; i< LBE4ControlsManager::LampCount; ++i)
|
||||
{
|
||||
previousLampState[i] = RIO::solid + RIO::state1Off + RIO::state2Off;
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
L4LampManager::~L4LampManager()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Unregister_Pointer(previousLampState);
|
||||
delete[] previousLampState;
|
||||
previousLampState = NULL;
|
||||
|
||||
Unregister_Pointer(lampActiveFlag);
|
||||
delete[] lampActiveFlag;
|
||||
lampActiveFlag = NULL;
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Logical
|
||||
L4LampManager::TestInstance() const
|
||||
{
|
||||
Check_Fpu();
|
||||
return LampManager::TestInstance();
|
||||
}
|
||||
|
||||
void
|
||||
L4LampManager::Update(ModeMask current_mode_mask)
|
||||
{
|
||||
Check(this);
|
||||
Logical
|
||||
flush_inactive_lamps = (current_mode_mask != previousModeMask);
|
||||
int
|
||||
i;
|
||||
|
||||
if (flush_inactive_lamps)
|
||||
{
|
||||
//---------------------------------------
|
||||
// Mark all lamps as inactive
|
||||
//---------------------------------------
|
||||
Check_Pointer(lampActiveFlag);
|
||||
for(i=0; i< LBE4ControlsManager::LampCount; ++i)
|
||||
{
|
||||
lampActiveFlag[i] = False;
|
||||
}
|
||||
}
|
||||
//---------------------------------------
|
||||
// Update LampManager, set lamp outputs,
|
||||
// and mark active lamps
|
||||
//---------------------------------------
|
||||
LampManager::Update(current_mode_mask);
|
||||
|
||||
if (flush_inactive_lamps)
|
||||
{
|
||||
//---------------------------------------
|
||||
// Turn off inactive lamps
|
||||
//---------------------------------------
|
||||
Check_Pointer(lampActiveFlag);
|
||||
for(i=0; i< LBE4ControlsManager::LampCount; ++i)
|
||||
{
|
||||
if (lampActiveFlag[i] == False)
|
||||
{
|
||||
AssertNewLampValue(i, RIO::solid+RIO::state1Off+RIO::state2Off);
|
||||
}
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
L4LampManager::AssertNewLampValue(int lamp_number, int lamp_value)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Verify(lamp_number >= 0);
|
||||
Verify(lamp_number < LBE4ControlsManager::LampCount);
|
||||
//--------------------------------------
|
||||
// Update external hardware
|
||||
//--------------------------------------
|
||||
Check_Pointer(previousLampState);
|
||||
if (previousLampState[lamp_number] != lamp_value)
|
||||
{
|
||||
previousLampState[lamp_number] = lamp_value;
|
||||
//--------------------------------------
|
||||
// Send RIO command to update L4 lamp
|
||||
//--------------------------------------
|
||||
Check(controlsManager);
|
||||
controlsManager->SetLamp(lamp_number, lamp_value);
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//#########################################################################
|
||||
//################################ L4Lamp #################################
|
||||
//#########################################################################
|
||||
L4Lamp::L4Lamp(
|
||||
LampID lamp_number,
|
||||
ModeMask mode_mask,
|
||||
L4LampManager *lamp_manager
|
||||
) :
|
||||
Lamp(lamp_number, mode_mask, (LampManager *) lamp_manager)
|
||||
{
|
||||
automatic = True;
|
||||
automaticValue = (ControlsButton) 0;
|
||||
previousAutomaticValue = automaticValue;
|
||||
|
||||
SetState(LampStateDim);
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
L4Lamp::~L4Lamp()
|
||||
{
|
||||
Check(this);
|
||||
SetState(LampStateOff);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Logical
|
||||
L4Lamp::TestInstance() const
|
||||
{
|
||||
return Lamp::TestInstance();
|
||||
}
|
||||
|
||||
void
|
||||
L4Lamp::SetAutomaticOperation(Logical automatic_flag)
|
||||
{
|
||||
Check(this);
|
||||
automatic = automatic_flag;
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
L4Lamp::Update(Logical force_notification)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
//--------------------------------------
|
||||
// Tell manager we're still active
|
||||
//--------------------------------------
|
||||
if (lampID >= 0)
|
||||
{
|
||||
if (lampID < LBE4ControlsManager::LampCount)
|
||||
{
|
||||
Check_Pointer(((L4LampManager *)manager)->lampActiveFlag);
|
||||
((L4LampManager *)manager)->lampActiveFlag[lampID] = True;
|
||||
}
|
||||
}
|
||||
//--------------------------------------
|
||||
// Update state if automatic
|
||||
//--------------------------------------
|
||||
if (automatic)
|
||||
{
|
||||
if (previousAutomaticValue != automaticValue)
|
||||
{
|
||||
previousAutomaticValue = automaticValue;
|
||||
|
||||
if (previousAutomaticValue > 0)
|
||||
{
|
||||
SetState(LampStateOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetState(LampStateDim);
|
||||
}
|
||||
}
|
||||
else if (force_notification)
|
||||
{
|
||||
NotifyOfStateChange();
|
||||
}
|
||||
}
|
||||
else if (force_notification)
|
||||
{
|
||||
NotifyOfStateChange();
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
L4Lamp::NotifyOfStateChange()
|
||||
{
|
||||
Check(this);
|
||||
Check((L4LampManager *) manager);
|
||||
|
||||
int
|
||||
output_value;
|
||||
|
||||
if (alertActive)
|
||||
{
|
||||
switch(previousState)
|
||||
{
|
||||
default:
|
||||
output_value = RIO::flashFast + RIO::state1Off + RIO::state2Dim;
|
||||
break;
|
||||
|
||||
case LampStateDim:
|
||||
case LampStateOn:
|
||||
output_value = RIO::flashFast + RIO::state1Dim + RIO::state2Bright;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(previousState)
|
||||
{
|
||||
default:
|
||||
output_value = RIO::solid + RIO::state1Off + RIO::state2Off;
|
||||
break;
|
||||
|
||||
case LampStateDim:
|
||||
output_value = RIO::solid + RIO::state1Dim + RIO::state2Dim;
|
||||
break;
|
||||
|
||||
case LampStateOn:
|
||||
output_value = RIO::solid + RIO::state1Bright + RIO::state2Bright;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
((L4LampManager *) manager)->AssertNewLampValue(lampID, output_value);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//#########################################################################
|
||||
//############################# L4VirtualLamp #############################
|
||||
//#########################################################################
|
||||
L4GraphicLamp::L4GraphicLamp(
|
||||
LampID lamp_id,
|
||||
ModeMask mode_mask,
|
||||
GaugeRenderer *the_renderer,
|
||||
int graphics_port_number,
|
||||
int left, int bottom,
|
||||
const char *map_name,
|
||||
int bg_color, int fg_color,
|
||||
Logical opaque_flag
|
||||
) :
|
||||
GraphicLamp(
|
||||
lamp_id,
|
||||
mode_mask,
|
||||
the_renderer->GetLampManager(),
|
||||
the_renderer,
|
||||
graphics_port_number
|
||||
)
|
||||
{
|
||||
//--------------------------------------------
|
||||
// Build copy of map name
|
||||
//--------------------------------------------
|
||||
Check_Pointer(map_name);
|
||||
mapName = new char[strlen(map_name)+1];
|
||||
Register_Pointer(mapName);
|
||||
strcpy(mapName, map_name);
|
||||
//--------------------------------------------
|
||||
// Get the image
|
||||
//--------------------------------------------
|
||||
# if defined(PRELOAD_ART)
|
||||
Check(renderer);
|
||||
L4Warehouse
|
||||
*warehouse = (L4Warehouse *) renderer->warehousePointer;
|
||||
Check(warehouse);
|
||||
|
||||
warehouse->bitMapBin.Get(mapName);
|
||||
# endif
|
||||
|
||||
opaque = opaque_flag;
|
||||
bgColor = bg_color;
|
||||
fgColor = fg_color;
|
||||
localView.SetOrigin(left, bottom);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
L4GraphicLamp::~L4GraphicLamp()
|
||||
{
|
||||
Check(this);
|
||||
Check_Pointer(mapName);
|
||||
//--------------------------------------------
|
||||
// Release the preloaded image
|
||||
//--------------------------------------------
|
||||
# if defined(PRELOAD_ART)
|
||||
Check(renderer);
|
||||
L4Warehouse
|
||||
*warehouse = (L4Warehouse *) renderer->warehousePointer;
|
||||
Check(warehouse);
|
||||
|
||||
warehouse->bitMapBin.Release(mapName);
|
||||
# endif
|
||||
//--------------------------------------------
|
||||
// Delete the copy of the name
|
||||
//--------------------------------------------
|
||||
Check_Pointer(mapName);
|
||||
delete[] mapName;
|
||||
mapName = NULL;
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Logical
|
||||
L4GraphicLamp::TestInstance() const
|
||||
{
|
||||
return GraphicLamp::TestInstance();
|
||||
}
|
||||
|
||||
void
|
||||
L4GraphicLamp::NotifyOfStateChange()
|
||||
{
|
||||
Check(this);
|
||||
//--------------------------------------------
|
||||
// Get the bitmap
|
||||
//--------------------------------------------
|
||||
Check(renderer);
|
||||
L4Warehouse
|
||||
*warehouse = (L4Warehouse *) renderer->warehousePointer;
|
||||
Check(warehouse);
|
||||
|
||||
BitMap
|
||||
*bitmap = warehouse->bitMapBin.Get(mapName);
|
||||
|
||||
if (bitmap != NULL)
|
||||
{
|
||||
Check(bitmap);
|
||||
//--------------------------------------------
|
||||
// Set the color
|
||||
//--------------------------------------------
|
||||
if (previousState <= LampStateOff)
|
||||
{
|
||||
localView.SetColor(bgColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
localView.SetColor(fgColor);
|
||||
}
|
||||
//--------------------------------------------
|
||||
// Draw the bitmap
|
||||
//--------------------------------------------
|
||||
if (opaque)
|
||||
{
|
||||
localView.DrawBitMapOpaque(bgColor, 0, bitmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
localView.DrawBitMap(0, bitmap);
|
||||
}
|
||||
//--------------------------------------------
|
||||
// Release the bitmap
|
||||
//--------------------------------------------
|
||||
Check(warehouse);
|
||||
warehouse->bitMapBin.Release(mapName);
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
Reference in New Issue
Block a user