Packing one into every zip meant a tester who unzipped a new build over
their folder got their configuration replaced. bindings.txt has never had
that problem, because the exe carries the template and writes the file
only when it is absent. environ.ini now works the same way, so a new
build can land on an existing folder and every setting survives.
The 245-line template moves out of pack-dist.ps1 and into RPL4ENVIRON.cpp
as the exe's own literal, which also means the exe alone can produce a
working install. It was lifted mechanically rather than retyped, and the
file it writes is line-for-line identical to the one we have been
shipping - only the line endings changed, from a mongrel 243 LF plus one
stray CRLF that PowerShell's Set-Content left on the end, to the uniform
LF the game already writes bindings.txt with.
It cannot simply become optional. Without environ.ini, L4GAUGE is unset -
which disables the gauge renderer and takes every MFD with it - and
L4MFDSPLIT is unset, which is the packed-window arcade layout rather than
the glass cockpit. The shipped values ARE the desktop game; the built-in
getenv fallbacks are the 1995 pod. So the game writes the file rather
than tolerating its absence.
The cost of a file that is never overwritten is that a tester carrying
one across many builds stops being offered new options. Nothing breaks -
an option added later defaults to "behave as before" - but it goes
unnoticed, and "the podium does not work" is a confusing bug report when
the real answer is that their environ.ini predates RP412PODIUM. So the
load names every template key the player's file has never mentioned, and
says they are at built-in defaults and that deleting the file brings the
documented one back. A stale seven-line file lists all 40.
The file is read, never rewritten. The mention test is deliberately
generous - a key counts as known if it appears in any form, commented or
not - because the failure it guards against is worse than a missed
notice: environ.ini is applied line by line, so a second copy of a key
appearing later in the file would silently override the player's own.
The version line also moves to the top of WinMain. It used to print after
the environment was loaded, so the first thing in rpl4.log was a message
about environ.ini rather than which build wrote it.
Verified: the written file matches the old shipped one line for line; an
edited file with a hand-added comment survives another run untouched; a
seven-line file from an older build boots and names all 40 options it has
never heard of; and a full mission on a self-written file brings up the
glass cockpit at 125% with the virtual RIO active and nothing alarming in
the log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A hand-maintained version says what somebody remembered to type. Pinning
it to the repository means a binary always names the commit it came from,
so a log from a test machine settles which changes are in it.
stamp-version.ps1 runs as RP_L4's pre-build step and writes the generated
RP_L4\rpl4build.h:
#define RP412_VERSION "4.12.96"
#define RP412_VERSION_LONG "4.12.96 (a1b2c3d)"
The hash beside the number names the commit exactly; a trailing '+' means
the tree had uncommitted changes to TRACKED files when it was built, which
is the state a puzzling bug report usually comes from. Untracked files do
not count - one scratch document in the tree would otherwise mark every
build dirty and the marker would stop meaning anything.
Generated rather than committed, and gitignored, because a hardcoded
number cannot work: the commit that records "4.12.96" is itself commit 96,
so the file is stale the moment it lands. The header is rewritten only
when the stamp changes, so ordinary rebuilds do not drag RPL4.CPP through
a recompile.
pack-dist.ps1 reads that header instead of asking git again - a commit
between building and packing would otherwise have the zip claiming a
version the binary inside it does not report - and warns when the build
it is packing came from a modified tree. The README banner, the zip name
and the shipped CONTROLS.html all take the same number.
Numbering stays ordered: 95 commits so far, so 4.12.95 follows 4.12.7 and
every future build sorts after it. Only the "4.12" line is set by hand,
at the top of the script.
Two things the wiring turned up:
Windows PowerShell turns a native command's stderr into ErrorRecords,
so with $ErrorActionPreference = 'Stop' git's routine "LF will be
replaced by CRLF" warning threw straight past the dirty check and
stamped a modified tree as clean. Every git call now goes through cmd,
which keeps stderr out of PowerShell's error stream entirely.
The script ended on "git diff --quiet", which exits 1 to mean "there
are changes" - as a pre-build step that failed the build on exactly
the tree a developer builds in. It exits 0 explicitly now.
Verified: deleting the header and building recreates it; a second build
reports "(unchanged)" and leaves the timestamp alone; a build on a
modified tree succeeds and stamps 4.12.95 (c1729e4+); and the packed game
logs "Red Planet 4.12.95 (c1729e4+)" on its first line while README.txt
and CONTROLS.html in the same package both read 4.12.95.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Flight sticks, HOTAS throttles, twist grips and rudder pedals, none of
which the game could see before: they arrive through DirectInput rather
than XInput, and PadRIO only read XInput.
joyconfig.bat is the setup: the wizard asks you to move each control in
turn and derives the sign convention from the direction of the move,
then writes the joystick rows of bindings.txt between marker lines,
leaving anything you have edited yourself alone.
Confirmed on the Logitech Extreme 3D: a full pass wrote all four axes,
six buttons and the hat, with X and the throttle lever inverted to match
the pod's convention and Y left alone - and the deadzone on the twist
grip was then hand-tuned from 0.08 to 0.18 in the file, which is the
workflow the marker section exists for.
Version strings bumped in RPL4.CPP, pack-dist.ps1 and the controls page
that ships in the zip.
Built clean, packed, zipped (1004 entries, nothing loose at the root)
and smoke-tested from the dist: boots reporting 4.12.7, virtual RIO up,
and no DirectInput enumeration at all on a default bindings.txt - the
joystick layer only opens when the profile asks for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ported from BT411, which needed the same thing for its glass cockpit.
PadRIO reads XInput, which covers Xbox-class pads and nothing else. A
flight stick, a HOTAS throttle, a twist grip, rudder pedals or a wheel
arrive through DirectInput instead, and until now the game could not see
any of them - the only generic-joystick path left was the 1995 single-
device DIJoystick behind L4CONTROLS=DIJOYSTICK, which is untouched here.
L4JOY is the reader: up to four devices as normalized state blocks, hot-
plug re-enumeration on the same ~3 s cadence PadRIO uses to look for a
pad, and a device lost mid-race zeroed rather than left holding whatever
was pressed when it went. XInput-class devices are excluded by VID/PID
against the RawInput paths carrying the "IG_" marker - without that an
Xbox pad arrives through both APIs and every button counts twice.
bindings.txt gains four rows in the grammar it already had, using its own
vocabulary (deadzone/rate) rather than BT411's:
joydev <slot> [product-name substring]
joyaxis <src> axis <axis> [invert] [deadzone <d>] [rate <n>]
joybutton <n> button <addr> [toggle]
joyhat <n> <up|down|left|right> button <addr>
Slots resolve to a live device every poll, by name substring or ordinal,
so unplugging and replugging does not rewrite anyone's file.
Two things the pod's shape forced that BT411 solved differently:
Pedals - a signed composite axis that decomposes into the pod's two
pedals, positive right and negative left. The pod has a pedal each
side; a twist grip or rudder bar is one signed control, and pressing
one or the other but never both is exactly what it wants to say. It
is a channel name like any other, so a pad stick can drive the turn
too.
A joyaxis on Throttle with no rate is a real lever and OWNS the
channel - full travel maps onto the 0..1 the pod runs on, instead of
nudging the accumulator that a spring-centred pad stick has to use.
RP412JOYCONFIG=1 (joyconfig.bat) runs the capture wizard before the
console screen: it asks the player to move each control, and derives the
sign convention from the DIRECTION of the move. That is the point of it -
a stick that reads positive pushed right and one that reads negative are
equally common, and no amount of documentation gets a player to work out
which they own. It writes only its own section, between marker lines, so
hand-edited keyboard and pad rows survive re-running it.
The wizard also prints every axis at rest before it starts. A driver that
refuses the +-32767 range we ask for reports its own, and an axis then
sits hard over instead of near zero; seeing "X +1.00" on an untouched
stick is the difference between a five-minute fix and a bug report that
says it configured itself. Each capture reports the move it saw for the
same reason.
Verified on the Logitech Extreme 3D on this machine. Enumeration finds
it and excludes the Xbox pad, which still arrives separately through
XInput. Every row shape parses - 7 axes, 2 buttons, 4 hat directions -
and three deliberately malformed rows (a bad axis name, button 99, a
"sideways" hat) are each rejected by line number rather than silently
dropped. The wizard lists the device with its axes at rest reading
X +0.00 Y -0.01 RZ -0.04 SL0 +1.00, waits on the first prompt without
self-triggering, and with a hand on the stick captures X to steering,
Y to pitch, RZ to the pedals and SL0 to the throttle, inverting the ones
that read backwards.
Running the captures through to a written file needs a hand on the
stick, so that part is the machine's to confirm, not this build's.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seven commits since 4.12.5, all about where things sit on screen.
RP412MFDLAYOUT remembers window placement across the menu-race-menu
loop, in mfd_layout.cfg beside bindings.txt: the game window, the
exploded view's display panes, and the plasma glass. Append ,noframe to
a line to take that window's title bar and border off - a cockpit
filling a monitor edge to edge at a rect you chose, where -fit could
only do it by taking the whole screen.
That needed a way out of a window with no title bar, so the setup screen
carries an EXIT GAME button, bottom left and diagonally opposite LAUNCH.
The Steam host/join buttons dim and say STEAM NOT RUNNING rather than
disappearing - two buttons quietly missing reads as a broken build.
And the Winners Circle camera is framed off the award stand rather than
off whoever is standing on it. It had been averaging the filled spots,
which moved the shot with the head count: eight finishers put the eye 24
units closer to the stand and aimed it at the middle of the tiers
instead of at the winner. The framing constants are untouched, so the
shot everybody gets now is the one that was dialled in.
Version strings bumped in RPL4.CPP, pack-dist.ps1 and the controls page
that ships in the zip.
Built clean, packed, and smoke-tested from the dist: boots to the
console screen reporting 4.12.6, virtual RIO up, Steam transport up,
nothing alarming in the log.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
,noframe takes the title bar away, and with it the only way out of the
game. The console screen now offers its own, bottom left: half width and
diagonally opposite LAUNCH GAME, because it is the one button on that
screen you cannot undo and it should not sit next to the one everybody
is aiming for.
It goes through the same door as closing the window - fe.closed, so
RPL4FrontEnd_Run returns False and the race loop breaks - rather than
opening a second shutdown path.
Two things had to move for it to make sense.
The saved placement now loads in RPL4.CPP, right after the main window
is shown, instead of only when SVGA16 builds the cockpit. That was not
until a mission started, so the console screen came up at the default
rect with its title bar still on and the window only jumped to the saved
placement once a race began - which, for a flag whose whole purpose is
to take the title bar off, meant it did nothing on the screen you land
on. RPL4.CPP now owns the main window's registration outright and
SVGA16's branches only reload; the reload after CockpitShellProc goes on
still matters, since its WM_SIZE is what re-fits the canvas.
That in turn made the exploded view's position-only registration
incoherent - the startup load had already applied the size - so the
game window is simply position and size everywhere now. The earlier
reasoning that its exploded size IS the -res render size does not hold:
the back buffer stretches to the window in either view, exactly as it
does for the cockpit.
WM_EXITSIZEMOVE moves from the cockpit subclass to RPL4.CPP's own
WndProc, which the subclass chains to anyway. In its old home it only
existed once a cockpit had been built, so dragging the window on the
console screen - the obvious moment to put it where you want it - saved
nothing. There is also a save on the way out of WinMain, for a session
that never started a race and so never ran SVGA16's teardown save.
Verified: on a bare-framed window the console screen comes up at the
saved 1280x760 with client == window rect, EXIT GAME ends the process
with code 0, and a screenshot shows it clear of the column content. A
console-only session dragged to 333,222 900x640 wrote that on the drag,
kept it through the exit, and came back to exactly it on relaunch -
without a race anywhere in the round trip. The noframe and cockpit
round trips still pass unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Version string, zip name and README, plus the new environ.ini options -
the podium and the mission-length override. Everything else in that file
documents itself, so these should not be the exception.
This one restores the Winners Circle: the race fades out and fades back
in on the award platform, finishers in finishing order with their
callsigns on the plates, held for a few seconds before the results.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Version string, zip name and README. Cut as its own release rather than
replacing 4.12.3's asset again: the three test machines need to be able
to tell builds apart from what the log and the README say, and a binary
that reports 4.12.3 while carrying the lobby work defeats exactly that.
This one is about the lobby. Both rooms show the host's mission setup,
the football team sheet names each player's VTV, and a lobby holds eight
players rather than four.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Version string, zip name and README. This one is about the screen:
the cockpit scales both ways and re-fits on resize keeping 16:9, -fit
runs it borderless over the monitor at a matched render size, the six
secondary displays are the players to scale and the radar to place,
and the button banks reach under the glass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Carries the worldwide lobby-search filter for cross-region internet
testing (the only change since 4.12.1 besides the README rewrite).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The environ.ini reader now skips comments (# or ;), blank lines, and
anything that is not KEY=VALUE, so the shipped file documents the
whole configuration surface: the core settings as-shipped (controls,
renderer, gauge canvas, plasma, single-window cockpit, frame rate,
Steam networking), the optional toggles (keyboard lighting, stick
flip, AA, particles, plasma scale/position, fixed seed), LAN hosting
without Steam, the developer/testing switches (RP412DEVKEYS,
L4CONSOLELEN, the Steam self-test), and the arcade multi-monitor
heritage variables. Stale L4MFDSCALE reference dropped from the
README.
Verified: the game boots on the commented file with values applied
(controls line honored, Steam transport up from the in-file switch).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per the user: the game never reads the pilot keypad, so the numpad
becomes the flight cluster - 8/2/4/6 stick, 7/9 pedals, 0 trigger -
with Shift/Ctrl as throttle up/down and Alt as reverse thrust. That
frees the entire letter board: W/A/S/D/Q/E return to their printed
MFD bank positions and B goes back to being the gap key, so the
default profile now carries the complete unmodified vRIO bank layout.
The keypad addresses stay bindable (arcade key events) but ship
unbound.
Alt as a held flight control meant every release popped the window
menu and stole focus - WndProc now eats SC_KEYMENU. Shift/Ctrl/Alt
key-name aliases added to the parser alongside the .NET names.
Profile parses clean (59 key buttons, 8 key axes); single-player
cycle and key-bomb tests green (Alt+Q abort unaffected).
Machines with an existing bindings.txt keep their old map - delete
the file to take the new defaults.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round seven was the first complete Steam race: three machines, five
minutes, wall deaths respawning, the console ending the race on time,
and rematches launched from the same lobby. The one gap the run showed:
only the owner saw scores - members went straight back to the room.
The owner now publishes its console results (host, score, name, keyed
by the race nonce) into lobby data right after teardown; members pull
the sheet (waiting up to 8s for it to land), inject it into the local
results intake, and the same RACE RESULTS screen shows on every
machine before the room.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round six raced all three machines (staging fix confirmed) and then
exposed what happens when a pod leaves mid-mission - which arcade pods
never did.
The B crash dump named it exactly: VTV::TakeDamageMessageHandler
resolved message->inflictingEntity to NULL (the entity belonged to the
departed owner) and dereferenced it - Verify is compiled out in
release. Collision damage from an entity that no longer exists is now
ignored.
And the race B and C were left in was a zombie: the owner (console)
had aborted, so the mission clock would count up forever and the
death/respawn flow hung with nobody to arbitrate. Lobby-member races
now set gConsoleLossEndsMission: losing the console mid-mission posts
StopMission locally, the pod tears down, and lands back in the lobby
room. Arcade -net pods keep the re-listen-and-wait behavior.
Loopback hosted race still green.
For the drivers: the ampersand key is the arcade mission-abort - that
was every crash-on-keypress so far; and a sleeping Bluetooth pad wakes
on the Xbox button and hot-connects within 3 seconds (PadRIO
re-probes).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round five reached the race - full mesh on all three machines, eggs,
ACKs, mission running - but the owner raced ALONE. Cause: the engine
self-runs a pod at WaitingForLaunch when its console host is not
online (the arcade no-console fallback), and the owner''s in-process
console never connects to its own pod. On fast-loading owners the
self-run beat the console''s staging gate, so RunMission was never
sent and the members sat staged at black screens until someone hit
the & emergency-abort key.
gConsoleMarshalsLaunch (APPMGR) now tells the engine an in-process
console owns the launch: the network-race install sets it and the
owner holds at WaitingForLaunch with everyone else; plain single
player leaves it False and auto-runs as always. Verified on loopback:
all pods staged - RUN is back in the hosted-race log and both the
hosted race and the single-player cycle pass.
Also: unhandled-exception minidumps (rpl4crash.dmp beside the exe,
dbghelp loaded lazily) so test-machine crashes hand back stacks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RPL4LOBBY implements the multiplayer front door on ISteamMatchmaking.
The setup menu grows HOST STEAM RACE / JOIN STEAM RACE buttons when
the Steam wire is live; hosting creates a tagged public lobby, joining
finds one. Every member publishes FakeIP + fake ports + persona +
loadout as member data; the room screen lists members (host marked)
and gives the owner a launch button.
Launching writes a nonced go-roster into lobby data. Each pod
registers every peer with the Steam transport (two-port peer table:
engine console/game ports map to Steam fake ports on connect) and
enters the race: the owner through the hosted-race path - it builds
the multi-pilot egg from real personas and loadouts and its console
marshals everyone - and members as network pods that boot straight
into WaitingForEgg for the owner to feed over the wire.
The lobby outlives races: members loop back through WinMain into the
room (no local console needed - MissionCompleted is waived for member
races), and the owner returns to the room after its results screen.
Leaving the lobby clears the hosted-race priming.
Verified on this box: menu buttons appear under RP412STEAM=1, hosting
creates a lobby on the Steam backend, the room runs and leaves back to
the menu; single-player cycling and the LAN hosted race both still
pass. Full three-account mesh test is next, on real hardware.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The lobby-owner-as-console architecture, in-engine. RPL4CONSOLE gains
RPL4LocalConsole_InstallNetworkRace: the owner pod switches to network
mode and meshes like any pod (egg fed locally via FeedLocalEgg, which
now opens the ConsoleOnly state gate), while the console tick also
marshals REMOTE pods over NetTransport speaking the exact arcade
protocol - egg chunks with 5s-retry-until-ACK, 1Hz state polling,
RunMission once every pod stages at WaitingForLaunch, StopMission at
expiry (remotes first, local pod holds until their EndMission scores
land), score intake labeled with [pilots]-order names on the results
screen.
The front end builds the multi-pilot egg: RP412HOSTPODS lists member
console channels (lobby stand-in; the Steam lobby feeds the same
path), RP412HOSTPORT/RP412HOSTADDR set the owner side.
Winsock Connect now redials with a fresh socket per attempt (a refused
TCP socket is dead; the old loop reused it) bounded at 120s - needed
whenever a peer boots after the caller, which is the normal Steam
lobby launch order.
Verified on loopback: member pod in -net, owner hosting from its menu;
mesh completed both sides, 30s race, remote score collected over the
wire (host 3), local stop after the drain, results screen shows both
pilots by name in one process that returns to the menu.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Steamworks SDK 1.64 vendored at extern/steamworks_sdk_164 (headers +
win32 redistributables only; .gitignore trims the rest). Both projects
build with RP412_STEAM; activation stays behind the RP412STEAM=1
environment switch, so plain desktop runs never touch Steam.
L4STEAMTRANSPORT.cpp implements NetTransport on ISteamNetworkingSockets
with FakeIP: SteamNetTransport_Install brings up SteamAPI, relay
network access, and a two-port FakeIP identity (fake port 0 = console
channel, 1 = game mesh), then swaps the process wire; any failure logs
the reason and the game carries on over TCP. Addressing keeps the
engine untouched: all pods share the -net port convention, eggs carry
fakeip:engineport, and the transport alone translates engine ports to
Steam fake ports via the lobby-fed peer table (RegisterPeer). Connect
mirrors the TCP retry-while-refused loop; Receive normalizes message
lanes back into the stream semantics CheckBuffers expects.
Runtime verified on this box: RP412STEAM=1 under AppID 480 came up as
169.254.59.52 (fake ports 32256/32257); without Steam credentials it
falls back to TCP cleanly; default boot logs no Steam lines at all.
steam_api.dll ships in the dist.
Next: the lobby layer (ISteamMatchmaking member data -> RegisterPeer +
egg build + RPL4CONSOLE marshal), which needs a second account to test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After a console-marshaled race ends, the race loop now shows a RACE
RESULTS screen (place / pilot / final score, sorted descending, with a
CONTINUE button) before returning to the setup menu. Scores come from
the local console's intake; single-player rows carry the pilot's own
name, additional pods show their host number until the Steam roster
maps IDs to personas.
The setup menu also keeps the player's selections and pilot name across
races now instead of resetting to defaults each cycle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WinMain now wraps the engine block in a loop: when a front-end-launched
mission ends under the local console, the setup screen comes back in the
same process instead of exiting (the arcade relaunch-per-mission model).
Replaces the CreateProcess self-respawn - required for Steam, where the
lobby and sockets must survive across races.
Second-cycle re-init crash fixed: d3d_OBJECT kept a static texture cache
keyed by filename, so race 2 got IDirect3DTexture9 pointers created on
race 1 destroyed device and died at first draw (DrawMesh AV). The cache
is now flushed in ~DPLRenderer before the device is released, and
ParticleEngine::Initialize drops particles left over from the previous
mission. Verified: three consecutive 30s races in one PID, each stopped
on time by the console with final scores collected.
Also: L4CONSOLELEN env override for test-length races, and the console
exposes MissionCompleted() for the loop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Domain correction from playtest: hand-fed eggs are a developer shortcut
- a mission only ends on a console command, so the clock hits 00:00 and
counts up forever. Even single-player games need a console marshal.
RPL4CONSOLE is that console. Like the real one it lives on its own
thread: it owns the mission clock and raises the stop request at the
selected length; the app-manager per-frame hook (new gPerFrameHook seam
in APPMGR, called while the application global is live - the loop
condition NULLs it on exit, which ate the first attempt) executes the
engine-safe part, dispatching the same StopMissionMessage TeslaConsole
sent. Final scores flow in through a new RP-layer sink
(gConsoleScoreSink in RPCNSL): RPPlayer feeds it the same score it
sends a real console at mission end.
It also inherits the launcher role: the application tears down after a
stop (arcade pods were relaunched per mission by TeslaLauncher), so
WinMain respawns the process when the console ended the mission,
landing back on the race-setup screen. L4NetworkManager grows
FeedLocalEgg (the single-user egg-inject path, callable mid-session)
for the future in-process loop.
Verified end to end: menu -> 3:00 race -> stop dispatched exactly on
time -> final score collected (host 1 = 4113) -> process respawned with
the front end up. -egg runs stay unmarshaled (the dev shortcut).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Starting without -egg, -net, or -mr now boots a race-setup screen
(RP_L4/RPL4FE.cpp) instead of aborting: track / vehicle / color / badge
/ time-of-day / weather / race length plus pilot name, populated from
TeslaConsole''s RPConfig.xml catalog (Death Race scenario). LAUNCH
builds the egg exactly as the console did - the RPMission.ToEggString
port, including the pilot name pre-rendered to 1bpp plasma bitmaps
(128x32 + 64x16) via GDI with the console''s auto-shrink font logic and
the verbatim ordinal graphics - writes frontend.egg, and injects it
into the standard egg-load path (new L4Application::
SetEggNotationFileName).
The menu is a GDI child of the main window (pod green-on-black, double
buffered, mouse driven, EDIT control for the name) running a modal loop
before engine init; closing the window exits cleanly. Found and fixed
along the way: the empty egg CString holds a NULL representation
(operator! is the safe emptiness test), and the modal loop needed a
queue nudge for launch clicks delivered via SendMessage.
Verified end to end: boot -> menu -> LAUNCH -> generated egg (7.5KB) ->
racing in the 1080p cockpit with score and mission clock running.
start-windowed.bat now boots into the front end.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The main game window becomes the cockpit shell (enlarged, clipping
children); every display folds in as a chrome-less child pane in the
pod interior arrangement:
[ MFD UL ] [ MFD UC ] [ MFD UR ]
[ plasma (reduced) ][ viewscreen (centered) ]
[ MFD LL ] [ Map ] [ MFD LR ]
The 3D scene presents into a black STATIC viewscreen child via
Present's hDestWindowOverride (new gMainPresentWindow global) - no
swap-chain changes, and STATIC's transparent hit-testing keeps mouse
input over the 3D view flowing to the game window. MFDSplitView gains a
parent/child mode; PlasmaScreen::Position reparents the glass into the
shell. Main window class background goes black for the cockpit gaps.
Verified by screenshot: live green gauges (LIFT CUT / BOOST / CHUTE /
trigger-program screens) with their red button strips, the 3D canyon in
the centered viewscreen, plasma score glass at its left, map with lit
amber preset lamps - one window, 976x1132 client at 50% scale.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The arcade startup hid the cursor unconditionally in release builds -
correct for a pod, but desktop windowed play needs the mouse for the
on-screen cockpit buttons and the cursor vanished over every display
window. Hide (and restore) it only when running fullscreen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:
- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies
Removed stale Subversion metadata and added .gitignore/.gitattributes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>