Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update AI functions #333

Merged
merged 10 commits into from
May 21, 2016
6 changes: 6 additions & 0 deletions addons/ai/CfgFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ class CfgFunctions
description = "A function used to add a waypoint to a group. Parameters: - Group (Group or Object) - Position (XYZ, Object, Location or Group) Optional: - Radius (Scalar) - Waypoint Type (String) - Behaviour (String) - Combat Mode (String) - Speed Mode (String) - Formation (String) - Code To Execute at Each Waypoint (String) - TimeOut at each Waypoint (Array [Min, Med, Max]) - Waypoint Completion Radius (Scalar) Example: [this, this, 300, ""MOVE"", ""AWARE"", ""YELLOW"", ""FULL"", ""STAG COLUMN"", ""this spawn CBA_fnc_searchNearby"", [3,6,9]] Returns: Waypoint Author: Rommel";
file = "\x\cba\addons\ai\fnc_addWaypoint.sqf";
};
// CBA_fnc_clearWaypoints
class clearWaypoints
{
description = "A function used to correctly clear all waypoints from a group.";
file = "\x\cba\addons\ai\fnc_clearWaypoints.sqf";
};
// CBA_fnc_searchNearby
class searchNearby
{
Expand Down
34 changes: 34 additions & 0 deletions addons/ai/fnc_clearWaypoints.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* ----------------------------------------------------------------------------
Function: CBA_fnc_clearWaypoints

Description:
A function used to correctly clear all waypoints from a group.

Parameters:
- Group (Group or Object)

Example:
(begin example)
[group player] call CBA_fnc_clearWaypoints
(end)

Returns:
None

Author:
SilentSpike

---------------------------------------------------------------------------- */
#include "script_component.hpp"
params [["_group", grpNull, [grpNull,objNull]]];
_group = _group call CBA_fnc_getGroup;

private _waypoints = waypoints _group;
{
// Waypoint index changes with each deletion, so don't delete _x
deleteWaypoint [_group,0];
} forEach _waypoints;

// Create a self-deleting waypoint at the leader position to halt all planned movement (based on old waypoints)
private _wp = _group addWaypoint [getPosATL (leader _group), 0];
_wp setWaypointStatements ["true", "deleteWaypoint [group this,0]"];
77 changes: 41 additions & 36 deletions addons/ai/fnc_searchNearby.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,56 @@ Parameters:

Example:
(begin example)
[group player] spawn CBA_fnc_searchNearby
[group player] call CBA_fnc_searchNearby
(end)

Returns:
Nil

Author:
Rommel
Rommel, SilentSpike

---------------------------------------------------------------------------- */

params ["_group"];
_group = _group call CBA_fnc_getgroup;
_group lockwp true;
private ["_leader","_behaviour"];
_leader = leader _group;
_behaviour = behaviour _leader;
_group setbehaviour "combat";

(_leader call CBA_fnc_getnearestbuilding) params ["_building", "_indices"];
_group setformdir ([_leader, _building] call bis_fnc_dirto);

if (_leader distance _building > 500) exitwith {_group lockwp false};

private ["_count","_units"];
_units = units _group;
_count = (count _units) - 1;

while {_indices > 0 && {_count > 0}} do {
sleep 10;
while {_indices > 0 && {_count > 0}} do {
private "_unit";
_unit = _units select _count;
if (unitready _unit) then {
_unit commandmove (_building buildingpos _indices);
_indices = _indices - 1;
};
_count = _count - 1;
_group = _group call CBA_fnc_getGroup;
_group lockWP true;

private _building = nearestBuilding (leader _group);
if ((leader _group) distanceSqr _building > 250e3) exitwith {_group lockWP false};

[_group,_building] spawn {
params ["_group","_building"];
private _behaviour = behaviour (leader _group);

// Prepare group to search
_group setBehaviour "Combat";
_group setFormDir ((leader _group) getDir _building);

// Search while there are still available positions
private _positions = _building buildingPos -1;
while {!(_positions isEqualTo [])} do {
// Abort search if the group has no units left
if ((units _group) isEqualTo []) exitWith {};

// Send all available units to the next available position
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change in behaviour: the old version does not send the first unit (leader?) into the building, but the new one does.

Copy link
Contributor Author

@kymckay kymckay May 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, though I'm going to write it such that the leader will only stay outside when the group has more than 2 units as otherwise it actually makes sense for the leader to enter 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I've noticed that the leader will continue on to the group's next waypoint if he isn't searching the building himself.

Should I change it to make him wait outside until the search is done? Otherwise he could run quite far ahead of the group.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's better than him going on without his team.

Copy link
Contributor Author

@kymckay kymckay May 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I can't find a reliable & safe way to do this.

I could create a waypoint - which introduces the problem of trying to safely remove the waypoint once it has served its purpose (since the creation of other waypoints could change the index - I think using the waypoint statement combined with currentWaypoint could do the trick though).

While doStop permanently stops the unit as far as I can tell - which can be avoided in future once the disableAI "PATH" functionality is in the release version of arma.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, got the waypoint solution working cleanly - it's actually preferable to using AI commands because it also takes care of spare group members too and allows me to put the post-search code into the completion statement instead of running a loop 😄

{
if (_positions isEqualTo []) exitWith {};
if (unitReady _x) then {
private _pos = _positions deleteAt 0;
_x commandMove _pos;
};
} forEach (units _group);

// Provide time for orders to be carried out
sleep 10;
};
_units = units _group;
_count = (count _units) - 1;

// Once units are all finished searching return to previous tasks
waitUntil {sleep 3; {unitReady _x} count (units _group) >= count (units _group) - 1};
{
_x doFollow (leader _group);
} forEach (units _group);
_group setBehaviour _behaviour;
_group lockWP false;
};
waituntil {sleep 3; {unitready _x} count _units >= count (units _group) - 1};
{
_x dofollow _leader;
} foreach _units;
_group setbehaviour _behaviour;
_group lockwp false;
110 changes: 58 additions & 52 deletions addons/ai/fnc_taskDefend.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -25,78 +25,84 @@ Returns:
Nil

Author:
Rommel
Rommel, SilentSpike

---------------------------------------------------------------------------- */

params ["_group", ["_position",[]], ["_radius",50], ["_threshold",2]];
params ["_group", ["_position",[]], ["_radius",50,[0]], ["_threshold",2,[0]], ["_patrol",true,[true]]];

_group = _group call CBA_fnc_getGroup;
if !(local _group) exitWith {}; // Don't create waypoints on each machine

_position = [_position,_group] select (_position isEqualTo []);
_position = _position call CBA_fnc_getPos;

_group enableattack false;
[_group] call CBA_fnc_clearWaypoints;
_group enableAttack false;

private ["_count", "_list", "_list2", "_units", "_i"];
_statics = [_position, vehicles, _radius, {(_x iskindof "StaticWeapon") && {(_x emptypositions "Gunner" > 0)}}] call CBA_fnc_getnearest;
_buildings = _position nearObjects ["building",_radius];
_units = units _group;
_count = count _units;
private _statics = _position nearObjects ["StaticWeapon", _radius];
private _buildings = _position nearObjects ["Building", _radius];

// Filter out occupied statics
_statics = _statics select { (_x emptyPositions "Gunner") > 0 };
Copy link
Contributor

@Killswitch00 Killswitch00 May 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not compatible with the MacOS/Linux ports, currently at v1.54. Needs workaround like commy2 did recently (the "linux.pbo" addon). (Or use CBA_fnc_filter for now)


// Filter out buildings below the size threshold (and store positions for later use)
_buildings = _buildings select {
private _positions = _x buildingPos -1;

if (isNil {_x getVariable "CBA_taskDefend_positions"}) then {
_x setVariable ["CBA_taskDefend_positions",_positions];
};

count (_positions) > _threshold
};

private _units = units _group;
private _assigned = 0;
{
if (str(_x buildingpos _threshold) == "[0,0,0]") then {_buildings = _buildings - [_x]};
} foreach _buildings;
_i = 0;
{
_count = (count _statics) - 1;
if (random 1 < 0.31 && {_count > -1}) then {
_x assignasgunner (_statics select _count);
_statics resize _count;
[_x] ordergetin true;
_i = _i + 1;
// 31% chance to occupy nearest free static weapon
if ((random 1 < 0.31) && { !(_statics isEqualto []) }) then {
_x assignAsGunner (_statics deleteAt 0);
[_x] orderGetIn true;

_assigned = _assigned + 1;
} else {
if (random 1 < 0.93 && {count _buildings > 0}) then {
private ["_building","_p","_array"];
_building = _buildings call BIS_fnc_selectRandom;
_array = _building getvariable "CBA_taskDefend_positions";
if (isnil "_array") then {
private "_k"; _k = 0;
_building setvariable ["CBA_taskDefend_positions",[]];
while {str(_building buildingpos _k) != "[0,0,0]"} do {
_building setvariable ["CBA_taskDefend_positions",(_building getvariable "CBA_taskDefend_positions") + [_k]];
_k = _k + 1;
};
_array = _building getvariable "CBA_taskDefend_positions";
};
if (count _array > 0) then {
_p = (_building getvariable "CBA_taskDefend_positions") call BIS_fnc_selectRandom;
_array = _array - [_p];
if (count _array == 0) then {
// 93% chance to occupy a random nearby building position
if ((random 1 < 0.93) && { !(_buildings isEqualto []) }) then {
private _building = selectRandom _buildings;
Copy link
Contributor

@Killswitch00 Killswitch00 May 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ports compatibiltiy issue here aswell - selectRandom is 1.56+. BIS_fnc_selectRandom can be used for now.

private _array = _building getVariable ["CBA_taskDefend_positions",[]];

if !(_array isEqualTo []) then {
private _pos = _array deleteAt (floor(random(count _array)));

// If building positions are all taken remove from possible buildings
if (_array isEqualTo []) then {
_buildings = _buildings - [_building];
_building setvariable ["CBA_taskDefend_positions",nil];
_building setVariable ["CBA_taskDefend_positions",nil];
};
_building setvariable ["CBA_taskDefend_positions",_array];
[_x,_building buildingpos _p] spawn {
if (surfaceIsWater (_this select 1)) exitwith {};
(_this select 0) domove (_this select 1);
_building setVariable ["CBA_taskDefend_positions",_array];

// AI manipulation trickey to keep them in position until commanded to move
[_x, _pos] spawn {
params ["_unit","_pos"];
if (surfaceIsWater _pos) exitwith {};

_unit doMove _pos;
sleep 5;
waituntil {unitready (_this select 0)};
(_this select 0) disableai "move";
dostop _this;
waituntil {not (unitready (_this select 0))};
(_this select 0) enableai "move";
waituntil {unitReady _unit};
_unit disableAI "move";
Copy link
Contributor Author

@kymckay kymckay May 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that this function should definitely be updated to use the new feature added in today's dev branch changelog:

Ability to enableAI / disableAI "PATH", which stops the AI’s movement but not the target alignment

However I have no idea if the dev branch stuff will appear in the next verison of arma, or the one after

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably something for a future PR when we do know in which version it will be available?

doStop _unit;
waituntil {!(unitReady _unit)};
_unit enableAI "move";
};
_i = _i + 1;

_assigned = _assigned + 1;
};
};
};
} foreach _units;
{
_x setvariable ["CBA_taskDefend_positions",nil];
} foreach _buildings;
if (count _this > 4 && {!(_this select 4)}) then {_i = _count};
if (_i < _count * 0.5) then {
} forEach _units;

// If half of the group's units aren't assigned then patrol
if (_patrol && {_assigned < (count _units) * 0.5}) then {
[_group, _position, _radius, 5, "sad", "safe", "red", "limited"] call CBA_fnc_taskpatrol;
};
22 changes: 15 additions & 7 deletions addons/ai/fnc_taskPatrol.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Optional:
Example:
(begin example)
[this, getmarkerpos "objective1"] call CBA_fnc_taskPatrol
[this, this, 300, 7, "MOVE", "AWARE", "YELLOW", "FULL", "STAG COLUMN", "this spawn CBA_fnc_searchNearby", [3,6,9]] call CBA_fnc_taskPatrol;
[this, this, 300, 7, "MOVE", "AWARE", "YELLOW", "FULL", "STAG COLUMN", "this call CBA_fnc_searchNearby", [3,6,9]] call CBA_fnc_taskPatrol;
(end)

Returns:
Expand All @@ -43,15 +43,23 @@ if !(local _group) exitWith {}; // Don't create waypoints on each machine
_position = [_position,_group] select (_position isEqualTo []);
_position = _position call CBA_fnc_getPos;

_this =+ _this;
// Clear existing waypoints first
[_group] call CBA_fnc_clearWaypoints;

private _this =+ _this;
if (count _this > 3) then {
_this deleteAt 3;
};
for "_x" from 1 to _count do {

// Store first WP to close loop later
private _wp = _this call CBA_fnc_addWaypoint;

for "_x" from 1 to (_count - 1) do {
_this call CBA_fnc_addWaypoint;
};
_this2 =+ _this;
_this2 set [3, "CYCLE"];
_this2 call CBA_fnc_addWaypoint;

deleteWaypoint ((waypoints _group) select 0);
// Close the patrol loop
_this set [1, getWPPos _wp];
_this set [2, 0];
_this set [3, "CYCLE"];
_this call CBA_fnc_addWaypoint;
27 changes: 15 additions & 12 deletions addons/ai/fnc_taskSearchArea.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Description:
Parameters:
_group - The group that will search [Group or Object]
_area - The area to search [Marker or Trigger]

Optional:
_behaviour - Waypoint behaviour [String, defaults to "UNCHANGED"]
_combat - Waypoint combat mode [String, defaults to "NO CHANGE"]
_speed - Waypoint speed [String, defaults to "UNCHANGED"]
Expand Down Expand Up @@ -38,32 +40,36 @@ params [
["_onComplete", "", [""]],
["_timeout", [0,0,0], [[]], 3]
];
private ["_pos","_args","_statement","_building"];

_group = _group call CBA_fnc_getGroup;
if !(local _group) exitWith {}; // Don't create waypoints on each machine

// Cache arguments as group variable for recursive calls
_args = [_area,_behaviour,_combat,_speed,_formation,_onComplete,_timeout];
if (_area isEqualTo "") then {
// Collect arguments for use in recursive calls (not using `select` to include default values)
private _args = [_area,_behaviour,_combat,_speed,_formation,_onComplete,_timeout];

// Retrieve cached arguments in case of recursive call
if (isNil {param [1]}) then {
_args = _group getVariable [QGVAR(taskSearch),_args];
} else {
// Clear existing waypoints and cache arguments upon first call
[_group] call CBA_fnc_clearWaypoints;
_group setVariable [QGVAR(taskSearch),_args];
};
_group setVariable [QGVAR(taskSearch),_args];
_args params ["_area","_behaviour","_combat","_speed","_formation","_onComplete","_timeout"];

// Select a random position in the area
_pos = [_area] call CBA_fnc_randPosArea;
private _pos = [_area] call CBA_fnc_randPosArea;

// Exit if any bad input was used (has to run after all the above code)
if ((_pos isEqualTo []) || {_area isEqualTo ""} || {isNull _group}) exitWith {};

// Prepare recursive function call statement
_statement = "[this] call CBA_fnc_taskSearchArea;";
private _statement = "[this] call CBA_fnc_taskSearchArea;";

// Prepare building search statement
_building = nearestBuilding _pos;
private _building = nearestBuilding _pos;
if ((_building distanceSqr _pos) < 400) then {
_statement = _statement + "[this] spawn CBA_fnc_searchNearby;";
_statement = _statement + "[this] call CBA_fnc_searchNearby;";
};

// Inject the statement in this order to ensure valid syntax
Expand All @@ -83,6 +89,3 @@ _onComplete = _statement + _onComplete;
_timeout,
5
] call CBA_fnc_addWaypoint;

// Remove starting/previous waypoint
deleteWaypoint [_group,0];