|
| 1 | +#include "script_component.hpp" |
| 2 | +/* |
| 3 | + * Author: mharis001 |
| 4 | + * Makes the unit throw a grenade at the given position. |
| 5 | + * |
| 6 | + * Arguments: |
| 7 | + * 0: Unit <OBJECT> |
| 8 | + * 1: Grenade Magazine <STRING> |
| 9 | + * 2: Position ASL <ARRAY> |
| 10 | + * |
| 11 | + * Return Value: |
| 12 | + * None |
| 13 | + * |
| 14 | + * Example: |
| 15 | + * [_unit, "HandGrenade", [0, 0, 0]] call zen_ai_fnc_throwGrenade |
| 16 | + * |
| 17 | + * Public: No |
| 18 | + */ |
| 19 | + |
| 20 | +#define AIMING_TIMEOUT 10 |
| 21 | +#define INITIAL_DELAY 0.5 |
| 22 | +#define CLEANUP_DELAY 1.5 |
| 23 | + |
| 24 | +#define TOLERANCE_TIME 2 |
| 25 | +#define MIN_TOLERANCE 10 |
| 26 | +#define MAX_TOLERANCE 45 |
| 27 | + |
| 28 | +#define DISABLED_ABILITIES ["AIMINGERROR", "AUTOTARGET", "FSM", "PATH", "SUPPRESSION", "TARGET"] |
| 29 | + |
| 30 | +params [ |
| 31 | + ["_unit", objNull, [objNull]], |
| 32 | + ["_magazine", "", [""]], |
| 33 | + ["_position", [0, 0, 0], [[]], 3] |
| 34 | +]; |
| 35 | + |
| 36 | +if (!local _unit) exitWith { |
| 37 | + [QGVAR(throwGrenade), _this, _unit] call CBA_fnc_targetEvent; |
| 38 | +}; |
| 39 | + |
| 40 | +// Exit if the unit cannot throw the given grenade type |
| 41 | +if !([_unit, _magazine] call FUNC(canThrowGrenade)) exitWith {}; |
| 42 | + |
| 43 | +// Disable AI abilities to make units more responsive to commands |
| 44 | +// Also interrupts a unit's movement to a waypoint, forcing them to stop |
| 45 | +private _abilities = DISABLED_ABILITIES apply {_unit checkAIFeature _x}; |
| 46 | +{_unit disableAI _x} forEach DISABLED_ABILITIES; |
| 47 | + |
| 48 | +// Set the unit's behaviour to COMBAT to make them raise their weapon and aim at the target |
| 49 | +private _behaviour = combatBehaviour _unit; |
| 50 | +_unit setCombatBehaviour "COMBAT"; |
| 51 | + |
| 52 | +// Set the unit's combat mode to BLUE to make them hold their fire |
| 53 | +// The unit will still track the target but only fire when told to |
| 54 | +private _combatMode = unitCombatMode _unit; |
| 55 | +_unit setUnitCombatMode "BLUE"; |
| 56 | + |
| 57 | +// Prevent the unit from changing stance due to combat behaviour by |
| 58 | +// forcing a unit position that matches their current stance |
| 59 | +// Usually only a problem with the AUTO unit position |
| 60 | +private _unitPos = unitPos _unit; |
| 61 | +private _stanceIndex = ["STAND", "CROUCH", "PRONE"] find stance _unit; |
| 62 | +private _unitPosForStance = ["UP", "MIDDLE", "DOWN"] param [_stanceIndex, "UP"]; |
| 63 | +_unit setUnitPos _unitPosForStance; |
| 64 | + |
| 65 | +// Create a helper object for the unit to target |
| 66 | +private _helper = createVehicle ["CBA_B_InvisibleTargetVehicle", [0, 0, 0], [], 0, "CAN_COLLIDE"]; |
| 67 | +_helper setPosASL _position; |
| 68 | + |
| 69 | +// Make the unit target the helper object |
| 70 | +_unit reveal [_helper, 4]; |
| 71 | +_unit lookAt _helper; |
| 72 | +_unit doWatch _helper; |
| 73 | +_unit doTarget _helper; |
| 74 | + |
| 75 | +// Wait until the unit is aiming at the helper object before throwing the grenade |
| 76 | +// Initial delay helps prevent weird issue when the unit is moving to a waypoint and the helper is directly in front of it |
| 77 | +[{ |
| 78 | + _this set [7, CBA_missionTime]; |
| 79 | + |
| 80 | + [{ |
| 81 | + params ["_unit", "_helper", "_magazine", "_abilities", "_behaviour", "_combatMode", "_unitPos", "_startTime"]; |
| 82 | + |
| 83 | + // Check that the unit is aiming at the helper, increase tolerance as more time passes |
| 84 | + private _direction = _unit getRelDir _helper; |
| 85 | + private _tolerance = linearConversion [0, TOLERANCE_TIME, CBA_missionTime - _startTime, MIN_TOLERANCE, MAX_TOLERANCE, true]; |
| 86 | + private _throwGrenade = _direction <= _tolerance || {_direction >= 360 - _tolerance}; |
| 87 | + |
| 88 | + if (_throwGrenade) then { |
| 89 | + private _index = magazines _unit find _magazine; |
| 90 | + |
| 91 | + if (_index != -1) then { |
| 92 | + private _magazine = magazinesDetail _unit select _index; |
| 93 | + _magazine call EFUNC(common,parseMagazineDetail) params ["_id", "_owner"]; |
| 94 | + CBA_logic action ["UseMagazine", _unit, _unit, _owner, _id]; |
| 95 | + }; |
| 96 | + }; |
| 97 | + |
| 98 | + if ( |
| 99 | + _throwGrenade |
| 100 | + || {!alive _unit} |
| 101 | + || {isNull _helper} |
| 102 | + || {CBA_missionTime >= _startTime + AIMING_TIMEOUT} |
| 103 | + ) exitWith { |
| 104 | + // Restore AI abilities, behaviour, combat mode, stance, and targeting |
| 105 | + // Small delay before cleanup to give the unit time to finish throwing the grenade |
| 106 | + [{ |
| 107 | + params ["_unit", "_helper", "_abilities", "_behaviour", "_combatMode", "_unitPos"]; |
| 108 | + |
| 109 | + { |
| 110 | + if (_x) then { |
| 111 | + _unit enableAI (DISABLED_ABILITIES select _forEachIndex); |
| 112 | + }; |
| 113 | + } forEach _abilities; |
| 114 | + |
| 115 | + _unit setCombatBehaviour _behaviour; |
| 116 | + _unit setUnitCombatMode _combatMode; |
| 117 | + _unit setUnitPos _unitPos; |
| 118 | + _unit doWatch objNull; |
| 119 | + _unit lookAt objNull; |
| 120 | + |
| 121 | + deleteVehicle _helper; |
| 122 | + }, [_unit, _helper, _abilities, _behaviour, _combatMode, _unitPos], CLEANUP_DELAY] call CBA_fnc_waitAndExecute; |
| 123 | + |
| 124 | + true // Exit |
| 125 | + }; |
| 126 | + |
| 127 | + false // Continue |
| 128 | + }, {}, _this] call CBA_fnc_waitUntilAndExecute; |
| 129 | +}, [_unit, _helper, _magazine, _abilities, _behaviour, _combatMode, _unitPos], INITIAL_DELAY] call CBA_fnc_waitAndExecute; |
0 commit comments