nkenny 1057 Posted February 27, 2019 (edited) This thread contains three AI scripts centred around the same theme.: hunting, stalking or rushing players. fn_taskRush Ever needed the AI to dumbly hunt down and rush the players? This is the script for you. The premise is simple. The AI will move with perfect knowledge towards any player unit within range. While not fearless, the AI is very aggressive and will enter buildings. The AI will know player location but not targeting information-- the AI must still locate the enemy to start shooting. Perfect for Black Hawk Down style scenarios or mad dashes through Tanoan jungles. Or both! Use Run the script on unit or group with an optional argument for range (Default is 500 meters). [someUnit, 550] execVM "fn_taskRush.sqf"; Features - Simple aggressive AI script - Multiplayer friendly. - Helicopters will generally be ignored, but low flying ones will be suppressed. - Tanks and tracked vehicles will be approached cautiously and with suppressive fire Code Spoiler fn_taskRush.sqf // Aggressive Attacker script // version 4.4 // by nkenny /* Aggressive tracking and attacking script Arguments 1, Group or object tracker [Object or Group] 2, Range of tracking [Number] */ // functions --- _fn_findTarget = { _newDist = _range; _all = (switchableUnits + playableUnits - entities "HeadlessClient_F"); _all = _all select {side _x != civilian && {side _x != side _group}}; _target = objNull; { _distance = (leader _group) distance2d _x; if (_distance < _newDist && {getpos _x select 2 < 200}) then {_target = _x;_newDist = _distance;}; true } count _all; _target }; _fn_rushOrders = { // Helicopters -- supress it! if ((leader _group distance2d _target < 220) && {vehicle _target isKindOf "Air"}) exitWith { { _x commandSuppressiveFire getposASL _target; true } count units _group; }; // Tank -- hide or ready AT if ((leader _group distance2d _target < 80) && {vehicle _target isKindOf "Tank"}) exitWith { { if (secondaryWeapon _x != "") then { _x setUnitpos "Middle"; _x selectWeapon (secondaryweapon _x); } else { _x setunitpos "DOWN"; _x commandSuppressiveFire getposASL _target; }; true } count units _group; _group enableGunLights "forceOff"; }; // Default -- run for it! {_x setunitpos "UP";_x domove (getposATL _target);true} count units _group; _group enableGunLights "forceOn"; }; // functions end --- // init params ["_group",["_range",500],["_cycle",15]]; // sort grp if (!local _group) exitWith {}; if (_group isEqualType objNull) then {_group = group _group;}; // orders _group setSpeedMode "FULL"; _group setFormation "WEDGE"; _group enableAttack false; {_x disableAI "AUTOCOMBAT";dostop _x;true} count units _group; // Hunting loop while {{alive _x} count units _group > 0} do { // performance waitUntil {sleep 1; simulationenabled leader _group}; // find _target = call _fn_findTarget; // act if (!isNull _target) then { call _fn_rushOrders; _cycle = 15; } else { _cycle = 60; }; // delay sleep _cycle; }; // end true fn_taskHunt A LRRP patrol style script that has the unit slowly patrol in an area which gradually centres on the nearest player, within the defined range. Good for having patrols which must absolutely trigger or when you need to be careful with your AI resources and want only a single patrol which will generate some heat. Use Run the script on unit or group with an optional argument for range (Default is 500 meters). [someUnit, 1000] execVM "fn_taskHunt.sqf"; Features - Aggressive patrolling script - Multiplayer friendly. - Known enemy buildings will be suppressed - Group will fire flares to coordinate friendly forces - Uses flashlights and IR lasers Code Spoiler fn_taskHunt // Tracker script // version 4.4 // by nkenny /* Slower more deliberate tracking and attacking script Spawns flares to coordinate Arguments 1, Group or object tracker [Object or Group] 2, Range of tracking [Number] */ // functions --- _fn_findTarget = { _newDist = _range; _all = (switchableUnits + playableUnits - entities "HeadlessClient_F"); _all = _all select {side _x != civilian && {side _x != side _group}}; _target = objNull; { _distance = (leader _group) distance2d _x; if (_distance < _newDist && {getpos _x select 2 < 200}) then {_target = _x;_newDist = _distance;}; true } count _all; _target }; _fn_flare = { _shootflare = "F_20mm_Red" createvehicle ((leader _group) ModelToWorld [0,0,200]); _shootflare setVelocity [0,0,-10]; }; _fn_suppress = { { _x doSuppressiveFire ((getposASL _target) vectorAdd [random 2,random 2,0.5 + random 3]); true } count units _group; }; // functions end --- // init params ["_group",["_range",500],["_cycle",60 + random 30]]; // sort grp if (!local _group) exitWith {}; if (_group isEqualType objNull) then {_group = group _group;}; // orders _group setbehaviour "SAFE"; _group setSpeedMode "LIMITED"; // Hunting loop while {{alive _x} count units _group > 0} do { // performance waitUntil {sleep 1;simulationenabled leader _group}; // settings _combat = behaviour leader _group isEqualTo "COMBAT"; _onFoot = (isNull objectParent (leader _group)); // find _target = call _fn_findTarget; // orders if (!isNull _target) then { _group move (_target getPos [random 100,random 360]); _group setFormDir (leader _group getDir _target); _group setSpeedMode "NORMAL"; _group enableGunLights "forceOn"; _group enableIRLasers true; // flare if (!_combat && {_onFoot} && {random 1 > 0.8}) then {[] call _fn_flare;}; // suppress building BUILDING SUPPRESSION! if (_combat && {(nearestBuilding _target distance2d _target < 25)}) then {[] call _fn_suppress;}; }; // WAIT FOR IT! sleep _cycle; }; fn_taskCreep Have the AI stalk, raptor style, the player forces. The group will attempt to move as close as possible before unleashing a hailstorm of fire. Sneaky, stealthy and quite scary. Use Run the script on unit or group with an optional argument for range (Default is 500 meters). [someUnit, 250] execVM "fn_taskStalk.sqf"; Features - Dynamic stalking script - Multiplayer friendly. - Change stance based on cover and concealment and distance to target Code Spoiler fn_taskCreep // Creep up close // version 4.4 // by nkenny /* Unit creeps up as close as possible before opening fire. Stance is based on distance Speed is always limited Hold fire for as long as possible. Arguments 1, Group or object tracker [Object or Group] 2, Range of tracking [Number] */ // functions --- _fn_findTarget = { _newDist = _range; _all = (switchableUnits + playableUnits - entities "HeadlessClient_F"); _all = _all select {side _x != civilian && {side _x != side _group}}; _target = objNull; { _distance = (leader _group) distance2d _x; if (_distance < _newDist && {getpos _x select 2 < 200}) then {_target = _x;_newDist = _distance;}; true } count _all; _target }; _fn_creepOrders = { // distance private _nearDist = leader _group distance2d _target; private _inForest = ((selectBestPlaces [getpos leader _group, 2,"(forest + trees)/2", 1, 1]) select 0) select 1; // danger mode? go for it! if (behaviour leader _group isEqualTo "COMBAT") exitWith {_group setCombatMode "RED";{_x setUnitpos "MIDDLE";_x domove (getposATL _target);true} count units _group;}; // vehicle? wait for it if (_nearDist < 150 && {vehicle _target isKindOf "Landvehicle"}) exitWith {_group reveal _target;{_x setunitpos "DOWN";true} count units _group;}; // adjust behaviour if (_inForest > 0.9 || _nearDist > 200) then {{_x setUnitpos "UP";true} count units _group}; if (_inForest < 0.6 || _nearDist < 100) then {{_x setUnitpos "MIDDLE";true} count units _group}; if (_inForest < 0.4 || _nearDist < 50) then {{_x setUnitpos "DOWN";true} count units _group}; if (_nearDist < 40) exitWith {_group setCombatMode "RED";_group setbehaviour "STEALTH";}; // move { _x doMove (_target getPos [random 10 + _foreachIndex * 5,random 360]); } foreach units _group; }; // functions end --- // init params ["_group",["_range",500],["_cycle",15]]; // sort grp if (!local _group) exitWith {}; if (_group isEqualType objNull) then {_group = group _group;}; // orders _group setbehaviour "AWARE"; _group setFormation "DIAMOND"; _group setSpeedMode "LIMITED"; _group setCombatMode "GREEN"; _group enableAttack false; ///{_x forceWalk true;} foreach units _group; <-- Use this if behaviour set to "STEALTH" // failsafe! { doStop _x; _x addEventhandler ["FiredNear",{ params ["_unit"]; doStop _x; _unit setCombatMode "RED"; _unit suppressFor 4; group _unit enableAttack true; _unit removeEventHandler ["FiredNear",_thisEventHandler]; }]; true } count units _group; // creep loop while {{alive _x} count units _group > 0} do { // performance waitUntil {sleep 1; simulationenabled leader _group}; // find _target = call _fn_findTarget; // act if (!isNull _target) then { call _fn_creepOrders; _cycle = 30; } else { _cycle = 120; _group setCombatMode "GREEN" }; // delay sleep _cycle; }; // end true --- GitHub GitHub Example mission Sample Mission YouTube video Special thanks - Nopry.no for extensive testing - Diwako and Joko for sound advice and putting up with my erratic space/tab combinations Cheers, nkenny Edited October 9, 2019 by nkenny Updated scripts 9 4 Share this post Link to post Share on other sites
LSValmont 789 Posted February 27, 2019 Very nice and useful script nkenny! Thanks! Maybe you want to put a sleep infront of your waitUntil to make your script even more performance friendly: waitUntil {sleep 1; simulationenabled leader _grp}; Quick question: Lets say a group of players get into that 500 radius but then leave super fast and get lets say 1500 mts away from that original 500 even before the Ai can get anywhere near the original 500's edge or triggering point... In that particular case the ai: 1- Will still chase the player no mater how far? 2- Will get to the edge of the original 500 and wait for another player in its new center's 500 radius? 3- When players out of range returns to their original location? Ok upon reading the script I can see that it is the option 2. Seems the best choice given the objectives of the script! Well done indeed. Also, for this line: // performance waitUntil {simulationenabled leader _grp}; To truly work for performance you should advice the mission editors to enable dynamic simulation on the units/group with the script on and also to uncheck "wake dynamic simulation" from the unit, otherwise the waitUntil always passes and serves no optimization purposes I believe. 2 Share this post Link to post Share on other sites
nkenny 1057 Posted February 27, 2019 Thanks for the input! Indeed, the second option is the intended behaviour. I will experiment with adding sleep on the waitUntil. While not particularly heavy, this isn't a script I would recommend running too many instances of in the first place. You have indeed deciphered the reason for the simulationEnabled parameter. I use it extensively in my own work. I considered adding it to the sample mission, but figured there is such a thing as too much information. 🙂 -k edit: I have more of these AI behaviour tweaks/scripts which I intend to release in the near future. I am actually surprised that CBA does not have this type of functionality already. 1 Share this post Link to post Share on other sites
nkenny 1057 Posted October 9, 2019 Original post updated. Stand alone scripts released as per. YouTube video should be available soon. ~ 09.10.2019: 18:31 -k 1 Share this post Link to post Share on other sites
johnnyboy 3793 Posted October 10, 2019 Excellent demonstration video Kenny, and very useful scripts. Thanks. For the fn_taskHunt script where units are patrolling casually initially, you might consider adding some code from my patrol chatter/lightsOnOff scripts. Rather than have lights on all the time, lights and chatter could occur less frequently, which can startle a player in a night mission when they don't see the patrol, then suddenly hear chatter or see a light come on. If you like the idea feel free to take some code. If not, no problem, your scripts are very cool as is. 2 Share this post Link to post Share on other sites
RCA3 593 Posted October 10, 2019 Hi, So, wonderful functions with lots of possible usage but I'd like to suggest something a little different: Instead of giving the AI a constantly updated position of the player, just give them the last known position, once on position tasks would: rush - occupy the position, form a 360º security perimeter and hold (rest) on a defensive/sentry stance. hunt - occupy the position and patrol around the area (hunting stance), 100m-300m max radius. creep - occupy the position and hide around it (ambush). This behaviour would deny that location to the player, which could be tactically decisive. During this process if they get new player intel they would continue on as before (rush-hunt-creep) moving to the new location. During init, you just place the task on unit/group init, but they wouldn't move until they receive intel. This would also avoid the need for a Sentry WP with the task on the Move WP. Thanks. 3 Share this post Link to post Share on other sites
ziegler62 2 Posted October 15, 2019 Hello to all! You have fulfilled my request, thank you! 1 Share this post Link to post Share on other sites
Robustcolor 31 Posted October 16, 2019 Hi, i can't get it to work, i placed this code inside a unit in the editor. [Unit1, 550] execVM "fn_taskRush.sqf"; Error Invalid number in expression. Also, is it possible to place this code inside a spawning ai script? Just example below, don't work. _group = [_randomPos, east, ["Unit"],[],[],[1,1],[1,1]] call BIS_fnc_spawnGroup; [_group, 550] execVM "fn_taskRush.sqf"; Share this post Link to post Share on other sites
nkenny 1057 Posted October 17, 2019 Sorry, haven't paid attention to this topic. @RCA3 Those would be pretty sleek upgrades, and I might steal borrow some of those ideas for a future game scenario. For now, I enjoy the simplicity of these scripts. They are also very easily expandable or modifiable to fit needs. As the video mentions, the intention is to hit or reach the players in a consistent manner. @johnnyboy Thank you, that is a great idea! @Robustcolor It certainly is! This is how I run many of my unit spawning scripts. Perhaps the path to the script or some other aspect of the script is wrong? Example: _group = [player getPos [600,random 360],EAST,6] call bis_fnc_spawnGroup; [_group,1000] execVM "fn_taskRush.sqf"; Should work just fine. 🙂 Share this post Link to post Share on other sites
RCA3 593 Posted October 17, 2019 I kind of saw the video and replied out of excitement and only then I read the scripts description: 😄 On 2/27/2019 at 1:03 PM, nkenny said: Ever needed the AI to dumbly hunt down and rush the players? Steal the ideas away, they're out there up for grabs. Also, I suggested this because in my eyes you had already done the hard part, don't want to burden you though. Cheers, keep up with the mind boggling work (took a first look at the Danger.FSM yesterday 😎). 👍 1 Share this post Link to post Share on other sites
Robustcolor 31 Posted October 17, 2019 12 hours ago, nkenny said: _group = [player getPos [600,random 360],EAST,6] call bis_fnc_spawnGroup; [_group,1000] execVM "fn_taskRush.sqf"; Thanks nkenny, it worked great. I have one more question about this, is it possible to call this as a function and how would the call look like? Share this post Link to post Share on other sites
nkenny 1057 Posted October 18, 2019 @Robustcolor Code example Spoiler // init.sqf /* Create an init.sqf and put it in your mission root folder */ // functions ~ precompiles nk_fnc_taskRush = compile preprocessFileLineNumbers "fn_taskRush.sqf"; // function ~ in the same file nk_fnc_unitSpawn = { params ["_pos",["_range",250],["_distance",600]]; // init _pos = _pos call bis_fnc_position; // find all players within range of objective. private _allPlayers = allPlayers - entities "HeadlessClient_F"; _allPlayers = _allPlayers select {_x distance _pos < _range}; // Exit on none if (_allPlayers isEqualTo []) exitWith {}; // pick one and use as spawning location private _player = selectRandom _allPlayers; _pos = nearestBuilding (_player getpos [_distance, (getDir player) + 180] ); // create group private _group = [getpos _pos,EAST,6] call bis_fnc_spawnGroup; // order group [_group,_distance + 500] spawn nk_fnc_taskRush; // end true }; Function use The nk_fnc_unitSpawn function accepts three arguments 0 : Position of unit spawn 1: Range within which players will trigger script, default is 250 meters (optional) 2: Distance which the enemy group will be spawned, default is 600 meters (optional) On running the function will consider one position. The position may be an ARRAY, a MARKER or OBJECT. It will then check among all players to see if any one of these is within RANGE meters (default 250). If present, it will pick a random one of the players. Behind one randomly selected player, the script will pick one position DISTANCE meters (default 600) away. It will then pick the nearest building and use that as a spawning point. The spawned unit will be given the taskRush mission. example use: ["marker1"] call nk_fnc_unitSpawn ; [ObjectiveTank, 500, 1000] call nk_fnc_unitSpawn; Homework: Firstly, these scripts are written offhand while at work. There is probably an error somewhere. Secondly, why not try to convert the in-file function to one that is preprocessed like fn_taskRush.sqf. :) 1 Share this post Link to post Share on other sites
Robustcolor 31 Posted October 18, 2019 Awesome work @nkenny One question, How would this code look like if i don't want to spawn them in buildings but just behind the selected player? _pos = nearestBuilding (_player getpos [_distance, (getDir player) + 180] ); Share this post Link to post Share on other sites
nkenny 1057 Posted October 18, 2019 // just the player ~ behind wherever he is facing. _pos = _player getpos [_distance, (getDir player) + 180]; // just the player ~ in the direction away from the center _pos = _player getpos [_distance,_pos getDir _player]; In either case you will also need to update the follow line to remove the getPos. // notice the removed 'getpos' private _group = [_pos,EAST,6] call bis_fnc_spawnGroup; Share this post Link to post Share on other sites
Robustcolor 31 Posted October 18, 2019 Thanks @nkenny Share this post Link to post Share on other sites
guttersnipe 1 Posted October 20, 2019 hi nkenny. Really enjoying using this script, so much easier than trying to script suppressive fire for each individual group. Can I make one request: - When I use fn_taskRush on a editor placed group which I've hidden by having them lie down in stealth mode the group behaves as scripted in the fn_taskRush i.e. they move to contact with the stance reset to auto. When I do the same with fn_taskHunt & fn_taskCreep the group moves as requested BUT they remain in the "DOWN" position through their approach (which if they've a large distance to cover is very time consuming). If I leave the group simply standing on start they move normally. Can u give them a default setunitpos line plse? (I'd not have a clue how to start). Many thanks for your work. Share this post Link to post Share on other sites
Robustcolor 31 Posted October 21, 2019 Works great. Share this post Link to post Share on other sites
Robustcolor 31 Posted October 30, 2019 @nkenny I've been using this to find a spawn position from center in player direction and it works good. But is it possible to find a position from center, but near player direction instead of always behind where the player comes from. // just the player ~ in the direction away from the center _pos = _player getpos [_distance,_pos getDir _player]; Share this post Link to post Share on other sites
nkenny 1057 Posted November 2, 2019 So infront of the player? As in closer to center? Not entirely sure what you mean. Try to reverse the _distance though. (i.e., _distance * -1) -k Share this post Link to post Share on other sites
Robustcolor 31 Posted November 2, 2019 1 hour ago, nkenny said: So infront of the player? As in closer to center? Not entirely sure what you mean. Try to reverse the _distance though. (i.e., _distance * -1) -k I meant if they can continue spawn in the same direction the player comes from but with a slight random angle behind the player. As of now they always spawn behind the player, 180. Share this post Link to post Share on other sites
nkenny 1057 Posted November 3, 2019 Not hard // just the player ~ in the direction away from the center _deviation = 40; _pos = _player getpos [_distance,(_pos getDir _player) + _deviation - (random (_deviation * 2))]; -k Share this post Link to post Share on other sites
Robustcolor 31 Posted November 3, 2019 1 hour ago, nkenny said: Not hard // just the player ~ in the direction away from the center _deviation = 40; _pos = _player getpos [_distance,(_pos getDir _player) + _deviation - (random (_deviation * 2))]; -k Thank you @nkenny What does * 2 do? Share this post Link to post Share on other sites
Robustcolor 31 Posted November 3, 2019 I'm using this code @nkenny private _pos = _player getpos [_distance,(_markerpos getDir _player) + _deviation - (random (_deviation *2))]; Works great. Share this post Link to post Share on other sites
Robustcolor 31 Posted November 6, 2019 @nkenny If i want to change so this code finds a player near a specific marker + range, do i only need to change _distance = getmarkerpos "Marker1" distance2d _x;? Not tested. _target = objNull; { _distance = (leader _group) distance2d _x; if (_distance < _newDist && {getpos _x select 2 < 200}) then {_target = _x;_newDist = _distance;}; true } count _all; _target Share this post Link to post Share on other sites
nkenny 1057 Posted November 6, 2019 @Robustcolor That is correct. The *2 is there to create a little deviation, so the units do not spawn perfectly away from the direction of the victim. 5 degrees deviation. 10 - 5 + random (5 * 2) Crates a range of 5-15 -k Share this post Link to post Share on other sites