Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acd4e40fa2 |
Binary file not shown.
Binary file not shown.
@@ -1,585 +0,0 @@
|
|||||||
<!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 “Load File” button |
|
|
||||||
Commit: <code>c3346561</code> (branch <code>5.1.0b-in-progress</code>) |
|
|
||||||
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> — 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>) —
|
|
||||||
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> — 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’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 — both accepted.</li>
|
|
||||||
<li><strong>Section headers:</strong> <code>[sectionname]</code> — <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, parser defaults are used. For <code>[mission]</code> this means built-in auto defaults (not current UI values). For <code>[slotX]</code> fields, omitted numeric values default to <code>0</code> and omitted strings default to empty.</li>
|
|
||||||
<li><strong>Missing section:</strong> If <code>[mission]</code> is absent, Load File still applies mission defaults. If <code>[slot0]</code> is absent, all slots are cleared and remain empty.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h2>4. Section: <code>[mission]</code></h2>
|
|
||||||
<p>Sets all game lobby parameters. This section is <span class="optional">optional</span>, but if omitted, Load File applies built-in mission defaults (it does not preserve current UI values).</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">NoReturn</span>=<span class="value">0</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 <code>MissionName</code> — 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>,
|
|
||||||
map selection falls back to index <code>0</code> (first available map for that game type).
|
|
||||||
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 <code>GameType</code> — 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 — 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 — <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 <code>Visibility</code> — 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 <code>Weather</code> — 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 <code>TimeOfDay</code> — 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 <code>TimeLimit</code> — 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’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 <code>Radar</code> — 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 <code>HeatOn</code> — integer</h3>
|
|
||||||
<table>
|
|
||||||
<tr><th>Value</th><th>Description</th></tr>
|
|
||||||
<tr><td><code>0</code></td><td>Off — heat never builds, weapons never overheat <span class="default-tag">DEFAULT</span></td></tr>
|
|
||||||
<tr><td><code>1</code></td><td>On — standard heat management enabled</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>4.9 <code>FriendlyFire</code> — integer</h3>
|
|
||||||
<table>
|
|
||||||
<tr><th>Value</th><th>Description</th></tr>
|
|
||||||
<tr><td><code>0</code></td><td>Off — friendly fire disabled <span class="default-tag">DEFAULT</span></td></tr>
|
|
||||||
<tr><td><code>1</code></td><td>On — 100% damage to teammates</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>4.10 <code>SplashDamage</code> — 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 — area/splash damage from missiles and explosions</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>4.11 <code>UnlimitedAmmo</code> — integer</h3>
|
|
||||||
<table>
|
|
||||||
<tr><th>Value</th><th>Description</th></tr>
|
|
||||||
<tr><td><code>0</code></td><td>Off — ammo is finite</td></tr>
|
|
||||||
<tr><td><code>1</code></td><td>On — unlimited ammo <span class="default-tag">DEFAULT (console/arcade)</span></td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>4.12 <code>NoReturn</code> — integer</h3>
|
|
||||||
<table>
|
|
||||||
<tr><th>Value</th><th>Description</th></tr>
|
|
||||||
<tr><td><code>0</code></td><td>Off — pilots can respawn <span class="default-tag">DEFAULT</span></td></tr>
|
|
||||||
<tr><td><code>1</code></td><td>On — no respawn ("No Return")</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p><strong>Implementation note:</strong> this maps to <code>RESPAWN_LIMIT_PARAMETER</code> (on/off), with <code>RESPAWN_LIMIT_NUMBER_PARAMETER</code> forced to <code>0</code>, matching current lobby behavior.</p>
|
|
||||||
|
|
||||||
<h3>4.13 <code>WeaponJam</code> — 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 — weapons can randomly jam</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>4.14 <code>AdvanceMode</code> — 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 <code>ArmorMode</code> — 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.16 <code>TeamAllowed</code> — 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.17 <code>TeamCount</code> — integer <span class="optional">(optional, only when TeamAllowed=1)</span></h3>
|
|
||||||
<p>Number of teams. Valid range: 2–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–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 <code>Type</code> — 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 — slot skipped, left empty in lobby</td></tr>
|
|
||||||
<tr><td><code>1</code></td><td>Player slot — 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 <code>PilotName</code> — 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–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 <code>Mech</code> — 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 <code>Team</code> — integer</h3>
|
|
||||||
<p>
|
|
||||||
<strong>Only applied in team game modes</strong> (game types marked “Team” 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 <code>Skin</code> — 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>–<code>15</code></td><td>Additional camo patterns</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>5.6 <code>Decal</code> — integer (faction emblem, 0–63)</h3>
|
|
||||||
<p>
|
|
||||||
Applied in <strong>both team and FFA modes.</strong>
|
|
||||||
Valid range: <code>0</code>–<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> ≤ 63 chars (truncated silently);
|
|
||||||
<code>MissionName</code> ≤ 255 chars;
|
|
||||||
<code>automaticfile</code> path ≤ 259 chars (<code>MAX_PATH − 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> — <code>CTCL_LoadAutoFile</code>,
|
|
||||||
<code>CTCL_GetAutoSlotName/Mech/Int</code> |
|
|
||||||
<code>ConLobby.script</code> — <code>o_load_file</code> handler |
|
|
||||||
<code>ConLobbyMission.script</code> — <code>MAIL_LOAD_AUTO_MISSION</code> |
|
|
||||||
Commit cd2fe73c | 2026-07-24
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
#!/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}")
|
|
||||||
@@ -292,363 +292,3 @@ At the time this doc was written:
|
|||||||
- non-playable mechs are explicitly marked `No`
|
- non-playable mechs are explicitly marked `No`
|
||||||
- Templar and other mechs with rear or side mounts have facing visible in the CSV
|
- 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
|
- 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, 0–100 |
|
|
||||||
| `ArmorRating` | `ArmorRating` | mechlab bar value, 0–100 |
|
|
||||||
| `SpeedRating` | `SpeedRating` | mechlab bar value, 0–100 |
|
|
||||||
| `HeatRating` | `HeatRating` | mechlab bar value, 0–100 |
|
|
||||||
| `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` (0–5)
|
|
||||||
|
|
||||||
**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` (0–5)
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
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,45 +0,0 @@
|
|||||||
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,145 +0,0 @@
|
|||||||
[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
|
|
||||||
NoReturn=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
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
[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
|
|
||||||
NoReturn=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
|
|
||||||
@@ -795,323 +795,7 @@ keeping old-RIO (type 0) protocol behavior byte-identical:
|
|||||||
alongside `mw4print.exe`.
|
alongside `mw4print.exe`.
|
||||||
- Version bumped to **2.0**, copyright year updated to **2026**.
|
- 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 3–4 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 3–4 from previous cycle) + right `BeginScene` (clear + grid)
|
|
||||||
- `sh_step 2–4`: channels 0–2 → left device
|
|
||||||
- `sh_step 5–6`: channels 3–4 → right device
|
|
||||||
- `sh_step 0` (next): flip radar + left MFD (shows channels 0–2 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.
|
|
||||||
|
|
||||||
### Load File autoconfig stabilization + docs update (2026-07-24)
|
|
||||||
End-to-end Load File flow for the console lobby was stabilized and verified with a
|
|
||||||
full-delta regression INI (all mission options + all 16 slots changed away from rookie
|
|
||||||
defaults), then round-tripped back via the Default button.
|
|
||||||
|
|
||||||
Key fixes in `ConLobby.script` / `ConLobbyMission.script`:
|
|
||||||
- Eliminated first-click vs second-click drift by moving auto-load to a dedicated
|
|
||||||
deterministic mission path and preventing redraw-time decal mutation.
|
|
||||||
- Corrected decal handling: map INI decal IDs to lobby dropdown indices, clamp invalid
|
|
||||||
indices, and show actual decal IDs in labels.
|
|
||||||
- Corrected mission+map sequencing: game type now rebuilds map list first; mission name
|
|
||||||
then resolves against that list (with fallback to map index 0).
|
|
||||||
- Fixed option-state application so first click applies all options (visibility/weather/
|
|
||||||
time/radar/heat/friendly fire/splash/unlimited/jam/advance/armor) without needing a
|
|
||||||
second click.
|
|
||||||
- Fixed Weapon Jam checkbox visibility refresh when set via Load File (UI now re-inits
|
|
||||||
correctly when heat/advanced states are applied).
|
|
||||||
|
|
||||||
New supported autoconfig key:
|
|
||||||
- Added `NoReturn=0|1` under `[mission]`:
|
|
||||||
- parser + script variable in `MW4Shell.cpp` (`g_nAutoNoReturn`),
|
|
||||||
- application in `ConLobbyMission.script` (`RESPAWN_LIMIT_PARAMETER`),
|
|
||||||
- docs + sample INIs updated.
|
|
||||||
|
|
||||||
Docs updates:
|
|
||||||
- `BTFrstrm/autoconfig-file-spec.html` corrected for real behavior:
|
|
||||||
- missing mission keys/section apply defaults (not current UI values),
|
|
||||||
- missing `MissionName` now falls back to first map for that game type,
|
|
||||||
- added `NoReturn` semantics and examples.
|
|
||||||
|
|
||||||
## 📋 STEP 10: Four-monitor MFD bring-up, display diagnostics, and the native-DirectDraw
|
|
||||||
## exclusive-mode wall (2026-07-25)
|
|
||||||
|
|
||||||
A long, dense session on the new 4-monitor bench (`MR_new`: AMD FirePro W4100, 4 outputs,
|
|
||||||
Win10). Everything below is empirical — measured on real hardware, not inferred.
|
|
||||||
|
|
||||||
### The root problem that made all of this hard
|
|
||||||
`CHSH_Device::InitFirst` / `InitSecond` (`CoreTech\Libraries\GameOS\render.cpp`) **discarded
|
|
||||||
every single `HRESULT`** (`SetCooperativeLevel`, `SetDisplayMode`, `CreateSurface`,
|
|
||||||
`GetAttachedSurface`, `QueryInterface`, `CreateDevice`) and **returned `true`
|
|
||||||
unconditionally**. A panel that failed to open produced no error, no crash and no log entry —
|
|
||||||
the monitor simply stayed on the desktop. SPEW is compiled out of shipping builds, so there
|
|
||||||
was no way to see any of it. **Fixing that visibility was what unblocked the whole session.**
|
|
||||||
|
|
||||||
### ✅ Diagnostics added (KEEP THESE — they are why everything below was findable)
|
|
||||||
- **`gos-displays.txt`** (written next to the exe by `VideoCard.cpp` `LogDisplayDevices()`):
|
|
||||||
`NumDevices`/`NumHWDevices`/`NumMonitors`, every DirectDraw device + `hw_rasterization`,
|
|
||||||
the role assignment (`FullScreenDevice`/`g_nNonDualHead`/`g_nDualHead`/`g_nDualHead2`/
|
|
||||||
`g_nMFD1`/`g_nMFD2`), `-tmon` APPLIED/REJECTED per slot, and the decisive
|
|
||||||
"mode 4 requires BOTH mfd1 and mfd2" line.
|
|
||||||
- **Per-call HRESULT logging** in `InitFirst`/`InitSecond` via `HSH_LogInit()` / `HSH_CheckHR()`
|
|
||||||
/ `HSH_HRName()` (27 `DDERR_*` codes decoded by name). Appends to the same file; each line is
|
|
||||||
written and flushed individually so **the log survives a crash**.
|
|
||||||
- **`gos-fps.txt`** — new **`-fps`** switch (`WinMain.cpp` `GOS_LogFrameRate`, hooked onto the
|
|
||||||
existing `frameRate` global). Per-second: frames, avg fps, **5% low**, worst frame in ms,
|
|
||||||
and a count of frames over 2x average ("hitches"), plus a **session summary** at exit with
|
|
||||||
true whole-session 1% / 0.1% lows (computed from a 0.5 ms-bucket histogram). Works in
|
|
||||||
**Release** — the engine's own `AddDebugData("FrameRate")` is `#ifdef LAB_ONLY`
|
|
||||||
(`MWMission.cpp`) so it only exists in `MW4pro.exe`. Off unless `-fps` is given (a global
|
|
||||||
read + branch when absent, no file created).
|
|
||||||
- ⚠️ **Lesson from the first real run: the instrument was measuring itself.** The original
|
|
||||||
version wrote one line per second straight to the file. The frame time is recorded
|
|
||||||
*before* the line is emitted, so the `WriteFile` cost landed in the **next** frame —
|
|
||||||
producing exactly one inflated frame every second (median worst-frame 24.4 ms against a
|
|
||||||
16.7 ms vsync interval, in 82% of seconds). Output is now buffered in memory and flushed
|
|
||||||
only when full or at exit (`atexit`), so a normal session performs no writes while
|
|
||||||
running. Trade-off: a hard crash loses the un-flushed tail — `gos-displays.txt` is the
|
|
||||||
crash-survivable log, this one is a measurement instrument.
|
|
||||||
- Also fixed: a "1% low" over 60 samples/sec degenerates to `nFrames/100 = 0` → clamped to
|
|
||||||
1 → literally the worst frame restated, so the column was redundant. Per-second is now a
|
|
||||||
5% low (worst 3-of-60); true 1% / 0.1% lows are in the session summary. And a single
|
|
||||||
frame longer than a second (a level load) no longer spills into following buckets and
|
|
||||||
emits a run of bogus one-frame rows.
|
|
||||||
|
|
||||||
### ✅ Crash-safety fix (independent of everything else, worth keeping)
|
|
||||||
`hsh_initialized` was set **unconditionally** after panel init. A failed panel therefore left
|
|
||||||
null surfaces and a null `IDirect3DDevice7` behind, and the per-frame path called straight
|
|
||||||
through them. Now: all four `InitSecond` overrides (`CMR`/`CRadar`/`CMFD`/`CMFDRight`) bail on
|
|
||||||
base failure, `CMFD_Device::InitFirst` reports the real result instead of always `true`, and
|
|
||||||
`hsh_initialized` is only set when the panels genuinely came up → **the game runs without MFDs
|
|
||||||
instead of dying**.
|
|
||||||
- **Crash signature to recognise:** `call [ecx+0x44]` with `ECX=0` = `IDirectDrawSurface7::GetDC`
|
|
||||||
on a never-created surface (verified against `build-env\dx7asdk\include\ddraw.h` vtable order).
|
|
||||||
Reported as `EXCEPTION : Attempt to read from address 0x00000044`.
|
|
||||||
|
|
||||||
### ✅ AppCompat shim — the single most important operational fact
|
|
||||||
**`DWM8And16BitMitigation` is keyed on the executable's FULL PATH.** A copy of the game at a
|
|
||||||
new path silently loses it. MW4 renders at `bitdepth=16` and **modern GPUs expose ZERO 16-bit
|
|
||||||
display modes** — the shim *synthesises* them (proved directly: crash dump shows
|
|
||||||
`16 bit modes :` EMPTY without the shim, populated with it).
|
|
||||||
- **Without the shim the error message actively misleads.** GameOS raises
|
|
||||||
`GOS_DXRASTERIZER_NOFULLSCREEN` — *"Another application is preventing use of full screen
|
|
||||||
mode"* (`DXRasterizer.cpp:~1125`). That is a **catch-all** fired after every `SetDisplayMode`
|
|
||||||
attempt fails; it even scans for NetMeeting. It sends you hunting for a conflicting program
|
|
||||||
that does not exist. The real cause is the missing shim / absent 16-bit modes.
|
|
||||||
- **NEW: `build-env\set-appcompat.ps1` + `set-appcompat.bat`** — self-locating (`$PSScriptRoot`)
|
|
||||||
one-click installer. Applies the layer to `MW4.exe`/`MW4pro.exe`/`MW4Ed2.exe` sitting next to
|
|
||||||
it, wherever that install lives; HKCU always, HKLM too if elevated (note the HKLM value format
|
|
||||||
differs — it carries a leading `$` marker). Verifies by reading back; detects the
|
|
||||||
`HIGHDPIAWARE`-only entry that *suppresses* the auto-shim (the original STEP 5 bug).
|
|
||||||
`-Remove` / `-WhatIfOnly` supported. **`deploy-mw4.ps1` now ships both into every deployment.**
|
|
||||||
|
|
||||||
### ✅ Exclusive fullscreen DOES work on Win10 without dgVoodoo2
|
|
||||||
Confirmed on the W4100 with system `ddraw.dll` and dgVoodoo2 physically removed: `MW4.exe`
|
|
||||||
alt-enters to exclusive fullscreen correctly **once the shim is applied to that exe path**.
|
|
||||||
Windowed mode also works natively with no shim at all (windowed sets
|
|
||||||
`Environment.bitDepth = DesktopBpp` = 32, so there is no mode switch). Console/shell confirmed
|
|
||||||
windowed; a **full mission windowed is still untested**.
|
|
||||||
|
|
||||||
### ❌ Native multi-monitor MFD is BLOCKED — and no flag combination fixes it
|
|
||||||
The panels' cooperative-level call was genuinely wrong (a latent 2002 bug): every panel asked to
|
|
||||||
be **both** the process focus window **and** its own device window, on the one shared `hWindow`,
|
|
||||||
*after* the main device had already taken exclusive mode on it. The main device
|
|
||||||
(`DXRasterizer.cpp:~1027`) already uses the correct two-call idiom
|
|
||||||
(`SETFOCUSWINDOW` alone, then `EXCLUSIVE|FULLSCREEN`) — tagged `//sanghoon`, same author. The
|
|
||||||
panels never were.
|
|
||||||
|
|
||||||
A new **`-tcoop <0-5>`** switch was added to test every plausible form on real hardware without a
|
|
||||||
rebuild between attempts. Results (no dgVoodoo2, shim applied, `-tmfds 4`):
|
|
||||||
|
|
||||||
| `-tcoop` | Flags | Result |
|
|
||||||
|---|---|---|
|
|
||||||
| 0 | `SETFOCUSWINDOW\|CREATEDEVICEWINDOW\|ALLOWREBOOT\|EXCLUSIVE\|FULLSCREEN` (legacy) | `DDERR_EXCLUSIVEMODEALREADYSET` |
|
|
||||||
| 1 | `CREATEDEVICEWINDOW\|EXCLUSIVE\|FULLSCREEN` (no focus claim) | `DDERR_INVALIDPARAMS` |
|
|
||||||
| 2 | `SETFOCUSWINDOW`, then `CREATEDEVICEWINDOW\|…` | `DDERR_INVALIDPARAMS` |
|
|
||||||
| 3 | `SETFOCUSWINDOW`, then `EXCLUSIVE\|FULLSCREEN` | 1st panel collides; **that collision steals exclusive from the main display**, after which panels 2+3 fully init (radar reached `CreateDevice(HAL) = DD_OK`). Side effect: desktop left at 1920x1080 **16bpp**. Not viable. |
|
|
||||||
| 4 | `EXCLUSIVE\|FULLSCREEN` only | `DDERR_EXCLUSIVEMODEALREADYSET` on **all** panels |
|
|
||||||
| 5 | `ALLOWREBOOT\|EXCLUSIVE\|FULLSCREEN` | `DDERR_EXCLUSIVEMODEALREADYSET` on **all** panels |
|
|
||||||
|
|
||||||
**CONCLUSION: on modern Windows only ONE DirectDraw object per process may hold exclusive
|
|
||||||
fullscreen.** The main display takes it; every secondary panel is refused. XP allowed multiple;
|
|
||||||
**dgVoodoo2 allows it because it is a full reimplementation of ddraw, not bound by that rule.**
|
|
||||||
=> **dgVoodoo2 cannot be removed for `-tmfds 1/3/4` by fixing flags.** Default stays `-tcoop 0`.
|
|
||||||
(`-tcoop` is retained: it is how this was settled and will re-settle it on different hardware.)
|
|
||||||
|
|
||||||
### ✅ Working 4-monitor config (WITH dgVoodoo2)
|
|
||||||
- **dgVoodoo2 Scaling mode MUST be "Stretched, Keep Aspect Ratio".** Plain "Stretched" fails
|
|
||||||
*silently*: main + radar go fullscreen black, both MFD monitors keep showing the desktop, and
|
|
||||||
**every DirectDraw call still returns `DD_OK`** — the devices are alive but dgVoodoo2 never
|
|
||||||
drives those outputs. Diagnosed with a temporary per-panel colour-flash test (since removed).
|
|
||||||
- Device index → physical monitor is **1:1** on this bench (colour test confirmed): main=0,
|
|
||||||
radar=1, mfd-left=2, mfd-right=3, so `-tmon 1,2,3,4` is identical to auto-detection.
|
|
||||||
- **Confirmed working end-to-end: all three secondary panels present, full mission played.**
|
|
||||||
- ⚠️ **dgVoodoo2 is deliberately NOT in the repo and must NOT be — it breaks Windows XP
|
|
||||||
deployments.** The pod fleet is mixed: XP pods need native DirectDraw and nothing else, while
|
|
||||||
Win10/11 pods need dgVoodoo2 for any MFD mode. Shipping it in the build (or having
|
|
||||||
`deploy-mw4.ps1` place it) would push a Win10-only dependency onto XP machines and break them.
|
|
||||||
It stays an **installed-per-machine, OS-dependent prerequisite** set up by the pod owner on
|
|
||||||
Win10/11 only. `dgVoodoo.conf`/`dgVoodooCpl.exe` were removed in `0ceba9c7` for this reason —
|
|
||||||
do not "fix" that by re-adding them. The required setting (Scaling mode
|
|
||||||
`Stretched, Keep Aspect Ratio`, on the **General** tab) is documented in the pod-owner release
|
|
||||||
notes instead.
|
|
||||||
|
|
||||||
### 📐 Engine is 4:3 ONLY (relevant to every display decision)
|
|
||||||
`ImageHlp.cpp:~464` asserts the complete supported resolution set: **640x480, 512x384, 800x600,
|
|
||||||
960x720, 1024x768, 1280x1024 (5:4), 1600x1200**. There is no 16:9 mode and **no aspect
|
|
||||||
correction anywhere** in the codebase (the only `aspect` hits are texture-dimension caps and
|
|
||||||
CameraShip). On a 16:9 monitor a 4:3 image must be adapted by the scaler: plain stretch =
|
|
||||||
distorted (circles → ovals, reticle wrong); keep-aspect = correct geometry + pillarbox bars.
|
|
||||||
Native widescreen would require re-authoring every 2D/shell layout (all fixed 640x480 pixel
|
|
||||||
coordinates) — a content project, not a code tweak.
|
|
||||||
|
|
||||||
### 📐 Assessment: moving to borderless windowed (discussed, NOT started)
|
|
||||||
The only native path to multi-monitor, since it removes exclusive mode from the picture entirely.
|
|
||||||
Smaller than it sounds because the mechanisms already exist:
|
|
||||||
- The windowed present is already `wBlt(FrontBufferSurface, &Window, BackBufferSurface, …)` —
|
|
||||||
a blit to an arbitrary screen rect. `Blt` stretches when the dest rect differs in size, so
|
|
||||||
aspect-preserving borderless output is **rect maths, not new machinery**.
|
|
||||||
- Clipper wrappers (`wCreateClipper`/`wSetClipper`/`wSetHWnd`) already exist in `DirectDraw.cpp`,
|
|
||||||
unused by the fullscreen path.
|
|
||||||
- **None of the drawing code changes** — panels already render into `pDDSTarget` and composite;
|
|
||||||
`DrawQuad`/`DrawTexture`/fonts/`SwapRightState` don't care whether the present is Flip or Blt.
|
|
||||||
- Work is confined to `render.cpp`, `DXRasterizer.cpp`, `WinMain.cpp` (+ a little `Windows.cpp`):
|
|
||||||
panel init → `DDSCL_NORMAL` + clipper + offscreen render surface (~150 lines); ~4 panel `Flip`
|
|
||||||
sites → `Blt`; break the `HSH_EnterFullScreen2()` ↔ `EnterFullScreenMode()` coupling
|
|
||||||
(`DXRasterizer.cpp:~1132` is its ONLY call site, which is also why windowed mode currently
|
|
||||||
creates no panels at all); **new: explicit frame pacing** (windowed Blt has no vsync — this is
|
|
||||||
the same root cause as the known mechlab fast-spin bug).
|
|
||||||
- **Gains:** no dgVoodoo2, no shim, no 16-bit dependency, no exclusive-mode contention, aspect
|
|
||||||
under our control, and panels stay at their monitor's native mode so "this panel won't accept
|
|
||||||
640x480" becomes impossible.
|
|
||||||
- **Risk that decides it:** windowed D3D7 device creation is per-GPU (the *editor* hit
|
|
||||||
`DDERR_INVALIDOBJECT` on Win11 and needed DDrawCompat; the *game* succeeded natively on the
|
|
||||||
W4100). Version lockstep means one build must serve every pod.
|
|
||||||
- **Recommended staging:** (1) `-borderless` for the MAIN display only, default off; (2) convert
|
|
||||||
**one** panel (radar) — the small, make-or-break test of `DDSCL_NORMAL` + clipper +
|
|
||||||
D3D-on-offscreen on the target GPU; (3) all three panels; (4) retire dgVoodoo2 + shim. Keep
|
|
||||||
exclusive fullscreen switch-selectable indefinitely.
|
|
||||||
|
|
||||||
### Incidental findings
|
|
||||||
- **`-2dt` is not a recognised switch anywhere in the codebase**, despite appearing in production
|
|
||||||
`ctcl.ini` launch lines. Completely inert. (2D targets are already the default; `-3dt` is what
|
|
||||||
switches to the 3D model.)
|
|
||||||
- `NumHWDevices` (5) can exceed `NumDevices` (4) — it counts D3D device-enumeration callbacks, and
|
|
||||||
an adapter exposing both a HAL and a T&L HAL yields two. Benign; `InitSecond` hardcodes
|
|
||||||
`IID_IDirect3DHALDevice`.
|
|
||||||
- New switches are documented in `-help` (`-fps` under LOGGING AND DIAGNOSTICS, `-tcoop` under
|
|
||||||
DISPLAY AND VIDEO).
|
|
||||||
|
|
||||||
## Next steps (proposed)
|
## Next steps (proposed)
|
||||||
- [ ] (Decision pending) Borderless-windowed migration — see the staged assessment in STEP 10.
|
|
||||||
Step 2 (one panel) is the cheap, decisive experiment. Note this is the only route that would
|
|
||||||
remove the dgVoodoo2 prerequisite **without** breaking the XP pods, since it needs no external
|
|
||||||
DLL on either OS.
|
|
||||||
- [ ] Test a **full mission windowed** (only the console/shell has been verified windowed).
|
|
||||||
- [x] ~~Windowed 3D viewport on Win11~~ — DONE via DDrawCompat (see STEP 8 viewport section).
|
- [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\`).
|
- [ ] (Optional) Curate remaining WIP content as features are exercised (editor loads dev `Content\`).
|
||||||
- [ ] (Optional) Build Debug/Armor configs (need `dbg.bin`/`arm.bin` output dirs).
|
- [ ] (Optional) Build Debug/Armor configs (need `dbg.bin`/`arm.bin` output dirs).
|
||||||
|
|||||||
@@ -48,10 +48,6 @@ int g_nDualHead2 = -1; // jcem
|
|||||||
int g_nNonDualHead = -1; // jcem
|
int g_nNonDualHead = -1; // jcem
|
||||||
int g_nMFD1 = -1; // mode 4: left 640x480 MFD monitor
|
int g_nMFD1 = -1; // mode 4: left 640x480 MFD monitor
|
||||||
int g_nMFD2 = -1; // mode 4: right 640x480 MFD monitor
|
int g_nMFD2 = -1; // mode 4: right 640x480 MFD monitor
|
||||||
// [tmon] Operator override for display-device selection, in Main/Radar/MFD1/MFD2 order.
|
|
||||||
// -1 = leave that slot to auto-detection. Populated by the -tmon command-line switch,
|
|
||||||
// which is parsed in MW4Application's WinMain - that runs before FindVideoCards().
|
|
||||||
int g_naMonitorOverride[4] = { -1, -1, -1, -1 };
|
|
||||||
// MSL 5.03 Mechview
|
// MSL 5.03 Mechview
|
||||||
int g_nMechViewType; // jcem : 0 - no mechview, 1 - on radar screen, 2 - on main screen
|
int g_nMechViewType; // jcem : 0 - no mechview, 1 - on radar screen, 2 - on main screen
|
||||||
|
|
||||||
@@ -281,129 +277,6 @@ HRESULT CALLBACK EnumModesCallback2( LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lp
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Called once at startup, enumerate and get caps of all 3D video cards
|
// Called once at startup, enumerate and get caps of all 3D video cards
|
||||||
//
|
|
||||||
// [tmon] Apply a hard-assigned display device, but only if the operator supplied one
|
|
||||||
// and it names a device that actually exists. Anything else is ignored, so a stale
|
|
||||||
// -tmon left on the command line of a machine with fewer monitors simply falls back
|
|
||||||
// to auto-detection instead of breaking startup. Rejections are reported in
|
|
||||||
// gos-displays.txt - silently doing nothing is worse than useless when the operator
|
|
||||||
// is trying to work out why a monitor did not light up.
|
|
||||||
//
|
|
||||||
static void ApplyMonitorOverride(int slot, int* pTarget)
|
|
||||||
{
|
|
||||||
int dev = g_naMonitorOverride[slot];
|
|
||||||
if ((dev >= 0) && (dev < (int)NumDevices))
|
|
||||||
*pTarget = dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// [displaylog] Append one printf-style line to the display report.
|
|
||||||
//
|
|
||||||
static void EmitDisplayLine( HANDLE hFile, const char* fmt, ... )
|
|
||||||
{
|
|
||||||
char szLine[512];
|
|
||||||
DWORD dwWritten=0;
|
|
||||||
va_list ap;
|
|
||||||
va_start( ap, fmt );
|
|
||||||
vsprintf( szLine, fmt, ap );
|
|
||||||
va_end( ap );
|
|
||||||
WriteFile( hFile, szLine, (DWORD)strlen(szLine), &dwWritten, NULL );
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// [displaylog] Write gos-displays.txt next to the executable, describing every
|
|
||||||
// DirectDraw device GameOS found and which display role each one was assigned.
|
|
||||||
//
|
|
||||||
// This exists because there is no other way to see it: the SPEW macros are compiled
|
|
||||||
// out of shipping builds, and a monitor that simply never lights up gives no clue
|
|
||||||
// whether the device was missing, lacked hardware caps, or was assigned elsewhere.
|
|
||||||
//
|
|
||||||
static void LogDisplayDevices()
|
|
||||||
{
|
|
||||||
char szDir[MAX_PATH];
|
|
||||||
char szPath[MAX_PATH];
|
|
||||||
|
|
||||||
szDir[0]='\0';
|
|
||||||
GetModuleFileNameA( NULL, szDir, sizeof(szDir) );
|
|
||||||
char* pSlash=strrchr( szDir, '\\' );
|
|
||||||
if( pSlash )
|
|
||||||
*(pSlash+1)='\0';
|
|
||||||
else
|
|
||||||
szDir[0]='\0';
|
|
||||||
sprintf( szPath, "%sgos-displays.txt", szDir );
|
|
||||||
|
|
||||||
HANDLE hFile=CreateFileA( szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
||||||
if( hFile==INVALID_HANDLE_VALUE )
|
|
||||||
return;
|
|
||||||
|
|
||||||
EmitDisplayLine( hFile, "GameOS display device enumeration\r\n" );
|
|
||||||
EmitDisplayLine( hFile, "=================================\r\n\r\n" );
|
|
||||||
EmitDisplayLine( hFile, "NumDevices = %d (DeviceArray holds at most 8)\r\n", (int)NumDevices );
|
|
||||||
EmitDisplayLine( hFile, "NumHWDevices = %d\r\n", (int)NumHWDevices );
|
|
||||||
EmitDisplayLine( hFile, "NumMonitors = %d\r\n\r\n", (int)NumMonitors );
|
|
||||||
|
|
||||||
for( DWORD t0=0; t0<NumDevices; t0++ )
|
|
||||||
{
|
|
||||||
EmitDisplayLine( hFile, " [%d] %-40s hw_rasterization=%s\r\n",
|
|
||||||
(int)t0,
|
|
||||||
DeviceArray[t0].DDid.szDescription,
|
|
||||||
(DeviceArray[t0].D3DCaps.dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) ? "yes" : "NO" );
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitDisplayLine( hFile, "\r\nAssigned roles (-1 means not found):\r\n" );
|
|
||||||
EmitDisplayLine( hFile, " main (FullScreenDevice) = %d\r\n", Environment.FullScreenDevice );
|
|
||||||
EmitDisplayLine( hFile, " radar (g_nNonDualHead) = %d\r\n", g_nNonDualHead );
|
|
||||||
EmitDisplayLine( hFile, " span (g_nDualHead) = %d\r\n", g_nDualHead );
|
|
||||||
EmitDisplayLine( hFile, " span2 (g_nDualHead2) = %d\r\n", g_nDualHead2 );
|
|
||||||
EmitDisplayLine( hFile, " mfd1 (g_nMFD1) = %d\r\n", g_nMFD1 );
|
|
||||||
EmitDisplayLine( hFile, " mfd2 (g_nMFD2) = %d\r\n", g_nMFD2 );
|
|
||||||
|
|
||||||
// Report what -tmon asked for and whether each slot could be honoured.
|
|
||||||
{
|
|
||||||
static const char* apszRole[4] = { "main", "radar", "mfd1", "mfd2" };
|
|
||||||
bool bAny=false;
|
|
||||||
for( int s=0; s<4; s++ )
|
|
||||||
if( g_naMonitorOverride[s] >= 0 )
|
|
||||||
bAny=true;
|
|
||||||
if( bAny )
|
|
||||||
{
|
|
||||||
EmitDisplayLine( hFile, "\r\n-tmon override:\r\n" );
|
|
||||||
for( int s2=0; s2<4; s2++ )
|
|
||||||
{
|
|
||||||
int dev=g_naMonitorOverride[s2];
|
|
||||||
if( dev < 0 )
|
|
||||||
EmitDisplayLine( hFile, " %-5s : auto\r\n", apszRole[s2] );
|
|
||||||
else if( dev < (int)NumDevices )
|
|
||||||
EmitDisplayLine( hFile, " %-5s : device %d APPLIED\r\n", apszRole[s2], dev );
|
|
||||||
else
|
|
||||||
EmitDisplayLine( hFile, " %-5s : device %d *** REJECTED - only %d device(s) exist ***\r\n",
|
|
||||||
apszRole[s2], dev, (int)NumDevices );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EmitDisplayLine( hFile, "\r\n-tmon override: not supplied (all roles auto-detected)\r\n" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The decisive line: whether the MFD subsystem will initialise at all.
|
|
||||||
EmitDisplayLine( hFile, "\r\nMFD mode (-tmfds) = %d\r\n", g_nTypeOfMFDs );
|
|
||||||
if( g_nTypeOfMFDs == 4 )
|
|
||||||
{
|
|
||||||
bool bOK = (g_nMFD1 != -1) && (g_nMFD2 != -1);
|
|
||||||
EmitDisplayLine( hFile, "mode 4 requires BOTH mfd1 and mfd2: %s\r\n", bOK ? "OK" : "*** FAILED ***" );
|
|
||||||
if( !bOK )
|
|
||||||
EmitDisplayLine( hFile,
|
|
||||||
" -> IsMultimonitorAvaliable() returns false, so HSH_EnterFullScreen2()\r\n"
|
|
||||||
" falls back to the single-secondary-monitor path and the two MFD\r\n"
|
|
||||||
" displays are never opened. Four hardware-rasterizing DirectDraw\r\n"
|
|
||||||
" devices are needed: main, radar, mfd1, mfd2.\r\n" );
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle( hFile );
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
void FindVideoCards()
|
void FindVideoCards()
|
||||||
@@ -509,10 +382,6 @@ void FindVideoCards()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// [tmon] Hard-assigned Main/Radar overrides. Applied BEFORE the mode 4 MFD search
|
|
||||||
// below so that search still skips whichever devices the operator picked.
|
|
||||||
ApplyMonitorOverride(0, &Environment.FullScreenDevice);
|
|
||||||
ApplyMonitorOverride(1, &g_nNonDualHead);
|
|
||||||
// Mode 4 (split dual 640x480): find two extra D3D devices beyond
|
// Mode 4 (split dual 640x480): find two extra D3D devices beyond
|
||||||
// FullScreenDevice and g_nNonDualHead. These need no special resolution.
|
// FullScreenDevice and g_nNonDualHead. These need no special resolution.
|
||||||
{
|
{
|
||||||
@@ -527,13 +396,6 @@ void FindVideoCards()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// [tmon] Hard-assigned MFD overrides (only meaningful with -tmfds 4).
|
|
||||||
ApplyMonitorOverride(2, &g_nMFD1);
|
|
||||||
ApplyMonitorOverride(3, &g_nMFD2);
|
|
||||||
// [displaylog] Write gos-displays.txt with the full enumeration and the role
|
|
||||||
// assignment. SPEW is compiled out of shipping builds, so a file is the only way
|
|
||||||
// the operator can see why a display did or did not get picked up.
|
|
||||||
LogDisplayDevices();
|
|
||||||
// jcem
|
// jcem
|
||||||
//
|
//
|
||||||
// Check any known video cards have up to date drivers (Only on Windows9x)
|
// Check any known video cards have up to date drivers (Only on Windows9x)
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "MemoryManager.hpp"
|
#include "MemoryManager.hpp"
|
||||||
#include "DirectX.hpp"
|
#include "DirectX.hpp"
|
||||||
#include "FileIO.hpp"
|
#include "FileIO.hpp"
|
||||||
#include <stdlib.h> // [fpslog] atexit() for the frame pacing summary
|
|
||||||
#include "ControlManager.hpp"
|
#include "ControlManager.hpp"
|
||||||
#include "LocalizationManager.hpp"
|
#include "LocalizationManager.hpp"
|
||||||
#include "Time.hpp"
|
#include "Time.hpp"
|
||||||
@@ -842,257 +841,6 @@ HRESULT App_Render( LPDIRECT3DDEVICE7 pd3dDevice,LPDIRECTDRAWSURFACE7 pddsTextur
|
|||||||
//sanghoon end
|
//sanghoon end
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// [fpslog] Frame pacing report, written to gos-fps.txt next to the executable.
|
|
||||||
//
|
|
||||||
// Off unless the -fps switch is given, so a normal pod run does no extra work at all.
|
|
||||||
// Set from MW4Application's command-line parser.
|
|
||||||
//
|
|
||||||
int g_nFpsLog = 0;
|
|
||||||
|
|
||||||
// The engine's own FrameRate readout is inside #ifdef LAB_ONLY, so it exists only in
|
|
||||||
// MW4pro.exe. This works in Release builds, which is what actually runs on the pods.
|
|
||||||
//
|
|
||||||
// Average FPS on its own hides stutter: 60 fps average is indistinguishable from 60 fps
|
|
||||||
// with a dropped frame every second. So this also reports the slowest frames, which is
|
|
||||||
// where rhythmic hitching shows up.
|
|
||||||
//
|
|
||||||
// IMPORTANT - why the output is buffered:
|
|
||||||
// The first version wrote one line per second straight to the file. That put a WriteFile
|
|
||||||
// on the render thread every second, and its cost landed in the NEXT frame's measurement
|
|
||||||
// (the frame time is recorded before the line is emitted). The result was exactly one
|
|
||||||
// inflated frame per second, forever - the tool was reporting its own overhead as the
|
|
||||||
// game's worst frame. Everything is now accumulated in memory and flushed only when the
|
|
||||||
// buffer fills or at exit, so a normal session performs no writes at all while running.
|
|
||||||
// Trade-off: a hard crash loses the un-flushed tail. gos-displays.txt is the
|
|
||||||
// crash-survivable log; this one is a measurement instrument.
|
|
||||||
//
|
|
||||||
static char s_szFpsBuf[131072]; // ~2000 lines, about 33 minutes at 1 line/sec
|
|
||||||
static int s_nFpsBufUsed = 0;
|
|
||||||
static HANDLE s_hFpsFile = INVALID_HANDLE_VALUE;
|
|
||||||
static int s_bFpsAtExit = 0;
|
|
||||||
|
|
||||||
// Whole-session frame time histogram in 0.5 ms buckets (0 - 1000 ms). Lets the true
|
|
||||||
// session-wide percentiles be computed at exit without retaining every frame time.
|
|
||||||
#define FPS_HIST_BUCKETS 2000
|
|
||||||
static DWORD s_adwFpsHist[FPS_HIST_BUCKETS];
|
|
||||||
static DWORD s_dwFpsFrames = 0;
|
|
||||||
static double s_dFpsTotalMS = 0.0;
|
|
||||||
static DWORD s_dwFpsHitches = 0;
|
|
||||||
|
|
||||||
static void GOS_FpsFlush( void )
|
|
||||||
{
|
|
||||||
DWORD dwWritten=0;
|
|
||||||
|
|
||||||
if( s_nFpsBufUsed<=0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( s_hFpsFile==INVALID_HANDLE_VALUE )
|
|
||||||
{
|
|
||||||
char szDir[MAX_PATH];
|
|
||||||
char szPath[MAX_PATH];
|
|
||||||
|
|
||||||
szDir[0]='\0';
|
|
||||||
GetModuleFileNameA( NULL, szDir, sizeof(szDir) );
|
|
||||||
char* pSlash=strrchr( szDir, '\\' );
|
|
||||||
if( pSlash )
|
|
||||||
*(pSlash+1)='\0';
|
|
||||||
else
|
|
||||||
szDir[0]='\0';
|
|
||||||
sprintf( szPath, "%sgos-fps.txt", szDir );
|
|
||||||
|
|
||||||
// FILE_SHARE_READ so the file can be read while the game is still running.
|
|
||||||
s_hFpsFile=CreateFileA( szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
||||||
if( s_hFpsFile==INVALID_HANDLE_VALUE )
|
|
||||||
{
|
|
||||||
s_nFpsBufUsed=0; // drop it rather than retry every second
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteFile( s_hFpsFile, s_szFpsBuf, (DWORD)s_nFpsBufUsed, &dwWritten, NULL );
|
|
||||||
s_nFpsBufUsed=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mean frame time (ms) of the slowest dwCount frames of the whole session.
|
|
||||||
static double GOS_FpsSlowestMean( DWORD dwCount )
|
|
||||||
{
|
|
||||||
double dSum=0.0;
|
|
||||||
DWORD dwSeen=0;
|
|
||||||
int b;
|
|
||||||
|
|
||||||
if( dwCount<1 )
|
|
||||||
dwCount=1;
|
|
||||||
|
|
||||||
for( b=FPS_HIST_BUCKETS-1; b>=0 && dwSeen<dwCount; b-- )
|
|
||||||
{
|
|
||||||
DWORD dwTake=s_adwFpsHist[b];
|
|
||||||
if( !dwTake )
|
|
||||||
continue;
|
|
||||||
if( dwTake>dwCount-dwSeen )
|
|
||||||
dwTake=dwCount-dwSeen;
|
|
||||||
dSum += (((double)b+0.5)/2.0)*(double)dwTake;
|
|
||||||
dwSeen += dwTake;
|
|
||||||
}
|
|
||||||
return dwSeen ? (dSum/(double)dwSeen) : 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GOS_FpsAppend( const char* pszLine )
|
|
||||||
{
|
|
||||||
int nLen=(int)strlen(pszLine);
|
|
||||||
|
|
||||||
if( nLen<=0 )
|
|
||||||
return;
|
|
||||||
if( s_nFpsBufUsed+nLen > (int)sizeof(s_szFpsBuf) )
|
|
||||||
GOS_FpsFlush(); // buffer full: one write, rarely
|
|
||||||
if( s_nFpsBufUsed+nLen > (int)sizeof(s_szFpsBuf) )
|
|
||||||
return; // still no room: give up quietly
|
|
||||||
|
|
||||||
memcpy( s_szFpsBuf+s_nFpsBufUsed, pszLine, nLen );
|
|
||||||
s_nFpsBufUsed += nLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: must be __cdecl. GameOS builds with /Gz (__stdcall by default), but atexit()
|
|
||||||
// takes a __cdecl callback, so without this the compiler rejects it with C2664.
|
|
||||||
static void __cdecl GOS_FpsAtExit( void )
|
|
||||||
{
|
|
||||||
char szLine[256];
|
|
||||||
|
|
||||||
if( s_dwFpsFrames )
|
|
||||||
{
|
|
||||||
// True session-wide percentiles, which a per-second bucket cannot give:
|
|
||||||
// 1% of 60 frames is 0, so a "1% low" computed per second degenerates into
|
|
||||||
// "the single worst frame". Over a whole session it is meaningful.
|
|
||||||
double dAvgMS = s_dFpsTotalMS/(double)s_dwFpsFrames;
|
|
||||||
double dOnePct = GOS_FpsSlowestMean( s_dwFpsFrames/100 );
|
|
||||||
double dTenthPct= GOS_FpsSlowestMean( s_dwFpsFrames/1000 );
|
|
||||||
|
|
||||||
GOS_FpsAppend( "\r\n" );
|
|
||||||
GOS_FpsAppend( "session summary\r\n" );
|
|
||||||
GOS_FpsAppend( "---------------\r\n" );
|
|
||||||
sprintf( szLine, " frames : %lu\r\n", (unsigned long)s_dwFpsFrames );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
sprintf( szLine, " elapsed : %.1f s\r\n", s_dFpsTotalMS/1000.0 );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
sprintf( szLine, " average : %.1f fps (%.2f ms)\r\n",
|
|
||||||
(dAvgMS>0.0)?1000.0/dAvgMS:0.0, dAvgMS );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
sprintf( szLine, " 1%% low : %.1f fps (%.2f ms)\r\n",
|
|
||||||
(dOnePct>0.0)?1000.0/dOnePct:0.0, dOnePct );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
sprintf( szLine, " 0.1%% low : %.1f fps (%.2f ms)\r\n",
|
|
||||||
(dTenthPct>0.0)?1000.0/dTenthPct:0.0, dTenthPct );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
sprintf( szLine, " frames over 2x avg: %lu\r\n", (unsigned long)s_dwFpsHitches );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
}
|
|
||||||
|
|
||||||
GOS_FpsFlush();
|
|
||||||
if( s_hFpsFile!=INVALID_HANDLE_VALUE )
|
|
||||||
{
|
|
||||||
CloseHandle( s_hFpsFile );
|
|
||||||
s_hFpsFile=INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GOS_LogFrameRate( float fFrameRate )
|
|
||||||
{
|
|
||||||
enum { MAX_WORST = 64 };
|
|
||||||
static float s_afWorst[MAX_WORST]; // longest frames this second, descending ms
|
|
||||||
static int s_nWorst = 0;
|
|
||||||
static float s_fAccumMS= 0.0f;
|
|
||||||
static float s_fSumMS = 0.0f;
|
|
||||||
static float s_fMaxMS = 0.0f;
|
|
||||||
static int s_nFrames = 0;
|
|
||||||
static int s_nSecond = 0;
|
|
||||||
char szLine[256];
|
|
||||||
|
|
||||||
if( !g_nFpsLog ) // [fpslog] -fps not given: do nothing
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( fFrameRate<=0.0f )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( !s_bFpsAtExit )
|
|
||||||
{
|
|
||||||
s_bFpsAtExit=1;
|
|
||||||
atexit( GOS_FpsAtExit ); // flush the buffer + write the summary
|
|
||||||
}
|
|
||||||
|
|
||||||
float fMS = 1000.0f/fFrameRate;
|
|
||||||
|
|
||||||
s_nFrames++;
|
|
||||||
s_fSumMS += fMS;
|
|
||||||
s_fAccumMS += fMS;
|
|
||||||
if( fMS>s_fMaxMS )
|
|
||||||
s_fMaxMS = fMS;
|
|
||||||
|
|
||||||
// Whole-session histogram (0.5 ms buckets, saturating at the top bucket).
|
|
||||||
int nBucket=(int)(fMS*2.0f);
|
|
||||||
if( nBucket<0 )
|
|
||||||
nBucket=0;
|
|
||||||
if( nBucket>=FPS_HIST_BUCKETS )
|
|
||||||
nBucket=FPS_HIST_BUCKETS-1;
|
|
||||||
s_adwFpsHist[nBucket]++;
|
|
||||||
s_dwFpsFrames++;
|
|
||||||
s_dFpsTotalMS += fMS;
|
|
||||||
|
|
||||||
// Maintain the longest frames of this second, sorted longest-first.
|
|
||||||
if( s_nWorst<MAX_WORST )
|
|
||||||
s_afWorst[s_nWorst++] = fMS;
|
|
||||||
else if( fMS>s_afWorst[MAX_WORST-1] )
|
|
||||||
s_afWorst[MAX_WORST-1] = fMS;
|
|
||||||
for( int i=s_nWorst-1; i>0 && s_afWorst[i]>s_afWorst[i-1]; i-- ) {
|
|
||||||
float fTmp=s_afWorst[i]; s_afWorst[i]=s_afWorst[i-1]; s_afWorst[i-1]=fTmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( s_fAccumMS<1000.0f )
|
|
||||||
return;
|
|
||||||
|
|
||||||
float fAvgMS = s_fSumMS/(float)s_nFrames;
|
|
||||||
float fAvgFPS = 1000.0f/fAvgMS;
|
|
||||||
|
|
||||||
// 5% low, not 1%: at 60 samples a "1% low" is a single frame, which is just the
|
|
||||||
// worst-frame column restated. Worst 3-of-60 is a number that actually differs.
|
|
||||||
int nLow = s_nFrames/20;
|
|
||||||
if( nLow<3 )
|
|
||||||
nLow = 3;
|
|
||||||
if( nLow>s_nWorst )
|
|
||||||
nLow = s_nWorst;
|
|
||||||
float fLowSum=0.0f;
|
|
||||||
for( int j=0; j<nLow; j++ )
|
|
||||||
fLowSum += s_afWorst[j];
|
|
||||||
float fLowFPS = (fLowSum>0.0f) ? (1000.0f/(fLowSum/(float)nLow)) : 0.0f;
|
|
||||||
|
|
||||||
// Frames that took more than twice the average - i.e. visible hitches.
|
|
||||||
int nHitch=0;
|
|
||||||
for( int k=0; k<s_nWorst; k++ )
|
|
||||||
if( s_afWorst[k]>fAvgMS*2.0f )
|
|
||||||
nHitch++;
|
|
||||||
s_dwFpsHitches += nHitch;
|
|
||||||
|
|
||||||
if( s_nSecond==0 )
|
|
||||||
GOS_FpsAppend( " sec frames avg fps 5% low worst frame hitches\r\n"
|
|
||||||
" --- ------ ------- ------ ----------- -------\r\n" );
|
|
||||||
|
|
||||||
sprintf( szLine, "%5d %6d %8.1f %8.1f %8.1f ms %s%d\r\n",
|
|
||||||
s_nSecond, s_nFrames, fAvgFPS, fLowFPS, s_fMaxMS,
|
|
||||||
nHitch ? "<-- " : "", nHitch );
|
|
||||||
GOS_FpsAppend( szLine );
|
|
||||||
|
|
||||||
s_nSecond++;
|
|
||||||
s_nFrames = 0;
|
|
||||||
s_nWorst = 0;
|
|
||||||
s_fSumMS = 0.0f;
|
|
||||||
s_fMaxMS = 0.0f;
|
|
||||||
// A single frame longer than a second (a level load) must not spill into the next
|
|
||||||
// buckets and emit a run of bogus one-frame rows, which is what used to happen.
|
|
||||||
s_fAccumMS-= 1000.0f;
|
|
||||||
if( s_fAccumMS<0.0f || s_fAccumMS>=1000.0f )
|
|
||||||
s_fAccumMS = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Update the display (call all the 'Update Renderers' routines
|
// Update the display (call all the 'Update Renderers' routines
|
||||||
//
|
//
|
||||||
@@ -1137,7 +885,6 @@ void gos_UpdateDisplay( bool Everything )
|
|||||||
|
|
||||||
frameRate=(float)frequency/(float)thisframe; // Calculate frame rate based on clock frequency
|
frameRate=(float)frequency/(float)thisframe; // Calculate frame rate based on clock frequency
|
||||||
StartOfRenderTime=GetCycles();
|
StartOfRenderTime=GetCycles();
|
||||||
GOS_LogFrameRate( frameRate ); // [fpslog] per-second pacing report
|
|
||||||
//
|
//
|
||||||
// Don't trigger watchdog timer
|
// Don't trigger watchdog timer
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <d3d.h>
|
#include <d3d.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h> // [displaylog] va_list/va_start for HSH_LogInit
|
|
||||||
//#include "directx.hpp"
|
//#include "directx.hpp"
|
||||||
#include "dxrasterizer.hpp"
|
#include "dxrasterizer.hpp"
|
||||||
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
|
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
|
||||||
@@ -523,185 +522,19 @@ void CHSH_Device::SetRenderTargetTexture()
|
|||||||
const DWORD dwFlags = DDSCL_SETFOCUSWINDOW | DDSCL_CREATEDEVICEWINDOW |
|
const DWORD dwFlags = DDSCL_SETFOCUSWINDOW | DDSCL_CREATEDEVICEWINDOW |
|
||||||
DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
|
DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
|
||||||
|
|
||||||
// [panelcoop] Which SetCooperativeLevel form the secondary panels use; 0 = legacy.
|
|
||||||
// Selected with -tcoop <0-3>. See CHSH_Device::InitFirst for what each one does.
|
|
||||||
int g_nHshCoopMode = 0;
|
|
||||||
|
|
||||||
extern HRESULT wDirectDrawCreateEx( GUID* lpGUID, void** lplpDD, REFIID iid, IUnknown* pUnkOuter );
|
extern HRESULT wDirectDrawCreateEx( GUID* lpGUID, void** lplpDD, REFIID iid, IUnknown* pUnkOuter );
|
||||||
|
|
||||||
//
|
|
||||||
// [displaylog] Append a printf-style line to gos-displays.txt, the report VideoCard.cpp
|
|
||||||
// writes at device-enumeration time.
|
|
||||||
//
|
|
||||||
// This exists because the panel initialisation below discarded every HRESULT it produced
|
|
||||||
// and returned true unconditionally. A display that failed to open therefore generated no
|
|
||||||
// error, no crash and no log entry - the monitor simply stayed on the desktop, which is
|
|
||||||
// indistinguishable from the display never having been asked to open at all.
|
|
||||||
//
|
|
||||||
void HSH_LogInit( const char* fmt, ... )
|
|
||||||
{
|
|
||||||
char szDir[MAX_PATH];
|
|
||||||
char szPath[MAX_PATH];
|
|
||||||
char szLine[512];
|
|
||||||
DWORD dwWritten=0;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
szDir[0]='\0';
|
|
||||||
GetModuleFileNameA( NULL, szDir, sizeof(szDir) );
|
|
||||||
char* pSlash=strrchr( szDir, '\\' );
|
|
||||||
if( pSlash )
|
|
||||||
*(pSlash+1)='\0';
|
|
||||||
else
|
|
||||||
szDir[0]='\0';
|
|
||||||
sprintf( szPath, "%sgos-displays.txt", szDir );
|
|
||||||
|
|
||||||
HANDLE hFile=CreateFileA( szPath, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
|
||||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
||||||
if( hFile==INVALID_HANDLE_VALUE )
|
|
||||||
return;
|
|
||||||
SetFilePointer( hFile, 0, NULL, FILE_END );
|
|
||||||
|
|
||||||
va_start( ap, fmt );
|
|
||||||
vsprintf( szLine, fmt, ap );
|
|
||||||
va_end( ap );
|
|
||||||
|
|
||||||
WriteFile( hFile, szLine, (DWORD)strlen(szLine), &dwWritten, NULL );
|
|
||||||
CloseHandle( hFile );
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// [displaylog] Readable name for the result codes multi-device fullscreen init can return.
|
|
||||||
//
|
|
||||||
static const char* HSH_HRName( HRESULT hr )
|
|
||||||
{
|
|
||||||
switch( hr )
|
|
||||||
{
|
|
||||||
case DD_OK: return "DD_OK";
|
|
||||||
case DDERR_ALREADYINITIALIZED: return "DDERR_ALREADYINITIALIZED";
|
|
||||||
case DDERR_CANNOTATTACHSURFACE: return "DDERR_CANNOTATTACHSURFACE";
|
|
||||||
case DDERR_DIRECTDRAWALREADYCREATED: return "DDERR_DIRECTDRAWALREADYCREATED";
|
|
||||||
case DDERR_EXCLUSIVEMODEALREADYSET: return "DDERR_EXCLUSIVEMODEALREADYSET";
|
|
||||||
case DDERR_GENERIC: return "DDERR_GENERIC";
|
|
||||||
case DDERR_HWNDALREADYSET: return "DDERR_HWNDALREADYSET";
|
|
||||||
case DDERR_HWNDSUBCLASSED: return "DDERR_HWNDSUBCLASSED";
|
|
||||||
case DDERR_INCOMPATIBLEPRIMARY: return "DDERR_INCOMPATIBLEPRIMARY";
|
|
||||||
case DDERR_INVALIDCAPS: return "DDERR_INVALIDCAPS";
|
|
||||||
case DDERR_INVALIDMODE: return "DDERR_INVALIDMODE";
|
|
||||||
case DDERR_INVALIDOBJECT: return "DDERR_INVALIDOBJECT";
|
|
||||||
case DDERR_INVALIDPARAMS: return "DDERR_INVALIDPARAMS";
|
|
||||||
case DDERR_INVALIDPIXELFORMAT: return "DDERR_INVALIDPIXELFORMAT";
|
|
||||||
case DDERR_INVALIDSURFACETYPE: return "DDERR_INVALIDSURFACETYPE";
|
|
||||||
case DDERR_NOCOOPERATIVELEVELSET: return "DDERR_NOCOOPERATIVELEVELSET";
|
|
||||||
case DDERR_NODIRECTDRAWHW: return "DDERR_NODIRECTDRAWHW";
|
|
||||||
case DDERR_NOEXCLUSIVEMODE: return "DDERR_NOEXCLUSIVEMODE";
|
|
||||||
case DDERR_NO3D: return "DDERR_NO3D";
|
|
||||||
case DDERR_NOTFOUND: return "DDERR_NOTFOUND";
|
|
||||||
case DDERR_OUTOFMEMORY: return "DDERR_OUTOFMEMORY";
|
|
||||||
case DDERR_OUTOFVIDEOMEMORY: return "DDERR_OUTOFVIDEOMEMORY";
|
|
||||||
case DDERR_PRIMARYSURFACEALREADYEXISTS: return "DDERR_PRIMARYSURFACEALREADYEXISTS";
|
|
||||||
case DDERR_SURFACEBUSY: return "DDERR_SURFACEBUSY";
|
|
||||||
case DDERR_UNSUPPORTED: return "DDERR_UNSUPPORTED";
|
|
||||||
case DDERR_UNSUPPORTEDMODE: return "DDERR_UNSUPPORTEDMODE";
|
|
||||||
case DDERR_WRONGMODE: return "DDERR_WRONGMODE";
|
|
||||||
}
|
|
||||||
return "(unrecognised)";
|
|
||||||
}
|
|
||||||
|
|
||||||
// [displaylog] Log one DirectDraw call result, flagging failures.
|
|
||||||
static void HSH_CheckHR( const char* pszWhat, HRESULT hr )
|
|
||||||
{
|
|
||||||
HSH_LogInit( " %-24s = 0x%08lX %s%s\r\n",
|
|
||||||
pszWhat, (unsigned long)hr, HSH_HRName(hr),
|
|
||||||
FAILED(hr) ? " *** FAILED ***" : "" );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CHSH_Device::InitFirst(int devicenum,DWORD resx,DWORD resy, CHSH_Device* pOtherHSHD/* = NULL*/)
|
bool CHSH_Device::InitFirst(int devicenum,DWORD resx,DWORD resy, CHSH_Device* pOtherHSHD/* = NULL*/)
|
||||||
{
|
{
|
||||||
m_pOtherHSHD = pOtherHSHD;
|
m_pOtherHSHD = pOtherHSHD;
|
||||||
if (pOtherHSHD) {
|
if (pOtherHSHD) {
|
||||||
pDD = pOtherHSHD->pDD;
|
pDD = pOtherHSHD->pDD;
|
||||||
HSH_LogInit( " InitFirst : shares the DirectDraw object of another panel\r\n" );
|
|
||||||
} else {
|
} else {
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
HSH_LogInit( " InitFirst : device %d, %lux%lu 16bpp 60Hz\r\n",
|
|
||||||
devicenum, (unsigned long)resx, (unsigned long)resy );
|
|
||||||
if( devicenum<0 || devicenum>=(int)NumDevices ) {
|
|
||||||
HSH_LogInit( " *** device index out of range - this panel cannot open ***\r\n" );
|
|
||||||
pDD=0;
|
|
||||||
size_back.cx=resx;
|
|
||||||
size_back.cy=resy;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//1. Resolution change.
|
//1. Resolution change.
|
||||||
hr=wDirectDrawCreateEx( &DeviceArray[devicenum].DeviceGUID , (VOID**) &(pDD), IID_IDirectDraw7, NULL );
|
wDirectDrawCreateEx( &DeviceArray[devicenum].DeviceGUID , (VOID**) &(pDD), IID_IDirectDraw7, NULL );
|
||||||
HSH_CheckHR( "DirectDrawCreateEx", hr );
|
pDD->SetCooperativeLevel(hWindow, dwFlags );
|
||||||
if( FAILED(hr) || !pDD ) {
|
|
||||||
pDD=0;
|
|
||||||
size_back.cx=resx;
|
|
||||||
size_back.cy=resy;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// [panelcoop] Establish the cooperative level for this panel.
|
|
||||||
//
|
|
||||||
// The legacy call asks every panel to be BOTH the process focus window and its own
|
|
||||||
// device window, using the one shared hWindow, after the main display device has
|
|
||||||
// already taken exclusive mode on it. XP's DirectDraw and dgVoodoo2 tolerate that;
|
|
||||||
// Windows 10's does not - it returns DDERR_EXCLUSIVEMODEALREADYSET on the first
|
|
||||||
// panel and DDERR_INVALIDPARAMS on the rest, after which the primary surface fails
|
|
||||||
// with DDERR_NOCOOPERATIVELEVELSET.
|
|
||||||
//
|
|
||||||
// The main device (DXRasterizer.cpp, EnterFullScreenMode) already uses the correct
|
|
||||||
// two-call form: SETFOCUSWINDOW on its own, then EXCLUSIVE|FULLSCREEN. The panels
|
|
||||||
// never were. -tcoop picks the form so the alternatives can be compared on real
|
|
||||||
// hardware without a rebuild between each one.
|
|
||||||
switch( g_nHshCoopMode )
|
|
||||||
{
|
|
||||||
case 1: // one call, but stop claiming the focus window
|
|
||||||
HSH_LogInit( " coop 1: CREATEDEVICEWINDOW|EXCLUSIVE|FULLSCREEN\r\n" );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow,
|
|
||||||
DDSCL_CREATEDEVICEWINDOW|DDSCL_ALLOWREBOOT|DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN );
|
|
||||||
HSH_CheckHR( "SetCoopLevel", hr );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // two calls, DirectDraw makes a device window for this monitor
|
|
||||||
HSH_LogInit( " coop 2: SETFOCUSWINDOW, then CREATEDEVICEWINDOW|EXCLUSIVE|FULLSCREEN\r\n" );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow, DDSCL_SETFOCUSWINDOW );
|
|
||||||
HSH_CheckHR( "SetCoopLevel(focus)", hr );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow,
|
|
||||||
DDSCL_CREATEDEVICEWINDOW|DDSCL_ALLOWREBOOT|DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN );
|
|
||||||
HSH_CheckHR( "SetCoopLevel(device)", hr );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // two calls, exactly what the main display device does
|
|
||||||
HSH_LogInit( " coop 3: SETFOCUSWINDOW, then EXCLUSIVE|FULLSCREEN\r\n" );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow, DDSCL_SETFOCUSWINDOW );
|
|
||||||
HSH_CheckHR( "SetCoopLevel(focus)", hr );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN );
|
|
||||||
HSH_CheckHR( "SetCoopLevel(device)", hr );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // exclusive only - the main device has ALREADY set the focus window,
|
|
||||||
// so re-claiming it is what makes the first panel collide with it.
|
|
||||||
HSH_LogInit( " coop 4: EXCLUSIVE|FULLSCREEN only (no focus claim)\r\n" );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN );
|
|
||||||
HSH_CheckHR( "SetCoopLevel", hr );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5: // as 4, plus ALLOWREBOOT (the legacy flag set minus the two window flags)
|
|
||||||
HSH_LogInit( " coop 5: ALLOWREBOOT|EXCLUSIVE|FULLSCREEN (no focus claim)\r\n" );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow,
|
|
||||||
DDSCL_ALLOWREBOOT|DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN );
|
|
||||||
HSH_CheckHR( "SetCoopLevel", hr );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // 0 = legacy, byte-for-byte the original behaviour
|
|
||||||
HSH_LogInit( " coop 0: legacy combined call\r\n" );
|
|
||||||
hr=pDD->SetCooperativeLevel( hWindow, dwFlags );
|
|
||||||
HSH_CheckHR( "SetCoopLevel", hr );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
hr=pDD->SetDisplayMode( resx, resy, 16, 60, 0 );
|
hr=pDD->SetDisplayMode( resx, resy, 16, 60, 0 );
|
||||||
HSH_CheckHR( "SetDisplayMode", hr );
|
|
||||||
}
|
}
|
||||||
size_back.cx=resx;
|
size_back.cx=resx;
|
||||||
size_back.cy=resy;
|
size_back.cy=resy;
|
||||||
@@ -780,46 +613,6 @@ char *mechnames[]={
|
|||||||
};
|
};
|
||||||
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
|
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||||
|
|
||||||
// [mode 4 split-MFD fix]
|
|
||||||
// Build the MFD texture set (mech image atlas + sprite atlas) on a given DirectDraw object.
|
|
||||||
//
|
|
||||||
// Mode 4 drives two physically separate 640x480 monitors. DirectDraw binds one
|
|
||||||
// IDirectDraw7 to one monitor, so the right-hand panel necessarily has its own
|
|
||||||
// IDirectDraw7 *and* its own IDirect3DDevice7. A DirectDraw surface belongs to the
|
|
||||||
// IDirectDraw7 that created it - NOT to the physical GPU - so a texture made by the left
|
|
||||||
// device can never be sampled by the right device's D3D device, even when both monitors
|
|
||||||
// hang off the same card. Attempting it makes the runtime emulate the access with a
|
|
||||||
// VRAM->system-memory readback every time, which stalls the whole GPU. Hence: each
|
|
||||||
// device gets its own complete copy of these textures.
|
|
||||||
static void HSH_CreateMFDTextures(LPDIRECTDRAW7 pdd,
|
|
||||||
LPDIRECTDRAWSURFACE7* ppMechTexture,
|
|
||||||
LPDIRECTDRAWSURFACE7* ppSpriteTexture)
|
|
||||||
{
|
|
||||||
char temppath[MAX_PATH];
|
|
||||||
|
|
||||||
if(g_f3dtarget){
|
|
||||||
*ppMechTexture=CreatePixelFormatTexture(pdd,128,128,&DDPF_R5G6B5);//Texture for storing 3D mech image...
|
|
||||||
}else{
|
|
||||||
// MSL 5.02 Target MFD Image
|
|
||||||
*ppMechTexture=CreatePixelFormatTexture(pdd,1024,1024,&DDPF_R5G6B5);//Texture for storing 2D mech image...
|
|
||||||
//Store images for all mechs.
|
|
||||||
for(int mech=0;mech<ARRAYSIZE(mechnames);mech++){
|
|
||||||
int x=(mech%8)*128;
|
|
||||||
int y=(mech/8)*128;
|
|
||||||
|
|
||||||
sprintf(temppath,"%s\\hsh\\mfd\\%s.bmp",AssetsDirectory1,mechnames[mech]);
|
|
||||||
|
|
||||||
DrawBitmapToSurface(*ppMechTexture,x,y,temppath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(temppath,"%s\\hsh\\%s",AssetsDirectory1,"mfd_texture.bmp");
|
|
||||||
*ppSpriteTexture=CreateATextureFromFile(pdd, temppath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// [mode 4 split-MFD fix] exchange one data member between two devices.
|
|
||||||
#define HSH_SWAP_MEMBER(type, a, b) { type _hsh_swaptmp = (a); (a) = (b); (b) = _hsh_swaptmp; }
|
|
||||||
|
|
||||||
bool CHSH_Device::InitSecond(DWORD tresx,DWORD tresy)
|
bool CHSH_Device::InitSecond(DWORD tresx,DWORD tresy)
|
||||||
{
|
{
|
||||||
if (m_pOtherHSHD) {
|
if (m_pOtherHSHD) {
|
||||||
@@ -831,50 +624,25 @@ bool CHSH_Device::InitSecond(DWORD tresx,DWORD tresy)
|
|||||||
//////Front Buffer
|
//////Front Buffer
|
||||||
DDSURFACEDESC2 ddsd={0,};
|
DDSURFACEDESC2 ddsd={0,};
|
||||||
DDSCAPS2 ddsCaps;
|
DDSCAPS2 ddsCaps;
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
// [displaylog] InitFirst can now fail cleanly; without this guard every call below
|
|
||||||
// dereferences a null IDirectDraw7 and takes the process down.
|
|
||||||
if( !pDD ) {
|
|
||||||
HSH_LogInit( " InitSecond: skipped - InitFirst produced no DirectDraw object\r\n" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ddsd.dwSize = sizeof(ddsd);
|
ddsd.dwSize = sizeof(ddsd);
|
||||||
ddsd.dwFlags = DDSD_BACKBUFFERCOUNT | DDSD_CAPS;
|
ddsd.dwFlags = DDSD_BACKBUFFERCOUNT | DDSD_CAPS;
|
||||||
ddsd.dwBackBufferCount = 1;
|
ddsd.dwBackBufferCount = 1;
|
||||||
ddsd.ddsCaps.dwCaps = DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_PRIMARYSURFACE| DDSCAPS_3DDEVICE;
|
ddsd.ddsCaps.dwCaps = DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_PRIMARYSURFACE| DDSCAPS_3DDEVICE;
|
||||||
|
|
||||||
hr=pDD->CreateSurface( &ddsd, &pDDSFront, NULL );
|
pDD->CreateSurface( &ddsd, &pDDSFront, NULL );
|
||||||
HSH_CheckHR( "CreateSurface(primary)", hr );
|
|
||||||
if( FAILED(hr) || !pDDSFront ) {
|
|
||||||
pDDSFront=0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////BackBuffer
|
//////BackBuffer
|
||||||
ZeroMemory( &ddsCaps, sizeof(ddsCaps) );
|
ZeroMemory( &ddsCaps, sizeof(ddsCaps) );
|
||||||
ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
|
ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
|
||||||
hr=pDDSFront->GetAttachedSurface( &ddsCaps, &pDDSBack );
|
pDDSFront->GetAttachedSurface( &ddsCaps, &pDDSBack );
|
||||||
HSH_CheckHR( "GetAttachedSurface", hr );
|
|
||||||
if( FAILED(hr) || !pDDSBack ) {
|
|
||||||
pDDSBack=0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////3D Device
|
//////3D Device
|
||||||
LPDIRECT3D7 pD3D=0;
|
LPDIRECT3D7 pD3D;
|
||||||
hr=pDD->QueryInterface( IID_IDirect3D7, (VOID**)&(pD3D) );
|
pDD->QueryInterface( IID_IDirect3D7, (VOID**)&(pD3D) );
|
||||||
HSH_CheckHR( "QueryInterface(D3D7)", hr );
|
pD3D->CreateDevice(IID_IDirect3DHALDevice,pDDSBack,&pD3DDevice);
|
||||||
if( FAILED(hr) || !pD3D )
|
|
||||||
return false;
|
|
||||||
hr=pD3D->CreateDevice(IID_IDirect3DHALDevice,pDDSBack,&pD3DDevice);
|
|
||||||
HSH_CheckHR( "CreateDevice(HAL)", hr );
|
|
||||||
pD3D->Release();
|
pD3D->Release();
|
||||||
if( FAILED(hr) || !pD3DDevice ) {
|
|
||||||
pD3DDevice=0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3DVIEWPORT7 vp = { 0, 0, size_back.cx, size_back.cy, 0.0f, 1.0f };
|
D3DVIEWPORT7 vp = { 0, 0, size_back.cx, size_back.cy, 0.0f, 1.0f };
|
||||||
pD3DDevice->SetViewport( &vp );
|
pD3DDevice->SetViewport( &vp );
|
||||||
@@ -1210,10 +978,7 @@ bool CMR_Device::InitFirst()
|
|||||||
bool CMR_Device::InitSecond()
|
bool CMR_Device::InitSecond()
|
||||||
{
|
{
|
||||||
map_loaded=false;
|
map_loaded=false;
|
||||||
// [panelcoop] see CRadar_Device::InitSecond - do not build surfaces on a device
|
CHSH_Device::InitSecond(0,0); // no off screen - do not create Target Texture.
|
||||||
// that never got a cooperative level.
|
|
||||||
if( !CHSH_Device::InitSecond(0,0) ) // no off screen - do not create Target Texture.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
char temppath[MAX_PATH],temppatha[MAX_PATH];
|
char temppath[MAX_PATH],temppatha[MAX_PATH];
|
||||||
|
|
||||||
@@ -1303,9 +1068,9 @@ bool CMR_Device::LoadMRTargetTexture(const char *mechtexturename)
|
|||||||
|
|
||||||
CMR_Device mr_device;
|
CMR_Device mr_device;
|
||||||
|
|
||||||
// Mode 4: the right-hand 640x480 MFD device. It owns a SEPARATE IDirectDraw7 and
|
// Mode 4: the right-hand 640x480 MFD device. Shares VRAM with mfd_device
|
||||||
// IDirect3DDevice7 (one DirectDraw device is bound to one monitor), so it also owns a
|
// (same GPU), so mfd_device's pDDSTarget texture can be passed to this
|
||||||
// complete, independent copy of the MFD texture set - see HSH_CreateMFDTextures.
|
// device's pD3DDevice->SetTexture without a copy.
|
||||||
CMFDRight_Device mfd_device_right;
|
CMFDRight_Device mfd_device_right;
|
||||||
|
|
||||||
bool CMFDRight_Device::InitFirst()
|
bool CMFDRight_Device::InitFirst()
|
||||||
@@ -1316,35 +1081,8 @@ bool CMFDRight_Device::InitFirst()
|
|||||||
|
|
||||||
bool CMFDRight_Device::InitSecond()
|
bool CMFDRight_Device::InitSecond()
|
||||||
{
|
{
|
||||||
// Create flip chain, D3D device, render target (1024x512) and fonts.
|
// Create flip chain, D3D device and a render-target slot (1024x512).
|
||||||
// [displaylog] If the device never came up, everything below dereferences a null
|
return CHSH_Device::InitSecond(1024, 512);
|
||||||
// IDirect3DDevice7; bail out and leave the reason in gos-displays.txt.
|
|
||||||
if( !CHSH_Device::InitSecond(1024, 512) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Independent copy of the MFD texture set owned by THIS DirectDraw object.
|
|
||||||
HSH_CreateMFDTextures(pDD, &pDDSMechTexture, &pDDSTexture);
|
|
||||||
pDDSDamageTexture = 0; // created on demand by LoadDamageTexture
|
|
||||||
pDDSTargetTexture = 0; // created on demand by LoadTargetTexture
|
|
||||||
tw = 512;
|
|
||||||
th = 512;
|
|
||||||
|
|
||||||
D3DMATERIAL7 mtrl;
|
|
||||||
ZeroMemory( &mtrl, sizeof(mtrl) );
|
|
||||||
mtrl.diffuse.r = mtrl.diffuse.g = mtrl.diffuse.b = 1.0f;
|
|
||||||
mtrl.ambient.r = mtrl.ambient.g = mtrl.ambient.b = 1.0f;
|
|
||||||
pD3DDevice->SetMaterial( &mtrl );
|
|
||||||
pD3DDevice->SetRenderState( D3DRENDERSTATE_AMBIENT, 0xffffffff );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMFDRight_Device::Release()
|
|
||||||
{
|
|
||||||
SAFE_RELEASE(pDDSMechTexture);
|
|
||||||
SAFE_RELEASE(pDDSDamageTexture);
|
|
||||||
SAFE_RELEASE(pDDSTargetTexture);
|
|
||||||
return CHSH_Device::Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////// CRadar_Device ///////////////////////////////////
|
/////////////////////////////////// CRadar_Device ///////////////////////////////////
|
||||||
@@ -1390,11 +1128,7 @@ bool CRadar_Device::InitFirst()
|
|||||||
bool CRadar_Device::InitSecond()
|
bool CRadar_Device::InitSecond()
|
||||||
{
|
{
|
||||||
ZeroMemory(recent_state,sizeof(recent_state));
|
ZeroMemory(recent_state,sizeof(recent_state));
|
||||||
// [panelcoop] If the device did not come up, stop here. Continuing would create
|
CHSH_Device::InitSecond(512,1024); // 480 x 640
|
||||||
// surfaces on a DirectDraw object with no cooperative level - they come back NULL,
|
|
||||||
// and the first GetDC on one faults (IDirectDrawSurface7 vtable +0x44).
|
|
||||||
if( !CHSH_Device::InitSecond(512,1024) ) // 480 x 640
|
|
||||||
return false;
|
|
||||||
MapDrawn=false;
|
MapDrawn=false;
|
||||||
char temppath[MAX_PATH],temppatha[MAX_PATH];
|
char temppath[MAX_PATH],temppatha[MAX_PATH];
|
||||||
|
|
||||||
@@ -1634,7 +1368,7 @@ static DWORD channel_color[6]={
|
|||||||
|
|
||||||
|
|
||||||
CMFD_Device::CMFD_Device()
|
CMFD_Device::CMFD_Device()
|
||||||
: ch((DWORD)-1), m_pRightDevice(NULL), m_bSwappedToRight(false)
|
: ch((DWORD)-1), m_pRightDevice(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1648,11 +1382,9 @@ bool CMFD_Device::InitFirst()
|
|||||||
// device here so both displays are ready before InitSecond runs.
|
// device here so both displays are ready before InitSecond runs.
|
||||||
if (g_nTypeOfMFDs == 4) {
|
if (g_nTypeOfMFDs == 4) {
|
||||||
m_pRightDevice = &mfd_device_right;
|
m_pRightDevice = &mfd_device_right;
|
||||||
// [panelcoop] Report the REAL result. This used to return true unconditionally,
|
CHSH_Device::InitFirst(g_nMFD1, 640, 480); // left device
|
||||||
// which hid a failed panel and let the per-frame code run on null surfaces.
|
mfd_device_right.InitFirst(); // right device display mode
|
||||||
bool bLeft = CHSH_Device::InitFirst(g_nMFD1, 640, 480);
|
return true;
|
||||||
bool bRight = mfd_device_right.InitFirst();
|
|
||||||
return (bLeft && bRight);
|
|
||||||
}
|
}
|
||||||
m_pRightDevice = NULL;
|
m_pRightDevice = NULL;
|
||||||
return CHSH_Device::InitFirst(g_nDualHead,1280,480);
|
return CHSH_Device::InitFirst(g_nDualHead,1280,480);
|
||||||
@@ -1660,17 +1392,39 @@ bool CMFD_Device::InitFirst()
|
|||||||
|
|
||||||
bool CMFD_Device::InitSecond()
|
bool CMFD_Device::InitSecond()
|
||||||
{
|
{
|
||||||
// [displaylog] see CMFDRight_Device::InitSecond - a failed device must not fall
|
CHSH_Device::InitSecond(1024,512); // 640 x 480
|
||||||
// through into the texture/material setup below.
|
if(g_f3dtarget){
|
||||||
if( !CHSH_Device::InitSecond(1024,512) ) // 640 x 480
|
pDDSMechTexture=CreatePixelFormatTexture(pDD,128,128,&DDPF_R5G6B5);//Texture for storing 3D mech image...
|
||||||
return false;
|
}else{
|
||||||
|
// MSL 5.02 Target MFD Image
|
||||||
|
// pDDSMechTexture=CreatePixelFormatTexture(pDD,512,512,&DDPF_R5G6B5);//texture for storing 2D mech image...
|
||||||
|
pDDSMechTexture=CreatePixelFormatTexture(pDD,1024,1024,&DDPF_R5G6B5);//Texture for storing 2D mech image...
|
||||||
|
//Store images for all mechs.
|
||||||
|
for(int mech=0;mech<ARRAYSIZE(mechnames);mech++){
|
||||||
|
// MSL 5.02 Target MFD Image
|
||||||
|
// int x=(mech%4)*128;
|
||||||
|
// int y=(mech/4)*128;
|
||||||
|
int x=(mech%8)*128;
|
||||||
|
int y=(mech/8)*128;
|
||||||
|
|
||||||
HSH_CreateMFDTextures(pDD, &pDDSMechTexture, &pDDSTexture);
|
char temppath[MAX_PATH];
|
||||||
|
sprintf(temppath,"%s\\hsh\\mfd\\%s.bmp",AssetsDirectory1,mechnames[mech]);
|
||||||
|
|
||||||
|
DrawBitmapToSurface(pDDSMechTexture,x,y,temppath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if(pDDSMechTexture==NULL)MessageBeep(0);
|
||||||
|
//pDDSMechTexture=0;
|
||||||
|
//pDDSDamageTexture=CreatePixelFormatTexture(pDD,512,512,&DDPF_R4G4B4A4);//damage image..
|
||||||
pDDSDamageTexture=0;//On Reset: recreated... no need to create now.
|
pDDSDamageTexture=0;//On Reset: recreated... no need to create now.
|
||||||
// MSL 5.03 Target Damage Display
|
// MSL 5.03 Target Damage Display
|
||||||
pDDSTargetTexture=0;
|
pDDSTargetTexture=0;
|
||||||
|
|
||||||
|
//////Texture
|
||||||
|
char temppath[MAX_PATH];
|
||||||
|
sprintf(temppath,"%s\\hsh\\%s",AssetsDirectory1,"mfd_texture.bmp");
|
||||||
|
pDDSTexture=CreateATextureFromFile(pDD, temppath);
|
||||||
|
//pDDSTexture=0;
|
||||||
tw=512;
|
tw=512;
|
||||||
th=512;
|
th=512;
|
||||||
|
|
||||||
@@ -1685,50 +1439,12 @@ bool CMFD_Device::InitSecond()
|
|||||||
|
|
||||||
// Mode 4: create flip chain + D3D device for the right 640x480 monitor.
|
// Mode 4: create flip chain + D3D device for the right 640x480 monitor.
|
||||||
if (g_nTypeOfMFDs == 4 && m_pRightDevice) {
|
if (g_nTypeOfMFDs == 4 && m_pRightDevice) {
|
||||||
if( !mfd_device_right.InitSecond() )
|
mfd_device_right.InitSecond();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [mode 4 split-MFD fix]
|
|
||||||
// Exchange this object's DATA members with the right-hand device's, so that every
|
|
||||||
// existing "mfd_device.<member>" drawing call in hudchat/huddamage/hudweapon/radar
|
|
||||||
// transparently targets whichever physical 640x480 panel the current channel belongs to.
|
|
||||||
// Only data is exchanged - never the vtable pointer, so virtual dispatch is unaffected.
|
|
||||||
void CMFD_Device::SwapRightState()
|
|
||||||
{
|
|
||||||
if (!m_pRightDevice)
|
|
||||||
return;
|
|
||||||
CMFDRight_Device* r = m_pRightDevice;
|
|
||||||
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAW7, pDD, r->pDD);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSFront, r->pDDSFront);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSBack, r->pDDSBack);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSTarget, r->pDDSTarget);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSTexture, r->pDDSTexture);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECT3DDEVICE7, pD3DDevice, r->pD3DDevice);
|
|
||||||
HSH_SWAP_MEMBER(SIZE, size_back, r->size_back);
|
|
||||||
HSH_SWAP_MEMBER(SIZE, size_target, r->size_target);
|
|
||||||
HSH_SWAP_MEMBER(float, tw, r->tw);
|
|
||||||
HSH_SWAP_MEMBER(float, th, r->th);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSMechTexture, r->pDDSMechTexture);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSDamageTexture, r->pDDSDamageTexture);
|
|
||||||
HSH_SWAP_MEMBER(LPDIRECTDRAWSURFACE7, pDDSTargetTexture, r->pDDSTargetTexture);
|
|
||||||
|
|
||||||
// CHSHFont has no virtual functions, so a raw byte swap is safe here and avoids
|
|
||||||
// running constructor/destructor side effects on a temporary copy.
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
char fonttmp[sizeof(CHSHFont)];
|
|
||||||
memcpy(fonttmp, &pFont[i], sizeof(CHSHFont));
|
|
||||||
memcpy(&pFont[i], &r->pFont[i], sizeof(CHSHFont));
|
|
||||||
memcpy(&r->pFont[i], fonttmp, sizeof(CHSHFont));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_bSwappedToRight = !m_bSwappedToRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMFD_Device::BeginChannel(DWORD channel)
|
bool CMFD_Device::BeginChannel(DWORD channel)
|
||||||
{
|
{
|
||||||
if (!sh_game_started)
|
if (!sh_game_started)
|
||||||
@@ -1775,13 +1491,6 @@ bool CMFD_Device::BeginChannel(DWORD channel)
|
|||||||
}
|
}
|
||||||
ch=channel;
|
ch=channel;
|
||||||
|
|
||||||
// [mode 4 split-MFD fix] Channels 3-4 belong to the right-hand 640x480 monitor,
|
|
||||||
// which is a separate DirectDraw object with its own D3D device and its own copies
|
|
||||||
// of the MFD textures and fonts. Swap our members over to it for the duration of
|
|
||||||
// the channel so all existing drawing code targets it, and swap back in EndChannel.
|
|
||||||
if (g_nTypeOfMFDs == 4 && m_pRightDevice && channel >= 3)
|
|
||||||
SwapRightState();
|
|
||||||
|
|
||||||
//Set the Render Target.
|
//Set the Render Target.
|
||||||
SetRenderTargetTexture();
|
SetRenderTargetTexture();
|
||||||
pD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET ,0,0,0);
|
pD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET ,0,0,0);
|
||||||
@@ -1804,13 +1513,40 @@ bool CMFD_Device::EndChannel()
|
|||||||
gCaptureScreen = 0;
|
gCaptureScreen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [mode 4 split-MFD fix] One composite path for every mode. In mode 4 BeginChannel
|
// Mode 4: route this channel's blit to the correct physical monitor.
|
||||||
// has already swapped our members over to the device that owns this channel, so
|
// Handled here, before the modes 1-3 SetRenderTargetBackbuffer/BeginScene
|
||||||
// pDDSTarget and pD3DDevice always belong to the SAME DirectDraw object. The old
|
// block, so each path has exactly one matched Begin/EndScene pair.
|
||||||
// code sampled the LEFT device's pDDSTarget from the RIGHT device's D3D device,
|
// Channels 0-2 go to the LEFT device (this, g_nMFD1).
|
||||||
// which DirectDraw cannot do natively and emulated with a VRAM->system-memory
|
// Channels 3-4 go to the RIGHT device (m_pRightDevice, g_nMFD2).
|
||||||
// readback on channels 3 and 4 - a full GPU stall twice every 7-frame cycle, which
|
// pDDSTarget was created by this device's pDD; both devices are on the
|
||||||
// is what produced the constant rhythmic stutter in mode 4.
|
// same GPU so VRAM textures are mutually accessible.
|
||||||
|
if (g_nTypeOfMFDs == 4 && m_pRightDevice) {
|
||||||
|
CHSH_Device* target = (ch >= 3) ? m_pRightDevice : static_cast<CHSH_Device*>(this);
|
||||||
|
target->SetRenderTargetBackbuffer();
|
||||||
|
target->pD3DDevice->BeginScene();
|
||||||
|
|
||||||
|
target->pD3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE);
|
||||||
|
target->pD3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,D3DBLEND_ONE);
|
||||||
|
target->pD3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_ONE);
|
||||||
|
|
||||||
|
target->pD3DDevice->SetTexture(0, pDDSTarget);
|
||||||
|
|
||||||
|
DWORD color4 = channel_color[ch];
|
||||||
|
float w4 = (float)target->size_back.cx; // 640
|
||||||
|
float h4 = (float)target->size_back.cy; // 480
|
||||||
|
float w2_4 = (float)size_target.cx; // 1024
|
||||||
|
float h2_4 = (float)size_target.cy; // 512
|
||||||
|
D3DTLVERTEX V4[4];
|
||||||
|
V4[0]=D3DTLVERTEX(D3DVECTOR( -0.5f,h4-0.5f,0.9f),1.0f,color4,0, 0/w2_4,h4/h2_4);
|
||||||
|
V4[1]=D3DTLVERTEX(D3DVECTOR( -0.5f, -0.5f,0.9f),1.0f,color4,0, 0/w2_4, 0/h2_4);
|
||||||
|
V4[2]=D3DTLVERTEX(D3DVECTOR(w4-0.5f,h4-0.5f,0.9f),1.0f,color4,0,w4/w2_4,h4/h2_4);
|
||||||
|
V4[3]=D3DTLVERTEX(D3DVECTOR(w4-0.5f, -0.5f,0.9f),1.0f,color4,0,w4/w2_4, 0/h2_4);
|
||||||
|
target->pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,D3DFVF_TLVERTEX,V4,4,NULL);
|
||||||
|
|
||||||
|
target->pD3DDevice->EndScene();
|
||||||
|
ch = (DWORD)-1;
|
||||||
|
} else {
|
||||||
|
// Modes 1-3: blit to half of the single 1280-wide (or 640-wide) backbuffer.
|
||||||
SetRenderTargetBackbuffer();
|
SetRenderTargetBackbuffer();
|
||||||
pD3DDevice->BeginScene();
|
pD3DDevice->BeginScene();
|
||||||
|
|
||||||
@@ -1823,19 +1559,11 @@ bool CMFD_Device::EndChannel()
|
|||||||
DWORD color;
|
DWORD color;
|
||||||
|
|
||||||
color = channel_color[ch];
|
color = channel_color[ch];
|
||||||
|
//color = 0xFFFFFFFF;
|
||||||
D3DTLVERTEX Vertices[4]; // Vertices for the cube
|
D3DTLVERTEX Vertices[4]; // Vertices for the cube
|
||||||
|
|
||||||
// Modes 1-3 pack two 640-wide panels into a single backbuffer, so channels 0-2 go
|
float w=(float)size_back.cx/2;//<==NOTE: this differs from radar.
|
||||||
// to the left half and 3-4 to the right half. In mode 4 each device IS a
|
float x1=(ch/3)*w;
|
||||||
// standalone 640x480 panel, so every channel composites full width at x=0.
|
|
||||||
float w, x1;
|
|
||||||
if (g_nTypeOfMFDs == 4) {
|
|
||||||
w = (float)size_back.cx;
|
|
||||||
x1 = 0.0f;
|
|
||||||
} else {
|
|
||||||
w = (float)size_back.cx/2;//<==NOTE: this differs from radar.
|
|
||||||
x1 = (ch/3)*w;
|
|
||||||
}
|
|
||||||
float x2=x1+w;
|
float x2=x1+w;
|
||||||
float h=(float)size_back.cy;
|
float h=(float)size_back.cy;
|
||||||
float w2=(float)size_target.cx;
|
float w2=(float)size_target.cx;
|
||||||
@@ -1850,10 +1578,7 @@ bool CMFD_Device::EndChannel()
|
|||||||
|
|
||||||
pD3DDevice->EndScene();
|
pD3DDevice->EndScene();
|
||||||
ch = (DWORD)-1;
|
ch = (DWORD)-1;
|
||||||
|
}
|
||||||
// Hand our members back to the left device if this channel was routed right.
|
|
||||||
if (m_bSwappedToRight)
|
|
||||||
SwapRightState();
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2924,57 +2649,21 @@ bool IsSecondaryMonitorAvaliable()
|
|||||||
|
|
||||||
void HSH_EnterFullScreen2()
|
void HSH_EnterFullScreen2()
|
||||||
{
|
{
|
||||||
// [displaylog] Record which of the three possible paths was taken and how each
|
|
||||||
// individual panel fared. Mode 4 opens four fullscreen DirectDraw devices that all
|
|
||||||
// share one focus window, so a partial failure is both plausible and, until now,
|
|
||||||
// completely invisible.
|
|
||||||
HSH_LogInit( "\r\nHSH_EnterFullScreen2\r\n" );
|
|
||||||
HSH_LogInit( "--------------------\r\n" );
|
|
||||||
|
|
||||||
if(IsMultimonitorAvaliable()){
|
if(IsMultimonitorAvaliable()){
|
||||||
HSH_LogInit( " path: multi-monitor (MFD + radar)\r\n" );
|
mfd_device.InitFirst();
|
||||||
|
radar_device.InitFirst();
|
||||||
|
|
||||||
HSH_LogInit( " [mfd]\r\n" );
|
mfd_device.InitSecond();
|
||||||
bool bMFD = mfd_device.InitFirst();
|
radar_device.InitSecond();
|
||||||
HSH_LogInit( " [radar]\r\n" );
|
|
||||||
bool bRadar = radar_device.InitFirst();
|
|
||||||
|
|
||||||
if( bMFD ) {
|
|
||||||
HSH_LogInit( " [mfd]\r\n" );
|
|
||||||
bMFD = mfd_device.InitSecond();
|
|
||||||
}
|
|
||||||
if( bRadar ) {
|
|
||||||
HSH_LogInit( " [radar]\r\n" );
|
|
||||||
bRadar = radar_device.InitSecond();
|
|
||||||
}
|
|
||||||
|
|
||||||
// [panelcoop] Only claim the panels are up if they actually are.
|
|
||||||
//
|
|
||||||
// This flag used to be set unconditionally. A panel that failed to initialise
|
|
||||||
// therefore left null surfaces and a null IDirect3DDevice7 behind, and the
|
|
||||||
// per-frame render path called straight through them - the process died on a
|
|
||||||
// null COM vtable call ("Attempt to read from address 0x00000044") rather than
|
|
||||||
// simply running without MFDs.
|
|
||||||
if( bMFD && bRadar ) {
|
|
||||||
hsh_initialized=true;
|
hsh_initialized=true;
|
||||||
HSH_LogInit( " result: panels initialised OK\r\n" );
|
|
||||||
if(g_f3dtarget){
|
if(g_f3dtarget){
|
||||||
CopyTargetImage(0,mfd_device.pDDSMechTexture);
|
CopyTargetImage(0,mfd_device.pDDSMechTexture);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
HSH_LogInit( " result: *** FAILED (mfd=%s radar=%s) - continuing WITHOUT panels ***\r\n",
|
|
||||||
bMFD ? "ok" : "failed", bRadar ? "ok" : "failed" );
|
|
||||||
mfd_device.Release();
|
|
||||||
radar_device.Release();
|
|
||||||
hsh_initialized=false;
|
|
||||||
}
|
|
||||||
}else if(IsSecondaryMonitorAvaliable()){
|
}else if(IsSecondaryMonitorAvaliable()){
|
||||||
HSH_LogInit( " path: single secondary monitor (mr_device)\r\n" );
|
|
||||||
mr_device.InitFirst();
|
mr_device.InitFirst();
|
||||||
mr_device.InitSecond();
|
mr_device.InitSecond();
|
||||||
hsh_mrdev_initialized=true;
|
hsh_mrdev_initialized=true;
|
||||||
}else{
|
|
||||||
HSH_LogInit( " path: none - no secondary displays opened\r\n" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,10 +159,6 @@ public:
|
|||||||
bool MapDrawn;
|
bool MapDrawn;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mode 4 (split dual 640x480): the right-hand MFD lives on its own DirectDraw object,
|
|
||||||
// so CMFD_Device keeps a typed pointer to it.
|
|
||||||
class CMFDRight_Device;
|
|
||||||
|
|
||||||
class CMFD_Device:public CHSH_Device
|
class CMFD_Device:public CHSH_Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -173,8 +169,7 @@ public:
|
|||||||
LPDIRECTDRAWSURFACE7 pDDSTargetTexture;//Target damage texture..
|
LPDIRECTDRAWSURFACE7 pDDSTargetTexture;//Target damage texture..
|
||||||
|
|
||||||
DWORD ch; //current channel....
|
DWORD ch; //current channel....
|
||||||
CMFDRight_Device* m_pRightDevice; // mode 4: right 640x480 device (NULL in modes 1-3)
|
CHSH_Device* m_pRightDevice; // mode 4: pointer to right 640x480 device (NULL in modes 1-3)
|
||||||
bool m_bSwappedToRight; // mode 4: true while our members point at the right device
|
|
||||||
public:
|
public:
|
||||||
CMFD_Device();
|
CMFD_Device();
|
||||||
~CMFD_Device();
|
~CMFD_Device();
|
||||||
@@ -182,7 +177,6 @@ public:
|
|||||||
bool InitSecond();
|
bool InitSecond();
|
||||||
bool BeginChannel(DWORD channel);// Select channel 0-4.
|
bool BeginChannel(DWORD channel);// Select channel 0-4.
|
||||||
bool EndChannel();
|
bool EndChannel();
|
||||||
void SwapRightState(); // mode 4: exchange data members with the right device
|
|
||||||
virtual bool BeginScene(); //Performs normal BeginScene/EndScene.
|
virtual bool BeginScene(); //Performs normal BeginScene/EndScene.
|
||||||
bool BeginSceneRight(); // mode 4: clear+grid on right device at sh_step==1
|
bool BeginSceneRight(); // mode 4: clear+grid on right device at sh_step==1
|
||||||
virtual bool EndScene(); //Performs the Flip.
|
virtual bool EndScene(); //Performs the Flip.
|
||||||
@@ -198,24 +192,16 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Mode 4 (split dual 640x480): right-side MFD device on its own 640x480 monitor.
|
// Mode 4 (split dual 640x480): right-side MFD device on its own 640x480 monitor.
|
||||||
// It owns a SEPARATE IDirectDraw7 and IDirect3DDevice7 (a DirectDraw device is bound to
|
// Same GPU as the left device (mfd_device), so VRAM textures are shared.
|
||||||
// one monitor), therefore it must also own a complete, independent copy of the MFD
|
|
||||||
// texture set: a surface belongs to the IDirectDraw7 that created it, not to the GPU,
|
|
||||||
// so surfaces can NOT be shared between the two devices even on the same card.
|
|
||||||
class CMFDRight_Device : public CHSH_Device
|
class CMFDRight_Device : public CHSH_Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LPDIRECTDRAWSURFACE7 pDDSMechTexture;
|
CMFDRight_Device() {}
|
||||||
LPDIRECTDRAWSURFACE7 pDDSDamageTexture;
|
|
||||||
LPDIRECTDRAWSURFACE7 pDDSTargetTexture;
|
|
||||||
public:
|
|
||||||
CMFDRight_Device()
|
|
||||||
: pDDSMechTexture(0), pDDSDamageTexture(0), pDDSTargetTexture(0) {}
|
|
||||||
bool InitFirst();
|
bool InitFirst();
|
||||||
bool InitSecond();
|
bool InitSecond();
|
||||||
virtual bool BeginScene() { return true; }
|
virtual bool BeginScene() { return true; }
|
||||||
virtual bool EndScene() { return true; }
|
virtual bool EndScene() { return true; }
|
||||||
virtual bool Release();
|
virtual bool Release() { return CHSH_Device::Release(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMR_Device:public CHSH_Device
|
class CMR_Device:public CHSH_Device
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "recscore.h"
|
#include "recscore.h"
|
||||||
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
||||||
|
|
||||||
#define MAX_TESLAS 16
|
#define MAX_TESLAS 31
|
||||||
#define MAX_CAMERAS 4
|
#define MAX_CAMERAS 4
|
||||||
|
|
||||||
#define PORT_Launcher 1000
|
#define PORT_Launcher 1000
|
||||||
@@ -260,7 +260,7 @@ CInviteCOOP g_InviteCOOP;
|
|||||||
// Mech4 only
|
// Mech4 only
|
||||||
int g_nMech4Comm = 0;
|
int g_nMech4Comm = 0;
|
||||||
BOOL g_bIsServer = FALSE;
|
BOOL g_bIsServer = FALSE;
|
||||||
SPlayerInfo g_aPlayerInfos[20];
|
SPlayerInfo g_aPlayerInfos[35]; // 31 pilots + 1 CS + 3 spare
|
||||||
int g_nPlayerInfos;
|
int g_nPlayerInfos;
|
||||||
int g_nServer;
|
int g_nServer;
|
||||||
int g_nTeslas;
|
int g_nTeslas;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ LONG g_LeftPedalLast = 0;
|
|||||||
LONG g_RightPedalLast = 0;
|
LONG g_RightPedalLast = 0;
|
||||||
BOOL g_StartGame = FALSE;
|
BOOL g_StartGame = FALSE;
|
||||||
|
|
||||||
static int g_nOpenComState = 0; // hyun
|
static int g_nOpenComState = 0; // 鉉
|
||||||
|
|
||||||
DWORD g_dwLastAnalogUpdate = 0;
|
DWORD g_dwLastAnalogUpdate = 0;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ DWORD g_dwLastAnalogUpdate = 0;
|
|||||||
#define RESTART_CHAR 0xFE
|
#define RESTART_CHAR 0xFE
|
||||||
#define IDLE_CHAR 0xFF
|
#define IDLE_CHAR 0xFF
|
||||||
|
|
||||||
//Use it like a member variable.
|
//member 변수처럼 활용할것.
|
||||||
static HANDLE g_hCom = INVALID_HANDLE_VALUE;
|
static HANDLE g_hCom = INVALID_HANDLE_VALUE;
|
||||||
static OVERLAPPED wos;
|
static OVERLAPPED wos;
|
||||||
static OVERLAPPED ros;
|
static OVERLAPPED ros;
|
||||||
@@ -108,7 +108,6 @@ int g_nRIOPacketCountB = (sizeof(g_baRIOLengthsB) / sizeof(g_baRIOLengthsB[0]));
|
|||||||
|
|
||||||
int g_nRIOType = 0; // 0: old(original) type, 1: new type
|
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_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
|
int g_nEjectButton = 61; // 61: original, 31: new type
|
||||||
BYTE* g_pbRIOLengths = NULL;
|
BYTE* g_pbRIOLengths = NULL;
|
||||||
int g_nRIOPacketCount = 0;
|
int g_nRIOPacketCount = 0;
|
||||||
@@ -319,7 +318,7 @@ void RequestVersion();
|
|||||||
void RequestAnalogUpdate(BYTE bFreq = 3);
|
void RequestAnalogUpdate(BYTE bFreq = 3);
|
||||||
|
|
||||||
// start - for new RIO Board only...
|
// start - for new RIO Board only...
|
||||||
//Received packets are handled in different threads.. a critical section is needed..
|
//received packet은 각각 다른 스레드에서 처리하므로.. ciritical section이 필요하다..
|
||||||
BYTE received_buffer[128][16];
|
BYTE received_buffer[128][16];
|
||||||
int received_packet=0;//packets count in the received_buffer.
|
int received_packet=0;//packets count in the received_buffer.
|
||||||
DWORD received_serial=0;
|
DWORD received_serial=0;
|
||||||
@@ -570,7 +569,7 @@ BOOL SetupConnection(HANDLE hCom)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Original processing routine..
|
원래 처리 루틴..
|
||||||
NPTTYINFO npTTYInfo=0 ;
|
NPTTYINFO npTTYInfo=0 ;
|
||||||
BYTE bset1 = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_DTRDSR) != 0) ;
|
BYTE bset1 = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_DTRDSR) != 0) ;
|
||||||
BYTE bset2 = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_RTSCTS) != 0) ;
|
BYTE bset2 = (BYTE) ((FLOWCTRL( npTTYInfo ) & FC_RTSCTS) != 0) ;
|
||||||
@@ -604,7 +603,7 @@ BOOL SetupConnection(HANDLE hCom)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Unhandled items.. handled using GetCommState default values.
|
//처리되지 않는 항목들.. GetCommState의 기본값으로 처리한다.
|
||||||
DWORD fDsrSensitivity:1; // DSR sensitivity
|
DWORD fDsrSensitivity:1; // DSR sensitivity
|
||||||
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
|
DWORD fTXContinueOnXoff:1; // XOFF continues Tx
|
||||||
DWORD fErrorChar: 1; // enable error replacement
|
DWORD fErrorChar: 1; // enable error replacement
|
||||||
@@ -648,7 +647,7 @@ HANDLE OpenConnection(int port)
|
|||||||
g_hLampEvent = CreateEvent(0,FALSE,0,0);
|
g_hLampEvent = CreateEvent(0,FALSE,0,0);
|
||||||
g_hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)CommWatchProc, NULL, CREATE_SUSPENDED, &g_dwThreadID);
|
g_hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)CommWatchProc, NULL, CREATE_SUSPENDED, &g_dwThreadID);
|
||||||
if (g_hCommWatchThread) {
|
if (g_hCommWatchThread) {
|
||||||
//Thread was created successfully.
|
//Thread까지 정상적으로 생성되었다.
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
//All OK
|
//All OK
|
||||||
//Exit Point <======
|
//Exit Point <======
|
||||||
@@ -745,11 +744,11 @@ retry:
|
|||||||
for(int i=0;i<(int)dwLength;i++){
|
for(int i=0;i<(int)dwLength;i++){
|
||||||
int ch=(BYTE)lpszBlock[i];
|
int ch=(BYTE)lpszBlock[i];
|
||||||
|
|
||||||
//Loop for the number of received characters.
|
//받은 문자만큼 루프를 돈다.
|
||||||
//Currently.. no packet is being received.. can receive any control character.
|
//현재.. 받고 있는 packet이 없고.. 어떤 control문자라도 올 수 있는 상태이다.
|
||||||
|
|
||||||
if(packetbyteremain!=0){
|
if(packetbyteremain!=0){
|
||||||
//Character corresponding to a packet has arrived.. receive the remaining characters.
|
//패킷에 해당하는 문자가 도착했다.. 나머지 문자들을 받는다.
|
||||||
if (ch & 0x80) {
|
if (ch & 0x80) {
|
||||||
packetbyteremain=0;
|
packetbyteremain=0;
|
||||||
chinpacket=0;
|
chinpacket=0;
|
||||||
@@ -759,7 +758,7 @@ retry:
|
|||||||
packetbytes[chinpacket] = ch;
|
packetbytes[chinpacket] = ch;
|
||||||
chinpacket++;
|
chinpacket++;
|
||||||
if (packetbyteremain == 0) {
|
if (packetbyteremain == 0) {
|
||||||
//A packet has been completed and arrived.. respond immediately.
|
//하나의 패킷이 완성되었다. 도착했다.. 즉시 반응한다.
|
||||||
chinpacket--; // exclude check byte
|
chinpacket--; // exclude check byte
|
||||||
int packettype=packetbytes[0];
|
int packettype=packetbytes[0];
|
||||||
BYTE bCheckByte = 0;
|
BYTE bCheckByte = 0;
|
||||||
@@ -838,21 +837,21 @@ retry:
|
|||||||
packettype++;
|
packettype++;
|
||||||
packettype--;
|
packettype--;
|
||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
case rio_Ack2:
|
case rio_Ack2://◆◆◆◆◆◆◆◆◆◆
|
||||||
if (g_nRIOType != 0) {
|
if (g_nRIOType != 0) {
|
||||||
BYTE id=packetbytes[1];
|
BYTE id=packetbytes[1];
|
||||||
int popped_index=PopPacket(id);
|
int popped_index=PopPacket(id);
|
||||||
ack_timeout=GetTickCount()+30;
|
ack_timeout=GetTickCount()+30;
|
||||||
if(popped_index==0){
|
if(popped_index==0){
|
||||||
//This is the very first one... Normal case.
|
//맨처음것... 정상적인 경우이다.
|
||||||
}else if(popped_index>0){
|
}else if(popped_index>0){
|
||||||
//Not the very first one.
|
//맨처음것이 아닌것.
|
||||||
//Previous ones were not sent successfully, resend...
|
//이전것들이 정상적으로 보내지지 않았으므로 다시 보낸다...
|
||||||
ReSendPackets(popped_index);//By the number of incorrect ones..
|
ReSendPackets(popped_index);//잘못된 개수 만큼..
|
||||||
}else{
|
}else{
|
||||||
//Failure means an id that does not exist arrived..
|
//실패 했다는것은.. 없는 id가 왔다는 것인데..
|
||||||
//An obvious error.. but there is no particular way to handle it.
|
//명백한 에러.. 그러나 특별히 대처할 방법은 없다.
|
||||||
//Simply ignore it.
|
//그냥 무시한다.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -871,9 +870,9 @@ retry:
|
|||||||
// WriteTTYBlock(hWnd,"|",1);
|
// WriteTTYBlock(hWnd,"|",1);
|
||||||
chinpacket=0;
|
chinpacket=0;
|
||||||
|
|
||||||
//Clear the packetbytes buffer.
|
//packetbytes버퍼를 지운다.
|
||||||
} else {
|
} else {
|
||||||
//Packet characters have not all arrived yet.. do nothing.
|
//아직 패킷 문자들이 다 도착하지 않았다.. 아무것도 하지 않는다.
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
@@ -890,7 +889,7 @@ retry:
|
|||||||
}//for(int i=0;i<(int)dwLength;i++)
|
}//for(int i=0;i<(int)dwLength;i++)
|
||||||
|
|
||||||
|
|
||||||
//sanghoon end
|
//상훈 뒤
|
||||||
if (!fReadStat){
|
if (!fReadStat){
|
||||||
if (GetLastError() == ERROR_IO_PENDING){
|
if (GetLastError() == ERROR_IO_PENDING){
|
||||||
while(!GetOverlappedResult( g_hCom,&ros, &dwLength, TRUE )){
|
while(!GetOverlappedResult( g_hCom,&ros, &dwLength, TRUE )){
|
||||||
@@ -1001,24 +1000,12 @@ DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_dwLastAnalogUpdate = GetTickCount() + 2800;
|
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) {
|
while(1) {
|
||||||
DWORD dwEvtMask = 0;
|
DWORD dwEvtMask = 0;
|
||||||
|
|
||||||
WaitCommEvent( g_hCom, &dwEvtMask,&wcos );
|
WaitCommEvent( g_hCom, &dwEvtMask,&wcos );
|
||||||
|
|
||||||
DWORD ret=WaitForMultipleObjects(3,events,FALSE,g_dwRIOPollTimeout);
|
DWORD ret=WaitForMultipleObjects(3,events,FALSE,50/*INFINITE*/);
|
||||||
if(ret==WAIT_OBJECT_0){
|
if(ret==WAIT_OBJECT_0){
|
||||||
//An Comm Event has arrived.
|
//An Comm Event has arrived.
|
||||||
DWORD bytesread;
|
DWORD bytesread;
|
||||||
@@ -1035,10 +1022,10 @@ DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
|
|||||||
}
|
}
|
||||||
} else {*/
|
} else {*/
|
||||||
if (g_nRIOType != 0) {
|
if (g_nRIOType != 0) {
|
||||||
///When ack is received from ReadCommBlock above, update duetime.
|
///◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆위의 ReadCommBlock에서 ack를 받았을 경우 duetime을 update시킨다.
|
||||||
///CheckSentBuffer...
|
///◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆CheckSentBuffer...
|
||||||
if(GetTickCount()>ack_timeout)
|
if(GetTickCount()>ack_timeout)
|
||||||
ReSendPackets(sent_packet);//Resend everything in sent_buffer.
|
ReSendPackets(sent_packet);//sent_buffer에 있는 모든것들을 다시 보내본다.
|
||||||
}
|
}
|
||||||
/*}*/
|
/*}*/
|
||||||
}else if(ret==WAIT_OBJECT_0+1){
|
}else if(ret==WAIT_OBJECT_0+1){
|
||||||
@@ -1052,9 +1039,9 @@ DWORD FAR PASCAL CommWatchProc( LPSTR lpData )
|
|||||||
//break;
|
//break;
|
||||||
}else if (ret==WAIT_TIMEOUT) {
|
}else if (ret==WAIT_TIMEOUT) {
|
||||||
if (g_nRIOType != 0) {
|
if (g_nRIOType != 0) {
|
||||||
///CheckSentBuffer...
|
///◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆CheckSentBuffer...
|
||||||
if(GetTickCount()>ack_timeout)
|
if(GetTickCount()>ack_timeout)
|
||||||
ReSendPackets(sent_packet);//Resend everything in sent_buffer.
|
ReSendPackets(sent_packet);//sent_buffer에 있는 모든것들을 다시 보내본다.
|
||||||
}
|
}
|
||||||
if (packetbyteremain == 0) {
|
if (packetbyteremain == 0) {
|
||||||
RequestAnalogUpdate();
|
RequestAnalogUpdate();
|
||||||
@@ -1249,8 +1236,8 @@ bool SaveReceivedPacket(const BYTE*ba,int length)
|
|||||||
|
|
||||||
bool GetSerialFromReceivedBuffer(char * ba)
|
bool GetSerialFromReceivedBuffer(char * ba)
|
||||||
{
|
{
|
||||||
//Store only ButtonPressed/Released..
|
//ButtonPressed/Released만 저장해 놓는다..
|
||||||
//Transfer all serial packets.
|
//모든 serial packet을 옮긴다.
|
||||||
if(received_packet>0){
|
if(received_packet>0){
|
||||||
if(received_buffer[0][2]==received_serial){
|
if(received_buffer[0][2]==received_serial){
|
||||||
CopyMemory(ba,received_buffer[0],16);
|
CopyMemory(ba,received_buffer[0],16);
|
||||||
@@ -1277,7 +1264,7 @@ bool QueuePacket(const BYTE *ba,int length)
|
|||||||
|
|
||||||
int PopPacket(BYTE id)
|
int PopPacket(BYTE id)
|
||||||
{
|
{
|
||||||
//lamp.. check by id..
|
//lamp는.. id가..
|
||||||
//search the packet..
|
//search the packet..
|
||||||
for(int i=0;i<sent_packet;i++){
|
for(int i=0;i<sent_packet;i++){
|
||||||
if(sent_buffer[i][3]==id){
|
if(sent_buffer[i][3]==id){
|
||||||
@@ -1440,7 +1427,7 @@ void CBUTTON_GROUP::SetTable(BOOL bFlag/* = true*/)
|
|||||||
|
|
||||||
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
|
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
|
||||||
s_aCtrlLamp[b] = s_aSaveLamp[b];// = s_aButtonTable[b].lamp;
|
s_aCtrlLamp[b] = s_aSaveLamp[b];// = s_aButtonTable[b].lamp;
|
||||||
// s_aButtonTable is the old value, so the initial value is Off
|
// s_aButtonTable는 Old값이므로 초기치는 Off
|
||||||
s_aButtonTable[b].lamp = LAMP_OFF;
|
s_aButtonTable[b].lamp = LAMP_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1452,7 +1439,7 @@ void CBUTTON_GROUP::SetTable(BOOL bFlag/* = true*/)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Restore all tables to original state
|
// 모든 테이블을 원래 상태로
|
||||||
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
|
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
|
||||||
s_aCtrlLamp[b] = s_aSaveLamp[b];
|
s_aCtrlLamp[b] = s_aSaveLamp[b];
|
||||||
}
|
}
|
||||||
@@ -1547,7 +1534,7 @@ void CBUTTON_GROUP::ResetTable(BOOL bFlag/* = true*/)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Turn off all tables
|
// 모든 테이블을 Off
|
||||||
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
|
for (int b = 0; b < MAXBUTTON_TABLE; b++) {
|
||||||
switch(b)
|
switch(b)
|
||||||
{
|
{
|
||||||
@@ -2225,13 +2212,13 @@ void CRIOMAIN::UpdatePadal (DIJOYSTATE &js)
|
|||||||
|
|
||||||
Down Up
|
Down Up
|
||||||
======================
|
======================
|
||||||
<-- (+) (-) -->
|
← (+) (-) →
|
||||||
======================
|
======================
|
||||||
/^
|
↗
|
||||||
g_LeftPedalStart: INT_MAX (+)
|
g_LeftPedalStart: INT_MAX (+)
|
||||||
|
|
||||||
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
|
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
|
||||||
lP result value is always negative
|
lP 결과값은 무조건 음수
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LONG lP = 0;
|
LONG lP = 0;
|
||||||
@@ -2375,9 +2362,9 @@ void CRIOMAIN::UpdateThrottle (DIJOYSTATE &js)
|
|||||||
|
|
||||||
Up Down
|
Up Down
|
||||||
======================
|
======================
|
||||||
<-- (-) (+) -->
|
← (-) (+) →
|
||||||
======================
|
======================
|
||||||
/^
|
↗
|
||||||
g_ThrottleStart: INT_MIN (-)
|
g_ThrottleStart: INT_MIN (-)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -2460,11 +2447,11 @@ void CRIOMAIN::UpdateJoystick (DIJOYSTATE &js)
|
|||||||
*/
|
*/
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
/*
|
/*
|
||||||
Meaning of 80
|
80 의 의미
|
||||||
|
|
||||||
Assuming RIO returns values in range (LEFT:120 ~ RIGHT:-80)
|
RIO (LEFT :120 ~ RIGHT :-80) 까지의 수를 돌려준다고 가정하고
|
||||||
(In the LEFT case) clip values above the RIGHT maximum of 80, and the rest
|
(LEFT의경우) RIGHT 의 최대값 80 이상인 값을 잘라버리고 나머지는
|
||||||
if discarded, LEFT and RIGHT can move at the same speed
|
버릴경우 LEFT 와 RIGHT가 동일한 속도로 움직일수 있다
|
||||||
*/
|
*/
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
LONG lJ;
|
LONG lJ;
|
||||||
|
|||||||
@@ -57,19 +57,6 @@
|
|||||||
|
|
||||||
#include "mechspecs.cpp"
|
#include "mechspecs.cpp"
|
||||||
|
|
||||||
// [16 pilots + 1 cameraship fix]
|
|
||||||
//
|
|
||||||
// A cameraship seat occupies a network (DirectPlay) slot but pilots no 'Mech, so slots must
|
|
||||||
// be reserved for cameraships on top of the pilot cap, and cameraships must NOT be counted
|
|
||||||
// against the 'Mech cap.
|
|
||||||
//
|
|
||||||
// The reserve has to be a CONSTANT rather than being derived from
|
|
||||||
// (CTCL_GetTeslaCountAll() - CTCL_GetTeslaCount()): the CTCL tesla table is only populated
|
|
||||||
// when CTCL_IsConsoleOrCOOP() is true (see ctcl.cpp), so on a pod - including the cameraship
|
|
||||||
// pod, which is the machine that actually creates the network session in CTCL_DoCreateGame()
|
|
||||||
// - both counters return 0 and the reserve collapsed to +0.
|
|
||||||
#define MW4_CAMERASHIP_RESERVE 4 // must be >= MAX_CAMERAS in ctcl.cpp
|
|
||||||
|
|
||||||
// MSL 5.03 Mechview
|
// MSL 5.03 Mechview
|
||||||
void __stdcall CTCL_AfterBeginScene();
|
void __stdcall CTCL_AfterBeginScene();
|
||||||
void __stdcall CTCL_UpdateMechView();
|
void __stdcall CTCL_UpdateMechView();
|
||||||
@@ -145,10 +132,6 @@ extern SCRIPTCALLBACK(CTCL_GetMissionState);
|
|||||||
extern SCRIPTCALLBACK(CTCL_DoBreak);
|
extern SCRIPTCALLBACK(CTCL_DoBreak);
|
||||||
extern SCRIPTCALLBACK(CTCL_IsGameLoaded);
|
extern SCRIPTCALLBACK(CTCL_IsGameLoaded);
|
||||||
extern SCRIPTCALLBACK(CTCL_DoReprint);
|
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_CheckPlayMovie);
|
||||||
extern SCRIPTCALLBACK(CTCL_CheckCoinCounts);
|
extern SCRIPTCALLBACK(CTCL_CheckCoinCounts);
|
||||||
extern SCRIPTCALLBACK(CTCL_CheckUseJPD);
|
extern SCRIPTCALLBACK(CTCL_CheckUseJPD);
|
||||||
@@ -175,54 +158,6 @@ int g_nWhyPaused = 0; // 0: not paused - invitation in menu-state, 1: briefing,
|
|||||||
extern SCRIPTCALLBACK(CTCL_WhyPaused);
|
extern SCRIPTCALLBACK(CTCL_WhyPaused);
|
||||||
int g_nTimeList_Index = 3; // 3rd item in listbox...
|
int g_nTimeList_Index = 3; // 3rd item in listbox...
|
||||||
int g_nTimeList_Value = 7; // 7 minutes
|
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_nAutoNoReturn = 0;
|
|
||||||
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
|
// MSL 5.06
|
||||||
int g_nMechVariant = 0;
|
int g_nMechVariant = 0;
|
||||||
int g_nMechLabOp = 0;
|
int g_nMechLabOp = 0;
|
||||||
@@ -792,55 +727,6 @@ void MW4Shell::StartUp()
|
|||||||
gosScript_RegisterCallback("CTCL_WhyPaused",&CTCL_WhyPaused,GOSVAR_INT,0,NULL);
|
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_Index", &g_nTimeList_Index, GOSVAR_INT, 0, NULL);
|
||||||
gosScript_RegisterVariable("g_nTimeList_Value", &g_nTimeList_Value, 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_nAutoNoReturn", &g_nAutoNoReturn, 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
|
// MSL 5.06
|
||||||
gosScript_RegisterVariable("g_nMechVariant", &g_nMechVariant, GOSVAR_INT, 0, NULL);
|
gosScript_RegisterVariable("g_nMechVariant", &g_nMechVariant, GOSVAR_INT, 0, NULL);
|
||||||
gosScript_RegisterVariable("g_nMechLabOp", &g_nMechLabOp, GOSVAR_INT, 0, NULL);
|
gosScript_RegisterVariable("g_nMechLabOp", &g_nMechLabOp, GOSVAR_INT, 0, NULL);
|
||||||
@@ -1222,45 +1108,6 @@ void MW4Shell::ShutDown()
|
|||||||
// jcem - start
|
// jcem - start
|
||||||
gosScript_UnregisterVariable("g_nTimeList_Value");
|
gosScript_UnregisterVariable("g_nTimeList_Value");
|
||||||
gosScript_UnregisterVariable("g_nTimeList_Index");
|
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_nAutoNoReturn");
|
|
||||||
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
|
// MSL 5.06
|
||||||
gosScript_UnregisterVariable("g_nBlackMech");
|
gosScript_UnregisterVariable("g_nBlackMech");
|
||||||
gosScript_UnregisterVariable("g_nMechVariant");
|
gosScript_UnregisterVariable("g_nMechVariant");
|
||||||
@@ -1981,13 +1828,7 @@ int MW4Shell::SetNetworkMissionParamater(void * instance, int numParms, void **d
|
|||||||
case PLAYER_LIMIT_PARAMETER:
|
case PLAYER_LIMIT_PARAMETER:
|
||||||
params->m_playerLimit = INTPARM(1);
|
params->m_playerLimit = INTPARM(1);
|
||||||
Min_Clamp(params->m_playerLimit, Network::GetInstance()->GetPlayerCount());
|
Min_Clamp(params->m_playerLimit, Network::GetInstance()->GetPlayerCount());
|
||||||
// [16 pilots + 1 cameraship fix] Add camera slots on top of the pilot limit so
|
|
||||||
// cameraships can still connect. Uses the constant reserve for the same reason as
|
|
||||||
// CTCL_DefaultHostSetup: the CTCL tesla counters are console-only and read 0 on a pod.
|
|
||||||
// Only applied under CTCL - a standalone (non-pod) host must keep its exact limit.
|
|
||||||
Environment.NetworkMaxPlayers = params->m_playerLimit;
|
Environment.NetworkMaxPlayers = params->m_playerLimit;
|
||||||
if (!CTCL_IsNone())
|
|
||||||
Environment.NetworkMaxPlayers += MW4_CAMERASHIP_RESERVE;
|
|
||||||
gos_NetServerCommands(gos_Commend_UpdateMaxPlayers,0);
|
gos_NetServerCommands(gos_Commend_UpdateMaxPlayers,0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -9089,24 +8930,6 @@ int MW4Shell::LoadDefaultOptions()
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
// [16 pilots + 1 cameraship fix]
|
|
||||||
// Number of cameraship participants in the current roster. Only meaningful on the server
|
|
||||||
// pod, which is the only machine that holds the full g_aPlayerInfos roster (filled by
|
|
||||||
// CSOC_Client::OnBOTS) and the only machine that calls AddBot in CTCL mode.
|
|
||||||
// A cameraship entry is a non-bot player with no 'Mech (m_nMechIndex == 0).
|
|
||||||
static int CTCL_CountCameraShipsInGame()
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
for (int i = 0; i < g_nPlayerInfos; i++)
|
|
||||||
{
|
|
||||||
const SPlayerInfo& PI = g_aPlayerInfos[i];
|
|
||||||
if (!PI.m_bBot && (PI.m_nMechIndex == 0))
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
//
|
//
|
||||||
int _stdcall MW4Shell::AddBot(void *instance, int numParams, void* data[])
|
int _stdcall MW4Shell::AddBot(void *instance, int numParams, void* data[])
|
||||||
@@ -9123,16 +8946,6 @@ int _stdcall MW4Shell::AddBot(void *instance, int numParams, void* data[])
|
|||||||
bot_count++;
|
bot_count++;
|
||||||
}
|
}
|
||||||
player_count = Network::GetInstance()->GetPlayerCount();
|
player_count = Network::GetInstance()->GetPlayerCount();
|
||||||
// [16 pilots + 1 cameraship fix] Cameraships hold a network slot but no 'Mech slot.
|
|
||||||
// Counting them here capped pilots+cameras+bots at m_maxPlayers (16), so a full 16-'Mech
|
|
||||||
// roster plus a cameraship silently lost its last bot; g_nBOTs then never matched the
|
|
||||||
// connected lancemates and CTCL_CheckServerReady() spun forever without ever launching.
|
|
||||||
if (CTCL_IsConsoleX())
|
|
||||||
{
|
|
||||||
player_count -= CTCL_CountCameraShipsInGame();
|
|
||||||
if (player_count < 0)
|
|
||||||
player_count = 0;
|
|
||||||
}
|
|
||||||
NetMissionParameters::MWNetMissionParameters *params = app->GetLocalNetParams();
|
NetMissionParameters::MWNetMissionParameters *params = app->GetLocalNetParams();
|
||||||
if (params->m_runDedicated)
|
if (params->m_runDedicated)
|
||||||
{
|
{
|
||||||
@@ -12918,144 +12731,6 @@ int __stdcall CTCL_SetCDSP(void* instance, int args, void* data[])
|
|||||||
{
|
{
|
||||||
ASSERT(CTCL_IsConsole());
|
ASSERT(CTCL_IsConsole());
|
||||||
CTCL_DefaultHostSetup(1);
|
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_nAutoNoReturn = 0; 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("NoReturn", &g_nAutoNoReturn);
|
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13632,20 +13307,19 @@ void CTCL_API CTCL_DefaultHostSetup(int nMode)
|
|||||||
params->m_allowdecaltransfer = 0;
|
params->m_allowdecaltransfer = 0;
|
||||||
if (g_bCOOP) {
|
if (g_bCOOP) {
|
||||||
Environment.NetworkMaxPlayers = 16;
|
Environment.NetworkMaxPlayers = 16;
|
||||||
// Reserve extra DirectPlay slots for camera seats (tracked separately from pilots in CTCL)
|
//if (CTCL_GetTeslaCount() < CTCL_GetTeslaCountAll()) {
|
||||||
// Original commented-out intent preserved and now implemented for non-COOP below
|
// any cameraship installed... so 1 more player can join
|
||||||
|
//params->m_maxPlayers = 9;
|
||||||
|
//params->m_maxBots = 8;
|
||||||
|
//} else {
|
||||||
params->m_maxPlayers = 9;
|
params->m_maxPlayers = 9;
|
||||||
params->m_maxBots = 8;
|
params->m_maxBots = 8;
|
||||||
|
//}
|
||||||
} else {
|
} else {
|
||||||
params->m_maxPlayers = 16;
|
params->m_maxPlayers = 31; // 31: max that fits in 5-bit bitstream (0-31)
|
||||||
// [16 pilots + 1 cameraship fix] Reserve network slots for cameraship seats on top of the
|
Environment.NetworkMaxPlayers = 31;
|
||||||
// pilot cap. This runs on the cameraship pod too (via CTCL_DoCreateGame -> nMode 0), which
|
params->m_maxBots = 31;
|
||||||
// is the machine that creates the session with dwMaxPlayers = Environment.NetworkMaxPlayers.
|
gos_NetServerCommands(gos_Commend_UpdateMaxPlayers, 0); // push the updated limit to the live DirectPlay session
|
||||||
// Do NOT derive the reserve from the CTCL tesla counters here: that table is console-only,
|
|
||||||
// so on the pod they both return 0, the reserve became +0, the session was created with
|
|
||||||
// 16 slots, and the 17th connection was refused - the mission then never finished loading.
|
|
||||||
Environment.NetworkMaxPlayers = params->m_maxPlayers + MW4_CAMERASHIP_RESERVE;
|
|
||||||
params->m_maxBots = 16;
|
|
||||||
}
|
}
|
||||||
params->m_allow3rdPerson = 1;
|
params->m_allow3rdPerson = 1;
|
||||||
if ((nMode != 0) && (nMode != 4)) {
|
if ((nMode != 0) && (nMode != 4)) {
|
||||||
|
|||||||
@@ -808,8 +808,8 @@ void NetMissionParameters::MWNetMissionParameters::SaveParameters(DynamicMemoryS
|
|||||||
stream->WriteBits(&m_pureMapCycle,1);
|
stream->WriteBits(&m_pureMapCycle,1);
|
||||||
|
|
||||||
|
|
||||||
stream->WriteBits(&m_maxPlayers, 5); // 16 players maximum
|
stream->WriteBits(&m_maxPlayers, 5); // max 31 players
|
||||||
stream->WriteBits(&m_maxBots, 5); // 16 players maximum
|
stream->WriteBits(&m_maxBots, 5); // max 31
|
||||||
stream->WriteBits(&m_mapID, 16); // 65535 maps available
|
stream->WriteBits(&m_mapID, 16); // 65535 maps available
|
||||||
stream->WriteBits(&m_mapClientCRC, 64);
|
stream->WriteBits(&m_mapClientCRC, 64);
|
||||||
|
|
||||||
@@ -927,8 +927,8 @@ void NetMissionParameters::MWNetMissionParameters::LoadParameters(MemoryStream *
|
|||||||
stream->ReadBits(&m_useMapCycle,1);
|
stream->ReadBits(&m_useMapCycle,1);
|
||||||
stream->ReadBits(&m_pureMapCycle,1);
|
stream->ReadBits(&m_pureMapCycle,1);
|
||||||
|
|
||||||
stream->ReadBits(&m_maxPlayers, 5); // 16 players max
|
stream->ReadBits(&m_maxPlayers, 5); // max 31 players
|
||||||
stream->ReadBits(&m_maxBots, 5); // 16 players max
|
stream->ReadBits(&m_maxBots, 5); // max 31
|
||||||
stream->ReadBits(&m_mapID, 16); // 65535 maps available
|
stream->ReadBits(&m_mapID, 16); // 65535 maps available
|
||||||
stream->ReadBits(&m_mapClientCRC, 64);
|
stream->ReadBits(&m_mapClientCRC, 64);
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ extern int g_nMech4Comm;
|
|||||||
// server & client only
|
// server & client only
|
||||||
extern BOOL g_bIsServer;
|
extern BOOL g_bIsServer;
|
||||||
// console(All), server & client only(g_aPlayerInfos[0]), server only(g_aPlayerInfos[1..g_nBOTs]
|
// console(All), server & client only(g_aPlayerInfos[0]), server only(g_aPlayerInfos[1..g_nBOTs]
|
||||||
extern SPlayerInfo g_aPlayerInfos[20];
|
extern SPlayerInfo g_aPlayerInfos[35]; // 31 pilots + 1 CS + 3 spare
|
||||||
extern int g_nPlayerInfos;
|
extern int g_nPlayerInfos;
|
||||||
extern int g_nServer;
|
extern int g_nServer;
|
||||||
extern int g_nTeslas;
|
extern int g_nTeslas;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "MW4AppHeaders.hpp"
|
#include "MW4AppHeaders.hpp"
|
||||||
#include "MW4Application.hpp"
|
#include "MW4Application.hpp"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <stdio.h> // [help] FILE/fopen/fprintf used by MW4_ShowCommandLineHelp
|
|
||||||
#include <MW4DedicatedUI\DedicatedUI.hpp>
|
#include <MW4DedicatedUI\DedicatedUI.hpp>
|
||||||
#include <GameOS\GameOS.hpp>
|
#include <GameOS\GameOS.hpp>
|
||||||
#include <GameOS\Platform.hpp>
|
#include <GameOS\Platform.hpp>
|
||||||
@@ -86,18 +85,9 @@ extern Time g_PlasmaClearTime;
|
|||||||
extern Time g_TOC_ADT;
|
extern Time g_TOC_ADT;
|
||||||
extern Time g_AUX_ADT;
|
extern Time g_AUX_ADT;
|
||||||
extern bool g_UseMSRSP;
|
extern bool g_UseMSRSP;
|
||||||
extern int g_nTypeOfMFDs; // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual), 4 - split dual 640x480
|
extern int g_nTypeOfMFDs; // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual)
|
||||||
// [tmon] Display-device override in Main/Radar/MFD1/MFD2 order; -1 = auto-detect.
|
|
||||||
// Defined in CoreTech GameOS VideoCard.cpp and consumed by FindVideoCards().
|
|
||||||
extern int g_naMonitorOverride[4];
|
|
||||||
extern int g_nRIOType;
|
extern int g_nRIOType;
|
||||||
extern DWORD g_dwRIOBaud; // [tbaud]
|
extern DWORD g_dwRIOBaud; // [tbaud]
|
||||||
// [fpslog] Non-zero enables the per-second frame pacing report (gos-fps.txt).
|
|
||||||
// Defined in CoreTech GameOS WinMain.cpp.
|
|
||||||
extern int g_nFpsLog;
|
|
||||||
// [panelcoop] SetCooperativeLevel form used by the radar/MFD panels; 0 = legacy.
|
|
||||||
// Defined in CoreTech GameOS render.cpp.
|
|
||||||
extern int g_nHshCoopMode;
|
|
||||||
extern LONG g_ThrottleDir;
|
extern LONG g_ThrottleDir;
|
||||||
extern bool g_bCanSuicideAllways;
|
extern bool g_bCanSuicideAllways;
|
||||||
extern bool g_f3dtarget;
|
extern bool g_f3dtarget;
|
||||||
@@ -826,242 +816,6 @@ GetEnvironmentVariableA(
|
|||||||
LPSTR lpBuffer,
|
LPSTR lpBuffer,
|
||||||
DWORD nSize
|
DWORD nSize
|
||||||
);
|
);
|
||||||
// [help] ---------------------------------------------------------------------
|
|
||||||
// Complete command-line reference, shown by "MW4.exe -help".
|
|
||||||
//
|
|
||||||
// MW4 is a GUI-subsystem application, so there is no console to print to when it
|
|
||||||
// is launched normally. -help therefore writes the full reference to
|
|
||||||
// mw4-help.txt next to the executable, opens it in Notepad, and exits without
|
|
||||||
// starting the game. If Notepad cannot be launched, a message box reports where
|
|
||||||
// the file was written.
|
|
||||||
//
|
|
||||||
// KEEP THIS IN SYNC when adding or changing a command-line switch.
|
|
||||||
static const char* const g_apszCommandLineHelp[] =
|
|
||||||
{
|
|
||||||
"BattleTech: FireStorm - MW4.exe command line reference",
|
|
||||||
"======================================================",
|
|
||||||
"",
|
|
||||||
"Usage: MW4.exe [options]",
|
|
||||||
"",
|
|
||||||
"Options may appear in any order and are case-insensitive. An option that takes",
|
|
||||||
"a value must be followed by a single space and then the value, for example:",
|
|
||||||
"",
|
|
||||||
" MW4.exe -tmfds 4 -tmon 1,2,3,4 -tbaud 115200",
|
|
||||||
"",
|
|
||||||
"Values outside the documented range are ignored and the default is kept.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"DISPLAY AND VIDEO",
|
|
||||||
"-----------------",
|
|
||||||
" -window Run in a window instead of fullscreen.",
|
|
||||||
" -fullscreen Force fullscreen. Overrides -window.",
|
|
||||||
" -tmfds <0-4> MFD output mode:",
|
|
||||||
" 0 = no MFDs",
|
|
||||||
" 1 = original 1280x480 span (5 channels + radar)",
|
|
||||||
" 2 = black and white (3 channels + 1 dual)",
|
|
||||||
" 3 = colour (3 channels + 2 dual)",
|
|
||||||
" 4 = split dual 640x480 - drives two independent",
|
|
||||||
" MFD monitors, so no span hardware is needed",
|
|
||||||
" -tmon <m>,<r>,<f1>,<f2> Hard-assign display devices in the order",
|
|
||||||
" main, radar, MFD1, MFD2. Values are 1-based; a",
|
|
||||||
" value of 0 leaves that slot auto-detected.",
|
|
||||||
" Separators may be ',' '/' or ':'.",
|
|
||||||
" MFD1 and MFD2 only apply with -tmfds 4.",
|
|
||||||
" Example: -tmon 1,2,4,3 swaps the two MFD panels.",
|
|
||||||
" -tcoop <0-5> How the radar/MFD panels claim their displays.",
|
|
||||||
" 0 = legacy single call (as shipped; works on XP",
|
|
||||||
" and under dgVoodoo2, fails on Windows 10).",
|
|
||||||
" 1 = drop SETFOCUSWINDOW, let DirectDraw create",
|
|
||||||
" the device window.",
|
|
||||||
" 2 = SETFOCUSWINDOW first, then CREATEDEVICEWINDOW.",
|
|
||||||
" 3 = SETFOCUSWINDOW first, then EXCLUSIVE only,",
|
|
||||||
" matching what the main display already does.",
|
|
||||||
" 4 = EXCLUSIVE|FULLSCREEN only. The main display",
|
|
||||||
" has already set the focus window, so the",
|
|
||||||
" panels never contend for it.",
|
|
||||||
" 5 = as 4, plus ALLOWREBOOT.",
|
|
||||||
" Results are reported in gos-displays.txt.",
|
|
||||||
" -mechview <1|2> Show the rotating mech view. 1 = on the radar",
|
|
||||||
" -mv <1|2> screen, 2 = on the main screen. Default: off.",
|
|
||||||
" -3dt Draw the MFD target as a live 3D model instead of",
|
|
||||||
" the pre-rendered 2D bitmaps.",
|
|
||||||
" -capdev <0-7> Select which device screen captures come from:",
|
|
||||||
" 0 main, 1 radar, 2 mission review, 3 hudchat,",
|
|
||||||
" 4 nav, 5 weapon, 6 target, 7 armor",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"AUDIO AND PLASMA DISPLAY",
|
|
||||||
"------------------------",
|
|
||||||
" -nosound Disable all sound output.",
|
|
||||||
" -noplasma Disable the COM2 plasma score display.",
|
|
||||||
" -pctime <seconds> Plasma display clear time. Minimum 3.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"POD HARDWARE AND ARCADE (CTCL)",
|
|
||||||
"------------------------------",
|
|
||||||
" -ctcltype <1-3> CTCL role for this machine:",
|
|
||||||
" -ctcl <1-3> 1 = console, 2 = game pod, 3 = cameraship.",
|
|
||||||
" Default when absent: console.",
|
|
||||||
" -dragon Disable CTCL entirely and run standalone.",
|
|
||||||
" -coop Co-operative arcade mode. Implies 1 coin per game",
|
|
||||||
" and the game-pod role.",
|
|
||||||
" -#coop <coins> Co-op with an explicit coin count. A negative",
|
|
||||||
" value selects the cameraship role instead.",
|
|
||||||
" -trio <0|1> RIO cockpit board protocol. 0 = original board,",
|
|
||||||
" 1 = newer protocol (different framing and acks).",
|
|
||||||
" -tbaud <9600-921600> Force the COM1 baud rate for the RIO board. Use",
|
|
||||||
" for replica boards with a high-speed UART. The",
|
|
||||||
" wire protocol is unchanged; only the rate differs.",
|
|
||||||
" -useorgjoy Use the original joystick axis mapping.",
|
|
||||||
" -ojoy Same as -useorgjoy.",
|
|
||||||
" -usetockeys Enable TOC keys. They are disabled by default.",
|
|
||||||
" -tocadt <seconds> TOC auto-detect timeout. Minimum 5.",
|
|
||||||
" -auxadt <seconds> AUX auto-detect timeout. Minimum 3.",
|
|
||||||
" -usecam Force camera mode. Implied by the cameraship role.",
|
|
||||||
" -suicide Allow self-destruct at any time.",
|
|
||||||
" -jpd Enable JPD input handling.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"ZOOM AND FIELD OF VIEW",
|
|
||||||
"----------------------",
|
|
||||||
" -zmfova <0.01-0.5> Zoom field-of-view, stage A.",
|
|
||||||
" -zmfovb <0.01-0.5> Zoom field-of-view, stage B. A and B are swapped",
|
|
||||||
" automatically so that A is the wider of the two.",
|
|
||||||
" -zmmrgn <0.01-0.49> Zoom left/right margin, as a fraction of width.",
|
|
||||||
" -zmtime <0, or 0.05-3.0> Zoom transition time in seconds. 0 = instant.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"MULTIPLAYER AND NETWORK",
|
|
||||||
"-----------------------",
|
|
||||||
" -serve Automatically host a server game on startup.",
|
|
||||||
" -lobby Indicates the game was started from a lobby.",
|
|
||||||
" -game <file> Load the named game/mission file.",
|
|
||||||
" -gamename <name> Name advertised for the hosted game.",
|
|
||||||
" -playername <name> Player name to use.",
|
|
||||||
" -password <password> Password for the hosted or joined game.",
|
|
||||||
" -advertiseport <port> Port used to advertise the server.",
|
|
||||||
" -ipx Use IPX in the server browser.",
|
|
||||||
" -nodplay Disable DirectPlay networking.",
|
|
||||||
" -directplayprotocol Force use of the DirectPlay protocol.",
|
|
||||||
" -secure Enable secure networking for the game session.",
|
|
||||||
" -set_internet Configure network settings for internet play.",
|
|
||||||
" -set_lan Configure network settings for LAN play.",
|
|
||||||
" -win32dedicated Run as a Win32 dedicated server. Disables sound",
|
|
||||||
" and rendering and uses the dedicated server UI.",
|
|
||||||
" -allowmultiple Allow more than one instance of the game to run.",
|
|
||||||
" -oldmpui Use the older multiplayer user interface.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"LOGGING AND DIAGNOSTICS",
|
|
||||||
"-----------------------",
|
|
||||||
" -zlogstyle <value> ZLog output style.",
|
|
||||||
" -zlogflush <value> ZLog flush behaviour.",
|
|
||||||
" -zlogbuffer <size> ZLog buffer size.",
|
|
||||||
" -zlogname <filename> ZLog output file name.",
|
|
||||||
" -olog Write the legacy .log match report instead of the",
|
|
||||||
" newer .rpt format.",
|
|
||||||
" -fps Write a per-second frame pacing report to",
|
|
||||||
" gos-fps.txt next to the executable: average fps,",
|
|
||||||
" 1% low, worst frame in ms, and a count of frames",
|
|
||||||
" taking over twice the average (hitches). Useful",
|
|
||||||
" for confirming smooth pacing on the MFD modes.",
|
|
||||||
" Off unless this switch is given.",
|
|
||||||
" -gamestats Save game statistics.",
|
|
||||||
" -help Show this reference and exit without starting.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"DEVELOPMENT AND TEST BUILDS",
|
|
||||||
"---------------------------",
|
|
||||||
"The following are only active in LAB (Debug, Armor and Profile) builds. In a",
|
|
||||||
"Release build they are accepted and ignored.",
|
|
||||||
"",
|
|
||||||
" -build Rebuild the resource packages at startup.",
|
|
||||||
" -norun Do not run the game after building.",
|
|
||||||
" -demo Demo mode.",
|
|
||||||
" -rekey Re-key resources.",
|
|
||||||
" -bloat Enable bloat diagnostics.",
|
|
||||||
" -reportnotresourced Report assets that are not in a resource package.",
|
|
||||||
" -armorlevel <0-4> Assertion/armor checking level.",
|
|
||||||
" -leak <n> Memory trace level.",
|
|
||||||
" -diskfirst Prefer loose files on disk over packaged data.",
|
|
||||||
" -testclasses Run internal class self-tests.",
|
|
||||||
" -aistats Enable AI statistics collection.",
|
|
||||||
" -bspmap Save BSP map differences.",
|
|
||||||
" -showscripterrors Display script errors as they occur.",
|
|
||||||
" -mrtest Mission review test mode.",
|
|
||||||
" -prtest Print report test mode.",
|
|
||||||
" -nowrc Disable weapon range checking.",
|
|
||||||
" -regs Debug builds only: enable script regions.",
|
|
||||||
" -spew Debug builds only: enable script spews.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"OTHER",
|
|
||||||
"-----",
|
|
||||||
" -nocd Skip the CD check. (The CD check is already",
|
|
||||||
" disabled unconditionally in this build.)",
|
|
||||||
" -noeula Skip the end user licence agreement screen.",
|
|
||||||
" -noautoconfig Skip automatic video and sound configuration.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"RECOGNISED BUT INACTIVE",
|
|
||||||
"-----------------------",
|
|
||||||
" -join Parsed for compatibility but has no effect; the",
|
|
||||||
" code that consumed it is commented out.",
|
|
||||||
" -noabzug Parsed only in a disabled code path.",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"RELATED CONFIGURATION",
|
|
||||||
"---------------------",
|
|
||||||
"Most persistent settings live in options.ini next to MW4.exe rather than on the",
|
|
||||||
"command line. See OPTIONS-INI.md in the source repository for a full reference.",
|
|
||||||
"Arcade pod assignments live in c:\\ctcl.ini.",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static void MW4_ShowCommandLineHelp()
|
|
||||||
{
|
|
||||||
char szDir[MAX_PATH];
|
|
||||||
char szPath[MAX_PATH];
|
|
||||||
|
|
||||||
// Write next to the executable rather than the working directory, so the file
|
|
||||||
// is easy to find no matter how the game was launched.
|
|
||||||
szDir[0] = '\0';
|
|
||||||
GetModuleFileNameA(NULL, szDir, sizeof(szDir));
|
|
||||||
char* pSlash = strrchr(szDir, '\\');
|
|
||||||
if (pSlash)
|
|
||||||
*(pSlash + 1) = '\0';
|
|
||||||
else
|
|
||||||
szDir[0] = '\0';
|
|
||||||
sprintf(szPath, "%smw4-help.txt", szDir);
|
|
||||||
|
|
||||||
FILE* f = fopen(szPath, "wt");
|
|
||||||
if (!f)
|
|
||||||
{
|
|
||||||
MessageBoxA(NULL,
|
|
||||||
"Could not write mw4-help.txt next to MW4.exe.\n"
|
|
||||||
"Check that the folder is writable.",
|
|
||||||
"MW4 command line help", MB_OK | MB_ICONWARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; g_apszCommandLineHelp[i] != NULL; i++)
|
|
||||||
fprintf(f, "%s\n", g_apszCommandLineHelp[i]);
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
// Open it for the user. WinExec is used rather than ShellExecute to avoid a
|
|
||||||
// dependency on shellapi.h in this translation unit.
|
|
||||||
char szCmd[MAX_PATH + 32];
|
|
||||||
sprintf(szCmd, "notepad.exe \"%s\"", szPath);
|
|
||||||
if (WinExec(szCmd, SW_SHOWNORMAL) < 32)
|
|
||||||
{
|
|
||||||
char szMsg[MAX_PATH + 128];
|
|
||||||
sprintf(szMsg,
|
|
||||||
"The MW4.exe command line reference was written to:\n\n%s",
|
|
||||||
szPath);
|
|
||||||
MessageBoxA(NULL, szMsg, "MW4 command line help", MB_OK | MB_ICONINFORMATION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void __stdcall GetGameOSEnvironment(char* CommandLine)
|
void __stdcall GetGameOSEnvironment(char* CommandLine)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@@ -1131,7 +885,7 @@ void __stdcall GetGameOSEnvironment(char* CommandLine)
|
|||||||
Environment.allowMultipleApps = false;
|
Environment.allowMultipleApps = false;
|
||||||
Environment.AllowJoinInProgress = true;
|
Environment.AllowJoinInProgress = true;
|
||||||
Environment.NetworkGame = true;
|
Environment.NetworkGame = true;
|
||||||
Environment.NetworkMaxPlayers = 16;
|
Environment.NetworkMaxPlayers = 31; // max 31 (fits in 5-bit bitstream)
|
||||||
Environment.NetworkGUID[0] = 0x95;
|
Environment.NetworkGUID[0] = 0x95;
|
||||||
Environment.NetworkGUID[1] = 0x78;
|
Environment.NetworkGUID[1] = 0x78;
|
||||||
Environment.NetworkGUID[2] = 0xFF;
|
Environment.NetworkGUID[2] = 0xFF;
|
||||||
@@ -1422,10 +1176,49 @@ DWORD __stdcall gos_EnableSetting( gosSetting Setting, DWORD Value );
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// [help] -help is handled at the top of WinMain (see MW4_ShowCommandLineHelp),
|
if (strstr(all_lower, "-help"))
|
||||||
// which runs before this and exits the process. The old partial list that used
|
{
|
||||||
// to live here was incomplete and only reached the debug log, so it was removed
|
SPEWALWAYS((NULL, "Command line options:\n"));
|
||||||
// in favour of a single maintained reference.
|
SPEWALWAYS((NULL, "-allowmultiple"));
|
||||||
|
SPEWALWAYS((NULL, "-lobby"));
|
||||||
|
SPEWALWAYS((NULL, "-game <file>"));
|
||||||
|
SPEWALWAYS((NULL, "-gamename <name>"));
|
||||||
|
SPEWALWAYS((NULL, "-playername <name>"));
|
||||||
|
SPEWALWAYS((NULL, "-serve"));
|
||||||
|
SPEWALWAYS((NULL, "-join"));
|
||||||
|
SPEWALWAYS((NULL, "-nosound"));
|
||||||
|
SPEWALWAYS((NULL, "-window"));
|
||||||
|
|
||||||
|
SPEWALWAYS((NULL, "-zlogstyle <value>"));
|
||||||
|
SPEWALWAYS((NULL, "-zlogflush <value>"));
|
||||||
|
SPEWALWAYS((NULL, "-zlogbuffer <size>"));
|
||||||
|
SPEWALWAYS((NULL, "-zlogname <filename>"));
|
||||||
|
|
||||||
|
SPEWALWAYS((NULL, "-secure"));
|
||||||
|
|
||||||
|
SPEWALWAYS((NULL, "-ipx"));
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LAB_ONLY
|
||||||
|
SPEWALWAYS((NULL, "-nodplay"));
|
||||||
|
SPEWALWAYS((NULL, "-dontuseipx"));
|
||||||
|
SPEWALWAYS((NULL, "-dontusetcp"));
|
||||||
|
SPEWALWAYS((NULL, "-dontusegamespy"));
|
||||||
|
SPEWALWAYS((NULL, "-dontusegun"));
|
||||||
|
SPEWALWAYS((NULL, "-aistats"));
|
||||||
|
SPEWALWAYS((NULL, "-oldmpui"));
|
||||||
|
SPEWALWAYS((NULL, "-testclasses"));
|
||||||
|
SPEWALWAYS((NULL, "-build"));
|
||||||
|
SPEWALWAYS((NULL, "-norun"));
|
||||||
|
SPEWALWAYS((NULL, "-demo"));
|
||||||
|
SPEWALWAYS((NULL, "-rekey"));
|
||||||
|
SPEWALWAYS((NULL, "-bloat"));
|
||||||
|
SPEWALWAYS((NULL, "-reportnotresourced"));
|
||||||
|
SPEWALWAYS((NULL, "-armorlevel <n>"));
|
||||||
|
SPEWALWAYS((NULL, "-leak <n>"));
|
||||||
|
SPEWALWAYS((NULL, "-diskfirst"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (MWApplication::AutoLaunchServerGame || MWApplication::Win32DedicatedServer)
|
if (MWApplication::AutoLaunchServerGame || MWApplication::Win32DedicatedServer)
|
||||||
@@ -1501,14 +1294,6 @@ int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int n
|
|||||||
for (p=all_lower; i>0; ++p,--i)
|
for (p=all_lower; i>0; ++p,--i)
|
||||||
*p=(char)tolower(*p);
|
*p=(char)tolower(*p);
|
||||||
|
|
||||||
// [help] Handle -help before anything is initialised: write the full command
|
|
||||||
// line reference next to the exe, open it, and exit without starting the game.
|
|
||||||
if (strstr(all_lower, "-help"))
|
|
||||||
{
|
|
||||||
MW4_ShowCommandLineHelp();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *token;
|
char *token;
|
||||||
// jcem - begin
|
// jcem - begin
|
||||||
token = strstr(all_lower, "-ctcltype ");
|
token = strstr(all_lower, "-ctcltype ");
|
||||||
@@ -1658,34 +1443,6 @@ int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int n
|
|||||||
if ((0 <= n) && (n <= 4)) // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual), 4 - split dual 640x480
|
if ((0 <= n) && (n <= 4)) // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual), 4 - split dual 640x480
|
||||||
g_nTypeOfMFDs = n;
|
g_nTypeOfMFDs = n;
|
||||||
}
|
}
|
||||||
// [tmon] Hard-assign the display devices when DirectDraw enumerates them in an
|
|
||||||
// unexpected order. Syntax: -tmon <main>,<radar>,<mfd1>,<mfd2>
|
|
||||||
// Values are 1-based (the normal case is "-tmon 1,2,3,4"); a value of 0 leaves that
|
|
||||||
// slot to auto-detection, so "-tmon 2,1,0,0" only swaps main and radar. Separators
|
|
||||||
// may be ',' '/' or ':'. mfd1/mfd2 are only used by -tmfds 4.
|
|
||||||
token = strstr(all_lower, "-tmon ");
|
|
||||||
if (token)
|
|
||||||
{
|
|
||||||
const char* p = &token[6];
|
|
||||||
int slot = 0;
|
|
||||||
while (slot < 4)
|
|
||||||
{
|
|
||||||
while (*p == ' ')
|
|
||||||
p++;
|
|
||||||
if ((*p < '0') || ('9' < *p))
|
|
||||||
break;
|
|
||||||
int v = atoi(p);
|
|
||||||
while (('0' <= *p) && (*p <= '9'))
|
|
||||||
p++;
|
|
||||||
// 0 = leave auto-detected, otherwise convert 1-based to a 0-based device index
|
|
||||||
g_naMonitorOverride[slot] = (0 < v) ? (v - 1) : -1;
|
|
||||||
slot++;
|
|
||||||
if ((*p == ',') || (*p == '/') || (*p == ':'))
|
|
||||||
p++;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token = strstr(all_lower, "-trio ");
|
token = strstr(all_lower, "-trio ");
|
||||||
if (token && token[6])
|
if (token && token[6])
|
||||||
{
|
{
|
||||||
@@ -1700,17 +1457,6 @@ int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int n
|
|||||||
if ((9600 <= n) && (n <= 921600))
|
if ((9600 <= n) && (n <= 921600))
|
||||||
g_dwRIOBaud = (DWORD)n;
|
g_dwRIOBaud = (DWORD)n;
|
||||||
}
|
}
|
||||||
// [panelcoop] Cooperative-level form for the radar/MFD panels. 0 = legacy combined
|
|
||||||
// call (what shipped); 1-3 are forms compatible with strict modern DirectDraw.
|
|
||||||
token = strstr(all_lower, "-tcoop ");
|
|
||||||
if (token && token[7])
|
|
||||||
{
|
|
||||||
int n = atoi(&token[7]);
|
|
||||||
if ((0 <= n) && (n <= 5))
|
|
||||||
g_nHshCoopMode = n;
|
|
||||||
}
|
|
||||||
// [fpslog] Enable the per-second frame pacing report written to gos-fps.txt.
|
|
||||||
g_nFpsLog = (strstr(all_lower, "-fps") != NULL) ? 1 : 0;
|
|
||||||
g_bCanSuicideAllways = (strstr(all_lower, "-suicide") != NULL);
|
g_bCanSuicideAllways = (strstr(all_lower, "-suicide") != NULL);
|
||||||
g_f3dtarget = (strstr(all_lower, "-3dt") != NULL);
|
g_f3dtarget = (strstr(all_lower, "-3dt") != NULL);
|
||||||
g_bOldLOG = (strstr(all_lower, "-olog") != NULL);
|
g_bOldLOG = (strstr(all_lower, "-olog") != NULL);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "recscore.h"
|
#include "recscore.h"
|
||||||
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
||||||
|
|
||||||
#define MAX_TESLAS 16
|
#define MAX_TESLAS 31
|
||||||
#define MAX_CAMERAS 4
|
#define MAX_CAMERAS 4
|
||||||
|
|
||||||
#define PORT_Launcher 1000
|
#define PORT_Launcher 1000
|
||||||
@@ -263,7 +263,7 @@ CInviteCOOP g_InviteCOOP;
|
|||||||
// Mech4 only
|
// Mech4 only
|
||||||
int g_nMech4Comm = 0;
|
int g_nMech4Comm = 0;
|
||||||
BOOL g_bIsServer = FALSE;
|
BOOL g_bIsServer = FALSE;
|
||||||
SPlayerInfo g_aPlayerInfos[20];
|
SPlayerInfo g_aPlayerInfos[35]; // 31 pilots + 1 CS + 3 spare
|
||||||
int g_nPlayerInfos;
|
int g_nPlayerInfos;
|
||||||
int g_nServer;
|
int g_nServer;
|
||||||
int g_nTeslas;
|
int g_nTeslas;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "recscore.h"
|
#include "recscore.h"
|
||||||
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
||||||
|
|
||||||
#define MAX_TESLAS 16
|
#define MAX_TESLAS 31
|
||||||
#define MAX_CAMERAS 4
|
#define MAX_CAMERAS 4
|
||||||
|
|
||||||
#define PORT_Launcher 1000
|
#define PORT_Launcher 1000
|
||||||
@@ -263,7 +263,7 @@ CInviteCOOP g_InviteCOOP;
|
|||||||
// Mech4 only
|
// Mech4 only
|
||||||
int g_nMech4Comm = 0;
|
int g_nMech4Comm = 0;
|
||||||
BOOL g_bIsServer = FALSE;
|
BOOL g_bIsServer = FALSE;
|
||||||
SPlayerInfo g_aPlayerInfos[20];
|
SPlayerInfo g_aPlayerInfos[35]; // 31 pilots + 1 CS + 3 spare
|
||||||
int g_nPlayerInfos;
|
int g_nPlayerInfos;
|
||||||
int g_nServer;
|
int g_nServer;
|
||||||
int g_nTeslas;
|
int g_nTeslas;
|
||||||
|
|||||||
@@ -359,39 +359,6 @@ void CChildView::OnFileBannerSetting()
|
|||||||
dlg.DoModal();
|
dlg.DoModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LoadBannerTextFile()
|
|
||||||
{
|
|
||||||
char szPath[MAX_PATH];
|
|
||||||
sprintf(szPath, "%s\\banner.txt", AssetsDirectory1);
|
|
||||||
|
|
||||||
FILE* f = fopen(szPath, "rt");
|
|
||||||
if (f) {
|
|
||||||
char szBuf[sizeof(g_szPrintBanner)];
|
|
||||||
if (fgets(szBuf, sizeof(szBuf), f)) {
|
|
||||||
size_t n = strlen(szBuf);
|
|
||||||
while (n && (szBuf[n - 1] == '\r' || szBuf[n - 1] == '\n')) {
|
|
||||||
szBuf[--n] = '\0';
|
|
||||||
}
|
|
||||||
strncpy(g_szPrintBanner, szBuf, sizeof(g_szPrintBanner) - 1);
|
|
||||||
g_szPrintBanner[sizeof(g_szPrintBanner) - 1] = '\0';
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SaveBannerTextFile()
|
|
||||||
{
|
|
||||||
char szPath[MAX_PATH];
|
|
||||||
sprintf(szPath, "%s\\banner.txt", AssetsDirectory1);
|
|
||||||
|
|
||||||
FILE* f = fopen(szPath, "wt");
|
|
||||||
if (f) {
|
|
||||||
fputs(g_szPrintBanner, f);
|
|
||||||
fputc('\n', f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// CDlgBannerSetting
|
// CDlgBannerSetting
|
||||||
|
|
||||||
@@ -411,7 +378,6 @@ void CDlgBannerSetting::DoDataExchange(CDataExchange* pDX)
|
|||||||
BOOL CDlgBannerSetting::OnInitDialog()
|
BOOL CDlgBannerSetting::OnInitDialog()
|
||||||
{
|
{
|
||||||
CDialog::OnInitDialog();
|
CDialog::OnInitDialog();
|
||||||
LoadBannerTextFile();
|
|
||||||
SetDlgItemText(IDC_BANNER_TEXT, g_szPrintBanner);
|
SetDlgItemText(IDC_BANNER_TEXT, g_szPrintBanner);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -419,7 +385,11 @@ BOOL CDlgBannerSetting::OnInitDialog()
|
|||||||
void CDlgBannerSetting::OnOK()
|
void CDlgBannerSetting::OnOK()
|
||||||
{
|
{
|
||||||
GetDlgItemText(IDC_BANNER_TEXT, g_szPrintBanner, sizeof(g_szPrintBanner));
|
GetDlgItemText(IDC_BANNER_TEXT, g_szPrintBanner, sizeof(g_szPrintBanner));
|
||||||
SaveBannerTextFile();
|
|
||||||
|
// Persist to options.ini so it survives a restart.
|
||||||
|
char szINI[MAX_PATH];
|
||||||
|
sprintf(szINI, "%s\\options.ini", AssetsDirectory1);
|
||||||
|
WritePrivateProfileString("battle tech print", "BannerText", g_szPrintBanner, szINI);
|
||||||
|
|
||||||
CDialog::OnOK();
|
CDialog::OnOK();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,8 +269,7 @@ static RECT s_rcTextMargin; // check!!! - margin 2 panes on bottom relative to t
|
|||||||
static int s_nTextHeight; // check!!! - text line height
|
static int s_nTextHeight; // check!!! - text line height
|
||||||
|
|
||||||
// Configurable banner text printed at the bottom of each sheet.
|
// Configurable banner text printed at the bottom of each sheet.
|
||||||
// Primary source: banner.txt next to mw4print.exe (first line only).
|
// Set via [battle tech print] BannerText= in options.ini next to the exe.
|
||||||
// Legacy fallback: [battle tech print] BannerText= in options.ini.
|
|
||||||
char g_szPrintBanner[256] = "Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!";
|
char g_szPrintBanner[256] = "Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!";
|
||||||
|
|
||||||
void _stdcall GetFileName4GUID(char szBuf[256], const char* pcszDrv, const char* pcszPath, const char* pcszExt, const GUID& guid = *(const GUID*)NULL);
|
void _stdcall GetFileName4GUID(char szBuf[256], const char* pcszDrv, const char* pcszPath, const char* pcszExt, const GUID& guid = *(const GUID*)NULL);
|
||||||
@@ -3491,7 +3490,7 @@ bool CRecScoreFull::LoadPR(const char* pcszFile)
|
|||||||
void CRecScoreFull::LoadPrintParams() const
|
void CRecScoreFull::LoadPrintParams() const
|
||||||
{
|
{
|
||||||
MString strOI(&AssetsDirectory1[0]);
|
MString strOI(&AssetsDirectory1[0]);
|
||||||
strOI += "\\options.ini";
|
strOI += "options.ini";
|
||||||
|
|
||||||
LoadPrintParam(strOI, "CellMarginLR", s_nCellMarginLR, U_point(200), U_point(10), U_point(500));
|
LoadPrintParam(strOI, "CellMarginLR", s_nCellMarginLR, U_point(200), U_point(10), U_point(500));
|
||||||
LoadPrintParam(strOI, "CellMarginTV", s_nCellMarginTV, U_point(200), U_point(10), U_point(500));
|
LoadPrintParam(strOI, "CellMarginTV", s_nCellMarginTV, U_point(200), U_point(10), U_point(500));
|
||||||
@@ -3513,33 +3512,9 @@ void CRecScoreFull::LoadPrintParams() const
|
|||||||
LoadPrintParam(strOI, "TextHeight", s_nTextHeight, U_point(1100), U_point(600), U_point(1500));
|
LoadPrintParam(strOI, "TextHeight", s_nTextHeight, U_point(1100), U_point(600), U_point(1500));
|
||||||
|
|
||||||
// Configurable banner/URL text printed at the bottom of each sheet.
|
// Configurable banner/URL text printed at the bottom of each sheet.
|
||||||
// Prefer banner.txt for persistence (game may sanitize unknown options.ini keys).
|
|
||||||
bool bLoadedBanner = false;
|
|
||||||
{
|
|
||||||
char szBannerPath[MAX_PATH];
|
|
||||||
sprintf(szBannerPath, "%s\\banner.txt", AssetsDirectory1);
|
|
||||||
FILE* fBanner = fopen(szBannerPath, "rt");
|
|
||||||
if (fBanner) {
|
|
||||||
char szBuf[sizeof(g_szPrintBanner)];
|
|
||||||
if (fgets(szBuf, sizeof(szBuf), fBanner)) {
|
|
||||||
size_t n = strlen(szBuf);
|
|
||||||
while (n && (szBuf[n - 1] == '\r' || szBuf[n - 1] == '\n')) {
|
|
||||||
szBuf[--n] = '\0';
|
|
||||||
}
|
|
||||||
if (szBuf[0]) {
|
|
||||||
strncpy(g_szPrintBanner, szBuf, sizeof(g_szPrintBanner) - 1);
|
|
||||||
g_szPrintBanner[sizeof(g_szPrintBanner) - 1] = '\0';
|
|
||||||
bLoadedBanner = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fBanner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!bLoadedBanner) {
|
|
||||||
GetPrivateProfileString("battle tech print", "BannerText",
|
GetPrivateProfileString("battle tech print", "BannerText",
|
||||||
"Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!",
|
"Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!",
|
||||||
g_szPrintBanner, sizeof(g_szPrintBanner), strOI);
|
g_szPrintBanner, sizeof(g_szPrintBanner), strOI);
|
||||||
}
|
|
||||||
LoadPrintParam(strOI, "ImageTitleHeight", g_aFonts[FNTI_IMAGE_TITLE].m_nTextHeight, U_point(1500), U_point(900), U_point(2000));
|
LoadPrintParam(strOI, "ImageTitleHeight", g_aFonts[FNTI_IMAGE_TITLE].m_nTextHeight, U_point(1500), U_point(900), U_point(2000));
|
||||||
LoadPrintParam(strOI, "TableTextHeight", g_aFonts[FNTI_TABLE_TEXT].m_nTextHeight, U_point(900), U_point(600), U_point(1200));
|
LoadPrintParam(strOI, "TableTextHeight", g_aFonts[FNTI_TABLE_TEXT].m_nTextHeight, U_point(900), U_point(600), U_point(1200));
|
||||||
LoadPrintParam(strOI, "MHTextHeight", g_aFonts[FNTI_MISSION_HIGHLIGHTS].m_nTextHeight, U_point(1200), U_point(800), U_point(1600));
|
LoadPrintParam(strOI, "MHTextHeight", g_aFonts[FNTI_MISSION_HIGHLIGHTS].m_nTextHeight, U_point(1200), U_point(800), U_point(1600));
|
||||||
|
|||||||
@@ -806,7 +806,7 @@ STRINGTABLE DISCARDABLE
|
|||||||
BEGIN
|
BEGIN
|
||||||
IDS_ML_CH_ACCELERATION "Acceleration (Meters/Sec.):"
|
IDS_ML_CH_ACCELERATION "Acceleration (Meters/Sec.):"
|
||||||
IDS_ML_CH_DECELERATION "Deceleration (Meters/Sec.):"
|
IDS_ML_CH_DECELERATION "Deceleration (Meters/Sec.):"
|
||||||
IDS_ML_CH_TURNRATE "Turn Rate (Top Speed Rad/Sec):"
|
IDS_ML_CH_TURNRATE "Turn Rate (Degrees/Sec.):"
|
||||||
IDS_ML_CH_TWISTRANGE "Torso Twist Range (Degrees):"
|
IDS_ML_CH_TWISTRANGE "Torso Twist Range (Degrees):"
|
||||||
IDS_ML_CH_TWISTSPEED "Torso Twist Speed (Degrees/Sec.):"
|
IDS_ML_CH_TWISTSPEED "Torso Twist Speed (Degrees/Sec.):"
|
||||||
IDS_ML_CH_NEWMECH "NEW 'MECH"
|
IDS_ML_CH_NEWMECH "NEW 'MECH"
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ void NetMissionParameters::AdeptNetMissionParameters::ResetParameters(void)
|
|||||||
{
|
{
|
||||||
m_runDedicated = 0;
|
m_runDedicated = 0;
|
||||||
m_closedGame = 0;
|
m_closedGame = 0;
|
||||||
m_playerLimit = 16;
|
m_playerLimit = 31; // max 31 (fits in 5-bit bitstream)
|
||||||
|
|
||||||
m_visibility = 0;
|
m_visibility = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "recscore.h"
|
#include "recscore.h"
|
||||||
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
|
||||||
|
|
||||||
#define MAX_TESLAS 16
|
#define MAX_TESLAS 31
|
||||||
#define MAX_CAMERAS 4
|
#define MAX_CAMERAS 4
|
||||||
|
|
||||||
#define PORT_Launcher 1000
|
#define PORT_Launcher 1000
|
||||||
@@ -263,7 +263,7 @@ CInviteCOOP g_InviteCOOP;
|
|||||||
// Mech4 only
|
// Mech4 only
|
||||||
int g_nMech4Comm = 0;
|
int g_nMech4Comm = 0;
|
||||||
BOOL g_bIsServer = FALSE;
|
BOOL g_bIsServer = FALSE;
|
||||||
SPlayerInfo g_aPlayerInfos[20];
|
SPlayerInfo g_aPlayerInfos[35]; // 31 pilots + 1 CS + 3 spare
|
||||||
int g_nPlayerInfos;
|
int g_nPlayerInfos;
|
||||||
int g_nServer;
|
int g_nServer;
|
||||||
int g_nTeslas;
|
int g_nTeslas;
|
||||||
|
|||||||
@@ -14,9 +14,6 @@
|
|||||||
// 10/10/03 MSL Added Cameraship to RESET_AFTER_LAUNCH
|
// 10/10/03 MSL Added Cameraship to RESET_AFTER_LAUNCH
|
||||||
// 10/17/05 MSL Added all remaining Mechs (55)
|
// 10/17/05 MSL Added all remaining Mechs (55)
|
||||||
// 06/19/18 AVB Update tab stops
|
// 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
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -75,8 +72,8 @@
|
|||||||
#define USE_O_MORE_PODS $$g_nMechPodNum$$ // 0 = use only 8
|
#define USE_O_MORE_PODS $$g_nMechPodNum$$ // 0 = use only 8
|
||||||
|
|
||||||
#if USE_O_MORE_PODS
|
#if USE_O_MORE_PODS
|
||||||
#define MAX_ROSTER_COUNT 16 // Was 8
|
#define MAX_ROSTER_COUNT 31 // max within 5-bit bitstream (0-31)
|
||||||
#define MAX_TEAMMATE_COUNT 8 // Was 4
|
#define MAX_TEAMMATE_COUNT 15 // Was 4, then 8
|
||||||
#else
|
#else
|
||||||
#define MAX_ROSTER_COUNT 8
|
#define MAX_ROSTER_COUNT 8
|
||||||
#define MAX_TEAMMATE_COUNT 4 // Was 4
|
#define MAX_TEAMMATE_COUNT 4 // Was 4
|
||||||
@@ -190,17 +187,13 @@
|
|||||||
#define MAIL_PREVIOUS_MISSION -2222
|
#define MAIL_PREVIOUS_MISSION -2222
|
||||||
#define MAIL_RECORD_MISSION -4444
|
#define MAIL_RECORD_MISSION -4444
|
||||||
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
|
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
|
||||||
#define MAIL_LOAD_AUTO_MISSION -6666 // [automaticmode] sync with Multiplayer/ConLobbyMission.script
|
|
||||||
|
|
||||||
// MSL 5.06
|
// MSL 5.06
|
||||||
// MSL ADD MECH
|
// MSL ADD MECH
|
||||||
// MechCorps Highlight: expanded from Fab4 to Super6 (added Archer and Warhammer)
|
#define ROOKIEMECH1 38 // Loki
|
||||||
#define ROOKIEMECH1 1 // Archer
|
#define ROOKIEMECH2 40 // Madcat
|
||||||
#define ROOKIEMECH2 38 // Loki
|
#define ROOKIEMECH3 56 // Thor
|
||||||
#define ROOKIEMECH3 40 // Madcat
|
#define ROOKIEMECH4 61 // Vulture
|
||||||
#define ROOKIEMECH4 56 // Thor
|
|
||||||
#define ROOKIEMECH5 61 // Vulture
|
|
||||||
#define ROOKIEMECH6 62 // Warhammer
|
|
||||||
|
|
||||||
main
|
main
|
||||||
{
|
{
|
||||||
@@ -253,7 +246,7 @@ main
|
|||||||
o_frame.type = 0
|
o_frame.type = 0
|
||||||
o_frame.location = 0, 0, 100
|
o_frame.location = 0, 0, 100
|
||||||
// MSL Version
|
// MSL Version
|
||||||
o_frame.screen_name = "BattleTech Console V5.1.0b1" // localize$(IDS_MP_LOBBY_GAME_LOBBY)
|
o_frame.screen_name = "BattleTech Console V5.0.7Df" // localize$(IDS_MP_LOBBY_GAME_LOBBY)
|
||||||
initialize(o_frame)
|
initialize(o_frame)
|
||||||
|
|
||||||
int team_camo[16] // team_camo[selected team number] is skin number(0..36)...
|
int team_camo[16] // team_camo[selected team number] is skin number(0..36)...
|
||||||
@@ -371,13 +364,12 @@ main
|
|||||||
cur_team_val = callback($$CTCL_GetTeamParams$$, cur_team_count[0])
|
cur_team_val = callback($$CTCL_GetTeamParams$$, cur_team_count[0])
|
||||||
|
|
||||||
int skin_id ///////VERY IMPORTANT FOR NEW SKIN INTERFACE
|
int skin_id ///////VERY IMPORTANT FOR NEW SKIN INTERFACE
|
||||||
int skin_ids[17]
|
int skin_ids[32]
|
||||||
int update_skin = false
|
int update_skin = false
|
||||||
// MSL 5.05
|
// MSL 5.05
|
||||||
int decal_id ///////VERY IMPORTANT FOR NEW DECAL INTERFACE
|
int decal_id ///////VERY IMPORTANT FOR NEW DECAL INTERFACE
|
||||||
int decal_ids[20] //JPP was 18
|
int decal_ids[20] //JPP was 18
|
||||||
int update_decal = false
|
int update_decal = false
|
||||||
int m_bApplyingAutoLoad = false // suppress skin_box auto decal sync during Load File apply
|
|
||||||
int ROSTER_top_of_list = 0
|
int ROSTER_top_of_list = 0
|
||||||
int block_launch_or_ready = FALSE
|
int block_launch_or_ready = FALSE
|
||||||
int MAX_ITEMS = 60
|
int MAX_ITEMS = 60
|
||||||
@@ -530,7 +522,7 @@ main
|
|||||||
|
|
||||||
|
|
||||||
///---------holds faction name for scroll list
|
///---------holds faction name for scroll list
|
||||||
string faction_name[17]
|
string faction_name[32]
|
||||||
|
|
||||||
callback($$Shell_CallbackHandler$$, ShellInitMPScreen)
|
callback($$Shell_CallbackHandler$$, ShellInitMPScreen)
|
||||||
|
|
||||||
@@ -653,12 +645,12 @@ main
|
|||||||
team_legal_to_join[8] = TRUE
|
team_legal_to_join[8] = TRUE
|
||||||
int update_team = 0
|
int update_team = 0
|
||||||
|
|
||||||
int max_players = 16
|
int max_players = 31 // max within 5-bit bitstream
|
||||||
|
|
||||||
int player_num = 0
|
int player_num = 0
|
||||||
// int num_of_teams = 8
|
// int num_of_teams = 8
|
||||||
int mech_id
|
int mech_id
|
||||||
int name_color[17]
|
int name_color[32]
|
||||||
int lupe
|
int lupe
|
||||||
|
|
||||||
for lupe = 0; lupe < nRosterCount;lupe++
|
for lupe = 0; lupe < nRosterCount;lupe++
|
||||||
@@ -683,33 +675,33 @@ main
|
|||||||
player_num = -1
|
player_num = -1
|
||||||
|
|
||||||
// ComboBox for the Player Type
|
// ComboBox for the Player Type
|
||||||
object o_BotList[17]
|
object o_BotList[32]
|
||||||
object o_BotList_bu[17]
|
object o_BotList_bu[32]
|
||||||
int m_naLastSelected[17]
|
int m_naLastSelected[32]
|
||||||
for x = 0; x < nRosterCount; x++
|
for x = 0; x < nRosterCount; x++
|
||||||
{
|
{
|
||||||
m_naLastSelected[x] = 0
|
m_naLastSelected[x] = 0
|
||||||
}
|
}
|
||||||
object o_Editbox[17]
|
object o_Editbox[32]
|
||||||
object o_print_text[17]
|
object o_print_text[32]
|
||||||
object o_mech_variant[17]
|
object o_mech_variant[32]
|
||||||
object o_mech_variant_bu[17]
|
object o_mech_variant_bu[32]
|
||||||
#if USE_O_MECH_VARIANT2
|
#if USE_O_MECH_VARIANT2
|
||||||
object o_mech_variant2[17]
|
object o_mech_variant2[32]
|
||||||
object o_mech_variant2_bu[17]
|
object o_mech_variant2_bu[32]
|
||||||
#endif // USE_O_MECH_VARIANT2
|
#endif // USE_O_MECH_VARIANT2
|
||||||
object o_mech_rand[17]
|
object o_mech_rand[32]
|
||||||
object o_skins[17]
|
object o_skins[32]
|
||||||
object o_skins_bu[17]
|
object o_skins_bu[32]
|
||||||
// MSL 5.05
|
// MSL 5.05
|
||||||
object o_decal[18]
|
object o_decal[32]
|
||||||
object o_decal_bu[18]
|
object o_decal_bu[32]
|
||||||
object o_team[18]
|
object o_team[32]
|
||||||
object o_team_bu[18]
|
object o_team_bu[32]
|
||||||
object o_decal_box[18]
|
object o_decal_box[32]
|
||||||
object o_skin_box[18]
|
object o_skin_box[32]
|
||||||
object o_status[18]
|
object o_status[32]
|
||||||
int roster_posy[18]
|
int roster_posy[32]
|
||||||
int coolbaby
|
int coolbaby
|
||||||
|
|
||||||
int stock_size = 0
|
int stock_size = 0
|
||||||
@@ -995,9 +987,26 @@ main
|
|||||||
// o_decal[x].list_item[15] = "SCORPION"
|
// o_decal[x].list_item[15] = "SCORPION"
|
||||||
// o_decal[x].list_item[16] = "331st"
|
// o_decal[x].list_item[16] = "331st"
|
||||||
|
|
||||||
for coolbaby = 0; coolbaby < MAX_DECAL_COUNT; coolbaby++
|
o_decal[x].list_item[0] = "1"
|
||||||
o_decal[x].list_item[coolbaby] = conv$(ffa_decal[coolbaby])
|
o_decal[x].list_item[1] = "2"
|
||||||
o_decal[x].list_item[MAX_DECAL_COUNT] = "00" // bot/default entry
|
o_decal[x].list_item[2] = "3"
|
||||||
|
o_decal[x].list_item[3] = "4"
|
||||||
|
o_decal[x].list_item[4] = "5"
|
||||||
|
o_decal[x].list_item[5] = "6"
|
||||||
|
o_decal[x].list_item[6] = "7"
|
||||||
|
o_decal[x].list_item[7] = "8"
|
||||||
|
o_decal[x].list_item[8] = "9"
|
||||||
|
o_decal[x].list_item[9] = "10"
|
||||||
|
o_decal[x].list_item[10] = "11"
|
||||||
|
o_decal[x].list_item[11] = "12"
|
||||||
|
o_decal[x].list_item[12] = "13"
|
||||||
|
o_decal[x].list_item[13] = "14"
|
||||||
|
o_decal[x].list_item[14] = "15"
|
||||||
|
o_decal[x].list_item[15] = "16"
|
||||||
|
o_decal[x].list_item[16] = "17"
|
||||||
|
|
||||||
|
for coolbaby = 17;coolbaby < o_decal[x].list_size;coolbaby++
|
||||||
|
o_decal[x].list_item[coolbaby] = conv$(coolbaby+1)
|
||||||
|
|
||||||
o_decal[x].nselected = 3
|
o_decal[x].nselected = 3
|
||||||
decal_ids[x] = 1
|
decal_ids[x] = 1
|
||||||
@@ -1063,7 +1072,7 @@ main
|
|||||||
o_skins[x].list_item[14] = "15"
|
o_skins[x].list_item[14] = "15"
|
||||||
o_skins[x].list_item[15] = "16"
|
o_skins[x].list_item[15] = "16"
|
||||||
|
|
||||||
for coolbaby = 16;coolbaby < o_skins[x].list_size+1;coolbaby++
|
for coolbaby = 16;coolbaby < o_skins[x].list_size;coolbaby++
|
||||||
o_skins[x].list_item[coolbaby] = conv$(coolbaby+1)
|
o_skins[x].list_item[coolbaby] = conv$(coolbaby+1)
|
||||||
|
|
||||||
o_skins[x].nselected = 3
|
o_skins[x].nselected = 3
|
||||||
@@ -1214,18 +1223,6 @@ main
|
|||||||
o_all_random_mech.state = 0 // initially enabled
|
o_all_random_mech.state = 0 // initially enabled
|
||||||
initialize(o_all_random_mech)
|
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
|
// MSL Added default settings button
|
||||||
object o_default = s_multistatepane
|
object o_default = s_multistatepane
|
||||||
o_default.total_states = 3
|
o_default.total_states = 3
|
||||||
@@ -1661,124 +1658,6 @@ main
|
|||||||
mail(MAIL_RESET_AFTER_LAUNCH, this) // reset
|
mail(MAIL_RESET_AFTER_LAUNCH, this) // reset
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// [automaticmode] Load File button handler
|
|
||||||
if (sender == o_load_file)
|
|
||||||
{
|
|
||||||
play press, 1
|
|
||||||
if callback($$CTCL_LoadAutoFile$$)
|
|
||||||
{
|
|
||||||
m_bApplyingAutoLoad = true
|
|
||||||
// Apply game options using dedicated Auto globals.
|
|
||||||
// Rookie Mission globals are NOT touched so the Default button keeps working.
|
|
||||||
int m
|
|
||||||
if exists(@ConLobbyMission@)
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
int nAutoDecal
|
|
||||||
int nAutoDecalIndex
|
|
||||||
o_team[k].nselected = callback($$CTCL_GetAutoSlotInt$$, k, 1)
|
|
||||||
o_skins[k].nselected = callback($$CTCL_GetAutoSlotInt$$, k, 2)
|
|
||||||
nAutoDecal = callback($$CTCL_GetAutoSlotInt$$, k, 3)
|
|
||||||
nAutoDecalIndex = 20 // fallback to safe/default decal slot
|
|
||||||
for (j = 0; j <= MAX_DECAL_COUNT; j++)
|
|
||||||
{
|
|
||||||
if (ffa_decal[j] == nAutoDecal)
|
|
||||||
{
|
|
||||||
nAutoDecalIndex = j
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
o_decal[k].nselected = nAutoDecalIndex
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_bApplyingAutoLoad = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (sender == o_reprint)
|
if (sender == o_reprint)
|
||||||
{
|
{
|
||||||
play press,1
|
play press,1
|
||||||
@@ -1882,39 +1761,38 @@ main
|
|||||||
for (k = 0; k < nRosterCount; k++)
|
for (k = 0; k < nRosterCount; k++)
|
||||||
{
|
{
|
||||||
// MSL
|
// MSL
|
||||||
// MechCorps Highlight: Super6 slot assignments (16 slots, 6-mech cycle; Vulture+Warhammer appear twice)
|
|
||||||
if k == 0
|
if k == 0
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH1 // Archer
|
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
|
||||||
if k == 1
|
if k == 1
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH2 // Loki
|
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
|
||||||
if k == 2
|
if k == 2
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH3 // Madcat
|
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
|
||||||
if k == 3
|
if k == 3
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH4 // Thor
|
o_mech_variant[k].nselected = ROOKIEMECH4 // vulture
|
||||||
if k == 4
|
if k == 4
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH5 // Vulture
|
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
|
||||||
if k == 5
|
if k == 5
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH6 // Warhammer
|
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
|
||||||
if k == 6
|
if k == 6
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH1 // Archer
|
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
|
||||||
if k == 7
|
if k == 7
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH2 // Loki
|
o_mech_variant[k].nselected = ROOKIEMECH4 // Vulture
|
||||||
if k == 8
|
if k == 8
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH3 // Madcat
|
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
|
||||||
if k == 9
|
if k == 9
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH4 // Thor
|
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
|
||||||
if k == 10
|
if k == 10
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH5 // Vulture
|
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
|
||||||
if k == 11
|
if k == 11
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH6 // Warhammer
|
o_mech_variant[k].nselected = ROOKIEMECH4 // vulture
|
||||||
if k == 12
|
if k == 12
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH1 // Archer
|
o_mech_variant[k].nselected = ROOKIEMECH1 // Loki
|
||||||
if k == 13
|
if k == 13
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH2 // Loki
|
o_mech_variant[k].nselected = ROOKIEMECH2 // Madcat
|
||||||
if k == 14
|
if k == 14
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH3 // Madcat
|
o_mech_variant[k].nselected = ROOKIEMECH3 // Thor
|
||||||
if k == 15
|
if k == 15
|
||||||
o_mech_variant[k].nselected = ROOKIEMECH4 // Thor
|
o_mech_variant[k].nselected = ROOKIEMECH4 // Vulture
|
||||||
#if USE_O_MECH_VARIANT2
|
#if USE_O_MECH_VARIANT2
|
||||||
if (USE_ALLOWED_MECHS)
|
if (USE_ALLOWED_MECHS)
|
||||||
last_count = stock_array[allowed_mechs[o_mech_variant[k].nselected]]
|
last_count = stock_array[allowed_mechs[o_mech_variant[k].nselected]]
|
||||||
@@ -2187,24 +2065,6 @@ main
|
|||||||
initialize(o_mech_variant2[k])
|
initialize(o_mech_variant2[k])
|
||||||
o_mech_variant2[k].nselected = 0
|
o_mech_variant2[k].nselected = 0
|
||||||
#endif // USE_O_MECH_VARIANT2
|
#endif // USE_O_MECH_VARIANT2
|
||||||
break
|
|
||||||
}
|
|
||||||
if (sender == o_skins[k])
|
|
||||||
{
|
|
||||||
if !cur_team_val
|
|
||||||
{
|
|
||||||
o_decal[k].nselected = o_skins[k].nselected
|
|
||||||
initialize(o_decal[k])
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (sender == o_team[k])
|
|
||||||
{
|
|
||||||
if cur_team_val
|
|
||||||
{
|
|
||||||
o_decal[k].nselected = team_camo[o_team[k].nselected]
|
|
||||||
initialize(o_decal[k])
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (sender == o_BotList[k])
|
if (sender == o_BotList[k])
|
||||||
@@ -2854,8 +2714,7 @@ main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// MSL 5.06
|
// MSL 5.06
|
||||||
// RT 07/19/26: raised cap from 16 to 17 to allow 16 pilots + 1 cameraship
|
if nLaunchState == 0 && (nTempPlayerCount > 31)
|
||||||
if nLaunchState == 0 && (nTempPlayerCount > 17)
|
|
||||||
{
|
{
|
||||||
o_error_string.text = "Too many player/bots"
|
o_error_string.text = "Too many player/bots"
|
||||||
nLaunchState = -7
|
nLaunchState = -7
|
||||||
@@ -3123,6 +2982,14 @@ skin_box
|
|||||||
{
|
{
|
||||||
nLastState = state
|
nLastState = state
|
||||||
p_pane = "Content\\ShellScripts\\Graphics\\Multiplayer\\LobbySkins\\skin" letters[state] "4.tga"
|
p_pane = "Content\\ShellScripts\\Graphics\\Multiplayer\\LobbySkins\\skin" letters[state] "4.tga"
|
||||||
|
if cur_team_val
|
||||||
|
{
|
||||||
|
o_decal[id].nselected = team_camo[o_team[id].nselected]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o_decal[id].nselected = o_skins[id].nselected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
render p_pane, SKIN_BOX_X-35,location.Y
|
render p_pane, SKIN_BOX_X-35,location.Y
|
||||||
|
|
||||||
@@ -3221,8 +3088,6 @@ decal_box
|
|||||||
}
|
}
|
||||||
if (dstate != nLastdState)
|
if (dstate != nLastdState)
|
||||||
{
|
{
|
||||||
if (dstate < 0) || (dstate > 20)
|
|
||||||
dstate = 20
|
|
||||||
nLastdState = dstate
|
nLastdState = dstate
|
||||||
d_pane = "Content\\ShellScripts\\Graphics\\Multiplayer\\LobbyDecals\\decal_" decals[dstate] ".tga"
|
d_pane = "Content\\ShellScripts\\Graphics\\Multiplayer\\LobbyDecals\\decal_" decals[dstate] ".tga"
|
||||||
}
|
}
|
||||||
@@ -4135,24 +4000,17 @@ mech_randomizer
|
|||||||
else // right button
|
else // right button
|
||||||
{
|
{
|
||||||
int pick_mech = 0
|
int pick_mech = 0
|
||||||
//pick_mech = random(0, 3) // Fab4
|
pick_mech = random(0, 3)
|
||||||
// MechCorps Highlight: Super6 - expand right-click random range to 6
|
|
||||||
pick_mech = random(0, 5)
|
|
||||||
|
|
||||||
// MSL
|
// MSL
|
||||||
// MechCorps Highlight: Super6 - added Archer (0) and Warhammer (5) to right-click mech pool
|
|
||||||
if pick_mech = 0
|
if pick_mech = 0
|
||||||
last_mech = ROOKIEMECH1 // Archer
|
last_mech = ROOKIEMECH1 // Loki
|
||||||
if pick_mech = 1
|
if pick_mech = 1
|
||||||
last_mech = ROOKIEMECH2 // Loki
|
last_mech = ROOKIEMECH2 // Madcat
|
||||||
if pick_mech = 2
|
if pick_mech = 2
|
||||||
last_mech = ROOKIEMECH3 // Madcat
|
last_mech = ROOKIEMECH3 // Thor
|
||||||
if pick_mech = 3
|
if pick_mech = 3
|
||||||
last_mech = ROOKIEMECH4 // Thor
|
last_mech = ROOKIEMECH4 // vulture
|
||||||
if pick_mech = 4
|
|
||||||
last_mech = ROOKIEMECH5 // Vulture
|
|
||||||
if pick_mech = 5
|
|
||||||
last_mech = ROOKIEMECH6 // Warhammer
|
|
||||||
}
|
}
|
||||||
if (USE_ALLOWED_MECHS)
|
if (USE_ALLOWED_MECHS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,8 +52,6 @@
|
|||||||
#define MAIL_PREVIOUS_MISSION -2222
|
#define MAIL_PREVIOUS_MISSION -2222
|
||||||
#define MAIL_RECORD_MISSION -4444
|
#define MAIL_RECORD_MISSION -4444
|
||||||
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
|
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
|
||||||
#define MAIL_LOAD_AUTO_MISSION -6666 // [automaticmode] sync with ConLobby.script
|
|
||||||
#define MAIL_LOAD_AUTO_MISSION_DONE -8888 // [automaticmode] internal finalize (self-mail)
|
|
||||||
|
|
||||||
#define ROOKIE_MISSION "ScarabStronghold - Attrition"
|
#define ROOKIE_MISSION "ScarabStronghold - Attrition"
|
||||||
|
|
||||||
@@ -570,15 +568,7 @@ main
|
|||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
nTempParam = HEAT_PARAMETER
|
nTempParam = HEAT_PARAMETER
|
||||||
if callback($$GetLocalNetworkMissionParamater$$,nTempParam)
|
if callback($$GetLocalNetworkMissionParamater$$,nTempParam)
|
||||||
{
|
|
||||||
o_game_options[7].state = 2
|
o_game_options[7].state = 2
|
||||||
nTempParam1 = WEAPON_JAM_PARAMETER
|
|
||||||
if callback($$GetLocalNetworkMissionParamater$$,nTempParam1)
|
|
||||||
o_game_options[14].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[14].state = 0
|
|
||||||
initialize(o_game_options[14])
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
o_game_options[7].state = 0
|
o_game_options[7].state = 0
|
||||||
@@ -694,170 +684,6 @@ main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// [automaticmode] Handle Load File in a dedicated early path for BOTH sender contexts.
|
|
||||||
// This prevents MAIL_LOAD_AUTO_MISSION self-mails from falling through to
|
|
||||||
// generic game/map handlers that re-apply Rookie defaults.
|
|
||||||
if (getmessage() == MAIL_LOAD_AUTO_MISSION)
|
|
||||||
{
|
|
||||||
// Phase 1: ensure game type is selected and rebuild map list for that type.
|
|
||||||
if (o_game_options[0].nselected != $$g_nAutoGameType$$)
|
|
||||||
{
|
|
||||||
o_game_options[0].nselected = $$g_nAutoGameType$$
|
|
||||||
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[o_game_options[0].nselected])
|
|
||||||
if (callback($$InitNetworkScenarios$$))
|
|
||||||
{
|
|
||||||
callback($$GetGameTypeString$$, selection_to_standard[o_game_options[0].nselected])
|
|
||||||
callback($$GetNetworkScenarios$$, o_game_options[1].list_item[])
|
|
||||||
o_game_options[1].list_size = $$networkScenarioCount$$
|
|
||||||
initialize(o_game_options[1])
|
|
||||||
if ($$networkScenarioCount$$ == 0)
|
|
||||||
{
|
|
||||||
deactivate(o_game_options[1])
|
|
||||||
deactivate(@conlobby@o_launch_button)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
activate(o_game_options[1])
|
|
||||||
activate(@conlobby@o_launch_button)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last_selected_game = o_game_options[0].nselected
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 2: resolve mission by name in the current (game-type-specific) map list.
|
|
||||||
int auto_map_index
|
|
||||||
auto_map_index = 0
|
|
||||||
int found_auto_map
|
|
||||||
found_auto_map = 0
|
|
||||||
int mk
|
|
||||||
for (mk = 0; mk < o_game_options[1].list_size; mk++)
|
|
||||||
{
|
|
||||||
if equal$(o_game_options[1].list_item[mk], $$g_szAutoMission$$)
|
|
||||||
{
|
|
||||||
auto_map_index = mk
|
|
||||||
found_auto_map = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found_auto_map
|
|
||||||
auto_map_index = 0
|
|
||||||
if (o_game_options[1].nselected != auto_map_index)
|
|
||||||
o_game_options[1].nselected = auto_map_index
|
|
||||||
|
|
||||||
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[$$g_nAutoGameType$$])
|
|
||||||
callback($$SetNetworkMissionParamater$$, map_type, o_game_options[1].nselected)
|
|
||||||
callback($$SelectNetworkScenario$$, o_game_options[1].nselected)
|
|
||||||
last_selected_map = o_game_options[1].nselected
|
|
||||||
|
|
||||||
// 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$$
|
|
||||||
if nTempValue
|
|
||||||
o_game_options[7].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[7].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// friendly fire
|
|
||||||
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
|
|
||||||
nTempValue = $$g_nAutoFriendlyFire$$
|
|
||||||
if nTempValue
|
|
||||||
{
|
|
||||||
o_game_options[8].state = 2
|
|
||||||
nTempValue = 100
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o_game_options[8].state = 0
|
|
||||||
nTempValue = 0
|
|
||||||
}
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// splash
|
|
||||||
nTempParam = SPLASH_ON_PARAMETER
|
|
||||||
nTempValue = $$g_nAutoSplash$$
|
|
||||||
if nTempValue
|
|
||||||
o_game_options[9].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[9].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// unlimited ammo
|
|
||||||
nTempParam = UNLIMITED_AMMO_PARAMETER
|
|
||||||
nTempValue = $$g_nAutoUnlimitedAmmo$$
|
|
||||||
if (nTempValue == 0)
|
|
||||||
o_game_options[10].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[10].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// no return
|
|
||||||
nTempParam = num_of_lives
|
|
||||||
nTempValue = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
nTempParam = respawn_onoff
|
|
||||||
nTempValue = $$g_nAutoNoReturn$$
|
|
||||||
if nTempValue
|
|
||||||
o_game_options[11].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[11].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// weapon jam
|
|
||||||
nTempParam = WEAPON_JAM_PARAMETER
|
|
||||||
nTempValue = $$g_nAutoWeaponJam$$
|
|
||||||
if nTempValue
|
|
||||||
o_game_options[14].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[14].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// advance mode
|
|
||||||
nTempParam = ADVANCE_MODE_PARAMETER
|
|
||||||
nTempValue = $$g_nAutoAdvanceMode$$
|
|
||||||
if nTempValue
|
|
||||||
o_game_options[15].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[15].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
// armor mode
|
|
||||||
nTempParam = ARMOR_MODE_PARAMETER
|
|
||||||
nTempValue = $$g_nAutoArmorMode$$
|
|
||||||
if nTempValue
|
|
||||||
o_game_options[16].state = 2
|
|
||||||
else
|
|
||||||
o_game_options[16].state = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
|
|
||||||
mail(MAIL_LOAD_AUTO_MISSION_DONE, this)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (sender == @conlobby@)
|
if (sender == @conlobby@)
|
||||||
{
|
{
|
||||||
if (getmessage() == -7777)
|
if (getmessage() == -7777)
|
||||||
@@ -967,15 +793,15 @@ main
|
|||||||
if (getmessage() == MAIL_SET_ROOKIE_MISSION) // set rookie, see conlobby.script
|
if (getmessage() == MAIL_SET_ROOKIE_MISSION) // set rookie, see conlobby.script
|
||||||
{
|
{
|
||||||
// caution: actually 0 is not correct but who care? ^^
|
// caution: actually 0 is not correct but who care? ^^
|
||||||
if (o_game_options[0].nselected != $$g_nRookieGameType$$)
|
if (o_game_options[0].nselected != 2)
|
||||||
{
|
{
|
||||||
o_game_options[0].nselected = $$g_nRookieGameType$$
|
o_game_options[0].nselected = 2
|
||||||
mail(MAIL_SET_ROOKIE_MISSION, this)
|
mail(MAIL_SET_ROOKIE_MISSION, this)
|
||||||
}
|
}
|
||||||
// find mission:
|
// find mission:
|
||||||
for k = 0; k < o_game_options[1].list_size; k++
|
for k = 0; k < o_game_options[1].list_size; k++
|
||||||
{
|
{
|
||||||
if equal$(o_game_options[1].list_item[k], $$g_szRookieMission$$)
|
if equal$(o_game_options[1].list_item[k], ROOKIE_MISSION)
|
||||||
{
|
{
|
||||||
// found mission
|
// found mission
|
||||||
break
|
break
|
||||||
@@ -1029,26 +855,24 @@ main
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// visibility
|
// visibility
|
||||||
o_game_options[2].nselected = $$g_nRookieVisibility$$ // [RookieMission] ini
|
o_game_options[2].nselected = 0 // Default
|
||||||
callback($$SetNetworkMissionParamater$$, visibility, o_game_options[2].nselected)
|
callback($$SetNetworkMissionParamater$$, visibility, o_game_options[2].nselected)
|
||||||
|
|
||||||
// weather
|
// weather
|
||||||
o_game_options[3].nselected = $$g_nRookieWeather$$ // [RookieMission] ini
|
o_game_options[3].nselected = 0 // off
|
||||||
callback($$SetNetworkMissionParamater$$, weather, o_game_options[3].nselected)
|
callback($$SetNetworkMissionParamater$$, weather, o_game_options[3].nselected)
|
||||||
|
|
||||||
// time of day
|
// time of day
|
||||||
o_game_options[4].nselected = $$g_nRookieTimeOfDay$$ // [RookieMission] ini
|
o_game_options[4].nselected = 0
|
||||||
callback($$SetNetworkMissionParamater$$, night_parameter, o_game_options[4].nselected)
|
callback($$SetNetworkMissionParamater$$, night_parameter, o_game_options[4].nselected)
|
||||||
callback($$Shell_CallbackHandler$$, ShellSetTime, o_game_options[4].nselected)
|
callback($$Shell_CallbackHandler$$, ShellSetTime, o_game_options[4].nselected)
|
||||||
}
|
}
|
||||||
// time limit - use ini override if set, else TIME_VALUE_DEFAULT
|
|
||||||
if $$g_nRookieTimeLimit$$ >= 0
|
// time limit
|
||||||
temp_num = $$g_nRookieTimeLimit$$ // [RookieMission] ini
|
|
||||||
else
|
|
||||||
temp_num = TIME_VALUE_DEFAULT
|
|
||||||
for k = 0; k < o_game_options[5].list_size; k++
|
for k = 0; k < o_game_options[5].list_size; k++
|
||||||
{
|
{
|
||||||
if (makeint(o_game_options[5].list_item[k]) == temp_num)
|
temp_num = makeint(o_game_options[5].list_item[k])
|
||||||
|
if (temp_num == TIME_VALUE_DEFAULT)
|
||||||
{
|
{
|
||||||
o_game_options[5].nselected = k
|
o_game_options[5].nselected = k
|
||||||
break
|
break
|
||||||
@@ -1111,31 +935,31 @@ main
|
|||||||
{
|
{
|
||||||
// radar
|
// radar
|
||||||
nTempParam = radar
|
nTempParam = radar
|
||||||
nTempValue = $$g_nRookieRadar$$ // [RookieMission] ini (0=Novice)
|
nTempValue = 0 // Novice
|
||||||
o_game_options[6].nselected = nTempValue
|
o_game_options[6].nselected = nTempValue
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
||||||
|
|
||||||
// heat management
|
// heat management
|
||||||
nTempParam = HEAT_PARAMETER
|
nTempParam = HEAT_PARAMETER
|
||||||
nTempValue = $$g_nRookieHeat$$ // [RookieMission] ini
|
nTempValue = 0
|
||||||
o_game_options[7].state = 0
|
o_game_options[7].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
// friendly fire
|
// friendly fire
|
||||||
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
|
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
|
||||||
nTempValue = $$g_nRookieFriendlyFire$$ // [RookieMission] ini
|
nTempValue = 0
|
||||||
o_game_options[8].state = 0
|
o_game_options[8].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
// splash
|
// splash
|
||||||
nTempParam = SPLASH_ON_PARAMETER
|
nTempParam = SPLASH_ON_PARAMETER
|
||||||
nTempValue = $$g_nRookieSplash$$ // [RookieMission] ini
|
nTempValue = 0
|
||||||
o_game_options[9].state = 0
|
o_game_options[9].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
// unlimited ammo
|
// unlimited ammo
|
||||||
nTempParam = UNLIMITED_AMMO_PARAMETER
|
nTempParam = UNLIMITED_AMMO_PARAMETER
|
||||||
nTempValue = $$g_nRookieUnlimitedAmmo$$ // [RookieMission] ini
|
nTempValue = 1 // be cautious
|
||||||
o_game_options[10].state = 0
|
o_game_options[10].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
@@ -1151,7 +975,7 @@ main
|
|||||||
|
|
||||||
// weapon jam
|
// weapon jam
|
||||||
nTempParam = WEAPON_JAM_PARAMETER
|
nTempParam = WEAPON_JAM_PARAMETER
|
||||||
nTempValue = $$g_nRookieWeaponJam$$ // [RookieMission] ini
|
nTempValue = 0
|
||||||
o_game_options[14].state = 0
|
o_game_options[14].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
@@ -1163,13 +987,13 @@ main
|
|||||||
|
|
||||||
// advance mode
|
// advance mode
|
||||||
nTempParam = ADVANCE_MODE_PARAMETER
|
nTempParam = ADVANCE_MODE_PARAMETER
|
||||||
nTempValue = $$g_nRookieAdvanceMode$$ // [RookieMission] ini
|
nTempValue = 0
|
||||||
o_game_options[15].state = 0
|
o_game_options[15].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
// armor mode
|
// armor mode
|
||||||
nTempParam = ARMOR_MODE_PARAMETER
|
nTempParam = ARMOR_MODE_PARAMETER
|
||||||
nTempValue = $$g_nRookieArmorMode$$ // [RookieMission] ini
|
nTempValue = 0
|
||||||
o_game_options[16].state = 0
|
o_game_options[16].state = 0
|
||||||
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
callback($$SetNetworkMissionParamater$$,nTempParam, nTempValue)
|
||||||
|
|
||||||
@@ -1180,42 +1004,6 @@ main
|
|||||||
initialize(this)
|
initialize(this)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// [automaticmode] finalize in sender==this context (initialize/parent safe here)
|
|
||||||
if (getmessage() == MAIL_LOAD_AUTO_MISSION_DONE)
|
|
||||||
{
|
|
||||||
initialize(this)
|
|
||||||
nTempParam = UNLIMITED_AMMO_PARAMETER
|
|
||||||
if ($$g_nAutoUnlimitedAmmo$$ == 0)
|
|
||||||
{
|
|
||||||
o_game_options[10].state = 2
|
|
||||||
nTempValue = 0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o_game_options[10].state = 0
|
|
||||||
nTempValue = 1
|
|
||||||
}
|
|
||||||
initialize(o_game_options[10])
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
nTempParam = num_of_lives
|
|
||||||
nTempValue = 0
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
nTempParam = respawn_onoff
|
|
||||||
if ($$g_nAutoNoReturn$$)
|
|
||||||
{
|
|
||||||
o_game_options[11].state = 2
|
|
||||||
nTempValue = 1
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
o_game_options[11].state = 0
|
|
||||||
nTempValue = 0
|
|
||||||
}
|
|
||||||
initialize(o_game_options[11])
|
|
||||||
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
|
|
||||||
mail(-9998, @conlobby@)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (getmessage() == MAIL_PREVIOUS_MISSION_PARAMS)
|
if (getmessage() == MAIL_PREVIOUS_MISSION_PARAMS)
|
||||||
{
|
{
|
||||||
// visibility
|
// visibility
|
||||||
@@ -1393,7 +1181,7 @@ main
|
|||||||
mail(MAIL_PREVIOUS_MISSION_PARAMS, this)
|
mail(MAIL_PREVIOUS_MISSION_PARAMS, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sender == o_game_options[0]) || ((sender == this) && ((getmessage() == MAIL_SET_ROOKIE_MISSION) || (getmessage() == MAIL_LOAD_AUTO_MISSION)))) && (o_game_options[0].nselected != last_selected_game)
|
if ((sender == o_game_options[0]) || ((sender == this) && (getmessage() == MAIL_SET_ROOKIE_MISSION))) && (o_game_options[0].nselected != last_selected_game)
|
||||||
{
|
{
|
||||||
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[o_game_options[0].nselected])
|
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[o_game_options[0].nselected])
|
||||||
|
|
||||||
@@ -1446,7 +1234,7 @@ main
|
|||||||
mail(MAIL_SET_ROOKIE_MISSION_PARAMS, this)
|
mail(MAIL_SET_ROOKIE_MISSION_PARAMS, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sender == o_game_options[1]) || ((sender == this) && ((getmessage() == MAIL_SET_ROOKIE_MISSION) || (getmessage() == MAIL_LOAD_AUTO_MISSION)))) && (o_game_options[1].nselected != last_selected_map)
|
if ((sender == o_game_options[1]) || ((sender == this) && (getmessage() == MAIL_SET_ROOKIE_MISSION))) && (o_game_options[1].nselected != last_selected_map)
|
||||||
{
|
{
|
||||||
callback($$SetNetworkMissionParamater$$, map_type, o_game_options[1].nselected)
|
callback($$SetNetworkMissionParamater$$, map_type, o_game_options[1].nselected)
|
||||||
callback($$SelectNetworkScenario$$, o_game_options[1].nselected)
|
callback($$SelectNetworkScenario$$, o_game_options[1].nselected)
|
||||||
|
|||||||
BIN
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
Reference in New Issue
Block a user