diff --git a/A3A/addons/core/CfgFunctions.hpp b/A3A/addons/core/CfgFunctions.hpp index b30930c1a1..09de4d7d7f 100644 --- a/A3A/addons/core/CfgFunctions.hpp +++ b/A3A/addons/core/CfgFunctions.hpp @@ -90,6 +90,7 @@ class CfgFunctions class generateRebelGear {}; class getRadio {}; class hasARadio {}; + class itemArrayWeight {}; class itemConfig {}; class itemConfigMass {}; class itemSort {}; diff --git a/A3A/addons/core/functions/Ammunition/fn_generateRebelGear.sqf b/A3A/addons/core/functions/Ammunition/fn_generateRebelGear.sqf index d81fa93f36..a8f1639542 100644 --- a/A3A/addons/core/functions/Ammunition/fn_generateRebelGear.sqf +++ b/A3A/addons/core/functions/Ammunition/fn_generateRebelGear.sqf @@ -19,173 +19,163 @@ Info("Started updating A3A_rebelGear"); #define ITEM_MAX 50 private _fnc_addItemNoUnlocks = { - params ["_array", "_class", "_amount"]; - if (_amount < 0) exitWith { _array append [_class, 1] }; + params ["_array", "_class", "_amount", ["_arrayWeight", 1]]; + if (_amount < 0) exitWith { _array append [_class, _arrayWeight] }; if (_amount <= ITEM_MIN) exitWith {}; _array pushBack _class; - _array pushBack linearConversion [ITEM_MIN, ITEM_MAX, _amount, 0, 1, true]; + _array pushBack (linearConversion [ITEM_MIN, ITEM_MAX, _amount, 0, 1, true] * _arrayWeight); // multiply weight (preference) by ratio of amount of item to max amount of that item such that items rebels have more of are more likely to be selected }; private _fnc_addItemUnlocks = { - params ["_array", "_class", "_amount"]; - if (_amount < 0) exitWith { _array append [_class, 1] }; + params ["_array", "_class", "_amount", ["_arrayWeight", 1]]; + if (_amount < 0) exitWith { _array append [_class, _arrayWeight] }; }; private _fnc_addItem = [_fnc_addItemUnlocks, _fnc_addItemNoUnlocks] select (minWeaps < 0); // Work with temporary array so that we're not transferring partials -private _rebelGear = createHashMap; - -// Primary weapon filtering -private _rifle = []; -private _smg = []; -private _shotgun = []; -private _sniper = []; -private _mg = []; -private _gl = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - - call { - if ("GrenadeLaunchers" in _categories) exitWith { [_gl, _class, _amount] call _fnc_addItem }; // call before rifles - if ("Rifles" in _categories) exitWith { [_rifle, _class, _amount/2] call _fnc_addItem }; - if ("SniperRifles" in _categories) exitWith { [_sniper, _class, _amount] call _fnc_addItem }; - if ("MachineGuns" in _categories) exitWith { [_mg, _class, _amount] call _fnc_addItem }; - if ("SMGs" in _categories) exitWith { [_smg, _class, _amount] call _fnc_addItem }; - if ("Shotguns" in _categories) exitWith { [_shotgun, _class, _amount] call _fnc_addItem }; - }; -} forEach (jna_dataList select IDC_RSCDISPLAYARSENAL_TAB_PRIMARYWEAPON); - -_rebelGear set ["Rifles", _rifle]; -_rebelGear set ["SMGs", _smg]; -_rebelGear set ["Shotguns", _shotgun]; -_rebelGear set ["MachineGuns", _mg]; -_rebelGear set ["SniperRifles", _sniper]; -_rebelGear set ["GrenadeLaunchers", _gl]; - -// Secondary weapon filtering -private _rlaunchers = []; -private _mlaunchersAT = []; -private _mlaunchersAA = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; -/* if !("Disposable" in _categories) then { - private _magcount = _class call _fnc_magCount; - _amount = _amount min (_magcount/2); - };*/ - - if ("RocketLaunchers" in _categories) then { [_rlaunchers, _class, _amount] call _fnc_addItem; continue }; - if ("MissileLaunchers" in _categories) then { - if ("AA" in _categories) exitWith { [_mlaunchersAA, _class, _amount] call _fnc_addItemNoUnlocks }; - if ("AT" in _categories) exitWith { [_mlaunchersAT, _class, _amount] call _fnc_addItemNoUnlocks }; - }; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_SECONDARYWEAPON); - -_rebelGear set ["RocketLaunchers", _rlaunchers]; -_rebelGear set ["MissileLaunchersAT", _mlaunchersAT]; -_rebelGear set ["MissileLaunchersAA", _mlaunchersAA]; - -// Vest filtering -private _avests = ["", [1.5,0.5] select (minWeaps < 0)]; // blank entry to phase in armour use gradually -private _uvests = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - private _array = [_uvests, _avests] select ("ArmoredVests" in _categories); - [_array, _class, _amount] call _fnc_addItem; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_VEST); - -_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 _uheadgear = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - private _array = [_uheadgear, _aheadgear] select ("ArmoredHeadgear" in _categories); - [_array, _class, _amount] call _fnc_addItem; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_HEADGEAR); - -_rebelGear set ["ArmoredHeadgear", _aheadgear]; -//_rebelGear set ["CosmeticHeadgear", _uheadgear]; // not used, rebels have template-defined basic headgear - -// Backpack filtering -private _backpacks = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - if ("BackpacksCargo" in _categories) then { [_backpacks, _class, _amount] call _fnc_addItem }; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_BACKPACK); - -_rebelGear set ["BackpacksCargo", _backpacks]; +private _rebelGear = createHashMapFromArray [ + ["Rifles", []], + ["SMGs", []], + ["Shotguns", []], + ["MachineGuns", []], + ["SniperRifles", []], + ["GrenadeLaunchers", []], + + ["RocketLaunchers", []], + ["MissileLaunchersAT", []], + ["MissileLaunchersAA", []], + + ["ArmoredVests", ["", [1.5, 0.5] select (minWeaps < 0)]], + ["CivilianVests", []], + ["ArmoredHeadgear", ["", [1.5, 0.5] select (minWeaps < 0)]], + ["BackpacksCargo", []], + + ["NVGs", ["", 0.5]], + ["Radios", []], + ["MineDetectors", []], + ["Toolkits", []], + ["Medikits", []], + + ["SmokeGrenades", []], + ["Grenades", []], + ["ExplosiveCharges", []], + + ["OpticsClose", []], + ["OpticsMid", []], + ["OpticsLong", []], + ["OpticsAll", []], + ["LightAttachments", []] +]; -// NVG filtering -private _nvgs = ["", 0.5]; // blank entry for phase-in { - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - if !("NVGThermal" in _categories) then { [_nvgs, _class, _amount] call _fnc_addItem }; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_NVGS); - -_rebelGear set ["NVGs", _nvgs]; - -// Unfiltered stuff (just radios atm? could add GPS) -private _radios = []; -{ [_radios, _x#0, _x#1] call _fnc_addItem } forEach (jna_dataList select IDC_RSCDISPLAYARSENAL_TAB_RADIO); -_rebelGear set ["Radios", _radios]; - -// Misc items. Don't really need weighting but whatever -private _minedetectors = []; -private _toolkits = []; -private _medikits = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - call { - if ("MineDetectors" in _categories) exitWith { [_minedetectors, _class, _amount] call _fnc_addItem }; - if ("Toolkits" in _categories) exitWith { [_toolkits, _class, _amount] call _fnc_addItem }; - if ("Medikits" in _categories) exitWith { [_medikits, _class, _amount] call _fnc_addItem }; - }; -} forEach (jna_dataList select IDC_RSCDISPLAYARSENAL_TAB_CARGOMISC); - -_rebelGear set ["MineDetectors", _minedetectors]; -_rebelGear set ["Toolkits", _toolkits]; -_rebelGear set ["Medikits", _medikits]; - -// Hand grenades -private _smokes = []; -private _nades = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - call { - if ("SmokeGrenades" in _categories) exitWith { [_smokes, _class, _amount] call _fnc_addItem }; - if ("Grenades" in _categories) exitWith { [_nades, _class, _amount] call _fnc_addItem }; - }; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_CARGOTHROW); - -_rebelGear set ["SmokeGrenades", _smokes]; -_rebelGear set ["Grenades", _nades]; - -// Explosives. Could add mines but don't want them atm. -private _charges = []; -{ - _x params ["_class", "_amount"]; - private _categories = _class call A3A_fnc_equipmentClassToCategories; - if ("ExplosiveCharges" in _categories) then { [_charges, _class, _amount] call _fnc_addItemNoUnlocks }; -} forEach (jna_datalist select IDC_RSCDISPLAYARSENAL_TAB_CARGOPUT); - -_rebelGear set ["ExplosiveCharges", _charges]; + { + _x params ["_class", "_amount"]; + private _categories = _class call A3A_fnc_equipmentClassToCategories; + private _mainCategory = _categories select 0; + + switch (_mainCategory) do { + // Primary Weapons + case "Rifles"; + case "SniperRifles"; + case "GrenadeLaunchers"; + case "MachineGuns"; + case "SMGs"; + case "Shotguns"; + case "PrimaryWeaponsCatchAll" : { + _arrayWeight = [_class, _categories] call A3A_fnc_itemArrayWeight; + _array = _rebelGear getOrDefault [[_mainCategory, "GrenadeLaunchers"] select ("GrenadeLaunchers" in _categories), [], true]; + [_array, _class, _amount, _arrayWeight] call _fnc_addItem; + }; + + // Secondary Weapons + case "RocketLaunchers": { + _arrayWeight = [_class, _categories] call A3A_fnc_itemArrayWeight; + _array = _rebelGear getOrDefault ["RocketLaunchers", [], true]; + [_array, _class, _amount, _arrayWeight] call _fnc_addItem; + }; + case "MissileLaunchers": { + if ("AA" in _categories) exitWith { + _array = _rebelGear getOrDefault ["MissileLaunchersAA", [], true]; + [_array, _class, _amount] call _fnc_addItemNoUnlocks + }; + if ("AT" in _categories) exitWith { + _array = _rebelGear getOrDefault ["MissileLaunchersAT", [], true]; + [_array, _class, _amount] call _fnc_addItemNoUnlocks + }; + }; + + // Handguns + case "Handguns": { + _arrayWeight = [_class, _categories] call A3A_fnc_itemArrayWeight; + _array = _rebelGear getOrDefault ["Handguns", [], true]; + [_array, _class, _amount, _arrayWeight] call _fnc_addItem; + }; + + // Gear + case "Vests": { + _array = _rebelGear getOrDefault [["CivilianVests", "ArmoredVests"] select ("ArmoredVests" in _categories), ["", [1.5,0.5] select (minWeaps < 0)], true]; + [_array, _class, _amount] call _fnc_addItem; + }; + case "Headgear": { + //_array = _rebelGear getOrDefault [["CosmeticHeadgear", "ArmoredHeadgear"] select ("ArmoredHeadgear" in _categories), ["", [1.5,0.5] select (minWeaps < 0)], true]; // not used, rebels have template-defined basic headgear + _array = _rebelGear getOrDefault ["ArmoredHeadgear", [], true]; + if ("ArmoredHeadgear" in _categories) then { [_array, _class, _amount] call _fnc_addItem }; + }; + case "Backpacks": { + _array = _rebelGear getOrDefault ["BackpacksCargo", [], true]; + if ("BackpacksCargo" in _categories) then { [_array, _class, _amount] call _fnc_addItem }; + }; + + // Items + case "NVGs": { + _array = _rebelGear getOrDefault ["NVGs", ["", 0.5], true]; + if !("NVGThermal" in _categories) then { [_array, _class, _amount] call _fnc_addItem }; + }; + case "Radios"; + case "MineDetectors"; + case "Toolkits"; + case "Medikits"; + case "CargoMiscCatchAll": { + _array = _rebelGear getOrDefault [_mainCategory, [], true]; + [_array, _class, _amount] call _fnc_addItem; + }; + + default { + // Grenades / Explosives + if ("SmokeGrenades" in _categories) exitWith { + _array = _rebelGear getOrDefault ["SmokeGrenades", [], true]; + [_array, _class, _amount] call _fnc_addItem + }; + if ("Grenades" in _categories) exitWith { + _array = _rebelGear getOrDefault ["Grenades", [], true]; + [_array, _class, _amount] call _fnc_addItem + }; + if ("ExplosiveCharges" in _categories) exitWith { + _array = _rebelGear getOrDefault ["ExplosiveCharges", [], true]; + [_array, _class, _amount] call _fnc_addItemNoUnlocks + }; + }; + }; + } forEach (jna_datalist select _x); +} forEach [ + IDC_RSCDISPLAYARSENAL_TAB_PRIMARYWEAPON, + IDC_RSCDISPLAYARSENAL_TAB_SECONDARYWEAPON, + IDC_RSCDISPLAYARSENAL_TAB_VEST, + IDC_RSCDISPLAYARSENAL_TAB_HEADGEAR, + IDC_RSCDISPLAYARSENAL_TAB_BACKPACK, + IDC_RSCDISPLAYARSENAL_TAB_NVGS, + IDC_RSCDISPLAYARSENAL_TAB_RADIO, + IDC_RSCDISPLAYARSENAL_TAB_CARGOMISC, + IDC_RSCDISPLAYARSENAL_TAB_CARGOTHROW, + IDC_RSCDISPLAYARSENAL_TAB_CARGOPUT +]; // Optic filtering. No weighting because of weapon compatibilty complexity -private _opticClose = []; -private _opticMid = []; -private _opticLong = []; +private _opticClose = _rebelGear getOrDefault ["OpticsClose", [], true]; +private _opticMid = _rebelGear getOrDefault ["OpticsMid", [], true]; +private _opticLong = _rebelGear getOrDefault ["OpticsLong", [], true]; private _midCount = 0; { _x params ["_class", "_amount"]; @@ -215,22 +205,16 @@ if (_midCount < ITEM_MAX*2) then { }; }; -_rebelGear set ["OpticsClose", _opticClose]; -_rebelGear set ["OpticsMid", _opticMid]; -_rebelGear set ["OpticsLong", _opticLong]; _rebelGear set ["OpticsAll", _opticClose + _opticMid + _opticLong]; // for launchers // Light attachments, also no weights because of weapon compat -private _lights = []; { _x params ["_class", "_amount"]; if (_amount > 0 and {minWeaps > 0 or _amount < ITEM_MIN}) then { continue }; private _categories = _class call A3A_fnc_equipmentClassToCategories; - if ("LightAttachments" in _categories) then { _lights pushBack _class }; + if ("LightAttachments" in _categories) then { (_rebelGear getOrDefault ["LightAttachments", [], true]) pushBack _class }; } forEach (jna_dataList select IDC_RSCDISPLAYARSENAL_TAB_ITEMACC); -_rebelGear set ["LightAttachments", _lights]; - // Update everything while unscheduled so that version numbers match isNil { A3A_rebelGearVersion = time; diff --git a/A3A/addons/core/functions/Ammunition/fn_itemArrayWeight.sqf b/A3A/addons/core/functions/Ammunition/fn_itemArrayWeight.sqf new file mode 100644 index 0000000000..38a41053e2 --- /dev/null +++ b/A3A/addons/core/functions/Ammunition/fn_itemArrayWeight.sqf @@ -0,0 +1,79 @@ +/** + Calculates weight (preference - for use in weighted list(s)) of given item based on attributes important to that item type. + Currently supports weapon item types, but is structured to calculate additional types in the future. + + Author: jwoodruff40 + + Uses some concepts (and particularly _impact calculation) from ACE. + Thanks ACE! + (https://github.com/acemod/ACE3) + + Params: + _class - ( String ) - Class of the item to calculate weight for. + _categories - ( Array (of Strings) ) - Categories the item fits in to, generated by _fn_equipmentClassToCategories + + Returns: + _arrayWeight - ( Number ) - weight for use in weighted list / selection by selectRandomWeighted. +**/ + +#include "..\..\script_component.hpp" +FIX_LINE_NUMBERS() + +params ["_class", ["_categories", []]]; + +private _config = _class call A3A_fnc_itemConfig; + +// Total "score" (array weight) of the item based on calculated properties +private _arrayWeight = 1; // in case this function is called with an itemType without custom weighting setup or without an item type, just use default weight (but this shouldn't be called unless you're attempting to change this behavior) + +if ("Weapons" in _categories) then { + private _magcfg = getArray (_config >> "Magazines") # 0 call A3A_fnc_itemConfig; + private _ammocfg = getText (_magcfg >> "ammo") call A3A_fnc_itemConfig; + private _firemode = getArray (_config >> "modes") # 0; // primary firemode ("SINGLE", "FULLAUTO", etc) + private _modecfg = [_config >> _firemode, _config] select (_firemode == "this"); + + private _weight = (_config call A3A_fnc_itemConfigMass); // Mass / Weight + private _accuracy = getNumber (_modecfg >> "dispersion"); // Dispersion / Accuracy + private _reloadtime = getNumber (_modecfg >> "reloadTime"); + private _rof = if (_reloadTime == 0) then {0} else {1 / _reloadTime}; + private _magcap = getNumber (_magcfg >> "count"); // Mag Capacity + private _hit = getNumber ( _ammocfg >> "hit"); + private _basevel = getNumber (_config >> "initSpeed"); + private _magvel = getNumber (_magcfg >> "initSpeed"); + private _muzvel = if (_basevel > 0) then { _basevel } else { if (_basevel == 0) then { _magvel } else { abs _basevel * _magvel } }; + private _impact = sqrt (_hit ^ 2 * _muzvel); // impact based on ACE3 concept combining hit / damage and velocity of projectile; modified from ACE3\addons\ACE_ARSENAL\functions\fnc_statBarStatement_impact.sqf + private _caliber = getNumber (_ammocfg >> "caliber"); // Unintuitively by name, this is a penetration multiplier. More useful for rockets / missiles than bullets. + private _effrange = 0; // not always reliable (hence not used in most weapons). Iterates through all firemodes, so probably doesn't need to be run for every weapon type. + if (_firemode == "this") then { + _effrange = getNumber (_config >> "MidRange") * getNumber (_config >> "MidRangeProbab"); + } else { + { + private _range = getNumber (_config >> _x >> "MidRange") * getNumber (_config >> _x >> "MidRangeProbab"); + if (_range > _effrange) then { _effrange = _range }; + } forEach getArray (_config >> "modes"); + }; + private _isDisposable = [0, 20] select ("Disposable" in _categories); + + switch (_categories select 0) do { + // Calculate _arrayWeight based on item attributes above, and scaling them as needed (which attributes and how they're scaled dependent on item type) + case "Rifles": { _arrayWeight = round ((_accuracy * 10000) + _rof + _magcap + (_impact / 30) - (_weight / 5)) }; // Rifles. Blend of most attributes. + case "SniperRifles" : { _arrayWeight = round ((_accuracy * 50000) + (_effrange / 15) + (_impact / 30) + _rof - (_weight/3)) }; // Favor accuracy and range more. RoF still factored due to generally shorter range / more frenetic AI engagements. + case "GrenadeLaunchers"; + case "MachineGuns"; + case "SMGs"; + case "Shotguns"; + case "PrimaryWeaponsCatchAll" : { _arrayWeight = round ((_accuracy * 10000) + _rof + _magcap + (_impact / 30) - (_weight / 5)) }; // Primary weapons catchall. Same as Rifles. + case "RocketLaunchers" : { _arrayWeight = round ((_impact * _caliber / 20) + (_effrange / 4) - (_weight / 2) - _isDisposable) }; // Multiplies impact by caliber (penetration) to favor launchers better against vehicles. + case "MissileLaunchersAT"; + case "MissileLaunchersAA"; + case "SecondaryWeaponsCatchAll" : { _arrayWeight = 1 }; // placeholder catchall for launchers + case "Handguns" : { _arrayWeight = round (_rof + _magcap + (_impact * 20000) - (_weight * 2.5)) }; // Handguns. Array weight favors weapons with high RoF, Mag capacity, and impact force while heavily discriminating against weight, since it's a backup weapon. + case "ArmoredVests"; + case "CivilianVests"; + case "ArmoredHeadgear"; + case "Backpacks"; + case "GearCatchAll" : { _arrayWeight = 1 }; // placeholder catchall for gear (vests, helmets, backpacks) + }; +}; + +[1, _arrayWeight] select (_arrayWeight > 0); diff --git a/A3A/addons/core/functions/Ammunition/fn_itemConfig.sqf b/A3A/addons/core/functions/Ammunition/fn_itemConfig.sqf index a96e240e2c..aa35f70e5a 100644 --- a/A3A/addons/core/functions/Ammunition/fn_itemConfig.sqf +++ b/A3A/addons/core/functions/Ammunition/fn_itemConfig.sqf @@ -4,7 +4,7 @@ * Description: * Returns the config of an item from its class name * Params: - * _class - Class of the item to retrieve the mass value of. + * _class - Class of the item to retrieve the config of. * Returns: * Config of the item, or configNull if not found. * Example Usage: @@ -14,14 +14,12 @@ params ["_class"]; -private _config = configFile >> "CfgMagazines" >> _class; -if (isClass _config) exitWith { - _config -}; +private _rootclass = ["CfgAmmo", "CfgMagazines", "CfgWeapons"]; +private _config = configNull; -_config = configFile >> "CfgWeapons" >> _class; -if (isClass _config) exitWith { - _config -}; +{ + _config = configFile >> _x >> _class; + if (isClass _config) then { break }; +} forEach _rootclass; -configNull \ No newline at end of file +_config \ No newline at end of file diff --git a/A3A/addons/core/functions/REINF/fn_equipRebel.sqf b/A3A/addons/core/functions/REINF/fn_equipRebel.sqf index 2d558120db..654f5a7041 100644 --- a/A3A/addons/core/functions/REINF/fn_equipRebel.sqf +++ b/A3A/addons/core/functions/REINF/fn_equipRebel.sqf @@ -118,7 +118,20 @@ switch (true) do { if (_grenades isNotEqualTo []) then { _unit addMagazines [selectRandomWeighted _grenades, 2] }; if (_smokes isNotEqualTo []) then { _unit addMagazines [selectRandomWeighted _smokes, 1] }; - // could throw in some disposable launchers here... + // increase likelihood of rifleman getting a disposable launcher by war level (10% chance * war level) + if (random 10 < (tierWar / 2)) then { + private _rlaunchers = A3A_rebelGear get "RocketLaunchers"; + private _launcherPool = []; + + { + private _categories = _x call A3A_fnc_equipmentClassToCategories; + + if ("Disposable" in _categories) then {_launcherPool append [_x, _rlaunchers select (_rlaunchers find _x) + 1 ]}; + } forEach (_rlaunchers select {_x isEqualType ""}); + + private _launcher = selectRandomWeighted _launcherPool; + if !(isNil "_launcher") then { [_unit, _launcher, 100] call _fnc_addSecondaryAndMags }; + }; }; case (_unitType isEqualTo FactionGet(reb,"unitMG")): { [_unit, "MachineGuns", 150] call A3A_fnc_randomRifle;