[Script] A neater, more consistent taskPatrol (+spawnPatrol)

Party-approved future science plus handbooks for the revolution
Post Reply
User avatar
Ciaran
Posts: 56
Joined: Thu Jul 21, 2016 9:36 pm
Location: Yorkshire, England

[Script] A neater, more consistent taskPatrol (+spawnPatrol)

Post by Ciaran » Mon Nov 07, 2016 9:51 pm

Why would you waste your time making this when we have BIS_fnc_taskPatrol?
BIS_fnc_taskPatrol is horribly inconsistent - it creates each new waypoint from the previous waypoint's position, so your patrol could end up either zigzagging back and forth or ending up 100 metres further out than you expected it to. taskPatrol's "distance" variable isn't a distance from the groups initial position, it's an arbitrary value.
That's where my patrol function fixes things. I wanted a consistent patrol function, that makes AI actually patrol around an area.
I'll admit this probably isn't the most user-friendly script, but it shouldn't take too long to figure out - everything is commented as best as I could. At some point I'll probably make this into one large function that handles everything neatly.
I have made this into 2 neat functions that handle both giving patrols and spawning units.
Obviously credit to BIS, as this is based on taskPatrol.

What does it do?
The first function generates 2-3 waypoints around a group's current position, within a set distance, and then gives the group a cycle waypoint so they follow the patrol route. It also generates an around each waypoint that is blacklisted, and cannot be used again for any call of the function in the mission (so it won't generate 2 waypoints touching each other).
The second function will spawn a group of units and give them a patrol. It can also be used to spawn a group of units and tell them to hold at their spawn position.

taskPatrol
What are the parameters for the function?
[grp,pos,distance] call cc_fnc_taskPatrol;
Where grp is the group to be given a patrol; pos is the position from which the units will patrol;distance is the distance they will patrol within.

How do I use it?
1. Create a new file in your mission folder called taskPatrol.sqf. In this file, paste the following:

Code: Select all

// Patrol function by Ciaran (vaguely based on BIS_fnc_taskPatrol)
params ["_grp","_pos","_maxDist"];
if (isNil "blacklistA") then {blacklistA = [];}; //if blacklist area isn't defined, define it so we can pushback blacklisted areas into it
[_grp,"SAFE","WEDGE","RED","LIMITED"] call ws_fnc_setAIMode; //set AI behaviour
if (_maxDist < 30) exitWith {private "_wp"; _wp = _grp addWaypoint [_pos, 0]; _wp setWaypointType "HOLD";} ; //no patrol for units with dist below 30, given hold waypoint instead
//create 2-3 waypoints
for "_i" from 0 to (1 + (floor (random 2))) do
{
  private ["_wp", "_newPos"];
  _newPos = [_pos,25,_maxDist,0.1,0,0,0,blacklistA,[_pos,_pos]] call BIS_fnc_findSafePos; //creates waypoints within maxDist of the group's starting pos
  // create an area around this waypoint that cannot be used again
  _blacklistP1 = _newPos getPos [25,315]; //top left point
  _blacklistP2 = _newPos getPos [25,135]; //bottom right point
  // DEBUG: Creates markers at these 2 points
  //_mkr1 = createMarker [format ["%1 -1",_blacklistP1],_blacklistP1];_mkr1 setMarkerSize [0.5,0.5];_mkr1 setMarkerType "mil_dot";_mkr1 setMarkerColor "ColorEAST";
  //_mkr2 = createMarker [format ["%1 -2",_blacklistP2],_blacklistP2];_mkr2 setMarkerSize [0.5,0.5];_mkr2 setMarkerType "mil_dot";_mkr2 setMarkerColor "ColorEAST";
  blacklistA pushback [_blacklistP1,_blacklistP2]; //pushback this area into the blacklist array
  //create move waypoint at the new position
  _wp = _grp addWaypoint [_newPos, 0];
  _wp setWaypointType "MOVE";
  _wp setWaypointCompletionRadius 3;
  // DEBUG: Marker placed at each wp, with maxDist displayed
  //_mkr = createMarker [format ["%1-bpos",_newPos],_newPos];_mkr setMarkerSize [0.5,0.5];_mkr setMarkerType "mil_dot";_mkr setMarkerColor "ColorGreen";_mkr setMarkerText format ["%1", _maxDist];
};

//cycle back to the group's original position
private ["_wp"];
_wp = (_this select 0) addWaypoint [_pos, 0];
_wp setWaypointType "CYCLE";
_wp setWaypointCompletionRadius 3;
2. In your mission's init.sqf, add the following line:

Code: Select all

cc_fnc_taskPatrol = compile preprocessfile "taskPatrol.sqf";
3. Call cc_fnc_taskPatrol from any script (run on server or HC only) with the parameters listed above, and the defined group will be given a random patrol.


spawnPatrol
What are the parameters for the function?
Mandatory; Optional.
[pos,distance,side,size,units] call cc_fnc_spawnPatrol;
Where pos is the position to spawn units at; distance is the distance they should patrol around; side is the side of the spawned units; size is the number of units to spawn.
Units can be either an array of units, from which the function will randomly select the number you defined with size, or if size is set to 0, units can be an array of arrays, for if you would like a specific group/groups of units to spawns. The function will randomly select one of the arrays within the units array.
If units is left undefined, the function will create a random group of units from the arrays in spawnPatrol.sqf.

