Add Advanced Throwing (#3477)

* Add Advanced Throwing

* Add feature documentation
This commit is contained in:
jonpas 2016-08-22 15:09:08 +02:00 committed by GitHub
parent 9a0d569c56
commit f8dfa4f933
36 changed files with 1350 additions and 4 deletions

View File

@ -0,0 +1 @@
z\ace\addons\advancedthrowing

View File

@ -0,0 +1,40 @@
class ACE_Settings {
class GVAR(enabled) {
category = CSTRING(Category);
displayName = CSTRING(Enable_DisplayName);
description = CSTRING(Enable_Description);
typeName = "BOOL";
value = 1;
isClientSettable = 1;
};
class GVAR(showThrowArc) {
category = CSTRING(Category);
displayName = CSTRING(ShowThrowArc_DisplayName);
description = CSTRING(ShowThrowArc_Description);
typeName = "BOOL";
value = 1;
isClientSettable = 1;
};
class GVAR(showMouseControls) {
category = CSTRING(Category);
displayName = CSTRING(ShowMouseControls_DisplayName);
description = CSTRING(ShowMouseControls_Description);
typeName = "BOOL";
value = 1;
isClientSettable = 1;
};
class GVAR(enablePickUp) {
category = CSTRING(Category);
displayName = CSTRING(EnablePickUp_DisplayName);
description = CSTRING(EnablePickUp_Description);
typeName = "BOOL";
value = 1;
};
class GVAR(enablePickUpAttached) {
category = CSTRING(Category);
displayName = CSTRING(EnablePickUpAttached_DisplayName);
description = CSTRING(EnablePickUpAttached_Description);
typeName = "BOOL";
value = 1;
};
};

View File

@ -0,0 +1,17 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
clientInit = QUOTE(call COMPILE_FILE(XEH_postInitClient));
};
};

View File

@ -0,0 +1,72 @@
class CBA_Extended_EventHandlers;
class CfgVehicles {
class ACE_Module;
class GVAR(Module): ACE_Module {
author = ECSTRING(common,ACETeam);
category = "ACE";
displayName = CSTRING(Category);
function = QFUNC(moduleInit);
scope = 2;
isGlobal = 1;
icon = QPATHTOF(UI\Icon_Module_AdvancedThrowing_ca.paa);
class Arguments {
class enabled {
displayName = CSTRING(Enable_DisplayName);
description = CSTRING(Enable_Description);
typeName = "BOOL";
defaultValue = 1;
};
class showThrowArc {
displayName = CSTRING(ShowThrowArc_DisplayName);
description = CSTRING(ShowThrowArc_Description);
typeName = "BOOL";
defaultValue = 1;
};
class showMouseControls {
displayName = CSTRING(ShowMouseControls_DisplayName);
description = CSTRING(ShowMouseControls_Description);
typeName = "BOOL";
defaultValue = 1;
};
class enablePickUp {
displayName = CSTRING(EnablePickUp_DisplayName);
description = CSTRING(EnablePickUp_Description);
typeName = "BOOL";
defaultValue = 1;
};
class enablePickUpAttached {
displayName = CSTRING(EnablePickUpAttached_DisplayName);
description = CSTRING(EnablePickUpAttached_Description);
typeName = "BOOL";
defaultValue = 1;
};
};
class ModuleDescription {
description = CSTRING(Module_Description);
};
};
class Items_base_F;
class GVAR(pickUpHelper): Items_base_F {
author = ECSTRING(common,ACETeam);
displayName = "ACE Throwable Pick Up Helper";
model = "\a3\weapons_f\dummyweapon.p3d";
scope = 1;
class ACE_Actions {
class GVAR(pickUp) {
displayName = CSTRING(PickUp);
condition = QUOTE(_player call FUNC(canPrepare));
statement = QUOTE(_this call FUNC(pickUp));
distance = 1.8; // Requires >1.7 to work when standing with weapon on back
icon = "\a3\ui_f\data\igui\cfg\actions\obsolete\ui_action_takemine_ca.paa";
};
};
class EventHandlers {
class CBA_Extended_EventHandlers: CBA_Extended_EventHandlers {};
};
};
};

View File

