by
Rydygier & Gunter Severloh
What is Drone Grenadier?
This is a script that will add various explosive functions to drones that can be assembled or existing drones.
You can drop different types of grenades including smoke, one at a time or all at once,
detonate drones remotely, set a drone to detonate with the proximity switch when they come within range of their target.
Dispense mines, and different satchels to be remotely detonated.
Code was written and adapted from another code by Rydygier.
Grenade functions, script review and setup video
Grenades, detonations, and mine use video
Download
https://drive.google.com/file/d/13xmassHbJPfbjZXXp0_uzfbnU2RX52nS/view?usp=sharing
Features
Drones can drop Rgo, Rgn, Smoke, chem-lights and other types of grenades one at a time or all at once.
Detonate - Drones can be driven or flown into enemy targets and detonate with a massive explosion.
Proximity Switch - Drones can be set to blow up if they get in range of an enemy target with an audible beeping sound, and target info.
Ability to place mines (included charges, like satchel charge or claymore, that require manual detonation). One at a time or all at once.
First case - a mine will appear on the ground 10-12 meters ahead.
Second case - mines will form a random minefield in the front of the drone, roughly from 25 m to the left to 25m to the right, 10-20 meters ahead.
Any newly assembled, or existing drones including UGVs, will get this ability as well as long as there is cargo space for a grenades and mines.
Add grenades to existing drones, just give the drone a name in the script and they will get the action to drop grenades.
Ability to add grenade drop action to drones/vehicles/objects via script mid-game.
Drop action is visible, when the player has the UAV controls and there's at least one grenade in UAV's cargo space.
Grenade counter shows how many grenades are left you can drop.
Option to change colors of ammo counter text and other actions in the script.
Installation and Setup
Setup - Script
1. Create a notepad/notepad++ document and copy and paste the following code into it:
2. Save and name the document: init.sqf and put it into your scenario folder.
// Drone Grenadier by Rydygier and Gunter Severloh
RYD_BD_drones = [UAV_1,UAV_2,UAV_3]; //Drones to be used as bombers at mission start for example: RYD_BD_drones = [UAV_1,UAV_2];
RYD_BD_addDetonate =
{
params ["_drone"];
if (_drone getVariable ["RYD_BD_hasDetonateAction",false]) exitWith {};
_drone setVariable ["RYD_BD_hasDetonateAction",true];
private _ix = _drone addAction
[
"<t color='#8f630f'>Switch proximity fuse</t>", // Switch proximity fuse is the text that shows in your action menu.
{
params ["_target", "_caller", "_actionId", "_arguments"];
_switchState = _target getVariable ["RYD_BD_impactFuseState",false];
_target setVariable ["RYD_BD_impactFuseState",(not _switchState)];
_info = ["Proximity fuse activated","Proximity fuse deactivated"] select _switchState;
"RYD_BD_ProximityFuse_CTLayer" cutText [(format ["<t color='#8f630f' size='2' font='PuristaMedium'>%1</t>",_info]),"PLAIN DOWN",-1,true,true];
},
nil,
1.5,
true,
true,
"",
"((_this == currentPilot vehicle _target) or (_this == gunner vehicle _target))",
1,
false,
"",
""
];
private _ix2 = _drone addAction
[
"<t color='#c30909'>Detonate</t>", // Detonate is the text that shows in your action menu.
{
params ["_target", "_caller", "_actionId", "_arguments"];
_target removeAction _actionId;
_pos = getPosATL _target;
_target action ["BackFromUAV"];
_explosive = "SatchelCharge_Remote_Ammo_Scripted" createVehicle _pos;//"IEDUrbanBig_Remote_Ammo" "HelicopterExploBig"
_explosive setDamage 1;
_explosive = "HelicopterExploBig" createVehicle _pos;
},
nil,
1.5,
true,
true,
"",
"((_this == currentPilot vehicle _target) or (_this == gunner vehicle _target))",
1,
false,
"",
""
];
private _actions = _drone getVariable ["RYD_BD_myActions",[]];
_actions appEnd [_ix,_ix2];
_drone setVariable ["RYD_BD_myActions",_actions];
};
RYD_BD_addGrenadeDrop =
{
params ["_drone"];
if (_drone getVariable ["RYD_BD_hasDropAction",false]) exitWith {};
_drone setVariable ["RYD_BD_hasDropAction",true];
private _ix3 = _drone addAction
[
"<t color='#728d5a'>Drop grenade</t>", // Drop grenade is the text that shows in your action menu.
{
params ["_target", "_caller", "_actionId", "_arguments"];
_magazines = magazineCargo _target;
_grenades = _magazines select {_x call BIS_fnc_isThrowable};//{(toLower getText (configFile >> "cfgAmmo" >> getText (configFile >> "CfgMagazines" >> _x >> "ammo") >> "simulation")) in ["shotgrenade","shotsmokex"]};
_count = count _grenades;
if (_count > 0) then
{
_grenade = _grenades select 0;
_grenadeObject = getText (configFile >> "CfgMagazines" >> _grenade >> "ammo");
{
if (_x isEqualTo _grenade) exitWith
{
_magazines set [_foreachIndex,""];
};
}
foreach _magazines;
_magazines = _magazines - [""];
clearMagazineCargoGlobal _target;
{
_target addMagazineCargoGlobal [_x,1];
}
foreach _magazines;
private _vehicle = vehicle _caller;
private _droneVelocity = velocity _vehicle;
_gren = objNull;
_grenVelocity = if ((isTouchingGround _target) or {(((getPosATL _target) select 2) < 1)}) then
{
_bb = boundingBoxReal _target;
_bbMin = _bb select 0;
_bbMax = _bb select 1;
_forward = (_bbMax select 1) + 1;
_upward = (_bbMax select 2) - (_bbMin select 2) + 1;
private _pos = _target modelToWorld [0,_forward,_upward];
_gren = _grenadeObject createVehicle _pos;
_gV = [_droneVelocity select 0,_droneVelocity select 1,_droneVelocity select 2] getPos [(10 + (random 5)),(getDir _target)];
_gV set [2,(_droneVelocity select 2) + (5 + (random 2.5))];
_gV
}
else
{
private _pos = _target modelToWorld [0,0,-0.2];
_gren = _grenadeObject createVehicle _pos;
_gren setVectorDirandUp [[0,0,-1],[0.1,0.1,1]];
[_droneVelocity select 0, _droneVelocity select 1, (_droneVelocity select 2) - 1]
};
_gren setVelocity _grenVelocity;
_info = ["No grenades left",(format ["Grenades left: %1",(_count - 1)])] select (_count > 1);
"RYD_BD_GrenadeDrop_CTLayer" cutText [(format ["<t color='#728d5a' size='2' font='PuristaMedium'>%1</t>",_info]),"PLAIN DOWN",-1,true,true];
};
},
nil,
1.5,
true,
true,
"",
"(((_this == currentPilot vehicle _target) or (_this == gunner vehicle _target)) and {not (_target getVariable ['RYD_BD_busy',false]) and {((count ((magazineCargo _target) select {_x call BIS_fnc_isThrowable})) > 0)}})",
1,
false,
"",
""
];
private _ix4 = _drone addAction
[
"<t color='#728d5a'>Drop all grenades</t>", // Drop all grenades is the text that shows in your action menu.
{
(_this select 0) setVariable ["RYD_BD_busy",true];
_this spawn
{
params ["_target", "_caller", "_actionId", "_arguments"];
_magazines = magazineCargo _target;
_grenades = _magazines select {_x call BIS_fnc_isThrowable};//{(toLower getText (configFile >> "cfgAmmo" >> getText (configFile >> "CfgMagazines" >> _x >> "ammo") >> "simulation")) in ["shotgrenade","shotsmokex"]};
_count = count _grenades;
_info = format ["%1 grenades dropped",_count];
for "_i" from 1 to _count do
{
_grenade = _grenades select 0;
_grenadeObject = getText (configFile >> "CfgMagazines" >> _grenade >> "ammo");
{
if (_x isEqualTo _grenade) exitWith
{
_magazines set [_foreachIndex,""];
};
}
foreach _magazines;
_magazines = _magazines - [""];
clearMagazineCargoGlobal _target;
{
_target addMagazineCargoGlobal [_x,1];
}
foreach _magazines;
private _vehicle = vehicle _caller;
private _droneVelocity = velocity _vehicle;
_gren = objNull;
_grenVelocity = if ((isTouchingGround _target) or {(((getPosATL _target) select 2) < 1)}) then
{
_bb = boundingBoxReal _target;
_bbMin = _bb select 0;
_bbMax = _bb select 1;
_forward = (_bbMax select 1) + 1;
_upward = (_bbMax select 2) - (_bbMin select 2) + 1;
private _pos = _target modelToWorld [0,_forward,_upward];
_gren = _grenadeObject createVehicle _pos;
_gV = [_droneVelocity select 0,_droneVelocity select 1,_droneVelocity select 2] getPos [(10 + (random 10)),((getDir _target) - 60 + (random 120))];
_gV set [2,(_droneVelocity select 2) + (5 + (random 2.5))];
_gV
}
else
{
private _pos = _target modelToWorld [0,0,-0.2];
_gren = _grenadeObject createVehicle _pos;
_gren setVectorDirandUp [[0,0,-1],[0.1,0.1,1]];
_droneVelocity = _droneVelocity apply {(0.8 * _x) + ((random 0.4) * _x)};
[_droneVelocity select 0, _droneVelocity select 1, (_droneVelocity select 2) - 1]
};
_gren setVelocity _grenVelocity;
_grenades = _magazines select {_x call BIS_fnc_isThrowable};//{(toLower getText (configFile >> "cfgAmmo" >> getText (configFile >> "CfgMagazines" >> _x >> "ammo") >> "simulation")) in ["shotgrenade","shotsmokex"]};
sleep (0.1 + (random 0.05));
};
"RYD_BD_GrenadeDrop_CTLayer" cutText [(format ["<t color='#728d5a' size='2' font='PuristaMedium'>%1</t>",_info]),"PLAIN DOWN",-1,true,true];
_target setVariable ["RYD_BD_busy",nil];
};
},
nil,
1.5,
true,
true,
"",
"(((_this == currentPilot vehicle _target) or (_this == gunner vehicle _target)) and {not (_target getVariable ['RYD_BD_busy',false]) and {((count ((magazineCargo _target) select {_x call BIS_fnc_isThrowable})) > 0)}})",
1,
false,
"",
""
];
private _ix5 = _drone addAction
[
"<t color='#8f630f'>Deploy mine</t>", // Deploy mine is the text that shows in your action menu.
{
params ["_target", "_caller", "_actionId", "_arguments"];
_magazines = magazineCargo _target;
_mines = _magazines select {(toLower getText (configFile >> "cfgAmmo" >> getText (configFile >> "CfgMagazines" >> _x >> "ammo") >> "simulation")) in ["shotmine","shotboundingmine","shotdirectionalbomb"]};
_count = count _mines;
if (_count > 0) then
{
_mine = _mines select 0;
_mineObject = getText (configFile >> "CfgMagazines" >> _mine >> "ammo");
{
if (_x isEqualTo _mine) exitWith
{
_magazines set [_foreachIndex,""];
};
}
foreach _magazines;
_magazines = _magazines - [""];
clearMagazineCargoGlobal _target;
{
_target addMagazineCargoGlobal [_x,1];
}
foreach _magazines;
private _vehicle = vehicle _caller;
if ((toLower _mine) isEqualTo "apersminedispenser_mag") then
{
private _droneVelocity = velocity _vehicle;
_bb = boundingBoxReal _target;
_bbMin = _bb select 0;
_bbMax = _bb select 1;
_forward = (_bbMax select 1) + 3;
_upward = (_bbMax select 2) - (_bbMin select 2) + 3;
private _pos = _target modelToWorld [0,_forward,_upward];
private _mn = "APERSMineDispenser_Ammo_Scripted" createVehicle _pos;
_mn setDir (getDir _target);
_mn setDamage 1;
}
else
{
private _pos = _target modelToWorld [0,(10 + (random 2)),0];
_pos set [2,0];
private _mn = _mineObject createVehicle _pos;
_mn setDir (_target getDir _pos);
_mn setPosATL _pos;
player addOwnedMine _mn;
(side player) revealMine _mn;
};
_info = ["No mines left",(format ["Mines left: %1",(_count - 1)])] select (_count > 1);
"RYD_BD_mineDrop_CTLayer" cutText [(format ["<t color='#8f630f' size='2' font='PuristaMedium'>%1</t>",_info]),"PLAIN DOWN",-1,true,true];
};
},
nil,
1.5,
true,
true,
"",
"(((_this == currentPilot vehicle _target) or (_this == gunner vehicle _target)) and {not (_target getVariable ['RYD_BD_busy',false]) and {((count ((magazineCargo _target) select {(toLower getText (configFile >> 'cfgAmmo' >> getText (configFile >> 'CfgMagazines' >> _x >> 'ammo') >> 'simulation')) in ['shotmine','shotboundingmine','shotdirectionalbomb']})) > 0)}})",
1,
false,
"",
""
];
private _ix6 = _drone addAction
[
"<t color='#8f630f'>Deploy all mines</t>", // Drop all mines is the text that shows in your action menu.
{
(_this select 0) setVariable ["RYD_BD_busy",true];
_this spawn
{
params ["_target", "_caller", "_actionId", "_arguments"];
_magazines = magazineCargo _target;
_mines = _magazines select {(toLower getText (configFile >> "cfgAmmo" >> getText (configFile >> "CfgMagazines" >> _x >> "ammo") >> "simulation")) in ["shotmine","shotboundingmine","shotdirectionalbomb"]};
_count = count _mines;
//_angleAdd = 120/((_count - 1) max 1);
_shiftAdd = 40/((_count - 1) max 1);
_info = format ["%1 mines deployed",_count];
for "_i" from 1 to _count do
{
_mine = selectRandom _mines;
_mineObject = getText (configFile >> "CfgMagazines" >> _mine >> "ammo");
{
if (_x isEqualTo _mine) exitWith
{
_magazines set [_foreachIndex,""];
};
}
foreach _magazines;
_magazines = _magazines - [""];
clearMagazineCargoGlobal _target;
{
_target addMagazineCargoGlobal [_x,1];
}
foreach _magazines;
private _vehicle = vehicle _caller;
if ((toLower _mine) isEqualTo "apersminedispenser_mag") then
{
private _droneVelocity = velocity _vehicle;
_bb = boundingBoxReal _target;
_bbMin = _bb select 0;
_bbMax = _bb select 1;
_forward = (_bbMax select 1) + 3;
_upward = (_bbMax select 2) - (_bbMin select 2) + 3;
private _pos = _target modelToWorld [0,_forward,_upward];
private _mn = "APERSMineDispenser_Ammo_Scripted" createVehicle _pos;
_mn setDir (getDir _target);
_mn setDamage 1;
}
else
{
private _pos = if (_count == 1) then
{
[0,(10 + (random 5)),0]
}
else
{
//_angleDiff = (-60 + _angleAdd * (_i - 1));
//(_target getPos [(10 + (random (2 + ((abs _angleDiff)/6)))),((getDir _target) + _angleDiff)])
_xCoord = -25 + (_shiftAdd * (_i - 1)) + (random 10);
_yCoord = 10 + (random 10);
(_target modelToWorld [_xCoord,_yCoord,0])
};
_pos set [2,0];
private _mn = _mineObject createVehicle _pos;
_mn setDir (_target getDir _pos);
_mn setPosATL _pos;
player addOwnedMine _mn;
(side player) revealMine _mn;
};
_mines = _magazines select {(toLower getText (configFile >> "cfgAmmo" >> getText (configFile >> "CfgMagazines" >> _x >> "ammo") >> "simulation")) in ["shotmine","shotboundingmine","shotdirectionalbomb"]};
sleep (0.05 + (random 0.025));
};
"RYD_BD_mineDrop_CTLayer" cutText [(format ["<t color='#8f630f' size='2' font='PuristaMedium'>%1</t>",_info]),"PLAIN DOWN",-1,true,true];
_target setVariable ["RYD_BD_busy",nil];
};
},
nil,
1.5,
true,
true,
"",
"(((_this == currentPilot vehicle _target) or (_this == gunner vehicle _target)) and {not (_target getVariable ['RYD_BD_busy',false]) and {((count ((magazineCargo _target) select {(toLower getText (configFile >> 'cfgAmmo' >> getText (configFile >> 'CfgMagazines' >> _x >> 'ammo') >> 'simulation')) in ['shotmine','shotboundingmine','shotdirectionalbomb']})) > 0)}})",
1,
false,
"",
""
];
private _actions = _drone getVariable ["RYD_BD_myActions",[]];
_actions appEnd [_ix3,_ix4,_ix5,_ix6];
_drone setVariable ["RYD_BD_myActions",_actions];
};
{
[_x] call RYD_BD_addGrenadeDrop;
[_x] call RYD_BD_addDetonate;
}
foreach RYD_BD_drones;
addMissionEventHandler ["UAVCrewCreated",
{
params ["_uav"];
[_uav] call RYD_BD_addGrenadeDrop;
[_uav] call RYD_BD_addDetonate;
RYD_BD_drones pushBackUnique _uav;
}];
RYD_BD_ProximityDetection =
{
params ["_drone"];
private _threshold = _drone getVariable ["RYD_BD_ProximityFuseThresholdAdd",1];
private _side = side _drone;
private _nearEnemies = (_drone nearEntities [["AllVehicles"],25]) select {((_side getFriend (side _x)) < 0.6)};
if ((count _nearEnemies) > 0) then
{
_nearEnemies = _nearEnemies apply {[((_x distance _drone) - (((sizeOf (typeOf _x))/3) + _threshold)),_x]};
_nearEnemies sort true;
private _closest = (_nearEnemies select 0) select 1;
private _distance = _drone distance _closest;
private _type = typeOf _closest;
private _detonationDistance = ((sizeOf _type)/3) + _threshold;
private _info = getText (configFile >> "CfgVehicles" >> _type >> "displayName");
if (_info isEqualTo "") then
{
_info = "Unidentified"
};
"RYD_BD_ProximityAlert_CTLayer" cutText [(format ["<t color='#c30909' size='1' font='PuristaMedium' align='left'>Proximity: %1<br/>Threshold: %2<br/>Distance: %3</t>",_info,(_detonationDistance toFixed 1),(_distance toFixed 1)]),"PLAIN",0.1,true,true];
playSound "beep";
((_drone distance _closest) < _detonationDistance)
}
else
{
false
};
};
RYD_BD_ProximityLoop =
{
while {true} do
{
waitUntil
{
sleep 0.25;
((RYD_BD_drones findIf {(alive _x) and {_x getVariable ["RYD_BD_impactFuseState",false]}}) >= 0)
};
{
private _drone = _x;
if ((_drone getVariable ["RYD_BD_impactFuseState",false]) and {([_drone] call RYD_BD_ProximityDetection)}) then
{
_drone setVariable ["RYD_BD_impactFuseState",false];
{
_drone removeAction _x;
}
foreach (_drone getVariable ["RYD_BD_myActions",[]]);
private _pos = getPosATL _drone;
_drone action ["BackFromUAV"];
private _explosive = "SatchelCharge_Remote_Ammo_Scripted" createVehicle _pos;//"IEDUrbanBig_Remote_Ammo" "HelicopterExploBig"
_explosive setDamage 1;
_explosive = "HelicopterExploBig" createVehicle _pos;
};
}
foreach RYD_BD_drones;
};
};
RYD_BD_ProximityLoop_handle = [] spawn RYD_BD_ProximityLoop;
Setup - External Script - (optional)
Instead of using a init.sqf directly for the code, you can setup an external script that can be used instead:
1. Create a notepad/notepad++ document and copy and paste the code from above into it:
2. Save and name the document: RYD_Drone_Grenadier.sqf and put it into your scenario folder.
3. Create a notepad/notepad++ document and add the following code to it: execVM "RYD_Drone_Grenadier.sqf";
4. Save and name the document: init.sqf
5. Add both RYD_Drone_Grenadier.sqf and the init.sqf to your scenario folder where the mission.sqm is located.
Usage:
Grenade drop functions
1. To add grenade drop action to any drone (probably any object in fact), put its name into this array defined at mission init (not mid-game):
RYD_BD_drones = [ ];
for example:
RYD_BD_drones = [UAV_1,UAV_2];
2. Any newly assembled drones, including UGVs, will get this ability as well. Of course, cargo space for a grenade is required.
ED-1/E Roller UGVs seem not to have any cargo space for example. Remove/disable those lines when you prefer point 2 not to happen:
addMissionEventHandler ["UAVCrewCreated",
{
params ["_uav"];
[_uav] call RYD_BD_addGrenadeDrop;
}];
3. Adding grenade drop action to drones/vehicles/objects via script mid-game:
[someUAVName] call RYD_BD_addGrenadeDrop;
4. Drop action is visible, when the player has the UAV controls and there's at least one grenade in UAV's cargo space. Each grenade drop will remove one grenade from this space.
5. Note, grenades presence in the cargo space is detected via certain config values of stored magazines ("simulation" being "shotGrenade").
Also 3D object equivalent class is taken from that config ("ammo").
Therefore modded content with customized config potentially may mislead this logic resulting with non-grenades being treated as grenades or vice versa.
Drone and Proximity detonations
6. Detonation distance depends on target's size (since distance is measured from the center of the object, larger objects require larger threshold,
otherwise even direct touching it would be still too far sometimes) and can be additionally increased or decreased by this:
droneName setVariable ["RYD_BD_ProximityFuseThresholdAdd",numberHere];
1 meter is default, use negative to decrease the threshold, but if absolute value of such negative value will be larger, than size of a target, obviously threshold will drop below 0,
and therefore target will be ignored. Theres a way to filter out small targets, say infantry:
droneName setVariable ["RYD_BD_ProximityFuseThresholdAdd",-0.5];
Code looks for the closest entity, (only knowing the target we can calculate the threshold based on its size), so if a
vehicle is close enough but closer infantry, it only refers to that infantry ignoring anything else.
This can be redone so any close enough enemy will trigger detonation, closest or not closest, just such logic will be heavier
(thresholds must be pre-calculated for all near enemies, not only for the closest one).
Additionally, below 25 meters from the closest target there will be a beep sound repeating and displayed proximity info including closest target display name,
detonation threshold for it and current distance from it. It is refreshed about 4 times per second.
===========
Credits
Rydygier wrote and adapted the code from someone else.
Both Gunter's and Rydygier's ideas.