Commit Graph
42 Commits
Author SHA1 Message Date
2f4ed244ea Fix -fps logger measuring its own overhead; buffer output, fix percentiles
First real run of -fps (222 seconds, 4-monitor bench, in-mission) showed a
rock-solid 60.0 fps average but a worst frame of ~24ms in 82% of seconds, with a
median of 24.4ms and a minimum of 23.2ms. That regularity was the tell: 60Hz vsync
intervals are 16.7 / 33.3 / 50.0 ms, so 24ms is not a multiple of anything and had
no business recurring once per second with that consistency.

Cause: the logger was measuring itself. The per-second line was written straight to
the file with WriteFile on the render thread, and the frame duration is recorded
BEFORE the line is emitted -- so the write's cost landed in the *next* frame's
measurement, which belongs to the next second's bucket. Result: exactly one
inflated frame per second, indefinitely. Holding the handle open (as the first
version did) was not enough; a single WriteFile is still several milliseconds when
something like antivirus is in the path.

Three fixes:

1. Buffered output. Lines accumulate in a 128 KB static buffer (~2000 rows, about
   33 minutes at one row per second) and are flushed only when the buffer fills or
   at exit via atexit(). A normal measurement session now performs no file writes
   at all while running. Trade-off, deliberately accepted: a hard crash loses the
   un-flushed tail. gos-displays.txt remains the crash-survivable log; gos-fps.txt
   is a measurement instrument and must not perturb what it measures.

2. Meaningful percentiles. A "1% low" over 60 samples per second computes
   nFrames/100 = 0, which was clamped to 1 -- so the column was literally
   1000/worst_frame, i.e. the worst-frame column restated in different units.
   Verified against the log: worst 24.1ms -> 41.5 fps, exactly the "1% low" printed.
   Per-second is now a 5% low (worst 3 of 60), which is a number that actually
   differs from the worst frame. True whole-session 1% and 0.1% lows are computed
   at exit from a 0.5ms-bucket frame time histogram (2000 buckets, 0-1000ms) and
   printed in a new session summary along with total frames, elapsed time, average,
   and total hitches. The histogram gives exact-enough percentiles over an entire
   session without retaining every frame time.

3. Carry-over. A single frame longer than one second (a level load: the log shows
   4338ms and 4799ms frames) left the accumulator above the 1000ms threshold, so
   the next few frames each emitted their own bogus one-frame row -- visible in the
   log as rows reporting "1 frame, 1339 fps". The excess is now discarded.

Also adds #include <stdlib.h> to WinMain.cpp for atexit(); it was not reachable
through pch.hpp. GOS_FpsAtExit is declared __cdecl: GameOS builds with /Gz, which
makes __stdcall the default calling convention, but atexit() takes a __cdecl
callback -- without it VC6 rejects the call with C2664.

What the run did establish, and still stands: 60 frames per second sustained for
~150 seconds with no rhythmic multi-hitch pattern, so the mode 4 split-MFD stutter
fix (0a657b59) is holding. Genuine dropped frames (>40ms) occurred in 18% of
seconds, including clusters of 80-90ms; those are real and unaffected by this fix.
Re-measure after this change to see the true baseline.

CLAUDE.md STEP 10 updated: the previous claim that holding the handle open kept the
logger from perturbing the measurement was wrong, and is replaced with what was
actually observed.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 18:43:42 -05:00
9c67dddd83 Display diagnostics + -fps/-tcoop; native multi-monitor MFD ruled out
Four-monitor MFD bring-up on the new bench (MR_new: AMD FirePro W4100, 4 outputs,
Win10). Adds permanent display diagnostics, a Release-capable frame pacing logger,
a self-locating AppCompat shim installer, and settles -- empirically -- whether
dgVoodoo2 can be dropped for the multi-monitor MFD modes. It cannot.

WHY THIS WAS HARD
-----------------
CHSH_Device::InitFirst/InitSecond discarded EVERY HRESULT (SetCooperativeLevel,
SetDisplayMode, CreateSurface, GetAttachedSurface, QueryInterface, CreateDevice)
and returned true unconditionally. A panel that failed to open produced no error,
no crash and no log entry -- the monitor just stayed on the desktop. SPEW is
compiled out of shipping builds, so none of it was visible. Restoring that
visibility is what unblocked everything else.

DIAGNOSTICS ADDED (keep these)
------------------------------
* VideoCard.cpp -- LogDisplayDevices() writes gos-displays.txt next to the exe:
  NumDevices/NumHWDevices/NumMonitors, every DirectDraw device with its
  hw_rasterization flag, the role assignment (FullScreenDevice / g_nNonDualHead /
  g_nDualHead / g_nDualHead2 / g_nMFD1 / g_nMFD2), per-slot -tmon APPLIED/REJECTED
  (previously silent), and whether mode 4's "BOTH mfd1 and mfd2" requirement is met.
* render.cpp -- HSH_LogInit()/HSH_CheckHR()/HSH_HRName() log every DirectDraw call
  in the panel init path with its HRESULT decoded by name (27 DDERR_* codes, all
  verified present and collision-free against build-env/dx7asdk/include/ddraw.h).
  Each line is opened/appended/closed individually so the log survives a crash.
* WinMain.cpp -- GOS_LogFrameRate() writes gos-fps.txt: per second, frame count,
  average fps, 1% low (mean of the worst 1% of frames), worst frame in ms, and a
  count of frames exceeding 2x average ("hitches"). Average fps alone cannot
  distinguish 60fps from 60fps-with-a-dropped-frame-every-second; the 1% low can.
  The engine's own FrameRate readout is #ifdef LAB_ONLY (MWMission.cpp) so it only
  exists in MW4pro.exe; this works in Release, which is what runs on the pods.

NEW SWITCHES (both documented in -help)
---------------------------------------
* -fps    Enable the frame pacing report. Off by default: the gate is the first
          statement in GOS_LogFrameRate, so an unflagged run does no arithmetic
          and does not even create the file. The file handle is held open for the
          process lifetime -- opening/closing it every second would put a syscall
          of unpredictable latency on the render thread, i.e. the measurement tool
          perturbing what it measures.
