Files
BT411/engine/MUNGA_L4/L4PLASMA.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

364 lines
8.3 KiB
C++

#include "mungal4.h"
#pragma hdrstop
#include "l4plasma.h"
#include "..\munga\time.h"
//########################################################################
//############################# PlasmaDisplay ############################
//########################################################################
PlasmaDisplay::PlasmaDisplay(
//Win32 Serial Support: ADB 02/24/2007
//Word port,
const char* port
//Word int_num
):
Video8BitBuffered(plasmaWidth, plasmaHeight),
forceAll(False)
{
//------------------------------------
// Create serial interface
//------------------------------------
//Win32 Serial Support: ADB 02/24/2007
//pc_serial = new PCSerial(PCS_9600, PCS_N81, port, int_num);
pc_serial = new PCSerial(PCS_9600, PCS_N81, port);
if (pc_serial != NULL)
{
Register_Object(pc_serial);
pc_serial->FlowControlEnable(False);
//------------------------------------
// Turn off the cursor
//------------------------------------
static char
cursorOffString[] = { 27 /*ESC*/, 'G', (char) 0x00 };
pc_serial->SendString(cursorOffString, 3);
}
//------------------------------------
// Clear update flag array
//------------------------------------
currentTransferLine = 0;
updateInProgress = False;
for(int i=0; i<plasmaHeight; ++i)
{
updateFlag[i] = 0;
}
this->DrawFilledRectangle(0, 0xFF, GraphicsDisplay::Replace, 0, 0, this->width - 1, this->height - 1);
this->Update(True);
Check_Fpu();
}
PlasmaDisplay::~PlasmaDisplay()
{
if (pc_serial != NULL)
{
//---------------------------------------------------------------------
// Flush data to plasma display
//---------------------------------------------------------------------
this->DrawFilledRectangle(0, 0xFF, GraphicsDisplay::Replace, 0, 0, this->width - 1, this->height - 1);
this->Update(True);
WaitForUpdate();
// This odd sequence of allocation, setting, and adding is necessary:
// if you try to combine them, the compiler gets confused as to which
// method to invoke for 'Now()'.
Time
then;
then = Now();
then += 5.0f;
while (Now() < then)
{
//---------------------------------------------------
// Drop out of loop when ALL data has been sent
//---------------------------------------------------
if (pc_serial->CanDraw() <= 0)
{
if (!pc_serial->IsActive())
{
break;
}
}
}
//---------------------------------------------------------------------
// Delete the serial interface
//---------------------------------------------------------------------
Unregister_Object(pc_serial);
delete pc_serial;
}
Check_Fpu();
}
Logical
PlasmaDisplay::TestInstance() const
{
return Video8BitBuffered::TestInstance();
}
void
PlasmaDisplay::ShowInstance(char *indent)
{
std::cout << indent << "Plasma:\n";
char
temp[80];
Str_Copy(temp,indent, 80);
Str_Cat(temp,"...", 80);
Video8BitBuffered::ShowInstance(temp);
Check_Fpu();
}
int
sendLine(
char *transfer_pointer,
int line_number,
Byte *source_pointer,
int source_width
)
{
int
x,
index;
Byte
byte,
byte_bit;
//--------------------------------------------
// Send header
//--------------------------------------------
transfer_pointer[0] = (char) 27; // ascii ESC
transfer_pointer[1] = 'P'; // graphics command
transfer_pointer[2] = (char) 0; // screen number
transfer_pointer[3] = (char) line_number; // Y position
transfer_pointer[4] = (char) 0; // X BYTE position
transfer_pointer[5] = (char) ((source_width+7)>>3); // width in bytes
transfer_pointer[6] = (char) 1;
transfer_pointer += 7;
//pc_serial->SendString(graphics, 7);
//--------------------------------------------
// Send data
//--------------------------------------------
byte = 0;
byte_bit = 0x80;
index = 0;
for(x=0; x<source_width; ++x)
{
//
// Add bit to byte
//
if (*source_pointer++)
{
byte |= byte_bit;
}
//
// Update bit position
//
byte_bit >>= 1;
//
// If entire byte collected, send it
//
if (byte_bit == 0)
{
Verify(index < sizeof(graphics));
*transfer_pointer = byte;
transfer_pointer++;
index++;
byte = 0;
byte_bit = 0x80;
}
}
//
// End of source line: send remaining bits
//
if (byte_bit != 0x80)
{
transfer_pointer[index++] = byte;
}
//
// Send graphics data
//
//pc_serial->SendString(graphics, index);
Check_Fpu();
return index + 7;
}
void
PlasmaDisplay::Scan(Logical forceAll)
{
Word
changed_line = (Word) forceAll,
*changed_line_pointer = changedLine;
char
*update_pointer=updateFlag;
int
y;
for(y=0; y<plasmaHeight; ++y,++update_pointer)
{
//---------------------------------------------------------
// Scan for changed lines
//---------------------------------------------------------
changed_line += *changed_line_pointer;
*changed_line_pointer++ = 0;
if (changed_line)
{
*update_pointer = 1;
updateInProgress = True;
}
}
Check_Fpu();
}
Logical
PlasmaDisplay::Update(Logical force_all)
{
Time startSendLine,endSendLine,startWhile,endWhile,dataSendStart,dataSendEnd;
Time start = Now();
int __totalTransferLength;
Time allStarts[100], allEnds[100];
int
transfer_count,
total_count;
Logical
result = True; // True == 'more to send, not done'
if (pc_serial != NULL && pc_serial->CanDraw())
{
//-----------------------------------------
// Accumulate changed lines
//-----------------------------------------
forceAll |= force_all;
if (currentTransferLine == 0)
{
Scan(forceAll);
forceAll = False;
}
//-----------------------------------------
// Send only if the buffer is empty
//-----------------------------------------
total_count = plasmaHeight;
transfer_count = 7;
int transfer_length = 32 * transfer_count;
char *transfer_data = new char[transfer_length];
char *transfer_pointer = transfer_data;
int total_transfer_count = transfer_count;
int total_transfer_length = 0;
startWhile = Now();
int i = 0;
while(transfer_count>0)
{
//-----------------------------------------
// Transfer a line if it's ready
//-----------------------------------------
allStarts[i] = Now();
if (updateFlag[currentTransferLine])
{
//-----------------------------------------
// Decrement the 'transfer count'
//-----------------------------------------
--transfer_count;
//-----------------------------------------
// Clear the 'update' flag
//-----------------------------------------
updateFlag[currentTransferLine] = 0;
//-----------------------------------------
// Send the line data to the display
//-----------------------------------------
startSendLine = Now();
int length = sendLine(transfer_pointer, currentTransferLine,
pixelBuffer->Data.MapPointer +
(pixelBuffer->Data.Size.x * currentTransferLine),
pixelBuffer->Data.Size.x
);
endSendLine = Now();
transfer_pointer += length;
total_transfer_length += length;
}
allEnds[i] = Now();
//-----------------------------------------
// Increment currentTransferLine
//-----------------------------------------
if (++currentTransferLine >= plasmaHeight)
{
currentTransferLine = 0;
}
//-----------------------------------------
// Reset to top if all lines scanned
//-----------------------------------------
if (--total_count < 0)
{
currentTransferLine = 0; // reset to top line
updateInProgress = False;
result = False; // False == 'all data sent'
break;
}
i++;
}
endWhile = Now();
dataSendStart = Now();
if (total_transfer_length > 0)
{
this->pc_serial->SendString(transfer_data, total_transfer_length);
}
dataSendEnd = Now();
__totalTransferLength = total_transfer_length;
delete [] transfer_data;
}
Check_Fpu();
Time end = Now();
if (end.ticks - start.ticks > 100)
{
start = end;
}
return result;
}
void
PlasmaDisplay::WaitForUpdate()
{
Time
end_time;
end_time = Now();
end_time += 5.0f; // We'll wait this many seconds to update the display-
while (updateInProgress)
{
Update(False);
if (Now() >= end_time)
{
break;
}
}
Check_Fpu();
}