Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ bux
CesarStef
classicarma
commy2
das attorney
dedmen
DeliciousJaffa
diaverso
Expand Down
2 changes: 2 additions & 0 deletions addons/ai/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
PREP(applySkills);
PREP(canThrowGrenade);
PREP(garrison);
PREP(handleObjectEdited);
PREP(handleSkillsChange);
PREP(initMan);
PREP(searchBuilding);
PREP(throwGrenade);
PREP(unGarrison);
2 changes: 2 additions & 0 deletions addons/ai/XEH_postInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ QGVAR(skills) addPublicVariableEventHandler LINKFUNC(handleSkillsChange);

["CAManBase", "InitPost", LINKFUNC(initMan), true, [], false] call CBA_fnc_addClassEventHandler;

[QGVAR(throwGrenade), LINKFUNC(throwGrenade)] call CBA_fnc_addEventHandler;

[QGVAR(unGarrison), LINKFUNC(unGarrison)] call CBA_fnc_addEventHandler;
28 changes: 28 additions & 0 deletions addons/ai/functions/fnc_canThrowGrenade.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Checks if the given unit can throw a grenade.
* When specified, also checks if the unit has the given grenade magazine.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Grenade Magazine <STRING> (default: "")
*
* Return Value:
* Can Throw Grenade <BOOL>
*
* Example:
* [_unit, "HandGrenade"] call zen_ai_fnc_canThrowGrenade
*
* Public: No
*/

params [["_unit", objNull, [objNull]], ["_magazine", "", [""]]];

alive _unit
&& {!isPlayer _unit}
&& {vehicle _unit == _unit}
&& {_unit isKindOf "CAManBase"}
&& {lifeState _unit in ["HEALTHY", "INJURED"]}
&& {!(_unit call EFUNC(common,isSwimming))}
&& {_magazine == "" || {_magazine in magazines _unit}}
129 changes: 129 additions & 0 deletions addons/ai/functions/fnc_throwGrenade.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Makes the unit throw a grenade at the given position.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Grenade Magazine <STRING>
* 2: Position ASL <ARRAY>
*
* Return Value:
* None
*
* Example:
* [_unit, "HandGrenade", [0, 0, 0]] call zen_ai_fnc_throwGrenade
*
* Public: No
*/

#define AIMING_TIMEOUT 10
#define INITIAL_DELAY 0.5
#define CLEANUP_DELAY 1.5

#define TOLERANCE_TIME 2
#define MIN_TOLERANCE 10
#define MAX_TOLERANCE 45

#define DISABLED_ABILITIES ["AIMINGERROR", "AUTOTARGET", "FSM", "PATH", "SUPPRESSION", "TARGET"]

params [
["_unit", objNull, [objNull]],
["_magazine", "", [""]],
["_position", [0, 0, 0], [[]], 3]
];

if (!local _unit) exitWith {
[QGVAR(throwGrenade), _this, _unit] call CBA_fnc_targetEvent;
};

// Exit if the unit cannot throw the given grenade type
if !([_unit, _magazine] call FUNC(canThrowGrenade)) exitWith {};

// Disable AI abilities to make units more responsive to commands
// Also interrupts a unit's movement to a waypoint, forcing them to stop
private _abilities = DISABLED_ABILITIES apply {_unit checkAIFeature _x};
{_unit disableAI _x} forEach DISABLED_ABILITIES;

// Set the unit's behaviour to COMBAT to make them raise their weapon and aim at the target
private _behaviour = combatBehaviour _unit;
_unit setCombatBehaviour "COMBAT";

// Set the unit's combat mode to BLUE to make them hold their fire
// The unit will still track the target but only fire when told to
private _combatMode = unitCombatMode _unit;
_unit setUnitCombatMode "BLUE";

// Prevent the unit from changing stance due to combat behaviour by
// forcing a unit position that matches their current stance
// Usually only a problem with the AUTO unit position
private _unitPos = unitPos _unit;
private _stanceIndex = ["STAND", "CROUCH", "PRONE"] find stance _unit;
private _unitPosForStance = ["UP", "MIDDLE", "DOWN"] param [_stanceIndex, "UP"];
_unit setUnitPos _unitPosForStance;