* -tcoop <0-5>  Selects the SetCooperativeLevel form used by the radar/MFD panels.
          0 = legacy (unchanged shipped behaviour, remains the default).
          Exists so every candidate fix could be compared on real hardware without
          a rebuild between attempts.

CRASH-SAFETY FIX
----------------
hsh_initialized was set unconditionally after panel init, so a failed panel left
null surfaces and a null IDirect3DDevice7 behind and the per-frame path called
straight through them. Now:
  - all four InitSecond overrides (CMR/CRadar/CMFD/CMFDRight) bail on base failure,
  - CMFD_Device::InitFirst reports its real result instead of always returning true,
  - hsh_initialized is only set when the panels genuinely came up.
A display failure now leaves the game running without MFDs instead of bombing to
desktop. Crash signature for the record: `call [ecx+0x44]` with ECX=0 is
IDirectDrawSurface7::GetDC on a never-created surface (vtable offset confirmed
against the DX7 header), reported as "Attempt to read from address 0x00000044".

APPCOMPAT SHIM INSTALLER (new)
------------------------------
build-env/set-appcompat.ps1 + set-appcompat.bat. Self-locating via $PSScriptRoot:
applies DWM8And16BitMitigation to the MW4 executables sitting next to it, wherever
that install lives. HKCU always, HKLM too when elevated (the HKLM value format
differs -- it carries a leading "$" marker -- so the two must not be interchanged).
Verifies by reading back; detects the HIGHDPIAWARE-only entry that SUPPRESSES the
automatic shim; supports -Remove and -WhatIfOnly. deploy-mw4.ps1 now ships both
files into every deployment.

This matters because the layer is keyed on the executable's FULL PATH -- any copy
of an install to another folder or machine silently loses it, and the resulting
error is actively misleading (see below).

WHAT WE LEARNED
---------------
* The AppCompat shim SYNTHESISES 16-bit display modes. Proved directly: the crash
  dump shows "16 bit modes :" EMPTY without it and fully populated with it. MW4
  renders at bitdepth=16 and modern GPUs expose no 16-bit modes at all.
* Without the shim, GameOS reports "Another application is preventing use of full
  screen mode" (GOS_DXRASTERIZER_NOFULLSCREEN, DXRasterizer.cpp ~1125). That is a
  catch-all fired after every SetDisplayMode attempt fails -- it even scans for
  NetMeeting -- and it sends you looking for a conflicting program that does not
  exist. The real cause is the missing shim.
* Exclusive fullscreen DOES work on Win10 with the system ddraw.dll and dgVoodoo2
  physically removed, once the shim is applied to that exe path.
* Windowed mode works natively with no shim at all: the windowed path sets
  Environment.bitDepth = DesktopBpp (32), so there is no mode switch. Verified for
  the console/shell; a full mission windowed is still untested.
* Native DirectDraw enumerates all four W4100 outputs, so dgVoodoo2 was never
  needed for device enumeration.
* The engine is 4:3 ONLY. ImageHlp.cpp ~464 asserts the complete supported set:
  640x480, 512x384, 800x600, 960x720, 1024x768, 1280x1024 (5:4), 1600x1200. No
  16:9 mode and no aspect correction anywhere in the codebase. On a 16:9 monitor
  the scaler must adapt: plain stretch distorts geometry, keep-aspect pillarboxes.
* -2dt is not a recognised switch anywhere in the codebase, despite appearing in
  production ctcl.ini launch lines. Completely inert.
* NumHWDevices (5) can exceed NumDevices (4): it counts D3D device-enumeration
  callbacks, and an adapter exposing both a HAL and a T&L HAL yields two. Benign.

WHAT WE TRIED AND WHY IT FAILED
-------------------------------
The panel cooperative-level call was genuinely wrong -- a latent 2002 bug. Every
panel asked to be BOTH the process focus window AND its own device window, on the
one shared hWindow, after the main device had already taken exclusive mode on it.
The main device (DXRasterizer.cpp ~1027) already uses the correct two-call idiom
(SETFOCUSWINDOW alone, then EXCLUSIVE|FULLSCREEN) -- tagged //sanghoon, the same
author. The panels never were.

Results on real hardware, no dgVoodoo2, shim applied, -tmfds 4:

  -tcoop 0  SETFOCUSWINDOW|CREATEDEVICEWINDOW|ALLOWREBOOT|EXCLUSIVE|FULLSCREEN
            -> DDERR_EXCLUSIVEMODEALREADYSET
  -tcoop 1  CREATEDEVICEWINDOW|EXCLUSIVE|FULLSCREEN (no focus claim)
            -> DDERR_INVALIDPARAMS (CREATEDEVICEWINDOW needs a focus window)
  -tcoop 2  SETFOCUSWINDOW, then CREATEDEVICEWINDOW|EXCLUSIVE|FULLSCREEN
            -> DDERR_INVALIDPARAMS
  -tcoop 3  SETFOCUSWINDOW, then EXCLUSIVE|FULLSCREEN
            -> first panel collides, but that collision STEALS exclusive mode from
               the main display, after which panels 2 and 3 fully initialise (the
               radar reached CreateDevice(HAL) = DD_OK -- a secondary panel running
               entirely on native DirectDraw). Side effect: the desktop was left at
               1920x1080 16bpp. Not viable.
  -tcoop 4  EXCLUSIVE|FULLSCREEN only          -> EXCLUSIVEMODEALREADYSET, all panels
  -tcoop 5  ALLOWREBOOT|EXCLUSIVE|FULLSCREEN   -> EXCLUSIVEMODEALREADYSET, all panels

CONCLUSION: on modern Windows only ONE DirectDraw object per process may hold
exclusive fullscreen. The main display takes it; every secondary panel is refused.
XP allowed multiple. dgVoodoo2 allows it because it is a full reimplementation of
ddraw and is not bound by that rule -- it is not papering over a bug we can fix.

=> dgVoodoo2 CANNOT be removed for -tmfds 1/3/4 by correcting these flags. The
default stays -tcoop 0. The switch is retained because it is how this was settled
and it will re-settle it on different hardware.