How do I use it?
1. Create a new file called spawnPatrol.sqf, and paste the following code into it

Code: Select all

// spawn the patrol
params ["_pos","_dist","_side","_size","_units"];
if (count _this < 5) then {
  // NATO
  _grpWest = ["B_Soldier_lite_F","B_Soldier_F"];
  // CSAT
  _grpEast = ["O_Soldier_lite_F","O_Soldier_F"];
  // AAF
  _grpResistance = ["I_Soldier_lite_F","I_Soldier_F"];
  // Syndikat Paramilitary
  // ["I_C_Soldier_Para_1_F","I_C_Soldier_Para_2_F","I_C_Soldier_Para_7_F"];
  // Syndikat Bandit
  // ["I_C_Soldier_Bandit_4_F","I_C_Soldier_Bandit_7_F","I_C_Soldier_Bandit_5_F"];
  // FIA
  // ["B_G_Soldier_lite_F","B_G_Soldier_F"];

  _units = switch (_side) do {
	  case west: {_grpWest};
	  case east: {_grpEast};
	  case resistance: {_grpResistance};
  };
};
_spawnUnits = [];
if (_size == 0) then {
  _comp = _units; //if size is 0 then the data given should be an array of arrays of units, one of which is chosen when spawning the group
  _grp = [_pos,_side,(selectRandom _comp)] call BIS_fnc_spawnGroup; //spawn the group
  [_grp,_pos,_dist] call cc_fnc_taskPatrol; //give them the patrol
  _spawnUnits append units _grp; //collect units so we can return them, for use with assignGear etc
};

if (_size > 0) then {
  _comp = [];
  for "_i" from 1 to (_size) do { //randomly select units from the array until we reach the specified size
    _comp append [(selectRandom _units)];
  };
  _grp = [_pos,_side,_comp] call BIS_fnc_spawnGroup;
  [_grp,_pos,_dist] call cc_fnc_taskPatrol;
  _spawnUnits append units _grp;
};
//return spawned units
_spawnUnits

2. In your mission's init.sqf, add the following line:

Code: Select all

cc_fnc_spawnPatrol = compile preprocessfile "spawnPatrol.sqf";
Implementation Example
1. In the editor, place down a playable headless client entity, found under Systems -> Logic Entities. Name it hc.

2. Place down Game Logics where you want a group to spawn and patrol around. Name these patrolSpawn, patrolSpawn_1,...,patrolSpawn_n (copy and paste will name them like this). In the init of the Game Logic, paste the following

Code: Select all

this setvariable ["cc_patrolDist",100]
Where 100 is the distance the spawned units will patrol within. If this is set below 30, the units will hold instead (I advise keeping it above 35).

3. Create a file called patrol.sqf, and paste the following into it

Code: Select all

if ((isNil "hc" && !isServer) || (!isNil "hc" && (isServer || hasInterface))) exitWith {}; //headless client

_spawns = [patrolSpawn] call ws_fnc_collectObjectsNum;

_comp = [
  ["I_C_Soldier_Para_7_F","I_C_Soldier_Para_7_F","I_C_Soldier_Para_7_F"],
  ["I_C_Soldier_Para_5_F","I_C_Soldier_Para_1_F","I_C_Soldier_Para_1_F"],
  ["I_C_Soldier_Para_5_F","I_C_Soldier_Para_4_F","I_C_Soldier_Para_1_F"],
  ["I_C_Soldier_Para_1_F","I_C_Soldier_Para_4_F"],
  ["I_C_Soldier_Para_1_F","I_C_Soldier_Para_5_F"]
];

_units = [];
{
  _units append ([getpos _x,_x getvariable ["cc_patrolDist",100],resistance,0,_comp] call cc_fnc_spawnPatrol);
} forEach _spawns;

[_units,"f\assignGear\f_assignGear_AI.sqf"] remoteExec ["BIS_fnc_execVM",2]; //remoteExec because assignGear/setSkill only run server side
[_units,"f\setAISkill\f_setAISkill.sqf"] remoteExec ["BIS_fnc_execVM",2];
The array _comp can of course be replaced with a single array of units, for example _comp = [["I_C_Soldier_Para_7_F","I_C_Soldier_Para_7_F","I_C_Soldier_Para_7_F"]]; if you only wanted groups of 3 specific units to spawn.
The final 2 lines will run assignGear and setSkill on the units.

4. Add the following line to your mission's init.sqf, after the lines for the function scripts.

Code: Select all

[] execVM "patrol.sqf"
Issues:
In the current version of ws_fnc in the FA3 template, ws_fnc_setAIMode will have a tantrum if you give it more than three entries (as this function does). There is no reason for this, it will happily accept four entries. To fix this, delete line 37 in ws_fnc/AI/fn_setAIMode.sqf

Feel free to post any feedback below.
Last edited by Ciaran on Thu Sep 28, 2017 2:52 pm, edited 2 times in total.
Signature, noun; a person's name written in a distinctive way as a form of identification in authorizing a cheque or document or concluding a letter.

User avatar
Ciaran
Posts: 56
Joined: Thu Jul 21, 2016 9:36 pm
Location: Yorkshire, England

Re: [Script] A neater, more consistent taskPatrol (+spawnPat

Post by Ciaran » Thu Jan 12, 2017 12:43 pm

Updated so it does things better.
Signature, noun; a person's name written in a distinctive way as a form of identification in authorizing a cheque or document or concluding a letter.

Post Reply