diff --git a/addons/compat_rhs_afrf3/CfgAmmo.hpp b/addons/compat_rhs_afrf3/CfgAmmo.hpp
index 54ff3bd2c2d..11aee03d795 100644
--- a/addons/compat_rhs_afrf3/CfgAmmo.hpp
+++ b/addons/compat_rhs_afrf3/CfgAmmo.hpp
@@ -219,6 +219,10 @@ class CfgAmmo {
EGVAR(frag,force) = 0;
};
+ class SmokeShell;
+ class rhs_ammo_rdg2_white: SmokeShell {
+ EGVAR(grenades,rollVectorDirAndUp)[] = {{0, 1, 0}, {0, 0, 1}};
+ };
class Sh_125mm_APFSDS;
class Sh_125mm_HE;
diff --git a/addons/compat_sog/CfgAmmo/grenades.hpp b/addons/compat_sog/CfgAmmo/grenades.hpp
index d280443b6e8..6395756f640 100644
--- a/addons/compat_sog/CfgAmmo/grenades.hpp
+++ b/addons/compat_sog/CfgAmmo/grenades.hpp
@@ -4,6 +4,13 @@ class vn_molotov_grenade_ammo: vn_grenadehand {
EGVAR(frag,enabled) = 0;
};
+class vn_t67_grenade_ammo: vn_grenadehand {
+ EGVAR(grenades,rollVectorDirAndUp)[] = {{-1, 0, 0}, {0, 0, 1}};
+};
+class vn_chicom_grenade_ammo: vn_grenadehand {
+ EGVAR(grenades,rollVectorDirAndUp)[] = {{1, 0, 0}, {0, 0, 1}};
+};
+
class SmokeShell;
class vn_m14_grenade_ammo: SmokeShell {
EGVAR(grenades,incendiary) = 1;
diff --git a/addons/grenades/XEH_postInit.sqf b/addons/grenades/XEH_postInit.sqf
index e5cfb56783a..21282ab1cef 100644
--- a/addons/grenades/XEH_postInit.sqf
+++ b/addons/grenades/XEH_postInit.sqf
@@ -32,3 +32,27 @@ GVAR(flashbangPPEffectCC) ppEffectForceInNVG true;
[] call FUNC(addChangeFuseItemContextMenuOptions);
};
}] call CBA_fnc_addEventHandler;
+
+["vehicle", {
+ private _currentThrowable = currentThrowable ACE_player;
+
+ // Make sure grenade can be rolled if in roll mode (detonation time has to be >= 1 second and player isn't in a vehicle)
+ if !(
+ GVAR(currentThrowMode) == 3 &&
+ {_currentThrowable isNotEqualTo []} &&
+ {
+ !isNull objectParent ACE_player ||
+ {getNumber (configFile >> "CfgAmmo" >> getText (configFile >> "CfgMagazines" >> _currentThrowable select 0 >> "ammo") >> "explosionTime") < MIN_EXPLOSION_TIME_FOR_ROLL}
+ }
+ ) exitWith {};
+
+ // If the player can't use throwables, don't change it
+ if !(ACE_player call CBA_fnc_canUseWeapon) exitWith {};
+
+ // Force the user into the normal throw mode
+ // Next throw mode after roll would be drop, which isn't ideal if the user tries to throw unknowingly...
+ [format [LLSTRING(RollGrenadeDisabled), LLSTRING(NormalThrow)], 2] call EFUNC(common,displayTextStructured);
+
+ GVAR(currentThrowMode) = 0;
+ GVAR(throwModePFEH) call CBA_fnc_removePerFrameHandler;
+}, true] call CBA_fnc_addPlayerEventHandler;
diff --git a/addons/grenades/XEH_preInit.sqf b/addons/grenades/XEH_preInit.sqf
index 894773534a4..9456dc9c9fb 100644
--- a/addons/grenades/XEH_preInit.sqf
+++ b/addons/grenades/XEH_preInit.sqf
@@ -6,6 +6,9 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
+GVAR(currentThrowMode) = 0;
+GVAR(throwModePFEH) = -1;
+
#include "initSettings.inc.sqf"
ADDON = true;
diff --git a/addons/grenades/functions/fnc_nextMode.sqf b/addons/grenades/functions/fnc_nextMode.sqf
index 1a64cf9f7ba..d3d25027b17 100644
--- a/addons/grenades/functions/fnc_nextMode.sqf
+++ b/addons/grenades/functions/fnc_nextMode.sqf
@@ -15,7 +15,7 @@
* Public: No
*/
-private _mode = missionNamespace getVariable [QGVAR(currentThrowMode), 0];
+private _mode = GVAR(currentThrowMode);
if (_mode == 4) then {
_mode = 0;
@@ -23,9 +23,18 @@ if (_mode == 4) then {
_mode = _mode + 1;
};
-// ROLL GRENADE DOESN'T WORK RIGHT NOW
-if (_mode == 3) then {
- _mode = 4;
+private _currentThrowable = currentThrowable ACE_player;
+
+// Make sure grenade can be rolled if in roll mode (detonation time has to be >= 1 second and player isn't in a vehicle)
+if (
+ _mode == 3 &&
+ {_currentThrowable isNotEqualTo []} &&
+ {
+ !isNull objectParent ACE_player ||
+ {getNumber (configFile >> "CfgAmmo" >> getText (configFile >> "CfgMagazines" >> _currentThrowable select 0 >> "ammo") >> "explosionTime") < MIN_EXPLOSION_TIME_FOR_ROLL}
+ }
+) then {
+ _mode = _mode + 1;
};
private _hint = localize ([
@@ -38,6 +47,37 @@ private _hint = localize ([
[_hint] call EFUNC(common,displayTextStructured);
+GVAR(throwModePFEH) call CBA_fnc_removePerFrameHandler;
GVAR(currentThrowMode) = _mode;
+// If in rolling mode, check every frame if current throwable is rollable
+if (GVAR(currentThrowMode) == 3) then {
+ GVAR(currentThrowable) = _currentThrowable;
+
+ GVAR(throwModePFEH) = {
+ private _currentThrowable = currentThrowable ACE_player;
+
+ if (GVAR(currentThrowable) isEqualTo _currentThrowable) exitWith {};
+
+ GVAR(currentThrowable) = _currentThrowable;
+
+ // Make sure grenade can be rolled if in roll mode (detonation time has to be >= 1 second and player isn't in a vehicle)
+ if !(
+ GVAR(currentThrowMode) == 3 &&
+ {_currentThrowable isNotEqualTo []} &&
+ {
+ !isNull objectParent ACE_player ||
+ {getNumber (configFile >> "CfgAmmo" >> getText (configFile >> "CfgMagazines" >> _currentThrowable select 0 >> "ammo") >> "explosionTime") < MIN_EXPLOSION_TIME_FOR_ROLL}
+ }
+ ) exitWith {};
+
+ // Force the user into the normal throw mode
+ // Next throw mode after roll would be drop, which isn't ideal if the user tries to throw unknowingly...
+ [format [LLSTRING(RollGrenadeDisabled), LLSTRING(NormalThrow)], 2] call EFUNC(common,displayTextStructured);
+
+ GVAR(throwModePFEH) call CBA_fnc_removePerFrameHandler;
+ GVAR(currentThrowMode) = 0;
+ } call CBA_fnc_addPerFrameHandler;
+};
+
true
diff --git a/addons/grenades/functions/fnc_throwGrenade.sqf b/addons/grenades/functions/fnc_throwGrenade.sqf
index 9a0168da3ea..939e0755da0 100644
--- a/addons/grenades/functions/fnc_throwGrenade.sqf
+++ b/addons/grenades/functions/fnc_throwGrenade.sqf
@@ -85,7 +85,7 @@ if (getNumber (_config >> QGVAR(incendiary)) == 1) then {
if (_unit != ACE_player) exitWith {};
if (_unit getVariable [QEGVAR(advanced_throwing,primed), false]) exitWith {LOG("advanced_throwing throw");};
-private _mode = missionNamespace getVariable [QGVAR(currentThrowMode), 0];
+private _mode = GVAR(currentThrowMode);
if (_mode != 0) then {
private _velocity = velocity _projectile;
@@ -103,9 +103,22 @@ if (_mode != 0) then {
case 2 : {
_velocity = (_unit weaponDirection _weapon) vectorMultiply (vectorMagnitude _velocity);
};
- //roll grande
+ //roll grenade
case 3 : {
- //@todo
+ private _posASL = getPosASL _projectile;
+
+ // getPos is unreliable, as surfaces in some ruins are not recognised as surfaces
+ private _lisPos = (lineIntersectsSurfaces [_posASL, _posASL vectorAdd [0, 0, -1e11], ACE_player, objNull, true, 1, "ROADWAY", "FIRE"]) select 0;
+ _projectile setPosASL ((_lisPos select 0) vectorAdd [0, 0, 0.2]);
+
+ // Rotate throwables by 90° to the side by default, so cylindrical throwables can be rolled
+ private _vectorDirAndUp = getArray (_config >> QGVAR(rollVectorDirAndUp));
+ _vectorDirAndUp params [["_vectorDir", [0, 1, 0], [[]], 3], ["_vectorUp", [1, 0, 0], [[]], 3]];
+
+ // Do as if object were facing north
+ _projectile setVectorDirAndUp ([[_vectorDir, _vectorUp], -(direction _projectile), 0, 0] call BIS_fnc_transformVectorDirAndUp);
+
+ _velocity = (vectorDir _unit) vectorMultiply 10;
};
//drop grenade
case 4 : {
diff --git a/addons/grenades/script_component.hpp b/addons/grenades/script_component.hpp
index 49dbe92a3f3..3da453de6fd 100644
--- a/addons/grenades/script_component.hpp
+++ b/addons/grenades/script_component.hpp
@@ -20,3 +20,5 @@
#define EFFECT_STAGE_DELETELIGHT 1
#define EFFECT_STAGE_PARTIALRECOVERY 2
#define EFFECT_STAGE_FULLRECOVERY 3
+
+#define MIN_EXPLOSION_TIME_FOR_ROLL 1
diff --git a/addons/grenades/stringtable.xml b/addons/grenades/stringtable.xml
index d5c1b142a65..ec009fa2baa 100644
--- a/addons/grenades/stringtable.xml
+++ b/addons/grenades/stringtable.xml
@@ -103,6 +103,9 @@
下丟投擲
Bombayı Yere Bırak
+
+ Can't roll this grenade, switched to %1
+
M84 Stun Grenade
M84 Blendgranate
diff --git a/docs/wiki/feature/grenades.md b/docs/wiki/feature/grenades.md
index 679b309b1ec..2b60c08d4ec 100644
--- a/docs/wiki/feature/grenades.md
+++ b/docs/wiki/feature/grenades.md
@@ -23,6 +23,8 @@ version:
### 1.1 Throw modes
Provides different modes for throwing grenades (high throw, precision throw and drop mode).
+A grenade is only rollable if the fuse time (`explosionTime`) is >= 1 second and the player isn't in a vehicle.
+
### 1.2 Hand flares
Adds throwable hand flares in the colors white, red, green and yellow. Additionally buffs existing flares by making them brighter and last longer.
diff --git a/docs/wiki/framework/grenades-framework.md b/docs/wiki/framework/grenades-framework.md
index 14c72e1cb57..755773c1710 100644
--- a/docs/wiki/framework/grenades-framework.md
+++ b/docs/wiki/framework/grenades-framework.md
@@ -112,6 +112,12 @@ If set to zero or left undefined, the grenade is not treated as a flare. If it i
Sets the color of the emitted light. The first 3 values of the array of the color, the last is the light intensity.
+### 2.4 Grenade Rolling
+
+#### 2.4.1 ace_grenades_rollVectorDirAndUp
+
+Sets the `setVectorDirAndUp` of the grenade when the grenade is rolled.
+
## 3. Events
### 3.1 Listenable