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,currentWaypoint (group this)]"];
85 changes: 48 additions & 37 deletions addons/ai/fnc_searchNearby.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,66 @@ Description:
A function for a group to search a nearby building.

Parameters:
Group (Group or Object)
- Group (Group or Object)

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;

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

[_group,_building] spawn {
params ["_group","_building"];
private _leader = leader _group;

// Add a waypoint to regroup after the search
_group lockWP true;
private _wp = _group addWaypoint [getPos _leader, 0, currentWaypoint _group];
private _cond = "({unitReady _x || !(alive _x)} count thisList) == count thisList";
private _comp = format ["this setFormation %1; this setBehaviour %2; deleteWaypoint [group this, currentWaypoint (group this)];",formation _group,behaviour _leader];
_wp setWaypointStatements [_cond,_comp];

// Prepare group to search
_group setBehaviour "Combat";
_group setFormDir ([_leader, _building] call BIS_fnc_dirTo);

// Leader will only wait outside if group larger than 2
if (count (units _group) <= 2) then {
_leader = objNull;
};
_units = units _group;
_count = (count _units) - 1;

// Search while there are still available positions
private _positions = _building buildingPos -1;
while {!(_positions isEqualTo [])} do {
// Update units in case of death
private _units = (units _group) - [_leader];

// Abort search if the group has no units left
if (_units 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;
sleep 2;
};
} forEach _units;
};

_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;
122 changes: 71 additions & 51 deletions addons/ai/fnc_taskDefend.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -25,78 +25,98 @@ 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,{(_x emptyPositions "Gunner") > 0},true] call CBA_fnc_filter;

// Filter out buildings below the size threshold (and store positions for later use)
{
if (str(_x buildingpos _threshold) == "[0,0,0]") then {_buildings = _buildings - [_x]};
} foreach _buildings;
_i = 0;
if ((_x buildingPos _threshold) isEqualto [0,0,0]) then {
_buildings set [_forEachIndex,nil];
} else {
private _positions = _x buildingPos -1;

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

// v1.56 version of the above
/*_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;
{
_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 = _buildings call BIS_fnc_selectRandom;
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;
Loading