Author SHA1 Message Date
dicion 06de4534da BTFrstrm: add Load File test ini files (FFA Coliseum + 4-team KotH CentralPark) 2026-07-24 11:01:11 -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 62dc0952b1 BTFrstrm: add Load File test configs (FFA Coliseum, Team KoH CentralPark) 2026-07-24 10:46:24 -05:00
dicion 9a9f2d0df2 autoconfig-file-spec.html: add TeamAllowed/TeamCount to example; fix caveat #3 2026-07-24 10:37:37 -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 3f2d79a038 BTFrstrm: add weapon location spreadsheets (Special1/2 and rear-facing) 2026-07-24 10:18:06 -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 76121f1c68 BTFrstrm: add autoconfig-file-spec.html (Load File format reference) 2026-07-24 09:13:37 -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 fccdc2dee4 CLAUDE.md: document 5813aeb6 through 0344418a work (2026-07-23)
- hsh/ BMP canonical renames (MFD + Mechs, commit 5813aeb6)
- RookieMission configurable defaults via options.ini (MW4Shell.cpp + ConLobbyMission.script, 5813aeb6)
- Mechlab turn rate label correction (StringResource.rc, 5813aeb6)
- BTFrstrm design docs: MechDependencyTree.docx + Special_Zones.docx (840bc96c)
- mech_loadouts.md: MechEditor data sources, field conversions, hsh naming reference (0344418a)
2026-07-23 22:18:43 -05:00
dicion 0344418aae mech_loadouts.md: document MechEditor data sources, conversions, hsh naming (2026-07-23) 2026-07-23 21:56:14 -05:00
dicion 520a860414 Update testing checklist through 2026-07-23 (RookieMission, turnrate label, hsh renames) 2026-07-23 21:47:08 -05:00
dicion 5813aeb6e9 Fix missing asset files with ones from 5.0.7D 2026-07-23 21:36:27 -05:00
dicion 840bc96cc1 Documentation! 2026-07-23 16:16:45 -05:00
dicion 72e1e59d8e CLAUDE.md: document eaa5fd3 through 5.1.0b-in-progress work
- MFD mode 4 right-device stagger fix (eaa5fd3): cycle diagram, files touched
- Linux→Windows rsync workflow: sync-to-windows.sh (55b9bfc5)
- ddraw.dll removed from repo; build-resources.ps1 moves it aside (0ceba9c7, 24825ff3)
- ConLobby V5.1.0b1 / Super6 6-mech rotation from Highlight (c768f7c4)
- CRIOMAIN.CPP Korean translation + RIO poll timeout scaling; CRLF hazard note;
  min/max undeclared in VC6 in this TU (16fca6c4, a712002f)
- 16 pilots + 1 cameraship: NetworkMaxPlayers formula, fall-through break warning (f76dc05f)
2026-07-19 19:55:46 -05:00
dicion b591bae273 add checklist of changes for testing! 2026-07-19 19:18:09 -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 24825ff396 more ddraw handling fixes for profiler. ugh. 2026-07-19 16:41:45 -05:00
dicion 0ceba9c778 ddraw.dll breaking mw4pro windowed mode 2026-07-19 16:28:51 -05:00
55b9bfc521 Add sync-to-windows.sh: rsync script for Linux→Windows build machine
Syncs all source, content, toolchain, and assets needed to build and test the game on the Windows machine at /vwe/firestorm. Excludes generated build outputs (bin dirs, *.mw4, *.dep), .git/LFS, _UNUSED, and the MW4 deploy dir.

