From 8582e6c5e5404e3a56418abb5b5b2859b318bc59 Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Sat, 23 May 2026 01:23:14 -0500 Subject: [PATCH] Discover enemy factions dynamically from loaded mods - Scan CfgFactionClasses/CfgVehicles at runtime for selectable enemy factions - Add CfgFactionUnitMap overrides and keep legacy params as fallback --- .../CfgEnemyFactions.hpp | 75 +++++---- .../CfgFactionUnitMap.hpp | 19 ++- arma/forge_pmc_simulator.Tanoa/CfgParams.hpp | 14 +- arma/forge_pmc_simulator.Tanoa/config.h | 1 - .../forge_pmc_simulator.Tanoa/description.ext | 1 + .../functions/helpers/README.md | 6 +- .../helpers/fn_getAllEnemyFactions.sqf | 4 +- .../helpers/fn_getEnemyFactionOptions.sqf | 147 ++++++++++++++---- .../helpers/fn_getEnemyFactionSide.sqf | 9 +- .../helpers/fn_getEnemyFactionUnitPool.sqf | 76 ++++++--- .../fn_populateEnemyFactionListbox.sqf | 3 +- .../helpers/fn_resolveEnemyFactionParam.sqf | 4 +- .../fn_attackMissionGenerator.sqf | 2 +- .../fn_captureHvtMissionGenerator.sqf | 6 +- .../fn_defendMissionGenerator.sqf | 2 +- .../fn_defuseMissionGenerator.sqf | 14 +- .../fn_deliveryMissionGenerator.sqf | 12 +- .../fn_destroyMissionGenerator.sqf | 8 +- .../fn_hostageMissionGenerator.sqf | 12 +- .../fn_hvtMissionGenerator.sqf | 6 +- .../missionManager/fn_missionManager.sqf | 2 +- .../fn_updateEnemyCountFromActivePlayers.sqf | 2 +- .../functions/missionSetup/README.md | 2 +- .../fn_handleMissionSetupUIEvents.sqf | 14 +- .../missionSetup/fn_openMissionSetupUI.sqf | 8 +- .../fn_setupMenu_applySettings.sqf | 22 ++- .../initPlayerLocal.sqf | 2 +- 27 files changed, 322 insertions(+), 151 deletions(-) delete mode 100644 arma/forge_pmc_simulator.Tanoa/config.h diff --git a/arma/forge_pmc_simulator.Tanoa/CfgEnemyFactions.hpp b/arma/forge_pmc_simulator.Tanoa/CfgEnemyFactions.hpp index fe760df..9619209 100644 --- a/arma/forge_pmc_simulator.Tanoa/CfgEnemyFactions.hpp +++ b/arma/forge_pmc_simulator.Tanoa/CfgEnemyFactions.hpp @@ -1,41 +1,58 @@ /* - * Enemy faction options for the PMC simulator setup flow. + * Runtime enemy faction discovery controls for the PMC simulator setup flow. * * Consumers: - * - forge_pmc_fnc_getEnemyFactionOptions reads this config for the startup UI. - * - forge_pmc_fnc_resolveEnemyFactionParam maps mission param values back to - * faction classnames during fallback/default setup. + * - forge_pmc_fnc_getEnemyFactionOptions scans loaded CfgFactionClasses and + * CfgVehicles at runtime, then uses this config to filter and polish the UI. + * - forge_pmc_fnc_resolveEnemyFactionParam uses override values here to keep + * legacy mission params compatible when the setup UI is cancelled. * - Mission generators use ENEMY_FACTION_STR and ENEMY_SIDE after setup has * applied the selected option. * - * Keep this list aligned with the server modpack. Classnames that are not - * present in CfgFactionClasses may still appear in the UI, but downstream unit - * pool generation may fall back or fail to find units. - * - * BLUFOR/WEST factions are intentionally omitted because generated missions use - * these options as opposing forces. + * This config is intentionally not the full faction list. Any loaded mod + * faction on an allowed side can appear automatically if it has spawnable + * infantry or a CfgFactionUnitMap override. */ class CfgEnemyFactions { /* - * Option format: - * - class name: stable config entry name, normally matching the faction. - * - value: legacy mission param numeric value. - * - faction: CfgFactionClasses classname used for spawning. - * - display: user-facing label shown in the setup UI. + * Arma side IDs allowed as generated enemy factions: + * - 0: EAST / OPFOR + * - 2: RESISTANCE / Independent + * + * WEST / BLUFOR is intentionally excluded because generated missions use + * these options as opposing forces. */ - class Options { - class OPF_F { value = 0; faction = "OPF_F"; display = "CSAT"; }; - class OPF_T_F { value = 1; faction = "OPF_T_F"; display = "CSAT (Pacific)"; }; - class OPF_V_F { value = 2; faction = "OPF_V_F"; display = "Viper"; }; - class OPF_R_F { value = 3; faction = "OPF_R_F"; display = "Spetnaz"; }; - class OPF_SFIA_lxWS { value = 4; faction = "OPF_SFIA_lxWS"; display = "SFIA"; }; - class OPF_TURA_lxWS { value = 5; faction = "OPF_TURA_lxWS"; display = "Tura"; }; - class IND_F { value = 6; faction = "IND_F"; display = "AAF"; }; - class IND_G_F { value = 7; faction = "IND_G_F"; display = "FIA"; }; - class IND_E_F { value = 8; faction = "IND_E_F"; display = "LDF"; }; - class IND_C_F { value = 9; faction = "IND_C_F"; display = "Syndikat"; }; - class IND_L_F { value = 10; faction = "IND_L_F"; display = "Looters"; }; - class IND_SFIA_lxWS { value = 11; faction = "IND_SFIA_lxWS"; display = "SFIA"; }; - class IND_TURA_lxWS { value = 12; faction = "IND_TURA_lxWS"; display = "Tura"; }; + sides[] = {0, 2}; + + /* + * Factions that should never be offered even if present in the active + * modset. Keep this for factions that are unsuitable for PMC contracts. + */ + denylist[] = { + "IND_UN_lxWS" + }; + + /* + * Optional display/order/value metadata for known factions. + * + * - value keeps legacy Params::enemyFaction values stable. + * - order controls setup UI ordering before dynamically discovered factions. + * - display overrides raw CfgFactionClasses names when we want cleaner text. + * + * Factions not listed here are still discovered automatically and sorted + * after these known options. + */ + class Overrides { + class OPF_F { value = 0; order = 0; display = "CSAT"; }; + class OPF_T_F { value = 1; order = 1; display = "CSAT (Pacific)"; }; + class OPF_R_F { value = 2; order = 2; display = "Spetnaz"; }; + class OPF_SFIA_lxWS { value = 3; order = 3; display = "SFIA"; }; + class OPF_TURA_lxWS { value = 4; order = 4; display = "Tura"; }; + class IND_F { value = 5; order = 5; display = "AAF"; }; + class IND_G_F { value = 6; order = 6; display = "FIA"; }; + class IND_E_F { value = 7; order = 7; display = "LDF"; }; + class IND_C_F { value = 8; order = 8; display = "Syndikat"; }; + class IND_L_F { value = 9; order = 9; display = "Looters"; }; + class IND_TURA_lxWS { value = 10; order = 10; display = "Tura"; }; }; }; diff --git a/arma/forge_pmc_simulator.Tanoa/CfgFactionUnitMap.hpp b/arma/forge_pmc_simulator.Tanoa/CfgFactionUnitMap.hpp index c06b075..33eccaf 100644 --- a/arma/forge_pmc_simulator.Tanoa/CfgFactionUnitMap.hpp +++ b/arma/forge_pmc_simulator.Tanoa/CfgFactionUnitMap.hpp @@ -1,19 +1,22 @@ /* - * Optional faction-to-unit template map. + * Optional faction-to-unit override map. * * Current behavior: - * - Enemy unit pools are primarily built from CfgGroups/CfgVehicles through - * forge_pmc_fnc_getEnemyFactionUnitPool. - * - This config is a template for deterministic per-faction unit pools if the - * automatic faction lookup is not specific enough for a server/modpack. + * - forge_pmc_fnc_getEnemyFactionOptions treats a mapped faction as selectable + * when at least one mapped vehicle exists. + * - forge_pmc_fnc_getEnemyFactionUnitPool checks this map first. + * - If a selected faction has a class here, the listed Units are used as the + * deterministic spawn pool for generated mission enemies. + * - If no class exists here, the helper falls back to CfgVehicles traversal for + * units whose faction and side match the selected faction. * - * To enable this map, wire it into forge_pmc_fnc_getEnemyFactionUnitPool or the - * generator spawn path before falling back to config traversal. + * Most mod factions do not need an entry here. Add a class only when a faction + * needs a curated or corrected spawn pool. */ class CfgFactionUnitMap { /* * Mapping key should match the selected faction classname from - * CfgEnemyFactions.Options[].faction, such as "IND_G_F". + * CfgFactionClasses, such as "IND_G_F". */ class IND_G_F { /* diff --git a/arma/forge_pmc_simulator.Tanoa/CfgParams.hpp b/arma/forge_pmc_simulator.Tanoa/CfgParams.hpp index bb6915c..0c0479f 100644 --- a/arma/forge_pmc_simulator.Tanoa/CfgParams.hpp +++ b/arma/forge_pmc_simulator.Tanoa/CfgParams.hpp @@ -1,11 +1,18 @@ +/* + * Mission lobby fallback params. + * + * The startup setup UI now discovers selectable factions dynamically from the + * active modset. Params remain intentionally static because Arma evaluates + * them before mission runtime scripts can scan loaded factions. If the setup UI + * is cancelled or never opened, these values provide the default fallback. + */ class Params { class enemyFaction { title = "Enemy Faction"; - values[] = {0,1,2,3,4,5,6,7,8,9,10,11,12}; + values[] = {0,1,2,3,4,5,6,7,8,9,10}; texts[] = { "CSAT", "CSAT (Pacific)", - "Viper", "Spetnaz", "SFIA (OPFOR)", "Tura (OPFOR)", @@ -14,10 +21,9 @@ class Params { "LDF", "Syndikat", "Looters", - "SFIA (Independent)", "Tura (Independent)" }; - default = 7; + default = 6; }; class maxConcurrentMissions { diff --git a/arma/forge_pmc_simulator.Tanoa/config.h b/arma/forge_pmc_simulator.Tanoa/config.h deleted file mode 100644 index 531dce8..0000000 --- a/arma/forge_pmc_simulator.Tanoa/config.h +++ /dev/null @@ -1 +0,0 @@ -#include "CfgFunctions.h" diff --git a/arma/forge_pmc_simulator.Tanoa/description.ext b/arma/forge_pmc_simulator.Tanoa/description.ext index 2c899e1..28b04aa 100644 --- a/arma/forge_pmc_simulator.Tanoa/description.ext +++ b/arma/forge_pmc_simulator.Tanoa/description.ext @@ -24,6 +24,7 @@ corpseManagerMode = 0; #include "CfgParams.hpp" #include "CfgEnemyFactions.hpp" +#include "CfgFactionUnitMap.hpp" #include "CfgMissions.hpp" #include "CfgFunctions.hpp" #include "ui\baseControls.hpp" diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/README.md b/arma/forge_pmc_simulator.Tanoa/functions/helpers/README.md index 343278f..ede7490 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/README.md +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/README.md @@ -4,7 +4,7 @@ Helper functions provide reusable lookups and conversions for the PMC simulator ## Registered Functions - `forge_pmc_fnc_getAllEnemyFactions` returns available non-BLUFOR faction classnames. -- `forge_pmc_fnc_getEnemyFactionOptions` reads configured faction options from `CfgEnemyFactions`. +- `forge_pmc_fnc_getEnemyFactionOptions` scans the active modset for selectable OPFOR/Independent factions and applies `CfgEnemyFactions` filters/labels. - `forge_pmc_fnc_resolveEnemyFactionParam` converts a mission parameter value into a faction classname. - `forge_pmc_fnc_getEnemyFactionSide` resolves a faction classname to an Arma side. - `forge_pmc_fnc_getEnemyFactionUnitPool` builds the unit pool used by generated enemy spawns. @@ -12,4 +12,8 @@ Helper functions provide reusable lookups and conversions for the PMC simulator - `forge_pmc_fnc_getEnemyFactionListboxSelection` and `forge_pmc_fnc_populateEnemyFactionListbox` support faction picker UI/listbox flows. ## Notes +The mission setup UI is populated dynamically from loaded `CfgFactionClasses` and `CfgVehicles`. `CfgEnemyFactions` only controls allowed sides, denylisted factions, and friendly labels/order for known factions. + +`forge_pmc_fnc_getEnemyFactionUnitPool` checks `CfgFactionUnitMap` first. Add a faction class there when the automatic `CfgVehicles` faction lookup is too broad, too sparse, or needs a curated unit pool. + These functions are registered under the `forge_pmc` tag, so their public names do not include the folder name. Moving a helper within this folder should not change callers as long as `CfgFunctions.hpp` remains updated. diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getAllEnemyFactions.sqf b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getAllEnemyFactions.sqf index f505977..3c84e5f 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getAllEnemyFactions.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getAllEnemyFactions.sqf @@ -1,8 +1,8 @@ /* * Author: IDSolutions, Blackbox AI, MrPākehā * Returns candidate enemy faction classnames available to the mission. - * This is a runtime helper only; description.ext mission params still use - * the static CfgEnemyFactions list. + * This is a runtime helper only. The setup UI uses + * forge_pmc_fnc_getEnemyFactionOptions so it can filter to spawnable factions. * * Arguments: * 0: Exclude BLUFOR/WEST factions (Default: true) diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionOptions.sqf b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionOptions.sqf index 692b476..efb08e4 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionOptions.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionOptions.sqf @@ -1,7 +1,8 @@ /* - * Author: IDSolutions, Blackbox AI, MrPākehā - * Reads enemy faction options from missionConfigFile >> CfgEnemyFactions - * >> Options for setup UI hydration and mission param fallback resolution. + * Author: IDSolutions, Blackbox AI, MrPakeha + * Builds setup UI faction options from the active modset. The helper scans + * loaded faction classes and only returns allowed OPFOR/Independent factions + * that can provide infantry through CfgVehicles or CfgFactionUnitMap. * * Arguments: * None @@ -12,40 +13,122 @@ * Public: No */ -private _optionsConfig = missionConfigFile >> "CfgEnemyFactions" >> "Options"; -private _options = []; - -if (isClass _optionsConfig) then { - { - private _configName = configName _x; - private _value = getNumber (_x >> "value"); - private _faction = getText (_x >> "faction"); - private _display = getText (_x >> "display"); - - if (_faction isEqualTo "") then { - _faction = _configName; - }; - - if (!isClass (configFile >> "CfgFactionClasses" >> _faction) && { - isClass (configFile >> "CfgFactionClasses" >> _configName) - }) then { - _faction = _configName; - }; - - if (_display isEqualTo "") then { - _display = _faction; - }; - - if (_faction isNotEqualTo "") then { - _options pushBack [_faction, _display, _value]; - }; - } forEach ("true" configClasses _optionsConfig); +private _config = missionConfigFile >> "CfgEnemyFactions"; +private _allowedSides = getArray (_config >> "sides"); +if (_allowedSides isEqualTo []) then { + _allowedSides = [0, 2]; }; +private _denylist = getArray (_config >> "denylist"); +private _overridesConfig = _config >> "Overrides"; + +private _spawnableFactions = createHashMap; +{ + if (getNumber (_x >> "scope") < 2) then { continue; }; + if !(configName _x isKindOf "CAManBase") then { continue; }; + + private _faction = getText (_x >> "faction"); + if (_faction isEqualTo "") then { continue; }; + + private _side = getNumber (_x >> "side"); + if !(_side in _allowedSides) then { continue; }; + + _spawnableFactions set [_faction, true]; +} forEach ("true" configClasses (configFile >> "CfgVehicles")); + +private _mappedFactions = createHashMap; +{ + private _unitsConfig = _x >> "Units"; + if (!(isClass _unitsConfig)) then { continue; }; + + private _hasUnits = false; + { + private _vehicle = getText (_x >> "vehicle"); + if (_vehicle isNotEqualTo "" && { isClass (configFile >> "CfgVehicles" >> _vehicle) }) exitWith { + _hasUnits = true; + }; + } forEach ("true" configClasses _unitsConfig); + + if (_hasUnits) then { + _mappedFactions set [configName _x, true]; + }; +} forEach ("true" configClasses (missionConfigFile >> "CfgFactionUnitMap")); + +private _getFactionSideNumber = { + params ["_factionConfig"]; + + if (isNumber (_factionConfig >> "side")) exitWith { + getNumber (_factionConfig >> "side") + }; + + switch (toUpperANSI getText (_factionConfig >> "side")) do { + case "0"; + case "EAST"; + case "OPFOR": { 0 }; + case "2"; + case "GUER"; + case "GUERRILA"; + case "GUERRILLA"; + case "INDEPENDENT"; + case "RESISTANCE": { 2 }; + default { -1 }; + }; +}; + +private _records = []; +private _dynamicIndex = 0; +{ + private _faction = configName _x; + if (_faction isEqualTo "") then { continue; }; + if (_faction in _denylist) then { continue; }; + + private _side = [_x] call _getFactionSideNumber; + if !(_side in _allowedSides) then { continue; }; + + if (!(_spawnableFactions getOrDefault [_faction, false]) && { + !(_mappedFactions getOrDefault [_faction, false]) + }) then { + continue; + }; + + private _override = _overridesConfig >> _faction; + private _display = getText (_x >> "displayName"); + private _order = 1000 + _dynamicIndex; + private _value = 1000 + _dynamicIndex; + + if (isClass _override) then { + private _overrideDisplay = getText (_override >> "display"); + if (_overrideDisplay isNotEqualTo "") then { + _display = _overrideDisplay; + }; + if (isNumber (_override >> "order")) then { + _order = getNumber (_override >> "order"); + }; + if (isNumber (_override >> "value")) then { + _value = getNumber (_override >> "value"); + }; + }; + + if (_display isEqualTo "") then { + _display = _faction; + }; + + _records pushBack [_order, _display, _faction, _value]; + _dynamicIndex = _dynamicIndex + 1; +} forEach ("true" configClasses (configFile >> "CfgFactionClasses")); + +_records sort true; + +private _options = []; +{ + _x params ["_order", "_display", "_faction", "_value"]; + _options pushBack [_faction, _display, _value]; +} forEach _records; + if (_options isEqualTo []) then { _options = [ ["OPF_F", "CSAT", 0], - ["IND_G_F", "FIA", 7] + ["IND_G_F", "FIA", 6] ]; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionSide.sqf b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionSide.sqf index 3b78fb2..01c871a 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionSide.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionSide.sqf @@ -25,10 +25,15 @@ if (_f isEqualTo "") exitWith { _fallbackSide }; private _side = _fallbackSide; private _cfgFaction = configFile >> "CfgFactionClasses" >> _enemyFaction; if (isClass _cfgFaction) then { - private _sideNumber = getNumber (_cfgFaction >> "side"); + private _hasSideNumber = isNumber (_cfgFaction >> "side"); + private _sideNumber = if (_hasSideNumber) then { getNumber (_cfgFaction >> "side") } else { -1 }; private _sideText = toUpperANSI getText (_cfgFaction >> "side"); - if (_sideNumber > 0 || { _sideText isEqualTo "0" }) then { + if (_hasSideNumber || { _sideText in ["0", "1", "2"] }) then { + if (!_hasSideNumber) then { + _sideNumber = parseNumber _sideText; + }; + switch (_sideNumber) do { case 0: { _side = east; }; case 2: { _side = resistance; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionUnitPool.sqf b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionUnitPool.sqf index 931f1a2..4077901 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionUnitPool.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_getEnemyFactionUnitPool.sqf @@ -6,6 +6,8 @@ * Arguments: * 0: Faction classname (Default: ENEMY_FACTION_STR or "IND_G_F") * 1: Fallback side (Default: ENEMY_SIDE or east) + * 2: Allow side-default fallback units when no faction units exist + * (Default: true) * * Return Value: * Unit definitions with vehicle, rank, and position keys @@ -15,7 +17,8 @@ params [ ["_faction", missionNamespace getVariable ["ENEMY_FACTION_STR", "IND_G_F"], [""]], - ["_fallbackSide", missionNamespace getVariable ["ENEMY_SIDE", east], [east]] + ["_fallbackSide", missionNamespace getVariable ["ENEMY_SIDE", east], [east]], + ["_allowSideFallback", true, [false]] ]; if (_faction isEqualTo "") then { @@ -25,33 +28,56 @@ if (_faction isEqualTo "") then { private _pool = []; private _sideNumber = [_fallbackSide] call BIS_fnc_sideID; -{ - if (getNumber (_x >> "scope") < 2) then { continue; }; - if (getText (_x >> "faction") isNotEqualTo _faction) then { continue; }; - if (getNumber (_x >> "side") isNotEqualTo _sideNumber) then { continue; }; - if !(configName _x isKindOf "CAManBase") then { continue; }; +// Check CfgFactionUnitMap first for explicit faction unit definitions +private _factionMapConfig = missionConfigFile >> "CfgFactionUnitMap" >> _faction; +if (isClass _factionMapConfig) then { + { + private _vehicle = getText (_x >> "vehicle"); + if (_vehicle isEqualTo "" || { !(isClass (configFile >> "CfgVehicles" >> _vehicle)) }) then { + continue; + }; - private _className = configName _x; - private _upperClassName = toUpperANSI _className; - private _rank = "PRIVATE"; - - if ( - (_upperClassName find "_SL_" >= 0) - || { _upperClassName find "_TL_" >= 0 } - || { _upperClassName find "OFFICER" >= 0 } - || { _upperClassName find "COMMANDER" >= 0 } - ) then { - _rank = "SERGEANT"; - }; - - _pool pushBack createHashMapFromArray [ - ["vehicle", _className], - ["rank", _rank], - ["position", [0, 0, 0]] - ]; -} forEach ("true" configClasses (configFile >> "CfgVehicles")); + _pool pushBack createHashMapFromArray [ + ["vehicle", _vehicle], + ["rank", getText (_x >> "rank")], + ["position", getArray (_x >> "position")] + ]; + } forEach ("true" configClasses (_factionMapConfig >> "Units")); +}; +// Fall back to config traversal if no explicit mapping exists. if (_pool isEqualTo []) then { + private _factionFallback = _faction; + + { + if (getNumber (_x >> "scope") < 2) then { continue; }; + private _unitFaction = getText (_x >> "faction"); + if ((_unitFaction isNotEqualTo _faction) && (_unitFaction isNotEqualTo _factionFallback)) then { continue; }; + if (getNumber (_x >> "side") isNotEqualTo _sideNumber) then { continue; }; + if !(configName _x isKindOf "CAManBase") then { continue; }; + + private _className = configName _x; + private _upperClassName = toUpperANSI _className; + private _rank = "PRIVATE"; + + if ( + (_upperClassName find "_SL_" >= 0) + || { _upperClassName find "_TL_" >= 0 } + || { _upperClassName find "OFFICER" >= 0 } + || { _upperClassName find "COMMANDER" >= 0 } + ) then { + _rank = "SERGEANT"; + }; + + _pool pushBack createHashMapFromArray [ + ["vehicle", _className], + ["rank", _rank], + ["position", [0, 0, 0]] + ]; + } forEach ("true" configClasses (configFile >> "CfgVehicles")); +}; + +if (_pool isEqualTo [] && { _allowSideFallback }) then { private _fallbackUnits = switch (_fallbackSide) do { case east: { ["O_Soldier_SL_F", "O_Soldier_TL_F", "O_Soldier_F", "O_Soldier_AR_F", "O_Soldier_GL_F", "O_medic_F"] }; case resistance: { ["I_G_Soldier_SL_F", "I_G_Soldier_TL_F", "I_G_Soldier_F", "I_G_Soldier_AR_F", "I_G_medic_F"] }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_populateEnemyFactionListbox.sqf b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_populateEnemyFactionListbox.sqf index db1ead5..98ea12a 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_populateEnemyFactionListbox.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_populateEnemyFactionListbox.sqf @@ -1,6 +1,7 @@ /* * Author: IDSolutions, Blackbox AI, MrPākehā - * Populates a listbox or combo control with CfgEnemyFactions options. + * Populates a listbox or combo control with dynamically discovered enemy + * faction options. * * Arguments: * 0: Listbox/combo control or display diff --git a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_resolveEnemyFactionParam.sqf b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_resolveEnemyFactionParam.sqf index eb651e3..e053c05 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_resolveEnemyFactionParam.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/helpers/fn_resolveEnemyFactionParam.sqf @@ -4,7 +4,7 @@ * enemy faction classname. * * Arguments: - * 0: Param value from Params::enemyFaction (Default: 7) + * 0: Param value from Params::enemyFaction (Default: 6) * 1: Fallback faction classname (Default: "IND_G_F") * * Return Value: @@ -14,7 +14,7 @@ */ params [ - ["_value", 7, [0, ""]], + ["_value", 6, [0, ""]], ["_fallback", "IND_G_F", [""]] ]; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_attackMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_attackMissionGenerator.sqf index cc453d3..c02dfc2 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_attackMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_attackMissionGenerator.sqf @@ -131,7 +131,7 @@ AttackMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_captureHvtMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_captureHvtMissionGenerator.sqf index e3b6f94..ca90b44 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_captureHvtMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_captureHvtMissionGenerator.sqf @@ -131,7 +131,7 @@ CaptureHvtMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; @@ -152,7 +152,7 @@ CaptureHvtMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; private _buildingPositions = []; - if (!isNull _building) then { + if !(isNull _building) then { for "_i" from 0 to 100 do { private _buildingPos = _building buildingPos _i; if (_buildingPos isEqualTo [0, 0, 0]) exitWith {}; @@ -214,7 +214,7 @@ CaptureHvtMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ selectRandom _buildingPositions }; private _escort = _group createUnit [_escortClass, _escortPos, [], 0, "NONE"]; - if (!isNull _escort) then { + if !(isNull _escort) then { _escort setRank (_escortDef getOrDefault ["rank", "PRIVATE"]); _escortUnits pushBack _escort; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defendMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defendMissionGenerator.sqf index e2be16f..a61e41d 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defendMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defendMissionGenerator.sqf @@ -131,7 +131,7 @@ DefendMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defuseMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defuseMissionGenerator.sqf index 481f5fb..f57aa2b 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defuseMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_defuseMissionGenerator.sqf @@ -132,7 +132,7 @@ DefuseMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; @@ -297,7 +297,7 @@ DefuseMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ if (_nearBuildings isNotEqualTo []) then { // prefer the closest building that actually contains the position { - if (!isNull _x && { _position inArea _x }) exitWith { + if !(isNull _x && { _position inArea _x }) exitWith { _building = _x; }; } forEach _nearBuildings; @@ -313,14 +313,14 @@ DefuseMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; }; - if (!isNull _building) then { + if !(isNull _building) then { for "_i" from 1 to _buildingSpawnAttempts do { private _posIndex = floor random 1000; private _candidate = _building buildingPos _posIndex; // buildingPos returns [0,0,0] for invalid positions if (_candidate isEqualTo [0, 0, 0]) then { continue; }; // ensure candidate is still inside the building footprint - if (!(_candidate isEqualType [])) then { continue; }; + if !((_candidate isEqualType [])) then { continue; }; if ((_candidate vectorDistance _position) <= 60) exitWith { _buildingPos = _candidate; }; @@ -328,7 +328,7 @@ DefuseMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; private _protectedPos = [0,0,0]; - if (!(_buildingPos isEqualTo [])) then { + if !((_buildingPos isEqualTo [])) then { _protectedPos = _buildingPos; } else { // Outdoor fallback: keep previous behavior @@ -337,7 +337,7 @@ DefuseMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ private _protectedObject = createVehicle [_protectedClass, _protectedPos, [], 0, "NONE"]; private _protectedObjects = []; - if (!isNull _protectedObject) then { + if !(isNull _protectedObject) then { _protectedObjects pushBack _protectedObject; }; @@ -356,7 +356,7 @@ DefuseMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ private _devicePos = _protectedPos vectorAdd _deviceOffset; private _deviceObject = createVehicle [_deviceClass, _devicePos, [], 0, "NONE"]; - if (!isNull _deviceObject) then { + if !(isNull _deviceObject) then { _devices pushBack _deviceObject; }; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_deliveryMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_deliveryMissionGenerator.sqf index e5ff367..02dec4b 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_deliveryMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_deliveryMissionGenerator.sqf @@ -131,7 +131,7 @@ DeliveryMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; @@ -193,22 +193,22 @@ DeliveryMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ if (_cargoPool isEqualTo []) exitWith { [] }; private _cargoSpawnObj = objNull; - if (!isNil "cargoSpawn") then { _cargoSpawnObj = cargoSpawn; }; + if !(isNil "cargoSpawn") then { _cargoSpawnObj = cargoSpawn; }; if (isNull _cargoSpawnObj) then { _cargoSpawnObj = missionNamespace getVariable ["cargoSpawn", objNull]; }; private _extZoneObj = objNull; - if (!isNil "ExtZone") then { _extZoneObj = ExtZone; }; + if !(isNil "ExtZone") then { _extZoneObj = ExtZone; }; if (isNull _extZoneObj) then { _extZoneObj = missionNamespace getVariable ["ExtZone", objNull]; }; for "_i" from 1 to _cargoCount do { private _cargoClass = selectRandom _cargoPool; private _spawnPos = [0, 0, 0]; - if (!isNull _cargoSpawnObj) then { + if !(isNull _cargoSpawnObj) then { private _basePos = getPosATL _cargoSpawnObj; _spawnPos = _basePos vectorAdd [(random 12 - 6), (random 12 - 6), 0]; } else { - if (!isNull _extZoneObj) then { + if !(isNull _extZoneObj) then { private _basePos = getPosATL _extZoneObj; _spawnPos = _basePos vectorAdd [(random 12 - 6), (random 12 - 6), 0]; } else { @@ -217,7 +217,7 @@ DeliveryMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; private _cargoObject = createVehicle [_cargoClass, _spawnPos, [], 0, "NONE"]; - if (!isNull _cargoObject) then { + if !(isNull _cargoObject) then { _cargoObjects pushBack _cargoObject; }; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_destroyMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_destroyMissionGenerator.sqf index 0ae5e9e..538fec9 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_destroyMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_destroyMissionGenerator.sqf @@ -132,7 +132,7 @@ DestroyMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; @@ -349,13 +349,13 @@ DestroyMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ _position ]] call forge_server_common_fnc_log; - if (!isNull _defendGroup) then { + if !(isNull _defendGroup) then { deleteGroup _defendGroup; }; }; private _spawnedGroups = [_group]; - if (!isNull _defendGroup && { units _defendGroup isNotEqualTo [] }) then { + if !(isNull _defendGroup && { units _defendGroup isNotEqualTo [] }) then { _spawnedGroups pushBack _defendGroup; }; @@ -442,7 +442,7 @@ DestroyMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ private _position = _missionRecord getOrDefault ["position", []]; private _groups = _missionRecord getOrDefault ["groups", []]; { - if (!isNull _x) then { + if !(isNull _x) then { { deleteVehicle _x; } forEach (units _x); deleteGroup _x; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hostageMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hostageMissionGenerator.sqf index 18a7524..ae5355c 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hostageMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hostageMissionGenerator.sqf @@ -132,7 +132,7 @@ HostageMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; @@ -155,7 +155,7 @@ HostageMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; private _buildingPositions = []; - if (!isNull _building) then { + if !(isNull _building) then { // buildingPos returns positions for building interiors; we random-pick from these. for "_i" from 0 to 100 do { private _bp = _building buildingPos _i; @@ -282,7 +282,7 @@ HostageMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ private _hostageClass = selectRandom _hostageClasses; private _hostagePos = [0,0,0]; - if (!_useBuildingPositions) then { + if !(_useBuildingPositions) then { private _bp = selectRandom _buildingPositions; _hostagePos = _bp; } else { @@ -290,7 +290,7 @@ HostageMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; private _hostage = _hostageGroup createUnit [_hostageClass, _hostagePos, [], 0, "NONE"]; - if (!isNull _hostage) then { + if !(isNull _hostage) then { _hostage setCaptive true; _hostages pushBack _hostage; }; @@ -369,7 +369,7 @@ HostageMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ _unitOffset set [1, (_unitOffset # 1) + (random 10 - 5)]; private _shooter = _group createUnit [_unitClass, _shootBasePos vectorAdd _unitOffset, [], 0, "NONE"]; - if (!isNull _shooter) then { + if !(isNull _shooter) then { _shooter setRank (_x getOrDefault ["rank", "PRIVATE"]); _shooters pushBack _shooter; }; @@ -483,7 +483,7 @@ HostageMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ // Put marker on ground. private _ground = +_mPos; private _safe = [_ground, 30, 0] call BIS_fnc_findSafePos; - if (!(_safe isEqualTo [0, 0, 0])) then { + if !((_safe isEqualTo [0, 0, 0])) then { _ground = _safe; }; _ground set [2, 0]; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hvtMissionGenerator.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hvtMissionGenerator.sqf index 18e7a32..eabe316 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hvtMissionGenerator.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionGenerators/fn_hvtMissionGenerator.sqf @@ -131,7 +131,7 @@ KillHvtMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; } forEach _blkListMarkers; - if (!_inBlkList) then { + if !(_inBlkList) then { _taskPos = _candidate; }; }; @@ -152,7 +152,7 @@ KillHvtMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ }; private _buildingPositions = []; - if (!isNull _building) then { + if !(isNull _building) then { for "_i" from 0 to 100 do { private _buildingPos = _building buildingPos _i; if (_buildingPos isEqualTo [0, 0, 0]) exitWith {}; @@ -214,7 +214,7 @@ KillHvtMissionGeneratorBaseClass = compileFinal createHashMapFromArray [ selectRandom _buildingPositions }; private _escort = _group createUnit [_escortClass, _escortPos, [], 0, "NONE"]; - if (!isNull _escort) then { + if !(isNull _escort) then { _escort setRank (_escortDef getOrDefault ["rank", "PRIVATE"]); _escortUnits pushBack _escort; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_missionManager.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_missionManager.sqf index 8a26a6a..64450a7 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_missionManager.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_missionManager.sqf @@ -55,7 +55,7 @@ if !(missionNamespace getVariable ["forge_pmc_defuseAceHandlerRegistered", false } forEach _this; if (_taskID isEqualTo "") exitWith {}; - if (!isNil "forge_server_task_TaskStore") then { + if !(isNil "forge_server_task_TaskStore") then { forge_server_task_TaskStore call ["incrementDefuseCount", [_taskID]]; }; }] call CBA_fnc_addEventHandler; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_updateEnemyCountFromActivePlayers.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_updateEnemyCountFromActivePlayers.sqf index 307df00..e1c76f6 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_updateEnemyCountFromActivePlayers.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionManager/fn_updateEnemyCountFromActivePlayers.sqf @@ -12,7 +12,7 @@ * Public: No */ -if (!isServer) exitWith { 1 }; +if !(isServer) exitWith { 1 }; private _table = missionNamespace getVariable [ "forge_pmc_enemyCountMultiplierTable", diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/README.md b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/README.md index e80fefc..092b771 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/README.md +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/README.md @@ -8,7 +8,7 @@ Mission setup functions own startup configuration for `forge_pmc_simulator.Tanoa - `forge_pmc_fnc_setupMenu_applySettings` applies UI overrides or mission parameter defaults into `forge_pmc_missionSettings`. ## Startup Flow -The client opens the setup UI from `initPlayerLocal.sqf` when `forge_pmc_missionSettingsApplied` is not set. +The CEO client opens the setup UI from `initPlayerLocal.sqf` when `forge_pmc_missionSettingsApplied` is not set. Other players do not receive the startup setup UI. Selecting **Start Mission** sends UI values to the server and applies them. Selecting **Cancel** applies the existing Arma mission params/defaults immediately. diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_handleMissionSetupUIEvents.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_handleMissionSetupUIEvents.sqf index cf13de6..486659a 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_handleMissionSetupUIEvents.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_handleMissionSetupUIEvents.sqf @@ -58,10 +58,22 @@ switch (_event) do { ]; } forEach ([] call forge_pmc_fnc_getEnemyFactionOptions); + private _defaultFaction = "IND_G_F"; + private _hasDefaultFaction = false; + { + if ((_x getOrDefault ["faction", ""]) isEqualTo _defaultFaction) exitWith { + _hasDefaultFaction = true; + }; + } forEach _factions; + + if (!_hasDefaultFaction && { _factions isNotEqualTo [] }) then { + _defaultFaction = (_factions select 0) getOrDefault ["faction", _defaultFaction]; + }; + private _payload = createHashMapFromArray [ ["factions", _factions], ["settings", createHashMapFromArray [ - ["enemyFaction", "IND_G_F"], + ["enemyFaction", _defaultFaction], ["maxConcurrentMissions", getNumber (_missionConfig >> "maxConcurrentMissions")], ["missionInterval", getNumber (_missionConfig >> "missionInterval")], ["moneyMin", (getArray (_attackConfig >> "Rewards" >> "money")) param [0, 25000]], diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_openMissionSetupUI.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_openMissionSetupUI.sqf index e5714d0..2a425e2 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_openMissionSetupUI.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_openMissionSetupUI.sqf @@ -12,15 +12,15 @@ * Public: No */ -if (!hasInterface) exitWith { false }; +if !(hasInterface) exitWith { false }; if (missionNamespace getVariable ["forge_pmc_missionSettingsApplied", false]) exitWith { false }; private _ceoUnit = missionNamespace getVariable ["ceo", objNull]; private _isCeoSlot = - (!isNull _ceoUnit && { player isEqualTo _ceoUnit }) || + !(isNull _ceoUnit && { player isEqualTo _ceoUnit }) || { toLowerANSI (vehicleVarName player) isEqualTo "ceo" }; -if (!_isCeoSlot) exitWith { false }; +if !(_isCeoSlot) exitWith { false }; private _display = createDialog ["RscPmcMissionSetup", true]; if (isNull _display) exitWith { false }; @@ -46,7 +46,7 @@ _control ctrlWebBrowserAction ["LoadFile", "ui\_site\index.html"]; }; private _display = uiNamespace getVariable ["RscPmcMissionSetup", displayNull]; - if (!isNull _display) then { + if !(isNull _display) then { closeDialog 1; }; }; diff --git a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_setupMenu_applySettings.sqf b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_setupMenu_applySettings.sqf index cdef74a..bccd1df 100644 --- a/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_setupMenu_applySettings.sqf +++ b/arma/forge_pmc_simulator.Tanoa/functions/missionSetup/fn_setupMenu_applySettings.sqf @@ -12,7 +12,7 @@ * Public: No */ -if (!isServer) exitWith {}; +if !(isServer) exitWith {}; params [ ["_overrides", createHashMap, [createHashMap]] @@ -56,13 +56,27 @@ private _timeMax = ["timeLimitMax", 1800] call _paramOrDefault; // Enemy faction selection falls back to Params::enemyFaction when the setup UI // is closed without pressing Start Mission. -private _enemyFactionParam = ["enemyFaction", 7] call BIS_fnc_getParamValue; +private _enemyFactionParam = ["enemyFaction", 6] call BIS_fnc_getParamValue; private _enemyFaction = _overrides getOrDefault ["enemyFaction", ""]; +private _fallbackEnemyFaction = "IND_G_F"; +private _factionOptions = [] call forge_pmc_fnc_getEnemyFactionOptions; +private _hasFallbackFaction = false; +{ + _x params ["_optionFaction", "_display", "_value"]; + if (_optionFaction isEqualTo _fallbackEnemyFaction) exitWith { + _hasFallbackFaction = true; + }; +} forEach _factionOptions; + +if (!_hasFallbackFaction && { _factionOptions isNotEqualTo [] }) then { + _fallbackEnemyFaction = (_factionOptions select 0) param [0, _fallbackEnemyFaction]; +}; + if (_enemyFaction isEqualTo "") then { if (_enemyFactionParam isEqualTo -1) then { - _enemyFactionParam = 7; + _enemyFactionParam = 6; }; - _enemyFaction = [_enemyFactionParam, "IND_G_F"] call forge_pmc_fnc_resolveEnemyFactionParam; + _enemyFaction = [_enemyFactionParam, _fallbackEnemyFaction] call forge_pmc_fnc_resolveEnemyFactionParam; } else { _enemyFactionParam = _enemyFaction; }; diff --git a/arma/forge_pmc_simulator.Tanoa/initPlayerLocal.sqf b/arma/forge_pmc_simulator.Tanoa/initPlayerLocal.sqf index 2c2879f..c7b803b 100644 --- a/arma/forge_pmc_simulator.Tanoa/initPlayerLocal.sqf +++ b/arma/forge_pmc_simulator.Tanoa/initPlayerLocal.sqf @@ -4,7 +4,7 @@ private _ceoUnit = missionNamespace getVariable ["ceo", objNull]; private _isCeoSlot = - (!isNull _ceoUnit && { player isEqualTo _ceoUnit }) || + !(isNull _ceoUnit && { player isEqualTo _ceoUnit }) || { toLowerANSI (vehicleVarName player) isEqualTo "ceo" }; if (_isCeoSlot && { !(missionNamespace getVariable ["forge_pmc_missionSettingsApplied", false]) }) then {