diff --git a/components/functions.hpp b/components/functions.hpp index a9bea32c..f4e21d02 100644 --- a/components/functions.hpp +++ b/components/functions.hpp @@ -47,3 +47,12 @@ class ace_medical_feedback class effectUnconscious{}; }; }; + +class ace_gunbag +{ + class overrides + { + file = "components\gearscript\gunbag"; + class hasGunbag{}; + }; +}; diff --git a/components/gearScript/fn_applyGunbag.sqf b/components/gearScript/fn_applyGunbag.sqf new file mode 100644 index 00000000..fbcf2854 --- /dev/null +++ b/components/gearScript/fn_applyGunbag.sqf @@ -0,0 +1,49 @@ +#include "macros.hpp" + +params ["_unit", "_typeofUnit", "_gearVariant"]; + +_gearVariant = toLower _gearVariant; +_typeofUnit = toLower _typeofUnit; + +private _gunbagContents = GUNBAG_ITEM_DYNAMIC(_gearVariant,_typeofUnit); +private _gunbagVariableValue = []; + +if !(_gunbagContents isEqualTo []) then +{ + _gunbagContents params ["_attachments", "_muzzles", "_magazines", "_ammo", "_baseWeapon"]; + + private _gunbagMags = []; + { + _gunbagMags pushBack [_x, _ammo # _forEachIndex]; + + } forEach _magazines; + + _gunbagVariableValue = + [ + _baseWeapon, + _attachments, + _gunbagMags + ]; +}; + +// Wait a short while and apply gunbag contents. Avoids issue on spawn where ACE can potentially override intended value. +[ + // Script + { + params ["_unit", "_contents"]; + + if (_contents isEqualTo []) then + { + _contents = nil; + }; + + (backpackContainer _unit) setVariable ["ace_gunbag_gunbagWeapon", _contents, true]; + }, + + // Arguments + [_unit, _gunbagVariableValue], + + // Delay + 0.5 + +] call CBA_fnc_waitAndExecute; \ No newline at end of file diff --git a/components/gearScript/fn_assignGear.sqf b/components/gearScript/fn_assignGear.sqf index 1b8eeb17..2483e0b7 100644 --- a/components/gearScript/fn_assignGear.sqf +++ b/components/gearScript/fn_assignGear.sqf @@ -87,6 +87,7 @@ if (_gearVariant == "") exitWith }; [_unit, _typeOfUnit, _gearVariant] call f_fnc_applyLoadout; +[_unit, _typeOfUnit, _gearVariant] call f_fnc_applyGunbag; // ==================================================================================== diff --git a/components/gearScript/functions.hpp b/components/gearScript/functions.hpp index fe8b9113..102faec5 100644 --- a/components/gearScript/functions.hpp +++ b/components/gearScript/functions.hpp @@ -5,6 +5,7 @@ class gearScript class addItemToLoadoutContainer{}; class addLinkedItemToLoadout{}; class applyLoadout{}; + class applyGunbag{}; class applyLoadoutModifications{}; class assignGear{}; class createLoadoutLocker{}; @@ -45,3 +46,11 @@ class gearScript_zen class zen_createSupplyCrate_followUpDialog{}; class zen_createSupplyCrate_performAction{}; }; + +class gearScript_gunbag +{ + file = "components\gearScript\gunbag"; + class getWeaponStateFromClassName{}; + class setGunbagVariableFromArsenalExport{}; + class setGunbagVariableState{}; +}; diff --git a/components/gearScript/globals.sqf b/components/gearScript/globals.sqf new file mode 100644 index 00000000..0f5e1bb4 --- /dev/null +++ b/components/gearScript/globals.sqf @@ -0,0 +1,9 @@ +private _keyValues = +[ + ["LinkedItemsMuzzle", 0], + ["LinkedItemsAcc", 1], + ["LinkedItemsOptic", 2], + ["LinkedItemsUnder", 3] +]; + +f_arr_gunbagAttachmentsMap = createHashMapFromArray _keyValues; \ No newline at end of file diff --git a/components/gearScript/gunbag/fn_getWeaponStateFromClassName.sqf b/components/gearScript/gunbag/fn_getWeaponStateFromClassName.sqf new file mode 100644 index 00000000..4d6233a5 --- /dev/null +++ b/components/gearScript/gunbag/fn_getWeaponStateFromClassName.sqf @@ -0,0 +1,32 @@ +// Derived from ace_common_fnc_getWeaponState. Used to get a 'getWeaponState' array from a weapon config string. +// Augments the standard 'getWeaponState' array by returning the base weapon name as a 5th field. +// Currently ignores configured default magazines. Arrays returned from this function will have no ammo. + +params [["_weapon", nil, [""]]]; +if (isNil "_weapon") throw "_weapon must not be nil."; + +private _weaponClass = configFile >> "CfgWeapons" >> _weapon; +if (isNil "_weapon") then {throw format ["_weapon (%1) must be a subclass of CfgWeapons.", _weapon]}; + +private _baseWeapon = getText (_weaponClass >> "baseWeapon"); + +private _attachments = ["","","",""]; +private _linkedItems = _weaponClass >> "LinkedItems"; +private _linkedItemsSubclasses = _linkedItems call BIS_fnc_getCfgSubClasses; + +{ + private _entry = _linkedItems >> _x; + private _index = f_arr_gunbagAttachmentsMap get _x; + + if !(isNil "_index") then + { + private _attachmentName = getText (_entry >> "item"); + _attachments set [_index, _attachmentName]; + }; +} foreach _linkedItemsSubclasses; + +private _muzzles = _weapon call ace_common_fnc_getWeaponMuzzles; +private _magazines = _muzzles apply {""}; +private _ammo = _muzzles apply {0}; + +[_attachments, _muzzles, _magazines, _ammo, _baseWeapon]; \ No newline at end of file diff --git a/components/gearScript/gunbag/fn_hasGunbag.sqf b/components/gearScript/gunbag/fn_hasGunbag.sqf new file mode 100644 index 00000000..e8b18f1d --- /dev/null +++ b/components/gearScript/gunbag/fn_hasGunbag.sqf @@ -0,0 +1,8 @@ +// Override of ace_gunbag_fnc_hasGunbag, to allow any backpack to be a gunbag if it has the gunbag variable set. + +params ["_unit"]; + +(getNumber (configFile >> "CfgVehicles" >> (backpack _unit) >> "ace_gunbag") == 1) or +{ + ((backpackContainer _unit) getVariable ["ace_gunbag_gunbagWeapon", []]) isNotEqualTo [] +} \ No newline at end of file diff --git a/components/gearScript/gunbag/fn_setGunbagVariableFromArsenalExport.sqf b/components/gearScript/gunbag/fn_setGunbagVariableFromArsenalExport.sqf new file mode 100644 index 00000000..cf5d38c1 --- /dev/null +++ b/components/gearScript/gunbag/fn_setGunbagVariableFromArsenalExport.sqf @@ -0,0 +1,43 @@ +#include "macros.hpp" + +// Used by gearscript to set the state of a unit's gunbag global variable. +// Provide an ACE Arsenal export array ("Unit Loadout Array"), or a string representing the weapon's CfgWeapons classname. + +params ["_faction", "_unitName", ["_contents", nil, [[], ""]]]; + +if (isNil "_contents") then {throw format ["_contents for gunbag contents must not be nil. (unit %1, side %2).", _unitName, _faction]}; + +if (typeName _contents isEqualTo "STRING") exitWith +{ + [_faction, _unitName, _contents] call f_fnc_setGunbagVariableState; +}; + +if (count _contents < 1) then {throw format ["_contents for gunbag contents is empty. (unit %1, side %2).", _unitName, _faction]}; + +private _rifle = _contents # 0; + +private _isRifleWellFormed = _rifle params +[ + ["_rifleClass", nil, [""]], + ["_muzzleDevice", nil, [""]], + ["_sideRailDevice", nil, [""]], + ["_optic", nil, [""]], + ["_muzzle1Mag", nil, [[]]], + ["_muzzle2Mag", nil, [[]]], + ["_bipod", nil, [""]] +]; + +if !(_isRifleWellFormed) then {throw format ["_contents for gunbag contents contained an invalid rifle. Please re-export the loadout. (unit %1, side %2).", _unitName, _faction]}; + +private _magsArray = [_muzzle1Mag, _muzzle2Mag] select {_x isNotEqualTo []}; + +private _contents = +[ + [_muzzleDevice, _sideRailDevice, _optic, _bipod], + _rifleClass call ace_common_fnc_getWeaponMuzzles, + _magsArray apply {_x # 0}, + _magsArray apply {_x # 1}, + _rifleClass +]; + +SET_GUNBAG_CONTENTS(_faction,_unitName,_contents); \ No newline at end of file diff --git a/components/gearScript/gunbag/fn_setGunbagVariableState.sqf b/components/gearScript/gunbag/fn_setGunbagVariableState.sqf new file mode 100644 index 00000000..6f73991f --- /dev/null +++ b/components/gearScript/gunbag/fn_setGunbagVariableState.sqf @@ -0,0 +1,12 @@ +#include "macros.hpp" + +// Used by gearscript to set the state of a unit's gunbag global variable. +// Should a string representing the weapon's CfgWeapons classname. + +params ["_faction", "_unitName", ["_contents", nil, [""]]]; + +if (isNil "_contents") throw "_contents must not be nil."; + +private _weaponState = [_contents] call f_fnc_getWeaponStateFromClassName; + +SET_GUNBAG_CONTENTS(_faction,_unitName,_weaponState); \ No newline at end of file diff --git a/components/gearScript/gunbag/macros.hpp b/components/gearScript/gunbag/macros.hpp new file mode 100644 index 00000000..c3630e65 --- /dev/null +++ b/components/gearScript/gunbag/macros.hpp @@ -0,0 +1 @@ +#include "../macros.hpp" diff --git a/configuration/loadouts/gear_blufor.sqf b/configuration/loadouts/gear_blufor.sqf index 0fcb7ee7..22ffdf12 100644 --- a/configuration/loadouts/gear_blufor.sqf +++ b/configuration/loadouts/gear_blufor.sqf @@ -186,6 +186,17 @@ CREATE_LOADOUT(mk,_baseLoadout); COPY_ADDONS(mk,rif); +// Sniper Loadout + +_baseLoadout = [["srifle_LRR_F","","","optic_LRPS",["7Rnd_408_Mag",7],[],""],[],["hgun_P07_F","","","",["16Rnd_9x21_Mag",17],[],""],["U_B_CombatUniform_mcam",[["ACE_elasticBandage",3],["ACE_morphine",2],["ACE_epinephrine",1],["ACE_packingBandage",3],["ACE_tourniquet",2],["ACE_RangeCard",1],["ACRE_PRC343_ID_3",1],["ACE_Flashlight_XL50",1],["ACE_MapTools",1],["ACE_splint",2],["ACE_Kestrel4500",1],["ACE_microDAGR",1],["16Rnd_9x21_Mag",1,17]]],["V_TacChestrig_oli_F",[["optic_NVS",1],["HandGrenade",2,1],["SmokeShell",4,1],["7Rnd_408_Mag",6,7],["16Rnd_9x21_Mag",1,17]]],["ace_gunbag_Tan",[["30Rnd_65x39_caseless_mag",3,30],["30Rnd_65x39_caseless_mag_Tracer",1,30],["NVGoggles",1]]],"H_Booniehat_mcamo","G_Lowprofile",["ACE_Vector","","","",[],[],""],["ItemMap","","","ItemCompass","ItemWatch",""]]; + +CREATE_LOADOUT(sniper,_baseLoadout); + +_gunbagLoadout = [["arifle_MXC_F","muzzle_snds_65_TI_blk_F","acc_pointer_IR","optic_Aco",["30Rnd_65x39_caseless_mag",30],[],""],[],[],[],[],[],"","",[],["","","","","",""]]; + +PUT_GUN_IN_GUNBAG(sniper,_gunbagLoadout); + + // Crewman Loadout _baseLoadout = [["SMG_01_F","","acc_flashlight_smg_01","optic_ACO_grn_smg",["30Rnd_45ACP_Mag_SMG_01",25],[],""],[],[],["U_B_HeliPilotCoveralls",[["ACE_elasticBandage",3],["ACE_morphine",2],["ACE_epinephrine",1],["ACE_packingBandage",3],["ACE_tourniquet",2],["ACE_splint",2],["ACRE_PRC343_ID_1",1],["ACRE_PRC148_ID_2",1],["ACRE_PRC152_ID_2",1]]],["V_Chestrig_oli",[["ACE_MapTools",1],["ACE_Flashlight_XL50",1],["SmokeShell",4,1],["30Rnd_45ACP_Mag_SMG_01",5,25],["SmokeShellBlue",1,1],["SmokeShellRed",1,1]]],["B_FieldPack_oli",[["ToolKit",1],["ACE_EntrenchingTool",1]]],"H_HelmetCrew_B","G_Combat",["Binocular","","","",[],[],""],["ItemMap","ItemGPS","","ItemCompass","ItemWatch","NVGoggles_INDEP"]]; diff --git a/configuration/loadouts/gear_indfor.sqf b/configuration/loadouts/gear_indfor.sqf index 61bec1d6..29c3aaa5 100644 --- a/configuration/loadouts/gear_indfor.sqf +++ b/configuration/loadouts/gear_indfor.sqf @@ -180,6 +180,16 @@ CREATE_LOADOUT(mk,_baseLoadout); COPY_ADDONS(mk,rif); +// Sniper Loadout + +_baseLoadout = [["srifle_GM6_F","","","optic_LRPS",["5Rnd_127x108_Mag",5],[],""],[],["hgun_ACPC2_F","","","",["9Rnd_45ACP_Mag",8],[],""],["U_I_CombatUniform_shortsleeve",[["ACE_elasticBandage",3],["ACE_epinephrine",1],["ACE_morphine",2],["ACE_packingBandage",3],["ACE_RangeCard",1],["ACE_MapTools",1],["ACE_Flashlight_XL50",1],["ACE_splint",2],["ACE_tourniquet",2],["ACRE_PRC343_ID_15",1],["ACE_Kestrel4500",1],["ACE_microDAGR",1],["9Rnd_45ACP_Mag",1,8]]],["V_TacChestrig_oli_F",[["ACRE_PRC343_ID_31",1],["optic_NVS",1],["SmokeShell",4,1],["5Rnd_127x108_Mag",6,5],["9Rnd_45ACP_Mag",1,8]]],["ace_gunbag",[["30Rnd_556x45_Stanag",4,30],["30Rnd_556x45_Stanag_Tracer_Yellow",1,30],["NVGoggles_INDEP",1]]],"H_Booniehat_dgtl","G_Lowprofile",["ACE_Vector","","","",[],[],""],["ItemMap","","","ItemCompass","ItemWatch",""]]; + +CREATE_LOADOUT(sniper,_baseLoadout); +COPY_ADDONS(sniper,rif); + +PUT_GUN_IN_GUNBAG(sniper,"arifle_Mk20C_ACO_pointer_F"); + + // Crewman Loadout _baseLoadout = [["hgun_PDW2000_F","","","optic_Aco_smg",["30Rnd_9x21_Mag",30],[],""],[],[],["U_I_HeliPilotCoveralls",[["ACE_elasticBandage",3],["ACE_morphine",2],["ACE_epinephrine",1],["ACE_packingBandage",3],["ACE_tourniquet",2],["ACRE_PRC343_ID_18",1],["ACRE_PRC148_ID_4",1],["ACE_splint",2],["ACRE_PRC152_ID_1",1]]],["V_Chestrig_khk",[["SmokeShell",4,1],["SmokeShellBlue",1,1],["SmokeShellRed",1,1],["30Rnd_9x21_Mag",5,30]]],["B_FieldPack_khk",[["ToolKit",1],["ACE_EntrenchingTool",1]]],"H_HelmetCrew_I","G_Combat",["Binocular","","","",[],[],""],["ItemMap","ItemGPS","","ItemCompass","ItemWatch","NVGoggles_INDEP"]]; diff --git a/configuration/loadouts/gearscript_readme.txt b/configuration/loadouts/gearscript_readme.txt index 099f4030..99f1971a 100644 --- a/configuration/loadouts/gearscript_readme.txt +++ b/configuration/loadouts/gearscript_readme.txt @@ -68,6 +68,15 @@ ADD_VARIANT(UNIT_NAME,); ADD_VARIANT(rif,); All riflemen will now randomly have an AK-47 or M-16 rifle. +PUT_GUN_IN_GUNBAG(UNIT_NAME,) +- Allows the backpack of the given unit to be turned into an ACE gunbag, with the given contents placed inside. + The contents can be: + - ACE Arsenal code: + An example of this exists in the default BLUFOR 'sniper' loadout. + - Weapon config class-name: + An example of this exists in the default INDFOR 'sniper' loadout. + You can get the class-name of any weapon by clicking on it in ACE arsenal and then pressing CTRL+C (gets copied to clipboard). + There are advanced weapon classes which have scopes, bipods etc pre-applied. You can find them in the CfgWeapons config. If you don't know how to find these, it may be better to use the ACE Arsenal approach. ------------------------------------------------- diff --git a/gearscript_macros.hpp b/gearscript_macros.hpp index de997f21..e36eb204 100644 --- a/gearscript_macros.hpp +++ b/gearscript_macros.hpp @@ -20,6 +20,7 @@ #define LOADOUT_ITEM_VAR(NAME,ITEM) CONCAT3(LOADOUT_VAR(NAME),_,ITEM) #define LOADOUT_ITEM_VAR_DYNAMIC(SIDE,NAME,ITEM) (missionNamespace getVariable [format ["f_loadouts_%1_%2_%3", SIDE, NAME, ITEM], []]) +#define SET_LOADOUT_ITEM_VAR_DYNAMIC(SIDE,NAME,ITEM,VALUE) missionNamespace setVariable [format ["f_loadouts_%1_%2_%3", SIDE, NAME, ITEM], VALUE] #define HATS_DYNAMIC(SIDE,NAME) LOADOUT_ITEM_VAR_DYNAMIC(SIDE,NAME,"hats") #define VESTS_DYNAMIC(SIDE,NAME) LOADOUT_ITEM_VAR_DYNAMIC(SIDE,NAME,"vest") @@ -28,6 +29,11 @@ #define ADD_VARIANT(NAME,LOADOUT) LOADOUT_VAR(NAME) pushBack ([LOADOUT] call f_fnc_applyLoadoutModifications) +#define GUNBAG_ITEM(NAME) LOADOUT_ITEM_VAR(NAME,gunbag) +#define GUNBAG_ITEM_DYNAMIC(SIDE,NAME) LOADOUT_ITEM_VAR_DYNAMIC(SIDE,NAME,"gunbag") +#define SET_GUNBAG_CONTENTS(SIDE,NAME,CONTENTS) SET_LOADOUT_ITEM_VAR_DYNAMIC(SIDE,NAME,"gunbag",CONTENTS) +#define PUT_GUN_IN_GUNBAG(NAME,CONTENTS) [#FACTION,#NAME,CONTENTS] call f_fnc_setGunbagVariableFromArsenalExport + #define HATS(NAME) LOADOUT_ITEM_VAR(NAME,hats) #define VESTS(NAME) LOADOUT_ITEM_VAR(NAME,vest) #define UNIFORMS(NAME) LOADOUT_ITEM_VAR(NAME,uniforms) diff --git a/init.sqf b/init.sqf index 8f455a70..bcdc70a2 100644 --- a/init.sqf +++ b/init.sqf @@ -35,8 +35,6 @@ if (IS_CLIENT) then { DEBUG_PRINT_LOG("Using CLIENT groups.") - #include "startup\components\globals\clientGlobals.sqf" - #include "customStartup_client.sqf" #include "startup\configuration\groups\clientConfigGroup.sqf" @@ -53,8 +51,6 @@ if (isServer) then { DEBUG_PRINT_LOG("Using SERVER groups.") - #include "startup\components\globals\serverGlobals.sqf" - #include "customStartup_server.sqf" #include "startup\configuration\groups\serverConfigGroup.sqf" diff --git a/startup/components/globals/clientGlobals.sqf b/startup/components/globals/clientGlobals.sqf index fb731230..4ba150c5 100644 --- a/startup/components/globals/clientGlobals.sqf +++ b/startup/components/globals/clientGlobals.sqf @@ -21,6 +21,8 @@ LOAD_GLOBALS(squadMarkers) LOAD_GLOBALS(miscShared) +LOAD_GLOBALS(gearScript) + //LOAD_GLOBALS(respawnWaves) //LOAD_GLOBALS(zeus_ui) diff --git a/startup/components/globals/serverGlobals.sqf b/startup/components/globals/serverGlobals.sqf index 20059de1..e250923e 100644 --- a/startup/components/globals/serverGlobals.sqf +++ b/startup/components/globals/serverGlobals.sqf @@ -15,6 +15,8 @@ LOAD_GLOBALS(miscShared) +LOAD_GLOBALS(gearScript) + // Kill tracker init #ifdef ENABLE_KILL_TRACKING diff --git a/startup/configuration/groups/clientConfigGroup_preInit.sqf b/startup/configuration/groups/clientConfigGroup_preInit.sqf index b606ae4f..ac46e6d6 100644 --- a/startup/configuration/groups/clientConfigGroup_preInit.sqf +++ b/startup/configuration/groups/clientConfigGroup_preInit.sqf @@ -1,6 +1,8 @@ #include "macros.hpp" CLIENT_ONLY; +#include "..\..\components\globals\clientGlobals.sqf" + // Client configuration group // Includes all config scripts needed for clients. diff --git a/startup/configuration/groups/serverConfigGroup_preInit.sqf b/startup/configuration/groups/serverConfigGroup_preInit.sqf index 11b62ede..fdce40dc 100644 --- a/startup/configuration/groups/serverConfigGroup_preInit.sqf +++ b/startup/configuration/groups/serverConfigGroup_preInit.sqf @@ -1,6 +1,8 @@ #include "macros.hpp" SERVER_ONLY; +#include "..\..\components\globals\serverGlobals.sqf" + // Server configuration group // Includes all config scripts needed for the server.