Fix Load File: separate Auto globals; fix team/FFA double-press

Issue 1 — double press to fix team/FFA display:
  cur_team_val is updated by ConLobbyMission's -9998 signal which was async.
  Fix: call SetNetworkMissionParamater(team_allowed, ...) directly in ConLobby
  before the slot loop, then re-read cur_team_val via CTCL_GetTeamParams.
  This is synchronous so the slot display is correct on the first click.
  New file keys: TeamAllowed=0/1, TeamCount=2 (default 2 teams).

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

Files changed:
  MW4Shell.cpp: 16 new globals, StartUp/ShutDown registration, CTCL_LoadAutoFile
  ConLobby.script: MAIL_LOAD_AUTO_MISSION define, updated handler
  ConLobbyMission.script: MAIL_LOAD_AUTO_MISSION define + handler
  autoconfig-file-spec.html: document TeamAllowed + TeamCount fields
Rebuild required: MW4.exe (Release + Profile).
This commit is contained in:
2026-07-24 10:35:11 -05:00
parent 3f2d79a038
commit cd2fe73c88
4 changed files with 189 additions and 19 deletions
+11
View File
@@ -266,6 +266,17 @@ Match is exact string equality, case-sensitive. Trailing/leading spaces are stri
<tr><td><code>1</code></td><td>On</td></tr>
</table>
<h3>4.15 &nbsp;<code>TeamAllowed</code> &mdash; integer <span class="optional">(required for correct team/FFA slot display on first click)</span></h3>
<p>Must match the <code>GameType</code> selected. Controls whether <code>Team</code> or <code>Skin</code> is applied to slots, and whether team dropdowns are shown.</p>
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr><td><code>0</code></td><td>FFA mode ? slot <code>Skin</code> field is used <span class="default-tag">DEFAULT</span></td></tr>
<tr><td><code>1</code></td><td>Team game ? slot <code>Team</code> field is used</td></tr>
</table>
<h3>4.16 &nbsp;<code>TeamCount</code> &mdash; integer <span class="optional">(optional, only when TeamAllowed=1)</span></h3>
<p>Number of teams. Valid range: 2&ndash;8. Default: <code>2</code>.</p>
<hr>
<h2>5. Sections: <code>[slot0]</code> through <code>[slot15]</code></h2>
+78 -17
View File
@@ -191,6 +191,24 @@ static SAutoFileSlot g_aAutoSlots[16];
static int g_nAutoSlotsCount = 0;
static int g_bAutomaticMode = 0;
static char g_szAutomaticFile[MAX_PATH] = "";
// [automaticmode] game option globals ? separate from Rookie Mission defaults to preserve Default button behavior
static char g_szAutoMission[256] = "";
static char* g_pszAutoMission = g_szAutoMission;
static int g_nAutoGameType = 2; // Attrition
static int g_nAutoVisibility = 0;
static int g_nAutoWeather = 0;
static int g_nAutoTimeOfDay = 0;
static int g_nAutoTimeLimit = -1; // -1 = server default
static int g_nAutoRadar = 0;
static int g_nAutoHeat = 0;
static int g_nAutoFriendlyFire = 0;
static int g_nAutoSplash = 0;
static int g_nAutoUnlimitedAmmo = 1;
static int g_nAutoWeaponJam = 0;
static int g_nAutoAdvanceMode = 0;
static int g_nAutoArmorMode = 0;
static int g_nAutoTeamAllowed = 0; // 0=FFA 1=team game; set via TeamAllowed= in [mission]
static int g_nAutoTeamCount = 2; // number of teams when TeamAllowed=1
// MSL 5.06
int g_nMechVariant = 0;
int g_nMechLabOp = 0;
@@ -791,6 +809,23 @@ void MW4Shell::StartUp()
strncpy(g_szAutomaticFile, sz, sizeof(g_szAutomaticFile)-1);
}
}
// [automaticmode] game option variables (separate from Rookie globals)
gosScript_RegisterVariable("g_szAutoMission", &g_pszAutoMission, GOSVAR_STRING, 0, NULL);
gosScript_RegisterVariable("g_nAutoGameType", &g_nAutoGameType, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoVisibility", &g_nAutoVisibility, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoWeather", &g_nAutoWeather, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTimeOfDay", &g_nAutoTimeOfDay, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTimeLimit", &g_nAutoTimeLimit, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoRadar", &g_nAutoRadar, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoHeat", &g_nAutoHeat, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoFriendlyFire", &g_nAutoFriendlyFire, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoSplash", &g_nAutoSplash, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoUnlimitedAmmo",&g_nAutoUnlimitedAmmo, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoWeaponJam", &g_nAutoWeaponJam, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoAdvanceMode", &g_nAutoAdvanceMode, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoArmorMode", &g_nAutoArmorMode, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTeamAllowed", &g_nAutoTeamAllowed, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nAutoTeamCount", &g_nAutoTeamCount, GOSVAR_INT, 0, NULL);
// MSL 5.06
gosScript_RegisterVariable("g_nMechVariant", &g_nMechVariant, GOSVAR_INT, 0, NULL);
gosScript_RegisterVariable("g_nMechLabOp", &g_nMechLabOp, GOSVAR_INT, 0, NULL);
@@ -1193,6 +1228,23 @@ void MW4Shell::ShutDown()
gosScript_UnregisterCallback("CTCL_GetAutoSlotName");
gosScript_UnregisterCallback("CTCL_LoadAutoFile");
gosScript_UnregisterVariable("g_bAutomaticMode");
// [automaticmode] game option variables
gosScript_UnregisterVariable("g_nAutoTeamCount");
gosScript_UnregisterVariable("g_nAutoTeamAllowed");
gosScript_UnregisterVariable("g_nAutoArmorMode");
gosScript_UnregisterVariable("g_nAutoAdvanceMode");
gosScript_UnregisterVariable("g_nAutoWeaponJam");
gosScript_UnregisterVariable("g_nAutoUnlimitedAmmo");
gosScript_UnregisterVariable("g_nAutoSplash");
gosScript_UnregisterVariable("g_nAutoFriendlyFire");
gosScript_UnregisterVariable("g_nAutoHeat");
gosScript_UnregisterVariable("g_nAutoRadar");
gosScript_UnregisterVariable("g_nAutoTimeLimit");
gosScript_UnregisterVariable("g_nAutoTimeOfDay");
gosScript_UnregisterVariable("g_nAutoWeather");
gosScript_UnregisterVariable("g_nAutoVisibility");
gosScript_UnregisterVariable("g_nAutoGameType");
gosScript_UnregisterVariable("g_szAutoMission");
// MSL 5.06
gosScript_UnregisterVariable("g_nBlackMech");
gosScript_UnregisterVariable("g_nMechVariant");
@@ -12859,28 +12911,37 @@ int __stdcall CTCL_LoadAutoFile(void* instance, int args, void* data[])
NotationFile autofile(g_szAutomaticFile, NotationFile::Standard, true);
// Game options ? overwrite Rookie Mission globals so the existing
// MAIL_SET_ROOKIE_MISSION script flow picks them up automatically.
// Game options ? stored in dedicated Auto globals, NOT Rookie Mission globals,
// so the Default button continues to use the original [RookieMission] defaults.
g_szAutoMission[0] = '\0';
g_nAutoGameType = 2; g_nAutoVisibility = 0; g_nAutoWeather = 0;
g_nAutoTimeOfDay = 0; g_nAutoTimeLimit = -1; g_nAutoRadar = 0;
g_nAutoHeat = 0; g_nAutoFriendlyFire = 0; g_nAutoSplash = 0;
g_nAutoUnlimitedAmmo = 1; g_nAutoWeaponJam = 0; g_nAutoAdvanceMode = 0;
g_nAutoArmorMode = 0; g_nAutoTeamAllowed = 0; g_nAutoTeamCount = 2;
Page *pMission = autofile.FindPage("mission");
if (pMission) {
const char *sz = NULL;
if (pMission->GetEntry("MissionName", &sz) && sz) {
strncpy(g_szRookieMission, sz, sizeof(g_szRookieMission)-1);
g_szRookieMission[sizeof(g_szRookieMission)-1] = '\0';
strncpy(g_szAutoMission, sz, sizeof(g_szAutoMission)-1);
g_szAutoMission[sizeof(g_szAutoMission)-1] = '\0';
}
pMission->GetEntry("GameType", &g_nRookieGameType);
pMission->GetEntry("Visibility", &g_nRookieVisibility);
pMission->GetEntry("Weather", &g_nRookieWeather);
pMission->GetEntry("TimeOfDay", &g_nRookieTimeOfDay);
pMission->GetEntry("TimeLimit", &g_nRookieTimeLimit);
pMission->GetEntry("Radar", &g_nRookieRadar);
pMission->GetEntry("HeatOn", &g_nRookieHeat);
pMission->GetEntry("FriendlyFire", &g_nRookieFriendlyFire);
pMission->GetEntry("SplashDamage", &g_nRookieSplash);
pMission->GetEntry("UnlimitedAmmo", &g_nRookieUnlimitedAmmo);
pMission->GetEntry("WeaponJam", &g_nRookieWeaponJam);
pMission->GetEntry("AdvanceMode", &g_nRookieAdvanceMode);
pMission->GetEntry("ArmorMode", &g_nRookieArmorMode);
pMission->GetEntry("GameType", &g_nAutoGameType);
pMission->GetEntry("Visibility", &g_nAutoVisibility);
pMission->GetEntry("Weather", &g_nAutoWeather);
pMission->GetEntry("TimeOfDay", &g_nAutoTimeOfDay);
pMission->GetEntry("TimeLimit", &g_nAutoTimeLimit);
pMission->GetEntry("Radar", &g_nAutoRadar);
pMission->GetEntry("HeatOn", &g_nAutoHeat);
pMission->GetEntry("FriendlyFire", &g_nAutoFriendlyFire);
pMission->GetEntry("SplashDamage", &g_nAutoSplash);
pMission->GetEntry("UnlimitedAmmo", &g_nAutoUnlimitedAmmo);
pMission->GetEntry("WeaponJam", &g_nAutoWeaponJam);
pMission->GetEntry("AdvanceMode", &g_nAutoAdvanceMode);
pMission->GetEntry("ArmorMode", &g_nAutoArmorMode);
pMission->GetEntry("TeamAllowed", &g_nAutoTeamAllowed);
pMission->GetEntry("TeamCount", &g_nAutoTeamCount);
}
// Per-slot data (up to 16 slots via [slot0]..[slot15] pages)
@@ -190,6 +190,7 @@
#define MAIL_PREVIOUS_MISSION -2222
#define MAIL_RECORD_MISSION -4444
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
#define MAIL_LOAD_AUTO_MISSION -6666 // [automaticmode] sync with Multiplayer/ConLobbyMission.script
// MSL 5.06
// MSL ADD MECH
@@ -1682,9 +1683,29 @@ main
play press, 1
if callback($$CTCL_LoadAutoFile$$)
{
// Apply mission + game options via existing Rookie Mission flow
// 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_SET_ROOKIE_MISSION, @ConLobbyMission@)
{
@ConLobbyMission@o_game_options[0].nselected = $$g_nAutoGameType$$
for (m = 0; m < @ConLobbyMission@o_game_options[1].list_size; m++)
{
if equal$(@ConLobbyMission@o_game_options[1].list_item[m], $$g_szAutoMission$$)
{
@ConLobbyMission@o_game_options[1].nselected = m
break
}
}
mail(MAIL_LOAD_AUTO_MISSION, @ConLobbyMission@)
}
// Update team state synchronously so slot display is correct on the first click.
// MAIL_LOAD_AUTO_MISSION is async; setting team_allowed directly here lets
// CTCL_GetTeamParams return the correct cur_team_val before the slot loop runs.
callback($$SetNetworkMissionParamater$$, team_allowed, $$g_nAutoTeamAllowed$$)
if $$g_nAutoTeamAllowed$$
callback($$SetNetworkMissionParamater$$, num_of_teams, $$g_nAutoTeamCount$$)
cur_team_val = callback($$CTCL_GetTeamParams$$, cur_team_count[0])
// Clear all slots
o_cam_list.nselected = 0
for (k = 0; k < nRosterCount; k++)
@@ -52,6 +52,7 @@
#define MAIL_PREVIOUS_MISSION -2222
#define MAIL_RECORD_MISSION -4444
#define MAIL_PREVIOUS_MISSION_PARAMS -5555
#define MAIL_LOAD_AUTO_MISSION -6666 // [automaticmode] sync with ConLobby.script
#define ROOKIE_MISSION "ScarabStronghold - Attrition"
@@ -1006,6 +1007,82 @@ main
initialize(this)
return
}
// [automaticmode] Load File button: apply Auto globals, separate from Rookie Mission defaults
if (getmessage() == MAIL_LOAD_AUTO_MISSION)
{
callback($$SetNetworkMissionParamater$$, game_type, selection_to_standard[$$g_nAutoGameType$$])
// visibility
o_game_options[2].nselected = $$g_nAutoVisibility$$
callback($$SetNetworkMissionParamater$$, visibility, o_game_options[2].nselected)
// weather
o_game_options[3].nselected = $$g_nAutoWeather$$
callback($$SetNetworkMissionParamater$$, weather, o_game_options[3].nselected)
// time of day
o_game_options[4].nselected = $$g_nAutoTimeOfDay$$
callback($$SetNetworkMissionParamater$$, night_parameter, o_game_options[4].nselected)
callback($$Shell_CallbackHandler$$, ShellSetTime, o_game_options[4].nselected)
// time limit
int nAutoTL = $$g_nAutoTimeLimit$$
int temp_num
if nAutoTL >= 0
temp_num = nAutoTL
else
temp_num = TIME_VALUE_DEFAULT
int m
for (m = 0; m < o_game_options[5].list_size; m++)
{
if (makeint(o_game_options[5].list_item[m]) == temp_num)
{
o_game_options[5].nselected = m
break
}
}
callback($$SetNetworkMissionParamater$$, time_limit, temp_num)
// radar
int nTempParam = radar
int nTempValue = $$g_nAutoRadar$$
o_game_options[6].nselected = nTempValue
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// heat
nTempParam = HEAT_PARAMETER
nTempValue = $$g_nAutoHeat$$
o_game_options[7].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// friendly fire
nTempParam = FRIENDLY_FIRE_PERCENTGE_PARAMETER
nTempValue = $$g_nAutoFriendlyFire$$
o_game_options[8].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// splash
nTempParam = SPLASH_ON_PARAMETER
nTempValue = $$g_nAutoSplash$$
o_game_options[9].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// unlimited ammo
nTempParam = UNLIMITED_AMMO_PARAMETER
nTempValue = $$g_nAutoUnlimitedAmmo$$
o_game_options[10].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// weapon jam
nTempParam = WEAPON_JAM_PARAMETER
nTempValue = $$g_nAutoWeaponJam$$
o_game_options[14].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// advance mode
nTempParam = ADVANCE_MODE_PARAMETER
nTempValue = $$g_nAutoAdvanceMode$$
o_game_options[15].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
// armor mode
nTempParam = ARMOR_MODE_PARAMETER
nTempValue = $$g_nAutoArmorMode$$
o_game_options[16].state = 0
callback($$SetNetworkMissionParamater$$, nTempParam, nTempValue)
initialize(this)
// notify ConLobby: game type/params changed (updates cur_game_type, cur_team_val display)
mail(-9998, parent)
return
}
if (getmessage() == MAIL_PREVIOUS_MISSION_PARAMS)
{
// visibility