// Create a helper object for the unit to target
private _helper = createVehicle ["CBA_B_InvisibleTargetVehicle", [0, 0, 0], [], 0, "CAN_COLLIDE"];
_helper setPosASL _position;

// Make the unit target the helper object
_unit reveal [_helper, 4];
_unit lookAt _helper;
_unit doWatch _helper;
_unit doTarget _helper;

// Wait until the unit is aiming at the helper object before throwing the grenade
// Initial delay helps prevent weird issue when the unit is moving to a waypoint and the helper is directly in front of it
[{
_this set [7, CBA_missionTime];

[{
params ["_unit", "_helper", "_magazine", "_abilities", "_behaviour", "_combatMode", "_unitPos", "_startTime"];

// Check that the unit is aiming at the helper, increase tolerance as more time passes
private _direction = _unit getRelDir _helper;
private _tolerance = linearConversion [0, TOLERANCE_TIME, CBA_missionTime - _startTime, MIN_TOLERANCE, MAX_TOLERANCE, true];
private _throwGrenade = _direction <= _tolerance || {_direction >= 360 - _tolerance};

if (_throwGrenade) then {
private _index = magazines _unit find _magazine;

if (_index != -1) then {
private _magazine = magazinesDetail _unit select _index;
_magazine call EFUNC(common,parseMagazineDetail) params ["_id", "_owner"];
CBA_logic action ["UseMagazine", _unit, _unit, _owner, _id];
};
};

if (
_throwGrenade
|| {!alive _unit}
|| {isNull _helper}
|| {CBA_missionTime >= _startTime + AIMING_TIMEOUT}
) exitWith {
// Restore AI abilities, behaviour, combat mode, stance, and targeting
// Small delay before cleanup to give the unit time to finish throwing the grenade
[{
params ["_unit", "_helper", "_abilities", "_behaviour", "_combatMode", "_unitPos"];

{
if (_x) then {
_unit enableAI (DISABLED_ABILITIES select _forEachIndex);
};
} forEach _abilities;

_unit setCombatBehaviour _behaviour;
_unit setUnitCombatMode _combatMode;
_unit setUnitPos _unitPos;
_unit doWatch objNull;
_unit lookAt objNull;

deleteVehicle _helper;
}, [_unit, _helper, _abilities, _behaviour, _combatMode, _unitPos], CLEANUP_DELAY] call CBA_fnc_waitAndExecute;

true // Exit
};

false // Continue
}, {}, _this] call CBA_fnc_waitUntilAndExecute;
}, [_unit, _helper, _magazine, _abilities, _behaviour, _combatMode, _unitPos], INITIAL_DELAY] call CBA_fnc_waitAndExecute;
2 changes: 2 additions & 0 deletions addons/common/XEH_PREP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ PREP(initSliderEdit);
PREP(isInScreenshotMode);
PREP(isPlacementActive);
PREP(isRemoteControlled);
PREP(isSwimming);
PREP(isVLS);
PREP(isWeapon);
PREP(loadMagazineInstantly);
PREP(messageBox);
PREP(openArsenal);
PREP(parseMagazineDetail);
PREP(runAfterSettingsInit);
PREP(selectPosition);
PREP(serializeInventory);
Expand Down
20 changes: 20 additions & 0 deletions addons/common/functions/fnc_isSwimming.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "script_component.hpp"
/*
* Author: das attorney, Jonpas
* Checks if the given unit is swimming (surface swimming or diving).
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* Is Swimming <BOOL>
*
* Example:
* [_unit] call zen_common_fnc_isSwimming
*
* Public: Yes
*/

params [["_unit", objNull, [objNull]]];

animationState _unit select [1, 3] in ["bdv", "bsw", "dve", "sdv", "ssw", "swm"]
23 changes: 23 additions & 0 deletions addons/common/functions/fnc_parseMagazineDetail.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Parses the given magazine detail string and returns the magazine's ID and creator.
*
* Arguments:
* 0: Magazine Detail <STRING>
*
* Return Value:
* Parsed Data <ARRAY>
* 0: ID <NUMBER>
* 1: Creator <NUMBER>
*
* Example:
* ["6.5 mm 30Rnd Sand Mag(30/30)[id/cr:10000001/0]"] call zen_common_fnc_parseMagazineDetail
*
* Public: No
*/

