Skip to content

Commit

Permalink
Add UI and supporting code to set custom loadouts for rebel troops (#…
Browse files Browse the repository at this point in the history
…3392)

* Add UI and supporting code to set custom loadouts for rebel troops

* Fix recruit-spam bug
  • Loading branch information
jaj22 authored Dec 6, 2024
1 parent 8c9bb2f commit e977748
Show file tree
Hide file tree
Showing 20 changed files with 660 additions and 129 deletions.
2 changes: 2 additions & 0 deletions A3A/addons/core/CfgFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class CfgFunctions
class Ammunition {
file = QPATHTOFOLDER(functions\Ammunition);
class ACEpvpReDress {};
class addPrimaryAndMags {};
class allMagazines {};
class ammunitionTransfer {};
class arsenalManage {};
Expand All @@ -92,6 +93,7 @@ class CfgFunctions
class launcherInfo {};
class loot {};
class randomRifle {};
class setRebelLoadouts {};
class transfer {};
class unlockEquipment {};
class vehicleSort {};
Expand Down
6 changes: 3 additions & 3 deletions A3A/addons/core/dialogs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1774,13 +1774,13 @@ class commander_comm {
class 10slots_R2: A3A_core_BattleMenuRedButton
{
idc = -1;
text = "";
text = "Customize loadouts"; //$STR_antistasi_dialogs_commander_comm_customLoadouts;
x = 0.482498 * safezoneW + safezoneX;
y = 0.365981 * safezoneH + safezoneY;
w = 0.175015 * safezoneW;
h = 0.0560125 * safezoneH;
tooltip = "";
action = "";
tooltip = "Customize loadouts for rebel AI troops"; //$STR_antistasi_dialogs_commander_comm_customLoadouts_tooltip;
action = "if (player == theBoss) then {closeDialog 0; createDialog ""A3A_customLoadoutsDialog""} else {[""Custom Loadouts"", ""Only commanders have access to this function""] call A3A_fnc_customHint}";
};
class 10slots_L3: A3A_core_BattleMenuRedButton
{
Expand Down
68 changes: 68 additions & 0 deletions A3A/addons/core/functions/Ammunition/fn_addPrimaryAndMags.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Equip rebel unit with primary weapon or handgun
Adds magazines by mass. Uses default magazine of selected weapon
Parameters:
0. <OBJECT> Rebel unit to equip with primary weapon.
1. <STRING> Weapon classname
2. <STRING> Optic type preference ("OpticsClose", "OpticsMid", "OpticsLong")
3. <NUMBER> Total mass of primary magazines to add to inventory.
4. <NUMBER> Optional: Number of GL mags to add if secondary.
Returns:
Nothing
Environment:
Scheduled, any machine
*/

#include "..\..\script_component.hpp"
FIX_LINE_NUMBERS()

params ["_unit", "_weapon", "_opticType", "_totalMagWeight", ["_glMags", 5]];

call A3A_fnc_fetchRebelGear; // Send current version of rebelGear from server if we're out of date

// Probably shouldn't ever be executed
if !(primaryWeapon _unit isEqualTo "") then {
if (_weapon == primaryWeapon _unit) exitWith {};
private _magazines = getArray (configFile / "CfgWeapons" / (primaryWeapon _unit) / "magazines");
{_unit removeMagazines _x} forEach _magazines; // Broken, doesn't remove mags globally. Pain to fix.
_unit removeWeapon (primaryWeapon _unit);
};

private _categories = _weapon call A3A_fnc_equipmentClassToCategories;

if ("GrenadeLaunchers" in _categories && {"Rifles" in _categories} ) then {
// lookup real underbarrel GL magazine, because not everything is 40mm
private _config = configFile >> "CfgWeapons" >> _weapon;
private _glmuzzle = getArray (_config >> "muzzles") select 1; // guaranteed by category
_glmuzzle = configName (_config >> _glmuzzle); // bad-case fix. compatibleMagazines is case-sensitive as of 2.12
private _glmag = compatibleMagazines [_weapon, _glmuzzle] select 0;
_unit addMagazines [_glmag, 5];
};

private _magazine = compatibleMagazines _weapon select 0;
private _magweight = 5 max getNumber (configFile >> "CfgMagazines" >> _magazine >> "mass");

_unit addWeapon _weapon;
if ("Handguns" in _categories) then {
_unit addHandgunItem _magazine;
} else {
_unit addPrimaryWeaponItem _magazine;
};
_unit addMagazines [_magazine, round (random 0.5 + _totalMagWeight / _magWeight)];


private _compatOptics = A3A_rebelOpticsCache get _weapon;
if (isNil "_compatOptics") then {
private _compatItems = compatibleItems _weapon;

_compatOptics = _compatItems arrayIntersect (A3A_rebelGear get _opticType);
if (_compatOptics isEqualTo [] and _opticType != "OpticsClose") then {
private _fallbackType = ["OpticsClose", "OpticsMid"] select (_opticType == "OpticsLong");
_compatOptics = _compatItems arrayIntersect (A3A_rebelGear get _fallbackType);
};
A3A_rebelOpticsCache set [_weapon, _compatOptics];
};
if (_compatOptics isNotEqualTo []) then { _unit addPrimaryWeaponItem (selectRandom _compatOptics) };
31 changes: 21 additions & 10 deletions A3A/addons/core/functions/Ammunition/fn_fetchRebelGear.sqf
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
/*
Ensures A3A_rebelGear and related caches are valid and updated for equipping rebel AIs
Ensures A3A_rebelGear, A3A_rebelLoadouts and related caches are locally valid and updated for equipping rebel AIs
Parameters:
None, returns nothing
Environment:
Scheduled, executed anywhere
Scheduled, executed locally
*/

#include "..\..\script_component.hpp"
FIX_LINE_NUMBERS()

// Send current version of rebelGear from server if we're out of date
if (!isNil "A3A_rebelGear" and { A3A_rebelGear get "Version" == A3A_rebelGearVersion}) exitWith {};
private _updateGear = isNil "A3A_rebelGear" or { A3A_rebelGear get "Version" != A3A_rebelGearVersion };
private _updateLoadouts = isNil "A3A_rebelLoadouts" or { A3A_rebelLoadouts get "Version" != A3A_rebelLoadoutsVersion };
if !(_updateGear or _updateLoadouts) exitWith {};

Info("Fetching new version of rebelGear data...");
[clientOwner, "A3A_rebelGear"] remoteExecCall ["publicVariableClient", 2];
waitUntil { sleep 1; !isNil "A3A_rebelGear" and { A3A_rebelGear get "Version" == A3A_rebelGearVersion } };
Info("New version of rebelGear data received");
if (_updateGear) then {
// Create/clear local accessory-compatibility caches
A3A_rebelOpticsCache = createHashMap;
A3A_rebelFlashlightsCache = createHashMap;

// Create/clear local accessory-compatibility caches
A3A_rebelOpticsCache = createHashMap;
A3A_rebelFlashlightsCache = createHashMap;
Info("Fetching new version of rebelGear data...");
[clientOwner, "A3A_rebelGear"] remoteExecCall ["publicVariableClient", 2];
};
if (_updateLoadouts) then {
Info("Fetching new version of rebelLoadouts data...");
[clientOwner, "A3A_rebelLoadouts"] remoteExecCall ["publicVariableClient", 2];
};
waitUntil { sleep 0.5;
!isNil "A3A_rebelGear" and { A3A_rebelGear get "Version" == A3A_rebelGearVersion }
and !isNil "A3A_rebelLoadouts" and { A3A_rebelLoadouts get "Version" == A3A_rebelLoadoutsVersion };
};
Info("New version of rebelGear data received");
18 changes: 15 additions & 3 deletions A3A/addons/core/functions/Ammunition/fn_generateRebelGear.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Info("Started updating A3A_rebelGear");

// Base weight mappings, MIN->0, MAX->1
#define ITEM_MIN 10
#define ITEM_MAX 50
#define ITEM_MAX 40

private _fnc_addItemNoUnlocks = {
params ["_array", "_class", "_amount"];
Expand Down Expand Up @@ -108,8 +108,18 @@ _rebelGear set ["RocketLaunchers", _rlaunchers];
_rebelGear set ["MissileLaunchersAT", _mlaunchersAT];
_rebelGear set ["MissileLaunchersAA", _mlaunchersAA];

// Function to phase in armour & helmets gradually
private _fnc_addEmptyEntry = {
params ["_list", "_targWeight"];
private _weight = 0;
{ if (_x isEqualType 0) then { _weight = _weight + _x } } forEach _list;
if (_weight >= _targWeight) exitWith {};
_list pushBack "";
_list pushBack (_targWeight - _weight);
};

// Vest filtering
private _avests = ["", [1.5,0.5] select (minWeaps < 0)]; // blank entry to phase in armour use gradually
private _avests = [];
private _uvests = [];
{
_x params ["_class", "_amount"];
Expand All @@ -118,11 +128,12 @@ private _uvests = [];
[_array, _class, _amount] call _fnc_addItem;
} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_VEST);

[_avests, 1] call _fnc_addEmptyEntry;
_rebelGear set ["ArmoredVests", _avests];
_rebelGear set ["CivilianVests", _uvests];

// Helmet filtering
private _aheadgear = ["", [1.5,0.5] select (minWeaps < 0)]; // blank entry to phase in armour use gradually
private _aheadgear = []; //"", [1.5,0.5] select (minWeaps < 0)]; // blank entry to phase in armour use gradually
private _uheadgear = [];
{
_x params ["_class", "_amount"];
Expand All @@ -131,6 +142,7 @@ private _uheadgear = [];
[_array, _class, _amount] call _fnc_addItem;
} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_HEADGEAR);

[_aheadgear, 1] call _fnc_addEmptyEntry;
_rebelGear set ["ArmoredHeadgear", _aheadgear];
//_rebelGear set ["CosmeticHeadgear", _uheadgear]; // not used, rebels have template-defined basic headgear

Expand Down
61 changes: 5 additions & 56 deletions A3A/addons/core/functions/Ammunition/fn_randomRifle.sqf
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
/*
Equip unit with random weapon of preferred type using A3A_rebelGear
Adds magazines by mass. Uses default magazine of selected weapon
Select random rebel weapon of preferred type using A3A_rebelGear
Parameters:
0. <OBJECT> Rebel unit to equip with primary weapon.
1. <STRING> Preferred weapon type ("Rifles", "MachineGuns" etc).
2. <NUMBER> Optional, total mass of carried magazines to add.
0. <STRING> Preferred weapon type ("Rifles", "MachineGuns" etc).
Returns:
Nothing
<STRING> Weapon classname selected
Environment:
Scheduled, any machine
Expand All @@ -17,7 +14,7 @@ Environment:
#include "..\..\script_component.hpp"
FIX_LINE_NUMBERS()

params ["_unit", "_weaponType", ["_totalMagWeight", 50]];
params ["_weaponType"];

call A3A_fnc_fetchRebelGear; // Send current version of rebelGear from server if we're out of date

Expand All @@ -36,52 +33,4 @@ if (_pool isEqualTo []) then {
};
};
private _weapon = selectRandomWeighted _pool;

// Probably shouldn't ever be executed
if !(primaryWeapon _unit isEqualTo "") then {
if (_weapon == primaryWeapon _unit) exitWith {};
private _magazines = getArray (configFile / "CfgWeapons" / (primaryWeapon _unit) / "magazines");
{_unit removeMagazines _x} forEach _magazines; // Broken, doesn't remove mags globally. Pain to fix.
_unit removeWeapon (primaryWeapon _unit);
};

private _categories = _weapon call A3A_fnc_equipmentClassToCategories;

if ("GrenadeLaunchers" in _categories && {"Rifles" in _categories} ) then {
// lookup real underbarrel GL magazine, because not everything is 40mm
private _config = configFile >> "CfgWeapons" >> _weapon;
private _glmuzzle = getArray (_config >> "muzzles") select 1; // guaranteed by category
_glmuzzle = configName (_config >> _glmuzzle); // bad-case fix. compatibleMagazines is case-sensitive as of 2.12
private _glmag = compatibleMagazines [_weapon, _glmuzzle] select 0;
_unit addMagazines [_glmag, 5];
};

private _magazine = compatibleMagazines _weapon select 0;
private _magweight = 5 max getNumber (configFile >> "CfgMagazines" >> _magazine >> "mass");

_unit addWeapon _weapon;
if ("Handguns" in _categories) then {
_unit addHandgunItem _magazine;
} else {
_unit addPrimaryWeaponItem _magazine;
};
_unit addMagazines [_magazine, round (random 0.5 + _totalMagWeight / _magWeight)];


private _compatOptics = A3A_rebelOpticsCache get _weapon;
if (isNil "_compatOptics") then {
private _compatItems = compatibleItems _weapon; // cached, should be fast
_compatOptics = _compatItems arrayIntersect call {
if (_weaponType in ["Rifles", "MachineGuns"]) exitWith { A3A_rebelGear get "OpticsMid" };
if (_weaponType == "SniperRifles") exitWith { A3A_rebelGear get "OpticsLong" };
A3A_rebelGear get "OpticsClose";
};
if (_compatOptics isEqualTo []) then {
_compatOptics = _compatItems arrayIntersect call {
if (_weaponType in ["Rifles", "MachineGuns"]) exitWith { A3A_rebelGear get "OpticsClose" };
A3A_rebelGear get "OpticsMid";
};
};
A3A_rebelOpticsCache set [_weapon, _compatOptics];
};
if (_compatOptics isNotEqualTo []) then { _unit addPrimaryWeaponItem (selectRandom _compatOptics) };
_weapon;
17 changes: 17 additions & 0 deletions A3A/addons/core/functions/Ammunition/fn_setRebelLoadouts.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
Writes custom rebel loadouts structure & updates version number
Parameters:
<HASHMAP> Custom rebel loadouts structure
Environment:
Unscheduled, server
*/

params ["_newLoadouts"];

A3A_rebelLoadoutsVersion = time;
_newLoadouts set ["Version", A3A_rebelLoadoutsVersion];
A3A_rebelLoadouts = _newLoadouts;

publicVariable "A3A_rebelLoadoutsVersion";
3 changes: 2 additions & 1 deletion A3A/addons/core/functions/Base/fn_initPetros.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ removeGoggles petros;
private _vest = selectRandomWeighted (A3A_rebelGear get "ArmoredVests");
if (_vest == "") then { _vest = selectRandomWeighted (A3A_rebelGear get "CivilianVests") };
petros addVest _vest;
[petros, "Rifles"] call A3A_fnc_randomRifle;
private _weapon = ["Rifles"] call A3A_fnc_randomRifle;
[petros, _weapon, "OpticsMid", 50] call A3A_fnc_addPrimaryAndMags;
petros selectWeapon (primaryWeapon petros);

if (petros == leader group petros) then {
Expand Down
Loading

0 comments on commit e977748

Please sign in to comment.