Jump to content
nkenny

[Release] fn_taskRush/Hunt/Creep by nkenny

Recommended Posts

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 by nkenny
Updated scripts
  • Like 9
  • Thanks 4

Share this post


Link to post
Share on other sites

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.

  • Like 2

Share this post


Link to post
Share on other sites

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. 

  • Like 1

Share this post


Link to post
Share on other sites

Original post  updated. Stand alone scripts released as per. 

 

YouTube video should be available soon. ~ 09.10.2019: 18:31

-k 

  • Like 1

Share this post


Link to post
Share on other sites

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.

  • Like 2

Share this post


Link to post
Share on other sites

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.

  • Like 3

Share this post


Link to post
Share on other sites

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

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

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 😎). 👍

  • Like 1

Share this post


Link to post
Share on other sites
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

@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. :) 

  • Like 1

Share this post


Link to post
Share on other sites

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
// 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

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

@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

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
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

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
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

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

@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

@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

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×