params [["_magazine", "", [""]]];

private _split = _magazine splitString "[:/]";
_split select [count _split - 2, 2] apply {parseNumber _x}
6 changes: 6 additions & 0 deletions addons/context_actions/CfgContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ class EGVAR(context_menu,actions) {
insertChildren = QUOTE(_objects call FUNC(getArtilleryActions));
priority = 70;
};
class ThrowGrenade {
displayName = CSTRING(ThrowGrenade);
icon = QPATHTOF(ui\grenade_ca.paa);
insertChildren = QUOTE(_objects call FUNC(getGrenadeActions));
priority = 70;
};
class Formation {
displayName = "$STR_3DEN_Group_Attribute_Formation_displayName";
condition = QUOTE(_groups findIf {units _x findIf {!isPlayer _x} != -1} != -1);
Expand Down
3 changes: 3 additions & 0 deletions addons/context_actions/XEH_PREP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ PREP(canRepairVehicles);
PREP(canToggleSurrender);
PREP(canUnloadViV);
PREP(copyVehicleAppearance);
PREP(compileGrenades);
PREP(getArtilleryActions);
PREP(getGrenadeActions);
PREP(healUnits);
PREP(openEditableObjectsDialog);
PREP(pasteVehicleAppearance);
PREP(rearmVehicles);
PREP(refuelVehicles);
PREP(repairVehicles);
PREP(selectArtilleryPos);
PREP(selectThrowPos);
PREP(setBehaviour);
PREP(setCombatMode);
PREP(setFormation);
Expand Down
2 changes: 2 additions & 0 deletions addons/context_actions/XEH_preStart.sqf
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "script_component.hpp"

#include "XEH_PREP.hpp"

call FUNC(compileGrenades);
32 changes: 32 additions & 0 deletions addons/context_actions/functions/fnc_compileGrenades.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Compiles a list of all throwable grenades.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call zen_context_actions_fnc_compileGrenades
*
* Public: No
*/

private _grenades = createHashMap;
private _cfgWeapons = configFile >> "CfgWeapons";
private _cfgMagazines = configFile >> "CfgMagazines";

{
{
private _config = _cfgMagazines >> _x;
private _name = getText (_config >> "displayName");
private _icon = getText (_config >> "picture");

_grenades set [configName _config, [_name, _icon]];
} forEach getArray (_cfgWeapons >> "Throw" >> _x >> "magazines");
} forEach getArray (_cfgWeapons >> "Throw" >> "muzzles");

uiNamespace setVariable [QGVAR(grenades), _grenades];
54 changes: 54 additions & 0 deletions addons/context_actions/functions/fnc_getGrenadeActions.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Returns children actions for grenades in unit inventories.
*
* Arguments:
* N: Objects <OBJECT>
*
* Return Value:
* Actions <ARRAY>
*
* Example:
* [_object] call zen_context_actions_fnc_getGrenadeActions
*
* Public: No
*/

// Get all magazines in the inventories of units that can throw grenades
private _magazines = flatten (_this select {
_x call EFUNC(ai,canThrowGrenade)
} apply {
magazines _x
});

// Filter out non-grenade magazines and sort them alphabetically by name
private _cache = uiNamespace getVariable QGVAR(grenades);
private _grenades = _magazines arrayIntersect keys _cache apply {
(_cache get _x) params ["_name", "_icon"];
[_name, _icon, _x]
};

_grenades sort true;

// Create actions for every grenade type
private _actions = [];

{
_x params ["_name", "_icon", "_magazine"];

private _action = [
_magazine,
_name,
_icon,
{
[_objects, _args] call FUNC(selectThrowPos);
},
{true},
_magazine
] call EFUNC(context_menu,createAction);

_actions pushBack [_action, [], 0];
} forEach _grenades;

_actions
Loading