The only native path is a borderless windowed panel design (DDSCL_NORMAL + clipper
per monitor, no exclusive mode anywhere). Assessment and staged plan are recorded
in CLAUDE.md STEP 10; not started.

WORKING 4-MONITOR CONFIG (with dgVoodoo2)
-----------------------------------------
dgVoodoo2 Scaling mode MUST be "Stretched, Keep Aspect Ratio". Plain "Stretched"
fails silently: main and radar go fullscreen black, both MFD monitors keep showing
the desktop, and every DirectDraw call still returns DD_OK -- the devices are alive
but dgVoodoo2 never drives those outputs. Diagnosed with a temporary per-panel
colour-flash test (since removed), which also established that device index maps
1:1 to physical monitor on this bench, so -tmon 1,2,3,4 equals auto-detection.
Confirmed working end to end: all three secondary panels present, full mission
played.

KNOWN GAP: the working dgVoodoo.conf is still not versioned in the repo (removed
in 0ceba9c7), so a fresh deploy will reproduce the silent MFD failure.

Behaviour with no new switches supplied is unchanged from the previous build
except on failure paths, which now degrade gracefully instead of crashing.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 18:22:31 -05:00
308fe041d4 Make -help a complete command line reference
MW4.exe accepts 76 command line switches. Only 27 of them appeared in the old
-help output, that output went to the debug log rather than to the user, and it
did not stop the game from launching afterwards -- so in practice the switches
were undocumented.

-help now writes a full, categorised reference to mw4-help.txt next to the
executable, opens it in Notepad, and returns from WinMain without starting the
game. If Notepad cannot be launched, a message box reports where the file was
written. The check runs immediately after the command line is lower-cased at the
top of WinMain, before any subsystem is initialised.

A file plus a viewer was chosen over a message box because MW4 is a GUI-subsystem
application with no console, and 76 switches with real descriptions do not fit
legibly in a dialog.

The reference documents every switch actually parsed, grouped as: display and
video, audio and plasma display, pod hardware and arcade (CTCL), zoom and field
of view, multiplayer and network, logging and diagnostics, development and test
builds, and other. Each entry records the accepted value range where the parser
enforces one -- for example -tbaud 9600-921600, -armorlevel 0-4, -tmfds 0-4,
-zmfovb 0.01-0.5, and the -zmtime special case where 0 means instant.

Two switches are listed under "recognised but inactive" so their behaviour is not
misrepresented: -join, whose consuming line is commented out, and -noabzug, which
is only parsed inside a disabled code path. The LAB-only switches are marked as
accepted and ignored in Release builds.

The old partial SPEWALWAYS list in GetGameOSEnvironment was removed so there is a
single maintained reference rather than two that can drift apart. A comment there
points at the new one.

Also adds an explicit #include <stdio.h>; this translation unit previously had no
direct stdio use and relied on transitive inclusion.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-25 08:54:10 -05:00
9ba2d594b1 Add -tmon switch to hard-assign display devices
Display roles are auto-detected in FindVideoCards(): the main view takes the
first hardware-rasterizing device that is not a 1280x480-capable span, the radar
takes the next, and mode 4's two MFD panels take the two after that. That works
until DirectDraw enumerates the adapters in an unexpected order, at which point
the wrong content appears on the wrong monitor with no way to correct it.

Adds an override that keeps auto-detection as the default:

    -tmon <main>,<radar>,<mfd1>,<mfd2>

Values are 1-based, so the normal case is "-tmon 1,2,3,4". A value of 0 leaves
that slot auto-detected, so "-tmon 2,1,0,0" swaps only main and radar. Separators
may be ',' '/' or ':'. mfd1/mfd2 are only meaningful with -tmfds 4. Omitting the
switch entirely preserves existing behaviour exactly.

Implementation:

- Parsed in MW4Application's WinMain alongside -tmfds. That runs before GameOS
  calls FindVideoCards(), so the values are in place for device selection.
- Stored in g_naMonitorOverride[4] (VideoCard.cpp), extern'd in MW4Application.cpp.
- ApplyMonitorOverride() validates the index against NumDevices, so a stale -tmon
  on a machine with fewer monitors falls back to auto-detection rather than
  breaking startup.
- Ordering matters and is deliberate: main and radar are applied BEFORE the mode 4
  MFD search so that search still skips whichever devices the operator picked;
  mfd1/mfd2 are applied after it. Partial overrides therefore compose correctly.

Role map: main = Environment.FullScreenDevice, radar = g_nNonDualHead,
mfd1 = g_nMFD1, mfd2 = g_nMFD2. The mode 1 span (g_nDualHead) is intentionally
not overridable -- it is detected by 1280x480 mode support, which only the span
card advertises, so that detection is reliable.

Also adds a SPEW line logging the final role -> device map and device count.
Note this is only visible in Profile/LAB builds; SPEW compiles out in Release
(gos2X/Gos.h), so in Release the order is determined by observation.

Verified: compiles clean, console launches. Multi-monitor testing pending.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:39:59 -05:00
0a657b5998 Fix constant rhythmic stutter in MFD mode 4 (split dual 640x480)
-tmfds 4 splits the 1280x480 MFD span into two independent 640x480 monitors so
the span hardware is no longer required. It rendered correctly but stuttered
rhythmically and constantly, making the game unplayable. -tmfds 1 on the same
binary was flawless.

Root cause: a cross-DirectDraw-object texture read, twice every 7-frame cycle.