@ -0,0 +1,11 @@
ace_advancedthrowing
===================
Integrates advanced throwing by [Dslyecxi](https://github.com/dslyecxi).
## Maintainers
The people responsible for merging changes to this component or answering potential questions.
- [Jonpas](https://github.com/jonpas)

View File

@ -0,0 +1,17 @@
PREP(canPrepare);
PREP(canThrow);
PREP(drawArc);
PREP(drawThrowable);
PREP(exitThrowMode);
PREP(getMuzzle);
PREP(moduleInit);
PREP(onKeyDown);
PREP(onMouseButtonDown);
PREP(onMouseScroll);
PREP(pickUp);
PREP(prepare);
PREP(prime);
PREP(renderPickUpInteraction);
PREP(throw);
PREP(throwFiredXEH);
PREP(updateControlsHint);

View File

@ -0,0 +1,107 @@
#include "script_component.hpp"
// Exit on HC
if (!hasInterface) exitWith {};
// Ammo/Magazines look-up hash for correctness of initSpeed
GVAR(ammoMagLookup) = call CBA_fnc_createNamespace;
{
{
private _ammo = getText (configFile >> "CfgMagazines" >> _x >> "ammo");
GVAR(ammoMagLookup) setVariable [_ammo, _x];
} count (getArray (configFile >> "CfgWeapons" >> "Throw" >> _x >> "magazines"));
nil
} count getArray (configFile >> "CfgWeapons" >> "Throw" >> "muzzles");
// Add keybinds
["ACE3 Weapons", QGVAR(prepare), localize LSTRING(Prepare), {
// Condition
if (!([ACE_player] call FUNC(canPrepare))) exitWith {false};
// Statement
[ACE_player] call FUNC(prepare);
true
}, {false}, [34, [true, false, false]], false] call CBA_fnc_addKeybind; // Shift + G
["ACE3 Weapons", QGVAR(dropModeToggle), localize LSTRING(DropModeToggle), {
// Condition
if !(ACE_player getVariable [QGVAR(inHand), false]) exitWith {false};
// Statement
private _currentDropMode = ACE_player getVariable [QGVAR(dropMode), false];
ACE_player setVariable [QGVAR(dropMode), !_currentDropMode];
ACE_player setVariable [QGVAR(throwType), THROW_TYPE_DEFAULT]; // Reset for consistency when opening
call FUNC(updateControlsHint); // Change controls hint for MMB
true
}, {false}, [34, [false, true, false]], false] call CBA_fnc_addKeybind; // Ctrl + G
["ACE3 Weapons", QGVAR(dropModeHold), localize LSTRING(DropModeHold), {
// Condition
if !(ACE_player getVariable [QGVAR(inHand), false]) exitWith {false};
// Statement
ACE_player setVariable [QGVAR(dropMode), true];
ACE_player setVariable [QGVAR(throwType), THROW_TYPE_DEFAULT]; // Reset for consistency when opening
call FUNC(updateControlsHint); // Change controls hint for MMB
true
}, {
// Condition
if !(ACE_player getVariable [QGVAR(inHand), false]) exitWith {false};
// Statement
ACE_player setVariable [QGVAR(dropMode), false];
call FUNC(updateControlsHint); // Change controls hint for MMB
true
}, [0, [false, false, false]], false] call CBA_fnc_addKeybind; // Empty
// Event handlers
["unit", {
[_this select 1, "Player changed"] call FUNC(exitThrowMode);
}] call CBA_fnc_addPlayerEventhandler;
["ace_interactMenuOpened", {
// Exit if advanced throwing is disabled (pick up only supports advanced throwing)
if (!GVAR(enabled)) exitWith {};
if (ACE_player getVariable [QGVAR(inHand), false]) then {
[ACE_player, "Interact menu opened"] call FUNC(exitThrowMode);
} else {
params ["_interactionType"];
// Ignore self-interaction menu, when in vehicle and when pick up is disabled
if (GVAR(enablePickUp) && {_interactionType == 0} && {vehicle ACE_player == ACE_player}) then {
// Show pick up actions on CfgAmmo's
call FUNC(renderPickUpInteraction);
};
};
}] call CBA_fnc_addEventHandler;
// Fired XEH
[QGVAR(throwFiredXEH), FUNC(throwFiredXEH)] call CBA_fnc_addEventHandler;
// Display handlers
["KeyDown", {_this call FUNC(onKeyDown)}] call CBA_fnc_addDisplayHandler;
["MouseButtonDown", {_this call FUNC(onMouseButtonDown)}] call CBA_fnc_addDisplayHandler;
["MouseZChanged", {_this call FUNC(onMouseScroll)}] call CBA_fnc_addDisplayHandler;
#ifdef DRAW_THROW_PATH
GVAR(predictedPath) = [];
GVAR(flightPath) = [];
addMissionEventHandler ["Draw3D", { // Blue is predicted before throw, red is real
{
_x params ["", "_newTrajAGL"];
drawIcon3D ["\a3\ui_f\data\gui\cfg\hints\icon_text\group_1_ca.paa", [0,0,1,1], _newTrajAGL, 1, 1, 0, "", 2];
} forEach GVAR(predictedPath);
{
_newTrajAGL = _x;
drawIcon3D ["\a3\ui_f\data\gui\cfg\hints\icon_text\group_1_ca.paa", [1,0,0,1], _newTrajAGL, 1, 1, 0, "", 2];
} forEach GVAR(flightPath)
}];
#endif

View File

@ -0,0 +1,7 @@
#include "script_component.hpp"
ADDON = false;
#include "XEH_PREP.hpp"
ADDON = true;

View File

@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@ -0,0 +1,19 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_common", "ace_weaponselect"};
author = ECSTRING(common,ACETeam);
authors[] = {"Jonpas", "Dslyecxi", "Zapat"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
#include "ACE_Settings.hpp"
#include "CfgEventHandlers.hpp"
#include "CfgVehicles.hpp"

View File

@ -0,0 +1,31 @@
/*
* Author: Jonpas
* Checks if a throwable can be prepared.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* Can Prepare <BOOL>
*
* Example:
* [unit] call ace_advancedthrowing_fnc_canPrepare
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit"];
GVAR(enabled) &&
#ifndef DEBUG_MODE_FULL
{_unit getVariable [QGVAR(lastThrownTime), CBA_missionTime - 3] < CBA_missionTime - 2} && // Prevent throwing in quick succession
#else
{true} &&
#endif
{!(call EFUNC(common,isFeatureCameraActive))} &&
{!EGVAR(common,isReloading)} &&
{[_unit, objNull, ["isNotInside", "isNotSitting"/*, "isNotOnLadder"*/]] call EFUNC(common,canInteractWith)} && // Ladder needs positioning fixes on throw
{_unit call CBA_fnc_canUseWeapon} // Disable in non-FFV seats due to surface detection issues

View File

