Files
BT411/engine/MUNGA_L4/L4GREND.cpp
T
Joe DiPrimaandClaude Opus 5 654277bb7b pod: freeze the ALPHA-MR rig in environ.ini (BT_FIT, L4PLASMA=NONE)
The cart's display config was carried by a launcher .bat, so it only came up
right if the game was started one particular way.  Move it into the file the
engine already reads before anything touches the environment.

  content/environ.ini  <- scratchpad/pod/podprofile.ini, merged idempotently
                          between markers by scratchpad/pod/mergeprofile.ps1

Two gates were missing for that to be enough:

  BT_FIT=1      the env spelling of -fit, so the borderless main view does not
                depend on one launcher's command line (shortcut, scheduled
                task and autostart all have to produce the same rig)
  L4PLASMA=     NONE / OFF / 0 -> no marquee at all.  Leaving it unset does
                NOT work: the GLASS profile force-defaults it to SCREEN, which
                drops a desktop plasma window on the cab's glass.  The boot
                banner now reports the live state instead of always claiming
                "plasma window".

Also lands the bring-up engine work this depended on: monitor:<name|index>
layout binding (device-bound, not pixel-bound -- desktop rects move when a
display re-enumerates), ",bare" implying frameless, rotation-aware radar
surface sizing, BT_GAUGE_SEC_ROT accepting 0-3 (it silently forced 3 for
anything but 1), 180-degree ExpandPlaneToBGRA, and the BT_POD_CHANMAP /
BT_POD_IDENT / BT_POD_CHANTEST identification gates.

Verified on the cart, build 4.11.813, launcher carrying none of it:
  [boot] environ.ini: 9 setting(s) applied
  [boot] platform profile: GLASS (PadRIO; plasma off [L4PLASMA])
  [cockpit] -fit: borderless 800x600
  [glasswin] radar rotation 0 (none)
  ... all three surfaces on their intended \.\DISPLAYn
No [plasmawin] line in an otherwise-logging run = the ctor never ran.

KB: pod-hardware.md gains the ALPHA-MR section (mapping, the two wiring
deviations, the frozen profile, the session-0 remote-work traps) and its RGB
SPLIT "OPEN: which way is the cart wired" is now SETTLED -- it is splitter
wired, the composite is what lit it.  glass-cockpit.md documents monitor:
binding, BT_FIT and L4PLASMA=NONE.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rw7No5wLTpkaUgA3ANbtZZ
2026-08-06 17:40:39 -05:00

878 lines
22 KiB
C++

