diff --git a/addons/ai/XEH_PREP.hpp b/addons/ai/XEH_PREP.hpp index 5fa7eef30..484f2b82e 100644 --- a/addons/ai/XEH_PREP.hpp +++ b/addons/ai/XEH_PREP.hpp @@ -5,3 +5,5 @@ PREP(handleSkillsChange); PREP(initMan); PREP(searchBuilding); PREP(unGarrison); +PREP(unpackStaticWeapon); +PREP(unpackStaticWeaponPFH); diff --git a/addons/ai/XEH_postInit.sqf b/addons/ai/XEH_postInit.sqf index 3db3b8514..848fcee35 100644 --- a/addons/ai/XEH_postInit.sqf +++ b/addons/ai/XEH_postInit.sqf @@ -5,3 +5,5 @@ QGVAR(skills) addPublicVariableEventHandler LINKFUNC(handleSkillsChange); ["CAManBase", "InitPost", LINKFUNC(initMan), true, [], false] call CBA_fnc_addClassEventHandler; [QGVAR(unGarrison), LINKFUNC(unGarrison)] call CBA_fnc_addEventHandler; + +[QGVAR(unpackStaticWeapon), LINKFUNC(unpackStaticWeapon)] call CBA_fnc_addEventHandler; diff --git a/addons/ai/functions/fnc_unpackStaticWeapon.sqf b/addons/ai/functions/fnc_unpackStaticWeapon.sqf new file mode 100644 index 000000000..1e81c75d2 --- /dev/null +++ b/addons/ai/functions/fnc_unpackStaticWeapon.sqf @@ -0,0 +1,65 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Unpacks a static weapon from units' backpacks. + * + * Arguments: + * 0: Gunner + * 1: Assistant + * 2: Target Position + * + * Return Value: + * None + * + * Example: + * [_gunner, _assistant] call zen_ai_fnc_unpackStaticWeapon + * + * Public: No + */ + +#define DISTANCE_CLOSE 3 + +params ["_gunner", "_assistant", ["_targetPos", [], [[]], 3]]; + +if (!local _gunner) exitWith { + [QGVAR(unpackStaticWeapon), _this, _gunner] call CBA_fnc_targetEvent; +}; + +if (!(_targetPos isEqualTo [])) then { + _gunner doWatch _targetPos; +}; + +// Too far, run PFH +if (_gunner distance _assistant > DISTANCE_CLOSE) exitWith { + [_gunner, _assistant, _targetPos] call FUNC(unpackStaticWeaponPFH); +}; + +if (!(_targetPos isEqualTo [])) then { + _assistant doWatch _targetPos; + _gunner setVariable [QGVAR(unpackStaticWeaponTargetPos), _targetPos]; +}; + +_gunner addEventHandler ["WeaponAssembled", { + params ["_gunner", "_weapon"]; + + _gunner removeEventHandler ["WeaponAssembled", _thisEventHandler]; + [QEGVAR(common,addObjects), [[_weapon]]] call CBA_fnc_serverEvent; + + private _targetPos = _gunner getVariable [QGVAR(unpackStaticWeaponTargetPos), []]; + if (!(_targetPos isEqualTo [])) then { + _weapon setDir (_weapon getDir _targetPos); + _gunner doWatch _targetPos; + }; + + // Added due to occassional tripod leg clipping through ground + _weapon setPosASL (getPosASL _weapon vectorAdd [0, 0, 0.05]); + _weapon setVectorUp surfaceNormal position _weapon; + + _gunner assignAsGunner _weapon; + _gunner moveInGunner _weapon; + + group _gunner addVehicle _weapon; +}]; + +_gunner action ["PutBag", _assistant]; +_gunner action ["Assemble", unitBackpack _assistant]; diff --git a/addons/ai/functions/fnc_unpackStaticWeaponPFH.sqf b/addons/ai/functions/fnc_unpackStaticWeaponPFH.sqf new file mode 100644 index 000000000..90378d75b --- /dev/null +++ b/addons/ai/functions/fnc_unpackStaticWeaponPFH.sqf @@ -0,0 +1,74 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Move units together if needed and unpacks a static weapon from units' backpacks. + * + * Arguments: + * 0: Gunner + * 1: Assistant + * 2: Target Position + * + * Return Value: + * None + * + * Example: + * [_gunner, _assistant] call zen_ai_fnc_unpackStaticWeaponPFH + * + * Public: No + */ + +#define DISTANCE_CLOSE 3 +#define MOVE_DELAY 5 +#define MOVE_TIMEOUT 60 + +params ["_gunner", "_assistant", ["_targetPos", [], [[]], 3]]; + +// Order assistant to move to gunner and start pfh +_gunner setVariable [QGVAR(statePATH), _gunner checkAIFeature "PATH"]; +_gunner disableAI "PATH"; +[_assistant] joinSilent grpNull; +private _group = group _assistant; +_group setBehaviourStrong "CARELESS"; +_group deleteGroupWhenEmpty true; +_group enableAttack false; +_assistant doMove getPosATL _gunner; +_assistant setVariable [QGVAR(stateFSM), _assistant checkAIFeature "FSM"]; +_assistant disableAI "FSM"; +_assistant setVariable [QGVAR(nextMoveTime), CBA_MissionTime + 5]; + +[{ + params ["_args", "_pfhID"]; + _args params ["_gunner", "_assistant", "_targetPos", "_nextMoveTime", "_endTime"]; + + private _closeEnough = _gunner distance _assistant <= DISTANCE_CLOSE; + + if (_closeEnough || {CBA_MissionTime > _endTime || {!alive _gunner || {!alive _assistant}}}) exitWith { + [_pfhID] call CBA_fnc_removePerFrameHandler; + + // Gunner AI PATH + if (_gunner getVariable [QGVAR(statePATH), true]) then { + _gunner setVariable [QGVAR(statePATH), nil]; + _gunner enableAI "PATH"; + }; + + // Reset assistant behaviour + private _group = group _gunner; + [_assistant] joinSilent _group; + _group setBehaviour behaviour _gunner; + + // Assistant AI FSM + if (_assistant getVariable [QGVAR(stateFSM), true]) then { + _assistant setVariable [QGVAR(stateFSM), nil]; + _assistant enableAI "FSM"; + }; + // Close enough, unpack + if (_closeEnough) then { + [_gunner, _assistant, _targetPos] call FUNC(unpackStaticWeapon); + }; + }; + + if (unitReady _assistant || {CBA_MissionTime >= _nextMoveTime}) then { + _assistant doMove ASLtoAGL getPosASL _gunner; + _args set [3, CBA_missionTime + MOVE_DELAY]; + }; +}, 0.1, [_gunner, _assistant, _targetPos, CBA_MissionTime + MOVE_DELAY, CBA_missionTime + MOVE_TIMEOUT]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/modules/CfgVehicles.hpp b/addons/modules/CfgVehicles.hpp index fe8d44c7f..5cf6ba215 100644 --- a/addons/modules/CfgVehicles.hpp +++ b/addons/modules/CfgVehicles.hpp @@ -457,6 +457,13 @@ class CfgVehicles { displayName = CSTRING(ModuleUnGarrison); function = QFUNC(moduleUnGarrison); }; + class GVAR(moduleUnpackStaticWeapon): GVAR(moduleBase) { + curatorCanAttach = 1; + category = QGVAR(AI); + displayName = CSTRING(moduleUnpackStaticWeapon); + function = QFUNC(moduleUnpackStaticWeapon); + icon = QPATHTOF(ui\unpack_static_ca.paa); + }; class GVAR(moduleVisibility): GVAR(moduleBase) { curatorCanAttach = 1; category = QGVAR(Objects); diff --git a/addons/modules/XEH_PREP.hpp b/addons/modules/XEH_PREP.hpp index b79eb2e06..d2040f606 100644 --- a/addons/modules/XEH_PREP.hpp +++ b/addons/modules/XEH_PREP.hpp @@ -87,5 +87,6 @@ PREP(moduleToggleLamps); PREP(moduleTracers); PREP(moduleTurretOptics); PREP(moduleUnGarrison); +PREP(moduleUnpackStaticWeapon); PREP(moduleVisibility); PREP(moduleWeather); diff --git a/addons/modules/config.cpp b/addons/modules/config.cpp index 09afceb4c..d2e9aaa93 100644 --- a/addons/modules/config.cpp +++ b/addons/modules/config.cpp @@ -70,6 +70,7 @@ class CfgPatches { QGVAR(moduleTracers), QGVAR(moduleTurretOptics), QGVAR(moduleUnGarrison), + QGVAR(moduleUnpackStaticWeapon), QGVAR(moduleVisibility), QGVAR(moduleWeather) }; diff --git a/addons/modules/functions/fnc_moduleUnpackStaticWeapon.sqf b/addons/modules/functions/fnc_moduleUnpackStaticWeapon.sqf new file mode 100644 index 000000000..8d32af238 --- /dev/null +++ b/addons/modules/functions/fnc_moduleUnpackStaticWeapon.sqf @@ -0,0 +1,120 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Zeus module function to unpack and assemble a static weapon from supported backpacks. + * + * Arguments: + * 0: Logic + * + * Return Value: + * None + * + * Example: + * [LOGIC] call zen_modules_fnc_moduleUnpackStaticWeapon + * + * Public: No + */ + +params ["_logic"]; + +private _gunner = attachedTo _logic; +deleteVehicle _logic; + +if (isNull _gunner) exitWith { + [LSTRING(NoUnitSelected)] call EFUNC(common,showMessage); +}; + +if !(_gunner isKindOf "CAManBase") exitWith { + [LSTRING(OnlyInfantry)] call EFUNC(common,showMessage); +}; + +if !(alive _gunner) exitWith { + [LSTRING(OnlyAlive)] call EFUNC(common,showMessage); +}; + +if (isPlayer _gunner) exitWith { + ["str_a3_cfgvehicles_moduleremotecontrol_f_errorPlayer"] call EFUNC(common,showMessage); +}; + +if (units _gunner findIf {isPlayer _x} != -1) exitWith { + [LSTRING(ModuleUnpackStaticWeapon_Player)] call EFUNC(common,showMessage); +}; + +private _backpack = backpack _gunner; +if (_backpack isEqualTo "") exitWith { + [LSTRING(ModuleUnpackStaticWeapon_Unit)] call EFUNC(common,showMessage); +}; + +private _assembleInfo = ["assembleTo", "base", "displayName", "dissasembleTo", "primary"] apply {(configOf backpackContainer _gunner >> "assembleInfo" >> _x) call BIS_fnc_getCfgData}; +_assembleInfo params ["_assembleTo", "_compatibleBases", "_displayName", "_dissasembleTo", "_primary"]; +if (_compatibleBases isEqualType "") then {_compatibleBases = [];}; + +if (_primary == 1 && {_compatibleBases isEqualTo [] || {_compatibleBases isEqualTo "" || {_compatibleBases isEqualTo [""]}}}) then { + // Single-backpack weapon + [_gunner, { + params ["_successful", "_gunner", "_position"]; + if (_successful) then { + _gunner setVariable [QGVAR(unpackStaticWeaponTargetPos), _position]; + + _gunner addEventHandler ["WeaponAssembled", { + params ["_gunner", "_weapon"]; + + _gunner removeEventHandler ["WeaponAssembled", _thisEventHandler]; + [QEGVAR(common,addObjects), [[_weapon]]] call CBA_fnc_serverEvent; + + private _targetPos = _gunner getVariable [QGVAR(unpackStaticWeaponTargetPos), []]; + if (!(_targetPos isEqualTo [])) then { + _weapon setDir (_weapon getDir _targetPos); + _gunner doWatch _targetPos; + }; + + // Added due to occassional tripod leg clipping through ground + _weapon setPosASL (getPosASL _weapon vectorAdd [0, 0, 0.05]); + _weapon setVectorUp surfaceNormal position _weapon; + + if (unitIsUAV _weapon) exitWith { + _gunner connectTerminalToUAV _weapon; + }; + + if (isNull gunner _weapon) exitWith { + _gunner assignAsGunner _weapon; + _gunner moveInGunner _weapon; + + group _gunner addVehicle _weapon; + }; + }]; + + _gunner action ["Assemble"]; + }; + }, [], LSTRING(ModuleUnpackStaticWeapon_Direction)] call EFUNC(common,selectPosition); +} else { + // Two-backpack weapon + private _backpackers = units _gunner select {!(backpack _x isEqualTo "")}; + private _assistant = objNull; + { + if ( + // Has matching backpack + (backpack _x in _compatibleBases || {_backpack in ((configOf backpackContainer _x >> "assembleInfo" >> "base") call BIS_fnc_getCfgData)}) + // Closer than current + && {_assistant == objNull || {(_gunner distance _x) < (_gunner distance _assistant)}} + ) then { + _assistant = _x; + }; + } forEach _backpackers; + + if (isNull _assistant) exitWith { + [LSTRING(ModuleUnpackStaticWeapon_Group)] call EFUNC(common,showMessage); + }; + + if (_assistant distance _gunner > 100) exitWith { + [LSTRING(ModuleUnpackStaticWeapon_Distance)] call EFUNC(common,showMessage); + }; + + // Get target position + [_gunner, { + params ["_successful", "_gunner", "_position", "_assistant"]; + if (_successful) then { + [QEGVAR(ai,unpackStaticWeapon), [_gunner, _assistant, ASLToAGL _position], _gunner] call CBA_fnc_targetEvent; + }; +}, _assistant, LSTRING(ModuleUnpackStaticWeapon_Direction)] call EFUNC(common,selectPosition); +}; diff --git a/addons/modules/stringtable.xml b/addons/modules/stringtable.xml index 4a2d9af66..17a500380 100644 --- a/addons/modules/stringtable.xml +++ b/addons/modules/stringtable.xml @@ -1308,6 +1308,24 @@ Odgarnizonuj grupę 非歩哨グループ + + Unpack Static Weapon + + + Unit must carry a weapon bag + + + Unit group must contain two compatible weapon bags + + + Assistant must be within 100 m of gunner + + + Unit must not be in a player's group + + + Weapon Direction + Group Side Сторона группы diff --git a/addons/modules/ui/unpack_static_ca.paa b/addons/modules/ui/unpack_static_ca.paa new file mode 100644 index 000000000..c9ac12242 Binary files /dev/null and b/addons/modules/ui/unpack_static_ca.paa differ diff --git a/docs/user_guide/modules_list.md b/docs/user_guide/modules_list.md index 653fb313f..30b7ea387 100644 --- a/docs/user_guide/modules_list.md +++ b/docs/user_guide/modules_list.md @@ -283,6 +283,10 @@ Creates an invisible unit that shoots customizable tracer bursts at a specified Un-garrisons units from the attached group. +## Unpack Static Weapon + +Orders AI with weapons bags to set up the static weapon facing the target position. + ## Update Editable Objects Adds or removes editable objects of the selected types from the local or all curators.