@ -0,0 +1,31 @@
/*
* Author: Jonpas
* Checks if a throwable can be thrown.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* Can Throw <BOOL>
*
* Example:
* [unit] call ace_advancedthrowing_fnc_canThrow
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit"];
if !(_unit getVariable [QGVAR(inHand), false]) exitWith {false};
if (vehicle _unit != _unit) exitWith {
private _startPos = eyePos _unit;
private _aimLinePos = AGLToASL (positionCameraToWorld [0, 0, 1]);
private _intersections = lineIntersectsSurfaces [_startPos, _aimLinePos, _unit, objNull, false];
//TRACE_1("Intersections",_intersections);
(_intersections select {(vehicle _unit) in (_x select 3)}) isEqualTo []
};
true

View File

@ -0,0 +1,87 @@
/*
* Author: Zapat, Dslyecxi, Jonpas
* Draws throw arc.
*
* Arguments:
* None
*
* Return Value:
* Flight path (just for debug) <ARRAY>
*
* Example:
* call ace_advancedthrowing_fnc_drawArc
*
* Public: No
*/
#include "script_component.hpp"
// Disable drawing when intersecting with the vehicle
if !([ACE_player] call FUNC(canThrow)) exitWith {
drawIcon3D ["\a3\ui_f\data\igui\cfg\actions\obsolete\ui_action_cancel_manualfire_ca.paa", [1, 0, 0, 1], positionCameraToWorld [0, 0, 1], 1, 1, 0, "", 1];
};
private _activeThrowable = ACE_player getVariable [QGVAR(activeThrowable), objNull];
// Exit during switches and similar where object can be null for a very short amount of time
if (isNull _activeThrowable) exitWith {};
private _dropMode = ACE_player getVariable [QGVAR(dropMode), false];
private _throwType = ACE_player getVariable [QGVAR(throwType), THROW_TYPE_DEFAULT];
private _throwSpeed = ACE_player getVariable [QGVAR(throwSpeed), THROW_SPEED_DEFAULT];
private _direction = [THROWSTYLE_NORMAL_DIR, THROWSTYLE_HIGH_DIR] select (_throwType == "high" || {_dropMode});
private _velocity = [_throwSpeed, _throwSpeed / THROWSTYLE_HIGH_VEL_COEF / 1.25] select (_throwType == "high");
_velocity = [_velocity, THROWSTYLE_DROP_VEL] select _dropMode;
private _viewStart = AGLToASL (positionCameraToWorld [0, 0, 0]);
private _viewEnd = AGLToASL (positionCameraToWorld _direction);
private _initialVelocity = (vectorNormalized (_viewEnd vectorDiff _viewStart)) vectorMultiply (_velocity);
private _prevTrajASL = getPosASLVisual _activeThrowable;
private _pathData = [];
for "_i" from 0.05 to 1.45 step 0.1 do {
private _newTrajASL = (getPosASLVisual _activeThrowable) vectorAdd (_initialVelocity vectorMultiply _i) vectorAdd ([0, 0, -4.9] vectorMultiply (_i * _i));
private _cross = 0;
if (_newTrajASL distance (getPosASLVisual ACE_player) <= 20) then {
if ((ASLToATL _newTrajASL) select 2 <= 0) then {
_cross = 1
} else {
// Even vanilla throwables go through glass, only "GEOM" LOD will stop it but that will also stop it when there is glass in a window
if (lineIntersects [_prevTrajASL, _newTrajASL]) then {
_cross = 2;
};
};
private _iDim = linearConversion [20, 0, _newTrajASL distance (getPosASLVisual ACE_player), 0.3, 2.5, true];
private _alpha = linearConversion [20, 0, _newTrajASL distance (getPosASLVisual ACE_player), 0.05, 0.7, true];
private _movePerc = linearConversion [3, 0, vectorMagnitude (velocity ACE_player), 0, 1, true];
_alpha = _alpha * _movePerc;
private _col = [ [1, 1, 1, _alpha], [0, 1, 0, _alpha], [1, 0, 0, _alpha] ] select _cross;
if (_cross != 2 && {lineIntersects [eyePos ACE_player, _newTrajASL]}) then {
_col set [3, 0.1]
};
_pathData pushBack [_col, ASLToAGL _newTrajASL, _iDim];
};
if (_cross > 0) exitWith {};
_prevTrajASL = _newTrajASL;
};
reverse _pathData;
// To get the sort order correct from our POV, particularly when using outlined dots
{
_x params ["_col", "_newTrajAGL", "_iDim"];
drawIcon3D ["\a3\ui_f\data\gui\cfg\hints\icon_text\group_1_ca.paa", _col, _newTrajAGL, _iDim, _iDim, 0, "", 2];
#ifdef DEBUG_MODE_FULL
drawIcon3D ["", _col, _newTrajAGL, _iDim, _iDim, 0, str (ACE_player distance _newTrajAGL), 2, 0.05, "RobotoCondensed"];
#endif
} forEach _pathData;
_pathData

View File

