diff --git a/addons/ai/CfgFunctions.hpp b/addons/ai/CfgFunctions.hpp index 715586fdf..33caeabb6 100644 --- a/addons/ai/CfgFunctions.hpp +++ b/addons/ai/CfgFunctions.hpp @@ -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 { diff --git a/addons/ai/fnc_clearWaypoints.sqf b/addons/ai/fnc_clearWaypoints.sqf new file mode 100644 index 000000000..378ffcd75 --- /dev/null +++ b/addons/ai/fnc_clearWaypoints.sqf @@ -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)]"]; diff --git a/addons/ai/fnc_searchNearby.sqf b/addons/ai/fnc_searchNearby.sqf index f9e921368..df38e2bad 100644 --- a/addons/ai/fnc_searchNearby.sqf +++ b/addons/ai/fnc_searchNearby.sqf @@ -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 + { + 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; diff --git a/addons/ai/fnc_taskDefend.sqf b/addons/ai/fnc_taskDefend.sqf index c16bf7a36..31dc860ea 100644 --- a/addons/ai/fnc_taskDefend.sqf +++ b/addons/ai/fnc_taskDefend.sqf @@ -25,11 +25,11 @@ 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 @@ -37,66 +37,86 @@ 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"; + 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; }; diff --git a/addons/ai/fnc_taskPatrol.sqf b/addons/ai/fnc_taskPatrol.sqf index c00f6b47f..40cfd05c7 100644 --- a/addons/ai/fnc_taskPatrol.sqf +++ b/addons/ai/fnc_taskPatrol.sqf @@ -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: @@ -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; diff --git a/addons/ai/fnc_taskSearchArea.sqf b/addons/ai/fnc_taskSearchArea.sqf index c9e39a3da..3d9de23dc 100644 --- a/addons/ai/fnc_taskSearchArea.sqf +++ b/addons/ai/fnc_taskSearchArea.sqf @@ -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"] @@ -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 @@ -83,6 +89,3 @@ _onComplete = _statement + _onComplete; _timeout, 5 ] call CBA_fnc_addWaypoint; - -// Remove starting/previous waypoint -deleteWaypoint [_group,0];