CHSH_Device::InitFirst with no pOtherHSHD peer creates its OWN IDirectDraw7 (via
wDirectDrawCreateEx on that monitor's device GUID) and InitSecond creates its OWN
primary flip chain and IDirect3DDevice7. CMFDRight_Device::InitFirst passes no
peer, so the right MFD is an entirely separate DirectDraw object.

EndChannel then did, for channels 3-4:

    target->pD3DDevice->SetTexture(0, pDDSTarget);

where target->pD3DDevice belonged to the RIGHT device but pDDSTarget was the LEFT
device's render-target texture. The old code comment asserted "both devices are
on the same GPU so VRAM textures are mutually accessible" -- that premise is
wrong. In DirectDraw 7 a surface belongs to the IDirectDraw7 that created it, not
to the physical GPU, so it is not a valid texture on another object's D3D device.

The MFDs still displayed, which means the runtime was emulating the access with a
VRAM -> system-memory readback and re-upload of the 1024x512 16-bit render target.
That forces a full GPU pipeline stall, and it happened on channels 3 and 4 (that
is, sh_step 5 and 6) -- twice per 7-frame cycle, on the same GPU drawing the main
view. Hence a fixed-period hitch in the whole game, forever.

This also explains why the earlier stagger work (eaa5fd3, BeginSceneRight) did not
help: it only moved a Flip from sh_step 0 to 1, and the flips already used
DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC and never blocked. The flips were never the
problem.

Fix: give the right device everything it draws with, and render channels 3-4
entirely on it.

- New HSH_CreateMFDTextures() builds the mech image atlas and the MFD sprite atlas
  on a caller-supplied IDirectDraw7. Both devices now call it, so each owns a
  complete independent texture set. CMFD_Device::InitSecond was refactored onto it.

- CMFDRight_Device gained its own pDDSMechTexture / pDDSDamageTexture /
  pDDSTargetTexture plus a Release() override, and its InitSecond now sets up
  tw/th and the material/render state exactly like the left device.

- New CMFD_Device::SwapRightState() exchanges this object's DATA members with the
  right device's. BeginChannel swaps in when channel >= 3 in mode 4; EndChannel
  swaps back. This routes all existing drawing to the correct monitor without
  touching the ~233 mfd_device.* call sites in hudchat/huddamage/hudweapon/
  GUIRadarManager. The vtable pointer is deliberately never swapped, so virtual
  dispatch is unaffected; CHSHFont has no virtual functions so its array is
  swapped bytewise to avoid ctor/dtor side effects on a temporary.

- EndChannel's composite is now a single path for all modes. Mode 4 composites
  full 640 width at x=0 (each device is a standalone panel); modes 1-3 keep the
  half-width (ch/3)*w packing into one backbuffer.

Side effects: startup builds the 65-bitmap mech atlas twice (once per device), and
VRAM use rises by a few MB. Modes 0-3 are behaviourally unchanged.

Known cosmetic leftover, deliberately not changed: huddamage.cpp lines ~1319 and
~1901 call LoadTargetTexture outside the channel-3 block, so those loads land on
the left device and go unused. The in-channel call at ~2209 runs every frame in
that branch and correctly populates the right device's copy, so behaviour is
correct -- it is just a redundant load on target change.

Requires rebuild: MW4.exe (GameOS changes recompile the engine library).
Verified: compiles clean, console launches. Two-monitor testing pending.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:39:42 -05:00
29aee4f71b Fix 16 pilots + 1 cameraship failing to launch
A full 16-'Mech roster plus a cameraship silently refused to launch: the console
sat in nLaunchState 3 ("loading") forever with no crash and no error. 15 mechs +
camera worked, and 16 mechs with no camera worked.

Two independent bugs, both counting the cameraship against the 16 'Mech slots.

1. Session capacity (broke all-human rosters)

   CTCL_DefaultHostSetup derived the camera reserve from
   CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount(). Those counters read the CTCL
   tesla table, which is only populated when CTCL_IsConsoleOrCOOP() is true --
   i.e. only on the console. But the machine that creates the network session is
   the cameraship pod (CTCL_DoMission sets g_nServer = nCameraship, and that pod
   runs CTCL_DoCreateGame -> CTCL_DefaultHostSetup(0) -> Mech4CreateGame ->
   gos_CreateGame(..., Environment.NetworkMaxPlayers, ...)).

   On that pod both counters return 0, so the reserve collapsed to +0 and the
   session was created with dwMaxPlayers = 16. The 17th connection was refused,
   CTCL_CheckServerReady never saw nCount == g_nTeslas + 1, and the launch hung.

   Fixed by reserving with a constant, MW4_CAMERASHIP_RESERVE (4, matching
   MAX_CAMERAS), which is valid on every machine regardless of the tesla table.

2. Bot admission (broke any roster containing bots)

   MW4Shell::AddBot rejects when (player_count + bot_count) >= m_maxPlayers.
   player_count is the DirectPlay player count, which includes the cameraship
   connection, so with m_maxPlayers = 16 the last bot was silently refused.
   g_nBOTs then never matched the connected lancemates and the same readiness
   check spun forever.

   Fixed by subtracting cameraship participants from player_count under CTCL,
   via a new CTCL_CountCameraShipsInGame() helper (non-bot entries with
   m_nMechIndex == 0). Cameraships hold a network slot but pilot no 'Mech, so
   they must not consume a 'Mech slot.

Also applies the constant reserve in the PLAYER_LIMIT_PARAMETER path, guarded by
!CTCL_IsNone() so a standalone non-pod host keeps its exact configured limit.

This supersedes commit f76dc05f, which had the right formula but evaluated it on
the console rather than on the pod that actually creates the session.

Note for later: m_maxPlayers is serialized in only 5 bits (MWApplication.cpp),
so 31 is the hard ceiling for any future player-cap work. See
RAISING-PLAYER-CAP.md.

Requires rebuild: MW4.exe (Release + Profile). No script or resource changes.
Verified: compiles clean, console launches. Pod testing pending.

Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-24 23:39:16 -05:00
dicion 515adc2413 mw4print: persist banner text via banner.txt
- Add banner.txt load/save helpers in banner settings dialog

- Load banner from banner.txt first, with options.ini BannerText fallback

- Fix options.ini path join to include missing path separator
2026-07-24 21:56:24 -05:00
dicion ff650f7bec Load File: finalize first-click deterministic apply, add NoReturn, and align docs
- Stabilize Load File first-click behavior across mission/map/options/slots.

- Fix map sequencing by rebuilding game-type scenario list before mission lookup.

- Resolve mission-name miss to map index 0 fallback for selected game type.

- Fix decal handling: map INI decal IDs to dropdown indices, clamp invalid indices, display actual decal IDs in UI labels, and avoid redraw-time decal overwrite.

- Fix option apply timing and UI visibility issues, including Weapon Jam refresh.

- Add NoReturn support end-to-end: parse/store in MW4Shell auto globals, expose script variable, and apply to respawn/no-return mission params and UI checkbox.

- Update autoconfig spec to reflect actual parser/default/fallback behavior and add NoReturn examples.
2026-07-24 21:42:54 -05:00
dicion e5561181bb Fix MAIL_LOAD_AUTO_MISSION crash: defer initialize(this) to sender==this self-mail
'gui_objects have no parent' crash at line 907: initialize(this) cannot be
called from sender==@conlobby@ context (external mail). Fix:

1. Replace initialize(this)+mail(-9998,parent) in MAIL_LOAD_AUTO_MISSION
   with mail(MAIL_LOAD_AUTO_MISSION_DONE=-8888, this) -- a self-mail.

2. Add MAIL_LOAD_AUTO_MISSION_DONE handler in sender==this block, which
   safely calls initialize(this)+mail(-9998,parent) from the correct context.

Also repaired a corrupt duplicate MAIL_PREVIOUS_MISSION_PARAMS block
that was left orphaned by an earlier edit (the original body of
MAIL_LOAD_AUTO_MISSION had been inserted there when the handler was
moved from sender==this to sender==@conlobby@).

No rebuild required (script-only).
2026-07-24 11:09:50 -05:00
dicion 23284b18b0 Fix MAIL_LOAD_AUTO_MISSION: move handler to sender==@conlobby@ block
Handler was inside 'if (sender == this)' but MAIL_LOAD_AUTO_MISSION is sent
from ConLobby (sender != this), so it never fired. All game params (FriendlyFire,
SplashDamage, UnlimitedAmmo, WeaponJam, AdvanceMode, ArmorMode, etc.) were
silently ignored on every Load File click.

Fix: move the ~70-line handler to the 'if (sender == @conlobby@)' block,
alongside MAIL_SET_ROOKIE_MISSION and MAIL_PREVIOUS_MISSION — where all
ConLobby-originated mails are handled. No rebuild required (script only).
2026-07-24 10:56:57 -05:00
dicion cd2fe73c88 Fix Load File: separate Auto globals; fix team/FFA double-press
Issue 1 — double press to fix team/FFA display:
  cur_team_val is updated by ConLobbyMission's -9998 signal which was async.
  Fix: call SetNetworkMissionParamater(team_allowed, ...) directly in ConLobby
  before the slot loop, then re-read cur_team_val via CTCL_GetTeamParams.
  This is synchronous so the slot display is correct on the first click.
  New file keys: TeamAllowed=0/1, TeamCount=2 (default 2 teams).

Issue 2 — Default button broken after Load File click:
  CTCL_LoadAutoFile was overwriting g_nRookieGameType/g_szRookieMission etc.
  so the Default button loaded the last auto-file params instead of defaults.
  Fix: 14 new dedicated g_nAutoXxx/g_szAutoMission globals. CTCL_LoadAutoFile
  populates ONLY these. Rookie Mission globals are never touched.
  New MAIL_LOAD_AUTO_MISSION (-6666) sent to ConLobbyMission applies the Auto
  globals (mirrors MAIL_SET_ROOKIE_MISSION_PARAMS but uses g_nAutoXxx).
  ConLobby reads game type/mission from Auto globals directly into the
  ConLobbyMission dropdowns (@ConLobbyMission@o_game_options[N].nselected).

Files changed:
  MW4Shell.cpp: 16 new globals, StartUp/ShutDown registration, CTCL_LoadAutoFile
  ConLobby.script: MAIL_LOAD_AUTO_MISSION define, updated handler
  ConLobbyMission.script: MAIL_LOAD_AUTO_MISSION define + handler
  autoconfig-file-spec.html: document TeamAllowed + TeamCount fields
Rebuild required: MW4.exe (Release + Profile).
2026-07-24 10:35:11 -05:00
dicion c3d82d78c9 Load File: hide button when automaticmode!=1; fix team/skin double-click
1. Expose g_bAutomaticMode as gosScript variable so ConLobby can check
   it at init time. o_load_file.state is set to 3 (disabled/hidden) if
   automaticmode != 1 in options.ini. Button is fully visible and active
   only when the feature is intentionally enabled.
   Rebuild required: MW4.exe (Release + Profile).

2. Remove cur_team_val conditional for team/skin slot assignment.
   Previously, only one of o_team[k] or o_skins[k] was set depending on
   cur_team_val at click time, but MAIL_SET_ROOKIE_MISSION propagates the
   new game type asynchronously -- cur_team_val would not reflect the
   file's GameType until the next frame, requiring a second click.
   Fix: always set both o_team[k] and o_skins[k] unconditionally. The
   mission launch code uses whichever is relevant for the active mode;
   the other is harmlessly ignored. No rebuild (script-only).
2026-07-24 10:08:21 -05:00
dicion 17ca966473 Fix Load File mech lookup: use stock_array to get chassis name
mech[j] in the flat sorted array hits variant entries (e.g. 'Assassin2 A'
sorts before 'AssassinII' alphabetically, pushing all subsequent chassis
indices off by 1 or more). The script's stock_array[] maps each chassis
index -> its actual position in the flat mech[] array, bypassing variant
entries.

Fix: mech[allowed_mechs[j]] -> mech[stock_array[allowed_mechs[j]]]

Only stock (chassis) names are supported in Mech= field. Operators can
adjust variants manually after Load File is clicked. No rebuild required
(script-only change).
2026-07-24 10:02:13 -05:00
dicion 7852269dc7 Fix Load File crash: VALUEPARM for literal field arg in CTCL_GetAutoSlotInt
Script calls callback(CTCL_GetAutoSlotInt, k, 0/1/2/3) where 0-3 are
integer literals. The script engine passes literals as (void*)N directly
(not as pointers), so INTPARM(1) = *((int*)data[1]) dereferences NULL
when field=0, producing the 'Attempt to read from NULL' STOP.

Fix: VALUEPARM(1) = (int)data[1] reads the value without dereferencing.
k (data[0]) remains INTPARM because it is a script variable (passed as
a pointer to the variable's storage, not a literal).

Also add exists(@ConLobbyMission@) guard before MAIL_SET_ROOKIE_MISSION
for defensive safety if the sub-script is not running.

Rebuild required: MW4.exe (Release + Profile).
2026-07-24 09:31:51 -05:00
dicion c33465611f Add [automaticmode] Load File button to console lobby
options.ini [automaticmode] section:
  automaticmode=1
  automaticfile=c:\path\to\config.ini

Right-click was considered then dropped in favor of a dedicated button
at 467,510 (below Pick Cond., left of Reprint).

C++ (MW4Shell.cpp):
- SAutoFileSlot struct + g_aAutoSlots[16], g_bAutomaticMode, g_szAutomaticFile globals
- [automaticmode] ini read at StartUp
- CTCL_LoadAutoFile: checks file exists, reads [mission] page into existing
  g_nRookieXxx globals + [slot0]..[slot15] pages into g_aAutoSlots[]; returns 1 if loaded
- CTCL_GetAutoSlotName(out_str, k): pilot name for slot k
- CTCL_GetAutoSlotMech(out_str, k): mech display name for slot k
- CTCL_GetAutoSlotInt(k, field): Type/Team/Skin/Decal for slot k (fields 0-3)
- Register/unregister all 4 callbacks in StartUp/ShutDown

Script (ConLobby.script):
- o_load_file button at 467, 510
- Handler: CTCL_LoadAutoFile -> if loaded, sends MAIL_SET_ROOKIE_MISSION to
  ConLobbyMission (game options), clears all slots, then applies per-slot data
  in a loop (pilot names, mech by display-name lookup in allowed_mechs[], team,
  skin, decal). USE_ALLOWED_MECHS/non-ALLOWED_MECHS both handled via #if.

File not consumed (stays on disk); external app overwrites for next load.
Rebuild required: MW4.exe (Release + Profile).
2026-07-24 08:44:57 -05:00
dicion 5813aeb6e9 Fix missing asset files with ones from 5.0.7D 2026-07-23 21:36:27 -05:00
dicion a712002fec CRIOMAIN.CPP: fix min/max undeclared identifier (VC6)
min() and max() are not in scope in this translation unit under VC6.
Replace with explicit ternary clamping expression; no new headers needed.
2026-07-19 19:00:08 -05:00
dicion f76dc05f46 Support 16 pilots + 1 cameraship in multiplayer
MW4Shell.cpp:
- CTCL_DefaultHostSetup (non-coop): replaced hardcoded
  Environment.NetworkMaxPlayers=16 with
  params->m_maxPlayers + (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount())
  so DirectPlay reserves one extra slot per installed cameraship.
  CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount() = camera-only seat count.
- SetNetworkMissionParamater / PLAYER_LIMIT_PARAMETER: applied the same
  camera-slot formula when the host changes the player limit at runtime.
  Also restored the gos_NetServerCommands(gos_Commend_UpdateMaxPlayers)
  call (was accidentally dropped) and the missing break that caused
  fall-through into JOIN_IN_PROGRESS_PARAMETER.
- COOP branch: no change (capped at 9+bots; camera seats not needed there).

ConLobby.script:
- Raised the launch-guard cap from nTempPlayerCount > 16 to > 17,
  allowing the 17th connection (the cameraship) to not trigger the
  'Too many player/bots' error.
2026-07-19 18:54:38 -05:00
dicion 16fca6c4f1 CRIOMAIN.CPP: translate Korean comments to English, clean UTF-8
Translated all EUC-KR/CP949 Korean developer comments (~35 lines) to
English. File re-saved as UTF-8 (was CP949/EUC-KR on disk).

Also included: g_dwRIOPollTimeout formula that scales WaitForMultipleObjects
timeout by baud rate: clamp(ceil(480000/baud), 5, 50) ms.

Key translations:
- Developer markers (sanghoon/hyun)
- Packet receive loop comments
- ACK/NAK handling comments
- Thread/COM init comments
- Button table comments
- Joystick pedal/throttle diagram comments

Decorative diamond markers (◆) stripped from case labels and section
dividers. Unicode arrows (← →) in diagram comments replaced with ASCII.
2026-07-19 18:52:30 -05:00
c768f7c46b Bump console version to V5.1.0b1; merge Highlight Super6 changes
Version bumped to V5.1.0b1 to align with the 5.1.0b-in-progress branch name and
the goal of producing a tested release candidate.

Incorporated manually-tested script changes from Buddy 'Highlight' Taylor of
MechCorps (MCHL), who expanded the default mech pool from Fab4 to Super6:
- ROOKIEMECH defines extended to 6 entries (added Archer ID=1, Warhammer ID=62)
- 16-slot default mech assignments updated to cycle through all 6 Super6 mechs
- Right-click mech randomizer expanded from random(0,3) to random(0,5) to
  include Archer and Warhammer in the pool

Changelog entries for Cyd (06/24/26), MCHL (06/27/26), and RT (07/19/26)
added to the script header.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-19 17:19:08 -05:00
dicion 0ceba9c778 ddraw.dll breaking mw4pro windowed mode 2026-07-19 16:28:51 -05:00
e45a67a8fe mfdsplit: mech loadouts, time list, build fixes, Korean→English, mw4print v2.0
Battlemaster stock loadouts (Content\Mechs\Battlemaster\battlemaster.subsystems):
- Replace lone MediumPulseLaser with full IS stock: PPC (Special2, group 1),
  6×ML (3 RT + 3 LT, group 1), 2×MG (LA, 200 rds, group 1), SRM6 (Special1,
  15 rds, group 2).

Battlemaster Clan 2C (battlemaster2c.subsystems):
- Replace ClanMediumPulseLaser with: ER PPC (RA, group 1), 6×ER ML (3 RT + 3
  LT, group 1), 2×Clan Gauss (LA, 16 rds each, group 1), Clan SSRM6 (Special1,
  15 rds, group 2).

Behemoth / Behemoth2 (.subsystems):
- Move Gauss rifles from weapon group 3 → group 1 (3 occurrences each).

Resource builder (build-resources.ps1):
- Always run with -window (windowed + DDrawCompat). Fullscreen native DDraw
  fails on VMs with the generic Microsoft display adapter.
- Remove dgVoodoo2 D3D interceptors (D3D8/D3D9/D3DImm.dll) from Gameleap\mw4:
  they silently break the builder (process exits 0 without packing anything).
  Script also defensively moves any such files aside via $dgvMoved block.
- Remove dgVoodoo.conf and dgVoodooCpl.exe (abandoned experiment, no longer used).
- Expected: 'Hardware Error: not compatible with MechWarrior 4' dialog at end
  of build on VMs -- click OK, packages are built correctly regardless.

CLAUDE.md: updated with mfdsplit branch notes covering all 2026-07-18 work.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 16:22:03 -05:00
456e197822 Fix ConLobbyMission time list (18 entries) and build-resources ddraw handling
ConLobbyMission.script:
- Expand MP time-limit dropdown from 9 to 18 entries (1-15, 20, 25, 30 min)
  with max_displayed=10 so the list scrolls cleanly.
- Fix bare else-if syntax (missing braces) that caused null-reference crash
  on console lobby load when this was merged from main.
- Fix i==5 vs i==6 max_displayed assignment for time vs radar dropdowns.

build-resources.ps1:
- DDrawCompat's ddraw.dll is fatal to MW4pro.exe in FULLSCREEN but works
  in WINDOWED mode. Don't move it aside; instead run the builder with
  -window when ddraw.dll is present. This fixes builds on VMs with generic
  display adapters (no hardware DirectDraw) and avoids the EnterWindowMode
  CreateSurface crash on Win10/11 without a functioning DWM shim.
- Also temporarily set options.ini bitdepth=32 when running without any
  ddraw interceptor (bare-metal fallback), restored in finally block.
- Remove stale comment about ddraw being fatal; update interceptor detection
  to correctly identify DDrawCompat vs dgVoodoo2 via dgVoodoo.conf presence.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 15:39:42 -05:00
a45be8044c mw4print: bump to version 2.0, update copyright year to 2026
Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 14:42:11 -05:00
9f3a50443a mw4print: bump version to 2.0, update copyright year to 2026
Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-18 14:39:15 -05:00
dicion 47000d6cf2 text version of db structure for reference 2026-07-18 14:10:51 -05:00
dicion 195db56b1e mw4print: add db_schema.sql documenting MySQL export structure 2026-07-18 14:10:04 -05:00
dicion b98cb87ef3 mw4print: MySQL export, configurable banner text
MySQL database export (dbexport.h / dbexport.cpp):
- New files dbexport.h / dbexport.cpp implement late-bound MySQL export.
  libmysql.dll is loaded at runtime via LoadLibrary/GetProcAddress so no
  MySQL SDK is required at compile time; the app runs normally if the DLL
  is absent.
- After each print job, match data is exported to a MySQL server before
  PrintDlg() is called: one row in 'match', one row per player in
  'player_result', one row per attacker/victim pair in 'pvp'.  An optional
  'event' table records every individual SRecScore entry (off by default).
  Tables are created automatically (CREATE TABLE IF NOT EXISTS) on first
  connect.
- Config stored in mw4print.ini (app directory), section [MySQLExport]:
  Enabled, Host, Port, Database, Username, Password, ExportEvents.
- Config loaded at startup (OnCreate); DB_LoadConfig() / DB_SaveConfig().
- Connection timeout set to 5 seconds so the app does not hang if the
  server is unreachable.
- libmysql.dll (MySQL Connector/C 32-bit) added to Gameleap/mw4/ so the
  deploy script copies it to MW4/ alongside mw4print.exe.

Database Settings dialog (File > Database Settings... / Ctrl+D):
- MFC dialog: enable checkbox, Host/Port/Database/Username/Password fields,
  Export Events checkbox, Test Connection button with live status label,
  OK/Cancel.  OK persists settings to mw4print.ini immediately.

Configurable banner text (File > Banner Setting...):
- The 'WWW.MECHJOCK.COM' URL string printed at the bottom of every score
  sheet is now configurable.  Stored as BannerText= in options.ini under
  [battle tech print] (same section/file as the other print layout params).
  File > Banner Setting... opens a dialog to edit it; OK saves to options.ini
  and takes effect on the next print job with no restart needed.
  Default value is the original MECHJOCK string if the key is absent.
2026-07-18 14:05:31 -05:00
dicion af416960fa Translate Korean comments/strings to English; fix UTF-8 encoding across source tree
Korean translation (84 source files, 876 lines):
- Translated all EUC-KR/CP949 Korean developer comments to English across
  84 source files in Gameleap/code/. Zero Korean bytes remain outside the
  intentional font-table headers (D3FFontEdit2/fontedit all.h etc.).
- Comment markers: //상훈 앞/뒤 -> //sanghoon begin/end (Sang-hun's code
  region markers); //상훈짱 begin/end, //상훈.. variants; // 鉉 -> // hyun
  (second developer's markers); // 鉉 - start/end patterns.
- Functional string translations in recscore.cpp (mw4 + mw4print copies):
  body-part return values (왼발/오른발/etc. -> Left Leg/Right Leg/etc.),
  kill-announcer format strings (~30 entries), and the nonmfc.h assert dialog.
- GosView profiler: 킪 -> us (microseconds) in timing display strings.
- Network/socket code (ctcl.cpp, mugsocs.h, ctime.cpp across Launcher/
  MW4Application/MW4GameEd2/AnimScript): state-machine comments, socket
  ID comments, login/session management comments.
- render.hpp CHSH_Device member comments; GUIRadarManager.cpp drawing
  routine comments; hudchat/hudcomp2/huddamage/hudmap/hudweapon/hudtarg
  HUD component comments.
- DXRasterizer.cpp: cleaned residual U+FFFD replacement characters left
  from a prior partial encoding conversion.

UTF-8 encoding cleanup (76+ files):
- Latin-1 single bytes converted to proper UTF-8 multi-byte sequences:
  © (0xA9) in 3dsmax4/Maxscrpt Autodesk/Wainwright copyright headers,
  ® (0xAE) in gosHelp/Remote.cpp, · (0xB7) bullet points in ai command.hpp,
  Û (0xDB) in SafeChain_Test.cpp tool header,
  ß (0xDF) in AnimationSuite version strings (8 files).
- Font lookup tables (D3FFontEdit2/, fontedit/ *.h) intentionally left
  as-is: raw byte values are C array data, not text.

Language DLL:
- Replaced Gameleap/mw4/Language.dll (original Korean binary) and
  MW4/Language.dll with freshly built English version from
  Language - Win32 English config (Language.dsp). Fixes Korean button
  labels in the GameOS exception/crash dialog (??? ??... / ?? / ???
  were showing instead of More Details.../Continue/Exit).
2026-07-18 13:10:31 -05:00
dicion eaa5fd3bbe fix mode 4: stagger right MFD BeginScene to step 1 to match flip timing
Root cause of the broken MFD2 display: CMFD_Device::BeginScene() cleared
the right device back buffer at old sh_step==0, but with the stagger the
right device flip also fired in that same frame (new sh_step==1 = old
sh_step==0 after increment). The flip presented a just-cleared buffer
with only the grid, no channel data.

Fix: split BeginScene for mode 4 - left device (step 0) vs right device
(step 1). Add BeginSceneRight() called from WinMain at old sh_step==1.
The right device flip at new sh_step==1 (= old sh_step==0) now shows
channels 3-4 rendered at steps 5-6 of the previous cycle - correct.

Cycle for mode 4 with stagger:
  old sh_step 0: radar+left BeginScene, no flip
  new sh_step 1: flip right MFD (shows prev cycle channels 3-4)
  old sh_step 1: right BeginScene (clear+grid)
  old sh_step 2-4: channels 0-2 -> left device
  old sh_step 5-6: channels 3-4 -> right device
  new sh_step 0: flip radar+left MFD (shows prev cycle channels 0-2)
2026-07-17 21:43:22 -05:00
dicion ab24aace11 stagger mfd device flips to try and prevent studder. 2026-07-17 21:43:21 -05:00
dicion 6f630c777e fix errors 2026-07-17 21:43:20 -05:00
dicion 87e25677b5 dgvoodoo support files for building MW4 with dgvoodoo2 instead of DDrawCompat. This is a workaround for the fact that DDrawCompat is not compatible with Windows 11, and dgvoodoo2 is a more modern alternative. 2026-07-17 21:43:20 -05:00
dicion e5c4993436 Added tmdfs mode 4 for 2x 640x480 monitors 2026-07-17 21:43:18 -05:00
CydandClaude Fable 5 4cb3ab8ea8 Rebuild + deploy after PR #4 throttle-zero fix
Incremental Release + Profile builds (0 errors) picking up the
CRIOMAIN.CPP UpdateThrottle change; rel.bin\MW4.exe and MW4pro.exe
deployed to MW4\. Build logs in build-env\build_pr4_*.log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:44:54 -05:00
CydandClaude Fable 5 4af0937661 Merge PR #4 from Dicion: fix throttle zero handling in CRIOMAIN
Removes the if(lT!=0) guard in UpdateThrottle that made an exact-zero
throttle reading keep the previous throttle value; adds a negative
clamp. Zero now falls into the deadzone branch -> ZERO_THROTTLE.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:41:17 -05:00
dicion e92e6adf2f Fix throttle zero handling: remove if(lT!=0) guard, add negative clamp 2026-07-15 20:37:42 -05:00
CydandClaude Fable 5 dbcf4052e2 Add -tbaud switch: force COM1 RIO baud for high-speed replica boards
-tbaud <rate> (9600-921600) overrides the COM1 baud rate while keeping
old-RIO (type 0) protocol behavior unchanged; independent of -trio.
SetupComm buffers 2/2 -> 1024/1024 only when the override is active.
Rebuilt Release + Profile (0 errors), deployed rel.bin\MW4.exe to MW4\.
run-firestorm driver: game-start now takes optional extra args.
CLAUDE.md: branch notes incl. the CP949 comment-encoding hazard
(mw4 sources must be edited byte-safely).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 21:50:10 -05:00
CydandClaude Fable 5 2f17631081 FS507D asset recovery, ConLobby V5.0.7Df promotion, mechlab New-Mech crash fix
FS507D_20161015 release analysis wrap-up (drop itself is gitignored; art-review
folder kept local for review):
- Recovered the only assets our tree lacked: 13 hsh HUD/radar/mech bmps and the
  two 5.07D lobby decals (decal_46/47.tga, extracted from release props.mw4 via
  a ported gos_LZDecompress).
- ResourceImagePool.cpp: missing-texture placeholder is now LAB_ONLY (editor
  keeps degraded mode); Release restores the original fatal STOP. Release +
  Profile rebuilt, 0 errors.

Console script reconciled:
- ConLobby.script.new ("BattleTech Console V5.0.7Df", newest revision anywhere)
  promoted to Content\ShellScripts\ConLobby.script; .new removed; stale loose
  deploy copy removed. Verified in-game (console title shows V5.0.7Df).
- Corrected CLAUDE.md: the release never "renamed" the console to
  ComputerPlayer.script -- the resource packer stores script contents under
  alphabetically skewed entry names (runtime resolves the same pairing).
  Packer quirks documented (stale entry carry-forward on incremental builds,
  name/content skew in directory sweeps).

Mechlab bug fix (first FireStorm bug hunt):
- New-Mech dialog showed blank rows for Wolfhound/Zeus and crashed (KERNELBASE
  read AV) when creating a Zeus. Root cause: newmech in chassis.script created
  its Type droplist without setting $$m_listBoxSize$$, so capacity defaulted to
  60 while 65 chassis were written in -- OOB script-array writes. Fixed by
  sizing the list from $$m_chassisCount$$. Latent stock-MW4 bug armed when the
  FireStorm roster passed 60 chassis. Verified: Zeus variant creates cleanly.

props.mw4 fully repacked (decals + promoted console + mechlab fix, junk
.new/.org entries gone); game deploy refreshed at MW4\. Gitignore: FS507D drop
and player-created MW4\Resource\Variants.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:04:45 -05:00
Cyd 035af2b10a verify buildability 2026-06-26 10:55:33 -05:00
CydandClaude Opus 4.8 202563065f Moved to proper subfolder to make room for other VWE projects.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 10:12:09 -05:00
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00