@ -0,0 +1,135 @@
/*
* Author: Dslyecxi, Jonpas, SilentSpike
* Handles drawing the currently selected or cooked throwable.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_advancedthrowing_fnc_drawThrowable
*
* Public: No
*/
#include "script_component.hpp"
if (dialog || {!(ACE_player getVariable [QGVAR(inHand), false])} || {!([ACE_player] call FUNC(canPrepare))}) exitWith {
[ACE_player, "In dialog or no throwable in hand or cannot prepare throwable"] call FUNC(exitThrowMode);
};
private _primed = ACE_player getVariable [QGVAR(primed), false];
private _activeThrowable = ACE_player getVariable [QGVAR(activeThrowable), objNull];
// Exit if throwable died primed in hand
if (isNull _activeThrowable && {_primed}) exitWith {
[ACE_player, "Throwable died primed in hand"] call FUNC(exitThrowMode);
};
private _throwable = currentThrowable ACE_player;
// Inventory check
if (_throwable isEqualTo [] && {!_primed}) exitWith {
[ACE_player, "No valid throwables"] call FUNC(exitThrowMode);
};
private _throwableMag = _throwable param [0, "#none"];
// Get correct throw power for primed grenade
if (_primed) then {
private _ammoType = typeOf _activeThrowable;
_throwableMag = GVAR(ammoMagLookup) getVariable _ammoType;
if (isNil "_throwableMag") then {
// What we're trying to throw must not be a normal throwable because it is not in our lookup hash (e.g. 40mm smoke)
// Just use HandGrenade as it has an average initSpeed value
_throwableMag = "HandGrenade";
};
};
// Some throwables have different classname for magazine and ammo
// Primed magazine may be different, read speed before checking primed magazine!
private _throwSpeed = getNumber (configFile >> "CfgMagazines" >> _throwableMag >> "initSpeed");
// Reduce power of throw over shoulder and to sides
private _unitDirVisual = getDirVisual ACE_player;
private _cameraDir = getCameraViewDirection ACE_player;
_cameraDir = (_cameraDir select 0) atan2 (_cameraDir select 1);
private _phi = abs (_cameraDir - _unitDirVisual) % 360;
_phi = [_phi, 360 - _phi] select (_phi > 180);
private _power = linearConversion [0, 180, _phi - 30, 1, 0.3, true];
ACE_player setVariable [QGVAR(throwSpeed), _throwSpeed * _power];
#ifdef DEBUG_MODE_FULL
hintSilent format ["Heading: %1\nPower: %2\nSpeed: %3\nThrowMag: %4", _phi, _power, _throwSpeed * _power, _throwableMag];
#endif
private _throwableType = getText (configFile >> "CfgMagazines" >> _throwableMag >> "ammo");
if (!([ACE_player] call FUNC(canThrow)) && {!_primed}) exitWith {
if (!isNull _activeThrowable) then {
deleteVehicle _activeThrowable;
};
};
if (isNull _activeThrowable || {(_throwableType != typeOf _activeThrowable) && {!_primed}}) then {
if (!isNull _activeThrowable) then {
deleteVehicle _activeThrowable;
};
_activeThrowable = _throwableType createVehicleLocal [0, 0, 0];
_activeThrowable enableSimulation false;
ACE_player setVariable [QGVAR(activeThrowable), _activeThrowable];
};
// Exit in case of explosion in hand
if (isNull _activeThrowable) exitWith {
[ACE_player, "No active throwable (explosion in hand)"] call FUNC(exitThrowMode);
};
// Set position
private _posHeadRel = ACE_player selectionPosition "head";
private _leanCoef = (_posHeadRel select 0) - 0.15; // 0.15 counters the base offset
// Don't take leaning into account when weapon is lowered due to jiggling when walking side-ways (bandaid)
if (abs _leanCoef < 0.15 || {vehicle ACE_player != ACE_player} || {weaponLowered ACE_player}) then {
_leanCoef = 0;
};
private _posCameraWorld = AGLToASL (positionCameraToWorld [0, 0, 0]);
_posHeadRel = _posHeadRel vectorAdd [-0.03, 0.01, 0.15]; // Bring closer to eyePos value
private _posFin = AGLToASL (ACE_player modelToWorldVisual _posHeadRel);
private _throwType = ACE_player getVariable [QGVAR(throwType), THROW_TYPE_DEFAULT];
// Orient it nicely, point towards player
_activeThrowable setDir (_unitDirVisual + 90);
private _pitch = [-30, -90] select (_throwType == "high");
[_activeThrowable, _pitch, 0] call BIS_fnc_setPitchBank;
if (ACE_player getVariable [QGVAR(dropMode), false]) then {
_posFin = _posFin vectorAdd (AGLToASL (positionCameraToWorld [_leanCoef, 0, ACE_player getVariable [QGVAR(dropDistance), DROP_DISTANCE_DEFAULT]]));
// Even vanilla throwables go through glass, only "GEOM" LOD will stop it but that will also stop it when there is no glass in a window
if (lineIntersects [_posCameraWorld, _posFin vectorDiff _posCameraWorld]) then {
ACE_player setVariable [QGVAR(dropDistance), ((ACE_player getVariable [QGVAR(dropDistance), DROP_DISTANCE_DEFAULT]) - 0.1) max DROP_DISTANCE_DEFAULT];
};
} else {
private _xAdjustBonus = [0, -0.075] select (_throwType == "high");
private _yAdjustBonus = [0, 0.1] select (_throwType == "high");
private _cameraOffset = [_leanCoef, 0, 0.3] vectorAdd [-0.1, -0.15, -0.03] vectorAdd [_xAdjustBonus, _yAdjustBonus, 0];
_posFin = _posFin vectorAdd (AGLToASL (positionCameraToWorld _cameraOffset));
if (vehicle ACE_player != ACE_player) then {
// Counteract vehicle velocity including acceleration
private _vectorDiff = (velocity (vehicle ACE_player)) vectorMultiply (time - (ACE_player getVariable [QGVAR(lastTick), time]) + 0.01);
_posFin = _posFin vectorAdd _vectorDiff;
ACE_player setVariable [QGVAR(lastTick), time];
};
};
_activeThrowable setPosASL (_posFin vectorDiff _posCameraWorld);

View File

@ -0,0 +1,50 @@
/*
* Author: Dslyecxi, Jonpas
* Exits throw mode.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Reason <STRING>
*
* Return Value:
* None
*
* Example:
* [unit, "reason"] call ace_advancedthrowing_fnc_exitThrowMode
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit", "_reason"];
TRACE_2("params",_unit,_reason);
if !(_unit getVariable [QGVAR(inHand), false]) exitWith {};
#ifdef DEBUG_MODE_FULL
systemChat format ["Exit Throw Mode: %1", _reason];
#endif
if !(_unit getVariable [QGVAR(primed), false]) then {
deleteVehicle (_unit getVariable [QGVAR(activeThrowable), objNull]);
};
_unit setVariable [QGVAR(inHand), false];
_unit setVariable [QGVAR(primed), false];
_unit setVariable [QGVAR(activeThrowable), objNull];
_unit setVariable [QGVAR(throwType), THROW_TYPE_DEFAULT];
_unit setVariable [QGVAR(throwSpeed), THROW_SPEED_DEFAULT];
_unit setVariable [QGVAR(dropMode), false];
_unit setVariable [QGVAR(dropDistance), DROP_DISTANCE_DEFAULT];
// Remove controls hint (check if ever enabled is inside the function)
call EFUNC(interaction,hideMouseHint);
// Remove throw action
[_unit, "DefaultAction", _unit getVariable [QGVAR(throwAction), -1]] call EFUNC(common,removeActionEventHandler);
// Remove throw arc draw
if (!isNil QGVAR(draw3DHandle)) then {
removeMissionEventHandler ["Draw3D", GVAR(draw3DHandle)];
GVAR(draw3DHandle) = nil;
};

View File

@ -0,0 +1,25 @@
/*
* Author: PabstMirror
* Retrieve muzzle name from config.
*
* Arguments:
* 0: Magazine Classname <STRING>
*
* Return Value:
* None
*
* Example:
* "magazine" call ace_advancedthrowing_fnc_getMuzzle
*
* Public: No
*/
#include "script_component.hpp"
params ["_magazineClassname"];
_magazineClassname = toLower _magazineClassname;
private _throwMuzzles = getArray (configFile >> "CfgWeapons" >> "Throw" >> "muzzles");
_throwMuzzles = _throwMuzzles select {_magazineClassname in ((getArray (configFile >> "CfgWeapons" >> "Throw" >> _x >> "magazines")) apply {toLower _x})};
[_throwMuzzles select 0, ""] select (_throwMuzzles isEqualTo [])

View File

