Safemode - Refactor (#10111)

* Refactor safemode

* Further improvements and fixes

* Update XEH_postInit.sqf

* Don't allow binoculars to be set to safe

* Add API for getting weapon safety status

* Update fnc_jamWeapon.sqf

* Added doc

* Update fnc_playChangeFiremodeSound.sqf

* Update addons/overheating/functions/fnc_jamWeapon.sqf

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

* Update addons/weaponselect/functions/fnc_selectWeaponMode.sqf

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

---------

Co-authored-by: PabstMirror <pabstmirror@gmail.com>
This commit is contained in:
johnb432 2024-08-02 13:59:18 +02:00 committed by GitHub
parent 4e56d58210
commit 6a25e9365a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 257 additions and 140 deletions

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: commy2 * Author: commy2, johnb43
* Get the muzzles of a weapon. * Get the muzzles of a weapon.
* *
* Arguments: * Arguments:
@ -10,19 +10,30 @@
* All weapon muzzles <ARRAY> * All weapon muzzles <ARRAY>
* *
* Example: * Example:
* ["gun"] call ace_common_fnc_getWeaponMuzzles * "arifle_AK12_F" call ace_common_fnc_getWeaponMuzzles
* *
* Public: Yes * Public: Yes
*/ */
params [["_weapon", "", [""]]]; params [["_weapon", "", [""]]];
private _muzzles = getArray (configFile >> "CfgWeapons" >> _weapon >> "muzzles"); private _config = configFile >> "CfgWeapons" >> _weapon;
if (!isClass _config) exitWith {
[] // return
};
private _muzzles = [];
// Get config case muzzle names
{ {
if (_x == "this") then { if (_x == "this") then {
_muzzles set [_forEachIndex, configName (configFile >> "CfgWeapons" >> _weapon)]; _muzzles pushBack (configName _config);
} else {
if (isClass (_config >> _x)) then {
_muzzles pushBack (configName (_config >> _x));
}; };
} forEach _muzzles; };
} forEach getArray (_config >> "muzzles");
_muzzles _muzzles // return

View File

@ -80,9 +80,11 @@ if (_unit getVariable [QGVAR(JammingActionID), -1] == -1) then {
private _condition = { private _condition = {
private _unit = _this select 1; private _unit = _this select 1;
[_unit] call CBA_fnc_canUseWeapon (weaponState _unit) params ["_currentWeapon", "_currentMuzzle"];
&& {currentMuzzle _unit in (_unit getVariable [QGVAR(jammedWeapons), []])}
&& {!(currentMuzzle _unit in (_unit getVariable [QEGVAR(safemode,safedWeapons), []]))} _unit call CBA_fnc_canUseWeapon
&& {_currentMuzzle in (_unit getVariable [QGVAR(jammedWeapons), []])}
&& {!(["ace_safemode"] call EFUNC(common,isModLoaded)) || {!([_unit, _currentWeapon, _currentMuzzle] call EFUNC(safemode,getWeaponSafety))}}
}; };
private _statement = { private _statement = {

View File

@ -1,4 +1,3 @@
class Extended_PreStart_EventHandlers { class Extended_PreStart_EventHandlers {
class ADDON { class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); init = QUOTE(call COMPILE_SCRIPT(XEH_preStart));

View File

@ -1,6 +1,6 @@
PREP(getWeaponSafety);
PREP(lockSafety); PREP(lockSafety);
PREP(playChangeFiremodeSound); PREP(playChangeFiremodeSound);
PREP(setSafeModeVisual); PREP(setSafeModeVisual);
PREP(unlockSafety);
PREP(setWeaponSafety); PREP(setWeaponSafety);
PREP(unlockSafety);

View File

@ -4,18 +4,26 @@
if (!hasInterface) exitWith {}; if (!hasInterface) exitWith {};
["ACE3 Weapons", QGVAR(safeMode), localize LSTRING(SafeMode), { ["ACE3 Weapons", QGVAR(safeMode), LLSTRING(SafeMode), {
// Conditions: canInteract // Conditions: canInteract
if !([ACE_player, objNull, ["isNotEscorting", "isNotInside", "isNotSwimming"]] call EFUNC(common,canInteractWith)) exitWith {false}; if !([ACE_player, objNull, ["isNotEscorting", "isNotInside", "isNotSwimming"]] call EFUNC(common,canInteractWith)) exitWith {false};
// Conditions: specific
if !([ACE_player] call CBA_fnc_canUseWeapon && {currentWeapon ACE_player != binocular ACE_player} && {currentWeapon ACE_player != ""}) exitWith {false};
// Statement (weaponState ACE_player) params ["_currentWeapon", "_currentMuzzle"];
[ACE_player, currentWeapon ACE_player, currentMuzzle ACE_player] call FUNC(lockSafety);
// Conditions: specific
if !(ACE_player call CBA_fnc_canUseWeapon && {_currentWeapon != ""} && {_currentWeapon != binocular ACE_player}) exitWith {false};
// Statement: Toggle weapon safety
[ACE_player, _currentWeapon, _currentMuzzle] call FUNC(lockSafety);
true true
}, {false}, [DIK_GRAVE, [false, true, false]], false] call CBA_fnc_addKeybind; }, {false}, [DIK_GRAVE, [false, true, false]], false] call CBA_fnc_addKeybind;
["unit", { ["unit", {
private _weaponSafe = currentWeapon ACE_player in (ACE_player getVariable [QGVAR(safedWeapons), []]); (weaponState ACE_player) params ["_currentWeapon", "_currentMuzzle"];
[!_weaponSafe] call FUNC(setSafeModeVisual);
private _weaponSafe = [ACE_player, _currentWeapon, _currentMuzzle] call FUNC(getWeaponSafety);
// Player HUD
!_weaponSafe call FUNC(setSafeModeVisual);
}] call CBA_fnc_addPlayerEventHandler; }] call CBA_fnc_addPlayerEventHandler;

View File

@ -0,0 +1,45 @@
#include "..\script_component.hpp"
/*
* Author: johnb43
* Getter for weapon safety state.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 2: Muzzle <STRING> (default: current muzzle of weapon)
*
* Return Value:
* Safety status <BOOL>
*
* Example:
* [ACE_player, currentWeapon ACE_player] call ace_safemode_fnc_getWeaponSafety
*
* Public: Yes
*/
params [
["_unit", objNull, [objNull]],
["_weapon", "", [""]],
["_muzzle", nil, [""]]
];
if (_weapon == "" || {!(_unit hasWeapon _weapon)}) exitWith {false};
// Check if weapon is a binocular
if ((_weapon call EFUNC(common,getItemType)) select 1 == "binocular") exitWith {false};
// Check for invalid muzzles
_muzzle = if (isNil "_muzzle") then {
// Get current weapon muzzle if not defined
(_unit weaponState _weapon) select 1
} else {
// Get config case muzzle names
private _muzzles = _weapon call EFUNC(common,getWeaponMuzzles);
_muzzles param [_muzzles findIf {_x == _muzzle}, ""]
};
// Weapon is not available
if (_muzzle == "") exitWith {false};
_muzzle in ((_unit getVariable [QGVAR(safedWeapons), createHashMap]) getOrDefault [_weapon, createHashMap]) // return

View File

@ -1,13 +1,13 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: commy2 * Author: commy2, johnb43
* Put weapon on safety, or take it off safety if safety is already put on. * Puts weapon on safety, or take it off safety if safety is already put on.
* *
* Arguments: * Arguments:
* 0: Unit <OBJECT> * 0: Unit <OBJECT>
* 1: Weapon <STRING> * 1: Weapon <STRING>
* 2: Muzzle <STRING> * 2: Muzzle <STRING>
* 3: Show hint <BOOL> * 3: Show hint <BOOL> (default: true)
* *
* Return Value: * Return Value:
* None * None
@ -18,67 +18,74 @@
* Public: No * Public: No
*/ */
params ["_unit", "_weapon", "_muzzle", ["_hint", true, [true]]]; params ["_unit", "_weapon", "_muzzle", ["_hint", true]];
private _safedWeapons = _unit getVariable [QGVAR(safedWeapons), []]; private _safedWeapons = _unit getVariable QGVAR(safedWeapons);
if (_weapon in _safedWeapons) exitWith { if (isNil "_safedWeapons") then {
_this call FUNC(unlockSafety); _safedWeapons = createHashMap;
};
_safedWeapons pushBack _weapon;
_unit setVariable [QGVAR(safedWeapons), _safedWeapons]; _unit setVariable [QGVAR(safedWeapons), _safedWeapons];
};
if (_unit getVariable [QGVAR(actionID), -1] == -1) then { // See if the current weapon has locked muzzles
private _safedWeaponMuzzles = _safedWeapons getOrDefault [_weapon, createHashMap, true];
// If muzzle is locked, unlock it (toggle)
if (_muzzle in _safedWeaponMuzzles) exitWith {
[_unit, _weapon, _muzzle, _hint] call FUNC(unlockSafety);
};
private _firemode = (_unit weaponState _muzzle) select 2;
// This syntax of selectWeapon doesn't mess with gun lights and lasers
_unit selectWeapon [_weapon, _muzzle, _firemode];
// Store new muzzle & firemode
_safedWeaponMuzzles set [_muzzle, _firemode];
// Lock muzzle
if (isNil {_unit getVariable QGVAR(actionID)}) then {
_unit setVariable [QGVAR(actionID), [ _unit setVariable [QGVAR(actionID), [
_unit, "DefaultAction", { _unit, "DefaultAction", {
params ["", "_unit"];
if ( if (
[_this select 1] call CBA_fnc_canUseWeapon _unit call CBA_fnc_canUseWeapon && {
&& { (weaponState _unit) params ["_currentWeapon", "_currentMuzzle"];
if (currentMuzzle (_this select 1) in ((_this select 1) getVariable [QGVAR(safedWeapons), []])) then {
if (inputAction "nextWeapon" > 0) exitWith { // Block firing the muzzle in safe mode
[_this select 1, currentWeapon (_this select 1), currentMuzzle (_this select 1)] call FUNC(unlockSafety); if (_currentMuzzle in ((_unit getVariable [QGVAR(safedWeapons), createHashMap]) getOrDefault [_currentWeapon, createHashMap])) then {
if (inputAction "nextWeapon" > 0 || {inputAction "prevWeapon" > 0}) exitWith {
[_unit, _currentWeapon, _currentMuzzle] call FUNC(unlockSafety);
false false
}; };
true
} else {false}
}
) then {
// player hud
[false] call FUNC(setSafeModeVisual);
true true
} else { } else {
// player hud false
[true] call FUNC(setSafeModeVisual); }
}
) then {
// Player HUD
false call FUNC(setSafeModeVisual);
true
} else {
// Player HUD
true call FUNC(setSafeModeVisual);
false false
}; };
}, {} }, {}
] call EFUNC(common,addActionEventHandler)]; ] call EFUNC(common,addActionEventHandler)];
}; };
if (_muzzle isEqualType "") then { // Play fire mode selector sound
private _laserEnabled = _unit isIRLaserOn _weapon || {_unit isFlashlightOn _weapon};
_unit selectWeapon _muzzle;
if (
_laserEnabled
&& {
_muzzle == primaryWeapon _unit // prevent UGL switch
|| {"" == primaryWeapon _unit} // Arma switches to primary weapon if exists
}
) then {
{_unit action [_x, _unit]} forEach ["GunLightOn", "IRLaserOn"];
};
};
// play fire mode selector sound
[_unit, _weapon, _muzzle] call FUNC(playChangeFiremodeSound); [_unit, _weapon, _muzzle] call FUNC(playChangeFiremodeSound);
// show info box unless disabled // Show info box unless disabled
if (_hint) then { if (_hint) then {
private _picture = getText (configFile >> "CfgWeapons" >> _weapon >> "picture"); [LLSTRING(PutOnSafety), getText (configFile >> "CfgWeapons" >> _weapon >> "picture")] call EFUNC(common,displayTextPicture);
[localize LSTRING(PutOnSafety), _picture] call EFUNC(common,displayTextPicture);
}; };

View File

@ -1,7 +1,7 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: commy2 * Author: commy2
* Play weapon firemode change sound. * Plays weapon firemode change sound.
* *
* Arguments: * Arguments:
* 0: Unit <OBJECT> * 0: Unit <OBJECT>
@ -21,21 +21,23 @@ params ["_unit", "_weapon"];
private _sound = getArray (configFile >> "CfgWeapons" >> _weapon >> "changeFiremodeSound"); private _sound = getArray (configFile >> "CfgWeapons" >> _weapon >> "changeFiremodeSound");
if (_sound isEqualTo []) exitWith { if (_sound isEqualTo []) exitWith {
playSound "ACE_Sound_Click"; playSoundUI ["ACE_Sound_Click"];
}; };
// get position where to play the sound (position of the weapon) _sound params [["_filename", ""], ["_volume", 1], ["_soundPitch", 1], ["_distance", 0]];
private _position = _unit modelToWorldVisualWorld (_unit selectionPosition "RightHand");
_sound params ["_filename", ["_volume", 1], ["_soundPitch", 1], ["_distance", 0]];
if (_filename == "") exitWith { if (_filename == "") exitWith {
playSound "ACE_Sound_Click"; playSoundUI ["ACE_Sound_Click"];
}; };
// add file extension .wss as default // Add file extension .wss as default
if !(toLowerANSI (_filename select [count _filename - 4]) in [".wav", ".ogg", ".wss"]) then { if !(toLowerANSI (_filename select [count _filename - 4]) in [".wav", ".ogg", ".wss"]) then {
_filename = format ["%1.wss", _filename]; _filename = format ["%1.wss", _filename];
}; };
playSound3D [_filename, objNull, false, _position, _volume, _soundPitch, _distance]; // Get position where to play the sound (position of the weapon)
private _position = _unit modelToWorldVisualWorld (_unit selectionPosition "RightHand");
playSound3D [_filename, objNull, insideBuilding _unit >= 0.5, _position, _volume, _soundPitch, _distance];
nil // return

View File

@ -1,7 +1,7 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: commy2 * Author: commy2
* Show firemode indicator, representing safety lock * Shows firemode indicator, representing safety lock.
* *
* Arguments: * Arguments:
* 0: Show firemode <BOOL> * 0: Show firemode <BOOL>
@ -10,7 +10,7 @@
* None * None
* *
* Example: * Example:
* [true] call ace_safemode_fnc_setSafeModeVisual * true call ace_safemode_fnc_setSafeModeVisual
* *
* Public: No * Public: No
*/ */
@ -27,8 +27,8 @@ if (_show) then {
private _config = configFile >> "RscInGameUI" >> "RscUnitInfoSoldier" >> "WeaponInfoControlsGroupLeft" >> "controls" >> "CA_ModeTexture"; private _config = configFile >> "RscInGameUI" >> "RscUnitInfoSoldier" >> "WeaponInfoControlsGroupLeft" >> "controls" >> "CA_ModeTexture";
_control ctrlSetPosition [getNumber (_config >> "x"), getNumber (_config >> "y"), getNumber (_config >> "w"), getNumber (_config >> "h")]; _control ctrlSetPosition [getNumber (_config >> "x"), getNumber (_config >> "y"), getNumber (_config >> "w"), getNumber (_config >> "h")];
_control ctrlCommit 0;
} else { } else {
_control ctrlSetPosition [0, 0, 0, 0]; _control ctrlSetPosition [0, 0, 0, 0];
_control ctrlCommit 0;
}; };
_control ctrlCommit 0;

View File

@ -1,13 +1,14 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: Brostrom.A * Author: Brostrom.A, johnb43
* Safe or unsafe the given weapon based on weapon state; locked or unlocked. * Lock or unlock the given weapon based on weapon state.
* *
* Arguments: * Arguments:
* 0: Unit <OBJECT> * 0: Unit <OBJECT>
* 1: Weapon <STRING> * 1: Weapon <STRING>
* 2: State <BOOL> * 2: State <BOOL>
* 3: Show hint <BOOL> (default: true) * 3: Show hint <BOOL> (default: true)
* 4: Muzzle <STRING> (default: current muzzle of weapon)
* *
* Return Value: * Return Value:
* None * None
@ -22,17 +23,31 @@ params [
["_unit", objNull, [objNull]], ["_unit", objNull, [objNull]],
["_weapon", "", [""]], ["_weapon", "", [""]],
["_state", true, [true]], ["_state", true, [true]],
["_hint", true, [true]] ["_hint", true, [true]],
["_muzzle", nil, [""]]
]; ];
if (_weapon == "") exitWith {}; // Don't allow to set weapon safety if unit doesn't have one (but allow removing safety, in case unit doesn't have weapon anymore)
if (_weapon == "" || {_state && {!(_unit hasWeapon _weapon)}}) exitWith {};
private _safedWeapons = _unit getVariable [QGVAR(safedWeapons), []]; // Check if weapon is a binocular
if ((_weapon call EFUNC(common,getItemType)) select 1 == "binocular") exitWith {};
_weapon = configName (configFile >> "CfgWeapons" >> _weapon); // Check for invalid muzzles
_muzzle = if (isNil "_muzzle") then {
// Get current weapon muzzle if not defined
(_unit weaponState _weapon) select 1
} else {
// Get config case muzzle names
private _muzzles = _weapon call EFUNC(common,getWeaponMuzzles);
private _muzzle = currentMuzzle _unit; _muzzles param [_muzzles findIf {_x == _muzzle}, ""]
if (_state isNotEqualTo (_weapon in _safedWeapons)) then {
[_unit, _weapon, _muzzle, _hint] call FUNC(lockSafety);
}; };
// Weapon is not available
if (_muzzle == "") exitWith {};
// If the weapon is already in the desired state, don't do anything
if (_state == (_muzzle in ((_unit getVariable [QGVAR(safedWeapons), createHashMap]) getOrDefault [_weapon, createHashMap]))) exitWith {};
[_unit, _weapon, _muzzle, _hint] call FUNC(lockSafety);

View File

@ -1,13 +1,13 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: commy2 * Author: commy2, johnb43
* Take weapon of safety lock. * Takes the weapon safety lock off.
* *
* Arguments: * Arguments:
* 0: Unit <OBJECT> * 0: Unit <OBJECT>
* 1: Weapon <STRING> * 1: Weapon <STRING>
* 2: Muzzle <STRING> * 2: Muzzle <STRING>
* 3: Show hint <BOOL> * 3: Show hint <BOOL> (default: true)
* *
* Return Value: * Return Value:
* None * None
@ -18,67 +18,37 @@
* Public: No * Public: No
*/ */
params ["_unit", "_weapon", "_muzzle", ["_hint", true, [true]]]; params ["_unit", "_weapon", "_muzzle", ["_hint", true]];
private _safedWeapons = _unit getVariable [QGVAR(safedWeapons), []]; private _safedWeaponMuzzles = (_unit getVariable QGVAR(safedWeapons)) get _weapon;
_safedWeapons deleteAt (_safedWeapons find _weapon); private _firemode = _safedWeaponMuzzles deleteAt _muzzle;
_unit setVariable [QGVAR(safedWeapons), _safedWeapons]; // Remove action if all weapons have removed their safeties
if (_safedWeaponMuzzles isEqualTo createHashMap) then {
(_unit getVariable QGVAR(safedWeapons)) deleteAt _weapon;
// remove action if all weapons have put their safety on private _ehID = _unit getVariable QGVAR(actionID);
if (_safedWeapons isEqualTo []) then {
[_unit, "DefaultAction", _unit getVariable [QGVAR(actionID), -1]] call EFUNC(common,removeActionEventHandler); if (!isNil "_ehID" && {(_unit getVariable QGVAR(safedWeapons)) isEqualTo createHashMap}) then {
_unit setVariable [QGVAR(actionID), -1]; [_unit, "DefaultAction", _ehID] call EFUNC(common,removeActionEventHandler);
_unit setVariable [QGVAR(actionID), nil];
};
}; };
private _laserEnabled = _unit isIRLaserOn _weapon || {_unit isFlashlightOn _weapon}; // Let engine handle switching to next firemode/muzzle
if (inputAction "nextWeapon" == 0 && {inputAction "prevWeapon" == 0}) then {
// This syntax of selectWeapon doesn't mess with gun lights and lasers
_unit selectWeapon [_weapon, _muzzle, _firemode];
_unit selectWeapon _muzzle; // Play fire mode selector sound
if (
_laserEnabled
&& {
_muzzle == primaryWeapon _unit // prevent UGL switch
|| {"" == primaryWeapon _unit} // Arma switches to primary weapon if exists
}
) then {
{_unit action [_x, _unit]} forEach ["GunLightOn", "IRLaserOn"];
};
if (inputAction "nextWeapon" > 0) then {
// switch to the last mode to roll over to first after the default nextWeapon action
// get weapon modes
private _modes = [];
{
if (getNumber (configFile >> "CfgWeapons" >> _weapon >> _x >> "showToPlayer") == 1) then {
_modes pushBack _x;
};
if (_x == "this") then {
_modes pushBack _weapon;
};
} forEach getArray (configFile >> "CfgWeapons" >> _weapon >> "modes");
// select last mode
private _mode = _modes select (count _modes - 1);
// switch to last mode
private _index = 0;
while {
_index < 299 && {currentMuzzle _unit != _weapon || {currentWeaponMode _unit != _mode}}
} do {
_unit action ["SwitchWeapon", _unit, _unit, _index];
_index = _index + 1;
};
} else {
// play fire mode selector sound
[_unit, _weapon, _muzzle] call FUNC(playChangeFiremodeSound); [_unit, _weapon, _muzzle] call FUNC(playChangeFiremodeSound);
}; };
// player hud // Player HUD
[true] call FUNC(setSafeModeVisual); true call FUNC(setSafeModeVisual);
// show info box unless disabled // Show info box unless disabled
if (_hint) then { if (_hint) then {
private _picture = getText (configFile >> "CfgWeapons" >> _weapon >> "picture"); [LLSTRING(TookOffSafety), getText (configFile >> "CfgWeapons" >> _weapon >> "picture")] call EFUNC(common,displayTextPicture);
[localize LSTRING(TookOffSafety), _picture] call EFUNC(common,displayTextPicture);
}; };

View File

@ -28,8 +28,8 @@ if (currentWeapon _unit != _weapon) exitWith {
}; };
// Unlock safety // Unlock safety
if (_weapon in (_unit getVariable [QEGVAR(safemode,safedWeapons), []])) exitWith { if ((["ace_safemode"] call EFUNC(common,isModLoaded)) && {[_unit, _weapon] call EFUNC(safemode,getWeaponSafety)}) exitWith {
[_unit, _weapon, _weapon] call EFUNC(safemode,unlockSafety); [_unit, _weapon, false] call EFUNC(safemode,setWeaponSafety);
}; };
private _modes = _weapon call EFUNC(common,getWeaponModes); private _modes = _weapon call EFUNC(common,getWeaponModes);

View File

@ -0,0 +1,58 @@
---
layout: wiki
title: Safemode Framework
description: Explains how to use the weapon safety API.
group: framework
order: 5
parent: wiki
mod: ace
version:
major: 3
minor: 0
patch: 0
---
## 1. Scripting
### 1.1 Setting weapon safety status
`ace_safemode_fnc_setWeaponSafety`
If you want the state of the currently selected muzzle, either pass the muzzle by name or leave it blank (= `nil`).
If the unit doesn't have a weapon, its safety can't be locked, but it can be unlocked.
```sqf
* Lock or unlock the given weapon based on weapon state.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 2: State <BOOL>
* 3: Show hint <BOOL> (default: true)
* 4: Muzzle <STRING> (default: current muzzle of weapon)
*
* Return Value:
* None
*
* Example:
* [ACE_player, currentWeapon ACE_player, true] call ace_safemode_fnc_setWeaponSafety
```
### 1.2 Getting weapon safety status
`ace_safemode_fnc_getWeaponSafety`
If you want the state of the currently selected muzzle, either pass the muzzle by name or leave it blank (= `nil`).
```sqf
* Getter for weapon safety state.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 2: Muzzle <STRING> (default: current muzzle of weapon)
*
* Return Value:
* Safety status <BOOL>
*
* Example:
* [ACE_player, currentWeapon ACE_player] call ace_safemode_fnc_getWeaponSafety
```