#include "mungal4.h"
#pragma hdrstop
#include "l4app.h"
#include "..\munga\time.h"
#include "l4grend.h"
#include "l4gauge.h"
#include "..\munga\mission.h"
#include "l4plasma.h"
#include "..\munga\notation.h"
#ifdef BT_GLASS
#include "l4plasmawin.h"
#endif
// #define LOCAL_TEST
#if defined(LOCAL_TEST)
# define Test_Tell(n) DEBUG_STREAM << n << std::flush
#else
# define Test_Tell(n)
#endif
//
//#############################################################################
// L4MethodDescription list
//#############################################################################
//
MethodDescription
L4ChainToPrevious = METHOD_DESCRIPTION_CHAIN(NULL); // Null:end of chain
MethodDescription
*L4MethodDescription[] =
{
&MakeConfigMethodDescription,
&MakeExternConfigMethodDescription,
&BackgroundReconfig::methodDescription,
&BackgroundLine::methodDescription,
&BackgroundRect::methodDescription,
&BackgroundFilledRect::methodDescription,
&BackgroundPixelmap::methodDescription,
&BackgroundBitmap::methodDescription,
&NumericDisplayScalar::methodDescription,
&NumericDisplaySpeed::methodDescription,
&NumericDisplayInteger::methodDescription,
&DigitalClock::methodDescription,
&RankAndScore::methodDescription,
&BarGraphSolidScalar::methodDescription,
&BarGraphPixelMapScalar::methodDescription,
&ColorState::methodDescription,
&TwoState::methodDescription,
&SegmentArcNormalized::methodDescription,
&L4ChainToPrevious
};
//
//#############################################################################
// L4GaugeRenderer
//#############################################################################
//
L4GaugeRenderer::L4GaugeRenderer(bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index):
GaugeRenderer()
{
//
//---------------------------------------------------------------------
// Ensure that graphicsDisplay is NULL
//---------------------------------------------------------------------
//
graphicsDisplay = NULL;
externalDisplay = NULL;
palettesAreActive = True;
scrambleVideoFlag = False;
char
*mode_string = getenv("L4GAUGE");
//
//---------------------------------------------------------------------
// NOTE that L4Application::Initialize() searches for the environment
// variable "L4GAUGE" to determine whether or not to create
// a gauge renderer.
//---------------------------------------------------------------------
//
if (mode_string != NULL)
{
//
//---------------------------------------------------------------------
// Attempt to initialize external display
//---------------------------------------------------------------------
//
char
*plasma_string = getenv("L4PLASMA");
//
// L4PLASMA=NONE / OFF / 0 -- no plasma marquee at all (2026-08-06).
// A real cab drives the 128x32 marquee over serial; a rebuilt cart may
// not have one wired, and the GLASS profile force-defaults L4PLASMA to
// SCREEN, which puts a desktop plasma window on the pod's glass where
// nothing should be. Leaving the variable UNSET is not an option
// there (the profile fills it in), so give "off" an explicit spelling.
//
if (plasma_string != NULL
&& (_stricmp(plasma_string, "NONE") == 0
|| _stricmp(plasma_string, "OFF") == 0
|| strcmp(plasma_string, "0") == 0))
{
Tell("Plasma display disabled (L4PLASMA=none)\n");
plasma_string = NULL;
}
if (plasma_string != NULL)
{
//Win32 Serial support: ADB 06/30/07
//if (strnicmp(plasma_string, "COM2", 4) == 0)
//{
// Tell("Plasma display created on COM2\n");
// externalDisplay = new PlasmaDisplay(PCS_COM2);
//}
//else
//{
// Tell("Plasma display created on COM1\n");
// externalDisplay = new PlasmaDisplay(PCS_COM1);
//}
#ifdef BT_GLASS
//
// Glass cockpit (step 2d): L4PLASMA=SCREEN renders the 128x32
// plasma into a desktop window instead of a serial device.
// Any other value keeps the authentic serial path below.
//
if (_stricmp(plasma_string, "SCREEN") == 0)
{
Tell("Plasma display created on desktop window\n");
externalDisplay = new PlasmaWindow();
}
else
#endif
{
Tell("Plasma display created on "); Tell(plasma_string); Tell("\n");
externalDisplay = new PlasmaDisplay(plasma_string);
}
if (externalDisplay != NULL)
{
Register_Object(externalDisplay);
}
}
//
//---------------------------------------------------------------------
// Attempt to initialize main display
//---------------------------------------------------------------------
//-------------------------------------------------
// Search notation file for specified resolution
//-------------------------------------------------
//
NotationFile
notation_file("GAUGE\\L4GAUGE.INI");
if (!notation_file.PageExists(mode_string))
{
Tell("L4GaugeRenderer::L4GaugeRenderer - '" <<
mode_string <<
"' not found in L4GAUGE.INI!\n"
);
}
else
{
//
//-------------------------------------------------
// Requested resolution exists, read parameters
//-------------------------------------------------
//
int
mode(0),
width(640),
height(480),
page_size(64),
gran(64),
bpl(640*2),
pfp(0),
special(0),
q(0);
q += notation_file.GetEntry(mode_string, "mode", &mode);
q += notation_file.GetEntry(mode_string, "width", &width);
q += notation_file.GetEntry(mode_string, "height", &height);
q += notation_file.GetEntry(mode_string, "sizeInKB", &page_size);
q += notation_file.GetEntry(mode_string, "granularityInKB", &gran);
q += notation_file.GetEntry(mode_string, "bytesPerLine", &bpl);
q += notation_file.GetEntry(mode_string, "pageFcnPtr", &pfp);
q += notation_file.GetEntry(mode_string, "special", &special);
if (q == 8)
{
graphicsDisplay = new
SVGA16(
mode,
width,
height,
page_size,
gran,
bpl,
pfp,
special,
windowed,
secondaryIndex,
aux1Index,
aux2Index
);
if (graphicsDisplay != NULL)
{
Register_Object(graphicsDisplay);
}
}
}
}
Check_Fpu();
}
//
//#############################################################################
// ~L4GaugeRenderer
//#############################################################################
//
L4GaugeRenderer::~L4GaugeRenderer()
{
# if defined (LOCAL_TEST)
Tell("L4GaugeRenderer::~L4GaugeRenderer()\n");
# endif
Check(this);
int i;
//----------------------------------------------------
// Make sure all gauges removed first
//----------------------------------------------------
Remove(0);
//----------------------------------------------------
// Delete the graphics ports
//----------------------------------------------------
for(i=0; i<maximumGraphicsPorts; ++i)
{
if (graphicsPort[i] != NULL)
{
Check(graphicsPort[i]);
Unregister_Object(graphicsPort[i]);
delete graphicsPort[i];
}
}
if (graphicsDisplay != NULL)
{
Check(graphicsDisplay);
Unregister_Object(graphicsDisplay);
delete graphicsDisplay;
}
if (externalDisplay != NULL)
{
Check(externalDisplay);
Unregister_Object(externalDisplay);
delete externalDisplay;
}
Check_Fpu();
}
//
//#############################################################################
// LocalEmergencyShutdown - restore the ABSOLUTE MINIMUM to get back
// to the system!
//#############################################################################
//
void
L4GaugeRenderer::LocalEmergencyShutdown()
{
if (graphicsDisplay != NULL)
{
// It's an EMERGENCY: a 'verify' somewhere may have already failed.
// Therefore don't check it, just kill it!
delete graphicsDisplay;
}
GaugeRenderer::LocalEmergencyShutdown();
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
L4GaugeRenderer::TestInstance() const
{
return GaugeRenderer::TestInstance();
}
//
//#############################################################################
// StartImplementation
//#############################################################################
//
void
L4GaugeRenderer::LoadMissionImplementation(Mission *mission)
{
Check(this);
# if defined (LOCAL_TEST)
Tell("L4GaugeRenderer::LoadMissionImplementation()\n");
# endif
GaugeRenderer::LoadMissionImplementation(mission);
Check_Fpu();
}
//
//#############################################################################
// StopImplementation
//#############################################################################
//
void
L4GaugeRenderer::ShutdownImplementation()
{
Check(this);
# if defined (LOCAL_TEST)
Tell("L4GaugeRenderer::ShutdownImplementation()\n");
# endif
// EVENTUALLY kill all interrupt-driven processes (if they exist)
GaugeRenderer::ShutdownImplementation();
Check_Fpu();
}
//
//#############################################################################
// SuspendImplementation
//#############################################################################
//
void
L4GaugeRenderer::SuspendImplementation()
{
Check(this);
# if defined (LOCAL_TEST)
Tell("L4GaugeRenderer::SuspendImplementation()\n");
# endif
GaugeRenderer::SuspendImplementation();
Check_Fpu();
}
//
//#############################################################################
// ResumeImplementation
//#############################################################################
//
void
L4GaugeRenderer::ResumeImplementation()
{
Check(this);
# if defined (LOCAL_TEST)
Tell("L4GaugeRenderer::ResumeImplementation()\n");
# endif
GaugeRenderer::ResumeImplementation();
Check_Fpu();
}
//
//#############################################################################
// ExecuteForeground
//#############################################################################
//
void
L4GaugeRenderer::ExecuteForeground()
{
Check(this);
//---------------------------------------
// We must call the ancestral method!
//---------------------------------------
Time old = Now();
GaugeRenderer::ExecuteForeground();
//---------------------------------------
// Process special effects
//---------------------------------------
if (graphicsDisplay != NULL)
{
Check(graphicsDisplay);
ProcessVideoEffects();
((SVGA16 *) graphicsDisplay)->UpdatePalette();
}
Time newTime = Now();
Scalar diff = newTime - old;
Check_Fpu();
}
//
//#############################################################################
// ExecuteImplementation
//#############################################################################
//
Logical
L4GaugeRenderer::ExecuteBackgroundDisplayUpdate()
{
Check(this);
Logical
result = False; // False == 'all done'
//--------------------------------------------------------
// Update external annunciator
//
// Allow the plasma display to continue if there's
// more background time available.
//
// This method is still called if taskmode is set to
// 'foreground'. If it's called in 'foreground' mode,
// it really means that we're still waiting for foreground
// to begin processing, so we might as well update the
// external display (if needed).
//--------------------------------------------------------
if (externalDisplay != NULL)
{
Check(externalDisplay);
result = externalDisplay->Update(False); // returns 'False' if done
}
//-------------------------------------------------------
// Update display ONLY in 'copy' mode
//--------------------------------------------------------
if (taskMode == copy)
{
if (graphicsDisplay != NULL)
{
Check(graphicsDisplay);
if (graphicsDisplay->Update(False) == False)
{
//--------------------------------------------------------
// Tell executionImplementation that we're basically done.
//--------------------------------------------------------
taskMode = foreground;
}
else
{
result = True; // We're not done yet!
}
}
} else if (l4_application->GetApplicationState() == Application::WaitingForEgg)
{
((SVGA16*)graphicsDisplay)->Refresh();
}
return result;
}
//
//#############################################################################
// NotifyOfNewInterestingEntity
//#############################################################################
//
void
L4GaugeRenderer::NotifyOfNewInterestingEntity(
Entity *interesting_entity
)
{
SET_GAUGE_RENDERER();
Check(this);
Check(interesting_entity);
//----------------------------------------------
// Make sure gaugeImage exists for entity
//----------------------------------------------
Check(warehousePointer);
// (task #17: the old DEV-COMPOSITE GUARD here -- "the reconstructed
// warehouse chain reads garbage / shadowed warehousePointer" -- described
// a bug fixed by later shadow sweeps; the BT ctor allocates a real
// L4Warehouse before the cfg parse and L4GaugeImage::Make is NULL-safe on
// missing type-0x12 resources. Guard removed; the radar pip cache now
// fills on dev boxes too.)
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
interesting_entity->GetResourceID()
);
//----------------------------------------------
// Update gaugeRenderer
//----------------------------------------------
GaugeRenderer::NotifyOfNewInterestingEntity(interesting_entity);
Check_Fpu();
CLEAR_GAUGE_RENDERER();
}
//
//#############################################################################
// NotifyOfBecomingUninterestingEntity
//#############################################################################
//
void
L4GaugeRenderer::NotifyOfBecomingUninterestingEntity(
Entity *uninteresting_entity
)
{
SET_GAUGE_RENDERER();
Check(this);
Check(uninteresting_entity);
//----------------------------------------------
// Remove gaugeImage if no longer used
//----------------------------------------------
Check(warehousePointer);
((L4Warehouse*)warehousePointer)->gaugeImageBin.Release(
uninteresting_entity->GetResourceID()
);
//----------------------------------------------
// Update gaugeRenderer
//----------------------------------------------
GaugeRenderer::NotifyOfBecomingUninterestingEntity(uninteresting_entity);
Check_Fpu();
CLEAR_GAUGE_RENDERER();
}
//
//#############################################################################
// BuildGraphicsPort
//#############################################################################
//
void
L4GaugeRenderer::BuildGraphicsPort(
int port_number,
const char *name,
int rotation,
int bitAllocation,
SVGA16::PaletteID palette_ID,
L4GraphicsPort::ChannelEnableID enable_ID,
Palette8 *palette
)
{
Test_Tell("L4GaugeRenderer::BuildGraphicsPort\n");
Check(this);
if (graphicsDisplay != NULL)
{
Test_Tell("...non-NULL display\n");
Verify(port_number < maximumGraphicsPorts);
Verify(port_number >= 0);
Test_Tell("...port_number= " << port_number << "\n");
//
//---------------------------------------------------------
// If it already exists, delete it
//---------------------------------------------------------
//
L4GraphicsPort
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
if (graphics_port != NULL)
{
Test_Tell("...deleting existing port\n");
Check(graphics_port);
Unregister_Object(graphics_port);
delete graphics_port;
}
//
//---------------------------------------------------------
// Attempt to create it
//---------------------------------------------------------
//
graphicsPort[port_number] = new L4GraphicsPort(
(Video16BitBuffered *) graphicsDisplay,
name,
rotation,
bitAllocation,
palette_ID,
enable_ID
);
graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
Test_Tell("...new port at " << graphics_port << "\n");
//
//---------------------------------------------------------
// If created, register it and build the palette
//---------------------------------------------------------
//
if (graphics_port != NULL)
{
Check(graphics_port);
Register_Object(graphics_port);
//
// Set the palette for the port
// (Note: secondary uses lowest 8 bits)
//
if ((bitAllocation > 0) && (bitAllocation < 0x0100))
{
Test_Tell("...secondary\n");
graphics_port->SetSecondaryPalette(palette);
}
else
{
Test_Tell("...auxiliary\n");
graphics_port->SetAuxiliaryPalette();
}
}
}
Check_Fpu();
}
//
//#############################################################################
// RemapGraphicsPort
//#############################################################################
//
void
L4GaugeRenderer::RemapGraphicsPort(
int port_number,
SVGA16::PaletteID palette_ID,
L4GraphicsPort::ChannelEnableID enable_ID,
Palette8 *palette
)
{
Check(this);
Test_Tell("L4GaugeRenderer::RemapGraphicsPort\n");
if (graphicsDisplay != NULL)
{
Test_Tell("...non-NULL display\n");
Verify(port_number < maximumGraphicsPorts);
Verify(port_number >= 0);
Test_Tell("...port_number= " << port_number << "\n");
//---------------------------------------------------------
// If it exists, change the palette ID and set the palette
//---------------------------------------------------------
L4GraphicsPort
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
if (graphics_port != NULL)
{
Check(graphics_port);
//
// Set the palette ID
//
graphics_port->SetPaletteID(palette_ID);
graphics_port->SetEnableID(enable_ID);
//
// Set the palette for the port
// (Note: secondary uses lowest 8 bits)
//
int
bit_mask = graphics_port->GetBitMask();
if (bit_mask & 0x00FF) // must be secondary
{
Test_Tell("...secondary\n");
Verify(palette != NULL);
graphics_port->SetSecondaryPalette(palette);
}
else
{
Test_Tell("...auxiliary\n");
graphics_port->SetAuxiliaryPalette();
}
}
}
Check_Fpu();
}
//
//#############################################################################
// BuildExternalGraphicsPort
//#############################################################################
//
void
L4GaugeRenderer::BuildExternalGraphicsPort(
int port_number,
const char *name,
int /*bitAllocation*/
)
{
Check(this);
Test_Tell(
"L4GaugeRenderer::BuildExternalGraphicsPort(" << port_number << ")\n"
);
if (externalDisplay != NULL)
{
Test_Tell("...non-NULL display\n");
Verify(port_number < maximumGraphicsPorts);
Verify(port_number >= 0);
Test_Tell("...port_number= " << port_number << "\n");
//
//---------------------------------------------------------
// If it already exists, delete it
//---------------------------------------------------------
//
L4GraphicsPort
*graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
if (graphics_port != NULL)
{
Test_Tell("...deleting existing port\n");
Check(graphics_port);
Unregister_Object(graphics_port);
delete graphics_port;
}
//
//---------------------------------------------------------
// Attempt to create it
//---------------------------------------------------------
//
graphicsPort[port_number] = new L4BytePort(
(Video8BitBuffered *) externalDisplay,
name,
0 // rotation
);
graphics_port = (L4GraphicsPort *) graphicsPort[port_number];
Test_Tell("...new port at " << graphics_port << "\n");
//
//---------------------------------------------------------
// If created, register it
//---------------------------------------------------------
//
if (graphics_port != NULL)
{
Check(graphics_port);
Register_Object(graphics_port);
}
}
Check_Fpu();
}
//
//#############################################################################
// FadeToWhite, FadeToGame
//#############################################################################
//
void
L4GaugeRenderer::FadeToWhite(Scalar number_of_seconds)
{
Check(this);
Verify(number_of_seconds >= 0.0); // zero is allowed (instant change)
// cout << "L4GaugeRenderer::FadeToWhite(" << number_of_seconds << ")\n";
if (graphicsDisplay != NULL)
{
Check(graphicsDisplay);
((SVGA16 *)graphicsDisplay)->FadeToWhite(number_of_seconds);
}
Check_Fpu();
}
void
L4GaugeRenderer::FadeToNormal(Scalar number_of_seconds)
{
Check(this);
Verify(number_of_seconds >= 0.0); // zero is allowed (instant change)
// cout << "L4GaugeRenderer::FadeToNormal(" << number_of_seconds << ")\n";
if (graphicsDisplay != NULL)
{
Check(graphicsDisplay);
((SVGA16 *)graphicsDisplay)->FadeToPalettes(number_of_seconds);
}
Check_Fpu();
}
//
//#############################################################################
// Configure
//#############################################################################
//
void
L4GaugeRenderer::ConfigureForModel(
const char *configuration_name,
Entity *entity
)
{
Check(this);
Check_Pointer(configuration_name);
Test_Tell(
"L4GaugeRenderer::ConfigureForModel(" <<
configuration_name << ", " <<
entity << ")\n"
);
Check(application);
Check(entity);
Mission
*mission = application->GetCurrentMission();
Check(mission);
const char
*model_name = mission->GetGameModel();
Check_Pointer(model_name);
char
full_name[64];
Str_Copy(full_name, model_name, sizeof(full_name));
Str_Cat(full_name, configuration_name, sizeof(full_name));
Test_Tell(
"L4GaugeRenderer::ConfigureForModel, full_name=" << full_name <<
")\n"
);
Configure(full_name, entity);
Check_Fpu();
}
//
//#############################################################################
// SpecialEffect
//#############################################################################
//
void
L4GaugeRenderer::SpecialEffect(
VideoEffectType type,
Scalar duration
)
{
Check(this);
switch (type)
{
case scrambleVideo:
if (graphicsDisplay != NULL)
{
scrambleVideoFlag = True;
scrambleVideoTimeout = ((Scalar)Now()) + duration;
Check(graphicsDisplay);
((SVGA16*) graphicsDisplay)->FunkyVideo(True);
}
break;
// other modes go here
}
Check_Fpu();
}
void
L4GaugeRenderer::ProcessVideoEffects()
{
Check(this);
Check(graphicsDisplay);
Time
now = Now();
if (scrambleVideoFlag)
{
if (now >= scrambleVideoTimeout)
{
scrambleVideoFlag = False;
((SVGA16*) graphicsDisplay)->FunkyVideo(False);
}
}
Check_Fpu();
}
//--------------------------------------------------------------------
// Profiling support
//--------------------------------------------------------------------
Scalar
L4GaugeRenderer::GetCurrentFramePercentage()
{
Check(this);
return Get_Frame_Percent_Used();
}