@ -0,0 +1,33 @@
/*
* Author: Jonpas
* Initializes the Advanced Throwing module.
*
* Arguments:
* 0: Logic <OBJECT>
* 1: Synchronized Units <ARRAY>
* 2: Module Activated <BOOL>
*
* Return Value:
* None
*
* Example:
* [logic, [unit1, unit2], true] call ace_advancedthrowing_fnc_moduleInit
*
* Public:
* No
*/
#include "script_component.hpp"
if (!isServer) exitWith {};
params ["_logic", "_units", "_activated"];
if (!_activated) exitWith {};
[_logic, QGVAR(enabled), "enabled"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(showThrowArc), "showThrowArc"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(showMouseControls), "showMouseControls"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(enablePickUp), "enablePickUp"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(enablePickUpAttached), "enablePickUpAttached"] call EFUNC(common,readSettingFromModule);
ACE_LOGINFO_1("Advanced Throwing Module Initialized. Enabled: %1",GVAR(enabled));

View File

@ -0,0 +1,45 @@
/*
* Author: Dslyecxi, Jonpas
* Key down event.
*
* Arguments:
* 0: Control <CONTROL>
* 1: Key <NUMBER>
* 2: Shift <BOOL>
* 3: Ctrl <BOOL>
* 4: Alt <BOOL>
*
* Return Value:
* None
*
* Example:
* [control, 5, false, true, false] call ace_advancedthrowing_fnc_onKeyDown
*
* Public: No
*/
#include "script_component.hpp"
if !(ACE_player getVariable [QGVAR(inHand), false]) exitWith {false};
params ["", "_key", "_shift", "_ctrl", "_alt"];
// Exit if any of the action keys is pressed
{
if (_key in _x) exitWith {
[ACE_player, "Pressed a key that cycles us out of throwables"] call FUNC(exitThrowMode);
};
} forEach [
actionKeys "ReloadMagazine",
actionKeys "Handgun",
actionKeys "Binoculars",
actionKeys "SwitchWeapon",
actionKeys "Optics",
actionKeys "NextWeapon",
actionKeys "PrevWeapon",
actionKeys "OpticsTemp",
actionKeys "SwitchPrimary",
actionKeys "SwitchHandgun",
actionKeys "SwitchSecondary"
];
false

View File

@ -0,0 +1,46 @@
/*
* Author: Dslyecxi, Jonpas
* Mouse button down event.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call ace_advancedthrowing_fnc_onMouseButtonDown
*
* Public: No
*/
#include "script_component.hpp"
if !(ACE_player getVariable [QGVAR(inHand), false]) exitWith {};
params ["", "_key"];
// Left mouse button
// "DefaultAction" doesn't get executed when in driver seat or in FFV seat with weapon lowered
if (_key == 0) exitWith {
if (!isNull (ACE_player getVariable [QGVAR(activeThrowable), objNull])) then {
// Look gets automatically pointed at weapon direction on first LMB press when in FFV seat, require weapon to be up if in vehicle
private _inVehicle = vehicle ACE_player != ACE_player;
if (!_inVehicle || {_inVehicle && {!weaponLowered ACE_player}}) then {
[ACE_player] call FUNC(throw);
};
};
};
private _primed = ACE_player getVariable [QGVAR(primed), false];
// Right mouse button
if (_key == 1) exitWith {
if (!_primed) then {
[ACE_player, "Storing throwable"] call FUNC(exitThrowMode);
};
};
// Middle mouse button
if (_key == 2 && {!_primed}) exitWith {
[ACE_player, true] call FUNC(prime);
};

View File

@ -0,0 +1,48 @@
/*
* Author: Dslyecxi, Jonpas
* Mouse scroll wheel changed event.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call ace_advancedthrowing_fnc_onMouseScroll
*
* Public: No
*/
#include "script_component.hpp"
if !(ACE_player getVariable [QGVAR(inHand), false]) exitWith {};
params ["", "_amount"];
if (ACE_player getVariable [QGVAR(dropMode), false]) then {
private _dropDistance = ACE_player getVariable [QGVAR(dropDistance), DROP_DISTANCE_DEFAULT];
if (_amount < 0) then {
// Move closer
ACE_player setVariable [QGVAR(dropDistance), (_dropDistance - 0.1) max DROP_DISTANCE_DEFAULT];
} else {
// Move further
ACE_player setVariable [QGVAR(dropDistance), (_dropDistance + 0.1) min 1];
};
// Limit distance in vehicle
if (vehicle ACE_player != ACE_player) then {
ACE_player setVariable [QGVAR(dropDistance), (ACE_player getVariable [QGVAR(dropDistance), DROP_DISTANCE_DEFAULT]) min 0.5];
};
} else {
private _throwType = ACE_player getVariable [QGVAR(throwType), THROW_TYPE_DEFAULT];
if (_amount < 0) then {
if (_throwType == "high") then {
ACE_player setVariable [QGVAR(throwType), THROW_TYPE_DEFAULT];
};
} else {
if (_throwType == "normal") then {
ACE_player setVariable [QGVAR(throwType), "high"];
};
};
TRACE_2("Change Throw Type",_amount,ACE_player getVariable QGVAR(throwType));
};

View File

@ -0,0 +1,52 @@
/*
* Author: Jonpas
* Picks up a throwable from the ground.
*
* Arguments:
* 0: Pick Up Helper <OBJECT>
* 1: Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* [helper, player] call ace_advancedthrowing_fnc_pickUp
*
* Public: No
*/
#include "script_component.hpp"
params ["_helper", "_unit"];
TRACE_2("params",_helper,_unit);
private _activeThrowable = _helper getVariable [QGVAR(throwable), objNull];
if (isNull _activeThrowable) exitWith {TRACE_2("throwable is null",_helper,_activeThrowable);};
// Detach if attached (to vehicle for example or another player)
private _attachedTo = attachedTo _activeThrowable;
if (!isNull _attachedTo) then {
private _attachedList = _attachedTo getVariable [QEGVAR(attach,attached), []];
{
_x params ["_xObject"];
if (_activeThrowable == _xObject) exitWith {
TRACE_2("removing from ace_attach",_attachedTo,_attachedList);
_attachedList deleteAt _forEachIndex;
_attachedTo setVariable [QEGVAR(attach,attached), _attachedList, true];
};
} forEach _attachedList;
detach _activeThrowable;
};
// Change locality for manipulation (some commands require local object, such as setVelocity)
if (!local _activeThrowable) then {
["ace_setOwner", [_activeThrowable, CBA_clientID]] call CBA_fnc_serverEvent;
};
// Invoke listenable event
["ace_throwablePickedUp", [_activeThrowable, _unit, _attachedTo]] call CBA_fnc_localEvent;
_unit setVariable [QGVAR(primed), true];
_unit setVariable [QGVAR(activeThrowable), _activeThrowable];
_unit call FUNC(prepare);