Co-authored-by: Claude Sonnet 4.6 (Anthropic) <noreply@anthropic.com>
Co-authored-by: GitHub Copilot <copilot@github.com>
2026-07-19 15:40:24 -05:00
188 changed files with 2780 additions and 400 deletions
Binary file not shown.
Binary file not shown.
+575
View File
@@ -0,0 +1,575 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Automatic Config File Specification ? Firestorm Console Load File</title>
<style>
body { font-family: Calibri, Arial, sans-serif; font-size: 11pt; margin: 60px 72px; color: #1a1a1a; max-width: 900px; }
h1 { font-size: 20pt; color: #1F3864; border-bottom: 3px solid #1F3864; padding-bottom: 8px; margin-top: 0; }
h2 { font-size: 14pt; color: #1F3864; border-bottom: 1px solid #BDD7EE; padding-bottom: 4px; margin-top: 28px; }
h3 { font-size: 12pt; color: #2E4D7B; margin-top: 20px; margin-bottom: 4px; }
h4 { font-size: 11pt; color: #2E4D7B; margin-top: 16px; margin-bottom: 2px; font-style: italic; }
.meta { color: #555; font-size: 10pt; margin-bottom: 28px; }
code, .code { font-family: Consolas, "Courier New", monospace; font-size: 10pt; background: #F4F7FB; padding: 1px 4px; border-radius: 2px; color: #1a1a1a; }
pre { font-family: Consolas, "Courier New", monospace; font-size: 10pt; background: #F4F7FB; border: 1px solid #BDD7EE; border-left: 4px solid #2E4D7B; padding: 12px 16px; border-radius: 0 4px 4px 0; overflow-x: auto; line-height: 1.5; }
pre .section { color: #7B2D8B; font-weight: bold; }
pre .key { color: #1F6A8F; }
pre .value { color: #2D6A2D; }
pre .comment { color: #888; font-style: italic; }
table { border-collapse: collapse; width: 100%; margin: 10px 0 18px 0; font-size: 10.5pt; }
th { background: #1F3864; color: white; padding: 7px 12px; text-align: left; font-weight: bold; font-size: 10pt; }
tr:nth-child(even) td { background: #EFF4FB; }
td { padding: 5px 12px; border-bottom: 1px solid #D0D9E8; vertical-align: top; }
td code { background: #E8EFF9; }
.warn { background: #FFF3CD; border: 1px solid #FFEAA0; border-left: 4px solid #F0B429; padding: 10px 14px; border-radius: 0 4px 4px 0; margin: 14px 0; font-size: 10.5pt; }
.warn strong { color: #7A4F00; }
.note { background: #E7F3FD; border: 1px solid #B8D9F5; border-left: 4px solid #1F3864; padding: 10px 14px; border-radius: 0 4px 4px 0; margin: 14px 0; font-size: 10.5pt; }
.optional { color: #666; font-size: 9.5pt; font-style: italic; }
ul, ol { margin: 6px 0 10px 0; padding-left: 24px; }
li { margin-bottom: 4px; }
hr { border: none; border-top: 1px solid #D0D9E8; margin: 30px 0; }
.key-block { background: #F0F4FA; border: 1px solid #C5D5E8; padding: 8px 12px; border-radius: 4px; margin: 8px 0 12px 0; }
.key-block .kname { font-family: Consolas, monospace; font-size: 10.5pt; font-weight: bold; color: #1F6A8F; }
.key-block .ktype { color: #888; font-size: 9.5pt; margin-left: 8px; }
.default-tag { background: #D4EDDA; color: #155724; font-size: 9pt; padding: 1px 6px; border-radius: 3px; margin-left: 6px; font-weight: bold; }
.ignored-tag { background: #F8D7DA; color: #721C24; font-size: 9pt; padding: 1px 6px; border-radius: 3px; margin-left: 6px; }
.mech-table td:first-child { font-family: Consolas, monospace; font-size: 10pt; white-space: nowrap; }
@media print { body { margin: 40px; } h2 { page-break-before: auto; } }
</style>
</head>
<body>
<h1>Automatic Config File Format Specification</h1>
<div class="meta">
Feature: Console Lobby &ldquo;Load File&rdquo; button &nbsp;|&nbsp;
Commit: <code>c3346561</code> (branch <code>5.1.0b-in-progress</code>) &nbsp;|&nbsp;
Date: 2026-07-24, updated 2026-07-24 (commit <code>cd2fe73c</code>)
</div>
<h2>1. Overview</h2>
<p>
The system operates with <strong>two separate files</strong>:
</p>
<ol>
<li>
<strong><code>options.ini</code></strong> &mdash; machine-level one-time configuration, read once at game startup.
Contains the path to the config file. Never changed by the game.
</li>
<li>
<strong>The config file</strong> (path specified in <code>options.ini</code>) &mdash;
written by the external application before each match.
Read by the game when the operator clicks <strong>Load File</strong> in the console lobby.
<strong>Not consumed</strong> &mdash; the game leaves it on disk. Overwrite it to set the next match.
</li>
</ol>
<p>
When <strong>Load File</strong> is clicked, the game:
</p>
<ol>
<li>Checks that <code>automaticmode=1</code> and the file path is configured.</li>
<li>Checks that the config file exists on disk (<code>GetFileAttributes</code>). If not, does nothing.</li>
<li>Reads the file, applies game options, clears all current lobby slots, then fills slots from the file.</li>
<li>Leaves the file on disk unchanged.</li>
</ol>
<hr>
<h2>2. <code>options.ini</code> Setup (One-Time, Per Machine)</h2>
<p>Add the following section to the game&rsquo;s <code>options.ini</code> in the game working directory.</p>
<pre><span class="section">[automaticmode]</span>
<span class="key">automaticmode</span>=<span class="value">1</span>
<span class="key">automaticfile</span>=<span class="value">c:\mw4files\nextmatch.ini</span></pre>
<table>
<tr><th>Key</th><th>Type</th><th>Description</th></tr>
<tr><td><code>automaticmode</code></td><td>integer</td><td><code>1</code> = enabled. <code>0</code> = disabled; Load File button does nothing.</td></tr>
<tr><td><code>automaticfile</code></td><td>string</td><td>Full absolute Windows path to the config file. Max 259 characters. Can be any local drive letter.</td></tr>
</table>
<div class="note">
<strong>Read at startup only.</strong> Changing <code>options.ini</code> requires a game restart.
If the <code>[automaticmode]</code> section is absent, or <code>automaticmode=0</code>, or
<code>automaticfile</code> is empty, Load File always does nothing regardless of what config file exists.
</div>
<hr>
<h2>3. Config File Format Rules</h2>
<p>The config file uses <strong>standard Windows INI format</strong>, parsed by <code>NotationFile::Standard</code>.</p>
<ul>
<li><strong>Encoding:</strong> ANSI / ASCII only. No Unicode, no UTF-8 BOM.</li>
<li><strong>Line endings:</strong> CRLF or LF &mdash; both accepted.</li>
<li><strong>Section headers:</strong> <code>[sectionname]</code> &mdash; <strong>case-sensitive, must be all lowercase exactly as shown.</strong></li>
<li><strong>Key names:</strong> Case-sensitive. Must match exactly as documented below.</li>
<li><strong>String values:</strong> Everything after <code>=</code> to end of line. Do <strong>not</strong> wrap in quotes. Example: <code>MissionName=ScarabStronghold - Attrition</code></li>
<li><strong>Integer values:</strong> Raw decimal integer only. No quotes, no sign unless negative. Example: <code>GameType=2</code></li>
<li><strong>Do not use comments</strong> (<code>//</code> or <code>;</code>). Parser behavior is not guaranteed. Keep the file clean.</li>
<li><strong>Missing key:</strong> If a key is absent from its section, the corresponding lobby value stays at its current setting. No error is raised.</li>
<li><strong>Missing section:</strong> If <code>[mission]</code> is absent, no game options are changed. If <code>[slot0]</code> is absent, no slots are changed.</li>
</ul>
<hr>
<h2>4. Section: <code>[mission]</code></h2>
<p>Sets all game lobby parameters. This section is <span class="optional">optional</span> &mdash; omit it to leave game options unchanged and only configure slots.</p>
<pre><span class="section">[mission]</span>
<span class="key">MissionName</span>=<span class="value">ScarabStronghold - Attrition</span>
<span class="key">GameType</span>=<span class="value">2</span>
<span class="key">TeamAllowed</span>=<span class="value">0</span>
<span class="key">Visibility</span>=<span class="value">0</span>
<span class="key">Weather</span>=<span class="value">0</span>
<span class="key">TimeOfDay</span>=<span class="value">0</span>
<span class="key">TimeLimit</span>=<span class="value">7</span>
<span class="key">Radar</span>=<span class="value">0</span>
<span class="key">HeatOn</span>=<span class="value">0</span>
<span class="key">FriendlyFire</span>=<span class="value">0</span>
<span class="key">SplashDamage</span>=<span class="value">0</span>
<span class="key">UnlimitedAmmo</span>=<span class="value">1</span>
<span class="key">WeaponJam</span>=<span class="value">0</span>
<span class="key">AdvanceMode</span>=<span class="value">0</span>
<span class="key">ArmorMode</span>=<span class="value">0</span></pre>
<h3>4.1 &nbsp;<code>MissionName</code> &mdash; string, max 255 characters</h3>
<p>
The scenario name, <strong>exactly as it appears in the mission list dropdown in the console lobby.</strong>
Match is exact string equality, case-sensitive. Trailing/leading spaces are stripped by the parser.
</p>
<div class="warn">
<strong>If the name is not found</strong> in the list for the selected <code>GameType</code>,
mission selection silently fails &mdash; the dropdown stays at its previous value.
No error is logged. All other parameters (Visibility, Weather, etc.) are still applied.
The available mission names depend on what scenario files are installed on the machine;
there is no static guaranteed list.
</div>
<h3>4.2 &nbsp;<code>GameType</code> &mdash; integer (dropdown index, 0-based)</h3>
<div class="warn">
<strong>This is the position in the game type dropdown, NOT the internal game type ID number.</strong>
The dropdown is built dynamically at lobby startup &mdash; only game types that have at least
one installed scenario are shown. If a game type has no installed maps it is skipped and all
subsequent indices shift down by one.
</div>
<p>For a <strong>standard full installation</strong> with all game types present, the order is fixed:</p>
<table>
<tr><th>Index</th><th>Display Name</th><th>Mode</th></tr>
<tr><td><code>0</code></td><td>Destruction</td><td>FFA</td></tr>
<tr><td><code>1</code></td><td>Team Destruction</td><td>Team</td></tr>
<tr><td><code>2</code></td><td>Attrition</td><td>FFA &mdash; <strong>default</strong> <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>3</code></td><td>Team Attrition</td><td>Team</td></tr>
<tr><td><code>4</code></td><td>Capture the Flag</td><td>FFA</td></tr>
<tr><td><code>5</code></td><td>King of the Hill</td><td>FFA</td></tr>
<tr><td><code>6</code></td><td>Team King of the Hill</td><td>Team</td></tr>
<tr><td><code>7</code></td><td>Steal the Bacon</td><td>FFA</td></tr>
<tr><td><code>8</code></td><td>Master Trial</td><td>Special</td></tr>
<tr><td><code>9</code></td><td>Siege Assault</td><td>Special</td></tr>
<tr><td><code>10+</code></td><td>Custom map variants</td><td>Varies (if custom content installed)</td></tr>
</table>
<p><strong>Team vs. FFA is important:</strong> it determines whether the <code>Team</code> or <code>Skin</code> field is applied to each slot. See Section 5 for details.</p>
<h3>4.3 &nbsp;<code>Visibility</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Display Name</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Default</td><td>Same as Clear <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>Clear</td><td>Full visibility range</td></tr>
<tr><td><code>2</code></td><td>Light Fog</td><td>Reduced visibility range</td></tr>
<tr><td><code>3</code></td><td>Heavy Fog</td><td>Heavily reduced visibility range</td></tr>
<tr><td><code>4</code></td><td>Pea Soup Fog</td><td>Near-zero visibility range</td></tr>
</table>
<h3>4.4 &nbsp;<code>Weather</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Display</th></tr>
<tr><td><code>0</code></td><td>Off <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On (rain or snow depending on map)</td></tr>
</table>
<h3>4.5 &nbsp;<code>TimeOfDay</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Display</th></tr>
<tr><td><code>0</code></td><td>Day <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>Night</td></tr>
</table>
<h3>4.6 &nbsp;<code>TimeLimit</code> &mdash; integer (<strong>actual minutes</strong>, not a list index)</h3>
<p>
Specify the desired match length in <strong>real minutes</strong>.
Valid values that appear in the lobby dropdown:
</p>
<p>
<code>1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 25, 30</code>
</p>
<p>
Other integers are accepted and applied to the network parameter but will not match a dropdown item
(the visual display may show the nearest value).
</p>
<p>
Special value <code>-1</code>: use the server&rsquo;s current default time (7 minutes unless changed separately).
</p>
<p>Default if key absent: server current default (equivalent to <code>-1</code>).</p>
<h3>4.7 &nbsp;<code>Radar</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Display</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Novice</td><td>All mechs visible, no range limit <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>Normal</td><td>Standard radar with range cutoff</td></tr>
<tr><td><code>2</code></td><td>Team Only</td><td>Only your own team visible on radar</td></tr>
<tr><td><code>3</code></td><td>No Radar</td><td>Radar completely disabled for all players</td></tr>
</table>
<h3>4.8 &nbsp;<code>HeatOn</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off &mdash; heat never builds, weapons never overheat <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On &mdash; standard heat management enabled</td></tr>
</table>
<h3>4.9 &nbsp;<code>FriendlyFire</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off &mdash; friendly fire disabled <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On &mdash; 100% damage to teammates</td></tr>
</table>
<h3>4.10 &nbsp;<code>SplashDamage</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On &mdash; area/splash damage from missiles and explosions</td></tr>
</table>
<h3>4.11 &nbsp;<code>UnlimitedAmmo</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off &mdash; ammo is finite</td></tr>
<tr><td><code>1</code></td><td>On &mdash; unlimited ammo <span class="default-tag">DEFAULT (console/arcade)</span></td></tr>
</table>
<h3>4.12 &nbsp;<code>WeaponJam</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On &mdash; weapons can randomly jam</td></tr>
</table>
<h3>4.13 &nbsp;<code>AdvanceMode</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On</td></tr>
</table>
<h3>4.14 &nbsp;<code>ArmorMode</code> &mdash; integer</h3>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Off <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>On</td></tr>
</table>
<h3>4.15 &nbsp;<code>TeamAllowed</code> &mdash; integer <span class="optional">(required for correct team/FFA slot display on first click)</span></h3>
<p>Must match the <code>GameType</code> selected. Controls whether <code>Team</code> or <code>Skin</code> is applied to slots, and whether team dropdowns are shown.</p>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>FFA mode ? slot <code>Skin</code> field is used <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>Team game ? slot <code>Team</code> field is used</td></tr>
</table>
<h3>4.16 &nbsp;<code>TeamCount</code> &mdash; integer <span class="optional">(optional, only when TeamAllowed=1)</span></h3>
<p>Number of teams. Valid range: 2&ndash;8. Default: <code>2</code>.</p>
<hr>
<h2>5. Sections: <code>[slot0]</code> through <code>[slot15]</code></h2>
<p>One section per roster slot, numbered starting from <strong>zero</strong>.</p>
<div class="warn">
<strong>Slots must be contiguous starting from <code>[slot0]</code>.</strong>
The parser stops at the first missing slot number. If <code>[slot2]</code> is absent,
<code>[slot3]</code> through <code>[slot15]</code> are never read, even if present in the file.
Do not skip numbers.
</div>
<p>Maximum: <strong>16 slots</strong> (indices 0&ndash;15). All slots are cleared before applying file data, so slots not defined in the file will be empty in the lobby after Load File is clicked.</p>
<pre><span class="section">[slot0]</span>
<span class="key">Type</span>=<span class="value">1</span>
<span class="key">PilotName</span>=<span class="value">Alpha One</span>
<span class="key">Mech</span>=<span class="value">Atlas</span>
<span class="key">Team</span>=<span class="value">0</span>
<span class="key">Skin</span>=<span class="value">0</span>
<span class="key">Decal</span>=<span class="value">3</span></pre>
<h3>5.1 &nbsp;<code>Type</code> &mdash; integer, required</h3>
<p>Determines what occupies this slot. All other fields are ignored when <code>Type=0</code>.</p>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>Empty &mdash; slot skipped, left empty in lobby</td></tr>
<tr><td><code>1</code></td><td>Player slot &mdash; a human pilot can connect to this seat</td></tr>
<tr><td><code>2</code></td><td>Bot, difficulty level 1 (easiest)</td></tr>
<tr><td><code>3</code></td><td>Bot, difficulty level 2</td></tr>
<tr><td><code>4</code></td><td>Bot, difficulty level 3</td></tr>
<tr><td><code>5</code></td><td>Bot, difficulty level 4</td></tr>
<tr><td><code>6</code></td><td>Bot, difficulty level 5</td></tr>
<tr><td><code>7</code></td><td>Bot, difficulty level 6</td></tr>
<tr><td><code>8</code></td><td>Bot, difficulty level 7</td></tr>
<tr><td><code>9</code></td><td>Bot, difficulty level 8 (hardest)</td></tr>
</table>
<p>If <code>Type</code> key is absent, it defaults to <code>0</code> (empty) due to zero-initialization.</p>
<h3>5.2 &nbsp;<code>PilotName</code> &mdash; string, max 63 characters</h3>
<p>Only meaningful when <code>Type=1</code> (player slot). Sets the callsign shown in the lobby pilot editbox. The connecting player can override this after they connect.</p>
<p>For bots (<code>Type=2&ndash;9</code>), this field is read from the file but ignored by the script.</p>
<p>Set to empty value or omit key to leave the name blank: <code>PilotName=</code></p>
<p>Strings longer than 63 characters are silently truncated.</p>
<h3>5.3 &nbsp;<code>Mech</code> &mdash; string (chassis name, exact and case-sensitive)</h3>
<p>Must match <strong>exactly</strong> one of the chassis names from <code>MechTable.tbl</code>. The lookup is case-sensitive exact string comparison. If the name is not found, the mech dropdown for that slot stays at its previous value. No error is raised.</p>
<div class="warn">
<strong>These are the table key strings, not display labels.</strong>
Pay close attention to capitalization, spacing, and special characters (hyphens, Roman numerals).
</div>
<table class="mech-table">
<tr><th>Exact <code>Mech=</code> value</th><th>Notes</th></tr>
<tr><td>Annihilator</td><td></td></tr>
<tr><td>Archer</td><td></td></tr>
<tr><td>Arctic Wolf</td><td>space between words</td></tr>
<tr><td>Ares</td><td></td></tr>
<tr><td>Argus</td><td></td></tr>
<tr><td>AssassinII</td><td>no spaces; capital I</td></tr>
<tr><td>Atlas</td><td></td></tr>
<tr><td>Avatar</td><td></td></tr>
<tr><td>Awesome</td><td></td></tr>
<tr><td>Battlemaster</td><td></td></tr>
<tr><td>BattlemasterIIC</td><td>no spaces</td></tr>
<tr><td>Behemoth</td><td></td></tr>
<tr><td>BehemothII</td><td>no space; capital I</td></tr>
<tr><td>Black Hawk</td><td>space between words</td></tr>
<tr><td>Black Knight</td><td>space between words</td></tr>
<tr><td>Black Lanner</td><td>space between words</td></tr>
<tr><td>Brigand</td><td></td></tr>
<tr><td>Bushwacker</td><td></td></tr>
<tr><td>Catapult</td><td></td></tr>
<tr><td>Cauldron-Born</td><td>hyphen between words</td></tr>
<tr><td>Chimera</td><td></td></tr>
<tr><td>Commando</td><td></td></tr>
<tr><td>Cougar</td><td></td></tr>
<tr><td>Cyclops</td><td></td></tr>
<tr><td>Daishi</td><td></td></tr>
<tr><td>Deimos</td><td></td></tr>
<tr><td>Dragon</td><td></td></tr>
<tr><td>Fafnir</td><td></td></tr>
<tr><td>Flea</td><td></td></tr>
<tr><td>Gladiator</td><td></td></tr>
<tr><td>Grizzly</td><td></td></tr>
<tr><td>Hauptmann</td><td></td></tr>
<tr><td>Hellhound</td><td></td></tr>
<tr><td>Hellspawn</td><td></td></tr>
<tr><td>Highlander</td><td></td></tr>
<tr><td>HollanderII</td><td>no space; capital I</td></tr>
<tr><td>Hunchback</td><td></td></tr>
<tr><td>Kodiak</td><td></td></tr>
<tr><td>Loki</td><td></td></tr>
<tr><td>Longbow</td><td></td></tr>
<tr><td>Mad Cat</td><td>space between words</td></tr>
<tr><td>Mad Cat MKII</td><td>space + "MKII" all caps, no period</td></tr>
<tr><td>Masakari</td><td></td></tr>
<tr><td>Mauler</td><td></td></tr>
<tr><td>Nova Cat</td><td>space between words</td></tr>
<tr><td>Osiris</td><td></td></tr>
<tr><td>Owens</td><td></td></tr>
<tr><td>Puma</td><td></td></tr>
<tr><td>Raven</td><td></td></tr>
<tr><td>Rifleman</td><td></td></tr>
<tr><td>Ryoken</td><td></td></tr>
<tr><td>Shadow Cat</td><td>space between words</td></tr>
<tr><td>Solitaire</td><td></td></tr>
<tr><td>Sunder</td><td></td></tr>
<tr><td>Templar</td><td></td></tr>
<tr><td>Thanatos</td><td></td></tr>
<tr><td>Thor</td><td></td></tr>
<tr><td>Uller</td><td></td></tr>
<tr><td>Urbanmech</td><td></td></tr>
<tr><td>Uziel</td><td></td></tr>
<tr><td>Victor</td><td></td></tr>
<tr><td>Vulture</td><td></td></tr>
<tr><td>Warhammer</td><td></td></tr>
<tr><td>Wolfhound</td><td></td></tr>
<tr><td>Zeus</td><td></td></tr>
</table>
<h3>5.4 &nbsp;<code>Team</code> &mdash; integer</h3>
<p>
<strong>Only applied in team game modes</strong> (game types marked &ldquo;Team&rdquo; in Section 4.2 table).
<strong class="ignored-tag">IGNORED in FFA modes.</strong>
</p>
<table>
<tr><th>Value</th><th>Team</th><th>Default Color</th></tr>
<tr><td><code>0</code></td><td>Team 1</td><td>Blue</td></tr>
<tr><td><code>1</code></td><td>Team 2</td><td>Red</td></tr>
<tr><td><code>2</code></td><td>Team 3</td><td>Yellow</td></tr>
<tr><td><code>3</code></td><td>Team 4</td><td>Green</td></tr>
<tr><td><code>4</code></td><td>Team 5</td><td>Purple</td></tr>
<tr><td><code>5</code></td><td>Team 6</td><td>Light Blue</td></tr>
<tr><td><code>6</code></td><td>Team 7</td><td>Orange</td></tr>
<tr><td><code>7</code></td><td>Team 8</td><td>Charcoal</td></tr>
</table>
<h3>5.5 &nbsp;<code>Skin</code> &mdash; integer (FFA camo, dropdown index 0-based)</h3>
<p>
<strong>Only applied in FFA game modes.</strong>
<strong class="ignored-tag">IGNORED in team modes.</strong>
</p>
<table>
<tr><th>Value</th><th>Camo Color/Pattern</th></tr>
<tr><td><code>0</code></td><td>Blue</td></tr>
<tr><td><code>1</code></td><td>Red</td></tr>
<tr><td><code>2</code></td><td>Yellow</td></tr>
<tr><td><code>3</code></td><td>Green</td></tr>
<tr><td><code>4</code></td><td>Purple</td></tr>
<tr><td><code>5</code></td><td>Light Blue</td></tr>
<tr><td><code>6</code></td><td>Orange</td></tr>
<tr><td><code>7</code></td><td>Charcoal</td></tr>
<tr><td><code>8</code>&ndash;<code>15</code></td><td>Additional camo patterns</td></tr>
</table>
<h3>5.6 &nbsp;<code>Decal</code> &mdash; integer (faction emblem, 0&ndash;63)</h3>
<p>
Applied in <strong>both team and FFA modes.</strong>
Valid range: <code>0</code>&ndash;<code>63</code>. All values in range are accepted. Common values:
</p>
<table>
<tr><th>Value</th><th>Faction</th></tr>
<tr><td><code>0</code></td><td>None</td></tr>
<tr><td><code>2</code></td><td>Davion</td></tr>
<tr><td><code>3</code></td><td>Steiner</td></tr>
<tr><td><code>4</code></td><td>Kurita</td></tr>
<tr><td><code>5</code></td><td>Liao</td></tr>
<tr><td><code>6</code></td><td>Marik</td></tr>
<tr><td><code>9</code></td><td>ComStar</td></tr>
<tr><td><code>10</code></td><td>Star League</td></tr>
<tr><td><code>11</code></td><td>Wolf Clan</td></tr>
<tr><td><code>12</code></td><td>Jade Falcon</td></tr>
<tr><td><code>16</code></td><td>Ghost Bear</td></tr>
<tr><td><code>20</code></td><td>Hell Horses</td></tr>
<tr><td><code>21</code></td><td>Coyote</td></tr>
<tr><td><code>22</code></td><td>Blood Spirit</td></tr>
<tr><td><code>24</code></td><td>Ice Hellion</td></tr>
<tr><td><code>27</code></td><td>Goliath Scorpion</td></tr>
<tr><td><code>46</code></td><td>BKG</td></tr>
<tr><td><code>47</code></td><td>FSA</td></tr>
<tr><td><code>48</code></td><td>331st</td></tr>
<tr><td><code>49</code></td><td>DDC</td></tr>
<tr><td><code>50</code></td><td>VGL</td></tr>
</table>
<hr>
<h2>6. Complete Example File</h2>
<pre><span class="section">[mission]</span>
<span class="key">MissionName</span>=<span class="value">ScarabStronghold - Attrition</span>
<span class="key">GameType</span>=<span class="value">2</span>
<span class="key">Visibility</span>=<span class="value">0</span>
<span class="key">Weather</span>=<span class="value">0</span>
<span class="key">TimeOfDay</span>=<span class="value">0</span>
<span class="key">TimeLimit</span>=<span class="value">7</span>
<span class="key">Radar</span>=<span class="value">0</span>
<span class="key">HeatOn</span>=<span class="value">0</span>
<span class="key">FriendlyFire</span>=<span class="value">0</span>
<span class="key">SplashDamage</span>=<span class="value">0</span>
<span class="key">UnlimitedAmmo</span>=<span class="value">1</span>
<span class="key">WeaponJam</span>=<span class="value">0</span>
<span class="key">AdvanceMode</span>=<span class="value">0</span>
<span class="key">ArmorMode</span>=<span class="value">0</span>
<span class="section">[slot0]</span>
<span class="key">Type</span>=<span class="value">1</span>
<span class="key">PilotName</span>=<span class="value">Blackjack</span>
<span class="key">Mech</span>=<span class="value">Atlas</span>
<span class="key">Team</span>=<span class="value">0</span>
<span class="key">Skin</span>=<span class="value">0</span>
<span class="key">Decal</span>=<span class="value">3</span>
<span class="section">[slot1]</span>
<span class="key">Type</span>=<span class="value">1</span>
<span class="key">PilotName</span>=<span class="value">Viper</span>
<span class="key">Mech</span>=<span class="value">Mad Cat</span>
<span class="key">Team</span>=<span class="value">0</span>
<span class="key">Skin</span>=<span class="value">1</span>
<span class="key">Decal</span>=<span class="value">4</span>
<span class="section">[slot2]</span>
<span class="key">Type</span>=<span class="value">2</span>
<span class="key">PilotName</span>=
<span class="key">Mech</span>=<span class="value">Thor</span>
<span class="key">Team</span>=<span class="value">1</span>
<span class="key">Skin</span>=<span class="value">1</span>
<span class="key">Decal</span>=<span class="value">4</span>
<span class="section">[slot3]</span>
<span class="key">Type</span>=<span class="value">2</span>
<span class="key">PilotName</span>=
<span class="key">Mech</span>=<span class="value">Warhammer</span>
<span class="key">Team</span>=<span class="value">1</span>
<span class="key">Skin</span>=<span class="value">1</span>
<span class="key">Decal</span>=<span class="value">4</span></pre>
<hr>
<h2>7. Known Constraints and Caveats</h2>
<ol>
<li>
<strong>Slots must be numbered contiguously from <code>[slot0]</code>.</strong>
The parser uses a loop that breaks on the first missing section. Defining <code>[slot0]</code>
and <code>[slot2]</code> without <code>[slot1]</code> means only slot 0 is processed.
</li>
<li>
<strong>All current slots are cleared before file slots are applied.</strong>
Any slot not defined in the file (or defined with <code>Type=0</code>) will be empty in the lobby.
</li>
<li>
<strong>Team vs. FFA display is correct on the first click</strong> provided <code>TeamAllowed=</code> is set correctly in the file.
The game updates <code>team_allowed</code> synchronously from this field before processing slots.
If <code>TeamAllowed</code> is absent, it defaults to <code>0</code> (FFA) regardless of <code>GameType</code>.
</li>
<li>
<strong><code>MissionName</code> and <code>Mech</code> matches are case-sensitive.</strong>
<code>atlas</code> will not match <code>Atlas</code>.
</li>
<li>
<strong>The file is never deleted by the game.</strong>
Write it before the match, overwrite it before the next match. No locking is performed.
</li>
<li>
<strong><code>options.ini</code> is read only at game startup.</strong>
Changing the <code>automaticfile</code> path or enabling/disabling the feature requires a restart.
The config file itself is re-read fresh on every Load File button click.
</li>
<li>
<strong>Buffer limits:</strong> <code>PilotName</code> &le; 63 chars (truncated silently);
<code>MissionName</code> &le; 255 chars;
<code>automaticfile</code> path &le; 259 chars (<code>MAX_PATH&nbsp;&minus;&nbsp;1</code>).
</li>
<li>
<strong>Mech names are sourced from <code>MechTable.tbl</code>.</strong>
If the game installation has custom mechs added or some mechs removed, the valid name list
changes accordingly. The table above reflects the standard Firestorm build.
</li>
</ol>
<hr>
<p style="font-size:9pt; color:#888; text-align:center; margin-top:30px;">
Generated from source: <code>MW4Shell.cpp</code> &mdash; <code>CTCL_LoadAutoFile</code>,
<code>CTCL_GetAutoSlotName/Mech/Int</code> &nbsp;|&nbsp;
<code>ConLobby.script</code> &mdash; <code>o_load_file</code> handler &nbsp;|&nbsp;
<code>ConLobbyMission.script</code> &mdash; <code>MAIL_LOAD_AUTO_MISSION</code> &nbsp;|&nbsp;
Commit cd2fe73c &nbsp;|&nbsp; 2026-07-24
</p>
</body>
</html>
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""Generate special_weapon_locations.csv and rear_facing_weapons.csv from stock mech subsystems."""
import csv
from pathlib import Path
BASE = Path("Gameleap/mw4/Content")
MECH_TABLE = BASE / "Tables" / "MechTable.tbl"
OUT_DIR = Path("BTFrstrm")
def parse_ini(path):
sections, cur_sec, cur_dict = [], None, {}
with open(path, 'r', encoding='latin-1', errors='replace') as f:
for raw in f:
line = raw.strip()
if line.startswith('[') and line.endswith(']'):
if cur_sec is not None:
sections.append((cur_sec, cur_dict))
cur_sec, cur_dict = line[1:-1], {}
elif '=' in line and cur_sec is not None:
k, _, v = line.partition('=')
cur_dict[k.strip()] = v.strip()
if cur_sec is not None:
sections.append((cur_sec, cur_dict))
return sections
def find_dir_ci(parent, name):
"""Case-insensitive directory lookup for Linux."""
exact = parent / name
if exact.exists():
return exact
name_lower = name.lower()
for child in parent.iterdir():
if child.is_dir() and child.name.lower() == name_lower:
return child
return None
def weapon_model(model_path):
return Path(model_path.replace('\\', '/')).stem
# Read chassis list from MechTable.tbl
chassis_list = []
with open(MECH_TABLE, 'r', encoding='latin-1') as f:
for raw in f:
line = raw.strip()
if not line or line.startswith('//') or line.startswith('[') or '=' not in line:
continue
name, _, rel = line.partition('=')
# rel is like "Mechs\Atlas\Atlas.instance"
parts = rel.strip().replace('\\', '/').split('/')
chassis_list.append((name.strip(), parts)) # parts = ['Mechs', 'Atlas', 'Atlas.instance']
print(f"Found {len(chassis_list)} chassis in MechTable.tbl")
HDR = ['Chassis', 'InternalLocation', 'Model', 'Site', 'AmmoCount', 'GroupIndex', 'WeaponFacing']
special_rows, rear_rows, missing = [], [], []
mechs_dir = BASE / "Mechs"
for chassis_name, path_parts in chassis_list:
# path_parts[1] is the mech directory name (e.g. 'Atlas', 'MadCat_MkII', 'Urbanmech')
if len(path_parts) < 2:
missing.append(chassis_name)
continue
mech_dir = find_dir_ci(mechs_dir, path_parts[1])
if not mech_dir:
missing.append(f"{chassis_name} (dir not found: {path_parts[1]})")
continue
subs = [f for f in mech_dir.glob('*.subsystems') if f.suffix == '.subsystems']
if not subs:
missing.append(f"{chassis_name} (no .subsystems in {mech_dir.name})")
continue
subsys_file = subs[0]
sections = parse_ini(subsys_file)
for sec_name, fields in sections:
if 'WeaponSubsystem' not in fields.get('Model', ''):
continue
loc = fields.get('InternalLocation', '')
model = weapon_model(fields.get('Model', ''))
site = fields.get('Site', '')
ammo = fields.get('AmmoCount', '')
group = fields.get('GroupIndex', '')
facing = fields.get('WeaponFacing', '')
if loc in ('Special1', 'Special2'):
special_rows.append([chassis_name, loc, model, site, ammo, group, facing])
if facing and facing != '0':
rear_rows.append([chassis_name, loc, model, site, ammo, group, facing])
# Write CSVs
out1 = OUT_DIR / 'special_weapon_locations.csv'
with open(out1, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(HDR)
w.writerows(special_rows)
out2 = OUT_DIR / 'rear_facing_weapons.csv'
with open(out2, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(HDR)
w.writerows(rear_rows)
print(f"Spreadsheet 1 ? Special1/2 weapon locations: {len(special_rows)} rows -> {out1}")
print(f"Spreadsheet 2 ? Non-forward WeaponFacing: {len(rear_rows)} rows -> {out2}")
if missing:
print(f"\nSkipped: {missing}")
+360
View File
@@ -292,3 +292,363 @@ At the time this doc was written:
- non-playable mechs are explicitly marked `No`
- Templar and other mechs with rear or side mounts have facing visible in the CSV
- the file is intended to be a living audit artifact for future data cleanup and enablement work
---
## MechEditor Web App — Data Sources, Fields, and Conversions (documented 2026-07-23)
The MechEditor is a single-file Python HTTP server at `/home/rich/Repositories/MechEditor/mech_editor.py`.
It runs at `localhost:8765`, parses the firestorm content tree, and presents a full mech configuration editor in the browser.
This section documents every data field it reads, every conversion it performs, and every naming rule it enforces.
---
### Data Files Parsed Per Mech
All files live under `Gameleap/mw4/Content/Mechs/<MechDir>/`.
#### `.data` file (`parse_data()`)
| Field in file | Editor key | Notes |
|---|---|---|
| `VehicleTonnage` | `tonnage` | chassis base tonnage (float) |
| `MaxVehicleTonnage` | `max_tonnage` | max loadout tonnage (float) |
| `TechType` | `tech` | `$(Tech_IS)` ? `"IS"`, `$(Tech_Clan)` ? `"Clan"` |
| `MaxHeat` | `max_heat` | heat capacity (int) |
| `VehicleTradeValue` | `trade_value` | C-bills |
| `DragoonValue` | `dragoon` | used in Power Rating bar scaling |
| `MinMaxSpeed` | `min_max_speed` | base speed ceiling in **m/s** |
| `MaxSpeed` | `max_speed` | absolute speed ceiling in **m/s** (engine upgrades may not exceed this) |
| `FullStopTurnRate` | `full_stop_turn` | turn rate at zero speed, in **degrees/sec** |
| `TopSpeedTurnRate` | `top_speed_turn` | turn rate at top speed, in **degrees/sec** |
| `Acceleration` | `acceleration` | forward acceleration in **m/s²** |
| `Decceleration` | `decceleration` | forward braking in **m/s²****double-c spelling is canonical in the engine source** |
| `ReverseAccelerationMultiplier` | `rev_accel_mult` | multiplier applied to Acceleration for reverse |
| `ReverseDeccelerationMultiplier` | `rev_decel_mult` | multiplier applied to Decceleration for reverse — double-c canonical |
| `MinStandTransitionSpeed` | not surfaced | animation threshold only — see note below |
| `CanLoadJumpJets` | `can_jj` | Yes/No |
| `CanLoadECM` | `can_ecm` | Yes/No |
| `CanLoadBeagle` | `can_bap` | Yes/No |
| `CanLoadLightAmp` | `can_lightamp` | Yes/No |
| `CanLoadAMS` | `can_ams` | Yes/No |
| `CanLoadLAMS` | `can_lams` | Yes/No |
| `CanLoadIFF_Jammer` | `can_iff` | Yes/No |
**MinStandTransitionSpeed** is the speed (m/s) below which the mech switches from its walking animation to its idle/standing animation.
It is NOT a hard movement limit. It is purely an animation state machine threshold in `Mech.cpp`.
Code path: `animStateEngine?RequestState(StandState)` when `currentSpeedMPS <= minStandTransitionSpeed`.
Must be > 0 (validated in `Vehicle_Tool.cpp`). Argus = 12.631 m/s = 45.5 kph.
#### `.instance` file (`parse_instance()`)
| Field | Editor key | Notes |
|---|---|---|
| `PowerRating` | `PowerRating` | mechlab bar value, 0100 |
| `ArmorRating` | `ArmorRating` | mechlab bar value, 0100 |
| `SpeedRating` | `SpeedRating` | mechlab bar value, 0100 |
| `HeatRating` | `HeatRating` | mechlab bar value, 0100 |
| `DoesHaveLightAmp` | `has_lightamp` | 0 or 1, default 1 — whether LightAmp is currently installed |
#### `.subsystems` file (`parse_subsystems()`)
Provides: armor type + per-zone multipliers, installed heatsinks, jump jets, engine upgrades, weapons, electronics.
**Armor block:**
- `ArmorType=` ? armor type string (`Standard`, `FerroFiberus`, `Reactive`, `Reflective`, `Solarian`)
- Per-zone entries: `LeftArm=1.0`, `RightTorso=2.5`, etc. — multiplier for that zone
**Engine:**
- `EngineUpgrade` blocks counted ? `engine_upgrades` (05)
**Weapons** — each weapon block contains:
- `Model=` ? weapon subsystem resource path (name extracted)
- `InternalLocation=` ? zone name
- `Site=` ? mount port name (from armature)
- `GroupIndex=` ? weapon group number
- `WeaponFacing=` ? 0=Front, 1=Rear, 2=Side (absent = Front)
- `AmmoCount=` ? rounds for ammo-using weapons
- `EjectSite=` ? optional ejection site for ammo
**Electronics:** ECM, Beagle (BAP), AMS, LAMS, IFF_Jammer detected by subsystem model path.
#### `.damage` file (`parse_damage()`)
Per zone section `[ZoneInternal]`:
- `BaseArmorValue` ? starting armor (float)
- `MaxArmorValue` ? hard cap on armor pts for that zone
- `InternalHPValue` ? internal structure HP
- `OmniSlots`, `BeamSlots`, `MissileSlots`, `ProjectileSlots` ? weapon slot counts
Special zones:
- `SpecialAttachedToZone=` ? which body section this special zone is attached to
- `DamagePropagationZone=` ? where overflow damage propagates
#### `.engine` file (`parse_engine()`)
| Field | Editor key | Notes |
|---|---|---|
| `NumHeatSinks` | `NumHeatSinks` | free heatsinks from engine (not in subsystems) |
| `TonsPerUpgrade` | `TonsPerUpgrade` | tonnage cost per engine upgrade tier |
| `MPSPerUpgrade` | `MPSPerUpgrade` | m/s speed gain per upgrade tier |
#### `.torso` file (`parse_torso()`)
| Field | Notes |
|---|---|
| `TwistSpeed` | torso horizontal rotation speed — may be a macro reference |
| `PitchSpeed` | torso vertical rotation speed — may be a macro reference |
| `TwistRadius` | max horizontal twist angle — may be a macro reference |
| `PitchRadius` | max vertical pitch angle — may be a macro reference |
| `ArmRatioAngle` | ratio of arm tracking vs torso rotation — may be a macro reference |
All torso fields may reference macros from `Content/Defines/MechTorso.defines`.
The editor resolves them using a preloaded `TORSO_DEFINES` dict. Notable values:
```
NORMAL_RATIO = 40
SNAIL_TSPEED = 40
NORMAL_TSPEED = 60
FAST_TSPEED = 80
WIDE_TRADIUS = 160
NORMAL_PRADIUS = 40
```
---
### Calculated Ratings and Conversions
#### Speed (kph)
```
top_speed_kph = min(MinMaxSpeed + MPSPerUpgrade × engine_upgrades, MaxSpeed) × 3.6
```
- `MinMaxSpeed` and `MaxSpeed` from `.data` (m/s)
- `MPSPerUpgrade` from `.engine` (m/s per tier)
- `engine_upgrades` from `.subsystems` (05)
- Multiply by 3.6 to convert m/s ? kph
- Argus example: (20.28 + 1.11 × 10) × 3.6 = 113.5 kph
#### Speed Rating (bar)
```
speed_rating = (top_speed_mps / MaxSpeed) × 100 [capped at 100]
```
#### Turn Rate — degrees to radians
The `.data` file stores turn rates in **degrees/sec**. The mechlab UI label was changed to show rad/sec:
- `StringResource.rc`: `IDS_ML_CH_TURNRATE` ? `"Turn Rate (Top Speed Rad/Sec):"`
- Conversion: `radians = degrees × ?/180` where `?/180 ? 0.017453`
- Argus: FullStopTurn = 75° = **1.309 rad/sec**, TopSpeedTurn = 45° = **0.785 rad/sec**
#### Acceleration / Deceleration (m/s²)
Stored directly in `.data`. Reverse values are derived:
```
reverse_accel = Acceleration × ReverseAccelerationMultiplier
reverse_decel = Decceleration × ReverseDeccelerationMultiplier
```
**The double-c spelling (`Decceleration`, `ReverseDeccelerationMultiplier`) is canonical — it matches the engine source. Do not "fix" the spelling.**
#### Heat Rating (bar)
```
total_hs = NumHeatSinks (engine) + installed_heatsinks (subsystems)
effective_hs = total_hs × (2 if Double else 1)
heat_rating = (effective_hs / MaxHeat) × 100 [capped at 100]
```
#### Power Rating (bar)
```
total_damage = ? (DamageAmount × NumFire) for each installed weapon
power_rating = (total_damage / 80) × 100 [capped at 100]
```
- `DamageAmount` and `NumFire` come from `WeaponSubsystems/<weapon>.data` following `!include` chains
- Parsed by `load_weapon_damages()` at server startup, cached in `Handler.weapon_damages`
- Argus example with default load: ~36.2 total damage ? 45 rating (stored = 42)
#### Armor Rating (bar)
```
for each zone:
pts = multiplier × ARMOR_PTS_PER_TON[armor_type]
effective = min(pts, MaxArmorValue[zone])
armor_rating = (? effective / ? MaxArmorValue) × 100
```
Armor pts per ton by type (from `Adept/ResourceImagePool.cpp` and game design):
| Type | Pts/ton |
|---|---|
| Standard | 32 |
| FerroFiberus | 38 |
| Reactive | 30 |
| Reflective | 30 |
| Solarian | 60 |
Note: `FerroFiberus` is the canonical internal token (not player-visible). The player sees `DNL_FERROFIB = "Ferro Fibrous"` via string lookup.
---
### Active-in-Game Detection
Source: `Gameleap/mw4/Content/Tables/MechChassisTable.tbl`
Format:
```
DisplayKey=Mechs\DirName\FileName.data
//CommentedKey=Mechs\DirName\FileName.data <- inactive
```
- Active = entry exists AND is not prefixed with `//`
- Currently the only inactive mech: **Dasher** (commented out)
- The editor shows a green **ACTIVE IN GAME** or red **NOT IN GAME** banner at top of Stats tab
---
### hsh/ Image Naming Conventions
The `hsh/` directory under `Gameleap/mw4/` holds loose BMP files loaded at runtime (not packed into `.mw4`).
There are four relevant subdirectories, each with a different naming authority.
#### `hsh/hud/` — in-game HUD damage silhouette (own mech)
#### `hsh/MFD/` — MFD target display silhouette (target mech)
#### `hsh/radar/hud/` — radar damage overlay
**All three use identical stems** sourced from `huddamage.cpp` `texturename[]` array.
Load path:
- hud/MFD: `hsh\<texturename>.bmp` where texturename = `hud\<stem>` ? file = `hsh/hud/<stem>.bmp`
- radar: `hsh\radar\<texturename>.bmp` ? file = `hsh/radar/hud/<stem>.bmp`
Code: `render.cpp` `CRadar_Device::LoadRadarDamageTexture()` and `huddamage.cpp` `HUDDamage`.
#### `hsh/Mechs/` — mw4print scorecard portrait
Load path: `recscore.cpp` ? `GetLocString(model->m_nameIndex)` ? `DNL_*` string from `StringResource.rc` ? lowercased filename.
Different naming authority from the other three.
---
### Complete Canonical Name Table
Key: mech directory name (case-insensitive) ? canonical stem for `hsh/hud/`, `hsh/MFD/`, `hsh/radar/hud/`.
Entries in **bold** differ from the directory name.
| Directory | hud/MFD/radar stem | hsh/Mechs/ portrait filename |
|---|---|---|
| Annihilator | annihilator | annihilator.bmp |
| Archer | archer | archer.bmp |
| ArcticWolf | arcticwolf | arctic wolf.bmp |
| Ares | ares | ares.bmp |
| Argus | argus | argus.bmp |
| Assassin2 | assassin2 | **assassin ii.bmp** |
| Atlas | atlas | atlas.bmp |
| Avatar | avatar | avatar.bmp |
| Awesome | awesome | awesome.bmp |
| Battlemaster | battlemaster | battlemaster.bmp |
| Battlemaster2c | **battlemasteriic** | **battlemaster iic.bmp** |
| Behemoth | behemoth | behemoth.bmp |
| Behemoth2 | **behemothii** | **behemoth ii.bmp** |
| Blackhawk | blackhawk | black hawk.bmp |
| Blacknight | **blackknight** | black knight.bmp |
| Blacklanner | blacklanner | black lanner.bmp |
| Brigand | brigand | brigand.bmp |
| Bushwacker | bushwacker | bushwacker.bmp |
| Catapult | catapult | catapult.bmp |
| CauldronBorn | cauldronborn | **cauldronborn.bmp** (table key has hyphen; DNL does not) |
| Chimera | chimera | chimera.bmp |
| Commando | commando | commando.bmp |
| Cougar | cougar | cougar.bmp |
| Cyclops | cyclops | cyclops.bmp |
| Daishi | daishi | daishi.bmp |
| Deimos | deimos | deimos.bmp |
| Dragon | dragon | dragon.bmp |
| Fafnir | fafnir | fafnir.bmp |
| Flea | flea | flea.bmp |
| Gladiator | gladiator | gladiator.bmp |
| Grizzly | grizzly | grizzly.bmp |
| Hauptmann | hauptmann | hauptmann.bmp |
| Hellhound | hellhound | hellhound.bmp |
| Hellspawn | hellspawn | hellspawn.bmp |
| Highlander | highlander | highlander.bmp |
| Hollander | **hollanderii** | **hollander ii.bmp** |
| Hunchback | hunchback | hunchback.bmp |
| Kodiak | kodiak | kodiak.bmp |
| Loki | loki | loki.bmp |
| Longbow | longbow | longbow.bmp |
| Madcat | madcat | mad cat.bmp |
| Madcat_MKII | **madcat2** | mad cat mkii.bmp |
| Masakari | masakari | masakari.bmp |
| Mauler | mauler | mauler.bmp |
| Novacat | novacat | nova cat.bmp |
| Osiris | osiris | osiris.bmp |
| Owens | owens | owens.bmp |
| Puma | puma | puma.bmp |
| Raven | raven | raven.bmp |
| Rifleman | rifleman | rifleman.bmp |
| Ryoken | ryoken | ryoken.bmp |
| Shadowcat | shadowcat | shadow cat.bmp |
| Solitaire | solitaire | solitaire.bmp |
| Sunder | sunder | sunder.bmp |
| Templar | templar | templar.bmp |
| Thanatos | thanatos | thanatos.bmp |
| Thor | thor | thor.bmp |
| Uller | uller | uller.bmp |
| Urbanmech | urbanmech | urbanmech.bmp |
| Uziel | uziel | uziel.bmp |
| Victor | victor | victor.bmp |
| Vulture | vulture | vulture.bmp |
| Warhammer | warhammer | warhammer.bmp |
| Wolfhound | wolfhound | wolfhound.bmp |
| Zeus | zeus | zeus.bmp |
**Critical mismatches where directory name ? hud/MFD stem (files must use the stem, not the dir name):**
| Directory | Wrong name (dir-based) | Correct name (stem) |
|---|---|---|
| Battlemaster2c | battlemaster2c.bmp | **battlemasteriic.bmp** |
| Behemoth2 | behemoth2.bmp | **behemothii.bmp** |
| Blacknight | blacknight.bmp | **blackknight.bmp** |
| Hollander | hollander.bmp | **hollanderii.bmp** |
| Madcat_MKII | madcat_mkii.bmp | **madcat2.bmp** |
**Portrait mismatches for hsh/Mechs/ (table key ? DNL string):**
The `MechChassisTable.tbl` display key and `GetLocString()` DNL string differ for these mechs.
mw4print uses the DNL string. The table key is NOT the correct portrait filename for these 5 mechs.
| Directory | Table key (wrong for mw4print) | DNL string (correct portrait stem) |
|---|---|---|
| Assassin2 | AssassinII ? assassinii | `DNL_ASSASSIN2 "Assassin II"` ? **assassin ii.bmp** |
| Battlemaster2c | BattlemasterIIC ? battlemasteriic | `DNL_BATTLEMASTERIIC "Battlemaster IIc"` ? **battlemaster iic.bmp** |
| Behemoth2 | BehemothII ? behemothii | `DNL_BEHEMOTHII "Behemoth II"` ? **behemoth ii.bmp** |
| CauldronBorn | Cauldron-Born ? cauldron-born | `DNL_CAULDRONBORN "Cauldronborn"` ? **cauldronborn.bmp** |
| Hollander | HollanderII ? hollanderii | `DNL_HOLLANDERII "Hollander II"` ? **hollander ii.bmp** |
---
### MechEditor Implementation Notes
The editor encodes all of the above knowledge in two Python dicts:
**`MECH_HSH_STEMS`** (in `mech_editor.py`)
Maps lowercase dir name ? canonical stem for `hsh/hud/`, `hsh/MFD/`, `hsh/radar/hud/`.
Source: `huddamage.cpp` `texturename[]` array.
**`MECH_PORTRAIT_OVERRIDES`** (in `mech_editor.py`)
Maps lowercase dir name ? portrait stem for `hsh/Mechs/` where the DNL string differs from the chassis table key.
Source: `DNL_*` entries in `Gameleap/code/mw4/Code/scriptstrings/StringResource.rc`.
For all other mechs, the portrait stem is derived dynamically from `MechChassisTable.tbl` (display key, lowercased).
The Assets tab in the editor shows all four image types. When an image is missing, it displays:
```
Wants: hsh/<subdir>/<expected_filename>.bmp
```
so the user knows exactly what to rename or create.
+64
View File
@@ -0,0 +1,64 @@
Chassis,InternalLocation,Model,Site,AmmoCount,GroupIndex,WeaponFacing
Archer,LeftTorso,LRM15,site_ldmissile,24,2,2
Archer,RightTorso,LRM15,site_rdmissile,24,2,2
Ares,Special2,ClanLRM10,site_lmissileport,24,2,2
Ares,Special2,ClanLRM10,site_missileport,24,2,2
Ares,Special2,ClanLRM10,site_rmissileport,24,2,2
AssassinII,Special1,LRM5,site_missileport,24,2,2
Atlas,LeftTorso,LRM20,site_lmissileport,12,2,2
Avatar,LeftTorso,LRM5,site_lmissileport,24,2,2
Avatar,RightTorso,LRM5,site_rmissileport,24,2,2
Avatar,LeftTorso,LRM10,site_lmissileport,24,2,2
Avatar,RightTorso,LRM10,site_rmissileport,24,2,2
Awesome,CenterTorso,LRM5,site_ctorsoport,24,2,2
Battlemaster,RightTorso,MediumLaser,site_rdtorsoport,,1,1
Battlemaster,LeftTorso,MediumLaser,site_ldtorsoport,,1,1
BattlemasterIIC,RightTorso,ClanERMediumLaser,site_rdtorsoport,,1,1
BattlemasterIIC,LeftTorso,ClanERMediumLaser,site_ldtorsoport,,1,1
Black Lanner,Special2,ClanSRM6,site_rmissileport,15,2,2
Black Lanner,Special1,ClanLRM10,site_lmissileport,24,2,2
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Catapult,LeftArm,LRM20,site_lmissileport,12,2,2
Catapult,RightArm,LRM20,site_rmissileport,12,2,2
Cauldron-Born,Special1,ClanLRM10,site_rmissileport,24,2,2
Cauldron-Born,Special2,ClanLRM10,site_lmissileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Chimera,RightTorso,LRM5,site_missileport,24,2,2
Cyclops,LeftTorso,LRM10,site_lmissile,24,2,2
Cougar,LeftTorso,ClanLRM10,site_lmissileport,12,2,2
Cougar,RightTorso,ClanLRM10,site_rmissileport,12,2,2
Deimos,Special1,ClanLRM15,site_lmissileport,16,2,2
Deimos,Special2,ClanLRM15,site_rmissileport,16,2,2
Dragon,CenterTorso,LRM10,site_missleport,24,2,2
Flea,LeftTorso,SmallLaser,site_ltorsoport,,1,1
Flea,RightTorso,SmallLaser,site_rtorsoport,,1,1
Grizzly,LeftTorso,CLANLRM10,site_missleport,24,2,2
Hellhound,LeftTorso,ClanLRM10,site_missileport,12,2,2
Mad Cat,Special1,ClanLRM10,site_lmissileport,24,2,2
Mad Cat,Special2,ClanLRM10,site_rmissileport,24,2,2
Mad Cat MKII,Special2,ClanLRM15,site_lmissileport,16,2,2
Mad Cat MKII,Special1,ClanLRM15,site_rmissileport,16,2,2
Masakari,LeftTorso,ClanLRM10,site_lmissleport,24,2,2
Masakari,LeftTorso,ClanLRM10,site_ltorsoport,24,2,2
Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2
Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2
Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2
Mauler,RightTorso,LRM5,site_rmissileport,24,2,2
Mauler,RightTorso,LRM5,site_rmissileport,24,2,2
Mauler,RightTorso,LRM5,site_rmissileport,24,2,2
Raven,RightTorso,LRM5,site_rtorsoport,24,2,2
Sunder,LeftTorso,MediumLaser,site_ldtorsoport,,1,1
Sunder,RightTorso,MediumLaser,site_rdtorsoport,,1,1
Templar,LeftTorso,MediumPulseLaser,site_ldrtorsoport,,1,1
Templar,RightTorso,MediumPulseLaser,site_rdrtorsoport,,1,1
Thor,Special1,CLANLRM10,site_missileport,24,2,2
Uziel,Special1,LRM10,site_missileport,24,2,2
Vulture,LeftTorso,ClanLRM5,site_lmissileport,24,2,2
Vulture,RightTorso,ClanLRM5,site_rmissileport,24,2,2
Vulture,LeftTorso,ClanLRM10,site_lmissileport,24,2,2
Vulture,RightTorso,ClanLRM10,site_rmissileport,24,2,2
Wolfhound,CenterTorso,ClanERMediumLaser,site_reartorsoport,,1,1
Zeus,RightTorso,MediumPulseLaser,site_reartorsoport,,1,1
1 Chassis InternalLocation Model Site AmmoCount GroupIndex WeaponFacing
2 Archer LeftTorso LRM15 site_ldmissile 24 2 2
3 Archer RightTorso LRM15 site_rdmissile 24 2 2
4 Ares Special2 ClanLRM10 site_lmissileport 24 2 2
5 Ares Special2 ClanLRM10 site_missileport 24 2 2
6 Ares Special2 ClanLRM10 site_rmissileport 24 2 2
7 AssassinII Special1 LRM5 site_missileport 24 2 2
8 Atlas LeftTorso LRM20 site_lmissileport 12 2 2
9 Avatar LeftTorso LRM5 site_lmissileport 24 2 2
10 Avatar RightTorso LRM5 site_rmissileport 24 2 2
11 Avatar LeftTorso LRM10 site_lmissileport 24 2 2
12 Avatar RightTorso LRM10 site_rmissileport 24 2 2
13 Awesome CenterTorso LRM5 site_ctorsoport 24 2 2
14 Battlemaster RightTorso MediumLaser site_rdtorsoport 1 1
15 Battlemaster LeftTorso MediumLaser site_ldtorsoport 1 1
16 BattlemasterIIC RightTorso ClanERMediumLaser site_rdtorsoport 1 1
17 BattlemasterIIC LeftTorso ClanERMediumLaser site_ldtorsoport 1 1
18 Black Lanner Special2 ClanSRM6 site_rmissileport 15 2 2
19 Black Lanner Special1 ClanLRM10 site_lmissileport 24 2 2
20 Bushwacker Special1 LRM5 site_missileport 24 2 2
21 Bushwacker Special1 LRM5 site_missileport 24 2 2
22 Catapult LeftArm LRM20 site_lmissileport 12 2 2
23 Catapult RightArm LRM20 site_rmissileport 12 2 2
24 Cauldron-Born Special1 ClanLRM10 site_rmissileport 24 2 2
25 Cauldron-Born Special2 ClanLRM10 site_lmissileport 24 2 2
26 Chimera RightTorso LRM5 site_missileport 24 2 2
27 Chimera RightTorso LRM5 site_missileport 24 2 2
28 Chimera RightTorso LRM5 site_missileport 24 2 2
29 Chimera RightTorso LRM5 site_missileport 24 2 2
30 Cyclops LeftTorso LRM10 site_lmissile 24 2 2
31 Cougar LeftTorso ClanLRM10 site_lmissileport 12 2 2
32 Cougar RightTorso ClanLRM10 site_rmissileport 12 2 2
33 Deimos Special1 ClanLRM15 site_lmissileport 16 2 2
34 Deimos Special2 ClanLRM15 site_rmissileport 16 2 2
35 Dragon CenterTorso LRM10 site_missleport 24 2 2
36 Flea LeftTorso SmallLaser site_ltorsoport 1 1
37 Flea RightTorso SmallLaser site_rtorsoport 1 1
38 Grizzly LeftTorso CLANLRM10 site_missleport 24 2 2
39 Hellhound LeftTorso ClanLRM10 site_missileport 12 2 2
40 Mad Cat Special1 ClanLRM10 site_lmissileport 24 2 2
41 Mad Cat Special2 ClanLRM10 site_rmissileport 24 2 2
42 Mad Cat MKII Special2 ClanLRM15 site_lmissileport 16 2 2
43 Mad Cat MKII Special1 ClanLRM15 site_rmissileport 16 2 2
44 Masakari LeftTorso ClanLRM10 site_lmissleport 24 2 2
45 Masakari LeftTorso ClanLRM10 site_ltorsoport 24 2 2
46 Mauler LeftTorso LRM5 site_lmissileport 24 2 2
47 Mauler LeftTorso LRM5 site_lmissileport 24 2 2
48 Mauler LeftTorso LRM5 site_lmissileport 24 2 2
49 Mauler RightTorso LRM5 site_rmissileport 24 2 2
50 Mauler RightTorso LRM5 site_rmissileport 24 2 2
51 Mauler RightTorso LRM5 site_rmissileport 24 2 2
52 Raven RightTorso LRM5 site_rtorsoport 24 2 2
53 Sunder LeftTorso MediumLaser site_ldtorsoport 1 1
54 Sunder RightTorso MediumLaser site_rdtorsoport 1 1
55 Templar LeftTorso MediumPulseLaser site_ldrtorsoport 1 1
56 Templar RightTorso MediumPulseLaser site_rdrtorsoport 1 1
57 Thor Special1 CLANLRM10 site_missileport 24 2 2
58 Uziel Special1 LRM10 site_missileport 24 2 2
59 Vulture LeftTorso ClanLRM5 site_lmissileport 24 2 2
60 Vulture RightTorso ClanLRM5 site_rmissileport 24 2 2
61 Vulture LeftTorso ClanLRM10 site_lmissileport 24 2 2
62 Vulture RightTorso ClanLRM10 site_rmissileport 24 2 2
63 Wolfhound CenterTorso ClanERMediumLaser site_reartorsoport 1 1
64 Zeus RightTorso MediumPulseLaser site_reartorsoport 1 1
+45
View File
@@ -0,0 +1,45 @@
Chassis,InternalLocation,Model,Site,AmmoCount,GroupIndex,WeaponFacing
Ares,Special1,ClanERSmallLaser,site_lgunport,,1,
Ares,Special1,ClanERSmallLaser,site_rgunport,,1,
Ares,Special2,ClanLRM10,site_lmissileport,24,2,2
Ares,Special2,ClanLRM10,site_missileport,24,2,2
Ares,Special2,ClanLRM10,site_rmissileport,24,2,2
AssassinII,Special1,LRM5,site_missileport,24,2,2
Atlas,Special1,AC20,site_rtorsoport,10,1,
Atlas,Special2,SRM6,site_llmissile,15,2,
Avatar,Special1,MachineGun,site_ltorsoport,200,1,
Avatar,Special1,MachineGun,site_rtorsoport,200,1,
Battlemaster,Special2,PPC,site_rdgunport,,1,
Battlemaster,Special1,SRM6,site_missile,15,2,
BattlemasterIIC,Special1,ClanSSRM6,site_missile,15,2,
Behemoth,Special1,LargeLaser,site_ctorsoport,,1,
BehemothII,Special1,LargeLaser,site_ctorsoport,,1,
Black Lanner,Special2,ClanSRM6,site_rmissileport,15,2,2
Black Lanner,Special1,ClanLRM10,site_lmissileport,24,2,2
Brigand,Special2,MediumLaser,site_ltorsoport,,1,
Brigand,Special1,MediumLaser,site_rtorsoport,,1,
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Bushwacker,Special1,LRM5,site_missileport,24,2,2
Cauldron-Born,Special1,ClanLRM10,site_rmissileport,24,2,2
Cauldron-Born,Special2,ClanLRM10,site_lmissileport,24,2,2
Cyclops,Special1,MediumPulseLaser,site_gunport,,1,
Deimos,Special1,ClanLRM15,site_lmissileport,16,2,2
Deimos,Special2,ClanLRM15,site_rmissileport,16,2,2
Hauptmann,Special1,ClanUltraAC20,site_rutorsoport,16,1,
Hellspawn,Special1,SSRM2,site_rmissileport,50,2,
HollanderII,Special2,SmallPulseLaser,site_cltorsoport,,1,
HollanderII,Special2,SmallPulseLaser,site_crtorsoport,,1,
HollanderII,Special1,GaussRifle,site_rtorsoport,16,1,
Hunchback,Special1,AC10,site_rutorsoport,20,1,
Loki,Special1,ClanSSRM6,site_missileport,15,2,
Mad Cat,Special1,ClanLRM10,site_lmissileport,24,2,2
Mad Cat,Special2,ClanLRM10,site_rmissileport,24,2,2
Mad Cat MKII,Special2,ClanLRM15,site_lmissileport,16,2,2
Mad Cat MKII,Special1,ClanLRM15,site_rmissileport,16,2,2
Rifleman,Special1,MediumLaser,site_ltorsoport,,1,
Rifleman,Special1,MediumLaser,site_rtorsoport,,1,
Solitaire,Special1,ClanERLargeLaser,site_rtorsoport,,1,
Thor,Special1,CLANLRM10,site_missileport,24,2,2
Uziel,Special1,LRM10,site_missileport,24,2,2
Vulture,Special1,ClanMachineGun,site_ltorsoport,200,1,
Vulture,Special1,ClanMachineGun,site_rtorsoport,200,1,
1 Chassis InternalLocation Model Site AmmoCount GroupIndex WeaponFacing
2 Ares Special1 ClanERSmallLaser site_lgunport 1
3 Ares Special1 ClanERSmallLaser site_rgunport 1
4 Ares Special2 ClanLRM10 site_lmissileport 24 2 2
5 Ares Special2 ClanLRM10 site_missileport 24 2 2
6 Ares Special2 ClanLRM10 site_rmissileport 24 2 2
7 AssassinII Special1 LRM5 site_missileport 24 2 2
8 Atlas Special1 AC20 site_rtorsoport 10 1
9 Atlas Special2 SRM6 site_llmissile 15 2
10 Avatar Special1 MachineGun site_ltorsoport 200 1
11 Avatar Special1 MachineGun site_rtorsoport 200 1
12 Battlemaster Special2 PPC site_rdgunport 1
13 Battlemaster Special1 SRM6 site_missile 15 2
14 BattlemasterIIC Special1 ClanSSRM6 site_missile 15 2
15 Behemoth Special1 LargeLaser site_ctorsoport 1
16 BehemothII Special1 LargeLaser site_ctorsoport 1
17 Black Lanner Special2 ClanSRM6 site_rmissileport 15 2 2
18 Black Lanner Special1 ClanLRM10 site_lmissileport 24 2 2
19 Brigand Special2 MediumLaser site_ltorsoport 1
20 Brigand Special1 MediumLaser site_rtorsoport 1
21 Bushwacker Special1 LRM5 site_missileport 24 2 2
22 Bushwacker Special1 LRM5 site_missileport 24 2 2
23 Cauldron-Born Special1 ClanLRM10 site_rmissileport 24 2 2
24 Cauldron-Born Special2 ClanLRM10 site_lmissileport 24 2 2
25 Cyclops Special1 MediumPulseLaser site_gunport 1
26 Deimos Special1 ClanLRM15 site_lmissileport 16 2 2
27 Deimos Special2 ClanLRM15 site_rmissileport 16 2 2
28 Hauptmann Special1 ClanUltraAC20 site_rutorsoport 16 1
29 Hellspawn Special1 SSRM2 site_rmissileport 50 2
30 HollanderII Special2 SmallPulseLaser site_cltorsoport 1
31 HollanderII Special2 SmallPulseLaser site_crtorsoport 1
32 HollanderII Special1 GaussRifle site_rtorsoport 16 1
33 Hunchback Special1 AC10 site_rutorsoport 20 1
34 Loki Special1 ClanSSRM6 site_missileport 15 2
35 Mad Cat Special1 ClanLRM10 site_lmissileport 24 2 2
36 Mad Cat Special2 ClanLRM10 site_rmissileport 24 2 2
37 Mad Cat MKII Special2 ClanLRM15 site_lmissileport 16 2 2
38 Mad Cat MKII Special1 ClanLRM15 site_rmissileport 16 2 2
39 Rifleman Special1 MediumLaser site_ltorsoport 1
40 Rifleman Special1 MediumLaser site_rtorsoport 1
41 Solitaire Special1 ClanERLargeLaser site_rtorsoport 1
42 Thor Special1 CLANLRM10 site_missileport 24 2 2
43 Uziel Special1 LRM10 site_missileport 24 2 2
44 Vulture Special1 ClanMachineGun site_ltorsoport 200 1
45 Vulture Special1 ClanMachineGun site_rtorsoport 200 1
+144
View File
@@ -0,0 +1,144 @@
[mission]
MissionName=Coliseum - Attrition
GameType=2
TeamAllowed=0
Visibility=1
Weather=0
TimeOfDay=0
TimeLimit=15
Radar=0
HeatOn=0
FriendlyFire=1
SplashDamage=1
UnlimitedAmmo=0
WeaponJam=1
AdvanceMode=1
ArmorMode=1
[slot0]
Type=1
PilotName=Viper
Mech=Atlas
Team=0
Skin=0
Decal=3
[slot1]
Type=1
PilotName=Blackjack
Mech=Mad Cat
Team=0
Skin=1
Decal=4
[slot2]
Type=1
PilotName=Ghost
Mech=Warhammer
Team=0
Skin=2
Decal=2
[slot3]
Type=1
PilotName=Maverick
Mech=Thor
Team=0
Skin=3
Decal=5
[slot4]
Type=1
PilotName=Reaper
Mech=Loki
Team=0
Skin=4
Decal=6
[slot5]
Type=1
PilotName=Phoenix
Mech=Daishi
Team=0
Skin=5
Decal=9
[slot6]
Type=1
PilotName=Hammer
Mech=Shadow Cat
Team=0
Skin=6
Decal=10
[slot7]
Type=1
PilotName=Blade
Mech=Battlemaster
Team=0
Skin=7
Decal=11
[slot8]
Type=1
PilotName=Ironside
Mech=Cauldron-Born
Team=0
Skin=8
Decal=12
[slot9]
Type=1
PilotName=Tempest
Mech=Vulture
Team=0
Skin=9
Decal=3
[slot10]
Type=1
PilotName=Saber
Mech=Black Hawk
Team=0
Skin=10
Decal=16
[slot11]
Type=1
PilotName=Ajax
Mech=Archer
Team=0
Skin=11
Decal=49
[slot12]
Type=1
PilotName=Thunder
Mech=Kodiak
Team=0
Skin=12
Decal=2
[slot13]
Type=1
PilotName=Raven
Mech=Uziel
Team=0
Skin=13
Decal=50
[slot14]
Type=1
PilotName=Wraith
Mech=Grizzly
Team=0
Skin=14
Decal=48
[slot15]
Type=1
PilotName=Goliath
Mech=Annihilator
Team=0
Skin=15
Decal=5
+145
View File
@@ -0,0 +1,145 @@
[mission]
MissionName=CentralPark - Team KotH
GameType=6
TeamAllowed=1
TeamCount=4
Visibility=1
Weather=0
TimeOfDay=0
TimeLimit=15
Radar=0
HeatOn=0
FriendlyFire=1
SplashDamage=1
UnlimitedAmmo=0
WeaponJam=1
AdvanceMode=1
ArmorMode=1
[slot0]
Type=1
PilotName=Viper
Mech=Atlas
Team=0
Skin=0
Decal=3
[slot1]
Type=1
PilotName=Blackjack
Mech=Mad Cat
Team=0
Skin=0
Decal=3
[slot2]
Type=1
PilotName=Ghost
Mech=Warhammer
Team=0
Skin=0
Decal=3
[slot3]
Type=1
PilotName=Maverick
Mech=Thor
Team=0
Skin=0
Decal=3
[slot4]
Type=1
PilotName=Reaper
Mech=Loki
Team=1
Skin=1
Decal=4
[slot5]
Type=1
PilotName=Phoenix
Mech=Daishi
Team=1
Skin=1
Decal=4
[slot6]
Type=1
PilotName=Hammer
Mech=Shadow Cat
Team=1
Skin=1
Decal=4
[slot7]
Type=1
PilotName=Blade
Mech=Battlemaster
Team=1
Skin=1
Decal=4
[slot8]
Type=1
PilotName=Ironside
Mech=Cauldron-Born
Team=2
Skin=2
Decal=2
[slot9]
Type=1
PilotName=Tempest
Mech=Vulture
Team=2
Skin=2
Decal=2
[slot10]
Type=1
PilotName=Saber
Mech=Black Hawk
Team=2
Skin=2
Decal=2
[slot11]
Type=1
PilotName=Ajax
Mech=Archer
Team=2
Skin=2
Decal=2
[slot12]
Type=1
PilotName=Thunder
Mech=Kodiak
Team=3
Skin=3
Decal=5
[slot13]
Type=1
PilotName=Raven
Mech=Uziel
Team=3
Skin=3
Decal=5
[slot14]
Type=1
PilotName=Wraith
Mech=Grizzly
Team=3
Skin=3
Decal=5
[slot15]
Type=1
PilotName=Goliath
Mech=Annihilator
Team=3
Skin=3
Decal=5
+112
View File
@@ -795,6 +795,118 @@ keeping old-RIO (type 0) protocol behavior byte-identical:
alongside `mw4print.exe`.
- Version bumped to **2.0**, copyright year updated to **2026**.
## 📋 MFD mode 4: right device stagger fix (2026-07-18, eaa5fd3)
Root cause: `CMFD_Device::BeginScene()` cleared BOTH MFD device back-buffers at `sh_step==0`.
But in mode 4 the right MFD flip also fires at `sh_step==1` (= old `sh_step==0` after the
stagger increment), so it presented a just-cleared buffer — only the grid, no channel data.
Fix: `BeginScene()` now only clears/grids the LEFT device at `sh_step==0`. New `BeginSceneRight()`
(added to `render.cpp` / `render.hpp`, called from `WinMain.cpp`) clears/grids the RIGHT device at
`sh_step==1` — one frame AFTER the right flip — so channels 34 render into a fresh buffer before
the next flip. Mode 1 is unchanged.
Mode 4 cycle (with stagger):
- `sh_step 0`: radar + left `BeginScene` (clear + grid); no flip
- `sh_step 1`: flip right MFD (shows channels 34 from previous cycle) + right `BeginScene` (clear + grid)
- `sh_step 24`: channels 02 → left device
- `sh_step 56`: channels 34 → right device
- `sh_step 0` (next): flip radar + left MFD (shows channels 02 from previous cycle)
Files: `CoreTech/Libraries/GameOS/WinMain.cpp`, `render.cpp`, `render.hpp`.
## 📋 Linux→Windows development workflow (2026-07-19, 55b9bfc5)
`build-env/sync-to-windows.sh` — rsync script that pushes all source, content, toolchain, and
assets to the Windows build machine at `/vwe/firestorm`. Excludes generated build outputs
(`rel.bin/`, `dbg.bin/`, `*.mw4`, `*.dep`), `.git`/LFS objects, `_UNUSED/`, and the game deploy
dir (`MW4/`). Run from Linux before triggering a Windows build.
## 📋 ddraw.dll removed from repo; build script moves it aside (2026-07-19, 0ceba9c7, 24825ff3)
`Gameleap/mw4/ddraw.dll` (DDrawCompat) removed from git (was LFS-tracked); now lives as
`ddraw.dll.old` in the same dir for reference. `deploy-editor.ps1` installs DDrawCompat as
`ddraw.dll` at deploy time (the editor needs it for its windowed D3D7 viewport).
`build-resources.ps1` moves `ddraw.dll` aside (`.buildaside`) before running `MW4pro.exe`
because **DDrawCompat is fatal to MW4pro.exe in BOTH windowed and fullscreen modes**, then
restores it in `finally`. The same block also defensively covers dgVoodoo2 interceptors
(`D3D8/D3D9/D3DImm.dll`) in case they reappear.
## 📋 Branch `5.1.0b-in-progress`: multiplayer + RIO + source cleanup (2026-07-19)
### ConLobby V5.1.0b1 — Super6 mech rotation (c768f7c4)
Changes from Buddy 'Highlight' Taylor (MCHL), merged manually:
- Console version string bumped to **V5.1.0b1**.
- `ROOKIEMECH` defines expanded from 4 → **6 mechs**: added Archer (ID=1) and Warhammer (ID=62).
- 16-slot default assignments cycle through all 6 Super6 mechs.
- Right-click randomizer expanded from `random(0,3)``random(0,5)`.
### CRIOMAIN.CPP — Korean translation + RIO poll timeout scaling (16fca6c4, a712002f)
- Translated all EUC-KR/CP949 Korean developer comments (~35 lines) to English. File re-saved
as clean UTF-8. CRLF line endings preserved (`* -text` in `.gitattributes`).
⚠️ **Encoding hazard:** always read/write CRIOMAIN.CPP as binary (or with an explicit encoding
codec). Python text-mode `readlines()` silently strips `\r`, causing a 3000+-line git diff when
every line's `\r\n` becomes `\n`. If that happens, restore CRLF with `re.sub(b'(?<!\r)\n', b'\r\n', data)` in binary mode, then amend the commit.
- Added **`g_dwRIOPollTimeout`** global (default 50 ms). Computed in `SetupConnection` before the
receive loop using: `clamp(⌈480000/baud⌉, 5, 50)`. Preserves 50 ms at 9600 baud; floors at
5 ms for 115200 baud. Implemented with explicit ternary — **`min`/`max` are undeclared in this
translation unit under VC6** (not pulled in by CRIOMAIN's includes); use ternary or `__min`/`__max`.
### 16 pilots + 1 cameraship in multiplayer (f76dc05f)
`MW4Shell.cpp`:
- `CTCL_DefaultHostSetup` (non-coop): `Environment.NetworkMaxPlayers` set to
`params->m_maxPlayers + (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount())`.
The delta = camera-only seats (Tesla seats not assigned to pilots), reserving one extra
DirectPlay slot per cameraship so the 17th connection isn't rejected.
- `SetNetworkMissionParamater / PLAYER_LIMIT_PARAMETER`: same formula applied at runtime when
host changes the player limit. `gos_NetServerCommands(gos_Commend_UpdateMaxPlayers)` still called;
`break` must be present in this case (was accidentally dropped once — fall-through to
`JOIN_IN_PROGRESS_PARAMETER` corrupts `m_joinInProgress`).
- COOP branch unchanged (capped at 9+bots; no camera seat needed there).
`ConLobby.script`: launch guard `nTempPlayerCount > 16` raised to `> 17` so the cameraship
connection doesn't trigger "Too many player/bots".
### hsh/ BMP canonical renames (5813aeb6, 2026-07-23)
All `hsh/MFD/*.bmp` and `hsh/Mechs/*.bmp` filenames reconciled against the canonical stems
expected by game code. The engine loads MFD images via `huddamage.cpp` `texturename[]` array
(lowercase, no spaces) and Mechs portraits via `GetLocString` DNL strings (mixed-case with
spaces). Any mismatch = silently missing image at runtime.
Key renames:
- `hsh/MFD/assassinii.bmp``assassin2.bmp` (matches `texturename[]` canonical)
- `hsh/Mechs/battlemasteriic.bmp``battlemaster iic.bmp`
- `hsh/Mechs/mad cat mk.ii.bmp``mad cat mkii.bmp`
- `hsh/Mechs/behemoth ii.bmp` added (was absent)
Most other files in both directories are LFS pointer updates only (content unchanged).
### RookieMission configurable defaults via options.ini (5813aeb6, 2026-07-23)
CTCL (console) arcade mode has a "Rookie Mission" quick-launch that previously hardcoded all
game params. Now all 13 params are overridable from an `[RookieMission]` section in
`options.ini` (read by `CTCL_SetCDSP` at startup):
- `MW4Shell.cpp`: added 14 `g_` globals (`g_szRookieMission`, `g_nRookieGameType`, and 12
numeric params); registered as `gosScript_RegisterVariable` in `StartUp`/`ShutDown`;
`CTCL_SetCDSP` reads the `[RookieMission]` page via `NotationFile` and populates them.
Defaults: `"ScarabStronghold - Attrition"`, GameType=2 (Attrition), UnlimitedAmmo=1, all
others zero. `g_nRookieTimeLimit=-1` means "use the server's current time setting".
- `ConLobbyMission.script`: all hardcoded values in `MAIL_SET_ROOKIE_MISSION` handler replaced
with `$$g_szRookieMission$$` / `$$g_nRookieXxx$$` references.
- Requires rebuild: `MW4.exe` (Release + Profile).
### Mechlab turn rate label (5813aeb6, 2026-07-23)
`StringResource.rc` `IDS_ML_CH_TURNRATE`: "Turn Rate (Degrees/Sec.):" →
"Turn Rate (Top Speed Rad/Sec):" to match the actual `.data` field semantics
(`TopSpeedTurnRate` is in rad/s at top speed, not deg/s).
Requires rebuild: `ScriptStrings.dll`.
### BTFrstrm design documentation (840bc96c, 2026-07-23)
Two Word documents added to `BTFrstrm/`:
- `MechDependencyTree.docx` — dependency relationships between mech chassis/variants.
- `Special_Zones.docx` — documentation of special zone types used in maps/missions.
### mech_loadouts.md: MechEditor data model (0344418a, 2026-07-23)
`BTFrstrm/mech_loadouts.md` extended with a full reference section documenting the
MechEditor web app (`/home/rich/Repositories/MechEditor/mech_editor.py`, localhost:8765):
every `.data`/`.instance`/`.subsystems` field parsed, conversions performed (m/s ↔ kph,
rad/s ↔ deg/s), and `hsh/` naming rules for MFD/Mechs/HUD/Radar images. Canonical
reference for future mech data work.
## Next steps (proposed)
- [x] ~~Windowed 3D viewport on Win11~~ — DONE via DDrawCompat (see STEP 8 viewport section).
- [ ] (Optional) Curate remaining WIP content as features are exercised (editor loads dev `Content\`).
+55 -42
View File
@@ -43,7 +43,7 @@ LONG g_LeftPedalLast = 0;
LONG g_RightPedalLast = 0;
BOOL g_StartGame = FALSE;
static int g_nOpenComState = 0; //
static int g_nOpenComState = 0; // hyun
DWORD g_dwLastAnalogUpdate = 0;
@@ -54,7 +54,7 @@ DWORD g_dwLastAnalogUpdate = 0;
#define RESTART_CHAR 0xFE
#define IDLE_CHAR 0xFF
//member 변수처럼 활용할것.
//Use it like a member variable.
static HANDLE g_hCom = INVALID_HANDLE_VALUE;
static OVERLAPPED wos;
static OVERLAPPED ros;
@@ -108,6 +108,7 @@ int g_nRIOPacketCountB = (sizeof(g_baRIOLengthsB) / sizeof(g_baRIOLengthsB[0]));
int g_nRIOType = 0; // 0: old(original) type, 1: new type
DWORD g_dwRIOBaud = 0; // [tbaud] 0: default by RIO type; else COM1 baud forced by -tbaud (high-speed replica of the original RIO board, protocol unchanged)
DWORD g_dwRIOPollTimeout = 50; // [tbaud] WaitForMultipleObjects timeout (ms); computed from baud rate before the receive loop starts
int g_nEjectButton = 61; // 61: original, 31: new type
BYTE* g_pbRIOLengths = NULL;
int g_nRIOPacketCount = 0;
@@ -318,7 +319,7 @@ void RequestVersion();
void RequestAnalogUpdate(BYTE bFreq = 3);
// start - for new RIO Board only...
//received packet은 각각 다른 스레드에서 처리하므로.. ciritical section이 필요하다..
//Received packets are handled in different threads.. a critical section is needed..
BYTE received_buffer[128][16];
int received_packet=0;//packets count in the received_buffer.
DWORD received_serial=0;
@@ -569,7 +570,7 @@ BOOL SetupConnection(HANDLE hCom)
return TRUE;
/*
원래 처리 루틴..
Original processing routine..
NPTTYINFO npTTYInfo=0 ;
BYTE bset1 = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_DTRDSR) != 0) ;
BYTE bset2 = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_RTSCTS) != 0) ;
@@ -603,7 +604,7 @@ BOOL SetupConnection(HANDLE hCom)
//처리되지 않는 항목들.. GetCommState의 기본값으로 처리한다.
//Unhandled items.. handled using GetCommState default values.
DWORD fDsrSensitivity:1; // DSR sensitivity
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
DWORD fErrorChar: 1; // enable error replacement
@@ -647,7 +648,7 @@ HANDLE OpenConnection(int port)
g_hLampEvent = CreateEvent(0,FALSE,0,0);
g_hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)CommWatchProc, NULL, CREATE_SUSPENDED, &g_dwThreadID);
if (g_hCommWatchThread) {
//Thread까지 정상적으로 생성되었다.
//Thread was created successfully.
//////////////////////////////////////////////
//All OK
//Exit Point <======
@@ -744,11 +745,11 @@ retry:
for(int i=0;i<(int)dwLength;i++){
int ch=(BYTE)lpszBlock[i];
//받은 문자만큼 루프를 돈다.
//현재.. 받고 있는 packet이 없고.. 어떤 control문자라도 올 수 있는 상태이다.
//Loop for the number of received characters.
//Currently.. no packet is being received.. can receive any control character.
if(packetbyteremain!=0){
//패킷에 해당하는 문자가 도착했다.. 나머지 문자들을 받는다.
//Character corresponding to a packet has arrived.. receive the remaining characters.
if (ch & 0x80) {
packetbyteremain=0;
chinpacket=0;
@@ -758,7 +759,7 @@ retry:
packetbytes[chinpacket] = ch;
chinpacket++;
if (packetbyteremain == 0) {
//하나의 패킷이 완성되었다. 도착했다.. 즉시 반응한다.
//A packet has been completed and arrived.. respond immediately.
chinpacket--; // exclude check byte
int packettype=packetbytes[0];
BYTE bCheckByte = 0;
@@ -837,21 +838,21 @@ retry:
packettype++;
packettype--;
#endif // _DEBUG
case rio_Ack2://◆◆◆◆◆◆◆◆◆◆
case rio_Ack2:
if (g_nRIOType != 0) {
BYTE id=packetbytes[1];
int popped_index=PopPacket(id);
ack_timeout=GetTickCount()+30;
if(popped_index==0){
//맨처음것... 정상적인 경우이다.
//This is the very first one... Normal case.
}else if(popped_index>0){
//맨처음것이 아닌것.
//이전것들이 정상적으로 보내지지 않았으므로 다시 보낸다...
ReSendPackets(popped_index);//잘못된 개수 만큼..
//Not the very first one.
//Previous ones were not sent successfully, resend...
ReSendPackets(popped_index);//By the number of incorrect ones..
}else{
//실패 했다는것은.. 없는 id가 왔다는 것인데..
//명백한 에러.. 그러나 특별히 대처할 방법은 없다.
//그냥 무시한다.
//Failure means an id that does not exist arrived..
//An obvious error.. but there is no particular way to handle it.
//Simply ignore it.
}
}
break;
@@ -870,9 +871,9 @@ retry:
// WriteTTYBlock(hWnd,"|",1);
chinpacket=0;
//packetbytes버퍼를 지운다.
//Clear the packetbytes buffer.
} else {
//아직 패킷 문자들이 다 도착하지 않았다.. 아무것도 하지 않는다.
//Packet characters have not all arrived yet.. do nothing.
;
}
}else{
@@ -889,7 +890,7 @@ retry:
}//for(int i=0;i<(int)dwLength;i++)
//상훈 뒤
//sanghoon end
if (!fReadStat){
if (GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult( g_hCom,&ros, &dwLength, TRUE )){
@@ -1000,12 +1001,24 @@ DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
}
g_dwLastAnalogUpdate = GetTickCount() + 2800;
// [tbaud] Scale the poll timeout proportionally to baud rate so faster links
// poll more frequently. Formula: clamp(ceil(480000/baud), 5, 50)
// preserves the existing 50ms at 9600 baud, floors at 5ms for high speeds.
// effectiveBaud: use -tbaud override if set, else the RIO type's default.
{
DWORD effectiveBaud = (g_dwRIOBaud != 0) ? g_dwRIOBaud
: ((g_nRIOType == 0) ? 9600UL : 115200UL);
{
DWORD t = (480000UL + effectiveBaud - 1) / effectiveBaud;
g_dwRIOPollTimeout = (t < 5UL) ? 5UL : (t > 50UL ? 50UL : t);
}
}
while(1) {
DWORD dwEvtMask = 0;
WaitCommEvent( g_hCom, &dwEvtMask,&wcos );
DWORD ret=WaitForMultipleObjects(3,events,FALSE,50/*INFINITE*/);
DWORD ret=WaitForMultipleObjects(3,events,FALSE,g_dwRIOPollTimeout);
if(ret==WAIT_OBJECT_0){
//An Comm Event has arrived.
DWORD bytesread;
@@ -1022,10 +1035,10 @@ DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
}
} else {*/
if (g_nRIOType != 0) {
///◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆위의 ReadCommBlock에서 ack를 받았을 경우 duetime을 update시킨다.
///◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆CheckSentBuffer...
///When ack is received from ReadCommBlock above, update duetime.
///CheckSentBuffer...
if(GetTickCount()>ack_timeout)
ReSendPackets(sent_packet);//sent_buffer에 있는 모든것들을 다시 보내본다.
ReSendPackets(sent_packet);//Resend everything in sent_buffer.
}
/*}*/
}else if(ret==WAIT_OBJECT_0+1){
@@ -1039,9 +1052,9 @@ DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
//break;
}else if (ret==WAIT_TIMEOUT) {
if (g_nRIOType != 0) {
///◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆CheckSentBuffer...
///CheckSentBuffer...
if(GetTickCount()>ack_timeout)
ReSendPackets(sent_packet);//sent_buffer에 있는 모든것들을 다시 보내본다.
ReSendPackets(sent_packet);//Resend everything in sent_buffer.
}
if (packetbyteremain == 0) {
RequestAnalogUpdate();
@@ -1236,8 +1249,8 @@ bool SaveReceivedPacket(const BYTE*ba,int length)
bool GetSerialFromReceivedBuffer(char * ba)
{
//ButtonPressed/Released만 저장해 놓는다..
//모든 serial packet을 옮긴다.
//Store only ButtonPressed/Released..
//Transfer all serial packets.
if(received_packet>0){
if(received_buffer[0][2]==received_serial){
CopyMemory(ba,received_buffer[0],16);
@@ -1264,7 +1277,7 @@ bool QueuePacket(const BYTE *ba,int length)
int PopPacket(BYTE id)
{
//lamp.. id..
//lamp.. check by id..
//search the packet..
for(int i=0;i<sent_packet;i++){
if(sent_buffer[i][3]==id){
@@ -1427,7 +1440,7 @@ void CBUTTON_GROUP::SetTable(BOOL bFlag/* = true*/)
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
s_aCtrlLamp[b] = s_aSaveLamp[b];// = s_aButtonTable[b].lamp;
// s_aButtonTable는 Old값이므로 초기치는 Off
// s_aButtonTable is the old value, so the initial value is Off
s_aButtonTable[b].lamp = LAMP_OFF;
}
@@ -1439,7 +1452,7 @@ void CBUTTON_GROUP::SetTable(BOOL bFlag/* = true*/)
}
else
{
// 모든 테이블을 원래 상태로
// Restore all tables to original state
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
s_aCtrlLamp[b] = s_aSaveLamp[b];
}
@@ -1534,7 +1547,7 @@ void CBUTTON_GROUP::ResetTable(BOOL bFlag/* = true*/)
}
else
{
// 모든 테이블을 Off
// Turn off all tables
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
switch(b)
{
@@ -2212,13 +2225,13 @@ void CRIOMAIN::UpdatePadal (DIJOYSTATE &js)
Down Up
======================
(+) (-)
<-- (+) (-) -->
======================
/^
g_LeftPedalStart: INT_MAX (+)
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
lP 결과값은 무조건 음수
lP result value is always negative
*/
LONG lP = 0;
@@ -2362,9 +2375,9 @@ void CRIOMAIN::UpdateThrottle (DIJOYSTATE &js)
Up Down
======================
(-) (+)
<-- (-) (+) -->
======================
/^
g_ThrottleStart: INT_MIN (-)
*/
@@ -2447,11 +2460,11 @@ void CRIOMAIN::UpdateJoystick (DIJOYSTATE &js)
*/
//////////////////////////////////////////////////////////////////////////////////
/*
80 의 의미
Meaning of 80
RIO (LEFT :120 ~ RIGHT :-80) 까지의 수를 돌려준다고 가정하고
(LEFT의경우) RIGHT 의 최대값 80 이상인 값을 잘라버리고 나머지는
버릴경우 LEFT RIGHT가 동일한 속도로 움직일수 있다
Assuming RIO returns values in range (LEFT:120 ~ RIGHT:-80)
(In the LEFT case) clip values above the RIGHT maximum of 80, and the rest
if discarded, LEFT and RIGHT can move at the same speed
*/
//////////////////////////////////////////////////////////////////////////////////
LONG lJ;
+282 -8
View File
@@ -132,6 +132,10 @@ extern SCRIPTCALLBACK(CTCL_GetMissionState);
extern SCRIPTCALLBACK(CTCL_DoBreak);
extern SCRIPTCALLBACK(CTCL_IsGameLoaded);
extern SCRIPTCALLBACK(CTCL_DoReprint);
extern SCRIPTCALLBACK(CTCL_LoadAutoFile);
extern SCRIPTCALLBACK(CTCL_GetAutoSlotName);
extern SCRIPTCALLBACK(CTCL_GetAutoSlotMech);
extern SCRIPTCALLBACK(CTCL_GetAutoSlotInt);
extern SCRIPTCALLBACK(CTCL_CheckPlayMovie);
extern SCRIPTCALLBACK(CTCL_CheckCoinCounts);
extern SCRIPTCALLBACK(CTCL_CheckUseJPD);
@@ -158,6 +162,53 @@ int g_nWhyPaused = 0; // 0: not paused - invitation in menu-state, 1: briefing,
extern SCRIPTCALLBACK(CTCL_WhyPaused);
int g_nTimeList_Index = 3; // 3rd item in listbox...
int g_nTimeList_Value = 7; // 7 minutes
// [RookieMission] options.ini overrides ? defaults match the hardcoded script values
char g_szRookieMission[256] = "ScarabStronghold - Attrition";
char* g_pszRookieMission = g_szRookieMission; // pointer used for script registration
int g_nRookieGameType = 2; // Attrition index in game-type list
int g_nRookieVisibility = 0; // 0=clear
int g_nRookieWeather = 0; // 0=off
int g_nRookieTimeOfDay = 0; // 0=day
int g_nRookieTimeLimit = -1; // -1=use g_nTimeList_Value
int g_nRookieRadar = 0; // 0=novice
int g_nRookieHeat = 0; // 0=off
int g_nRookieFriendlyFire= 0;
int g_nRookieSplash = 0;
int g_nRookieUnlimitedAmmo = 1; // 1=on (be cautious)
int g_nRookieWeaponJam = 0;
int g_nRookieAdvanceMode = 0;
int g_nRookieArmorMode = 0;
// [automaticmode] options.ini ? Load File button
struct SAutoFileSlot {
char szName[64]; // pilot name
char szMech[64]; // mech display name (matched against mech[] array)
int nType; // 0=empty, 1=player, 2-9=bot difficulty level
int nTeam;
int nSkin;
int nDecal;
};
static SAutoFileSlot g_aAutoSlots[16];
static int g_nAutoSlotsCount = 0;
static int g_bAutomaticMode = 0;
static char g_szAutomaticFile[MAX_PATH] = "";
// [automaticmode] game option globals ? separate from Rookie Mission defaults to preserve Default button behavior
static char g_szAutoMission[256] = "";
static char* g_pszAutoMission = g_szAutoMission;
static int g_nAutoGameType = 2; // Attrition
static int g_nAutoVisibility = 0;
static int g_nAutoWeather = 0;
static int g_nAutoTimeOfDay = 0;
static int g_nAutoTimeLimit = -1; // -1 = server default
static int g_nAutoRadar = 0;
static int g_nAutoHeat = 0;
static int g_nAutoFriendlyFire = 0;
static int g_nAutoSplash = 0;
static int g_nAutoUnlimitedAmmo = 1;
static int g_nAutoWeaponJam = 0;
static int g_nAutoAdvanceMode = 0;
static int g_nAutoArmorMode = 0;
static int g_nAutoTeamAllowed = 0; // 0=FFA 1=team game; set via TeamAllowed= in [mission]
static int g_nAutoTeamCount = 2; // number of teams when TeamAllowed=1
// MSL 5.06
int g_nMechVariant = 0;
int g_nMechLabOp = 0;
@@ -727,6 +778,54 @@ void MW4Shell::StartUp()
gosScript_RegisterCallback("CTCL_WhyPaused",&CTCL_WhyPaused,GOSVAR_INT,0,NULL);
gosScript_RegisterVariable("g_nTimeList_Index", &g_nTimeList_Index, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nTimeList_Value", &g_nTimeList_Value, GOSVAR_INT, 0, NULL);
// [RookieMission] ini-driven defaults
gosScript_RegisterVariable("g_szRookieMission", &g_pszRookieMission, GOSVAR_STRING, 0, NULL);
gosScript_RegisterVariable("g_nRookieGameType", &g_nRookieGameType, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieVisibility", &g_nRookieVisibility, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieWeather", &g_nRookieWeather, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieTimeOfDay", &g_nRookieTimeOfDay, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieTimeLimit", &g_nRookieTimeLimit, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieRadar", &g_nRookieRadar, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieHeat", &g_nRookieHeat, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieFriendlyFire",&g_nRookieFriendlyFire, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieSplash", &g_nRookieSplash, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieUnlimitedAmmo",&g_nRookieUnlimitedAmmo,GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieWeaponJam", &g_nRookieWeaponJam, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieAdvanceMode", &g_nRookieAdvanceMode, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nRookieArmorMode", &g_nRookieArmorMode, GOSVAR_INT, 0, NULL);
// [automaticmode] callbacks + g_bAutomaticMode variable for script visibility
gosScript_RegisterVariable("g_bAutomaticMode", &g_bAutomaticMode, GOSVAR_INT, 0, NULL);
gosScript_RegisterCallback("CTCL_LoadAutoFile", &CTCL_LoadAutoFile, GOSVAR_INT, 0, NULL);
gosScript_RegisterCallback("CTCL_GetAutoSlotName", &CTCL_GetAutoSlotName, GOSVAR_INT, 0, NULL);
gosScript_RegisterCallback("CTCL_GetAutoSlotMech", &CTCL_GetAutoSlotMech, GOSVAR_INT, 0, NULL);
gosScript_RegisterCallback("CTCL_GetAutoSlotInt", &CTCL_GetAutoSlotInt, GOSVAR_INT, 0, NULL);
{
NotationFile opts("options.ini", NotationFile::Standard, true);
Page *pAuto = opts.FindPage("automaticmode");
if (pAuto) {
pAuto->GetEntry("automaticmode", &g_bAutomaticMode);
const char *sz = NULL;
if (pAuto->GetEntry("automaticfile", &sz) && sz)
strncpy(g_szAutomaticFile, sz, sizeof(g_szAutomaticFile)-1);
}
}
// [automaticmode] game option variables (separate from Rookie globals)
gosScript_RegisterVariable("g_szAutoMission", &g_pszAutoMission, GOSVAR_STRING, 0, NULL);
gosScript_RegisterVariable("g_nAutoGameType", &g_nAutoGameType, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoVisibility", &g_nAutoVisibility, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoWeather", &g_nAutoWeather, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTimeOfDay", &g_nAutoTimeOfDay, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTimeLimit", &g_nAutoTimeLimit, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoRadar", &g_nAutoRadar, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoHeat", &g_nAutoHeat, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoFriendlyFire", &g_nAutoFriendlyFire, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoSplash", &g_nAutoSplash, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoUnlimitedAmmo",&g_nAutoUnlimitedAmmo, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoWeaponJam", &g_nAutoWeaponJam, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoAdvanceMode", &g_nAutoAdvanceMode, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoArmorMode", &g_nAutoArmorMode, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTeamAllowed", &g_nAutoTeamAllowed, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTeamCount", &g_nAutoTeamCount, GOSVAR_INT, 0, NULL);
// MSL 5.06
gosScript_RegisterVariable("g_nMechVariant", &g_nMechVariant, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nMechLabOp", &g_nMechLabOp, GOSVAR_INT, 0, NULL);
@@ -1108,6 +1207,44 @@ void MW4Shell::ShutDown()
// jcem - start
gosScript_UnregisterVariable("g_nTimeList_Value");
gosScript_UnregisterVariable("g_nTimeList_Index");
// [RookieMission] globals
gosScript_UnregisterVariable("g_szRookieMission");
gosScript_UnregisterVariable("g_nRookieGameType");
gosScript_UnregisterVariable("g_nRookieVisibility");
gosScript_UnregisterVariable("g_nRookieWeather");
gosScript_UnregisterVariable("g_nRookieTimeOfDay");
gosScript_UnregisterVariable("g_nRookieTimeLimit");
gosScript_UnregisterVariable("g_nRookieRadar");
gosScript_UnregisterVariable("g_nRookieHeat");
gosScript_UnregisterVariable("g_nRookieFriendlyFire");
gosScript_UnregisterVariable("g_nRookieSplash");
gosScript_UnregisterVariable("g_nRookieUnlimitedAmmo");
gosScript_UnregisterVariable("g_nRookieWeaponJam");
gosScript_UnregisterVariable("g_nRookieAdvanceMode");
gosScript_UnregisterVariable("g_nRookieArmorMode");
// [automaticmode] callbacks + variable
gosScript_UnregisterCallback("CTCL_GetAutoSlotInt");
gosScript_UnregisterCallback("CTCL_GetAutoSlotMech");
gosScript_UnregisterCallback("CTCL_GetAutoSlotName");
gosScript_UnregisterCallback("CTCL_LoadAutoFile");
gosScript_UnregisterVariable("g_bAutomaticMode");
// [automaticmode] game option variables
gosScript_UnregisterVariable("g_nAutoTeamCount");
gosScript_UnregisterVariable("g_nAutoTeamAllowed");
gosScript_UnregisterVariable("g_nAutoArmorMode");
gosScript_UnregisterVariable("g_nAutoAdvanceMode");
gosScript_UnregisterVariable("g_nAutoWeaponJam");
gosScript_UnregisterVariable("g_nAutoUnlimitedAmmo");
gosScript_UnregisterVariable("g_nAutoSplash");
gosScript_UnregisterVariable("g_nAutoFriendlyFire");
gosScript_UnregisterVariable("g_nAutoHeat");
gosScript_UnregisterVariable("g_nAutoRadar");
gosScript_UnregisterVariable("g_nAutoTimeLimit");
gosScript_UnregisterVariable("g_nAutoTimeOfDay");
gosScript_UnregisterVariable("g_nAutoWeather");
gosScript_UnregisterVariable("g_nAutoVisibility");
gosScript_UnregisterVariable("g_nAutoGameType");
gosScript_UnregisterVariable("g_szAutoMission");
// MSL 5.06
gosScript_UnregisterVariable("g_nBlackMech");
gosScript_UnregisterVariable("g_nMechVariant");
@@ -1828,7 +1965,8 @@ int MW4Shell::SetNetworkMissionParamater(void * instance, int numParms, void **d
case PLAYER_LIMIT_PARAMETER:
params->m_playerLimit = INTPARM(1);
Min_Clamp(params->m_playerLimit, Network::GetInstance()->GetPlayerCount());
Environment.NetworkMaxPlayers = params->m_playerLimit;
// Add camera slots on top of the pilot limit so cameraships can still connect
Environment.NetworkMaxPlayers = params->m_playerLimit + (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount());
gos_NetServerCommands(gos_Commend_UpdateMaxPlayers,0);
break;
@@ -12731,6 +12869,143 @@ int __stdcall CTCL_SetCDSP(void* instance, int args, void* data[])
{
ASSERT(CTCL_IsConsole());
CTCL_DefaultHostSetup(1);
// Read [RookieMission] section from options.ini and populate script globals.
// Missing keys leave globals at their initialised defaults (backward-compatible).
{
NotationFile options_ini("options.ini", NotationFile::Standard, true);
Page *page = options_ini.FindPage("RookieMission");
if (page)
{
const char *sz = NULL;
if (page->GetEntry("MissionName", &sz) && sz)
{
strncpy(g_szRookieMission, sz, sizeof(g_szRookieMission) - 1);
g_szRookieMission[sizeof(g_szRookieMission) - 1] = '\0';
}
page->GetEntry("GameType", &g_nRookieGameType);
page->GetEntry("Visibility", &g_nRookieVisibility);
page->GetEntry("Weather", &g_nRookieWeather);
page->GetEntry("TimeOfDay", &g_nRookieTimeOfDay);
page->GetEntry("TimeLimit", &g_nRookieTimeLimit);
page->GetEntry("Radar", &g_nRookieRadar);
page->GetEntry("HeatOn", &g_nRookieHeat);
page->GetEntry("FriendlyFire", &g_nRookieFriendlyFire);
page->GetEntry("SplashDamage", &g_nRookieSplash);
page->GetEntry("UnlimitedAmmo", &g_nRookieUnlimitedAmmo);
page->GetEntry("WeaponJam", &g_nRookieWeaponJam);
page->GetEntry("AdvanceMode", &g_nRookieAdvanceMode);
page->GetEntry("ArmorMode", &g_nRookieArmorMode);
}
}
return 0;
}
// [automaticmode] Load File button ? reads automatic file into Rookie Mission globals + per-slot data.
// Returns 1 if the file was found and loaded, 0 otherwise. File is NOT consumed (stays on disk).
int __stdcall CTCL_LoadAutoFile(void* instance, int args, void* data[])
{
if (!g_bAutomaticMode || !g_szAutomaticFile[0])
return 0;
if (GetFileAttributes(g_szAutomaticFile) == 0xFFFFFFFF)
return 0;
NotationFile autofile(g_szAutomaticFile, NotationFile::Standard, true);
// Game options ? stored in dedicated Auto globals, NOT Rookie Mission globals,
// so the Default button continues to use the original [RookieMission] defaults.
g_szAutoMission[0] = '\0';
g_nAutoGameType = 2; g_nAutoVisibility = 0; g_nAutoWeather = 0;
g_nAutoTimeOfDay = 0; g_nAutoTimeLimit = -1; g_nAutoRadar = 0;
g_nAutoHeat = 0; g_nAutoFriendlyFire = 0; g_nAutoSplash = 0;
g_nAutoUnlimitedAmmo = 1; g_nAutoWeaponJam = 0; g_nAutoAdvanceMode = 0;
g_nAutoArmorMode = 0; g_nAutoTeamAllowed = 0; g_nAutoTeamCount = 2;
Page *pMission = autofile.FindPage("mission");
if (pMission) {
const char *sz = NULL;
if (pMission->GetEntry("MissionName", &sz) && sz) {
strncpy(g_szAutoMission, sz, sizeof(g_szAutoMission)-1);
g_szAutoMission[sizeof(g_szAutoMission)-1] = '\0';
}
pMission->GetEntry("GameType", &g_nAutoGameType);
pMission->GetEntry("Visibility", &g_nAutoVisibility);
pMission->GetEntry("Weather", &g_nAutoWeather);
pMission->GetEntry("TimeOfDay", &g_nAutoTimeOfDay);
pMission->GetEntry("TimeLimit", &g_nAutoTimeLimit);
pMission->GetEntry("Radar", &g_nAutoRadar);
pMission->GetEntry("HeatOn", &g_nAutoHeat);
pMission->GetEntry("FriendlyFire", &g_nAutoFriendlyFire);
pMission->GetEntry("SplashDamage", &g_nAutoSplash);
pMission->GetEntry("UnlimitedAmmo", &g_nAutoUnlimitedAmmo);
pMission->GetEntry("WeaponJam", &g_nAutoWeaponJam);
pMission->GetEntry("AdvanceMode", &g_nAutoAdvanceMode);
pMission->GetEntry("ArmorMode", &g_nAutoArmorMode);
pMission->GetEntry("TeamAllowed", &g_nAutoTeamAllowed);
pMission->GetEntry("TeamCount", &g_nAutoTeamCount);
}
// Per-slot data (up to 16 slots via [slot0]..[slot15] pages)
g_nAutoSlotsCount = 0;
memset(g_aAutoSlots, 0, sizeof(g_aAutoSlots));
char szPageName[16];
for (int i = 0; i < 16; i++) {
sprintf(szPageName, "slot%d", i);
Page *pSlot = autofile.FindPage(szPageName);
if (!pSlot) break;
g_nAutoSlotsCount = i + 1;
const char *sz = NULL;
if (pSlot->GetEntry("PilotName", &sz) && sz)
strncpy(g_aAutoSlots[i].szName, sz, sizeof(g_aAutoSlots[i].szName)-1);
sz = NULL;
if (pSlot->GetEntry("Mech", &sz) && sz)
strncpy(g_aAutoSlots[i].szMech, sz, sizeof(g_aAutoSlots[i].szMech)-1);
pSlot->GetEntry("Type", &g_aAutoSlots[i].nType);
pSlot->GetEntry("Team", &g_aAutoSlots[i].nTeam);
pSlot->GetEntry("Skin", &g_aAutoSlots[i].nSkin);
pSlot->GetEntry("Decal", &g_aAutoSlots[i].nDecal);
}
return 1;
}
// Returns pilot name string for slot k ? data[0]=string output buffer, data[1]=int k
int __stdcall CTCL_GetAutoSlotName(void* instance, int args, void* data[])
{
int k = INTPARM(1);
const char *sz = (k >= 0 && k < 16) ? g_aAutoSlots[k].szName : "";
if (STRPARM(0)) gos_Free(STRPARM(0));
STRPARM(0) = (char *)gos_Malloc(64);
strncpy(STRPARM(0), sz, 63);
STRPARM(0)[63] = '\0';
return 1;
}
// Returns mech display name for slot k ? data[0]=string output buffer, data[1]=int k
int __stdcall CTCL_GetAutoSlotMech(void* instance, int args, void* data[])
{
int k = INTPARM(1);
const char *sz = (k >= 0 && k < 16) ? g_aAutoSlots[k].szMech : "";
if (STRPARM(0)) gos_Free(STRPARM(0));
STRPARM(0) = (char *)gos_Malloc(64);
strncpy(STRPARM(0), sz, 63);
STRPARM(0)[63] = '\0';
return 1;
}
// Returns int field for slot k ? data[0]=int k, data[1]=int field (0=Type 1=Team 2=Skin 3=Decal)
// field is passed as a script literal (0/1/2/3), so use VALUEPARM not INTPARM (literals are passed
// as (void*)N directly, not as pointers; INTPARM would dereference NULL for field=0 and crash).
int __stdcall CTCL_GetAutoSlotInt(void* instance, int args, void* data[])
{
int k = INTPARM(0);
int field = VALUEPARM(1);
if (k < 0 || k >= 16) return 0;
switch (field) {
case 0: return g_aAutoSlots[k].nType;
case 1: return g_aAutoSlots[k].nTeam;
case 2: return g_aAutoSlots[k].nSkin;
case 3: return g_aAutoSlots[k].nDecal;
}
return 0;
}
@@ -13307,17 +13582,16 @@ void CTCL_API CTCL_DefaultHostSetup(int nMode)
params->m_allowdecaltransfer = 0;
if (g_bCOOP) {
Environment.NetworkMaxPlayers = 16;
//if (CTCL_GetTeslaCount() < CTCL_GetTeslaCountAll()) {
// any cameraship installed... so 1 more player can join
//params->m_maxPlayers = 9;
//params->m_maxBots = 8;
//} else {
// Reserve extra DirectPlay slots for camera seats (tracked separately from pilots in CTCL)
// Original commented-out intent preserved and now implemented for non-COOP below
params->m_maxPlayers = 9;
params->m_maxBots = 8;
//}
} else {
params->m_maxPlayers = 16;
Environment.NetworkMaxPlayers = 16;
// Reserve extra DirectPlay slots for cameraships: they share the session but are tracked
// separately from pilot seats in CTCL (CTCL_GetTeslaCountAll - CTCL_GetTeslaCount = camera count).
// Without this, cameraship connection is rejected by DirectPlay because slot 17 doesn't exist.
Environment.NetworkMaxPlayers = params->m_maxPlayers + (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount());
params->m_maxBots = 16;
}
params->m_allow3rdPerson = 1;
@@ -806,7 +806,7 @@ STRINGTABLE DISCARDABLE
BEGIN
IDS_ML_CH_ACCELERATION "Acceleration (Meters/Sec.):"
IDS_ML_CH_DECELERATION "Deceleration (Meters/Sec.):"
IDS_ML_CH_TURNRATE "Turn Rate (Degrees/Sec.):"
IDS_ML_CH_TURNRATE "Turn Rate (Top Speed Rad/Sec):"
IDS_ML_CH_TWISTRANGE "Torso Twist Range (Degrees):"
IDS_ML_CH_TWISTSPEED "Torso Twist Speed (Degrees/Sec.):"
IDS_ML_CH_NEWMECH "NEW 'MECH"
+168 -27
View File
@@ -14,6 +14,9 @@
// 10/10/03 MSL Added Cameraship to RESET_AFTER_LAUNCH
// 10/17/05 MSL Added all remaining Mechs (55)
// 06/19/18 AVB Update tab stops
// 06/24/26 Cyd MechCorps is given access to this script and the loading method (-diskfirst). This includes the CS timer fix.
// 06/27/26 MCHL MechCorps Highlight: Change Fab4 to Super6.
// 07/19/26 RT Bumped console title to V5.1.0b1
//-------------------------------------------------------------------------
@@ -187,13 +190,17 @@
#define MAIL_PREVIOUS_MISSION -2222
#define MAIL_RECORD_MISSION -4444
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
#define MAIL_LOAD_AUTO_MISSION -6666 // [automaticmode] sync with Multiplayer/ConLobbyMission.script
// MSL 5.06
// MSL ADD MECH
#define ROOKIEMECH1 38 // Loki
#define ROOKIEMECH2 40 // Madcat
#define ROOKIEMECH3 56 // Thor
#define ROOKIEMECH4 61 // Vulture
// MechCorps Highlight: expanded from Fab4 to Super6 (added Archer and Warhammer)
#define ROOKIEMECH1 1 // Archer
#define ROOKIEMECH2 38 // Loki
#define ROOKIEMECH3 40 // Madcat
#define ROOKIEMECH4 56 // Thor
#define ROOKIEMECH5 61 // Vulture
#define ROOKIEMECH6 62 // Warhammer
main
{
@@ -246,7 +253,7 @@ main
o_frame.type = 0
o_frame.location = 0, 0, 100
// MSL Version
o_frame.screen_name = "BattleTech Console V5.0.7Df" // localize$(IDS_MP_LOBBY_GAME_LOBBY)
o_frame.screen_name = "BattleTech Console V5.1.0b1" // localize$(IDS_MP_LOBBY_GAME_LOBBY)
initialize(o_frame)
int team_camo[16] // team_camo[selected team number] is skin number(0..36)...
@@ -1223,6 +1230,18 @@ main
o_all_random_mech.state = 0 // initially enabled
initialize(o_all_random_mech)
// [automaticmode] Load File button -- below Pick Cond., left of Reprint
object o_load_file = s_multistatepane
o_load_file.total_states = 3
o_load_file.textsize = 1
o_load_file.text = "Load File"
o_load_file.file = WPATH "button_reg_138x23m_3state.tga"
o_load_file.location = 467, 510, o_frame.location.z + 1
o_load_file.state = 0 // initially enabled
initialize(o_load_file)
if ($$g_bAutomaticMode$$ != 1)
o_load_file.state = 3 // hide when automaticmode is not active
// MSL Added default settings button
object o_default = s_multistatepane
o_default.total_states = 3
@@ -1658,6 +1677,119 @@ main
mail(MAIL_RESET_AFTER_LAUNCH, this) // reset
return
}
// [automaticmode] Load File button handler
if (sender == o_load_file)
{
play press, 1
if callback($$CTCL_LoadAutoFile$$)
{
// Apply game options using dedicated Auto globals.
// Rookie Mission globals are NOT touched so the Default button keeps working.
int m
if exists(@ConLobbyMission@)
{
@ConLobbyMission@o_game_options[0].nselected = $$g_nAutoGameType$$
for (m = 0; m < @ConLobbyMission@o_game_options[1].list_size; m++)
{
if equal$(@ConLobbyMission@o_game_options[1].list_item[m], $$g_szAutoMission$$)
{
@ConLobbyMission@o_game_options[1].nselected = m
break
}
}
mail(MAIL_LOAD_AUTO_MISSION, @ConLobbyMission@)
}
// Update team state synchronously so slot display is correct on the first click.
// MAIL_LOAD_AUTO_MISSION is async; setting team_allowed directly here lets
// CTCL_GetTeamParams return the correct cur_team_val before the slot loop runs.
callback($$SetNetworkMissionParamater$$, team_allowed, $$g_nAutoTeamAllowed$$)
if $$g_nAutoTeamAllowed$$
callback($$SetNetworkMissionParamater$$, num_of_teams, $$g_nAutoTeamCount$$)
cur_team_val = callback($$CTCL_GetTeamParams$$, cur_team_count[0])
// Clear all slots
o_cam_list.nselected = 0
for (k = 0; k < nRosterCount; k++)
{
if (1 <= o_BotList[k].nselected)
{
if (o_BotList[k].nselected == 1) && (k < MAXTESLA_P)
{
o_Editbox[k].boxValue = ""
initialize(o_Editbox[k])
deactivate(o_Editbox[k])
}
o_BotList[k].nselected = 0
m_naLastSelected[k] = 0
if (ROSTER_top_of_list <= k) && (k < ROSTER_top_of_list + ROSTER_DISPLAY_COUNT)
mail(MAIL_PLAYER_TYPE_CHANGED, k, this)
}
}
// Apply per-slot data from file
string szAutoName
string szAutoMech
int nAutoType
int j
for (k = 0; k < nRosterCount; k++)
{
nAutoType = callback($$CTCL_GetAutoSlotInt$$, k, 0)
if nAutoType > 0
{
o_BotList[k].nselected = nAutoType
m_naLastSelected[k] = nAutoType
if (o_BotList[k].nselected > 0)
activate(o_BotList[k])
if (nAutoType == 1) && (k < MAXTESLA_P)
{
callback($$CTCL_GetAutoSlotName$$, szAutoName, k)
o_Editbox[k].boxValue = szAutoName
initialize(o_Editbox[k])
activate(o_Editbox[k])
}
callback($$CTCL_GetAutoSlotMech$$, szAutoMech, k)
if (!equal$(szAutoMech, ""))
{
#if USE_ALLOWED_MECHS
// stock_array[j] = index in mech[] where chassis j lives (skips variant entries).
// mech[allowed_mechs[j]] would hit variant names; stock_array corrects this.
for (j = 0; j < MAX_ALLOWED_MECHS; j++)
{
if equal$(mech[stock_array[allowed_mechs[j]]], szAutoMech)
{
o_mech_variant[k].nselected = j
break
}
}
#else
for (j = 0; j < $$m_mechCount$$; j++)
{
if equal$(mech[j], szAutoMech)
{
o_mech_variant[k].nselected = j
break
}
}
#endif
}
// Always set both team and skin unconditionally.
// The game type may not have propagated to cur_team_val yet
// (MAIL_SET_ROOKIE_MISSION is async); setting both is safe --
// the launch code uses whichever is correct for the active mode.
o_team[k].nselected = callback($$CTCL_GetAutoSlotInt$$, k, 1)
o_skins[k].nselected = callback($$CTCL_GetAutoSlotInt$$, k, 2)
o_decal[k].nselected = callback($$CTCL_GetAutoSlotInt$$, k, 3)
initialize(o_mech_variant[k])
if (ROSTER_top_of_list <= k) && (k < ROSTER_top_of_list + ROSTER_DISPLAY_COUNT)
{
activate(o_mech_variant[k])
mail(MAIL_PLAYER_TYPE_CHANGED, k, this)
}
else
deactivate(o_mech_variant[k])
}
}
}
return
}
if (sender == o_reprint)
{
play press,1
@@ -1761,38 +1893,39 @@ main
for (k = 0; k < nRosterCount; k++)
{
// MSL
// MechCorps Highlight: Super6 slot assignments (16 slots, 6-mech cycle; Vulture+Warhammer appear twice)
if k == 0
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
o_mech_variant[k].nselected = ROOKIEMECH1 // Archer
if k == 1
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
o_mech_variant[k].nselected = ROOKIEMECH2 // Loki
if k == 2
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
o_mech_variant[k].nselected = ROOKIEMECH3 // Madcat
if k == 3
o_mech_variant[k].nselected = ROOKIEMECH4 // vulture
o_mech_variant[k].nselected = ROOKIEMECH4 // Thor
if k == 4
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
o_mech_variant[k].nselected = ROOKIEMECH5 // Vulture
if k == 5
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
o_mech_variant[k].nselected = ROOKIEMECH6 // Warhammer
if k == 6
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
o_mech_variant[k].nselected = ROOKIEMECH1 // Archer
if k == 7
o_mech_variant[k].nselected = ROOKIEMECH4 // Vulture
o_mech_variant[k].nselected = ROOKIEMECH2 // Loki
if k == 8
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
o_mech_variant[k].nselected = ROOKIEMECH3 // Madcat
if k == 9
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
o_mech_variant[k].nselected = ROOKIEMECH4 // Thor
if k == 10
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
o_mech_variant[k].nselected = ROOKIEMECH5 // Vulture
if k == 11
o_mech_variant[k].nselected = ROOKIEMECH4 // vulture
o_mech_variant[k].nselected = ROOKIEMECH6 // Warhammer
if k == 12
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
o_mech_variant[k].nselected = ROOKIEMECH1 // Archer
if k == 13
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
o_mech_variant[k].nselected = ROOKIEMECH2 // Loki
if k == 14
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
o_mech_variant[k].nselected = ROOKIEMECH3 // Madcat
if k == 15
o_mech_variant[k].nselected = ROOKIEMECH4 // Vulture
o_mech_variant[k].nselected = ROOKIEMECH4 // Thor
#if USE_O_MECH_VARIANT2
if (USE_ALLOWED_MECHS)
last_count = stock_array[allowed_mechs[o_mech_variant[k].nselected]]
@@ -2714,7 +2847,8 @@ main
}
}
// MSL 5.06
if nLaunchState == 0 && (nTempPlayerCount > 16)
// RT 07/19/26: raised cap from 16 to 17 to allow 16 pilots + 1 cameraship
if nLaunchState == 0 && (nTempPlayerCount > 17)
{
o_error_string.text = "Too many player/bots"
nLaunchState = -7
@@ -4000,17 +4134,24 @@ mech_randomizer
else // right button
{
int pick_mech = 0
pick_mech = random(0, 3)
//pick_mech = random(0, 3) // Fab4
// MechCorps Highlight: Super6 - expand right-click random range to 6
pick_mech = random(0, 5)
// MSL
// MechCorps Highlight: Super6 - added Archer (0) and Warhammer (5) to right-click mech pool
if pick_mech = 0
last_mech = ROOKIEMECH1 // Loki
last_mech = ROOKIEMECH1 // Archer
if pick_mech = 1
last_mech = ROOKIEMECH2 // Madcat
last_mech = ROOKIEMECH2 // Loki
if pick_mech = 2
last_mech = ROOKIEMECH3 // Thor
last_mech = ROOKIEMECH3 // Madcat
if pick_mech = 3
last_mech = ROOKIEMECH4 // vulture
last_mech = ROOKIEMECH4 // Thor
if pick_mech = 4
last_mech = ROOKIEMECH5 // Vulture
if pick_mech = 5
last_mech = ROOKIEMECH6 // Warhammer
}
if (USE_ALLOWED_MECHS)
{
@@ -52,6 +52,7 @@
#define MAIL_PREVIOUS_MISSION -2222
#define MAIL_RECORD_MISSION -4444
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
#define MAIL_LOAD_AUTO_MISSION -6666 // [automaticmode] sync with ConLobby.script
#define ROOKIE_MISSION "ScarabStronghold - Attrition"
@@ -793,15 +794,15 @@ main
if (getmessage() == MAIL_SET_ROOKIE_MISSION) // set rookie, see conlobby.script
{
// caution: actually 0 is not correct but who care? ^^
if (o_game_options[0].nselected != 2)
if (o_game_options[0].nselected != $$g_nRookieGameType$$)
{
o_game_options[0].nselected = 2
o_game_options[0].nselected = $$g_nRookieGameType$$
mail(MAIL_SET_ROOKIE_MISSION, this)
}
// find mission:
for k = 0; k < o_game_options[1].list_size; k++
{
if equal$(o_game_options[1].list_item[k], ROOKIE_MISSION)
if equal$(o_game_options[1].list_item[k], $$g_szRookieMission$$)
{
// found mission
break
@@ -830,6 +831,82 @@ main
mail(MAIL_PREVIOUS_MISSION_PARAMS, this)
return
}
// [automaticmode] Load File button: apply Auto globals, separate from Rookie Mission defaults
if (getmessage() == MAIL_LOAD_AUTO_MISSION)
{
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[$$g_nAutoGameType$$])
// visibility
o_game_options[2].nselected = $$g_nAutoVisibility$$
callback($$SetNetworkMissionParamater$$, visibility, o_game_options[2].nselected)
// weather
o_game_options[3].nselected = $$g_nAutoWeather$$
callback($$SetNetworkMissionParamater$$, weather, o_game_options[3].nselected)
// time of day
o_game_options[4].nselected = $$g_nAutoTimeOfDay$$
callback($$SetNetworkMissionParamater$$, night_parameter, o_game_options[4].nselected)
callback($$Shell_CallbackHandler$$, ShellSetTime, o_game_options[4].nselected)
// time limit
int nAutoTL = $$g_nAutoTimeLimit$$
int temp_num
if nAutoTL >= 0
temp_num = nAutoTL
else
temp_num = TIME_VALUE_DEFAULT
int m
for (m = 0; m < o_game_options[5].list_size; m++)
{
if (makeint(o_game_options[5].list_item[m]) == temp_num)
{
o_game_options[5].nselected = m
break
}
}
callback($$SetNetworkMissionParamater$$, time_limit, temp_num)
// radar
int nTempParam = radar
int nTempValue = $$g_nAutoRadar$$
o_game_options[6].nselected = nTempValue
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// heat
nTempParam = HEAT_PARAMETER
nTempValue = $$g_nAutoHeat$$
o_game_options[7].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// friendly fire
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
nTempValue = $$g_nAutoFriendlyFire$$
o_game_options[8].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// splash
nTempParam = SPLASH_ON_PARAMETER
nTempValue = $$g_nAutoSplash$$
o_game_options[9].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// unlimited ammo
nTempParam = UNLIMITED_AMMO_PARAMETER
nTempValue = $$g_nAutoUnlimitedAmmo$$
o_game_options[10].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// weapon jam
nTempParam = WEAPON_JAM_PARAMETER
nTempValue = $$g_nAutoWeaponJam$$
o_game_options[14].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// advance mode
nTempParam = ADVANCE_MODE_PARAMETER
nTempValue = $$g_nAutoAdvanceMode$$
o_game_options[15].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// armor mode
nTempParam = ARMOR_MODE_PARAMETER
nTempValue = $$g_nAutoArmorMode$$
o_game_options[16].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
initialize(this)
// notify ConLobby: game type/params changed (updates cur_game_type, cur_team_val display)
mail(-9998, parent)
return
}
}
if (sender == this)
{
@@ -855,24 +932,26 @@ main
else
{
// visibility
o_game_options[2].nselected = 0 // Default
o_game_options[2].nselected = $$g_nRookieVisibility$$ // [RookieMission] ini
callback($$SetNetworkMissionParamater$$, visibility, o_game_options[2].nselected)
// weather
o_game_options[3].nselected = 0 // off
o_game_options[3].nselected = $$g_nRookieWeather$$ // [RookieMission] ini
callback($$SetNetworkMissionParamater$$, weather, o_game_options[3].nselected)
// time of day
o_game_options[4].nselected = 0
o_game_options[4].nselected = $$g_nRookieTimeOfDay$$ // [RookieMission] ini
callback($$SetNetworkMissionParamater$$, night_parameter, o_game_options[4].nselected)
callback($$Shell_CallbackHandler$$, ShellSetTime, o_game_options[4].nselected)
}
// time limit
// time limit - use ini override if set, else TIME_VALUE_DEFAULT
if $$g_nRookieTimeLimit$$ >= 0
temp_num = $$g_nRookieTimeLimit$$ // [RookieMission] ini
else
temp_num = TIME_VALUE_DEFAULT
for k = 0; k < o_game_options[5].list_size; k++
{
temp_num = makeint(o_game_options[5].list_item[k])
if (temp_num == TIME_VALUE_DEFAULT)
if (makeint(o_game_options[5].list_item[k]) == temp_num)
{
o_game_options[5].nselected = k
break
@@ -935,31 +1014,31 @@ main
{
// radar
nTempParam = radar
nTempValue = 0 // Novice
nTempValue = $$g_nRookieRadar$$ // [RookieMission] ini (0=Novice)
o_game_options[6].nselected = nTempValue
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// heat management
nTempParam = HEAT_PARAMETER
nTempValue = 0
nTempValue = $$g_nRookieHeat$$ // [RookieMission] ini
o_game_options[7].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
// friendly fire
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
nTempValue = 0
nTempValue = $$g_nRookieFriendlyFire$$ // [RookieMission] ini
o_game_options[8].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
// splash
nTempParam = SPLASH_ON_PARAMETER
nTempValue = 0
nTempValue = $$g_nRookieSplash$$ // [RookieMission] ini
o_game_options[9].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
// unlimited ammo
nTempParam = UNLIMITED_AMMO_PARAMETER
nTempValue = 1 // be cautious
nTempValue = $$g_nRookieUnlimitedAmmo$$ // [RookieMission] ini
o_game_options[10].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
@@ -975,7 +1054,7 @@ main
// weapon jam
nTempParam = WEAPON_JAM_PARAMETER
nTempValue = 0
nTempValue = $$g_nRookieWeaponJam$$ // [RookieMission] ini
o_game_options[14].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
@@ -987,13 +1066,13 @@ main
// advance mode
nTempParam = ADVANCE_MODE_PARAMETER
nTempValue = 0
nTempValue = $$g_nRookieAdvanceMode$$ // [RookieMission] ini
o_game_options[15].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
// armor mode
nTempParam = ARMOR_MODE_PARAMETER
nTempValue = 0
nTempValue = $$g_nRookieArmorMode$$ // [RookieMission] ini
o_game_options[16].state = 0
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
@@ -1005,6 +1084,81 @@ main
return
}
if (getmessage() == MAIL_PREVIOUS_MISSION_PARAMS)
{
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[$$g_nAutoGameType$$])
// visibility
o_game_options[2].nselected = $$g_nAutoVisibility$$
callback($$SetNetworkMissionParamater$$, visibility, o_game_options[2].nselected)
// weather
o_game_options[3].nselected = $$g_nAutoWeather$$
callback($$SetNetworkMissionParamater$$, weather, o_game_options[3].nselected)
// time of day
o_game_options[4].nselected = $$g_nAutoTimeOfDay$$
callback($$SetNetworkMissionParamater$$, night_parameter, o_game_options[4].nselected)
callback($$Shell_CallbackHandler$$, ShellSetTime, o_game_options[4].nselected)
// time limit
int nAutoTL = $$g_nAutoTimeLimit$$
int temp_num
if nAutoTL >= 0
temp_num = nAutoTL
else
temp_num = TIME_VALUE_DEFAULT
int m
for (m = 0; m < o_game_options[5].list_size; m++)
{
if (makeint(o_game_options[5].list_item[m]) == temp_num)
{
o_game_options[5].nselected = m
break
}
}
callback($$SetNetworkMissionParamater$$, time_limit, temp_num)
// radar
int nTempParam = radar
int nTempValue = $$g_nAutoRadar$$
o_game_options[6].nselected = nTempValue
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// heat
nTempParam = HEAT_PARAMETER
nTempValue = $$g_nAutoHeat$$
o_game_options[7].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// friendly fire
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
nTempValue = $$g_nAutoFriendlyFire$$
o_game_options[8].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// splash
nTempParam = SPLASH_ON_PARAMETER
nTempValue = $$g_nAutoSplash$$
o_game_options[9].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// unlimited ammo
nTempParam = UNLIMITED_AMMO_PARAMETER
nTempValue = $$g_nAutoUnlimitedAmmo$$
o_game_options[10].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// weapon jam
nTempParam = WEAPON_JAM_PARAMETER
nTempValue = $$g_nAutoWeaponJam$$
o_game_options[14].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// advance mode
nTempParam = ADVANCE_MODE_PARAMETER
nTempValue = $$g_nAutoAdvanceMode$$
o_game_options[15].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// armor mode
nTempParam = ARMOR_MODE_PARAMETER
nTempValue = $$g_nAutoArmorMode$$
o_game_options[16].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
initialize(this)
// notify ConLobby: game type/params changed (updates cur_game_type, cur_team_val display)
mail(-9998, parent)
return
}
if (getmessage() == MAIL_PREVIOUS_MISSION_PARAMS)
{
// visibility
o_game_options[2].nselected = game_options_2
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More