Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Throw Grenade context menu action #132

Merged
merged 13 commits into from
Sep 13, 2021
Merged
1 change: 1 addition & 0 deletions addons/ai/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
PREP(garrison);
PREP(throwGrenade);
PREP(unGarrison);
3 changes: 2 additions & 1 deletion addons/ai/XEH_postInit.sqf
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "script_component.hpp"

[QGVAR(unGarrison), FUNC(unGarrison)] call CBA_fnc_addEventHandler;
[QGVAR(throwGrenade), LINKFUNC(throwGrenade)] call CBA_fnc_addEventHandler;
[QGVAR(unGarrison), LINKFUNC(unGarrison)] call CBA_fnc_addEventHandler;
88 changes: 88 additions & 0 deletions addons/ai/functions/fnc_throwGrenade.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Makes the unit throw a grenade at the given position.
* Must be called where the unit is local.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Grenade Muzzle <STRING>
* 2: Position ASL <ARRAY>
*
* Return Value:
* None
*
* Example:
* [_unit, "HandGrenadeMuzzle", [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

params ["_unit", "_muzzle", "_position"];

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

// Make the unit target the helper object
_unit reveal _helper;
_unit doWatch _helper;
_unit doTarget _helper;

// Disable AI intelligence until unit throws the grenade
// Also interrupts a unit's movement to a waypoint, forcing them to stop
_unit disableAI "FSM";
_unit disableAI "PATH";

// 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 [2, CBA_missionTime];

[
{
params ["_unit", "_helper", "_startTime"];

if (!alive _unit) exitWith {false};

// 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];

_direction <= _tolerance || {_direction >= 360 - _tolerance}
},
{
params ["_unit", "_helper", "", "_muzzle"];

if (alive _unit) then {
[_unit, _muzzle] call BIS_fnc_fire;

// Reset AI intelligence and targeting
[{
params ["_unit"];

_unit enableAI "FSM";
_unit enableAI "PATH";
_unit doWatch objNull;
}, _unit, CLEANUP_DELAY] call CBA_fnc_waitAndExecute;
};

deleteVehicle _helper;
},
_this,
AIMING_TIMEOUT,
{
params ["", "_helper"];

deleteVehicle _helper;
}
] call CBA_fnc_waitUntilAndExecute;
}, [_unit, _helper, 0, _muzzle], INITIAL_DELAY] call CBA_fnc_waitAndExecute;
14 changes: 12 additions & 2 deletions addons/context_actions/CfgContext.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
class EGVAR(context_menu,actions) {
class ThrowGrenade {
displayName = CSTRING(ThrowGrenade);
icon = QPATHTOF(ui\grenade_ca.paa);
priority = -50;
insertChildren = QUOTE([_selectedObjects] call FUNC(getGrenadeActions));
};
class Formation {
displayName = "$STR_3DEN_Group_Attribute_Formation_displayName";
icon = "\a3\3den\data\displays\display3den\entitymenu\movetoformation_ca.paa";
condition = QUOTE(!(_selectedGroups isEqualTo []));
priority = -60;
class Wedge {
displayName = "$STR_wedge";
icon = "\a3\Ui_F_Curator\Data\RscCommon\RscAttributeFormation\wedge_ca.paa";
Expand Down Expand Up @@ -61,6 +68,7 @@ class EGVAR(context_menu,actions) {
class Behaviour {
displayName = "$STR_3DEN_Group_Attribute_Behaviour_displayName";
condition = QUOTE(!(_selectedGroups isEqualTo []));
priority = -61;
class Careless {
displayName = "$STR_3DEN_Attributes_Behaviour_Careless_text";
icon = QPATHTOF(ui\careless_ca.paa);
Expand Down Expand Up @@ -94,6 +102,7 @@ class EGVAR(context_menu,actions) {
class Speed {
displayName = "$STR_HC_Menu_Speed";
condition = QUOTE(!(_selectedGroups isEqualTo []));
priority = -62;
class Limited {
displayName = "$STR_speed_limited";
icon = "\a3\Ui_F_Curator\Data\RscCommon\RscAttributeSpeedMode\limited_ca.paa";
Expand All @@ -116,6 +125,7 @@ class EGVAR(context_menu,actions) {
class Stance {
displayName = "$STR_A3_RscAttributeUnitPos_Title";
condition = QUOTE(_selectedObjects findIf {_x isKindOf 'CAManBase' && {!isPlayer _x}} > -1);
priority = -63;
class Auto {
displayName = "$STR_A3_RscAttributeUnitPos_Auto_tooltip";
icon = QPATHTOF(ui\default_ca.paa);
Expand All @@ -141,6 +151,7 @@ class EGVAR(context_menu,actions) {
displayName = ECSTRING(modules,ModuleHeal);
icon = QPATHTOF(ui\medical_cross_ca.paa);
condition = QUOTE(_selectedObjects findIf {crew _x findIf {_x isKindOf 'CAManBase' && {alive _x}} != -1} != -1);
priority = -70;
class All {
displayName = ECSTRING(common,All);
statement = QUOTE([ARR_2(_selectedObjects,HEAL_MODE_ALL)] call FUNC(healUnits));
Expand All @@ -158,7 +169,6 @@ class EGVAR(context_menu,actions) {
statement = QUOTE([ARR_2(_selectedObjects,HEAL_MODE_PLAYERS)] call FUNC(healUnits));
priority = 1;
};
priority = -70;
};
class VehicleLogistics {
displayName = CSTRING(VehicleLogistics);
Expand All @@ -179,6 +189,7 @@ class EGVAR(context_menu,actions) {
} \
} != -1 \
);
priority = -71;
class Repair {
displayName = CSTRING(Repair);
icon = "\A3\ui_f\data\igui\cfg\simpleTasks\types\repair_ca.paa";
Expand Down Expand Up @@ -206,7 +217,6 @@ class EGVAR(context_menu,actions) {
statement = QUOTE(_selectedObjects call FUNC(refuelVehicles));
priority = 1;
};
priority = -71;
};
class Loadout {
displayName = "$STR_3den_display3den_entitymenu_arsenal_text";
Expand Down
3 changes: 3 additions & 0 deletions addons/context_actions/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
PREP(compileGrenades);
PREP(getGrenadeActions);
PREP(healUnits);
PREP(rearmVehicles);
PREP(refuelVehicles);
PREP(repairVehicles);
PREP(selectThrowPos);
PREP(setBehaviour);
PREP(setFormation);
PREP(setSpeed);
Expand Down
2 changes: 2 additions & 0 deletions addons/context_actions/XEH_preInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;

call FUNC(compileGrenades);

ADDON = true;
35 changes: 35 additions & 0 deletions addons/context_actions/functions/fnc_compileGrenades.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#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
*/

GVAR(grenades) = [] call CBA_fnc_createNamespace;
GVAR(grenadesList) = []; // For fast filtering of non-grenade magazines

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

{
private _muzzle = _x;

{
private _config = _cfgMagazines >> _x;
private _displayName = getText (_config >> "displayName");
private _picture = getText (_config >> "picture");

GVAR(grenades) setVariable [_x, [_displayName, _picture, _x, _muzzle]];
GVAR(grenadesList) pushBackUnique _x;
} forEach getArray (_cfgWeapons >> "Throw" >> _muzzle >> "magazines");
} forEach getArray (_cfgWeapons >> "Throw" >> "muzzles");
49 changes: 49 additions & 0 deletions addons/context_actions/functions/fnc_getGrenadeActions.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Returns children actions based on grenades in selected AI's inventories.
*
* Arguments:
* 0: Selected Objects <ARRAY>
*
* Return Value:
* Actions <ARRAY>
*
* Example:
* [[_unit1, _unit2]] call zen_context_actions_fnc_getGrenadeActions
*
* Public: No
*/

params ["_objects"];

// Get all magazines in the inventories of on foot AI
private _magazines = [];
{
if (!isPlayer _x && {vehicle _x == _x}) then {
_magazines append magazines _x;
};
} forEach _objects;

// Filter out non-grenade magazines, sort alphabetically by display name
private _grenades = _magazines arrayIntersect GVAR(grenadesList) apply {GVAR(grenades) getVariable _x};
_grenades sort true;

private _actions = [];

{
_x params ["_displayName", "_picture", "_magazine", "_muzzle"];

private _action = [
format [QGVAR(%1), _forEachIndex],
_displayName,
_picture,
{[_selectedObjects, _args] call FUNC(selectThrowPos)},
{true},
[_magazine, _muzzle]
] call EFUNC(context_menu,createAction);

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

_actions
45 changes: 45 additions & 0 deletions addons/context_actions/functions/fnc_selectThrowPos.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "script_component.hpp"
/*
* Author: mharis001
* Allows Zeus to select a position to make AI throw grenades at.
*
* Arguments:
* 0: Selected Objects <ARRAY>
* 1: Arguments <ARRAY>
*
* Return Value:
* None
*
* Example:
* [[_unit1, _unit2], ["HandGrenade", "HandGrenadeMuzzle"]] call zen_context_actions_fnc_selectThrowPos
*
* Public: No
*/

#define MAX_DISTANCE 50

params ["_selectedObjects", "_args"];
_args params ["_magazine", "_muzzle"];

// Get AI units that are alive, on foot, and have the selected grenade type
private _units = _selectedObjects select {
alive _x
&& {!isPlayer _x}
&& {_x isKindOf "CAManBase"}
&& {vehicle _x == _x}
&& {_magazine in magazines _x}
};

private _text = format [localize LSTRING(ThrowX), getText (configFile >> "CfgMagazines" >> _magazine >> "displayName")];

[_units, {
params ["_successful", "_units", "_position", "_muzzle"];

if (!_successful) exitWith {};

{
if (_x distance2D _position < MAX_DISTANCE) then {
[QEGVAR(ai,throwGrenade), [_x, _muzzle, _position], _x] call CBA_fnc_targetEvent;
};
} forEach _units;
}, _muzzle, _text] call EFUNC(common,getTargetPos);
6 changes: 6 additions & 0 deletions addons/context_actions/stringtable.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ZEN">
<Package name="Context_Actions">
<Key ID="STR_ZEN_Context_Actions_ThrowGrenade">
<English>Throw Grenade</English>
</Key>
<Key ID="STR_ZEN_Context_Actions_ThrowX">
<English>Throw %1</English>
</Key>
<Key ID="STR_ZEN_Context_Actions_EditInventory">
<English>Edit Inventory</English>
<Russian>Редактировать снаряжение</Russian>
Expand Down
Binary file added addons/context_actions/ui/grenade_ca.paa
Binary file not shown.