View File

@ -0,0 +1,50 @@
/*
* Author: Dslyecxi, Jonpas
* Prepares throwable or selects the next.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* [unit] call ace_advancedthrowing_fnc_prepare
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit"];
TRACE_1("params",_unit);
// Select next throwable if one already in hand
if (_unit getVariable [QGVAR(inHand), false]) exitWith {
TRACE_1("inHand",_unit);
if (!(_unit getVariable [QGVAR(primed), false])) then {
TRACE_1("not primed",_unit);
[_unit] call EFUNC(weaponselect,selectNextGrenade);
};
};
// Try selecting next throwable if none currently selected
if ((isNull (_unit getVariable [QGVAR(activeThrowable), objNull])) && {(currentThrowable _unit) isEqualTo []} && {!([_unit] call EFUNC(weaponselect,selectNextGrenade))}) exitWith {
TRACE_1("no throwables",_unit);
};
_unit setVariable [QGVAR(inHand), true];
// Add controls hint
call FUNC(updateControlsHint);
// Add throw action to suppress weapon firing (not possible to suppress mouseButtonDown event)
_unit setVariable [QGVAR(throwAction), [_unit, "DefaultAction", {true}, {true}] call EFUNC(common,addActionEventHandler)];
// Draw throwable and throw arc if enabled
GVAR(draw3DHandle) = addMissionEventHandler ["Draw3D", {
call FUNC(drawThrowable);
if (GVAR(showThrowArc)) then {
call FUNC(drawArc);
};
}];

View File

@ -0,0 +1,57 @@
/*
* Author: Dslyecxi, Jonpas
* Primes the throwable, creates global throwable vehicle and throws Fired XEH.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Show Hint <BOOL> (default: false)
*
* Return Value:
* None
*
* Example:
* [unit] call ace_advancedthrowing_fnc_prime
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit", ["_showHint", false]];
TRACE_2("params",_unit,_showHint);
_unit setVariable [QGVAR(primed), true];
// Remove item before cooking to prevent weaponselect showing more throwables than there actually are in inventory
private _throwableMag = (currentThrowable _unit) select 0;
_unit removeItem _throwableMag;
private _throwableType = getText (configFile >> "CfgMagazines" >> _throwableMag >> "ammo");
private _muzzle = _throwableMag call FUNC(getMuzzle);
// Create actual throwable globally
private _activeThrowableOld = _unit getVariable [QGVAR(activeThrowable), objNull];
private _activeThrowable = createVehicle [_throwableType, _activeThrowableOld, [], 0, "CAN_COLLIDE"];
_unit setVariable [QGVAR(activeThrowable), _activeThrowable];
deleteVehicle _activeThrowableOld;
// Throw Fired XEH
[QGVAR(throwFiredXEH), [
_unit, // unit
"Throw", // weapon
_muzzle, // muzzle
_muzzle, // mode
_throwableType, // ammo
_throwableMag, // magazine
_activeThrowable // projectile
]] call CBA_fnc_globalEvent;
if (_showHint) then {
// Show primed hint
private _displayNameShort = getText (configFile >> "CfgMagazines" >> _throwableMag >> "displayNameShort");
private _picture = getText (configFile >> "CfgMagazines" >> _throwableMag >> "picture");
[[_displayNameShort, localize LSTRING(Primed)] joinString " ", _picture] call EFUNC(common,displayTextPicture);
// Change controls hint for RMB
call FUNC(updateControlsHint);
};

View File

@ -0,0 +1,61 @@
/*
* Author: PabstMirror, Jonpas
* When interact_menu starts rendering (from "interact_keyDown" event).
* Add pick up helpers to all nearby throwables and keep setting them to their position (setVariable and attachTo does not work on CfgAmmo).
*
* Arguments:
* None
*
* Return Value:
* Nothing
*
* Example:
* call ace_advancedthrowing_fnc_renderPickUpInteraction
*
* Public: No
*/
#include "script_component.hpp"
[{
params ["_args", "_idPFH"];
_args params ["_setPosition", "_addedPickUpHelpers", "_throwablesHelped", "_nearThrowables"];
// isNull is necessarry to prevent rare error when ending mission with interact key down
if (EGVAR(interact_menu,keyDown) && {!isNull ACE_player}) then {
// Rescan when player moved >5 meters from last pos, nearObjects is costly
if ((getPosASL ACE_player) distance _setPosition > 5) then {
// IR throwbles inherit from GrenadeCore, others from GrenadeHand
_nearThrowables = ACE_player nearObjects ["GrenadeHand", 10];
_nearThrowables append (ACE_player nearObjects ["GrenadeCore", 10]);
{
if (_x in _throwablesHelped) exitWith {};
TRACE_2("Making PickUp Helper",_x,typeOf _x);
private _pickUpHelper = QGVAR(pickUpHelper) createVehicleLocal [0, 0, 0];
_pickUpHelper attachTo [_x, [0, 0, 0]];
_pickUpHelper setVariable [QGVAR(throwable), _x];
_addedPickUpHelpers pushBack _pickUpHelper;
_throwablesHelped pushBack _x;
true
} count _nearThrowables;
_args set [0, getPosASL ACE_player];
_args set [3, _nearThrowables];
};
{
if (!GVAR(enablePickUpAttached) && {!isNull (attachedTo _x)}) exitWith {};
// Only handling with attachTo works nicely
_x attachTo [_x getVariable [QGVAR(throwable), objNull], [0, 0, 0]];
true
} count _addedPickUpHelpers;
} else {
TRACE_1("Cleaning Pick Up Helpers",count _addedPickUpHelpers);
{deleteVehicle _x} count _addedPickUpHelpers;
[_idPFH] call CBA_fnc_removePerFrameHandler;
};
}, 0, [(getPosASL ACE_player) vectorAdd [-100, 0, 0], [], [], []]] call CBA_fnc_addPerFrameHandler;

View File

@ -0,0 +1,85 @@
/*
* Author: Dslyecxi, Jonpas
* Throw selected throwable.
*
* Arguments:
* 0: Unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* unit call ace_advancedthrowing_fnc_throw
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit"];
TRACE_1("params",_unit);
// Prime the throwable if it hasn't been cooking already
// Next to proper simulation this also has to happen before delay for orientation of the throwable to be set
if (!(_unit getVariable [QGVAR(primed), false])) then {
[_unit] call FUNC(prime);
};
[_unit, "ThrowGrenade"] call EFUNC(common,doGesture);
// Pass position to reset later because animation may change it in certain stances
[{
params ["_unit", "_activeThrowable", "_posThrown", "_throwType", "_throwSpeed", "_dropMode"];
TRACE_6("delayParams",_unit,_activeThrowable,_posThrown,_throwType,_throwSpeed,_dropMode);
// Reset position in case animation changed it
_activeThrowable setPosASL _posThrown;
// Launch actual throwable
private _direction = [THROWSTYLE_NORMAL_DIR, THROWSTYLE_HIGH_DIR] select (_throwType == "high" || {_dropMode});
private _velocity = [_throwSpeed, _throwSpeed / THROWSTYLE_HIGH_VEL_COEF / 1.25] select (_throwType == "high");
_velocity = [_velocity, THROWSTYLE_DROP_VEL] select _dropMode;
private _p2 = (eyePos _unit) vectorAdd (AGLToASL (positionCameraToWorld _direction)) vectorDiff (AGLToASL (positionCameraToWorld [0, 0, 0]));
private _p1 = AGLtoASL (_activeThrowable modelToWorldVisual [0, 0, 0]);
private _newVelocity = (_p1 vectorFromTo _p2) vectorMultiply _velocity;
// Adjust for throwing from inside vehicles, where we have a vehicle-based velocity that can't be compensated for by a human
if (vehicle _unit != _unit) then {
_newVelocity = _newVelocity vectorAdd (velocity (vehicle _unit));
};
// Drop if unit dies during throw process
if (alive _unit) then {
_activeThrowable setVelocity _newVelocity;
};
_unit setVariable [QGVAR(lastThrownTime), CBA_missionTime];
// Invoke listenable event
["ace_throwableThrown", [_unit, _activeThrowable]] call CBA_fnc_localEvent;
}, [
_unit,
_unit getVariable [QGVAR(activeThrowable), objNull],
getPosASLVisual (_unit getVariable [QGVAR(activeThrowable), objNull]),
_unit getVariable [QGVAR(throwType), THROW_TYPE_DEFAULT],
_unit getVariable [QGVAR(throwSpeed), THROW_SPEED_DEFAULT],
_unit getVariable [QGVAR(dropMode), false]
], 0.3] call CBA_fnc_waitAndExecute;
#ifdef DRAW_THROW_PATH
GVAR(predictedPath) = call FUNC(drawArc); // save the current throw arc
GVAR(flightPath) = [];
[_unit getVariable QGVAR(activeThrowable)] spawn {
params ["_grenade"];
while {!isNull _grenade} do {
GVAR(flightPath) pushBack ASLtoAGL getPosASL _grenade;
sleep 0.05;
};
};
#endif
// Stop rendering arc and doing rendering magic while throw is happening
[_unit, "Completed a throw fully"] call FUNC(exitThrowMode);

View File

@ -0,0 +1,28 @@
/*
* Author: CBA Team
* Throws Fired XEH.
*
* Arguments:
* 0: unit - Object the event handler is assigned to <OBJECT>
* 1: weapon - Fired weapon <STRING>
* 2: muzzle - Muzzle that was used <STRING>
* 3: mode - Current mode of the fired weapon <STRING>
* 4: ammo - Ammo used <STRING>
* 5: magazine - magazine name which was used <STRING>
* 6: projectile - Object of the projectile that was shot <OBJECT>
*
* Return Value:
* None
*
* Example:
* [unit, "weapon", "muzle", "mode", "ammo", "magazine", projectile] call ace_advancedthrowing_fnc_throwFiredXEH
*
* Public: No
*/
#include "script_component.hpp"
TRACE_1("Fired",_this);
{
_this call _x;
} forEach ((_this select 0) getVariable "cba_xeh_fired");

View File

@ -0,0 +1,32 @@
/*
* Author: Jonpas
* Updates controls hints based on current state.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_advancedthrowing_fnc_updateControlsHint
*
* Public: No
*/
#include "script_component.hpp"
if (!GVAR(showMouseControls)) exitWith {};
private _primed = ACE_player getVariable [QGVAR(primed), false];
private _mmb = [localize LSTRING(ChangeMode), localize LSTRING(Extend)] select (ACE_player getVariable [QGVAR(dropMode), false]);
if (!_primed) then {
_mmb = [_mmb, localize LSTRING(Cook)] joinString " / ";
};
[
localize LSTRING(Throw),
[localize ELSTRING(common,Cancel), ""] select _primed,
_mmb
] call EFUNC(interaction,showMouseHint);

View File

@ -0,0 +1 @@
#include "\z\ace\addons\advancedthrowing\script_component.hpp"

View File

@ -0,0 +1,29 @@
#define COMPONENT advancedthrowing
#define COMPONENT_BEAUTIFIED Advanced Throwing
#include "\z\ace\addons\main\script_mod.hpp"
// #define DRAW_THROW_PATH
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define CBA_DEBUG_SYNCHRONOUS
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_ADVANCEDTHROWING
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_ADVANCEDTHROWING
#define DEBUG_SETTINGS DEBUG_SETTINGS_ADVANCEDTHROWING
#endif
#include "\z\ace\addons\main\script_macros.hpp"
#define THROWSTYLE_NORMAL_DIR [0, 70, 500]
#define THROWSTYLE_HIGH_DIR [0, 200, 500]
#define THROWSTYLE_HIGH_VEL_COEF 2
#define THROWSTYLE_DROP_VEL 2
#define THROW_TYPE_DEFAULT "normal"
#define THROW_SPEED_DEFAULT 18
#define DROP_DISTANCE_DEFAULT 0.2

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="AdvancedThrowing">
<Key ID="STR_ACE_AdvancedThrowing_Category">
<English>Advanced Throwing</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Module_Description">
<English>Allows changing advanced throwing behaviour.</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Enable_DisplayName">
<English>Enable Advanced Throwing</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Enable_Description">
<English>Enables advanced throwing system.</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_ShowThrowArc_DisplayName">
<English>Show Throw Arc</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_ShowThrowArc_Description">
<English>Enables visualization of the throw arc (where throwable will fly).</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_ShowMouseControls_DisplayName">
<English>Show Throwing Mouse Controls</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_ShowMouseControls_Description">
<English>Enables visual cues for mouse controls when throwable is prepared.</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_EnablePickUp_DisplayName">
<English>Enable Throwables Pick Up</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_EnablePickUp_Description">
<English>Enables ability to pick up throwables from the ground.</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_EnablePickUpAttached_DisplayName">
<English>Enable Attached Throwables Pick Up</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_EnablePickUpAttached_Description">
<English>Enables ability to pick up throwables from attached objects.</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Prepare">
<English>Prepare/Change Throwable</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_dropModeHold">
<English>Throwable Drop Mode (Hold)</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_DropModeToggle">
<English>Throwable Drop Mode (Toggle)</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Primed">
<English>Primed</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Throw">
<English>Throw</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_ChangeMode">
<English>(Scroll) Change Mode</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Extend">
<English>(Scroll) Extend</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_Cook">
<English>(Click) Cook</English>
</Key>
<Key ID="STR_ACE_AdvancedThrowing_PickUp">
<English>Pick Up</English>
</Key>
</Package>
</Project>

View File

@ -15,6 +15,8 @@
//IGNORE_PRIVATE_WARNING ["_unit", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_vehicle", "_gunner", "_turret"];
TRACE_10("firedEH:",_unit, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile, _vehicle, _gunner, _turret);
disableSerialization;
// check if compatible scope is used
private _display = uiNamespace getVariable [QGVAR(RscWeaponInfo2D), displayNull];

View File

@ -72,8 +72,6 @@ if (!hasInterface) exitWith {};
["ACE3 Weapons", QGVAR(SelectGrenadeFrag), localize LSTRING(SelectGrenadeFrag), {
// Conditions: canInteract
if !([ACE_player, ACE_player, ["isNotInside", "isNotEscorting"]] call EFUNC(common,canInteractWith)) exitWith {false};
// Conditions: specific
if !([ACE_player] call CBA_fnc_canUseWeapon) exitWith {false};
// Statement
[ACE_player, 1] call FUNC(selectNextGrenade);
@ -85,8 +83,6 @@ if (!hasInterface) exitWith {};
["ACE3 Weapons", QGVAR(SelectGrenadeOther), localize LSTRING(SelectGrenadeOther), {
// Conditions: canInteract
if !([ACE_player, ACE_player, ["isNotInside", "isNotEscorting"]] call EFUNC(common,canInteractWith)) exitWith {false};
// Conditions: specific
if !([ACE_player] call CBA_fnc_canUseWeapon) exitWith {false};
// Statement
[ACE_player, 2] call FUNC(selectNextGrenade);

View File

@ -0,0 +1,60 @@
---
layout: wiki
title: Advanced Throwing
description: Precise system for throwing grenades and other throwable items.
group: feature
category: interaction
parent: wiki
---
## 1. Overview
Advanced Throwing adds an alternate way of throwing grenades and other throwable items. It solves the issues vanilla Arma throwing has, such as hard to effectively throw through a window even when you are standing right next to it, hard to judge distance and similar. Additionally, it adds other features, such as cooking, picking up already thrown grenades, different throw modes, distance and flight arc indicators, which vanilla lacks. It is also entirely optional (can be disabled through settings) and configurable.
### 1.1 Throwing Modes
There are 3 throwing modes in Advanced Throwing, normal, high and drop. Normal mode is similar in power to vanilla throwing, high mode lobs the grenade higher, for example for throwing over walls, drop mode drops the grenade at your feet or slightly away, for example for dropping smoke or chemlights. Drop mods are indicated by both the throw arc indicator and the actual representation of the grenade in the world.
Drop mode has additional functionality which allows you to extend the arm and drop the grenade slightly further away, for example for dropping through windows, especially helpful where Arma's geometry would simply make it bounce back into your lap.
After cooking a grenade any mode can be used to throw it further or it can simply be held in hand (at least with smoke grenades, chemlights and similar, not recommended with frag grenades).
Grenade will be thrown where you are currently looking at, free-look (including TrackIR) is fully supported. However, the further to the side and back you are looking, the lower the throw power will be.
### 1.2 Rethrowing
Advanced Throwing allows you to pick up grenades that have already been thrown. You have to be very close to it to effectively pick it up. Useful for rethrowing smoke grenades or repositioning chemlights. Frag grenades can also be thrown back, for example out of the house, however doing so is extremely risky.
### 1.3 Settings
Various settings can be set according to your likeness. Next to global toggle to disable Advanced Throwing there are settings to hide the throw arc indicating approximate throw distance and the arc it will fly through and hide mouse controls. Additionally for server administrators and mission makers, picking up grenades can be entirely disabled, as well as a setting to enable picking up attached items such as chemlights attached to vehicles or other player's shoulder.
## 2. Usage
Make sure to use different keybinds for Advanced Throwing or unbind vanilla throwing keybinds to prevent incidents.
### 2.1 Throwing
- You need a throwable item.
- Press <kbd>&nbsp;Shift</kbd>+<kbd>G</kbd> (ACE3 default) to prepare the item.
- Press the keybind again to select next throwable item in the inventory.
- Press <kbd>LMB</kbd> to throw.
- Press <kbd>MMB</kbd> to prime/cook.
- Press <kbd>RMB</kbd> to cancel throw.
- Throw will also be cancelled when opening any interface, reloading, switching weapons and aiming.
### 2.2 Changing Mode
- Use <kbd>Scroll Wheel</kbd> to change between **normal** and **high** modes.
- Press <kbd>Ctrl</kbd>+<kbd>G</kbd> (ACE3 default) to change to **drop** mode.
- Use <kbd>Scroll Wheel</kbd> to extend or contract drop distance.
### 2.3 Picking Up
- Approach a throwable item on the ground.
- Interact with the item <kbd>&nbsp;Win</kbd> (ACE3 default).
- Select `Pick Up`.
- Item will be placed in your hands, but you may not cancel the throw.
## 3. Dependencies
{% include dependencies_list.md component="advancedthrowing" %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB