diff --git a/A3XAI/$PREFIX$ b/A3XAI/$PREFIX$ new file mode 100644 index 0000000..5cc6ba6 --- /dev/null +++ b/A3XAI/$PREFIX$ @@ -0,0 +1 @@ +A3XAI \ No newline at end of file diff --git a/A3XAI/PboPrefix.txt b/A3XAI/PboPrefix.txt new file mode 100644 index 0000000..a9807f0 --- /dev/null +++ b/A3XAI/PboPrefix.txt @@ -0,0 +1 @@ +A3EAI \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_findclosestposition.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_findclosestposition.sqf new file mode 100644 index 0000000..43018b9 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_findclosestposition.sqf @@ -0,0 +1,19 @@ +// Return the closest position from array to the positionA. +// In: [positionA,[array of positions]] +// Out: positionB +private ["_pA","_ps","_p1","_p2"]; +_pA = _this select 0; +_ps = _this select 1; + +_p1 = _ps select 0; + +if (count _ps > 1) then { + for "_i" from 1 to (count _ps - 1) do { + _p2 = _ps select _i; + if ((_p2 distance _pA) < (_p1 distance _pA)) then { + _p1 = _p2; + }; + }; +}; + +_p1 \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getmarkercorners.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getmarkercorners.sqf new file mode 100644 index 0000000..1cac68b --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getmarkercorners.sqf @@ -0,0 +1,59 @@ +// In: marker +// Out: array of positions +private ["_area","_corners"]; +_area = _this; +_corners = []; + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _area; +_centerX = _center select 0; +_centerY = _center select 1; + +// Direction and make sure it's between 0 and 360. +private ["_dir","_dirCos","_dirSin"]; +_dir = (markerDir _area) * -1; +_dir = _dir % 360; +_dirCos = cos _dir; +_dirSin = sin _dir; + +// Size +private ["_size","_sizeX","_sizeY"]; +_size = getMarkerSize _area; +_sizeX = _size select 0; +_sizeY = _size select 1; + + +private ["_cosX","_sinX","_cosY","_sinY","_addX","_addY","_subX","_subY"]; +_cosX = _dirCos * _sizeX; +_sinX = _dirSin * _sizeX; +_cosY = _dirCos * _sizeY; +_sinY = _dirSin * _sizeY; + +_addX = _cosX + _sinY; +_addY = _sinX + _cosY; +_subX = _cosX - _sinY; +_subY = _sinX - _cosY; + +private ["_posX","_posY"]; +// Bottom Left +_posX = _centerX - _subX; +_posY = _centerY - _addY; +_corners set [0,[_posX,_posY]]; + +// Top Left +_posX = _centerX - _addX; +_posY = _centerY - _subY; +_corners set [1,[_posX,_posY]]; + +// Top Right +_posX = _centerX + _subX; +_posY = _centerY + _addY; +_corners set [2,[_posX,_posY]]; + +// Bottom Right +_posX = _centerX + _addX; +_posY = _centerY + _subY; +_corners set [3,[_posX,_posY]]; + +_corners \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getmarkershape.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getmarkershape.sqf new file mode 100644 index 0000000..e397c5e --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getmarkershape.sqf @@ -0,0 +1,30 @@ +// In: marker +// Out: string (marker shape) + +private ["_size","_x","_y","_ret"]; +_size = markersize _this; +_x = _size select 0; +_y = _size select 1; + +_ret = ""; + +switch (tolower(markershape _this)) do { + case "rectangle": { + if (_x == _y) then { + _ret = "SQUARE"; + } else { + _ret = "RECTANGLE"; + }; + }; + case "ellipse": { + if (_x == _y) then { + _ret = "CIRCLE"; + } else { + _ret = "ELLIPSE"; + }; + }; + case "icon": { + _ret = "ICON"; + }; +}; +_ret \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getpos.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getpos.sqf new file mode 100644 index 0000000..d5c5fb4 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getpos.sqf @@ -0,0 +1,13 @@ +// In: [position,distance,direction] +// Out: position +private ["_pos","_dst","_dir","_orgX","_orgY","_posX","_posY"]; +_pos = _this select 0; +_dst = _this select 1; +_dir = _this select 2; + +_orgX = _pos select 0; +_orgY = _pos select 1; +_posX = _orgX + (_dst * sin _dir); +_posY = _orgY + (_dst * cos _dir); + +[_posX,_posY,0] \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromcircle.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromcircle.sqf new file mode 100644 index 0000000..87269f8 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromcircle.sqf @@ -0,0 +1,26 @@ +// In: marker +// Out: position + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _this; +_centerX = _center select 0; +_centerY = _center select 1; + +// Size +private ["_size"]; +_size = getMarkerSize _this; +_size = _size select 0; + +// Randomly pick a direction, +private ["_dir","_posX","_posY","_rand","_pos"]; +_dir = random 360; +_rand = sqrt random 1; +_posX = (_size * (cos _dir)) * _rand; +_posY = (_size * (sin _dir)) * _rand; +_pos = [_posX,_posY]; + +_posX = _centerX + (_pos select 0); +_posY = _centerY + (_pos select 1); + +[_posX,_posY,0] diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromellipse.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromellipse.sqf new file mode 100644 index 0000000..e4dd101 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromellipse.sqf @@ -0,0 +1,43 @@ +// In: ellipseMarker +// Out: position + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _this; +_centerX = _center select 0; +_centerY = _center select 1; + +// Direction and make sure it's between 0 and 360. +private ["_dirMrk"]; +_dirMrk = (markerDir _this) * -1; +_dirMrk = _dirMrk % 360; + +// Size +private ["_size","_sizeX","_sizeY"]; +_size = getMarkerSize _this; +_sizeX = _size select 0; +_sizeY = _size select 1; + +// If B axis is longer than A, switch them and fix direction. +if (_sizeX < _sizeY) then { + _sizeX = _size select 1; + _sizeY = _size select 0; + _dirMrk = _dirMrk + 90; +}; + +// Randomly pick a direction, +private ["_dir","_posX","_posY","_rand","_pos"]; +_dir = random 360; +_rand = sqrt random 1; +_posX = (_sizeX * (cos _dir)) * _rand; +_posY = (_sizeY * (sin _dir)) * _rand; +_pos = [_posX,_posY]; + +if (_dirMrk != 0) then { + _pos = [_pos,_dirMrk] call A3XAI_SHK_pos_fnc_rotatePosition; +}; + +_posX = _centerX + (_pos select 0); +_posY = _centerY + (_pos select 1); + +[_posX,_posY,0] diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromrectangle.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromrectangle.sqf new file mode 100644 index 0000000..76ebd7d --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromrectangle.sqf @@ -0,0 +1,37 @@ +// In: marker +// Out: position + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _this; +_centerX = _center select 0; +_centerY = _center select 1; + +// Size +private ["_size","_sizeX","_sizeY"]; +_size = getMarkerSize _this; +_sizeX = _size select 0; +_sizeY = _size select 1; + +// Direction and make sure it's between 0 and 360. +private ["_dir","_dirCos","_dirSin"]; +_dir = (markerDir _this) * -1; +_dir = _dir % 360; +_dirCos = cos _dir; +_dirSin = sin _dir; + +private ["_rndX","_rndY","_posX","_posY"]; +// Select random X and Y +_rndX = (random (_sizeX * 2)) - _sizeX; +_rndY = (random (_sizeY * 2)) - _sizeY; + +// If area is angled, shift X and Y +if (_dir != 0) then { + _posX = _centerX + (_dirCos * _rndX - _dirSin * _rndY); + _posY = _centerY + (_dirSin * _rndX + _dirCos * _rndY); +} else { + _posX = _centerX + _rndX; + _posY = _centerY + _rndY; +}; + +[_posX,_posY,0] \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromsquare.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromsquare.sqf new file mode 100644 index 0000000..c47df5b --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_getposfromsquare.sqf @@ -0,0 +1,36 @@ +// In: marker +// Out: position + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _this; +_centerX = _center select 0; +_centerY = _center select 1; + +// Size +private ["_size"]; +_size = getMarkerSize _this; +_size = _size select 0; + +// Direction and make sure it's between 0 and 360. +private ["_dir","_dirCos","_dirSin"]; +_dir = (markerDir _this) * -1; +_dir = _dir % 360; +_dirCos = cos _dir; +_dirSin = sin _dir; + +private ["_rndX","_rndY","_posX","_posY"]; +// Select random X and Y +_rndX = (random (_size * 2)) - _size; +_rndY = (random (_size * 2)) - _size; + +// If area is angled, shift X and Y +if (_dir != 0) then { + _posX = _centerX + (_dirCos * _rndX - _dirSin * _rndY); + _posY = _centerY + (_dirSin * _rndX + _dirCos * _rndY); +} else { + _posX = _centerX + _rndX; + _posY = _centerY + _rndY; +}; + +[_posX,_posY,0] \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isblacklisted.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isblacklisted.sqf new file mode 100644 index 0000000..ba0dd92 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isblacklisted.sqf @@ -0,0 +1,85 @@ +// In: [position,blackListMarker] +// Out: boolean + +private ["_pos","_area","_return"]; +_pos = _this select 0; +_area = _this select 1; +_return = false; + +// Find corner positions of the rectangle +private ["_dir"]; +_dir = markerDir _area; +_dir = _dir % 360; + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _area; +_centerX = _center select 0; +_centerY = _center select 1; + +private ["_shape"]; +_shape = _area call A3XAI_SHK_pos_fnc_getMarkerShape; + +if (_shape == "ICON") then { + // Icon has only one position, so if it equals to the given position, then it's blacklisted. + if ([_pos,_center] call A3XAI_SHK_pos_fnc_isSamePosition) then { + _return = true; + }; + +// Markers that have an area. +} else { + if (_shape in ["RECTANGLE","SQUARE"]) then { + private ["_corners"]; + _corners = _area call A3XAI_SHK_pos_fnc_getMarkerCorners; + + // If rectangle is not axis-aligned. + if (_dir % 90 != 0) then { + // Add the point position to the array to have it shifted by the FOR below + _corners set [4,_pos]; + + // Rotate each corner position so that the rectangle is aligned with x and y axises + // Use origo as center while rotating, but for comparison shift positions back + private ["_posCor","_posNew","_orgX","_orgY","_shiftedX","_shiftedY","_newX","_newY"]; + for "_i" from 0 to (count _corners - 1) do { + _posCor = _corners select _i; + + // Original coordinates + _orgX = _posCor select 0; + _orgY = _posCor select 1; + + // Subtract the marker center coordinates from corner coordinates. + // Rotation is done using origo (0,0) as anchor/centerpoint. + _shiftedX = _orgX - _centerX; + _shiftedY = _orgY - _centerY; + + // Axis-aligned corner position + _posNew = [[_shiftedX,_shiftedY],_dir] call A3XAI_SHK_pos_fnc_rotatePosition; + + // Shift the aligned corner position back near to the original marker location. + _newX = _posNew select 0; + _newY = _posNew select 1; + _newX = _newX + _centerX; + _newY = _newY + _centerY; + + _posCor = [_newX,_newY]; + + _corners set [_i,_posCor]; + }; + + // Point position + _pos = _corners select 4; + }; + + // Check if the position is within the marker area. + _return = [_pos,_corners] call A3XAI_SHK_pos_fnc_isInRectangle; + } else { + if (_shape == "CIRCLE") then { + _return = [_pos,_area] call A3XAI_SHK_pos_fnc_isInCircle; + } else { + _return = [_pos,_area] call A3XAI_SHK_pos_fnc_isInEllipse; + }; + }; +}; + + +_return \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isincircle.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isincircle.sqf new file mode 100644 index 0000000..39616d9 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isincircle.sqf @@ -0,0 +1,36 @@ +// In: [position,marker] +// Out: boolean + +private ["_pos","_area","_posX","_posY"]; +_pos = _this select 0; +_area = _this select 1; + +_posX = _pos select 0; +_posY = _pos select 1; + +// Center point +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _area; +_centerX = _center select 0; +_centerY = _center select 1; + +// Size +private ["_size"]; +_size = getMarkerSize _area; +_size = _size select 0; + +// Difference in coordinates +private ["_difX","_difY"]; +_difX = _posX - _centerX; +_difY = _posY - _centerY; + +private ["_return"]; +_return = false; + +// If distance from center of marker to the given position is +// smaller than the radius of the circle, then position is inside. +if (sqrt((_difX * _difX) + (_difY * _difY)) < _size) then { + _return = true; +}; + +_return \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isinellipse.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isinellipse.sqf new file mode 100644 index 0000000..b48a0e0 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isinellipse.sqf @@ -0,0 +1,54 @@ +// In: [position,ellipseMarker] +// Out: boolean + +private ["_pos","_area","_return"]; +_pos = _this select 0; +_area = _this select 1; +_return = false; + +// Ellipse size +private ["_size","_sizeX","_sizeY"]; +_size = getMarkerSize _area; +_sizeX = _size select 0; +_sizeY = _size select 1; + +// Direction and make sure it's between 0 and 360. +private ["_dir"]; +_dir = markerDir _area; +_dir = _dir % 360; + +// Ellipse center position +private ["_center","_centerX","_centerY"]; +_center = getMarkerPos _area; +_centerX = _center select 0; +_centerY = _center select 1; + +// If marker is not axis-aligned, rotate the dot position. +if (_dir % 90 != 0) then { + private ["_orgX","_orgY","_shiftedX","_shiftedY"]; + _orgX = _pos select 0; + _orgY = _pos select 1; + _shiftedX = _orgX - _centerX; + _shiftedY = _orgY - _centerY; + _pos = [[_shiftedX,_shiftedY],_dir] call A3XAI_SHK_pos_fnc_rotatePosition; + _pos set [0,(_pos select 0) + _centerX]; + _pos set [1,(_pos select 1) + _centerY]; +}; +// Dot position +private ["_posX","_posY"]; +_posX = _pos select 0; +_posY = _pos select 1; + +// Distance between dot and ellipse center +private ["_dstX","_dstY"]; +_dstX = abs(_posX - _centerX); +_dstY = abs(_posY - _centerY); + +private ["_sum"]; +_sum = ((_dstX * _dstX)/(_sizeX * _sizeX)) + ((_dstY * _dstY)/(_sizeY * _sizeY)); + +if (_sum <= 1) then { + _return = true; +}; + +_return \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isinrectangle.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isinrectangle.sqf new file mode 100644 index 0000000..43d7d36 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_isinrectangle.sqf @@ -0,0 +1,26 @@ +// In: [pointPosition,corners] +// Out: boolean +private ["_pos","_corners","_return"]; +_pos = _this select 0; +_corners = _this select 1; +_return = false; + +private ["_dotX","_dotY","_bottomLeft","_left","_bottom","_topRight","_right","_top"]; +_dotX = _pos select 0; +_dotY = _pos select 1; + +_bottomLeft = _corners select 0; +_left = _bottomLeft select 0; +_bottom = _bottomLeft select 1; + +_topRight = _corners select 2; +_right = _topRight select 0; +_top = _topRight select 1; + +// x is between left and right +// y is between bottom and top +if (_dotX >= _left && _dotX < _right && _dotY >= _bottom && _dotY < _top) then { + _return = true; +}; + +_return \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_issameposition.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_issameposition.sqf new file mode 100644 index 0000000..32dbf4c --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_issameposition.sqf @@ -0,0 +1,16 @@ +// In: [array1,array2] +// Out: boolean + +private ["_p1","_p2","_return"]; +_p1 = _this select 0; +_p2 = _this select 1; +_return = true; + +// Only compare X and Y coordinates, ignore Z. +for "_i" from 0 to 1 do { + if ((_p1 select _i) != (_p2 select _i)) exitwith { + _return = false; + }; +}; + +_return \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_rotateposition.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_rotateposition.sqf new file mode 100644 index 0000000..c0afaae --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_fnc_rotateposition.sqf @@ -0,0 +1,13 @@ +// In: [position,direction] +// Out: position +private ["_pos","_dir","_orgX","_orgY","_newX","_newY"]; +_pos = _this select 0; +_dir = _this select 1; + +_orgX = _pos select 0; +_orgY = _pos select 1; + +_newX = (_orgX * (cos _dir)) - (_orgY * (sin _dir)); +_newY = (_orgX * (sin _dir)) + (_orgY * (cos _dir)); + +[_newX,_newY] \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_getpos.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_getpos.sqf new file mode 100644 index 0000000..88f214d --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_getpos.sqf @@ -0,0 +1,170 @@ +/* Select a random position based on anchor position, direction and distance. + In: [position,distance,direction,water,road,emptySpace] + Out: position +*/ +private ["_org","_dst","_dir","_pos","_water","_road","_empty"]; +_org = _this select 0; +_dst = _this select 1; +_dir = if (count _this > 2) then {_this select 2} else {random 360}; +_water = if (count _this > 3) then {_this select 3} else {0}; +_road = if (count _this > 4) then {_this select 4} else {[0,200]}; +_empty = if (count _this > 5) then {_this select 5} else {[]}; + +// Object instead of position array given +if (typename _org == "OBJECT") then {_org = getpos _org}; + +// Distance given as an array of min and max. Pick a random between them. +if (typename _dst == "ARRAY") then { + private ["_min","_max"]; + _min = _dst select 0; + _max = _dst select 1; + _dst = (_min + random(_max - _min)); +}; + +// Direction given as an array of min and max. Pick a random dir between them. +if (typename _dir == "ARRAY") then { + private ["_min","_max","_ang"]; + _min = _dir select 0; + _max = _dir select 1; + + _ang = _max - _min; + + // Min bigger than max, can happen with directions around north + if (_ang < 0) then { _ang = _ang + 360 }; + + _dir = (_min + random _ang); +}; + +_pos = [_org,_dst,_dir] call A3XAI_SHK_pos_fnc_getPos; + +// Water position +if (typeName _water == "SCALAR") then { + switch _water do { + case 0: { // Water not allowed + if (surfaceIsWater _pos) then { + private ["_p","_d","_l"]; + _d = 0; _l = true; + + // Search for a land position starting from the randomly picked position and + // then going outwards from it in full circles in 20m steps. + while {_d = _d + 20; _l && _d < 5000} do { + for "_i" from 0 to 340 step 20 do { + _p = [_pos,_d,_i] call A3XAI_SHK_pos_fnc_getpos; + if (!surfaceIsWater _p) exitwith {_l = false}; + }; + }; + _pos = _p; + }; + }; + case 1: { // Water allowed + + }; + case 2: { // Only water allowed + if !(surfaceIsWater _pos) then { + private ["_p","_d","_l"]; + _d = 0; _l = true; + + // Search for a water position starting from the randomly picked position and + // then going outwards from it in full circles in 20m steps. + while {_d = _d + 20; _l && _d < 5000} do { + for "_i" from 0 to 340 step 20 do { + _p = [_pos,_d,_i] call A3XAI_SHK_pos_fnc_getpos; + if (surfaceIsWater _p) exitwith {_l = false}; + }; + }; + _pos = _p; + }; + }; + }; +} else { // For backward compatibility + // Water position is not allowed + if !_water then { + if (surfaceIsWater _pos) then { + private ["_p","_d","_l"]; + _d = 0; _l = true; + + // Search for a land position starting from the randomly picked position and + // then going outwards from it in full circles in 20m steps. + while {_d = _d + 20; _l && _d < 5000} do { + for "_i" from 0 to 340 step 20 do { + _p = [_pos,_d,_i] call A3XAI_SHK_pos_fnc_getpos; + if (!surfaceIsWater _p) exitwith {_l = false}; + }; + }; + _pos = _p; + }; + }; +}; + +// Road position. +if (count _road > 0) then { + if ((_road select 0) > 0) then { + private ["_mode","_range","_roads","_cnt","_p","_p2"]; + _mode = _road select 0; + _range = _road select 1; + _roads = _pos nearroads _range; + _cnt = count _roads; + _p = []; + + // Road position(s) found. + if (_cnt > 0) then { + _p = getpos (_roads select 0); + + // Found more than one road position, return closest. + if (_cnt > 1) then { + for "_i" from 1 to (_cnt - 1) do { + _p2 = getpos (_roads select _i); + if ((_p2 distance _pos) < (_p distance _pos)) then { + _p = _p2; + }; + }; + }; + }; + + switch _mode do { + // Road position preferred but not forced. + case 1: { + if (count _p > 0) then { + _pos = _p; + }; + }; + // Only accept road position, return empty array if none found. + case 2: { + if (count _p > 0) then { + _pos = _p; + } else { + _pos resize 0; + }; + }; + }; + }; +}; + +// Find empty position +private ["_dst","_veh","_p"]; + +_dst = 200; +_veh = ""; +switch (typename _empty) do { + case "OBJECT": { _veh = typeof _empty }; // Only vehicle given, use default distance + case "SCALAR": {_dst = _empty;}; + case "ARRAY": { + if (count _empty > 0) then { + _dst = _empty select 0; + _veh = _empty select 1; + if (typename _veh == typename objNull) then { _veh = typeof _veh }; + }; + }; +}; + +_p = []; +if (count _pos > 0) then {_p = _pos findEmptyPosition [0,_dst,_veh];}; + + +// If an empty position is found, use it. Otherwise, return the original position. +if (count _p > 0) then { + _pos = _p; +}; + +// Return position +_pos \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_getposmarker.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_getposmarker.sqf new file mode 100644 index 0000000..1b90a63 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_getposmarker.sqf @@ -0,0 +1,101 @@ +/* Select a random position from an area defined by a marker. + In: [marker,water,blacklist,emptySpace] + Out: position +*/ +private ["_area","_water","_blist","_pos","_empty"]; +_area = _this select 0; +_water = if (count _this > 1) then {_this select 1} else {0}; +_blist = if (count _this > 2) then {_this select 2} else {[]}; +_empty = if (count _this > 3) then {_this select 3} else {[]}; +_pos = []; + +if (typename _blist == "STRING") then {_blist = [_blist]}; + +private ["_shape"]; +_shape = _area call A3XAI_SHK_pos_fnc_getMarkerShape; + +// Limited loop so the script won't get stuck +private ["_i","_exit"]; +_exit = false; +for [{_i = 0}, {_i < 1000 && !_exit}, {_i = _i + 1}] do { + + // Rectangle or Ellipse marker given? + if (_shape in ["SQUARE","RECTANGLE"]) then { + _pos = _area call A3XAI_SHK_pos_fnc_getPosFromRectangle; + } else { + _pos = _area call A3XAI_SHK_pos_fnc_getPosFromEllipse; + }; + + // Find empty position + private ["_dst","_veh","_p"]; + + _dst = 200; + _veh = ""; + switch (typename _empty) do { + case (typename objNull): { _veh = typeof _empty }; // Only vehicle given, use default distance + case ("STRING"): { _veh = _empty }; + case (typename []): { + if (count _empty > 0) then { + _dst = _empty select 0; + _veh = _empty select 1; + if (typename _veh == typename objNull) then { _veh = typeof _veh }; + }; + }; + }; + + _p = _pos findEmptyPosition [0,_dst,_veh]; + + // If an empty position is found, use it. Otherwise, return the original position. + if (count _p > 0) then { + _pos = _p; + }; + + // Water position + if (typeName _water == "SCALAR") then { + switch _water do { + + case 0: { // Water position is not allowed + // Position is on land, try to exit script. + if !(surfaceIsWater _pos) then { + _exit = true; + }; + }; + + case 1: { // Doesn't matter if position is on water or land. + _exit = true; + }; + + case 2: { // Only water position is allowed + // Position is on water, try to exit script. + if (surfaceIsWater _pos) then { + _exit = true; + }; + }; + }; + } else { // For backward compatibility + // Water position is not allowed + if !_water then { + // Position is on land, try to exit script. + if !(surfaceIsWater _pos) then { + _exit = true; + }; + // Doesn't matter if position is on water or land. + } else { + _exit = true; + }; + }; + + // Position is not allowed in blacklisted areas + if (count _blist > 0 && _exit) then { + // Check each blacklist marker + { + // If blacklisted, jump out of blacklist check and continue main loop. + if ([_pos,_x] call A3XAI_SHK_pos_fnc_isBlacklisted) exitwith { + _exit = false; + }; + } foreach _blist; + }; +}; + +// Return position +_pos \ No newline at end of file diff --git a/A3XAI/SHK_pos/A3XAI_SHK_pos_init.sqf b/A3XAI/SHK_pos/A3XAI_SHK_pos_init.sqf new file mode 100644 index 0000000..1539506 --- /dev/null +++ b/A3XAI/SHK_pos/A3XAI_SHK_pos_init.sqf @@ -0,0 +1,105 @@ +/* + SHK_pos + + Version 0.24 + Author: Shuko (shuko@quakenet, miika@miikajarvinen.fi) + Contributors: Cool=Azroul13, Hatifnat + + Forum: http://forums.bistudio.com/showthread.php?162695-SHK_pos + + Marker Based Selection + Required Parameters: + 0 String Area marker's name. + + Optional Parameters: + 1 Number Water position. Default is only land positions allowed. + 0 Find closest land. Search outwards 360 degrees (20 degree steps) and 20m steps. + 1 Allow water positions. + 2 Find only water positions. + 2 Array or String One or multiple blacklist area markers which are excluded from the main marker area. + 3 Array, Number, Object or Vehicle Type Force finding large enough empty position. + 0 Max range from the selection position to look for empty space. Default is 200. + 1 Vehicle or vehicle type to fit into an empty space. + + Examples: + [...,[300,heli]] Array with distance and vehicle object. + [...,350] Only distance given + [...,(typeof heli)] Only vehicle type given + [...,heli] Only vehicle object given + + Position Based Selection + Required Parameters: + 0 Object or Position Anchor point from where the relative position is calculated from. + 1 Array or Number Distance from anchor. + + Optional Parameters: + 2 Array of Number Direction from anchor. Default is random between 0 and 360. + 3 Number Water position. Default is only land positions allowed. + 0 Find closest land. Search outwards 360 degrees (20 degree steps) and 20m steps. + 1 Allow water positions. + 2 Find only water positions. + 4 Array Road positions. + 0 Number Road position forcing. Default is 0. + 0 Do not search for road positions. + 1 Find closest road position. Return the generated random position if none found. + 2 Find closest road position. Return empty array if none found. + 1 Number Road search range. Default is 200m. + 5 Array, Number, Object or Vehicle Type Force finding large enough empty position. + 0 Max range from the selection position to look for empty space. Default is 200. + 1 Vehicle or vehicle type to fit into an empty space. + + Examples: + [...,[300,heli]] Array with distance and vehicle object. + [...,350] Only distance given + [...,(typeof heli)] Only vehicle type given + [...,heli] Only vehicle object given + + Usage: + Preprocess the file in init.sqf: + call compile preprocessfile "SHK_pos\A3XAI_SHK_pos_init.sqf"; + + Actually getting the position: + pos = [parameters] call A3XAI_SHK_pos; +*/ +// Functions +A3XAI_SHK_pos_getPos = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_getpos.sqf",A3XAI_directory]; +A3XAI_SHK_pos_getPosMarker = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_getposmarker.sqf",A3XAI_directory]; + +// Sub functions +A3XAI_SHK_pos_fnc_findClosestPosition = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_findclosestposition.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getMarkerCorners = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getmarkercorners.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getMarkerShape = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getmarkershape.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getPos = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getpos.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getPosFromCircle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getposfromcircle.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getPosFromEllipse = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getposfromellipse.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getPosFromRectangle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getposfromrectangle.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_getPosFromSquare = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_getposfromsquare.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_isBlacklisted = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_isblacklisted.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_isInCircle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_isincircle.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_isInEllipse = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_isinellipse.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_isInRectangle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_isinrectangle.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_isSamePosition = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_issameposition.sqf",A3XAI_directory]; +A3XAI_SHK_pos_fnc_rotatePosition = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3XAI_SHK_pos_fnc_rotateposition.sqf",A3XAI_directory]; + +// Wrapper function +// Decide which function to call based on parameters. +A3XAI_SHK_pos = { + private ["_pos"]; + _pos = []; + + // Only marker is given as parameter + if (typename _this isEqualTo "STRING") then { + _pos = [_this] call A3XAI_SHK_pos_getPosMarker; + + // Parameter array + } else { + if (typename (_this select 0) isEqualTo "STRING") then { + _pos = _this call A3XAI_SHK_pos_getPosMarker; + } else { + _pos = _this call A3XAI_SHK_pos_getPos; + }; + }; + + // Return position + _pos +}; \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_BIN_taskPatrol.sqf b/A3XAI/compile/A3XAI_BIN_taskPatrol.sqf new file mode 100644 index 0000000..48ff72e --- /dev/null +++ b/A3XAI/compile/A3XAI_BIN_taskPatrol.sqf @@ -0,0 +1,262 @@ +#include "\A3XAI\globaldefines.hpp" + +/* +======================================================================================================================= +Script: BIN_taskPatrol.sqf v1.3 +Author(s): Binesi +Partly based on original code by BIS + +Description: +Creates a continually randomized patrol path which circles and intersects a given position. + +Parameter(s): +_this select 0: the group to which to assign the waypoints (Group) +_this select 1: the position on which to base the patrol (Array) +_this select 2: the maximum distance between waypoints (Number) +_this select 3: (optional) debug markers on or off (Number) +_this select 4: (optional) blacklist of areas (Array) + +Returns: +Boolean - success flag + +Example(s): +null = [group this,(getPos this),250] execVM "BIN_taskPatrol.sqf" +null = [group this,(getPos this),250,1] execVM "BIN_taskPatrol.sqf" // Same with debug markers + +----------------------------------------------------------------------------------------------------------------------- +Notes: Wolffy.au +If anyone is interested, I've made some additions to Binesi's BIN_taskPatrol script. +Random initial patrol direction - I noticed every patrol started off in the same direction, so I've randomised it. +Fixed the 2D position / findSafePos errors +Added building positions as possible patrol locations using Random Building Position Script v1.0 by Tophe of Östgöta Ops +Added check that BIS Functions has been initialized + +ArmaIIholic +-- added JTD direction normalization function +-- changed numbers for waypoints to match previous waypoints +-- randomized initial direction - Wolffy.au added only the offset +-- fixed error with building position format +-- randomized initial direction -- Wolffy.au added only the offset which had to be reduced to 180 + - however this script is making full circle from wherever it starts + +Edited version for A3XAI (https://github.com/dayzai/A3XAI) +======================================================================================================================= +*/ + + +_unitGroup = _this select 0; +_pos = _this select 1; +_max_dist = [_this,2,100] call A3XAI_param; +_speedMode = [_this,3,"LIMITED"] call A3XAI_param; + +_unitType = _unitGroup getVariable ["unitType",""]; +_allowWater = (_unitType in ["aircustom","air_reinforce"]); +_searchLoot = _unitType in ["static","vehiclecrew","dynamic","random"]; +_isVehicle = (_unitType isEqualTo "landcustom"); +_combatMode = (combatMode _unitGroup); +_behavior = (behaviour (leader _unitGroup)); + +_trigger = _unitGroup getVariable ["trigger",objNull]; + +if !(isNull _trigger) then { + _locationArray = _trigger getVariable ["locationArray",[]]; + if (_locationArray isEqualTo []) then { + _trigger = objNull; + }; +}; + +// if (_max_dist < 75) then {_unitGroup setSpeedMode "LIMITED"}; + +_randomizeChance = linearConversion [125,350,_max_dist,0.25,0.45,true]; +//diag_log format ["DEBUG: PatrolDist %1 has RandomizeChance %2",_max_dist,_randomizeChance]; + +_wpStatements = call { + if (_searchLoot && {_max_dist > 100}) exitWith {format ["if !(local this) exitWith {}; if (%1 call A3XAI_chance) then {_nul = [group this] call A3XAI_setRandomWaypoint;} else {_nul = [group this] spawn A3XAI_areaSearching;};",_randomizeChance]}; + if (_unitType isEqualTo "aircustom") exitWith {format ["if !(local this) exitWith {}; if (%1 call A3XAI_chance) then {_nul = [group this] call A3XAI_setRandomWaypoint;} else {_nul = [(assignedVehicle this),(group this)] spawn A3XAI_customHeliDetect;};",_randomizeChance]}; + format ["if !(local this) exitWith {}; if (%1 call A3XAI_chance) then {_nul = [group this] call A3XAI_setRandomWaypoint};",_randomizeChance] +}; + +_wpTimeouts = if (_max_dist >= 100) then {[0, 3, 5]} else {[3, 6, 9]}; + +_center_x = (_pos) select 0; +_center_y = (_pos) select 1; +_center_z = (_pos) select 2; +if(isNil "_center_z")then{_center_z = 0;}; + +_wp_count = 4 + (floor random 3) + (floor (_max_dist / 100 )); +_angle = (360 / (_wp_count -1)); + +_newangle = 0; +_wp_array = []; +_slack = _max_dist / 5.5; +_completionRadius = if (_isVehicle) then {(50 + _slack)} else {(25 + _slack)}; +if ( _slack < 20 ) then { _slack = 20 }; + +_angle_offset = random 180; +while {count _wp_array < _wp_count} do { + private ["_x1","_y1","_wp_pos", "_prepos","_bldgpos","_bldgs","_a","_b"]; + + _newangle = (count _wp_array * _angle) + _angle_offset; + + if ((_newangle > 360) || {_newangle < 0}) then + { + _newangle = abs (abs (_newangle) - 360); + }; + + if ((random 1) < 0.5) then + { + _newangle = -_newangle; + + if ((_newangle > 360) || {_newangle < 0}) then + { + _newangle = abs (abs (_newangle) - 360); + }; + }; + + _x1 = _center_x - (sin _newangle * _max_dist); + _y1 = _center_y - (cos _newangle * _max_dist); + + _prepos = [_x1, _y1, _center_z]; + if ( isNil "_center_z" ) then { + _prepos = [_x1, _y1]; + }; + + _wp_pos = [_prepos, 0, _slack, 6, 0, 50 * (pi / 180), 0, [],[_prepos]] call BIS_fnc_findSafePos; + + _retry = false; + if ((surfaceIsWater _wp_pos) && {!_allowWater}) then { + _retry = true; + _retryCount = 0; + _retryPos = []; + _newMaxDist = _max_dist; + while {_retry && {_retryCount < 3}} do { + _newMaxDist = _newMaxDist * 0.5; + _x1 = _center_x - (sin _newangle * _newMaxDist); + _y1 = _center_y - (cos _newangle * _newMaxDist); + + _prepos = [_x1, _y1, _center_z]; + if ( isNil "_center_z" ) then { + _prepos = [_x1, _y1]; + }; + + _retryPos = [_prepos, 0, _slack, 6, 0, 50 * (pi / 180), 0, [],[_prepos]] call BIS_fnc_findSafePos; + _retryCount = _retryCount + 1; + if (!(surfaceIsWater _wp_pos) or {_allowWater}) then { + _retry = false; + _wp_pos = _retryPos; + }; + }; + }; + + if !(_retry) then { + _a = 0 + (_wp_pos select 0); + _b = 0 + (_wp_pos select 1); + + call { + if (_searchLoot) exitWith { + ////////////////////////////////////////////////////////////////// + // The following code is an extract from Random Building Position Script v1.0 by Tophe of Östgöta Ops + ////////////////////////////////////////////////////////////////// + _bldgpos = []; + _bldgs = nearestObjects [[_a,_b,0], ["HouseBase"], 50]; + { + private["_i","_y"]; + _i = 0; + _y = _x buildingPos _i; + //while {format["%1", _y] != "[0,0,0]"} do { + while {!(_y isEqualTo [0,0,0]) } do { + //_bldgpos = _bldgpos + [_y]; + _bldgpos pushBack _y; + _i = _i + 1; + _y = _x buildingPos _i; + }; + } forEach _bldgs; + + if !(_bldgpos isEqualTo []) then { + _wp_pos = _bldgpos call A3XAI_selectRandom; + }; + }; + + if (_isVehicle) exitWith { + _nearRoads = _wp_pos nearRoads ((_max_dist/2) min 100); + _roadsCount = count _nearRoads; + _returnPos = []; + if (_roadsCount > 0) then { + _returnPos = getPosATL (_nearRoads select 0); + if (_roadsCount > 1) then { + for "_i" from 1 to (_roadsCount -1) do { + _comparePos = getPosATL (_nearRoads select _i); + if ((_comparePos distance _wp_pos) < (_returnPos distance _wp_pos)) then { + _returnPos = _comparePos; + }; + }; + }; + _wp_pos = _returnPos; + }; + }; + }; + + _wp_array pushBack _wp_pos; + } else { + if !(isNull _trigger) then { + _wp_pos = getPosATL ((_trigger getVariable ["locationArray",[_pos]]) call A3XAI_selectRandom); + if ((_wp_pos distance2D [0,0]) > 0) then { + _wp_array pushBack _wp_pos; + } else { + _wp_array pushBack _pos; + }; + } else { + _wp_array pushBack _pos; + }; + }; + + uiSleep 0.25; +}; + +for "_i" from 1 to (_wp_count - 1) do +{ + private ["_wp","_cur_pos"]; + + _cur_pos = (_wp_array select _i); + + _wp = _unitGroup addWaypoint [_cur_pos, 0]; + _wp setWaypointType "MOVE"; + _wp setWaypointCompletionRadius _completionRadius; + _wp setWaypointTimeout [_wpTimeouts select 0, _wpTimeouts select 1, _wpTimeouts select 2]; + _wp setWaypointStatements ["true",_wpStatements]; + _wp setWaypointCombatMode _combatMode; + _wp setWaypointBehaviour _behavior; + _wp setWaypointSpeed _speedMode; + uiSleep 0.25; +}; + +_endWP = [_pos, 0, 50, 6, 0, 50 * (pi / 180), 0, [],[_pos]] call BIS_fnc_findSafePos; + +// End back near start point and then pick a new random point +_wp1 = _unitGroup addWaypoint [_endWP, 0]; +_wp1 setWaypointType "MOVE"; +_wp1 setWaypointCompletionRadius (_max_dist max 50); +_wp1 setWaypointCombatMode _combatMode; +_wp1 setWaypointBehaviour _behavior; +_wp1 setWaypointSpeed _speedMode; +[_unitGroup,(count waypoints _unitGroup)] setWaypointStatements ["true", "if !(local this) exitWith {}; group this setCurrentWaypoint [(group this), (round (random 2) + 1)];"]; + +// Cycle in case we reach the end +_wp2 = _unitGroup addWaypoint [_endWP, 0]; +_wp2 setWaypointType "CYCLE"; +_wp2 setWaypointCompletionRadius (_max_dist max 50); +_wp2 setWaypointCombatMode _combatMode; +_wp2 setWaypointBehaviour _behavior; +_wp2 setWaypointSpeed _speedMode; + +for "_i" from ((count (waypoints _unitGroup)) - 1) to 0 step -1 do { + if (((getWPPos [_unitGroup,_i]) distance2D [0,0]) isEqualTo 0) then { + diag_log format ["A3XAI Error: Waypoint %1 is invalid position.",[_unitGroup,_i]]; + // deleteWaypoint [_unitGroup,_i]; + [_unitGroup,_i] setWaypointPosition _pos; + }; +}; + +if (A3XAI_enableHC && {_unitType in A3XAI_HCAllowedTypes}) then {_unitGroup setVariable ["HC_Ready",true];}; + +true diff --git a/A3XAI/compile/A3XAI_UAVDetection.sqf b/A3XAI/compile/A3XAI_UAVDetection.sqf new file mode 100644 index 0000000..21b4f43 --- /dev/null +++ b/A3XAI/compile/A3XAI_UAVDetection.sqf @@ -0,0 +1,67 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_canCall","_vehicle","_detectStartPos"]; +_unitGroup = _this select 0; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +if (_unitGroup getVariable ["EnemiesIgnored",false]) then {[_unitGroup,"Default"] call A3XAI_forceBehavior}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_canCall = true; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 %2 detection started.",_unitGroup,(typeOf (_vehicle))];}; + +if ((diag_tickTime - (_unitGroup getVariable ["UVLastCall",-A3XAI_UAVCallReinforceCooldown])) > A3XAI_UAVCallReinforceCooldown) then { + _detectStartPos = getPosATL _vehicle; + _vehicle flyInHeight (FLYINHEIGHT_UAV_SEARCHING_BASE + (random FLYINHEIGHT_UAV_SEARCHING_VARIANCE)); + _unitGroup setVariable ["IsDetecting",true]; + + while {!(_vehicle getVariable ["VehicleDisabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}} do { + private ["_detected","_vehPos","_nearBlacklistAreas","_playerPos","_canReveal"]; + _vehPos = getPosATL _vehicle; + _canReveal = ((combatMode _unitGroup) in ["YELLOW","RED"]); + _detected = (getPosATL _vehicle) nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_UAV]; + + { + if !(isPlayer _x) then { + _detected deleteAt _forEachIndex; + }; + if (_forEachIndex > 4) exitWith {}; + } forEach _detected; + + _nearBlacklistAreas = if (_detected isEqualTo []) then {[]} else {nearestLocations [_vehPos,[BLACKLIST_OBJECT_GENERAL],1500]}; + { + _playerPos = getPosATL _x; + if ((isPlayer _x) && {({if (_playerPos in _x) exitWith {1}} count _nearBlacklistAreas) isEqualTo 0}) then { + if (((lineIntersectsSurfaces [(aimPos _vehicle),(eyePos _x),_vehicle,_x,true,1]) isEqualTo []) && {A3XAI_UAVDetectChance call A3XAI_chance}) then { + if (_canCall) then { + if (isDedicated) then { + _nul = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]] spawn A3XAI_spawn_reinforcement; + } else { + A3XAI_spawnReinforcements_PVS = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]]; + publicVariableServer "A3XAI_spawnReinforcements_PVS"; + }; + _unitGroup setVariable ["UVLastCall",diag_tickTime]; + _canCall = false; + }; + if !(isNull (objectParent _x)) then { //Reveal vehicles + _unitGroup reveal [_x,2.5]; + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[41+(floor (random 5)),[_unitGroup,[configFile >> "CfgVehicles" >> (typeOf _vehicle),"displayName",""] call BIS_fnc_returnConfigEntry]]] call A3XAI_radioSend; + }; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; + if (((_vehicle distance2D _detectStartPos) > DETECT_LENGTH_UAV_2D) or {_vehicle getVariable ["VehicleDisabled",false]}) exitWith {}; + uiSleep 15; + }; + + _vehicle flyInHeight (FLYINHEIGHT_UAV_PATROLLING_BASE + (random FLYINHEIGHT_UAV_PATROLLING_VARIANCE)); +}; + +_unitGroup setVariable ["IsDetecting",false]; +[_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 %2 detection end.",_unitGroup,(typeOf (_vehicle))];}; diff --git a/A3XAI/compile/A3XAI_UAVStartPatrol.sqf b/A3XAI/compile/A3XAI_UAVStartPatrol.sqf new file mode 100644 index 0000000..235ea3c --- /dev/null +++ b/A3XAI/compile/A3XAI_UAVStartPatrol.sqf @@ -0,0 +1,28 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_tooClose","_detectionWaypoint","_exitWaypoint","_vehicle","_dirPosToVehicle","_locationSelected"]; +_unitGroup = _this select 0; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; + +_tooClose = true; +_locationSelected = [0,0,0]; + +while {_tooClose} do { + _locationSelected = (A3XAI_locationsAir call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _locationSelected) > NEXT_WP_DIST_UAV) then { + _tooClose = false; + } else { + uiSleep 0.1; + }; +}; + +_dirPosToVehicle = [_locationSelected,_vehicle] call BIS_fnc_dirTo; +_detectionWaypoint = [_locationSelected,WP_POS_INGRESS_BASE_UAV+(random WP_POS_INGRESS_VARIANCE_UAV),_dirPosToVehicle,1] call A3XAI_SHK_pos; +[_unitGroup,0] setWaypointPosition [_detectionWaypoint,0]; + +_dirPosToVehicle = [_vehicle,_locationSelected] call BIS_fnc_dirTo; +_exitWaypoint = [_detectionWaypoint,WP_POS_EGRESS_BASE_UAV+(random WP_POS_EGRESS_VARIANCE_UAV),_dirPosToVehicle,1] call A3XAI_SHK_pos; +[_unitGroup,2] setWaypointPosition [_detectionWaypoint,0]; + +true diff --git a/A3XAI/compile/A3XAI_UAV_destroyed.sqf b/A3XAI/compile/A3XAI_UAV_destroyed.sqf new file mode 100644 index 0000000..f5f0c8e --- /dev/null +++ b/A3XAI/compile/A3XAI_UAV_destroyed.sqf @@ -0,0 +1,21 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitLevel"]; + +_vehicle = (_this select 0); + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["VehicleDisabled",false]) exitWith {}; +_vehicle setVariable ["VehicleDisabled",true]; +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; +[_vehicle,true] call A3XAI_respawnAIVehicle; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitGroup setVariable ["GroupSize",-1]; +if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_UGVDetection.sqf b/A3XAI/compile/A3XAI_UGVDetection.sqf new file mode 100644 index 0000000..d4b6085 --- /dev/null +++ b/A3XAI/compile/A3XAI_UGVDetection.sqf @@ -0,0 +1,66 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_vehicle","_canCall"]; +_unitGroup = _this select 0; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +if (_unitGroup getVariable ["EnemiesIgnored",false]) then {[_unitGroup,"Default"] call A3XAI_forceBehavior}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_canCall = true; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 %2 detection start.",_unitGroup,(typeOf (_vehicle))];}; + +if ((diag_tickTime - (_unitGroup getVariable ["UVLastCall",-A3XAI_UGVCallReinforceCooldown])) > A3XAI_UGVCallReinforceCooldown) then { + _detectStartPos = getPosATL _vehicle; + _unitGroup setVariable ["IsDetecting",true]; + + while {!(_vehicle getVariable ["VehicleDisabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}} do { + private ["_detected","_detectOrigin","_startPos","_vehPos","_nearBlacklistAreas","_playerPos","_canReveal"]; + _vehPos = getPosATL _vehicle; + _startPos = getPosATL _vehicle; + _canReveal = ((combatMode _unitGroup) in ["YELLOW","RED"]); + _detectOrigin = [_startPos,0,getDir _vehicle,1] call A3XAI_SHK_pos; + _detected = _detectOrigin nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_UGV]; + + { + if !(isPlayer _x) then { + _detected deleteAt _forEachIndex; + }; + if (_forEachIndex > 4) exitWith {}; + } forEach _detected; + + _nearBlacklistAreas = if (_detected isEqualTo []) then {[]} else {nearestLocations [_vehPos,[BLACKLIST_OBJECT_GENERAL],1500]}; + { + _playerPos = getPosATL _x; + if ((isPlayer _x) && {({if (_playerPos in _x) exitWith {1}} count _nearBlacklistAreas) isEqualTo 0}) then { + if (((lineIntersectsSurfaces [(aimPos _vehicle),(eyePos _x),_vehicle,_x,true,1]) isEqualTo []) && {A3XAI_UGVDetectChance call A3XAI_chance}) then { + if (_canCall) then { + if (isDedicated) then { + _nul = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]] spawn A3XAI_spawn_reinforcement; + } else { + A3XAI_spawnReinforcements_PVS = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]]; + publicVariableServer "A3XAI_spawnReinforcements_PVS"; + }; + _unitGroup setVariable ["UVLastCall",diag_tickTime]; + _canCall = false; + }; + if (isNull (objectParent _x)) then { //Reveal infantry + _unitGroup reveal [_x,2.5]; + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[51+(floor (random 5)),[_unitGroup,[configFile >> "CfgVehicles" >> (typeOf _vehicle),"displayName",""] call BIS_fnc_returnConfigEntry]]] call A3XAI_radioSend; + }; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; + if (((_vehicle distance2D _detectStartPos) > DETECT_LENGTH_UGV_2D) or {_vehicle getVariable ["VehicleDisabled",false]}) exitWith {}; + uiSleep 15; + }; +}; + +_unitGroup setVariable ["IsDetecting",false]; +[_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 %2 detection end.",_unitGroup,(typeOf (_vehicle))];}; diff --git a/A3XAI/compile/A3XAI_UGVStartPatrol.sqf b/A3XAI/compile/A3XAI_UGVStartPatrol.sqf new file mode 100644 index 0000000..d07633c --- /dev/null +++ b/A3XAI/compile/A3XAI_UGVStartPatrol.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_tooClose","_locationSelected"]; +_unitGroup = _this select 0; + +_tooClose = true; +_locationSelected = [0,0,0]; + +while {_tooClose} do { + _locationSelected = (A3XAI_locationsLand call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _locationSelected) > 300) then { + _tooClose = false; + } else { + uiSleep 0.1; + }; +}; + +_locationSelected = [_locationSelected,random(300),random(360),0,[1,300]] call A3XAI_SHK_pos; +[_unitGroup,0] setWPPos _locationSelected; +[_unitGroup,1] setWPPos _locationSelected; +[_unitGroup,2] setWaypointPosition [_locationSelected,0]; + +true diff --git a/A3XAI/compile/A3XAI_UGV_destroyed.sqf b/A3XAI/compile/A3XAI_UGV_destroyed.sqf new file mode 100644 index 0000000..db501d7 --- /dev/null +++ b/A3XAI/compile/A3XAI_UGV_destroyed.sqf @@ -0,0 +1,21 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitsAlive"]; + +_vehicle = (_this select 0); + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["VehicleDisabled",false]) exitWith {}; +_vehicle setVariable ["VehicleDisabled",true]; +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; +[_vehicle,true] call A3XAI_respawnAIVehicle; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitGroup setVariable ["GroupSize",-1]; +if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; diff --git a/A3XAI/compile/A3XAI_addGroupManagerSingle.sqf b/A3XAI/compile/A3XAI_addGroupManagerSingle.sqf new file mode 100644 index 0000000..e8af22e --- /dev/null +++ b/A3XAI/compile/A3XAI_addGroupManagerSingle.sqf @@ -0,0 +1,172 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_unitType", "_vehicle", "_stuckCheckTime", "_groupLeadMarker", "_groupWPMarker", "_currentTime", "_managerStartTime", "_updateServerLoot", +"_pullRate", "_unitPos", "_unitMarker", "_result", "_groupVariables", "_assignedVehicle"]; + +_unitGroup = _this select 0; +_unitLevel = _this select 1; + +scopeName "GroupManagerScope"; + +if (_unitGroup getVariable ["isManaged",false]) exitWith {}; +_unitGroup setVariable ["isManaged",true]; + +_unitType = (_unitGroup getVariable ["unitType",""]); +_vehicle = objNull; + +if (_unitType in ["air","land","aircustom","landcustom","air_reinforce","uav","ugv"]) then { + _vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +} else { + call { + _assignedVehicle = (assignedVehicle (leader _unitGroup)); + if (isNull _vehicle) exitWith {}; + if (_vehicle isKindOf "ParachuteBase") exitWith {}; + if (_vehicle isKindOf "StaticWeapon") exitWith {}; + _unitGroup setVariable ["assignedVehicle",_assignedVehicle]; + _vehicle = _assignedVehicle; + }; +}; + + +if (isNil {_unitGroup getVariable "antistuckPos"}) then {_unitGroup setVariable ["antistuckPos",(getWPPos [_unitGroup,(currentWaypoint _unitGroup)])];}; +if (isNil {_unitGroup getVariable "GroupSize"}) then {_unitGroup setVariable ["GroupSize",(count (units _unitGroup))]}; +_stuckCheckTime = _unitType call A3XAI_getAntistuckTime; + +//set up debug variables +_groupLeadMarker = format ["%1_Lead",_unitGroup]; +_groupWPMarker = format ["%1_WP",_unitGroup]; + +//Get group variables +_groupVariables = _unitGroup getVariable "GroupVariables"; +if (isNil "_groupVariables") then { + _unitGroup setVariable ["GroupVariables",[]]; + _groupVariables = _unitGroup getVariable "GroupVariables"; + _groupVariables = [_unitGroup,_unitType] call A3XAI_setUnitType; + _unitGroup setVariable ["GroupVariables",_groupVariables]; + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Group %1 variables not found. Setting them now.",_unitGroup]; + }; +} else { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Group %1 variables check passed.",_unitGroup]; + }; +}; + +//Set up timer variables +_currentTime = diag_tickTime; +_managerStartTime = _currentTime; +if (isNil {_unitGroup getVariable "lastRearmTime"}) then {_unitGroup setVariable ["lastRearmTime",_currentTime];}; +if (isNil {_unitGroup getVariable "antistuckTime"}) then {_unitGroup setVariable ["antistuckTime",_currentTime];}; +if (isNil {_unitGroup getVariable "lootGenTime"}) then {_unitGroup setVariable ["lootGenTime",_currentTime];}; + +//Setup loot variables +_updateServerLoot = (A3XAI_enableHC && {!isDedicated}); +_pullRate = 30; + +if (isDedicated) then { + [_unitGroup,_unitType,_unitLevel] call A3XAI_setLoadoutVariables; +} else { + waitUntil {uiSleep 0.25; (local _unitGroup)}; + [_unitGroup,_unitType,_unitLevel] call A3XAI_setLoadoutVariables_HC; +}; + +if (A3XAI_groupManageMode isEqualTo 1) exitWith { + [_unitGroup, _vehicle, _groupVariables, _unitLevel] call A3XAI_addGroupManangerGlobal; +}; + +//Main loop +while {(!isNull _unitGroup) && {(_unitGroup getVariable ["GroupSize",-1]) > 0}} do { + //Every-loop check + [_unitGroup,_vehicle] call (_groupVariables select 0); + + //Check units + [_unitGroup] call (_groupVariables select 1); + + //Generate loot + if ((diag_tickTime - (_unitGroup getVariable ["lootGenTime",diag_tickTime])) > _pullRate) then { + [_unitGroup,_unitLevel] call (_groupVariables select 2); + }; + + //Vehicle ammo/fuel check + if ((alive _vehicle) && {(diag_tickTime - (_unitGroup getVariable ["lastRearmTime",0])) > 180}) then { //If _vehicle is objNull (if no vehicle was assigned to the group) then nothing in this bracket should be executed + [_unitGroup,_vehicle] call (_groupVariables select 3); + }; + + //Antistuck + if ((diag_tickTime - (_unitGroup getVariable ["antistuckTime",diag_tickTime])) > (_groupVariables select 5)) then { + [_unitGroup,_vehicle,(_groupVariables select 5)] call (_groupVariables select 4); + }; + + if (A3XAI_HCIsConnected && {_unitGroup getVariable ["HC_Ready",false]} && {(diag_tickTime - _managerStartTime) > 30}) then { + private ["_result"]; + _result = _unitGroup call A3XAI_transferGroupToHC; + if (_result) then { + waitUntil {sleep 1.5; (!(local _unitGroup) or {isNull _unitGroup})}; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Transferred ownership of %1 group %2 to HC %3.",(_unitGroup getVariable ["unitType",_unitType]),_unitGroup,A3XAI_HCObjectOwnerID];}; + //breakOut "GroupManagerScope"; //To-do add "Local" EH to group units first! + waitUntil {sleep 15; ((local _unitGroup) or {isNull _unitGroup})}; + if ((_unitGroup getVariable ["GroupSize",-1]) > 0) then { + _currentTime = diag_tickTime; + // _unitGroup call A3XAI_initNoAggroStatus; + _unitGroup setVariable ["lastRearmTime",_currentTime]; + _unitGroup setVariable ["antistuckTime",_currentTime]; + _unitGroup setVariable ["lootGenTime",_currentTime]; + }; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 group %2 ownership was returned to server.",(_unitGroup getVariable ["unitType",_unitType]),_unitGroup];}; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Waiting to transfer %1 group %2 ownership to headless client (ID: %3).",(_unitGroup getVariable ["unitType",_unitType]),_unitGroup,A3XAI_HCObjectOwnerID];}; + }; + }; + + if (isDedicated) then { + if !((groupOwner _unitGroup) in [2,A3XAI_HCObjectOwnerID]) then { + _unitGroup setGroupOwner 2; + diag_log format ["[A3XAI] Returned improperly transferred group %1 to server.",_unitGroup]; + }; + }; + + if ((_unitGroup getVariable ["GroupSize",0]) > 0) then {uiSleep 15}; +}; + +if (A3XAI_enableDebugMarkers) then { + deleteMarker _groupLeadMarker; + deleteMarker _groupWPMarker; +}; + +if !(isNull _unitGroup) then { + _unitGroup setVariable ["isManaged",false]; //allow group manager to run again on group respawn. + + if !(isDedicated) exitWith { + A3XAI_transferGroup_PVS = _unitGroup; + publicVariableServer "A3XAI_transferGroup_PVS"; //Return ownership to server. + A3XAI_HCGroupsCount = A3XAI_HCGroupsCount - 1; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Returned ownership of AI %1 group %2 to server.",_unitType,_unitGroup];}; + }; + + while {(_unitGroup getVariable ["GroupSize",-1]) isEqualTo 0} do { //Wait until group is either respawned or marked for deletion. A dummy unit should be created to preserve group. + uiSleep 5; + }; + + if ((_unitGroup getVariable ["GroupSize",-1]) < 0) then { //GroupSize value of -1 marks group for deletion + if (!isNull _unitGroup) then { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Deleting %2 group %1.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"])]}; + _result = _unitGroup call A3XAI_deleteGroup; + }; + }; +} else { + diag_log "A3XAI Error: An A3XAI-managed group was deleted unexpectedly!"; +}; + +if (local _vehicle) then { + call { + if (_vehicle getVariable ["DeleteVehicle",false]) exitWith { + _vehicle setPosATL [0,0,100]; + deleteVehicle _vehicle; + }; + if (isEngineOn _vehicle) exitWith { + _vehicle engineOn false; + }; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_addGroupManangerGlobal.sqf b/A3XAI/compile/A3XAI_addGroupManangerGlobal.sqf new file mode 100644 index 0000000..5f8e1a6 --- /dev/null +++ b/A3XAI/compile/A3XAI_addGroupManangerGlobal.sqf @@ -0,0 +1,99 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_groupVariables", "_unitLevel"]; + +//Expected input: +// _unitGroup = _this select 0; +// _vehicle = _this select 1; +// _groupVariables = _this select 2; +// _unitLevel = _this select 3; + +if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: %1 groups in global group manager.",(count A3XAI_managedGroups)]; +}; + +A3XAI_managedGroups pushBack _this; + +if (A3XAI_managedGroups isEqualTo [_this]) then { + diag_log "Starting new global group manager."; + _nul = [] spawn { + while {!(A3XAI_managedGroups isEqualTo [])} do { + // diag_log "DEBUG: Global group manager is checking groups."; + { + _unitGroup = _x select 0; + _vehicle = _x select 1; + _groupVariables = _x select 2; + _unitLevel = _x select 3; + + if ((!isNull _unitGroup) && {(_unitGroup getVariable ["GroupSize",-1]) > 0}) then { + call { + if (A3XAI_HCIsConnected && {local _unitGroup}) exitWith { + _result = _unitGroup call A3XAI_transferGroupToHC; + if (_result) then { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Transferred ownership of %1 group %2 to HC %3.",(_unitGroup getVariable ["unitType",_unitType]),_unitGroup,A3XAI_HCObjectOwnerID];}; + }; + }; + + [_unitGroup,_vehicle] call (_groupVariables select 0); + [_unitGroup] call (_groupVariables select 1); + if ((diag_tickTime - (_unitGroup getVariable ["lootGenTime",diag_tickTime])) > LOOT_PULL_RATE) then { + [_unitGroup,_unitLevel] call (_groupVariables select 2); + }; + if ((alive _vehicle) && {(diag_tickTime - (_unitGroup getVariable ["lastRearmTime",0])) > CHECK_VEHICLE_AMMO_FUEL_TIME}) then { //If _vehicle is objNull (if no vehicle was assigned to the group) then nothing in this bracket should be executed + [_unitGroup,_vehicle] call (_groupVariables select 3); + }; + if ((diag_tickTime - (_unitGroup getVariable ["antistuckTime",diag_tickTime])) > (_groupVariables select 5)) then { + [_unitGroup,_vehicle,(_groupVariables select 5)] call (_groupVariables select 4); + }; + }; + } else { + // diag_log format ["DEBUG: Global group manager is removing group %1.",_unitGroup]; + + if (A3XAI_enableDebugMarkers) then { + deleteMarker format ["%1_Lead",_unitGroup]; + deleteMarker format ["%1_WP",_unitGroup]; + }; + + _nul = [_unitGroup,_vehicle] spawn { + _unitGroup = _this select 0; + _vehicle = _this select 1; + + while {(_unitGroup getVariable ["GroupSize",-1]) isEqualTo 0} do { //Wait until group is either respawned or marked for deletion. A dummy unit should be created to preserve group. + uiSleep 5; + }; + + if ((_unitGroup getVariable ["GroupSize",-1]) < 0) then { //GroupSize value of -1 marks group for deletion + if (!isNull _unitGroup) then { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Deleting %2 group %1.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"])]}; + _result = _unitGroup call A3XAI_deleteGroup; + }; + }; + + if (local _vehicle) then { + call { + if (_vehicle getVariable ["DeleteVehicle",false]) exitWith { + _vehicle setPosATL [0,0,100]; + deleteVehicle _vehicle; + }; + if (isEngineOn _vehicle) exitWith { + _vehicle engineOn false; + }; + }; + }; + }; + + A3XAI_managedGroups deleteAt _forEachIndex; + }; + uiSleep 0.1; + } forEach A3XAI_managedGroups; + + uiSleep 10; + }; + + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: %1 groups in global group manager. Exiting.",(count A3XAI_managedGroups)]; + }; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_addItem.sqf b/A3XAI/compile/A3XAI_addItem.sqf new file mode 100644 index 0000000..8523328 --- /dev/null +++ b/A3XAI/compile/A3XAI_addItem.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +_unit = (_this select 0); +_item = (_this select 1); + +_slot = floor (random 3); +if ((_slot isEqualTo 0) && {_unit canAddItemToUniform _item}) exitWith {_unit addItemToUniform _item; true}; +if ((_slot isEqualTo 1) && {_unit canAddItemToVest _item}) exitWith {_unit addItemToVest _item; true}; +if ((_slot isEqualTo 2) && {_unit canAddItemToBackpack _item}) exitWith {_unit addItemToBackpack _item; true}; + +false \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addLandVehEH.sqf b/A3XAI/compile/A3XAI_addLandVehEH.sqf new file mode 100644 index 0000000..c93c820 --- /dev/null +++ b/A3XAI/compile/A3XAI_addLandVehEH.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","[(_this select 0),true] call A3XAI_vehDestroyed"]; +_this addEventHandler ["HandleDamage","_this call A3XAI_handleDamageVeh"]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addMapMarker.sqf b/A3XAI/compile/A3XAI_addMapMarker.sqf new file mode 100644 index 0000000..ed8a265 --- /dev/null +++ b/A3XAI/compile/A3XAI_addMapMarker.sqf @@ -0,0 +1 @@ +#include "\A3XAI\globaldefines.hpp" private ["_trigger", "_objectString", "_mapMarkerArray"]; _trigger = _this select 0; _objectString = _this select 1; if !(isNull _trigger) then { if !(_objectString in allMapMarkers) then { _objectString = createMarker [_objectString, _trigger]; _objectString setMarkerType "mil_warning"; _objectString setMarkerBrush "Solid"; _mapMarkerArray = missionNamespace getVariable ["A3XAI_mapMarkerArray",[]]; _trigger setVariable ["MarkerName",_objectString]; _mapMarkerArray pushBack _objectString; }; _objectString setMarkerText "STATIC TRIGGER (ACTIVE)"; _objectString setMarkerColor "ColorRed"; }; // diag_log format ["%1 %2",__FILE__,_this]; true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addParaGroup.sqf b/A3XAI/compile/A3XAI_addParaGroup.sqf new file mode 100644 index 0000000..1613b9a --- /dev/null +++ b/A3XAI/compile/A3XAI_addParaGroup.sqf @@ -0,0 +1,41 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_targetPlayer", "_vehicle", "_startPos", "_unitLevel", "_unitGroup", "_paraGroup", "_cargoAvailable", "_unit", "_vehiclePos", "_parachute", "_unitsAlive", "_trigger", "_rearm", "_cargoAvailable"]; + +_vehicle = _this select 0; +_unitGroup = _this select 1; +_cargoAvailable = _this select 2; +_targetPlayer = _this select 3; + +_target = if (isPlayer _targetPlayer) then {_targetPlayer} else {_vehicle}; +_startPos = getPosATL _target; +_startPos set [2,0]; + +_unitLevel = _unitGroup getVariable ["unitLevel",1]; +_paraGroup = ["vehiclecrew"] call A3XAI_createGroup; + +for "_i" from 1 to _cargoAvailable do { + _unit = [_paraGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + _vehiclePos = (getPosATL _vehicle); + _parachute = createVehicle [PARACHUTE_OBJECT, [_vehiclePos select 0, _vehiclePos select 1, (_vehiclePos select 2)], [], (-10 + (random 10)), "FLY"]; + _unit moveInDriver _parachute; + _unit call A3XAI_addTempNVG; +}; + +_unitsAlive = {alive _x} count (units _paraGroup); +_trigger = [_startPos,(format ["Heli AI Reinforcement %1",mapGridPosition _vehicle])] call A3XAI_createTriggerArea; +0 = [5,_trigger,[_unitGroup],PATROL_DIST_PARAGROUP,_unitLevel,[_unitsAlive,0]] call A3XAI_initializeTrigger; + +_paraGroup setVariable ["GroupSize",_unitsAlive]; +_paraGroup setVariable ["trigger",_trigger]; + +[_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; +A3XAI_staticSpawnObjects pushBackUnique _trigger; + +[_paraGroup,_startPos] call A3XAI_setFirstWPPos; +0 = [_paraGroup,_startPos,PATROL_DIST_PARAGROUP] spawn A3XAI_BIN_taskPatrol; +_rearm = [_paraGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Paradrop group %1 with %2 units deployed at %3 by %4 group %5.",_paraGroup,_cargoAvailable,_startPos,typeOf _vehicle,_unitGroup];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addRespawnQueue.sqf b/A3XAI/compile/A3XAI_addRespawnQueue.sqf new file mode 100644 index 0000000..e51d6a3 --- /dev/null +++ b/A3XAI/compile/A3XAI_addRespawnQueue.sqf @@ -0,0 +1,104 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_respawnSleep", "_nextRespawnTime", "_mode", "_trigger", "_unitGroup", "_fastMode", "_respawnLimit", "_spawnParams", "_vehicleType"]; + +if (isDedicated) then { + _respawnSleep = 0; + _nextRespawnTime = 0; + _mode = _this select 0; + + call { + if (_mode isEqualTo 0) exitWith { + //Infantry AI respawn + _trigger = _this select 1; //spawn area to respawn + _unitGroup = _this select 2; //infantry group to respawn + // _fastMode = if ((count _this) > 3) then {_this select 3} else {false}; //shorter wait time if retrying a spawn + _fastMode = [_this,3,false] call A3XAI_param; + + if (isNull _trigger) then {_trigger = _unitGroup getVariable ["trigger",objNull];}; + _respawnSleep = _trigger getVariable ["respawnTime",(A3XAI_respawnTimeMin + (random A3XAI_respawnTimeVariance))]; //Calculate wait time for respawn. Respawn time may be individually defined for custom spawns. + if (_fastMode) then {_respawnSleep = ADD_RESPAWN_FAST_TIME;}; + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3XAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_trigger,_unitGroup]; + _respawnLimit = _trigger getVariable ["respawnLimit",-1]; + if !(_respawnLimit isEqualTo 0) then { + if (_respawnLimit > 0) then {_trigger setVariable ["respawnLimit",(_respawnLimit -1)];}; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Added group %1 to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",_unitGroup,(count A3XAI_respawnQueue),_respawnSleep];}; + }; + + if (_mode isEqualTo 1) exitWith { + //Custom vehicle AI respawn + _spawnParams = _this select 1; //parameters used to call A3XAI_createVehicleSpawn + _respawnSleep = if ((count _spawnParams) > 5) then {_spawnParams select 5} else {600}; //calculate respawn time + + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3XAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_spawnParams]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Added custom AI vehicle %1 patrol to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",(_spawnParams select 1),(count A3XAI_respawnQueue),_respawnSleep];}; + }; + + if (_mode isEqualTo 2) exitWith { + //Vehicle patrol AI respawn + _vehicleType = _this select 1; + _fastMode = [_this,2,false] call A3XAI_param; + + call { + if (_fastMode) exitWith { + _respawnSleep = ADD_RESPAWN_FAST_TIME; + }; + if (_vehicleType isKindOf "Air") exitWith { + _respawnSleep = (A3XAI_respawnAirMinTime + random A3XAI_respawnTimeVarAir); + }; + if (_vehicleType isKindOf "LandVehicle") exitWith { + _respawnSleep = (A3XAI_respawnLandMinTime + random A3XAI_respawnTimeVarLand); + }; + }; + + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3XAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_vehicleType]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Added AI vehicle patrol type %1 to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",_vehicleType,(count A3XAI_respawnQueue),_respawnSleep];}; + }; + + if (_mode isEqualTo 3) exitWith { + //UAV/UGV respawn + _vehicleType = _this select 1; + // _fastMode = if ((count _this) > 2) then {_this select 2} else {false}; //shorter wait time if retrying a spawn + _fastMode = [_this,2,false] call A3XAI_param; + if (_fastMode) then {_respawnSleep = ADD_RESPAWN_FAST_TIME}; + + if (_vehicleType isKindOf "Air") then { + _respawnSleep = (A3XAI_respawnUAVMinTime + random A3XAI_respawnTimeVarUAV); + } else { + _respawnSleep = (A3XAI_respawnUGVMinTime + random A3XAI_respawnTimeVarUGV); + }; + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3XAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_vehicleType]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Added unmanned vehicle patrol type %1 to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",_vehicleType,(count A3XAI_respawnQueue),_respawnSleep];}; + }; + + if (_mode isEqualTo 4) exitWith { + //Custom UAV/UGV respawn + }; + }; + + if (!isNil "A3XAI_respawnActive") exitWith {}; //If the first respawn has already occured, no need to modify the initial wait time. + + if (!isNil "A3XAI_nextRespawnTime") then { + if (_nextRespawnTime < A3XAI_nextRespawnTime) then { //If the newest respawn is scheduled to happen sooner than the next closest respawn, reduce the initial wait time appropriately. + A3XAI_nextRespawnTime = _nextRespawnTime; //Time of next spawn + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Decreased time to next respawn to %1 seconds.",_respawnSleep];}; + }; + } else { + A3XAI_nextRespawnTime = _nextRespawnTime; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Time to first respawn set to %1 seconds.",_respawnSleep];}; + }; + + if (!isNil "A3XAI_queueActive") exitWith {}; + A3XAI_queueActive = true; //The respawn queue is established, so don't create another one until it's finished. + A3XAI_addRespawnQueueHandle = [] spawn A3XAI_processRespawn; +} else { + A3XAI_respawnGroup_PVS = _this; + publicVariableServer "A3XAI_respawnGroup_PVS"; +}; + +true diff --git a/A3XAI/compile/A3XAI_addTempNVG.sqf b/A3XAI/compile/A3XAI_addTempNVG.sqf new file mode 100644 index 0000000..5a34cc6 --- /dev/null +++ b/A3XAI/compile/A3XAI_addTempNVG.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +if (_this hasWeapon NVG_ITEM_PLAYER) exitWith {false}; +_this addWeapon NVG_ITEM_AI; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Generated temporary NVGs for AI %1.",_this];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addTemporaryWaypoint.sqf b/A3XAI/compile/A3XAI_addTemporaryWaypoint.sqf new file mode 100644 index 0000000..9daae61 --- /dev/null +++ b/A3XAI/compile/A3XAI_addTemporaryWaypoint.sqf @@ -0,0 +1,21 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_waypoint","_pos","_wpStatements"]; + +_unitGroup = _this select 0; +_pos = _this select 1; +_wpStatements = [_this,2,"if !(local this) exitWith {}; (group this) call A3XAI_moveToPosAndDeleteWP;"] call A3XAI_param; + +if !(_pos isEqualTo [0,0,0]) then { + _waypoint = _unitGroup addWaypoint [_pos,0]; + _waypoint setWaypointType "MOVE"; + _waypoint setWaypointSpeed "FULL"; + _waypoint setWaypointCompletionRadius 30; + _waypoint setWaypointStatements ["true",_wpStatements]; + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 assigned temporary waypoint at %2 with statements %3.",_unitGroup,_pos,_wpStatements];}; + + _waypoint +} else { + diag_log format ["A3XAI Error: Group %1 was assigned temporary waypoint at %2.",_unitGroup,_pos]; +}; diff --git a/A3XAI/compile/A3XAI_addToExternalObjectMonitor.sqf b/A3XAI/compile/A3XAI_addToExternalObjectMonitor.sqf new file mode 100644 index 0000000..6016670 --- /dev/null +++ b/A3XAI/compile/A3XAI_addToExternalObjectMonitor.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_index"]; + +_index = -1; +if !(isNull _this) then { + _this setVariable ["ExileIsSimulationMonitored", true]; + _index = A3XAI_externalObjectMonitor pushBack _this; +}; + +_index \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addUAVEH.sqf b/A3XAI/compile/A3XAI_addUAVEH.sqf new file mode 100644 index 0000000..99c0448 --- /dev/null +++ b/A3XAI/compile/A3XAI_addUAVEH.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3XAI_UAV_destroyed"]; +_this addEventHandler ["Hit","_this call A3XAI_defensiveAggression"]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addUGVEH.sqf b/A3XAI/compile/A3XAI_addUGVEH.sqf new file mode 100644 index 0000000..3c5369d --- /dev/null +++ b/A3XAI/compile/A3XAI_addUGVEH.sqf @@ -0,0 +1,9 @@ +#include "\A3XAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3XAI_UGV_destroyed"]; +_this addEventHandler ["HandleDamage","_this call A3XAI_handleDamageUGV"]; +_this addEventHandler ["Hit","_this call A3XAI_defensiveAggression"]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addUVUnitEH.sqf b/A3XAI/compile/A3XAI_addUVUnitEH.sqf new file mode 100644 index 0000000..dd41a6c --- /dev/null +++ b/A3XAI/compile/A3XAI_addUVUnitEH.sqf @@ -0,0 +1,7 @@ +#include "\A3XAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3XAI_handle_death_UV;"]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addUnitEH.sqf b/A3XAI/compile/A3XAI_addUnitEH.sqf new file mode 100644 index 0000000..685bf46 --- /dev/null +++ b/A3XAI/compile/A3XAI_addUnitEH.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3XAI_handleDeathEvent;"]; +_this addEventHandler ["HandleDamage","_this call A3XAI_handleDamageUnit;"]; + +_this setVariable ["bodyName",(name _this)]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addVehAirEH.sqf b/A3XAI/compile/A3XAI_addVehAirEH.sqf new file mode 100644 index 0000000..7e44add --- /dev/null +++ b/A3XAI/compile/A3XAI_addVehAirEH.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +if (isNil {_this getVariable "durability"}) then {_this setVariable ["durability",[0,0,0,0]];}; +_this addEventHandler ["Killed","_this call A3XAI_heliDestroyed"]; +_this addEventHandler ["GetOut","_this call A3XAI_heliLanded"]; +_this addEventHandler ["HandleDamage","_this call A3XAI_handleDamageHeli"]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_addVehicleGroup.sqf b/A3XAI/compile/A3XAI_addVehicleGroup.sqf new file mode 100644 index 0000000..3727bcf --- /dev/null +++ b/A3XAI/compile/A3XAI_addVehicleGroup.sqf @@ -0,0 +1,104 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_unitsAlive", "_unitLevel", "_trigger", "_rearm" ,"_pos", "_posReflected", "_leader","_airEvacType"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_airEvacType = [_this,2,0] call A3XAI_param; + +_leader = leader _unitGroup; +_pos = getPosATL _leader; +_pos set [2,0]; +_unitsAlive = {alive _x} count (units _unitGroup); + +try { + if (_unitsAlive isEqualTo 0) then { + throw format ["A3XAI Debug: %1 cannot create trigger area for empty group %2.",__FILE__,_unitGroup]; + }; + + for "_i" from ((count (waypoints _unitGroup)) - 1) to 0 step -1 do { + deleteWaypoint [_unitGroup,_i]; + }; + + if ([_pos,NO_AGGRO_RANGE_LAND] call A3XAI_checkInNoAggroArea) then { + _pos = [_pos,NO_AGGRO_RANGE_LAND] call A3XAI_getSafePosReflected; + [_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + if !(_pos isEqualTo []) then { + _tempWP = [_unitGroup,_pos,format ["if !(local this) exitWith {}; [(group this),%1] call A3XAI_moveToPosAndPatrol;",PATROL_DIST_VEHICLEGROUP]] call A3XAI_addTemporaryWaypoint; + }; + } else { + _unitGroup setCombatMode "YELLOW"; + _unitGroup setBehaviour "AWARE"; + [_unitGroup,_pos] call A3XAI_setFirstWPPos; + 0 = [_unitGroup,_pos,PATROL_DIST_VEHICLEGROUP] spawn A3XAI_BIN_taskPatrol; + }; + + if (_pos isEqualTo []) then { + _unitGroup setVariable ["GroupSize",-1]; + if !(local _unitGroup) then { + A3XAI_updateGroupSizeManual_PVC = [_unitGroup,-1]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeManual_PVC"; + }; + + deleteVehicle _vehicle; + + throw format ["A3XAI Debug: Vehicle group %1 inside no-aggro area at %2. Deleting group.",_unitGroup,_pos]; + }; + + //new + _unitLevel = _unitGroup getVariable ["unitLevel",1]; //A3EAI to-do - grab unitLevel value here + + if (_airEvacType > 0) then { + _cargoAvailable = (_vehicle emptyPositions "cargo") min A3XAI_paraDropAmount; //To do: Replace A3XAI_paraDropAmount with Cargo amount + if (_airEvacType isEqualTo 1) then { + for "_i" from 1 to _cargoAvailable do { + _unit = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + _unit moveInCargo _vehicle; + _unit action ["getOut",_vehicle]; + _unit call A3XAI_addTempNVG; + }; + } else { + for "_i" from 1 to _cargoAvailable do { + _unit = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + _vehiclePos = (getPosATL _vehicle); + _parachute = createVehicle [PARACHUTE_OBJECT, [_vehiclePos select 0, _vehiclePos select 1, (_vehiclePos select 2)], [], (-10 + (random 10)), "FLY"]; + _unit moveInDriver _parachute; + _unit call A3XAI_addTempNVG; + + }; + }; + _unitsAlive = {alive _x} count (units _unitGroup); + if !(local _unitGroup) then { + A3XAI_updateGroupSizeAuto_PVC = _unitGroup; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeAuto_PVC"; + }; + }; + + _trigger = [_pos,(format ["AI Vehicle Group %1",mapGridPosition _leader])] call A3XAI_createTriggerArea; + 0 = [4,_trigger,[_unitGroup],PATROL_DIST_VEHICLEGROUP,_unitLevel,[_unitsAlive,0]] call A3XAI_initializeTrigger; + + _unitGroup setVariable ["GroupSize",_unitsAlive]; + _unitGroup setVariable ["trigger",_trigger]; + + [_unitGroup,"vehiclecrew"] call A3XAI_setUnitType; + [_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + A3XAI_staticSpawnObjects pushBackUnique _trigger; + + { + if (alive _x) then { + if ((_x getHit "legs") > 0) then {_x setHit ["legs",0]}; + unassignVehicle _x; + }; + } count (units _unitGroup); + + if !(local _unitGroup) then { + A3XAI_sendGroupTriggerVars_PVC = [_unitGroup,[_unitGroup],PATROL_DIST_VEHICLEGROUP,1,1,[_unitsAlive,0],0,"vehiclecrew",false,true]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_sendGroupTriggerVars_PVC"; + }; +} catch { + if (A3XAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_addVehicleGunners.sqf b/A3XAI/compile/A3XAI_addVehicleGunners.sqf new file mode 100644 index 0000000..b48e565 --- /dev/null +++ b/A3XAI/compile/A3XAI_addVehicleGunners.sqf @@ -0,0 +1,30 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_vehicle", "_maxGunners", "_vehicleTurrets", "_maxGunnersAssigned", "_gunnersAdded", "_turretWeapons", "_turretMagazines", "_gunner"]; + +_unitGroup = _this select 0; +_unitLevel = _this select 1; +_vehicle = _this select 2; +_maxGunners = _this select 3; + +_vehicleTurrets = allTurrets [_vehicle,false]; +_maxGunnersAssigned = (_maxGunners min (count _vehicleTurrets)); +_gunnersAdded = 0; + +{ + if (_gunnersAdded isEqualTo _maxGunnersAssigned) exitWith {}; + _turretWeapons = _vehicle weaponsTurret _x; + if !(_turretWeapons isEqualTo []) then { + _turretMagazines = _vehicle magazinesTurret _x; + if !(_turretMagazines isEqualTo []) then { + _gunner = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + _gunner call A3XAI_addTempNVG; + _gunner assignAsTurret [_vehicle,_x]; + _gunner moveInTurret [_vehicle,_x]; + _gunnersAdded = _gunnersAdded + 1; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Added gunner unit %1 to %2 %3 with weapon %4 (%5 of %6).",_gunner,_unitGroup,(typeOf _vehicle),(_turretWeapons select 0),_gunnersAdded,_maxGunnersAssigned];}; + }; + }; +} count _vehicleTurrets; + +_gunnersAdded diff --git a/A3XAI/compile/A3XAI_antistuck_air.sqf b/A3XAI/compile/A3XAI_antistuck_air.sqf new file mode 100644 index 0000000..2fbabe2 --- /dev/null +++ b/A3XAI/compile/A3XAI_antistuck_air.sqf @@ -0,0 +1,37 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_stuckCheckTime", "_checkPos", "_tooClose", "_wpSelect", "_leader"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_stuckCheckTime = _this select 2; + +if (isNull _vehicle) exitWith {}; + +_checkPos = (getPosATL _vehicle); +_leader = (leader _unitGroup); +if ((((_leader distance (_leader findNearestEnemy _vehicle)) > NEAREST_ENEMY_RANGE) or {[_checkPos,NO_AGGRO_RANGE_AIR] call A3XAI_checkInActiveNoAggroArea}) && {_checkPos distance2D (_unitGroup getVariable ["antistuckPos",[0,0,0]]) < ANTISTUCK_MIN_TRAVEL_DIST_AIR} && {canMove _vehicle}) then { + _tooClose = true; + _wpSelect = []; + while {_tooClose} do { + _wpSelect = (A3XAI_locationsAir call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _wpSelect) < ANTISTUCK_AIR_MIN_WP_DIST) then { + _tooClose = false; + } else { + uiSleep 0.1; + }; + }; + _wpSelect = [_wpSelect,ANTISTUCK_AIR_WP_DIST_BASE+(random ANTISTUCK_AIR_WP_DIST_VARIANCE),(random 360),1] call A3XAI_SHK_pos; + [_unitGroup,0] setWPPos _wpSelect; + [_unitGroup,1] setWPPos _wpSelect; + if ((count (waypoints _unitGroup)) > 2) then {[_unitGroup,2] setWPPos _wpSelect;}; + [_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + _unitGroup setVariable ["antistuckPos",_wpSelect]; + _unitGroup setVariable ["antistuckTime",diag_tickTime + (_stuckCheckTime/2)]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Antistuck triggered for AI air vehicle %1 (Group: %2). Forcing next waypoint.",(typeOf _vehicle),_unitGroup];}; +} else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_antistuck_aircustom.sqf b/A3XAI/compile/A3XAI_antistuck_aircustom.sqf new file mode 100644 index 0000000..a8ea8de --- /dev/null +++ b/A3XAI/compile/A3XAI_antistuck_aircustom.sqf @@ -0,0 +1,27 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_stuckCheckTime", "_checkPos", "_currentWP", "_allWP", "_nextWP","_leader"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_stuckCheckTime = _this select 2; + +if (isNull _vehicle) exitWith {}; + +_checkPos = (getWPPos [_unitGroup,(currentWaypoint _unitGroup)]); +_leader = (leader _unitGroup); +if ((((_leader distance (_leader findNearestEnemy _vehicle)) > NEAREST_ENEMY_RANGE) or {[_checkPos,NO_AGGRO_RANGE_AIR] call A3XAI_checkInActiveNoAggroArea}) && {_checkPos isEqualTo (_unitGroup getVariable ["antistuckPos",[0,0,0]])} && {canMove _vehicle}) then { + _currentWP = (currentWaypoint _unitGroup); + _allWP = (waypoints _unitGroup); + _nextWP = _currentWP + 1; + if ((count _allWP) isEqualTo _nextWP) then {_nextWP = 1}; //Cycle back to first added waypoint if group is currently on last waypoint. + _unitGroup setCurrentWaypoint [_unitGroup,_nextWP]; + _unitGroup setVariable ["antistuckPos",(getWPPos [_unitGroup,(currentWaypoint _unitGroup)])]; + _unitGroup setVariable ["antistuckTime",diag_tickTime + (_stuckCheckTime/2)]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Antistuck triggered for AI air (custom) group %1. Forcing next waypoint.",_unitGroup];}; +} else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_antistuck_generic.sqf b/A3XAI/compile/A3XAI_antistuck_generic.sqf new file mode 100644 index 0000000..ef82843 --- /dev/null +++ b/A3XAI/compile/A3XAI_antistuck_generic.sqf @@ -0,0 +1,27 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_stuckCheckTime", "_checkPos", "_allWP", "_currentWP", "_nextWP","_leader"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_stuckCheckTime = _this select 2; + +_allWP = (waypoints _unitGroup); +_leader = (leader _unitGroup); +if ((count _allWP) > 1) then { + _checkPos = (getPosATL (leader _unitGroup)); + if ((((_leader distance (_leader findNearestEnemy _leader)) > NEAREST_ENEMY_INFANTRY) or {[_checkPos,NO_AGGRO_RANGE_MAN] call A3XAI_checkInActiveNoAggroArea}) && ((_unitGroup getVariable ["antistuckPos",[0,0,0]]) distance _checkPos) < ANTISTUCK_MIN_TRAVEL_DIST_INFANTRY) then { + _currentWP = (currentWaypoint _unitGroup); + _nextWP = _currentWP + 1; + if ((count _allWP) isEqualTo _nextWP) then {_nextWP = 0}; //Cycle back to first waypoint if group is currently on last waypoint. + [_unitGroup] call A3XAI_fixStuckGroup; + _unitGroup setCurrentWaypoint [_unitGroup,_nextWP]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Antistuck triggered for AI %1 group %2. Forcing next waypoint.",(_unitGroup getVariable ["unitType","unknown"]),_unitGroup];}; + _unitGroup setVariable ["antistuckTime",diag_tickTime + (_stuckCheckTime/2)]; + } else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; + }; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_antistuck_land.sqf b/A3XAI/compile/A3XAI_antistuck_land.sqf new file mode 100644 index 0000000..e2d6f1e --- /dev/null +++ b/A3XAI/compile/A3XAI_antistuck_land.sqf @@ -0,0 +1,46 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_stuckCheckTime", "_checkPos", "_allWP", "_currentWP", "_nextWP","_leader"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_stuckCheckTime = _this select 2; + +if (isNull _vehicle) exitWith {}; + +_checkPos = (getPosATL _vehicle); +_leader = (leader _unitGroup); +if ((((_leader distance (_leader findNearestEnemy _vehicle)) > NEAREST_ENEMY_LAND) or {[_checkPos,NO_AGGRO_RANGE_LAND] call A3XAI_checkInActiveNoAggroArea}) && {((_unitGroup getVariable ["antistuckPos",[0,0,0]]) distance _checkPos) < ANTISTUCK_MIN_TRAVEL_DIST_LAND}) then { + if (canMove _vehicle) then { + [_unitGroup] call A3XAI_fixStuckGroup; + if ((count (waypoints _unitGroup)) isEqualTo 1) then { + _tooClose = true; + _wpSelect = []; + while {_tooClose} do { + _wpSelect = (A3XAI_locationsLand call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _wpSelect) < ANTISTUCK_LAND_MIN_WP_DIST) then { + _tooClose = false; + } else { + uiSleep 0.1; + }; + }; + _wpSelect = [_wpSelect,ANTISTUCK_LAND_WP_DIST_BASE+(random ANTISTUCK_LAND_WP_DIST_VARIANCE),(random 360),0] call A3XAI_SHK_pos; + [_unitGroup,0] setWaypointPosition [_wpSelect,0]; + _unitGroup setCurrentWaypoint [_unitGroup,0]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Antistuck triggered for AI land vehicle %1 (Group: %2). Forcing next waypoint.",(typeOf _vehicle),_unitGroup];}; + }; + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime + (_stuckCheckTime/2)]; + } else { + if (!(_vehicle getVariable ["VehicleDisabled",false])) then { + [_vehicle,false] call A3XAI_vehDestroyed; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: AI vehicle %1 (Group: %2) is immobilized. Respawning vehicle patrol group. Damage: %3. WaterPos: %4.",(typeOf _vehicle),_unitGroup,(damage _vehicle),(surfaceIsWater _checkPos)];}; + if (A3XAI_enableDebugMarkers) then {_checkPos call A3XAI_debugMarkerLocation;}; + }; + }; +} else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_antistuck_uav.sqf b/A3XAI/compile/A3XAI_antistuck_uav.sqf new file mode 100644 index 0000000..13bb778 Binary files /dev/null and b/A3XAI/compile/A3XAI_antistuck_uav.sqf differ diff --git a/A3XAI/compile/A3XAI_antistuck_ugv.sqf b/A3XAI/compile/A3XAI_antistuck_ugv.sqf new file mode 100644 index 0000000..0ff2b51 --- /dev/null +++ b/A3XAI/compile/A3XAI_antistuck_ugv.sqf @@ -0,0 +1,45 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_stuckCheckTime", "_checkPos", "_tooClose", "_wpSelect"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_stuckCheckTime = _this select 2; + +if (isNull _vehicle) exitWith {}; + +_checkPos = (getPosATL _vehicle); +_leader = (leader _unitGroup); +if ((((_leader distance (_leader findNearestEnemy _vehicle)) > NEAREST_ENEMY_LAND) or {[_checkPos,NO_AGGRO_RANGE_UGV] call A3XAI_checkInActiveNoAggroArea}) && {((_unitGroup getVariable ["antistuckPos",[0,0,0]]) distance _checkPos) < 100}) then { + if (canMove _vehicle) then { + [_unitGroup] call A3XAI_fixStuckGroup; + _tooClose = true; + _wpSelect = []; + while {_tooClose} do { + _wpSelect = (A3XAI_locationsLand call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _wpSelect) < ANTISTUCK_LAND_MIN_WP_DIST) then { + _tooClose = false; + } else { + uiSleep 0.1; + }; + }; + _wpSelect = [_wpSelect,ANTISTUCK_LAND_WP_DIST_BASE+(random ANTISTUCK_LAND_WP_DIST_VARIANCE),(random 360),0] call A3XAI_SHK_pos; + [_unitGroup,0] setWaypointPosition [_wpSelect,0]; + [_unitGroup,1] setWaypointPosition [_wpSelect,0]; + _unitGroup setCurrentWaypoint [_unitGroup,1]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Antistuck triggered for UGV %1 (Group: %2). Forcing next waypoint.",(typeOf _vehicle),_unitGroup];}; + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime + (_stuckCheckTime/2)]; + } else { + if (!(_vehicle getVariable ["VehicleDisabled",false])) then { + [_vehicle] call A3XAI_UGV_destroyed; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: UGV %1 (Group: %2) is immobilized. Respawning UGV group. Damage: %3. WaterPos: %4.",(typeOf _vehicle),_unitGroup,(damage _vehicle),(surfaceIsWater _checkPos)];}; + if (A3XAI_enableDebugMarkers) then {_checkPos call A3XAI_debugMarkerLocation;}; + }; + }; +} else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_areaSearching.sqf b/A3XAI/compile/A3XAI_areaSearching.sqf new file mode 100644 index 0000000..ec04802 --- /dev/null +++ b/A3XAI/compile/A3XAI_areaSearching.sqf @@ -0,0 +1,31 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_searchPoints", "_trigger", "_radius", "_posBetween", "_searchType", "_objects", "_waypoint"]; + +_unitGroup = _this select 0; + +if ((count (waypoints _unitGroup)) > 9) exitWith {}; + +_searchPoints = call { + _trigger = _unitGroup getVariable "trigger"; + if (isNil "_trigger") exitWith {[]}; + _radius = (_trigger getVariable ["patrolDist",100])/2; + _posBetween = [_trigger,(leader _unitGroup),_radius] call A3XAI_getPosBetween; + _searchType = floor (random 2); + if (_searchType isEqualTo 0) exitWith { + _objects = _posBetween nearObjects [LOOT_HOLDER_CLASS,_radius]; + _objects + }; + if (_searchType isEqualTo 1) exitWith { + _objects = _posBetween nearEntities [[PLAYER_UNITS,"LandVehicle"],_radius]; + _objects + }; + [] +}; + +{ + if ((count (waypoints _unitGroup)) > 9) exitWith {}; + _waypoint = [_unitGroup,getPosATL _x] call A3XAI_addTemporaryWaypoint; +} forEach _searchPoints; + +true diff --git a/A3XAI/compile/A3XAI_cancelDynamicSpawn.sqf b/A3XAI/compile/A3XAI_cancelDynamicSpawn.sqf new file mode 100644 index 0000000..5224eee --- /dev/null +++ b/A3XAI/compile/A3XAI_cancelDynamicSpawn.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_trigger"]; +_trigger = _this; + +A3XAI_dynamicTriggerArray = A3XAI_dynamicTriggerArray - [_trigger]; +_playerUID = _trigger getVariable "targetplayerUID"; +if (!isNil "_playerUID") then {A3XAI_failedDynamicSpawns pushBack _playerUID}; +if (A3XAI_enableDebugMarkers) then { + deleteMarker (_trigger getVariable ["MarkerName",""]); +}; + +deleteVehicle _trigger; + +false diff --git a/A3XAI/compile/A3XAI_cancelRandomSpawn.sqf b/A3XAI/compile/A3XAI_cancelRandomSpawn.sqf new file mode 100644 index 0000000..222e42c --- /dev/null +++ b/A3XAI/compile/A3XAI_cancelRandomSpawn.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_trigger","_triggerLocation"]; +_trigger = _this; + +[_trigger,"A3XAI_randomTriggerArray",false] call A3XAI_updateSpawnCount; +if (A3XAI_enableDebugMarkers) then { + deleteMarker (_trigger getVariable ["MarkerName",""]); +}; + +_triggerLocation = _trigger getVariable "triggerLocation"; +deleteLocation _triggerLocation; +deleteVehicle _trigger; + +false diff --git a/A3XAI/compile/A3XAI_chance.sqf b/A3XAI/compile/A3XAI_chance.sqf new file mode 100644 index 0000000..d5d04bf --- /dev/null +++ b/A3XAI/compile/A3XAI_chance.sqf @@ -0,0 +1,6 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_result"]; +_result = ((random 1) < _this); + +_result \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_checkAmmoFuel.sqf b/A3XAI/compile/A3XAI_checkAmmoFuel.sqf new file mode 100644 index 0000000..957e5a2 --- /dev/null +++ b/A3XAI/compile/A3XAI_checkAmmoFuel.sqf @@ -0,0 +1,13 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unit", "_loadout", "_currentMagazines", "_magazine"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_result = _vehicle call A3XAI_reloadVehicleTurrets; //Rearms vehicle weapons/turrets individually +if ((A3XAI_debugLevel > 0) && {_result}) then {diag_log format ["A3XAI Debug: Reloaded ammo for group %1 %2.",_unitGroup,(typeOf _vehicle)];}; +if ((fuel _vehicle) < 0.50) then {_vehicle setFuel 1; if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Refueled group %1 %2.",_unitGroup,(typeOf _vehicle)];};}; +_unitGroup setVariable ["lastRearmTime",diag_tickTime]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_checkClassname.sqf b/A3XAI/compile/A3XAI_checkClassname.sqf new file mode 100644 index 0000000..1c3ebdc --- /dev/null +++ b/A3XAI/compile/A3XAI_checkClassname.sqf @@ -0,0 +1,66 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_classname","_checkType","_result","_config","_banString","_check","_configIndex"]; + +_classname = _this select 0; +_checkType = _this select 1; +_result = false; +_configIndex = -1; +_checkType = (toLower _checkType); +_startTime = diag_tickTime; + +call { + if (_checkType isEqualTo "weapon") exitWith { + if (_classname in (A3XAI_checkedClassnames select 0)) then { + _result = true; + } else { + if (!(_classname in (A3XAI_invalidClassnames select 0))) then { + _config = "CfgWeapons"; + _banString = "bin\config.bin/CfgWeapons/FakeWeapon"; + _configIndex = 0; + }; + }; + }; + if (_checkType isEqualTo "magazine") exitWith { + if (_classname in (A3XAI_checkedClassnames select 1)) then { + _result = true; + } else { + if (!(_classname in (A3XAI_invalidClassnames select 0))) then { + _config = "CfgMagazines"; + _banString = "bin\config.bin/CfgMagazines/FakeMagazine"; + _configIndex = 1; + }; + }; + }; + if (_checkType isEqualTo "vehicle") exitWith { + if (_classname in (A3XAI_checkedClassnames select 2)) then { + _result = true; + } else { + if (!(_classname in (A3XAI_invalidClassnames select 0))) then { + _config = "CfgVehicles"; + _banString = "bin\config.bin/CfgVehicles/Banned"; + _configIndex = 2; + }; + }; + }; + diag_log format ["A3XAI Error: Attempted to check %1 as an invalid classname type! Provided type: %2. Valid types: weapon, magazine, vehicle.",_checkType]; +}; + +if (_configIndex > -1) then { + _check = (str(inheritsFrom (configFile >> _config >> _classname))); + _classnameArray = []; + if ((_check != "") && {(_check != _banString)} && {(getNumber (configFile >> _config >> _classname >> "scope")) != 0}) then { + _classnameArray = A3XAI_checkedClassnames; + _result = true; + } else { + _classnameArray = A3XAI_invalidClassnames; + diag_log format ["A3XAI Warning: %1 is an invalid %2 classname!",_classname,_checkType]; + }; + //(_classnameArray select _configIndex) set [(count (_classnameArray select _configIndex)),_classname]; //Classname now known to be either valid or invalid, no need to check it again + (_classnameArray select _configIndex) pushBack _classname; + //;diag_log format ["DEBUG :: Classname check result: %1. ClassnameArray: %2.",_result,_classnameArray]; +}; + +//diag_log format ["DEBUG :: Classname %1 (check result: %2) completed in %3 seconds.",_classname,_result,diag_tickTime - _startTime]; + +_result \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_checkGroupUnits.sqf b/A3XAI/compile/A3XAI_checkGroupUnits.sqf new file mode 100644 index 0000000..0af0297 --- /dev/null +++ b/A3XAI/compile/A3XAI_checkGroupUnits.sqf @@ -0,0 +1,45 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unit", "_loadout", "_currentMagazines", "_magazine"]; + +_unitGroup = _this select 0; + +{ + if ((isNull (objectParent _x)) && {_x getVariable ["canCheckUnit",true]} && {local _x}) then { + _x setVariable ["canCheckUnit",false]; + _nul = _x spawn { + if (!alive _this) exitWith {}; + _unit = _this; + _loadout = _unit getVariable "loadout"; + if (!isNil "_loadout") then { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unpacked unit manager for unit %1. Loadout found: %2.",_unit,_loadout];}; + while {(alive _unit) && {isNull (objectParent _unit)} && {local _unit}} do { + _currentMagazines = (magazines _unit); + _magazine = ((_loadout select 1) select 0); + if (((_unit ammo ((_loadout select 0) select 0)) isEqualTo 0) || {!((_magazine in _currentMagazines))}) then { + _unit removeMagazines _magazine; + [_unit,_magazine] call A3XAI_addItem; + if (_unit getVariable ["extraMag",false]) then { + [_unit,_magazine] call A3XAI_addItem; + }; + }; + for "_i" from 1 to ((count (_loadout select 0)) - 1) do { + _magazine = ((_loadout select 1) select _i); + if (((_unit ammo ((_loadout select 0) select _i)) isEqualTo 0) && {!((_magazine in _currentMagazines))}) then { + _unit removeMagazines _magazine; + [_unit,_magazine] call A3XAI_addItem; + }; + }; + if (alive _unit) then {uiSleep 15}; + }; + }; + if (alive _unit) then { + _unit setVariable ["canCheckUnit",true]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Repacking unit manager for unit %1.",_unit];}; + }; + }; + }; + uiSleep 0.1; +} forEach (units _unitGroup); + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_checkInActiveNoAggroArea.sqf b/A3XAI/compile/A3XAI_checkInActiveNoAggroArea.sqf new file mode 100644 index 0000000..d460d1b --- /dev/null +++ b/A3XAI/compile/A3XAI_checkInActiveNoAggroArea.sqf @@ -0,0 +1,18 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_inNoAggroArea", "_objectPos", "_noAggroRange"]; + +_objectPos = _this select 0; +_noAggroRange = [_this,1,900] call A3XAI_param; + +if (_objectPos isEqualTo objNull) exitWith {false}; +if (((typeName _objectPos) isEqualTo "OBJECT") && {_objectPos isEqualTo objNull}) exitWith {false}; + +_inNoAggroArea = false; +{ + if ((_x distance2D _objectPos) < _noAggroRange) exitWith { + _inNoAggroArea = true; + }; +} count A3XAI_activeNoAggroAreas; + +_inNoAggroArea diff --git a/A3XAI/compile/A3XAI_checkInActiveStaticSpawnArea.sqf b/A3XAI/compile/A3XAI_checkInActiveStaticSpawnArea.sqf new file mode 100644 index 0000000..bf484f1 --- /dev/null +++ b/A3XAI/compile/A3XAI_checkInActiveStaticSpawnArea.sqf @@ -0,0 +1,18 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_inArea", "_object", "_distance"]; + +_object = _this select 0; +_distance = [_this,1,750] call A3XAI_param; + +if (((typeName _object) isEqualTo "OBJECT") && {_object isEqualTo objNull}) exitWith {false}; +// if ((typeName _object) isEqualTo "OBJECT") then {_object = getPosATL _object}; + +_inArea = false; +{ + if ((_x distance2D _object) < _distance) exitWith { + _inArea = true; + }; +} count A3XAI_activePlayerAreas; + +_inArea diff --git a/A3XAI/compile/A3XAI_checkInNoAggroArea.sqf b/A3XAI/compile/A3XAI_checkInNoAggroArea.sqf new file mode 100644 index 0000000..45789e8 --- /dev/null +++ b/A3XAI/compile/A3XAI_checkInNoAggroArea.sqf @@ -0,0 +1,18 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_inNoAggroArea", "_objectPos", "_noAggroRange"]; + +_objectPos = _this select 0; +_noAggroRange = [_this,1,900] call A3XAI_param; + +if (_objectPos isEqualTo objNull) exitWith {false}; +if ((typeName _objectPos) isEqualTo "OBJECT") then {_objectPos = getPosATL _objectPos}; + +_inNoAggroArea = false; +{ + if (((position _x) distance2D _objectPos) < _noAggroRange) exitWith { + _inNoAggroArea = true; + }; +} count A3XAI_noAggroAreas; + +_inNoAggroArea diff --git a/A3XAI/compile/A3XAI_checkIsWeapon.sqf b/A3XAI/compile/A3XAI_checkIsWeapon.sqf new file mode 100644 index 0000000..0dde9ea --- /dev/null +++ b/A3XAI/compile/A3XAI_checkIsWeapon.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_magazineTypes","_ammo","_ammoMaxRange","_ammoHit"]; + +if ((typeName _this) != "STRING") exitWith {false}; +_magazineTypes = [configFile >> "CfgWeapons" >> _this,"magazines",[]] call BIS_fnc_returnConfigEntry; +if (_magazineTypes isEqualTo []) exitWith {false}; +_cursorAim = [configFile >> "CfgWeapons" >> _this,"cursorAim","throw"] call BIS_fnc_returnConfigEntry; +if (_cursorAim isEqualTo "throw") exitWith {false}; +_ammo = [configFile >> "CfgMagazines" >> (_magazineTypes select 0),"ammo",""] call BIS_fnc_returnConfigEntry; +if (_ammo isEqualTo "") exitWith {false}; +_ammoHit = [configFile >> "CfgAmmo" >> _ammo,"hit",0] call BIS_fnc_returnConfigEntry; +if (_ammoHit isEqualTo 0) exitWith {false}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_cleanupReinforcementGroup.sqf b/A3XAI/compile/A3XAI_cleanupReinforcementGroup.sqf new file mode 100644 index 0000000..e3310ac --- /dev/null +++ b/A3XAI/compile/A3XAI_cleanupReinforcementGroup.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +_unitGroup = _this; + +if (!((typeName _unitGroup) isEqualTo "GROUP") || {isNull _unitGroup}) exitWith {diag_log format ["A3XAI Error: Invalid group %1 provided to %2.",_unitGroup,__FILE__];}; + +diag_log format ["Debug: Cleaning up reinforcement group %1.",_unitGroup]; +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_vehicle allowDamage false; +// _vehicle enableSimulationGlobal false; +// _vehicle hideObjectGlobal true; +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +// {_x enableSimulationGlobal false;} forEach (units _unitGroup); +_unitGroup setVariable ["GroupSize",-1]; +if (A3XAI_HCIsConnected) then { + A3XAI_cleanupReinforcement_PVC = [_unitGroup,_vehicle]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_cleanupReinforcement_PVC"; +}; + +true diff --git a/A3XAI/compile/A3XAI_clearVehicleCargo.sqf b/A3XAI/compile/A3XAI_clearVehicleCargo.sqf new file mode 100644 index 0000000..df9dad2 --- /dev/null +++ b/A3XAI/compile/A3XAI_clearVehicleCargo.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle"]; +_vehicle = _this; + +clearWeaponCargoGlobal _vehicle; +clearMagazineCargoGlobal _vehicle; +clearItemCargoGlobal _vehicle; +clearBackpackCargoGlobal _vehicle; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_countVehicleGunners.sqf b/A3XAI/compile/A3XAI_countVehicleGunners.sqf new file mode 100644 index 0000000..bde5bd3 --- /dev/null +++ b/A3XAI/compile/A3XAI_countVehicleGunners.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle", "_gunnerCount"]; + +_vehicle = _this; + +_gunnerCount = {!((_vehicle weaponsTurret _x) isEqualTo []) && {!((_vehicle magazinesTurret _x) isEqualTo [])}} count (allTurrets [_vehicle,false]); + +diag_log format ["Debug: %1 has %2 gunners.",(typeOf _vehicle),_gunnerCount]; + +_gunnerCount \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createBlackListArea.sqf b/A3XAI/compile/A3XAI_createBlackListArea.sqf new file mode 100644 index 0000000..2e11222 --- /dev/null +++ b/A3XAI/compile/A3XAI_createBlackListArea.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_pos","_size"]; + +_pos = _this select 0; +_size = _this select 1; + +createLocation [BLACKLIST_OBJECT_GENERAL,_pos,_size,_size] \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createBlackListAreaDynamic.sqf b/A3XAI/compile/A3XAI_createBlackListAreaDynamic.sqf new file mode 100644 index 0000000..7be4a24 --- /dev/null +++ b/A3XAI/compile/A3XAI_createBlackListAreaDynamic.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_pos","_size"]; + +_pos = _this select 0; +_size = _this select 1; + +createLocation [BLACKLIST_OBJECT_DYNAMIC,_pos,_size,_size] \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createBlackListAreaRandom.sqf b/A3XAI/compile/A3XAI_createBlackListAreaRandom.sqf new file mode 100644 index 0000000..c45e51a --- /dev/null +++ b/A3XAI/compile/A3XAI_createBlackListAreaRandom.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_pos","_size"]; + +_pos = _this select 0; +_size = _this select 1; + +createLocation [BLACKLIST_OBJECT_DYNAMIC,_pos,_size,_size] \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createBlacklistAreaQueue.sqf b/A3XAI/compile/A3XAI_createBlacklistAreaQueue.sqf new file mode 100644 index 0000000..10ac260 --- /dev/null +++ b/A3XAI/compile/A3XAI_createBlacklistAreaQueue.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; +if (A3XAI_customBlacklistQueue isEqualTo []) then { + A3XAI_customBlacklistQueue pushBack _this; + _blacklistQueue = [] spawn { + while {!(A3XAI_customBlacklistQueue isEqualTo [])} do { + _statement = (A3XAI_customBlacklistQueue select 0); + _blacklistName = _statement select 0; + _statement deleteAt 0; + if ((_statement select 1) > 1499) then {_statement set [1,1499];}; + _statement call A3XAI_createBlackListArea; + A3XAI_customBlacklistQueue deleteAt 0; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Creating blacklist area at %1 (pos: %2) with radius %3.",_blacklistName,_statement select 0,_statement select 1];}; + uiSleep 1; + }; + }; +} else { + A3XAI_customBlacklistQueue pushBack _this; +}; diff --git a/A3XAI/compile/A3XAI_createCustomInfantryQueue.sqf b/A3XAI/compile/A3XAI_createCustomInfantryQueue.sqf new file mode 100644 index 0000000..1080c69 --- /dev/null +++ b/A3XAI/compile/A3XAI_createCustomInfantryQueue.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1 (%2).",__FILE__,_this]}; +if (A3XAI_createCustomSpawnQueue isEqualTo []) then { + A3XAI_createCustomSpawnQueue pushBack _this; + _infantryQueue = [] spawn { + while {!(A3XAI_createCustomSpawnQueue isEqualTo [])} do { + (A3XAI_createCustomSpawnQueue select 0) call A3XAI_createCustomSpawn; + A3XAI_createCustomSpawnQueue deleteAt 0; + uiSleep 1; + }; + }; +} else { + A3XAI_createCustomSpawnQueue pushBack _this; +}; diff --git a/A3XAI/compile/A3XAI_createCustomInfantrySpawnQueue.sqf b/A3XAI/compile/A3XAI_createCustomInfantrySpawnQueue.sqf new file mode 100644 index 0000000..cbf0b52 --- /dev/null +++ b/A3XAI/compile/A3XAI_createCustomInfantrySpawnQueue.sqf @@ -0,0 +1,57 @@ +#include "\A3XAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; +private ["_trigger", "_grpArray", "_infantryQueue","_triggerStatements"]; + +_trigger = _this select 3; +_grpArray = _trigger getVariable ["GroupArray",[]]; + +if (_grpArray isEqualTo []) then { + if (A3XAI_customInfantrySpawnQueue isEqualTo []) then { + A3XAI_customInfantrySpawnQueue pushBack _this; + _infantryQueue = [] spawn { + //uiSleep 0.5; + while {!(A3XAI_customInfantrySpawnQueue isEqualTo [])} do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Custom Infantry AI spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + private ["_args","_trigger"]; + _args = (A3XAI_customInfantrySpawnQueue select 0); + _trigger = _args select 3; + if (triggerActivated _trigger) then { + _trigger setVariable ["isCleaning",false]; + _triggerStatements = (triggerStatements _trigger); + _triggerStatements set [1,""]; + _trigger setTriggerStatements _triggerStatements; + [_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + 0 = _args call A3XAI_spawnInfantryCustom; + if (A3XAI_enableDebugMarkers) then { + _nul = [_trigger,str(_trigger)] call A3XAI_addMapMarker; + }; + }; + A3XAI_customInfantrySpawnQueue deleteAt 0; + uiSleep 1; + }; + }; + } else { + if !(_this in A3XAI_customInfantrySpawnQueue) then { + A3XAI_customInfantrySpawnQueue pushBack _this; + }; + }; +} else { + private ["_triggerStatements"]; + _triggerStatements = (triggerStatements _trigger); + _triggerStatements set [1,""]; + _trigger setTriggerStatements _triggerStatements; + _trigger setTriggerArea [TRIGGER_SIZE_EXPANDED,TRIGGER_SIZE_EXPANDED,0,false]; + [_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + if (A3XAI_enableDebugMarkers) then { + _nul = [_trigger,str(_trigger)] call A3XAI_addMapMarker; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Maximum number of groups already spawned at custom %1. Exiting spawn script.",(_trigger getVariable ["TriggerText","Unknown Trigger"])];}; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createCustomSpawn.sqf b/A3XAI/compile/A3XAI_createCustomSpawn.sqf new file mode 100644 index 0000000..d4944f9 --- /dev/null +++ b/A3XAI/compile/A3XAI_createCustomSpawn.sqf @@ -0,0 +1,44 @@ +#include "\A3XAI\globaldefines.hpp" + +if ((typeName _this) isEqualTo "ARRAY") then { + private ["_arraySize","_spawnName","_spawnPos","_patrolDist","_trigStatements","_trigger","_respawn","_unitLevel","_totalAI","_respawnTime"]; + + _arraySize = (count _this); + _spawnName = _this select 0; + _spawnPos = _this select 1; + _patrolDist = if (_arraySize> 2) then {_this select 2} else {100}; + _totalAI = if (_arraySize > 3) then {_this select 3} else {2}; + _unitLevel = if (_arraySize > 4) then {_this select 4} else {2}; + _respawn = if (_arraySize > 5) then {_this select 5} else {false}; + _respawnTime = if (_arraySize > 6) then {_this select 6} else {0}; + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Creating custom spawn area with params %1.",_this];}; + + if !(_unitLevel in A3XAI_unitLevels) then {_unitLevel = 3;}; + + if !(surfaceIsWater _spawnPos) then { + _trigStatements = format ["0 = [%1,0,%2,thisTrigger,%3,%4] call A3XAI_createCustomInfantrySpawnQueue;",_totalAI,_patrolDist,_unitLevel,_respawnTime]; + _trigger = createTrigger [SENSOR_OBJECT,_spawnPos,false]; + _trigger setTriggerArea [TRIGGER_SIZE_NORMAL,TRIGGER_SIZE_NORMAL,0,false]; + _trigger setTriggerActivation ["ANY", "PRESENT", true]; + _trigger setTriggerTimeout [TRIGGER_TIMEOUT_STATICCUSTOM, true]; + _trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList != 0;",_trigStatements,"0 = [thisTrigger] spawn A3XAI_despawn_static;"]; + _trigger setVariable ["respawn",_respawn]; + _trigger setVariable ["TriggerText",_spawnName]; + //_trigger setVariable ["spawnmarker",_spawnName]; + _trigger setVariable ["isCustom",true]; + if (_respawnTime > 0) then {_trigger setVariable ["respawnTime",_respawnTime];}; + + 0 = [3,_trigger,[],_patrolDist,_unitLevel,[],[_totalAI,0]] call A3XAI_initializeTrigger; + //diag_log format ["DEBUG: triggerstatements variable is %1",_trigger getVariable "triggerStatements"]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created custom spawn area %1 at %2 with %3 AI units, unitLevel %4, respawn %5, respawn time %6.",_spawnName,mapGridPosition _trigger,_totalAI,_unitLevel,_respawn,_respawnTime];}; + + _trigger + } else { + diag_log format ["A3XAI Error: Unable to create custom spawn %1, position at %2 is water.",_spawnName,_spawnPos]; + objNull + }; +} else { + diag_log format ["Error: Wrong arguments sent to %1 (%2).",__FILE__,_this]; + objNull +}; diff --git a/A3XAI/compile/A3XAI_createCustomVehicleQueue.sqf b/A3XAI/compile/A3XAI_createCustomVehicleQueue.sqf new file mode 100644 index 0000000..fa61998 --- /dev/null +++ b/A3XAI/compile/A3XAI_createCustomVehicleQueue.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; +if (A3XAI_customVehicleSpawnQueue isEqualTo []) then { + A3XAI_customVehicleSpawnQueue pushBack _this; + _vehicleQueue = [] spawn { + while {!(A3XAI_customVehicleSpawnQueue isEqualTo [])} do { + _vehicleType = (A3XAI_customVehicleSpawnQueue select 0) select 2; + if (!(_vehicleType isKindOf "StaticWeapon") && {[_vehicleType,"vehicle"] call A3XAI_checkClassname}) then { + (A3XAI_customVehicleSpawnQueue select 0) call A3XAI_spawnVehicleCustom; + } else { + diag_log format ["A3XAI Error: %1 attempted to spawn unsupported vehicle type %2.",__FILE__,_vehicleType]; + }; + A3XAI_customVehicleSpawnQueue deleteAt 0; + uiSleep 2; + }; + }; +} else { + A3XAI_customVehicleSpawnQueue pushBack _this; +}; diff --git a/A3XAI/compile/A3XAI_createGroup.sqf b/A3XAI/compile/A3XAI_createGroup.sqf new file mode 100644 index 0000000..1aa966e --- /dev/null +++ b/A3XAI/compile/A3XAI_createGroup.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_unitGroup","_protect","_unitType"]; +_unitType = _this select 0; + +_unitGroup = createGroup A3XAI_side; +if ((count _this) > 1) then {_unitGroup call A3XAI_protectGroup}; +[_unitGroup,_unitType] call A3XAI_setUnitType; +A3XAI_activeGroups pushBack _unitGroup; + +_unitGroup \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createInfantryQueue.sqf b/A3XAI/compile/A3XAI_createInfantryQueue.sqf new file mode 100644 index 0000000..d517e33 --- /dev/null +++ b/A3XAI/compile/A3XAI_createInfantryQueue.sqf @@ -0,0 +1,74 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger", "_grpArray", "_numGroups", "_infantryQueue","_triggerStatements"]; +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; + +_trigger = _this select 3; +_grpArray = _trigger getVariable ["GroupArray",[]]; +_numGroups = if ((count _this) > 6) then {_this select 6} else {1}; + +if ((count _grpArray) < _numGroups) then { + if (A3XAI_staticInfantrySpawnQueue isEqualTo []) then { + A3XAI_staticInfantrySpawnQueue pushBack _this; + _infantryQueue = [] spawn { + while {!(A3XAI_staticInfantrySpawnQueue isEqualTo [])} do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Static Infantry AI spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + private ["_args","_trigger"]; + _args = (A3XAI_staticInfantrySpawnQueue select 0); + _trigger = _args select 3; + if (({(_trigger distance2D _x) < NO_AGGRO_RANGE_MAN} count A3XAI_activeNoAggroAreas) isEqualTo 0) then { + if (triggerActivated _trigger) then { + _trigger setVariable ["isCleaning",false]; + _triggerStatements = (triggerStatements _trigger); + if (_trigger getVariable ["A3XAI_static_spawn",false]) then { + _triggerStatements set [1,"if !(thisTrigger in A3XAI_activePlayerAreas) then {A3XAI_activePlayerAreas pushBack thisTrigger;};"]; + } else { + _triggerStatements set [1,""]; + }; + _trigger setTriggerStatements _triggerStatements; + [_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + 0 = _args call A3XAI_spawnUnits_static; + if (A3XAI_enableDebugMarkers) then { + //diag_log format ["Trigger object: %1, position: %2, type: %3",_trigger,getPosASL _trigger,typeOf _trigger]; + _nul = [_trigger,str(_trigger)] call A3XAI_addMapMarker; + if (A3XAI_enableDebugMarkers) then { + _marker = str(_trigger); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; + }; + }; + }; + }; + A3XAI_staticInfantrySpawnQueue deleteAt 0; + uiSleep 3; + }; + }; + } else { + if !(_this in A3XAI_staticInfantrySpawnQueue) then { + A3XAI_staticInfantrySpawnQueue pushBack _this; + }; + }; +} else { + private ["_triggerStatements"]; + _triggerStatements = (triggerStatements _trigger); + if (_trigger getVariable ["A3XAI_static_spawn",false]) then { + _triggerStatements set [1,"if !(thisTrigger in A3XAI_activePlayerAreas) then {A3XAI_activePlayerAreas pushBack thisTrigger;};"]; + } else { + _triggerStatements set [1,""]; + }; + _trigger setTriggerStatements _triggerStatements; + _trigger setTriggerArea [TRIGGER_SIZE_EXPANDED,TRIGGER_SIZE_EXPANDED,0,false]; + [_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + if (A3XAI_enableDebugMarkers) then { + //diag_log format ["Trigger object: %1, position: %2, type: %3",_trigger,getPosASL _trigger,typeOf _trigger]; + _nul = [_trigger,str(_trigger)] call A3XAI_addMapMarker; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Maximum number of groups already spawned at %1. Exiting spawn script.",(_trigger getVariable ["TriggerText","Unknown Trigger"])];}; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createNoAggroArea.sqf b/A3XAI/compile/A3XAI_createNoAggroArea.sqf new file mode 100644 index 0000000..6cc779e --- /dev/null +++ b/A3XAI/compile/A3XAI_createNoAggroArea.sqf @@ -0,0 +1,28 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_size", "_trigger"]; + +_areaPos = _this select 0; +_size = _this select 1; + +_trigger = createTrigger [SENSOR_OBJECT,_areaPos,false]; +_trigger setTriggerArea [_size,_size,0,false]; +_trigger setTriggerActivation ["ANY", "PRESENT", true]; +_trigger setTriggerTimeout [TRIGGER_TIMEOUT_NOAGGROAREA,true]; +_trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList > 0;", "0 = [thisTrigger] call A3XAI_noAggroAreaActivate;", "0 = [thisTrigger] call A3XAI_noAggroAreaDeactivate;"]; + +_trigger setVariable ["TriggerText",format ["No-Aggro Area %1",_areaPos]]; + +A3XAI_noAggroAreas pushBack _trigger; + +/* +diag_log format ["Debug: Trigger object %1",_trigger]; +diag_log format ["Debug: Trigger area %1",triggerArea _trigger]; +diag_log format ["Debug: Trigger activation %1",triggerActivation _trigger]; +diag_log format ["Debug: Trigger timeout %1",triggerTimeout _trigger]; +diag_log format ["Debug: Trigger text %1",_trigger getVariable ["TriggerText","Unknown Trigger"]]; +diag_log format ["Debug: Trigger statements %1",triggerStatements _trigger]; +diag_log format ["Debug: A3XAI_noAggroAreas: %1",A3XAI_noAggroAreas]; +*/ + +_trigger diff --git a/A3XAI/compile/A3XAI_createRandomInfantrySpawnQueue.sqf b/A3XAI/compile/A3XAI_createRandomInfantrySpawnQueue.sqf new file mode 100644 index 0000000..65b3729 --- /dev/null +++ b/A3XAI/compile/A3XAI_createRandomInfantrySpawnQueue.sqf @@ -0,0 +1,46 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger", "_infantryQueue","_triggerStatements"]; +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; + +_trigger = _this select 1; +//diag_log format ["DEBUG: Started random spawn queue with args %1",_this]; + +if ((_trigger getVariable ["GroupArray",[]]) isEqualTo []) then { + if (A3XAI_randomInfantrySpawnQueue isEqualTo []) then { + A3XAI_randomInfantrySpawnQueue pushBack _this; + _infantryQueue = [] spawn { + while {!(A3XAI_randomInfantrySpawnQueue isEqualTo [])} do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Random Infantry AI spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + private ["_args","_trigger"]; + _args = (A3XAI_randomInfantrySpawnQueue select 0); + _trigger = _args select 1; + if (triggerActivated _trigger) then { + _trigger setVariable ["isCleaning",false]; + _triggerStatements = (triggerStatements _trigger); + _triggerStatements set [1,""]; + _trigger setTriggerStatements _triggerStatements; + 0 = _args call A3XAI_spawnUnits_random; + if (A3XAI_enableDebugMarkers) then { + _marker = str(_trigger); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; + }; + }; + A3XAI_randomInfantrySpawnQueue deleteAt 0; + uiSleep 3; + }; + }; + } else { + if !(_this in A3XAI_randomInfantrySpawnQueue) then { + A3XAI_randomInfantrySpawnQueue pushBack _this; + }; + }; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_createTriggerArea.sqf b/A3XAI/compile/A3XAI_createTriggerArea.sqf new file mode 100644 index 0000000..85ee6ea --- /dev/null +++ b/A3XAI/compile/A3XAI_createTriggerArea.sqf @@ -0,0 +1,19 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_size", "_trigger", "_areaName"]; + +_areaPos = _this select 0; +_areaName = _this select 1; + +if (_areaPos isEqualTo [0,0,0]) exitWith { + diag_log format ["A3XAI Error: Invalid parameters sent to %1: %2",__FILE__,_this]; + objNull +}; + +// _trigger = TRIGGER_OBJECT createVehicleLocal _areaPos; //triggerless version +_trigger = createTrigger [TRIGGER_OBJECT,_areaPos,false]; //triggerless version +_trigger enableSimulation false; //Disable to reduce performance impact +_trigger setVariable ["TriggerText",_areaName]; +diag_log format ["Debug: Created trigger object %1 at %2",_trigger,_areaName]; + +_trigger diff --git a/A3XAI/compile/A3XAI_createUnit.sqf b/A3XAI/compile/A3XAI_createUnit.sqf new file mode 100644 index 0000000..5742553 --- /dev/null +++ b/A3XAI/compile/A3XAI_createUnit.sqf @@ -0,0 +1,24 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unit", "_unitGroup", "_spawnPos", "_unitLevel", "_type"]; +_unitGroup = _this select 0; +_unitLevel = _this select 1; +_spawnPos = _this select 2; +_antistuck = if ((count _this) > 3) then {_this select 3} else {false}; + +_unit = _unitGroup createUnit [DEFAULT_UNIT_CLASSNAME,_spawnPos,[],0,"FORM"]; +[_unit] joinSilent _unitGroup; +0 = _unit call A3XAI_addUnitEH; +0 = [_unit, _unitLevel] call A3XAI_generateLoadout; // Assign unit loadout +0 = [_unit, _unitLevel] call A3XAI_setSkills; // Set AI skill +_unit enableFatigue false; +A3XAI_monitoredObjects pushBack _unit; + +if (_antistuck) then { + _unit setPosATL _spawnPos; + _unit setVelocity [0,0,0.5]; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: Spawned AI %1 with unitLevel %2 for group %3.",_unit,_unitLevel,_unitGroup];}; + +_unit \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_create_UV_unit.sqf b/A3XAI/compile/A3XAI_create_UV_unit.sqf new file mode 100644 index 0000000..4b89f5b --- /dev/null +++ b/A3XAI/compile/A3XAI_create_UV_unit.sqf @@ -0,0 +1,16 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unit", "_unitGroup", "_spawnPos", "_unitLevel", "_type"]; +_unitGroup = _this select 0; +_unitLevel = _this select 1; +_spawnPos = _this select 2; + +_unit = _unitGroup createUnit ["I_UAV_AI",_spawnPos,[],0,"FORM"]; +[_unit] joinSilent _unitGroup; +0 = _unit call A3XAI_addUVUnitEH; +0 = [_unit, _unitLevel] call A3XAI_setSkills; // Set AI skill +A3XAI_monitoredObjects pushBack _unit; + +if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Spawned UAV AI %1 with unitLevel %2 for group %3.",_unit,_unitLevel,_unitGroup];}; + +_unit \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_customHeliDetect.sqf b/A3XAI/compile/A3XAI_customHeliDetect.sqf new file mode 100644 index 0000000..dec2e39 --- /dev/null +++ b/A3XAI/compile/A3XAI_customHeliDetect.sqf @@ -0,0 +1,34 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_detectOrigin", "_vehicle", "_detected", "_unitGroup", "_heliAimPos", "_playerAimPos"]; + +_vehicle = _this select 0; +_unitGroup = _this select 1; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +_unitGroup setVariable ["IsDetecting",true]; + +uiSleep (round (random 20)); + +if (!(_vehicle getVariable ["VehicleDisabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}) then{ + _detectOrigin = [getPosATL _vehicle,0,getDir _vehicle,1] call A3XAI_SHK_pos; + _detectOrigin set [2,0]; + _detected = _detectOrigin nearEntities [[PLAYER_UNITS],DETECT_RANGE_AIR_CUSTOM]; + if ((count _detected) > 5) then {_detected resize 5}; + { + if ((isPlayer _x) && {(_unitGroup knowsAbout _x) < 2}) then { + _heliAimPos = aimPos _vehicle; + _playerAimPos = aimPos _x; + if (((lineIntersectsSurfaces [_heliAimPos,_playerEyePos,_vehicle,_x,true,1]) isEqualTo []) && {A3XAI_airDetectChance call A3XAI_chance}) then { + //if (!(terrainIntersectASL [_heliAimPos,_playerAimPos]) && {!(lineIntersects [_heliAimPos,_playerAimPos,_vehicle,_x])} && {A3XAI_airDetectChance call A3XAI_chance}) then { //if no intersection of terrain and objects between helicopter and player, then reveal player + _unitGroup reveal [_x,2.5]; + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[31+(floor (random 5)),[name (leader _unitGroup)]]] call A3XAI_radioSend; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; +}; + +_unitGroup setVariable ["IsDetecting",false]; diff --git a/A3XAI/compile/A3XAI_debugMarkerLocation.sqf b/A3XAI/compile/A3XAI_debugMarkerLocation.sqf new file mode 100644 index 0000000..a313e8c --- /dev/null +++ b/A3XAI/compile/A3XAI_debugMarkerLocation.sqf @@ -0,0 +1,16 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_mapMarkerArray","_objectString"]; +_mapMarkerArray = missionNamespace getVariable ["A3XAI_mapMarkerArray",[]]; +_objectString = str (_this); +if !(_objectString in _mapMarkerArray) then { //Determine if marker is new + if !(_objectString in allMapMarkers) then { + private ["_marker"]; + _marker = createMarker [_objectString, _this]; + _marker setMarkerType "Waypoint"; + _marker setMarkerColor "ColorRed"; + _marker setMarkerBrush "Solid"; + }; + _mapMarkerArray pushBack _objectString; + missionNamespace setVariable ["A3XAI_mapMarkerArray",_mapMarkerArray]; +}; diff --git a/A3XAI/compile/A3XAI_defensiveAggression.sqf b/A3XAI/compile/A3XAI_defensiveAggression.sqf new file mode 100644 index 0000000..a806752 --- /dev/null +++ b/A3XAI/compile/A3XAI_defensiveAggression.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle", "_hitSource", "_damage", "_unitGroup", "_aggroExpiry"]; + +_vehicle = _this select 0; +_hitSource = _this select 1; +_damage = _this select 2; + +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (_unitGroup call A3XAI_getNoAggroStatus) exitWith {false}; + +if ((isPlayer _hitSource) && {(combatMode _unitGroup isEqualTo "BLUE")}) then { + _aggroExpiry = diag_tickTime + DEFENSIVE_AGGRESSION_TIME; + _vehicle setVariable ["AggroTime",_aggroExpiry]; + [_unitGroup,"Default"] call A3XAI_forceBehavior; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Defensive aggression enabled for %1 %2",_unitGroup,(typeOf _vehicle)];}; +}; + +true diff --git a/A3XAI/compile/A3XAI_deleteCustomSpawn.sqf b/A3XAI/compile/A3XAI_deleteCustomSpawn.sqf new file mode 100644 index 0000000..5dfbcd5 --- /dev/null +++ b/A3XAI/compile/A3XAI_deleteCustomSpawn.sqf @@ -0,0 +1,42 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_triggerType"]; + +_trigger = call { + _triggerType = (typeName _this); + if (_triggerType isEqualTo "OBJECT") exitWith { + _this + }; + if (_triggerType isEqualTo "GROUP") exitWith { + _this getVariable ["trigger",objNull] + }; + _this +}; + +if (A3XAI_enableDebugMarkers) then { + deleteMarker (_trigger getVariable ["MarkerName",""]); +}; +_trigger setTriggerStatements ["this","true","false"]; //Disable trigger from activating or deactivating while cleanup is performed +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Deleting custom-defined AI spawn %1 at %2 in 30 seconds.",_trigger getVariable ["TriggerText","Unknown Trigger"], mapGridPosition _trigger];}; + +uiSleep 30; + +{ + _x setVariable ["GroupSize",-1]; + if (A3XAI_HCIsConnected) then { + A3XAI_updateGroupSizeManual_PVC = [_x,-1]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeManual_PVC"; + }; +} forEach (_trigger getVariable ["GroupArray",[]]); + +[_trigger,"A3XAI_staticTriggerArray",false] call A3XAI_updateSpawnCount; + +if (_trigger in A3XAI_staticSpawnObjects) then { + A3XAI_staticSpawnObjects = A3XAI_staticSpawnObjects - [_trigger,objNull]; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Deleting custom-defined AI spawn %1 at %2.",_trigger getVariable ["TriggerText","Unknown Trigger"], mapGridPosition _trigger];}; + +deleteVehicle _trigger; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_deleteGroup.sqf b/A3XAI/compile/A3XAI_deleteGroup.sqf new file mode 100644 index 0000000..76876d2 --- /dev/null +++ b/A3XAI/compile/A3XAI_deleteGroup.sqf @@ -0,0 +1,28 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle"]; + +_unitGroup = _this; + +{ + if (alive _x) then { + deleteVehicle _x; + } else { + [_x] joinSilent grpNull; + }; +} count (units _unitGroup); + +if (_unitGroup getVariable ["RecycleGroup",false]) then { + _vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; //If infantry AI have vehicle assigned, will need to change this line + if (isNull _vehicle) then { //Groups with assigned vehicles do not need to be preserved + _unitGroup call A3XAI_protectGroup; + } else { + deleteGroup _unitGroup; + A3XAI_activeGroups = A3XAI_activeGroups - [_unitGroup,grpNull]; + }; +} else { + A3XAI_activeGroups = A3XAI_activeGroups - [_unitGroup,grpNull]; + deleteGroup _unitGroup; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_despawnUniversal.sqf b/A3XAI/compile/A3XAI_despawnUniversal.sqf new file mode 100644 index 0000000..5be57b1 --- /dev/null +++ b/A3XAI/compile/A3XAI_despawnUniversal.sqf @@ -0,0 +1,75 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_grpArray","_grpCount","_permDelete"]; + +_trigger = _this select 0; //Get the trigger object + +_grpArray = _trigger getVariable ["GroupArray",[]]; //Find the groups spawned by the trigger. +_grpCount = count _grpArray; + +if (A3XAI_debugLevel > 0) then { + diag_log format["A3XAI Debug: No players remain in trigger area at %1. Deleting %2 AI groups.", (_trigger getVariable ["TriggerText","Unknown Trigger"]),_grpCount]; //replace trigger text with function suitable for non-triggers + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Trigger %1 Group Array: %2. In static trigger array: %3",_trigger getVariable ["TriggerText","Unknown Trigger"],_grpArray,(_trigger in A3XAI_staticTriggerArray)];}; +}; + +_permDelete = _trigger getVariable ["permadelete",false]; + +{ + if (!isNull _x) then { + _groupSize = (_x getVariable ["GroupSize",0]); + if ((_groupSize > 0) or {_permDelete}) then { //If trigger is not set to permanently despawn, then ignore empty groups. + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Despawning group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3XAI_HCIsConnected) then { + A3XAI_updateGroupSizeManual_PVC = [_x,-1]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeManual_PVC"; + }; + _grpArray set [_forEachIndex,grpNull]; + }; + }; +} forEach _grpArray; + +if !(_permDelete) then { + //Cleanup variables attached to trigger + _trigger setVariable ["GroupArray",_grpArray - [grpNull]]; + _trigger setVariable ["isCleaning",false]; + _trigger setVariable ["unitLevelEffective",(_trigger getVariable ["unitLevel",1])]; + if !((_trigger getVariable ["respawnLimitOriginal",-1]) isEqualTo -1) then {_trigger setVariable ["respawnLimit",_trigger getVariable ["respawnLimitOriginal",-1]];}; + if (A3XAI_enableDebugMarkers) then { + _marker = _trigger getVariable ["MarkerName",""]; + call { + if (_trigger in A3XAI_staticSpawnObjects) exitWith { + if (_marker in allMapMarkers) then { + _marker setMarkerText "STATIC TRIGGER (INACTIVE)"; + _marker setMarkerColor "ColorGreen"; + }; + }; + + if (_trigger in A3XAI_randomTriggerArray) exitWith { + deleteMarker _marker; + }; + + if (_trigger in A3XAI_dynamicTriggerArray) exitWith { + deleteMarker _marker; + }; + + deleteMarker _marker; + diag_log format ["Debug: Unhandled case for static spawn %1 in %2.",_trigger getVariable ["TriggerText","Unknown Spawn"],__FILE__]; + }; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Despawned AI units at %1. Reset trigger's group array to: %2.",(_trigger getVariable ["TriggerText","Unknown Trigger"]),_trigger getVariable "GroupArray"];}; +} else { + if (A3XAI_enableDebugMarkers) then { + _marker = str (_trigger); + if (_marker in allMapMarkers) then { + deleteMarker _marker; + }; + }; + + //Replace trigger-specific functions + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Permanently deleting a static spawn at %1.",_trigger getVariable ["TriggerText","Unknown Trigger"]]}; + deleteVehicle _trigger; +}; + +true diff --git a/A3XAI/compile/A3XAI_despawn_dynamic.sqf b/A3XAI/compile/A3XAI_despawn_dynamic.sqf new file mode 100644 index 0000000..ccfc7d1 --- /dev/null +++ b/A3XAI/compile/A3XAI_despawn_dynamic.sqf @@ -0,0 +1,84 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_triggerLocation","_isForceDespawn","_grpArray","_canDespawn","_triggerExists","_triggerStatements","_deactStatements"]; + +_trigger = _this select 0; //Get the trigger object +_isForceDespawn = if ((count _this) > 1) then {_this select 1} else {false}; + +_triggerStatements = triggerStatements _trigger; +_grpArray = _trigger getVariable ["GroupArray",[]]; //Find the groups spawned by the trigger. Or set an empty group array if none are found. + +if ((_trigger getVariable ["isCleaning",false]) && (!_isForceDespawn)) exitWith {if (A3XAI_debugLevel > 1) then {diag_log "A3XAI Debug: Despawn script is already running. Exiting despawn script.";};}; + +_trigger setVariable["isCleaning",true]; //Mark the trigger as being in a cleanup state so that subsequent requests to despawn for the same trigger will not run. +_deactStatements = _triggerStatements select 2; +_trigger setTriggerStatements (_triggerStatements set [2,""]); +_canDespawn = true; +_triggerExists = true; + + +if (_isForceDespawn) then { + _trigger setTriggerStatements ["this","",""]; + if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: All units of dynamic AI group spawned by trigger %1 have been killed. Starting force despawn in 30 seconds.",_trigger getVariable ["TriggerText","Unknown Spawn"]];}; + uiSleep 30; +} else { + if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: No players remain in %1. Deleting spawned AI in %2 seconds.",_trigger getVariable ["TriggerText","Unknown Spawn"],A3XAI_despawnDynamicSpawnTime];}; + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorGreen"; + _marker setMarkerAlpha 0.7; //Light green: Active trigger awaiting despawn. + }; + }; + uiSleep A3XAI_despawnDynamicSpawnTime; //Wait some time before deleting units. (amount of time to allow units to exist when the trigger area has no players) + + if !(isNull _trigger) then { //Check if dynamic spawn area has been force-despawned (deleted). Force despawn will happen when all units have been killed. + _canDespawn = ((!triggerActivated _trigger) or {isNull (_grpArray select 0)}); //Can despawn dynamic spawn area if trigger isn't activated or spawned group is null + } else { + _triggerExists = false; + }; +}; + +if !(_triggerExists) exitWith {}; //Cancel despawn process if it has already happened + +if (_canDespawn) then { + _trigger setTriggerStatements ["this","",""]; //temporarily disable trigger from activating or deactivating while cleanup is performed + _grpArray = _grpArray - [grpNull]; + { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Deleting group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3XAI_HCIsConnected) then { + A3XAI_updateGroupSizeManual_PVC = [_x,-1]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeManual_PVC"; + }; + } forEach _grpArray; + + //Remove dynamic trigger from global dyn trigger array and clean up trigger + [_trigger,"A3XAI_dynamicTriggerArray",false] call A3XAI_updateSpawnCount; + if (A3XAI_enableDebugMarkers) then { + deleteMarker (_trigger getVariable ["MarkerName",""]); + }; + + //Begin deletion timer for temporary blacklist area and add it to global dyn location array to allow deletion + _triggerLocation = _trigger getVariable "triggerLocation"; + _triggerLocation setVariable ["deletetime",(diag_tickTime + A3XAI_tempBlacklistTime)]; + A3XAI_areaBlacklists pushBack _triggerLocation; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Removing expired dynamic trigger at %1.",mapGridPosition _trigger];}; + deleteVehicle _trigger; + + true +} else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: A player has entered the trigger area at %1. Cancelling despawn.",(_trigger getVariable ["TriggerText","Unknown Spawn"])];}; //Exit script if trigger has been reactivated since A3XAI_despawnDynamicSpawnTime seconds has passed. + _trigger setVariable ["isCleaning",false]; //Allow next despawn request. + _triggerStatements set [2,_deactStatements]; + _trigger setTriggerStatements _triggerStatements; + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; //Reset trigger indicator color to Active. + }; + }; + false +}; \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_despawn_random.sqf b/A3XAI/compile/A3XAI_despawn_random.sqf new file mode 100644 index 0000000..6af52f2 --- /dev/null +++ b/A3XAI/compile/A3XAI_despawn_random.sqf @@ -0,0 +1,84 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_triggerLocation","_isForceDespawn","_grpArray","_canDespawn","_triggerExists","_triggerStatements","_deactStatements"]; + +_trigger = _this select 0; //Get the trigger object +_isForceDespawn = if ((count _this) > 1) then {_this select 1} else {false}; + +_triggerStatements = triggerStatements _trigger; +_grpArray = _trigger getVariable ["GroupArray",[]]; //Find the groups spawned by the trigger. Or set an empty group array if none are found. + +if ((_trigger getVariable ["isCleaning",false]) && (!_isForceDespawn)) exitWith {if (A3XAI_debugLevel > 1) then {diag_log "A3XAI Debug: Despawn script is already running. Exiting despawn script.";};}; + +_trigger setVariable["isCleaning",true]; //Mark the trigger as being in a cleanup state so that subsequent requests to despawn for the same trigger will not run. +_deactStatements = _triggerStatements select 2; +_trigger setTriggerStatements (_triggerStatements set [2,""]); +_canDespawn = true; +_triggerExists = true; + + +if (_isForceDespawn) then { + _trigger setTriggerStatements ["this","",""]; + if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: All units of random AI group spawned by trigger %1 have been killed. Starting force despawn in 30 seconds.",_trigger getVariable ["TriggerText","Unknown Spawn"]];}; + uiSleep 30; +} else { + if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: No players remain in %1. Deleting spawned AI in %2 seconds.",_trigger getVariable ["TriggerText","Unknown Spawn"],A3XAI_despawnRandomSpawnTime];}; + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorGreen"; + _marker setMarkerAlpha 0.7; //Light green: Active trigger awaiting despawn. + }; + }; + uiSleep A3XAI_despawnRandomSpawnTime; //Wait some time before deleting units. (amount of time to allow units to exist when the trigger area has no players) + + if !(isNull _trigger) then { //Check if random spawn area has been force-despawned (deleted). Force despawn will happen when all units have been killed. + _canDespawn = ((!triggerActivated _trigger) or {isNull (_grpArray select 0)}); //Can despawn random spawn area if trigger isn't activated or spawned group is null + } else { + _triggerExists = false; + }; +}; + +if !(_triggerExists) exitWith {}; //Cancel despawn process if it has already happened + +if (_canDespawn) then { + _trigger setTriggerStatements ["this","",""]; //temporarily disable trigger from activating or deactivating while cleanup is performed + _grpArray = _grpArray - [grpNull]; + { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Deleting group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3XAI_HCIsConnected) then { + A3XAI_updateGroupSizeManual_PVC = [_x,-1]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeManual_PVC"; + }; + } forEach _grpArray; + + //Remove random trigger from global dyn trigger array and clean up trigger + [_trigger,"A3XAI_randomTriggerArray",false] call A3XAI_updateSpawnCount; + if (A3XAI_enableDebugMarkers) then { + deleteMarker (_trigger getVariable ["MarkerName",""]); + }; + + //Begin deletion timer for temporary blacklist area and add it to global dyn location array to allow deletion + _triggerLocation = _trigger getVariable "triggerLocation"; + _triggerLocation setVariable ["deletetime",(diag_tickTime + A3XAI_tempBlacklistTime)]; + A3XAI_areaBlacklists pushBack _triggerLocation; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Removing expired random trigger at %1.",mapGridPosition _trigger];}; + deleteVehicle _trigger; + + true +} else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: A player has entered the trigger area at %1. Cancelling despawn script.",(_trigger getVariable ["TriggerText","Unknown Spawn"])];}; //Exit script if trigger has been reactivated since A3XAI_despawnRandomSpawnTime seconds has passed. + _trigger setVariable ["isCleaning",false]; //Allow next despawn request. + _triggerStatements set [2,_deactStatements]; + _trigger setTriggerStatements _triggerStatements; + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; //Reset trigger indicator color to Active. + }; + }; + false +}; diff --git a/A3XAI/compile/A3XAI_despawn_static.sqf b/A3XAI/compile/A3XAI_despawn_static.sqf new file mode 100644 index 0000000..074dabf --- /dev/null +++ b/A3XAI/compile/A3XAI_despawn_static.sqf @@ -0,0 +1,92 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_grpArray","_isCleaning","_grpCount","_triggerStatements","_deactStatements","_permDelete"]; + +_trigger = _this select 0; //Get the trigger object + +_grpArray = _trigger getVariable ["GroupArray",[]]; //Find the groups spawned by the trigger. +_isCleaning = _trigger getVariable ["isCleaning",true]; //Find whether or not the trigger has been marked for cleanup. Triggers will flag themselves for cleaning after a successful spawn/respawn with setVariable ["isCleaning",false]; +_triggerStatements = triggerStatements _trigger; +_grpCount = count _grpArray; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Trigger %1 Group Array: %2. isCleaning: %3. In static trigger array: %4",_trigger getVariable ["TriggerText","Unknown Spawn"],_grpArray,_isCleaning,(_trigger in A3XAI_staticTriggerArray)];}; +if (!(_trigger in A3XAI_staticTriggerArray) or {_isCleaning}) exitWith {if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Trigger %1 has a despawn script already running. Exiting despawn script.",_trigger getVariable ["TriggerText","Unknown Spawn"]];};}; + +_trigger setVariable["isCleaning",true]; //Mark the trigger as being in a cleanup state so that subsequent requests to despawn for the same trigger will not run. +_deactStatements = _triggerStatements select 2; +_triggerStatements set [2,""]; +_trigger setTriggerStatements _triggerStatements; + + +if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: No players remain in trigger area at %3. Deleting %1 AI groups in %2 seconds.",_grpCount, A3XAI_despawnWait,(_trigger getVariable ["TriggerText","Unknown Spawn"])];}; + +if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _tMarker = str (_this); + _tMarker setMarkerText "STATIC TRIGGER (DESPAWNING)"; + _tMarker setMarkerColor "ColorOrange"; + }; +}; + +if (({isNull _x} count _grpArray) < _grpCount) then {uiSleep A3XAI_despawnWait}; + +if (isNull _trigger) exitWith {[_trigger,"A3XAI_staticTriggerArray",false] call A3XAI_updateSpawnCount}; + +if ((triggerActivated _trigger) && {({isNull _x} count _grpArray) < _grpCount}) exitWith { //Exit script if trigger has been reactivated since A3XAI_despawnWait seconds has passed. + _trigger setVariable ["isCleaning",false]; //Allow next despawn request. + _triggerStatements set [2,_deactStatements]; + _trigger setTriggerStatements _triggerStatements; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: A player has entered the trigger area at %1. Cancelling despawn script.",(_trigger getVariable ["TriggerText","Unknown Spawn"])];}; + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _tMarker = str (_this); + _tMarker setMarkerText "STATIC TRIGGER (ACTIVE)"; + _tMarker setMarkerColor "ColorRed"; + }; + }; +}; + +_trigger setTriggerStatements ["this","true","false"]; //temporarily disable trigger from activating or deactivating while cleanup is performed +_permDelete = _trigger getVariable ["permadelete",false]; +{ + if (!isNull _x) then { + _groupSize = (_x getVariable ["GroupSize",0]); + if ((_groupSize > 0) or {_permDelete}) then { //If trigger is not set to permanently despawn, then ignore empty groups. + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Despawning group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3XAI_HCIsConnected) then { + A3XAI_updateGroupSizeManual_PVC = [_x,-1]; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_updateGroupSizeManual_PVC"; + }; + _grpArray set [_forEachIndex,grpNull]; + }; + }; +} forEach _grpArray; + +[_trigger,"A3XAI_staticTriggerArray",false] call A3XAI_updateSpawnCount; +if !(_permDelete) then { + //Cleanup variables attached to trigger + _trigger setVariable ["GroupArray",_grpArray - [grpNull]]; + _trigger setVariable ["isCleaning",false]; + _trigger setVariable ["unitLevelEffective",(_trigger getVariable ["unitLevel",1])]; + _trigger setTriggerArea [TRIGGER_SIZE_NORMAL,TRIGGER_SIZE_NORMAL,0,false]; + _trigger setTriggerStatements (_trigger getVariable "triggerStatements"); //restore original trigger statements + if !((_trigger getVariable ["respawnLimitOriginal",-1]) isEqualTo -1) then {_trigger setVariable ["respawnLimit",_trigger getVariable ["respawnLimitOriginal",-1]];}; + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _tMarker = str (_this); + _tMarker setMarkerText "STATIC TRIGGER (INACTIVE)"; + _tMarker setMarkerColor "ColorGreen"; + }; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Despawned AI units at %1. Reset trigger's group array to: %2 and statements to %3.",(_trigger getVariable ["TriggerText","Unknown Spawn"]),_trigger getVariable "GroupArray",_trigger getVariable "triggerStatements"];}; +} else { + if (A3XAI_enableDebugMarkers) then { + deleteMarker (_trigger getVariable ["MarkerName",""]); + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Permanently deleting a static spawn at %1.",_trigger getVariable ["TriggerText","Unknown Spawn"]]}; + deleteVehicle _trigger; +}; + +true diff --git a/A3XAI/compile/A3XAI_ejectParachute.sqf b/A3XAI/compile/A3XAI_ejectParachute.sqf new file mode 100644 index 0000000..3a87eca --- /dev/null +++ b/A3XAI/compile/A3XAI_ejectParachute.sqf @@ -0,0 +1,13 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unit","_unitPos","_parachute"]; + +_unit = _this; + +_unitPos = getPosATL _unit; +_parachute = createVehicle [PARACHUTE_OBJECT, _unitPos, [], (-10 + (random 10)), "FLY"]; +unassignVehicle _unit; +_unit setPosATL _unitPos; +_unit moveInDriver _parachute; + +_parachute diff --git a/A3XAI/compile/A3XAI_execEveryLoop_air.sqf b/A3XAI/compile/A3XAI_execEveryLoop_air.sqf new file mode 100644 index 0000000..257331e --- /dev/null +++ b/A3XAI/compile/A3XAI_execEveryLoop_air.sqf @@ -0,0 +1,30 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_leader", "_inArea", "_assignedTarget"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_leader = (leader _unitGroup); +_inArea = [_vehicle,NO_AGGRO_RANGE_AIR] call A3XAI_checkInActiveNoAggroArea; + +if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_AIR) then { + _inArea = [_assignedTarget,300] call A3XAI_checkInActiveNoAggroArea; + }; +}; + +if (_inArea) exitWith { + [_unitGroup,_vehicle,(typeOf _vehicle)] call A3XAI_recycleGroup; +}; + +if (((_unitGroup getVariable ["unitType",""]) == "air") && {!(_unitGroup getVariable ["IsDetecting",false])} && {[_vehicle,BEGIN_DETECT_DIST_AIR] call A3XAI_checkInActiveStaticSpawnArea}) then { + [_unitGroup] spawn A3XAI_heliDetection; + + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: %1 %2 is scanning for players in active trigger area at %3.",_unitGroup,(typeOf _vehicle),(getPosATL _vehicle)]; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_execEveryLoop_infantry.sqf b/A3XAI/compile/A3XAI_execEveryLoop_infantry.sqf new file mode 100644 index 0000000..eeed1ae --- /dev/null +++ b/A3XAI/compile/A3XAI_execEveryLoop_infantry.sqf @@ -0,0 +1,22 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_inArea", "_result", "_trigger", "_maxDistance","_leader", "_assignedTarget"]; + +_unitGroup = _this select 0; +//_vehicle = _this select 1; + +_leader = (leader _unitGroup); +_inArea = [_leader,NO_AGGRO_RANGE_MAN] call A3XAI_checkInActiveNoAggroArea; + +if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_MAN) then { + _inArea = [_assignedTarget,300] call A3XAI_checkInActiveNoAggroArea; + }; +}; + +if (_inArea) exitWith { + [_unitGroup,objNull,""] call A3XAI_recycleGroup; +}; + +true diff --git a/A3XAI/compile/A3XAI_execEveryLoop_uav.sqf b/A3XAI/compile/A3XAI_execEveryLoop_uav.sqf new file mode 100644 index 0000000..6a09841 --- /dev/null +++ b/A3XAI/compile/A3XAI_execEveryLoop_uav.sqf @@ -0,0 +1,43 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_leader", "_inArea", "_assignedTarget", "_lastAggro"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_inArea = false; + +if ((combatMode _unitGroup) == "YELLOW") then { + _leader = (leader _unitGroup); + _inArea = [_leader,NO_AGGRO_RANGE_UAV] call A3XAI_checkInActiveNoAggroArea; + + if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_UAV) then { + _inArea = [_assignedTarget,300] call A3XAI_checkInActiveNoAggroArea; + }; + }; + + if (_inArea) exitWith { + [_unitGroup,_vehicle,(typeOf _vehicle)] call A3XAI_recycleGroup; + }; + + _lastAggro = _vehicle getVariable "AggroTime"; + if (!(isNil "_lastAggro") && {diag_tickTime > _lastAggro}) then { + _vehicle setVariable ["AggroTime",nil]; + [_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Reset Group %1 %2 UAV to non-hostile mode.",_unitGroup,(typeOf _vehicle)]}; + }; +}; + +if (_inArea) exitWith {}; + +if (((_unitGroup getVariable ["unitType",""]) == "uav") && {!(_unitGroup getVariable ["IsDetecting",false])} && {[_vehicle,BEGIN_DETECT_DIST_UAV] call A3XAI_checkInActiveStaticSpawnArea}) then { + [_unitGroup] spawn A3XAI_UAVDetection; + + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: %1 %2 is scanning for players in active trigger area at %3.",_unitGroup,(typeOf _vehicle),(getPosATL _vehicle)]; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_execEveryLoop_ugv.sqf b/A3XAI/compile/A3XAI_execEveryLoop_ugv.sqf new file mode 100644 index 0000000..b579924 --- /dev/null +++ b/A3XAI/compile/A3XAI_execEveryLoop_ugv.sqf @@ -0,0 +1,43 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_leader", "_inArea", "_assignedTarget", "_lastAggro"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_inArea = false; + +if ((combatMode _unitGroup) == "YELLOW") then { + _leader = (leader _unitGroup); + _inArea = [_leader,NO_AGGRO_RANGE_UGV] call A3XAI_checkInActiveNoAggroArea; + + if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_UGV) then { + _inArea = [_assignedTarget,300] call A3XAI_checkInActiveNoAggroArea; + }; + }; + + if (_inArea) exitWith { + [_unitGroup,_vehicle,(typeOf _vehicle)] call A3XAI_recycleGroup; + }; + + _lastAggro = _vehicle getVariable "AggroTime"; + if (!(isNil "_lastAggro") && {diag_tickTime > _lastAggro}) then { + _vehicle setVariable ["AggroTime",nil]; + [_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Reset Group %1 %2 UGV to non-hostile mode.",_unitGroup,(typeOf _vehicle)]}; + }; +}; + +if (_inArea) exitWith {}; + +if (((_unitGroup getVariable ["unitType",""]) == "ugv") && {!(_unitGroup getVariable ["IsDetecting",false])} && {[_vehicle,BEGIN_DETECT_DIST_UGV] call A3XAI_checkInActiveStaticSpawnArea}) then { + [_unitGroup] spawn A3XAI_UGVDetection; + + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: %1 %2 is scanning for players in active trigger area at %3.",_unitGroup,(typeOf _vehicle),(getPosATL _vehicle)]; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_execEveryLoop_vehicle.sqf b/A3XAI/compile/A3XAI_execEveryLoop_vehicle.sqf new file mode 100644 index 0000000..d01d77e --- /dev/null +++ b/A3XAI/compile/A3XAI_execEveryLoop_vehicle.sqf @@ -0,0 +1,37 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_lastRegroupCheck","_respawnType","_inArea","_result","_leader","_unitType"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_leader = (leader _unitGroup); +_inArea = [_leader,NO_AGGRO_RANGE_LAND] call A3XAI_checkInActiveNoAggroArea; + +if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_LAND) then { + _inArea = [_assignedTarget,300] call A3XAI_checkInActiveNoAggroArea; + }; +}; + +if (_inArea) exitWith { + [_unitGroup,_vehicle,(typeOf _vehicle)] call A3XAI_recycleGroup; +}; + +_lastRegroupCheck = _vehicle getVariable "LastRegroupCheck"; +if (isNil "_lastRegroupCheck") then { + _lastRegroupCheck = diag_tickTime; + _vehicle setVariable ["LastRegroupCheck",0]; +}; + +if ((diag_tickTime - _lastRegroupCheck) > 30) then { + if ((alive _vehicle) && {_unitGroup getVariable ["regrouped",true]} && {({if ((_x distance2D _vehicle) > REGROUP_VEHICLEGROUP_DIST) exitWith {1}} count (assignedCargo _vehicle)) > 0}) then { + _unitGroup setVariable ["regrouped",false]; + [_unitGroup,_vehicle] call A3XAI_vehCrewRegroup; + }; + + _vehicle setVariable ["LastRegroupCheck",diag_tickTime]; +}; + +true diff --git a/A3XAI/compile/A3XAI_findSpawnPos.sqf b/A3XAI/compile/A3XAI_findSpawnPos.sqf new file mode 100644 index 0000000..0145eba --- /dev/null +++ b/A3XAI/compile/A3XAI_findSpawnPos.sqf @@ -0,0 +1,31 @@ +#include "\A3XAI\globaldefines.hpp" + +//Finds a position that does not have a player within a certain distance. +private ["_spawnPos","_attempts","_continue","_spawnpool","_maxAttempts"]; + +_attempts = 0; +_continue = true; +_spawnPos = []; +_spawnpool = +_this; +_maxAttempts = ((count _spawnpool) min 3); //3: Maximum number of attempts +while {_continue && {(_attempts < _maxAttempts)}} do { + _index = floor (random (count _spawnpool)); + _spawnPosSelected = (getPosATL (_spawnpool select _index)) findEmptyPosition [0.5,30,SPACE_FOR_OBJECT]; + if !(_spawnPosSelected isEqualTo []) then { + _spawnPosSelected = _spawnPosSelected isFlatEmpty [0,0,0.75,5,0,false,objNull]; + }; + if ( + !(_spawnPosSelected isEqualTo []) && + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPosSelected select 0),(_spawnPosSelected select 1),(_spawnPosSelected select 2) + 1.7],_x] call A3XAI_hasLOS) or ((_x distance _spawnPosSelected) < PLAYER_DISTANCE_NO_LOS_STATIC)}) exitWith {1}} count (_spawnPosSelected nearEntities [[PLAYER_UNITS,"LandVehicle"],PLAYER_DISTANCE_WITH_LOS_STATIC])) isEqualTo 0} + ) then { + _spawnPos = _spawnPosSelected; + _spawnPos set [2,0]; + _continue = false; + } else { + _spawnpool deleteAt _index; + _attempts = _attempts + 1; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Unable to find suitable spawn position. (attempt %1/%2).",_attempts,_maxAttempts];}; + }; +}; + +_spawnPos diff --git a/A3XAI/compile/A3XAI_fixStuckGroup.sqf b/A3XAI/compile/A3XAI_fixStuckGroup.sqf new file mode 100644 index 0000000..51ee33f --- /dev/null +++ b/A3XAI/compile/A3XAI_fixStuckGroup.sqf @@ -0,0 +1,69 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_isInfantry", "_nearPlayers", "_leaderPos", "_newPosEmpty","_unitType","_vehicleType","_leader"]; + +_unitGroup = _this select 0; + +_vehicle = (_unitGroup getVariable ["assignedVehicle",objNull]); +_isInfantry = (isNull _vehicle); +_unitType = _unitGroup getVariable ["unitType",""]; +_leaderPos = getPosATL (leader _unitGroup); + +if (_isInfantry) then { + _newPosEmpty = _leaderPos findEmptyPosition [0.5,30,SPACE_FOR_OBJECT]; + + if !(_newPosEmpty isEqualTo []) then { + _newPosEmpty = _newPosEmpty isFlatEmpty [0,0,0.75,5,0,false,objNull]; + }; + + if (_newPosEmpty isEqualTo []) then { + _newPosEmpty = [_leaderPos,10 + random(25),random(360),0,[0,0],[25,SPACE_FOR_OBJECT]] call A3XAI_SHK_pos; + }; + + if (({isPlayer _x} count (_newPosEmpty nearEntities [[PLAYER_UNITS,"AllVehicles"], PLAYER_DISTANCE_WITH_LOS_ANTISTUCK]) isEqualTo 0) && {((_newPosEmpty nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])}) then { + _newPosEmpty set [2,0]; + { + _x setPosATL _newPosEmpty; + _x setVelocity [0,0,0.25]; + } forEach (units _unitGroup); + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Relocated stuck group %1 (%2) to new location %3m away.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"]),(_leaderPos distance _newPosEmpty)];}; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to relocate stuck group %1 (%2).",_unitGroup,(_unitGroup getVariable ["unitType","unknown"])];}; + }; +} else { + _newPosEmpty = [0,0,0]; + if (_unitType in ["land","ugv"]) then { + _keepLooking = true; + _vehicleType = (typeOf _vehicle); + while {_keepLooking} do { + _newPosEmpty = [(getMarkerPos "A3XAI_centerMarker"),300 + random((getMarkerSize "A3XAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3XAI_SHK_pos; + if ((count _newPosEmpty) > 1) then { + if (({isPlayer _x} count (_newPosEmpty nearEntities [[PLAYER_UNITS,"AllVehicles"], PLAYER_DISTANCE_WITH_LOS_ANTISTUCK]) isEqualTo 0) && {((_newPosEmpty nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])}) then { + _keepLooking = false; + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to find road position to relocate AI group %1 %2. Retrying in 15 seconds.",_unitGroup,_vehicleType]}; + uiSleep 15; + }; + }; + } else { + _newPosEmpty = [_leaderPos,10 + random(25),random(360),0,[1,300],[25,(typeOf _vehicle)]] call A3XAI_SHK_pos; + }; + _leader = (leader _unitGroup); + if ((_leader distance (_leader findNearestEnemy _vehicle)) > NEAREST_ENEMY_RANGE_ANTISTUCK) then { + _vehicle setPosATL _newPosEmpty; + _vehicle setVelocity [0,0,0.25]; + { + if ((isNull (objectParent _x)) && {(_x distance _vehicle) > 100}) then { + _newUnitPos = [_vehicle,25,random(360),0,[0,0],[25,DEFAULT_UNIT_CLASSNAME]] call A3XAI_SHK_pos; + _x setPosATL _newUnitPos; + _x setVelocity [0,0,0.25]; + }; + } forEach (units _unitGroup); + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Relocated stuck group %1 (%2) to new location %3m away.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"]),(_leaderPos distance _newPosEmpty)];}; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to relocate stuck group %1 (%2) due to nearby enemy presence.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"])];}; + }; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_forceBehavior.sqf b/A3XAI/compile/A3XAI_forceBehavior.sqf new file mode 100644 index 0000000..ff66b51 --- /dev/null +++ b/A3XAI/compile/A3XAI_forceBehavior.sqf @@ -0,0 +1,39 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_action", "_unitGroup", "_result"]; + +_action = _this select 1; +_unitGroup = _this select 0; + +_result = call { + if (_action isEqualTo "Nonhostile") exitWith { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + {_x doWatch objNull} forEach (units _unitGroup); + + true + }; + + if (_action isEqualTo "Default") exitWith { + _unitGroup setBehaviour "AWARE"; + _unitGroup setCombatMode "YELLOW"; + + false + }; + + if (_action isEqualTo "DefendOnly") exitWith { + _unitGroup setBehaviour "AWARE"; + _unitGroup setCombatMode "GREEN"; + {_x doWatch objNull} forEach (units _unitGroup); + + false + }; + + false +}; + +_unitGroup setVariable ["EnemiesIgnored",_result]; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Setting group %1 behavior mode to %2 (result: %3).",_unitGroup,_action,_result];}; + +_result diff --git a/A3XAI/compile/A3XAI_forceRespawnGroup.sqf b/A3XAI/compile/A3XAI_forceRespawnGroup.sqf new file mode 100644 index 0000000..84f9dba --- /dev/null +++ b/A3XAI/compile/A3XAI_forceRespawnGroup.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +private []; + +_unitGroup = _this select 0; +_unitType = _this select 1; +_retryOnFail = _this select 2; + +_fnc_respawnGroup = missionNamespace getVariable [format ["A3XAI_respawn_%1",_unitType],{}]; +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; + +if !(isNull _vehicle) then { + _vehicle setVariable ["DeleteVehicle",true]; +}; + +_result = [_unitType] call _fnc_respawnGroup; + +_unitGroup setVariable ["GroupSize",-1]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_generateGroupLoot.sqf b/A3XAI/compile/A3XAI_generateGroupLoot.sqf new file mode 100644 index 0000000..ec90cac --- /dev/null +++ b/A3XAI/compile/A3XAI_generateGroupLoot.sqf @@ -0,0 +1,29 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_lootPool", "_updateServerLoot", "_pullChance", "_lootUnit", "_lootIndex", "_loot", "_unitLevel"]; + +_unitGroup = _this select 0; +_unitLevel = _this select 1; + +_lootPool = _unitGroup getVariable ["LootPool",[]]; +_updateServerLoot = (A3XAI_enableHC && {!isDedicated}); +_pullChance = missionNamespace getVariable [format ["A3XAI_lootPullChance%1",_unitLevel],0.40]; +if !(_lootPool isEqualTo []) then { + if (_pullChance call A3XAI_chance) then { + _lootUnit = (units _unitGroup) call A3XAI_selectRandom; + _lootIndex = floor (random (count _lootPool)); + _loot = _lootPool select _lootIndex; + if (alive _lootUnit) then { + if ([_lootUnit,_loot] call A3XAI_addItem) then { + _lootPool deleteAt _lootIndex; + if (_updateServerLoot) then { + [_unitGroup,_lootIndex] call A3XAI_updateServerLoot; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Pulled %1 from %2 loot pool (%3 items remain).",_loot,_unitGroup,(count _lootPool)];}; + }; + }; + }; +}; +_unitGroup setVariable ["lootGenTime",diag_tickTime]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_generateLoadout.sqf b/A3XAI/compile/A3XAI_generateLoadout.sqf new file mode 100644 index 0000000..1494d2c --- /dev/null +++ b/A3XAI/compile/A3XAI_generateLoadout.sqf @@ -0,0 +1,163 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unit", "_unitLevel", "_unitLevelInvalid", "_loadout", "_weaponSelected", "_unitLevelString", "_uniforms", "_uniformItem", "_backpackChance", "_backpacks", "_backpackItem", +"_vestChance", "_vests", "_vestItem", "_headgearChance", "_headgears", "_headgearItem", "_magazine", "_useGL", "_weaponMuzzles", "_GLWeapon", "_GLMagazines", "_isRifle", "_opticsList", +"_opticsType", "_pointersList", "_pointerType", "_muzzlesList", "_muzzleType", "_underbarrelList", "_underbarrelType", "_gadgetsArray", "_gadget"]; + +_unit = _this select 0; +_unitLevel = _this select 1; + +if !(isNil {_unit getVariable "loadout"}) exitWith {diag_log format ["A3XAI Error: Unit already has loadout! (%1)",__FILE__];}; + +if !(_unitLevel in A3XAI_unitLevelsAll) then { + _unitLevelInvalid = _unitLevel; + _unitLevel = A3XAI_unitLevels call A3XAI_selectRandom; + diag_log format ["A3XAI Error: Invalid unitLevel provided: %1. Generating new unitLevel value: %2. (%3)",_unitLevelInvalid,_unitLevel,__FILE__]; +}; + +_unit call A3XAI_purgeUnitGear; //Clear unwanted gear from unit first. + +_loadout = [[],[]]; +_weaponSelected = _unitLevel call A3XAI_getWeapon; +_unitLevelString = str (_unitLevel); + +_uniforms = missionNamespace getVariable ["A3XAI_uniformTypes"+_unitLevelString,[]]; +if !(_uniforms isEqualTo []) then { + _uniformItem = _uniforms call A3XAI_selectRandom; + _unit forceAddUniform _uniformItem; + //diag_log format ["DEBUG: %1",_uniformItem]; +}; + + +_backpackChance = missionNamespace getVariable ["A3XAI_addBackpackChance"+_unitLevelString,1.00]; +if (_backpackChance call A3XAI_chance) then { + _backpacks = missionNamespace getVariable ["A3XAI_backpackTypes"+_unitLevelString,[]]; + if !(_backpacks isEqualTo []) then { + _backpackItem = _backpacks call A3XAI_selectRandom; + _unit addBackpack _backpackItem; + clearAllItemsFromBackpack _unit; + //diag_log format ["DEBUG: %1",_backpackItem]; + }; +}; + +_vestChance = missionNamespace getVariable ["A3XAI_addVestChance"+_unitLevelString,1.00]; +if (_vestChance call A3XAI_chance) then { + _vests = missionNamespace getVariable ["A3XAI_vestTypes"+_unitLevelString,[]]; + if !(_vests isEqualTo []) then { + _vestItem = _vests call A3XAI_selectRandom; + _unit addVest _vestItem; + //diag_log format ["DEBUG: %1",_vestItem]; + }; +}; + +_headgearChance = missionNamespace getVariable ["A3XAI_addHeadgearChance"+_unitLevelString,1.00]; +if (_headgearChance call A3XAI_chance) then { + _headgears = missionNamespace getVariable ["A3XAI_headgearTypes"+_unitLevelString,[]]; + if !(_headgears isEqualTo []) then { + _headgearItem = _headgears call A3XAI_selectRandom; + _unit addHeadgear _headgearItem; + //diag_log format ["DEBUG: %1",_headgearItem]; + }; +}; + +_magazine = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "magazines") select 0; + +_unit addMagazine _magazine; +_unit addWeapon _weaponSelected; +_unit selectWeapon _weaponSelected; +(_loadout select 0) pushBack _weaponSelected; +(_loadout select 1) pushBack _magazine; +if ((getNumber (configFile >> "CfgMagazines" >> _magazine >> "count")) < 6) then { + _unit setVariable ["extraMag",true]; + _unit addMagazine _magazine; +}; + +//Grenades +_useGL = if !(A3XAI_levelRequiredGL isEqualTo -1) then {_unitLevel >= A3XAI_levelRequiredGL} else {false}; +if (_useGL) then { + _weaponMuzzles = getArray(configFile >> "cfgWeapons" >> _weaponSelected >> "muzzles"); + if ((count _weaponMuzzles) > 1) then { + _GLWeapon = _weaponMuzzles select 1; + _GLMagazines = (getArray (configFile >> "CfgWeapons" >> _weaponSelected >> _GLWeapon >> "magazines")); + if (GRENADE_AMMO_3RND in _GLMagazines) then { + _unit addMagazine GRENADE_AMMO_3RND; + (_loadout select 0) pushBack _GLWeapon; + (_loadout select 1) pushBack GRENADE_AMMO_3RND; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Modified unit %1 loadout to %2.",_unit,_loadout];}; + } else { + if (GRENADE_AMMO_1RND in _GLMagazines) then { + _unit addMagazine GRENADE_AMMO_1RND; + (_loadout select 0) pushBack _GLWeapon; + (_loadout select 1) pushBack GRENADE_AMMO_1RND; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Modified unit %1 loadout to %2.",_unit,_loadout];}; + } + }; + }; +}; + +//Select weapon optics +_isRifle = ((getNumber (configFile >> "CfgWeapons" >> _weaponSelected >> "type")) isEqualTo 1); +if ((missionNamespace getVariable [("A3XAI_opticsChance"+_unitLevelString),3]) call A3XAI_chance) then { + _opticsList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "CowsSlot" >> "compatibleItems"); + if !(_opticsList isEqualTo []) then { + _opticsType = A3XAI_weaponOpticsList call A3XAI_selectRandom; + if (_opticsType in _opticsList) then { + if (_isRifle) then {_unit addPrimaryWeaponItem _opticsType} else {_unit addHandGunItem _opticsType}; + }; + }; +}; + +//Select weapon pointer +if ((missionNamespace getVariable [("A3XAI_pointerChance"+_unitLevelString),3]) call A3XAI_chance) then { + _pointersList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "PointerSlot" >> "compatibleItems"); + if !(_pointersList isEqualTo []) then { + _pointerType = _pointersList call A3XAI_selectRandom; + if (_isRifle) then {_unit addPrimaryWeaponItem _pointerType} else {_unit addHandGunItem _pointerType}; + //diag_log format ["DEBUG :: Added pointer item %1 to unit %2.",_pointerType,_unit]; + }; +}; + +//Select weapon muzzle +if ((missionNamespace getVariable [("A3XAI_muzzleChance"+_unitLevelString),3]) call A3XAI_chance) then { + _muzzlesList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "MuzzleSlot" >> "compatibleItems"); + if !(_muzzlesList isEqualTo []) then { + _muzzleType = _muzzlesList call A3XAI_selectRandom; + if (_isRifle) then {_unit addPrimaryWeaponItem _muzzleType} else {_unit addHandGunItem _muzzleType}; + //diag_log format ["DEBUG :: Added muzzle item %1 to unit %2.",_muzzleType,_unit]; + }; +}; + +//Select weapon muzzle +if ((missionNamespace getVariable [("A3XAI_underbarrelChance"+_unitLevelString),3]) call A3XAI_chance) then { + _underbarrelList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "UnderBarrelSlot" >> "compatibleItems"); + if !(_underbarrelList isEqualTo []) then { + _underbarrelType = _underbarrelList call A3XAI_selectRandom; + if (_isRifle) then {_unit addPrimaryWeaponItem _underbarrelType} else {_unit addHandGunItem _underbarrelType}; + //diag_log format ["DEBUG :: Added underbarrel item %1 to unit %2.",_underbarrelType,_unit]; + }; +}; + +_gadgetsArray = missionNamespace getVariable ["A3XAI_gadgetsList"+_unitLevelString,[]]; +for "_i" from 0 to ((count _gadgetsArray) - 1) do { + if (((_gadgetsArray select _i) select 1) call A3XAI_chance) then { + _gadget = ((_gadgetsArray select _i) select 0); + _unit addWeapon _gadget; + }; +}; + +//If unit was not given NVGs, give the unit temporary NVGs which will be removed at death. +if (A3XAI_enableTempNVGs && {sunOrMoon < 1}) then { + _unit call A3XAI_addTempNVG; +}; + +//Give unit temporary first aid kits to allow self-healing (unit level 1+) +if (A3XAI_enableHealing) then { + for "_i" from 1 to (_unitLevel min 3) do { + [_unit,FIRST_AID_ITEM_AI] call A3XAI_addItem; + }; +}; + +_unit setVariable ["loadout",_loadout]; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Created loadout for unit %1 (unitLevel: %2): %3.",_unit,_unitLevel,_loadout];}; + +true diff --git a/A3XAI/compile/A3XAI_generateLootOnDeath.sqf b/A3XAI/compile/A3XAI_generateLootOnDeath.sqf new file mode 100644 index 0000000..5061712 --- /dev/null +++ b/A3XAI/compile/A3XAI_generateLootOnDeath.sqf @@ -0,0 +1,47 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unit", "_unitLevel", "_weaponLoot", "_toolLoot", "_pistol", "_magazine", "_toolsArray", "_item", "_loadout", "_primaryWeapon"]; + +_unit = _this select 0; +_unitLevel = _this select 1; + +if (_unit getVariable ["LootGenerated",false]) exitWith {}; +_unit setVariable ["LootGenerated",true]; + +if !(local _unit) then { + waitUntil {uiSleep 1; local _unit}; +}; + +if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Generating loot for AI unit with unitLevel %2.",_unit,_unitLevel];}; + +_weaponLoot = []; +_toolLoot = []; + +_loadout = _unit getVariable ["loadout",[[],[]]]; +_primaryWeapon = [_loadout select 0,0,""] call A3XAI_param; + +//Generate a pistol if one wasn't assigned with loadout script. +if ((getNumber (configFile >> "CfgWeapons" >> _primaryWeapon >> "type")) != 2) then { + _pistol = A3XAI_pistolList call A3XAI_selectRandom; + _magazine = getArray (configFile >> "CfgWeapons" >> _pistol >> "magazines") select 0; + _unit addMagazine _magazine; + _unit addWeapon _pistol; + if (A3XAI_debugLevel > 1) then { + _weaponLoot pushBack _pistol; + _weaponLoot pushBack _magazine + }; +}; + +//Add tool items +_toolsArray = missionNamespace getVariable ["A3XAI_toolsList"+str(_unitLevel),[]]; +{ + _item = _x select 0; + if (((_x select 1) call A3XAI_chance) && {[_item,"weapon"] call A3XAI_checkClassname}) then { + _unit addWeapon _item; + if (A3XAI_debugLevel > 1) then { + _toolLoot pushBack _item; + }; + } +} forEach _toolsArray; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Generated loot for AI death: %1,%2,%3.",_weaponLoot,_toolLoot];}; diff --git a/A3XAI/compile/A3XAI_generateLootPool.sqf b/A3XAI/compile/A3XAI_generateLootPool.sqf new file mode 100644 index 0000000..2a89427 --- /dev/null +++ b/A3XAI/compile/A3XAI_generateLootPool.sqf @@ -0,0 +1,59 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_lootPool","_groupSize"]; + +_unitGroup = _this; + +_lootPool = _unitGroup getVariable ["LootPool",[]]; +_groupSize = _unitGroup getVariable ["GroupSize",0]; +_unitType = _unitGroup getVariable ["unitType",""]; + +if (_unitType != "dynamic") then { + for "_j" from 1 to _groupSize do { + for "_i" from 1 to A3XAI_medicalLootCount do { + // _lootPool pushBack FIRST_AID_ITEM_PLAYER; + _lootPool pushBack (A3XAI_medicalLoot call A3XAI_selectRandom); + }; + + //Add food to loot list + for "_i" from 1 to A3XAI_foodLootCount do { + _lootPool pushBack (A3XAI_foodLoot call A3XAI_selectRandom); + }; + + //Add items to loot list + for "_i" from 1 to A3XAI_miscLootCount do { + _lootPool pushBack (A3XAI_MiscLoot call A3XAI_selectRandom); + }; + + sleep 0.25; + }; +} else { + for "_j" from 1 to _groupSize do { + for "_i" from 1 to A3XAI_medicalLootCount do { + _lootUnit = (units _unitGroup) call A3XAI_selectRandom; + _lootItem = (A3XAI_medicalLoot call A3XAI_selectRandom); + [_lootUnit,_lootItem] call A3XAI_addItem; + }; + + //Add food to randomly chosen unit + for "_i" from 1 to A3XAI_foodLootCount do { + _lootUnit = (units _unitGroup) call A3XAI_selectRandom; + _lootItem = (A3XAI_foodLoot call A3XAI_selectRandom); + [_lootUnit,_lootItem] call A3XAI_addItem; + }; + + //Add items to randomly chosen unit + for "_i" from 1 to A3XAI_miscLootCount do { + _lootUnit = (units _unitGroup) call A3XAI_selectRandom; + _lootItem = (A3XAI_MiscLoot call A3XAI_selectRandom); + [_lootUnit,_lootItem] call A3XAI_addItem; + }; + + sleep 0.25; + }; +}; + +//Update local group loot pool +_unitGroup setVariable ["LootPool",_lootPool]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_generateVehicleLoot.sqf b/A3XAI/compile/A3XAI_generateVehicleLoot.sqf new file mode 100644 index 0000000..2a90211 --- /dev/null +++ b/A3XAI/compile/A3XAI_generateVehicleLoot.sqf @@ -0,0 +1,37 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle", "_unitLevel", "_weapon", "_magazine", "_lootItem"]; + +_vehicle = _this; + +if (isNull _vehicle) exitWith {}; + +_unitLevel = A3XAI_unitLevels call A3XAI_selectRandom; + +for "_i" from 1 to A3XAI_weaponLootVehicleCount do { + _weapon = _unitLevel call A3XAI_getWeapon; + _magazine = getArray (configFile >> "CfgWeapons" >> _weapon >> "magazines") select 0; + _vehicle addWeaponCargoGlobal [_weapon,1]; + _vehicle addMagazineCargoGlobal [_magazine,A3XAI_ammoLootPerWeapon]; +}; + +for "_i" from 1 to A3XAI_foodLootVehicleCount do { + _lootItem = (A3XAI_foodLoot call A3XAI_selectRandom); + _vehicle addItemCargoGlobal [_lootItem,1]; +}; + +for "_i" from 1 to A3XAI_miscLootVehicleCount do { + _lootItem = (A3XAI_miscLoot call A3XAI_selectRandom); + _vehicle addItemCargoGlobal [_lootItem,1]; +}; + +for "_i" from 1 to A3XAI_medicalLootVehicleCount do { + _lootItem = (A3XAI_medicalLoot call A3XAI_selectRandom); + _vehicle addItemCargoGlobal [_lootItem,1]; +}; + +if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Generated loot for AI %1 at %2 with unitLevel %3.",(typeOf _vehicle),(getPosATL _vehicle),_unitLevel]; +}; + +true diff --git a/A3XAI/compile/A3XAI_getAntistuckTime.sqf b/A3XAI/compile/A3XAI_getAntistuckTime.sqf new file mode 100644 index 0000000..94d37fd --- /dev/null +++ b/A3XAI/compile/A3XAI_getAntistuckTime.sqf @@ -0,0 +1,12 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitType"]; + +_unitType = _this; + +call { + if (_unitType in ["static","staticcustom","vehiclecrew","aircustom","landcustom"]) exitWith {ANTISTUCK_CHECK_TIME_INFANTRY}; + if (_unitType in ["air","uav"]) exitWith {ANTISTUCK_CHECK_TIME_AIR}; + if (_unitType in ["land","ugv"]) exitWith {ANTISTUCK_CHECK_TIME_LAND}; + 300 +}; diff --git a/A3XAI/compile/A3XAI_getLocalFunctions.sqf b/A3XAI/compile/A3XAI_getLocalFunctions.sqf new file mode 100644 index 0000000..82bcd7d --- /dev/null +++ b/A3XAI/compile/A3XAI_getLocalFunctions.sqf @@ -0,0 +1 @@ +#include "\A3XAI\globaldefines.hpp" private ["_unitGroup", "_unitType", "_fnc_execEveryLoop", "_fnc_checkUnits", "_fnc_generateLoot", "_fnc_vehicleAmmoFuelCheck", "_fnc_antistuck"]; _unitGroup = _this select 0; _unitType = _this select 1; call { if (_unitType isEqualTo "static") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_infantry; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = A3XAI_antistuck_generic; }; if (_unitType isEqualTo "random") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_infantry; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = A3XAI_antistuck_generic; }; if (_unitType isEqualTo "dynamic") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_infantry; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = A3XAI_antistuck_generic; }; if (_unitType isEqualTo "air") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_air; _fnc_checkUnits = {}; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = A3XAI_checkAmmoFuel; _fnc_antistuck = A3XAI_antistuck_air; }; if (_unitType isEqualTo "land") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_vehicle; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = A3XAI_checkAmmoFuel; _fnc_antistuck = A3XAI_antistuck_land; }; if (_unitType isEqualTo "uav") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_uav; _fnc_checkUnits = {}; _fnc_generateLoot = {}; _fnc_vehicleAmmoFuelCheck = A3XAI_checkAmmoFuel; _fnc_antistuck = A3XAI_antistuck_uav; }; if (_unitType isEqualTo "ugv") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_ugv; _fnc_checkUnits = {}; _fnc_generateLoot = {}; _fnc_vehicleAmmoFuelCheck = A3XAI_checkAmmoFuel; _fnc_antistuck = A3XAI_antistuck_ugv; }; if (_unitType isEqualTo "air_reinforce") exitWith { _fnc_execEveryLoop = {}; _fnc_checkUnits = {}; _fnc_generateLoot = {}; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = {}; }; if (_unitType isEqualTo "infantry_reinforce") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_infantry; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = A3XAI_antistuck_generic; }; if (_unitType isEqualTo "vehiclecrew") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_infantry; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = A3XAI_antistuck_generic; }; if (_unitType isEqualTo "staticcustom") exitWith { _fnc_execEveryLoop = {}; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = A3XAI_antistuck_generic; }; if (_unitType isEqualTo "aircustom") exitWith { _fnc_execEveryLoop = {}; _fnc_checkUnits = {}; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = A3XAI_checkAmmoFuel; _fnc_antistuck = A3XAI_antistuck_aircustom; }; if (_unitType isEqualTo "landcustom") exitWith { _fnc_execEveryLoop = A3XAI_execEveryLoop_vehicle; _fnc_checkUnits = A3XAI_checkGroupUnits; _fnc_generateLoot = A3XAI_generateGroupLoot; _fnc_vehicleAmmoFuelCheck = A3XAI_checkAmmoFuel; _fnc_antistuck = A3XAI_antistuck_generic; }; _fnc_execEveryLoop = {}; _fnc_checkUnits = {}; _fnc_generateLoot = {}; _fnc_vehicleAmmoFuelCheck = {}; _fnc_antistuck = {}; diag_log format ["A3XAI Warning: Group functions for unit type %1 not found.",_unitType]; }; [_fnc_execEveryLoop,_fnc_checkUnits,_fnc_generateLoot,_fnc_vehicleAmmoFuelCheck,_fnc_antistuck] \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_getNoAggroStatus.sqf b/A3XAI/compile/A3XAI_getNoAggroStatus.sqf new file mode 100644 index 0000000..51bc8ce --- /dev/null +++ b/A3XAI/compile/A3XAI_getNoAggroStatus.sqf @@ -0,0 +1,9 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_status"]; + +_unitGroup = _this; + +_status = _unitGroup getVariable ["NoAggroStatus",false]; + +_status diff --git a/A3XAI/compile/A3XAI_getPosBetween.sqf b/A3XAI/compile/A3XAI_getPosBetween.sqf new file mode 100644 index 0000000..6f9a22a --- /dev/null +++ b/A3XAI/compile/A3XAI_getPosBetween.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_pos1", "_pos2", "_direction", "_posBetween", "_distance"]; + +_pos1 = _this select 0; +_pos2 = _this select 1; +_distance = _this select 2; + +if ((typeName _pos1) isEqualTo "OBJECT") then {_pos1 = getPosATL _pos1}; +if ((typeName _pos2) isEqualTo "OBJECT") then {_pos2 = getPosATL _pos2}; + +_direction = [_pos1,_pos2] call BIS_fnc_dirTo; +_posBetween = [_pos1, _distance, _direction] call BIS_fnc_relPos; + +_posBetween \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_getSafePosReflected.sqf b/A3XAI/compile/A3XAI_getSafePosReflected.sqf new file mode 100644 index 0000000..4fd3596 --- /dev/null +++ b/A3XAI/compile/A3XAI_getSafePosReflected.sqf @@ -0,0 +1,22 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_originPos", "_noAggroArea", "_posReflected", "_locationPos", "_locationSize", "_direction","_noAggroRange"]; + +_originPos = _this select 0; //origin +_noAggroRange = _this select 1; + +if ((typeName _originPos) isEqualTo "OBJECT") then {_originPos = getPosATL _originPos}; + +_posReflected = []; +_noAggroArea = [_originPos,_noAggroRange] call A3XAI_returnNoAggroAreaAll; + +if !(isNull _noAggroArea) then { + // _locationPos = locationPosition _noAggroArea; + _locationPos = getPosATL _noAggroArea; + _locationSize = ((size _noAggroArea) select 0) + 300; + _direction = [_locationPos,_originPos] call BIS_fnc_dirTo; + _posReflected = [_locationPos, _locationSize,_direction] call BIS_fnc_relPos; + if ((surfaceIsWater _posReflected) or {[_posReflected,_noAggroRange] call A3XAI_checkInNoAggroArea}) then {_posReflected = []}; +}; + +_posReflected diff --git a/A3XAI/compile/A3XAI_getSpawnParams.sqf b/A3XAI/compile/A3XAI_getSpawnParams.sqf new file mode 100644 index 0000000..c9602db --- /dev/null +++ b/A3XAI/compile/A3XAI_getSpawnParams.sqf @@ -0,0 +1,30 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_position","_nearestLocations", "_nearestLocationType", "_spawnParams","_locationName","_nearestLocation"]; + +_position = _this; +_nearestLocations = nearestLocations [_position,["NameCityCapital","NameCity","NameVillage","NameLocal"],1000]; + +if !(_nearestLocations isEqualTo []) then { + _nearestLocation = (_nearestLocations select 0); + _nearestLocationType = type _nearestLocation; + _locationName = (text _nearestLocation) + " " + str (_position); +} else { + _nearestLocationType = "wilderness"; + _locationName = format ["Wilderness %1",_position]; +}; +_spawnParams = call { + if (_nearestLocationType isEqualTo "NameCityCapital") exitWith {[A3XAI_minAI_capitalCity,A3XAI_addAI_capitalCity,A3XAI_unitLevel_capitalCity,A3XAI_spawnChance_capitalCity]}; + if (_nearestLocationType isEqualTo "NameCity") exitWith {[A3XAI_minAI_city,A3XAI_addAI_city,A3XAI_unitLevel_city,A3XAI_spawnChance_city]}; + if (_nearestLocationType isEqualTo "NameVillage") exitWith {[A3XAI_minAI_village,A3XAI_addAI_village,A3XAI_unitLevel_village,A3XAI_spawnChance_village]}; + if (_nearestLocationType isEqualTo "NameLocal") exitWith {[A3XAI_minAI_remoteArea,A3XAI_addAI_remoteArea,A3XAI_unitLevel_remoteArea,A3XAI_spawnChance_remoteArea]}; + + [A3XAI_minAI_wilderness,A3XAI_addAI_wilderness,A3XAI_unitLevel_wilderness,A3XAI_spawnChance_wilderness] //Default +}; + +_spawnParams pushBack _locationName; +_spawnParams pushBack _nearestLocationType; + +// diag_log format ["%1 got spawn params %2",__FILE__,_spawnParams]; + +_spawnParams \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_getUnitLevel.sqf b/A3XAI/compile/A3XAI_getUnitLevel.sqf new file mode 100644 index 0000000..4d195fc --- /dev/null +++ b/A3XAI/compile/A3XAI_getUnitLevel.sqf @@ -0,0 +1,14 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_indexWeighted","_unitLevelIndexTable"]; +_unitLevelIndexTable = _this; + +_indexWeighted = call { + if (_unitLevelIndexTable isEqualTo "airvehicle") exitWith {A3XAI_levelIndicesAir}; + if (_unitLevelIndexTable isEqualTo "landvehicle") exitWith {A3XAI_levelIndicesLand}; + if (_unitLevelIndexTable isEqualTo "uav") exitWith {A3XAI_levelIndicesUAV}; + if (_unitLevelIndexTable isEqualTo "ugv") exitWith {A3XAI_levelIndicesUGV}; + [0] +}; + +A3XAI_unitLevels select (_indexWeighted call A3XAI_selectRandom) \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_getWeapon.sqf b/A3XAI/compile/A3XAI_getWeapon.sqf new file mode 100644 index 0000000..53012d9 --- /dev/null +++ b/A3XAI/compile/A3XAI_getWeapon.sqf @@ -0,0 +1,16 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitLevel", "_weaponIndices", "_weaponList", "_weaponsList", "_weaponSelected", "_weaponListLeveled"]; + +_unitLevel = _this; + +_weaponIndices = missionNamespace getVariable ["A3XAI_weaponTypeIndices"+str(_unitLevel),[0,1,2,3]]; +_weaponList = ["A3XAI_pistolList","A3XAI_rifleList","A3XAI_machinegunList","A3XAI_sniperList"] select (_weaponIndices call A3XAI_selectRandom); +_weaponListLeveled = _weaponList+str(_unitLevel); +_weaponsList = missionNamespace getVariable _weaponListLeveled; +if (isNil "_weaponsList") then {_weaponsList = missionNamespace getVariable _weaponList;}; +_weaponSelected = _weaponsList call A3XAI_selectRandom; + +//diag_log format ["Debug: Generated weapon %1",_weaponSelected]; + +_weaponSelected \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDamageHeli.sqf b/A3XAI/compile/A3XAI_handleDamageHeli.sqf new file mode 100644 index 0000000..d0d87d6 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDamageHeli.sqf @@ -0,0 +1,77 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_object","_hit","_damage","_source","_ammo","_partdamage","_durability","_objectGroup","_currentDamage","_hitPoint"]; + +_object = _this select 0; //Object the event handler is assigned to. (the unit taking damage) +_hit = _this select 1; //Name of the selection where the unit was damaged. "" for over-all structural damage, "?" for unknown selections. +_damage = _this select 2; //Resulting level of damage for the selection. (Received damage) +_source = _this select 3; //The source unit that caused the damage. +_ammo = _this select 4; //Classname of the projectile that caused inflicted the damage. ("" for unknown, such as falling damage.) +_hitPartIndex = _this select 5; //Hit part index of the hit point, -1 otherwise. + +_hitPoint = (_object getHitIndex _hitPartIndex); +if (_damage > _hitPoint) then { + if (isNull _source) exitWith {_damage = _hitPoint;}; //No physics damage + if ((group _object) call A3XAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; + + _durability = _object getVariable "durability"; + if (isNil "_durability") then { + _object setVariable ["durability",[0,0,0,0]]; + _durability = _object getVariable "durability"; + }; + + if ((side _source) != A3XAI_side) then { + _destroyed = false; + _disabled = false; + call { + if (_hit isEqualTo "hull_hit") exitWith { + //Structural damage + _currentDamage = (_durability select 0); + _partdamage = _currentDamage + (_damage - _currentDamage); + _durability set [0,_partdamage]; + if ((_partdamage > 0.9) && {alive _object}) then { + _damage = _hitPoint; + _destroyed = true; + _disabled = true; + }; + }; + if (_hit in ["engine_hit","engine_1_hit","engine_2_hit","engine_3_hit","engine_4_hit"]) exitWith { + _currentDamage = (_durability select 1); + _partdamage =_currentDamage + (_damage - _currentDamage); + _durability set [1,_partdamage]; + if ((_partdamage > 0.9) && {alive _object}) then { + _damage = _hitPoint; + _destroyed = true; + _disabled = true; + }; + }; + if (_hit in ["tail_rotor_hit","main_rotor_hit","main_rotor_1_hit","main_rotor_2_hit"]) exitWith { + _currentDamage = (_durability select 2); + _partdamage = _currentDamage + (_damage - _currentDamage); + _durability set [2,_partdamage]; + if ((_partdamage > 0.9)&& {alive _object}) then { + { + _object setHit [_x,1]; + } forEach ["tail_rotor_hit","main_rotor_hit","main_rotor_1_hit","main_rotor_2_hit"]; + _destroyed = false; + _disabled = true; + }; + }; + if (_hit isEqualTo "fuel_hit") exitWith {_damage = _hitPoint}; + }; + if (_disabled) then { + 0 = [_object] call A3XAI_heliEvacuated; + //{_object removeAllEventHandlers _x} forEach ["HandleDamage","GetOut","Killed","Hit"]; + if (_destroyed) then { + _nul = _object spawn { + uiSleep 3; + _this setVehicleAmmo 0; + _this setFuel 0; + _this setDamage 1; + }; + }; + }; + }; +}; + +_damage diff --git a/A3XAI/compile/A3XAI_handleDamageUGV.sqf b/A3XAI/compile/A3XAI_handleDamageUGV.sqf new file mode 100644 index 0000000..d76eeb9 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDamageUGV.sqf @@ -0,0 +1,22 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_object","_hit","_damage","_source","_ammo","_hitPoint"]; + +_object = _this select 0; //Object the event handler is assigned to. (the unit taking damage) +_hit = _this select 1; //Name of the selection where the unit was damaged. "" for over-all structural damage, "?" for unknown selections. +_damage = _this select 2; //Resulting level of damage for the selection. (Received damage) +_source = _this select 3; //The source unit that caused the damage. +//_ammo = _this select 4; //Classname of the projectile that caused inflicted the damage. ("" for unknown, such as falling damage.) +_hitPartIndex = _this select 5; //Hit part index of the hit point, -1 otherwise. + +_hitPoint = (_object getHitIndex _hitPartIndex); +if (_damage > _hitPoint) then { + call { + if (isNull _source) exitWith {_damage = _hitPoint;}; //No physics damage + if ((group _object) call A3XAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; + if ((side _source) isEqualTo A3XAI_side) exitWith {_damage = _hitPoint;}; + if ((_hit find "wheel") > -1) exitWith {_damage = _hitPoint;}; + }; +}; + +_damage \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDamageUnit.sqf b/A3XAI/compile/A3XAI_handleDamageUnit.sqf new file mode 100644 index 0000000..99bddf5 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDamageUnit.sqf @@ -0,0 +1,27 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_object","_hit","_damage","_source","_ammo","_hitPartIndex","_hitPoint"]; + +_object = _this select 0; //Object the event handler is assigned to. (the unit taking damage) +_hit = _this select 1; //Name of the selection where the unit was damaged. "" for over-all structural damage, "?" for unknown selections. +_damage = _this select 2; //Resulting level of damage for the selection. (Received damage) +_source = _this select 3; //The source unit that caused the damage. +_ammo = _this select 4; //Classname of the projectile that caused inflicted the damage. ("" for unknown, such as falling damage.) +_hitPartIndex = _this select 5; //Hit part index of the hit point, -1 otherwise. + +_hitPoint = (_object getHitIndex _hitPartIndex); +if (_damage > _hitPoint) then { + call { + if (isNull _source) exitWith {_damage = _hitPoint;}; //No physics damage + if ((group _object) call A3XAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; //No damage from any source when non-hostile + if ((side _source) isEqualTo A3XAI_side) exitWith {_damage = _hitPoint;}; //No damage from units on same side + if ((!isNull (objectParent _source)) && {_ammo isEqualTo ""}) then { //No damage if source is a vehicle and damage has no ammo (vehicle collision) + call { + if (A3XAI_noCollisionDamage) exitWith {_damage = _hitPoint;}; + if ((_damage >= 0.9) && {_hit in ["","body","head"]} && {_hitPartIndex > -1}) exitWith {_object setVariable ["CollisionKilled",A3XAI_roadKillPenalty];}; + }; + }; + }; +}; + +_damage diff --git a/A3XAI/compile/A3XAI_handleDamageVeh.sqf b/A3XAI/compile/A3XAI_handleDamageVeh.sqf new file mode 100644 index 0000000..b897510 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDamageVeh.sqf @@ -0,0 +1,25 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_object","_hit","_damage","_source","_ammo","_hitPoint"]; + +_object = _this select 0; //Object the event handler is assigned to. (the unit taking damage) +_hit = _this select 1; //Name of the selection where the unit was damaged. "" for over-all structural damage, "?" for unknown selections. +_damage = _this select 2; //Resulting level of damage for the selection. (Received damage) +_source = _this select 3; //The source unit that caused the damage. +//_ammo = _this select 4; //Classname of the projectile that caused inflicted the damage. ("" for unknown, such as falling damage.) +_hitPartIndex = _this select 5; //Hit part index of the hit point, -1 otherwise. + +_hitPoint = (_object getHitIndex _hitPartIndex); +if (_damage > _hitPoint) then { + call { + if (isNull _source) exitWith {_damage = _hitPoint;}; //No physics damage + if ((group _object) call A3XAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; + if ((side _source) isEqualTo A3XAI_side) exitWith {_damage = _hitPoint;}; + if (((_hit find "wheel") > -1) && {_damage > 0.8} && {!(_object getVariable ["VehicleDisabled",false])}) exitWith { + [_object,true] call A3XAI_vehDestroyed; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI vehicle %1 (%2) is immobilized. Respawning vehicle patrol group.",_object,(typeOf _object)];}; + }; + }; +}; + +_damage \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDeathEvent.sqf b/A3XAI/compile/A3XAI_handleDeathEvent.sqf new file mode 100644 index 0000000..94568aa --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeathEvent.sqf @@ -0,0 +1,112 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_victim", "_killer", "_vehicle", "_unitGroup", "_victimPos", "_unitsAlive", "_groupIsEmpty", "_unitType", "_unitLevel", "_groupSize", "_fnc_deathHandler", +"_launcherItem", "_bodyPosEmpty", "_collisionKilled"]; + +_victim = _this select 0; +_killer = _this select 1; + +if (_victim getVariable ["deathhandled",false]) exitWith {}; +_victim setVariable ["deathhandled",true]; + +_vehicle = (objectParent _victim); +_unitGroup = (group _victim); +_victimPos = (getPosATL _victim); + +{_victim removeAllEventHandlers _x} count ["Killed","HandleDamage","Local","Hit"]; +_victim setDamage 1; +_victim removeItems FIRST_AID_ITEM_AI; +_victim removeWeapon NVG_ITEM_AI; + +//Check number of units alive, preserve group immediately if empty. +_unitsAlive = ({alive _x} count (units _unitGroup)); +_groupIsEmpty = if (_unitsAlive isEqualTo 0) then {_unitGroup call A3XAI_protectGroup; true} else {false}; + +//Retrieve group type +_unitType = _unitGroup getVariable ["unitType",""]; +_unitLevel = _unitGroup getVariable ["unitLevel",0]; + +//Update group size counter +_groupSize = (_unitGroup getVariable ["GroupSize",0]); +if (_groupSize > 0) then { + _unitGroup setVariable ["GroupSize",(_groupSize - 1)]; +}; + +_fnc_deathHandler = missionNamespace getVariable [format ["A3XAI_handleDeath_%1",_unitType],{diag_log format ["A3XAI Error: Death handler not found for unit type %1",_unitType];}]; +[_victim,_killer,_unitGroup,_groupIsEmpty] call _fnc_deathHandler; +if (_unitType in ["static","staticcustom","vehiclecrew","dynamic","random","air","aircustom","air_reinforce","land","landcustom"]) then { + 0 = [_victim,_killer,_unitGroup,_unitType,_unitsAlive] call A3XAI_handleDeath_generic; +} else { + call { + if (_unitType isEqualTo "aircrashed") exitWith {}; + if (_groupIsEmpty) then { + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; + }; + }; + }; +}; + +if !(isNull _victim) then { + { + if (_x in A3XAI_launcherTypes) exitWith { + if (_x in (weapons _victim)) then { + _victim removeWeapon _x; + } else { + _launcherItem = _x; + { + if (_launcherItem in weaponCargo _x) exitWith { + deleteVehicle _x; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Deleted WeaponHolderSimulated containing launcher %1.",_launcherItem];}; + }; + } forEach (_victimPos nearObjects ["WeaponHolderSimulated",10]); + }; + }; + } forEach ((_victim getVariable ["loadout",[[],[]]]) select 0); + + { + if (_forEachIndex > 0) then { + _victim removeMagazines _x; + }; + } forEach ((_victim getVariable ["loadout",[[],[]]]) select 1); + + if !(isNull _vehicle) then { + _bodyPosEmpty = _victimPos findEmptyPosition [0,2,DEFAULT_UNIT_CLASSNAME]; + if (_bodyPosEmpty isEqualTo []) then { + _victim setPosATL _victimPos; + } else { + _victim setPosATL _bodyPosEmpty; + }; + _victim setVelocity [0,0,0.25]; + }; + + if (isPlayer _killer) then { + _collisionKilled = _victim getVariable ["CollisionKilled",false]; + if (isDedicated) then { + _nul = [_killer,_victim,_collisionKilled] spawn A3XAI_sendKillResponse; + } else { + A3XAI_sendKillResponse_PVS = [_killer,_victim,_collisionKilled]; + publicVariableServer "A3XAI_sendKillResponse_PVS"; + }; + }; + + if (isDedicated) then { + if ((isPlayer _killer) && {_unitGroup getVariable ["ReinforceAvailable",false]} && {(missionNamespace getVariable [format ["A3XAI_airReinforcementSpawnChance%1",_unitLevel],0]) call A3XAI_chance}) then { + _unitGroup setVariable ["ReinforceAvailable",false]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 (Level %2) is calling reinforcements.",_unitGroup,_unitLevel];}; + _nul = [_victimPos,_killer,_unitLevel] spawn A3XAI_spawn_reinforcement; + }; + _victim setVariable ["A3XAI_deathTime",diag_tickTime]; + } else { + A3XAI_registerDeath_PVS = [_victim,_killer,_unitGroup]; + publicVariableServer "A3XAI_registerDeath_PVS"; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 AI unit %2 killed by %3, %4 units left alive in group %5.",_unitType,_victim,_killer,_unitsAlive,_unitGroup];}; +} else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: AI unit %1 killed by %2 is null.",_victim,_killer];}; +}; + +_victim diff --git a/A3XAI/compile/A3XAI_handleDeath_air.sqf b/A3XAI/compile/A3XAI_handleDeath_air.sqf new file mode 100644 index 0000000..9fc52fd --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_air.sqf @@ -0,0 +1,28 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_victim","_unitGroup","_parachuted"]; + +_victim = _this select 0; +_unitGroup = _this select 2; + +_vehicle = (_unitGroup getVariable ["assignedVehicle",objNull]); +//diag_log format ["Debug: %1 assigned vehicle is %2.",_unitGroup,_vehicle]; +if (alive _vehicle) then { + //diag_log format ["Debug: %1 assigned vehicle %2 is alive.",_victim,_unitGroup,_vehicle]; + if (_victim getVariable ["isDriver",false]) then { + //diag_log format ["Debug: %1 is driver of %2 assigned vehicle %3",_unitGroup,_vehicle]; + if !((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then {[_unitGroup,"aircrashed"] call A3XAI_setUnitType;}; + _parachuted = [_vehicle,_unitGroup] call A3XAI_heliEvacuated; + if (_parachuted) then { + _nul = _vehicle spawn { + _this setFuel 0; + _this setVehicleAmmo 0; + uiSleep 2.5; + _this setDamage 1; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI %1 pilot unit %2 was killed, ejecting surviving crew.",(typeOf _vehicle),(typeOf _victim)];}; + }; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_handleDeath_air_reinforce.sqf b/A3XAI/compile/A3XAI_handleDeath_air_reinforce.sqf new file mode 100644 index 0000000..159e07b --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_air_reinforce.sqf @@ -0,0 +1 @@ +#include "A3XAI_handleDeath_air.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDeath_aircrashed.sqf b/A3XAI/compile/A3XAI_handleDeath_aircrashed.sqf new file mode 100644 index 0000000..159e07b --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_aircrashed.sqf @@ -0,0 +1 @@ +#include "A3XAI_handleDeath_air.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDeath_aircustom.sqf b/A3XAI/compile/A3XAI_handleDeath_aircustom.sqf new file mode 100644 index 0000000..159e07b --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_aircustom.sqf @@ -0,0 +1 @@ +#include "A3XAI_handleDeath_air.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDeath_dynamic.sqf b/A3XAI/compile/A3XAI_handleDeath_dynamic.sqf new file mode 100644 index 0000000..6357eb7 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_dynamic.sqf @@ -0,0 +1,24 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_victim","_killer","_groupIsEmpty","_trigger","_unitGroup"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; +_groupIsEmpty = _this select 3; + +if (_groupIsEmpty) then { + if (isDedicated) then { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; + [_trigger,true] spawn A3XAI_despawn_dynamic; //force despawning even if players are present in trigger area. + } else { + A3XAI_despawnDynamicGroup_PVS = _unitGroup; + publicVariableServer "A3XAI_despawnDynamicGroup_PVS"; + }; +} else { + if ((A3XAI_enableFindKiller) && {(combatMode _unitGroup) isEqualTo "YELLOW"}) then { + 0 = [_killer,_unitGroup] spawn A3XAI_huntKiller; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_handleDeath_generic.sqf b/A3XAI/compile/A3XAI_handleDeath_generic.sqf new file mode 100644 index 0000000..7448c50 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_generic.sqf @@ -0,0 +1,39 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_victim","_killer","_unitGroup","_unitType","_unitsAlive"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; +_unitType = _this select 3; +_unitsAlive = _this select 4; + +try { + + if (isPlayer _killer) then { + if (A3XAI_cleanupDelay isEqualTo 0) then { + throw format ["A3XAI Debug: Clearing gear for %1 (cleanupDelay = 0)",_victim]; + }; + if (_victim getVariable ["CollisionKilled",false]) then { + throw format ["A3XAI Debug: %1 AI unit %2 was killed by collision damage caused by %3. Unit gear cleared.",_unitType,_victim,_killer]; + }; + _unitLevel = _unitGroup getVariable ["unitLevel",1]; + if (isDedicated) then { + 0 = [_victim,_unitLevel] spawn A3XAI_generateLootOnDeath; + } else { + A3XAI_generateLootOnDeath_PVS = [_victim,_unitLevel]; + publicVariableServer "A3XAI_generateLootOnDeath_PVS"; + }; + } else { + if (_killer isEqualTo _victim) then { + throw format ["A3XAI Debug: %1 AI unit %2 was killed by non-player. Unit gear cleared.",_unitType,_victim]; + }; + }; +} catch { + _victim call A3XAI_purgeUnitGear; + if (A3XAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_handleDeath_land.sqf b/A3XAI/compile/A3XAI_handleDeath_land.sqf new file mode 100644 index 0000000..cf5f1ac --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_land.sqf @@ -0,0 +1,67 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_victim","_killer","_vehicle","_unitGroup","_groupIsEmpty"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; +_groupIsEmpty = _this select 3; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; + +//diag_log format ["%1 params: %2",__FILE__,_this]; + +if (_groupIsEmpty) then { + if (_vehicle isKindOf "LandVehicle") then { + [_vehicle,true] call A3XAI_respawnAIVehicle; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI vehicle patrol destroyed, adding vehicle %1 to cleanup queue.",(typeOf _vehicle)];}; + }; + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; + }; +} else { + private ["_groupUnits","_newDriver","_unit"]; + _groupUnits = (units _unitGroup) - [_victim,gunner _vehicle]; + _groupSize = _unitGroup getVariable ["GroupSize",(count _groupUnits)]; + if (_groupSize > 1) then { + if (_victim getVariable ["isDriver",false]) then { + _newDriver = _groupUnits call A3XAI_selectRandom; //Find another unit to serve as driver (besides the gunner) + _nul = [_newDriver,_vehicle] spawn { + private ["_newDriver","_vehicle"]; + _newDriver = _this select 0; + _vehicle = _this select 1; + unassignVehicle _newDriver; + _newDriver assignAsDriver _vehicle; + if (_newDriver in _vehicle) then { + _newDriver moveInDriver _vehicle; + }; + [_newDriver] orderGetIn true; + _newDriver setVariable ["isDriver",true]; + if !(isDedicated) then { + A3XAI_setDriverUnit_PVS = _newDriver; + publicVariableServer "A3XAI_setDriverUnit_PVS"; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Replaced driver unit for group %1 vehicle %2.",(group _newDriver),(typeOf _vehicle)];}; + }; + }; + } else { + { + if (alive _x) then { + _forceOut = ((speed _vehicle) < 5); + if !((gunner _vehicle) isEqualTo _x) then { + unassignVehicle _x; + [_x] orderGetIn false; + if (_forceOut) then { + _x action ["eject",_vehicle]; + }; + }; + [_vehicle,true] call A3XAI_vehDestroyed; + }; + } forEach _groupUnits; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 vehicle %2 has single unit remaining. Adding patrol to respawn queue.",_unitGroup,(typeOf _vehicle)];}; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_handleDeath_landcustom.sqf b/A3XAI/compile/A3XAI_handleDeath_landcustom.sqf new file mode 100644 index 0000000..30db64b --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_landcustom.sqf @@ -0,0 +1 @@ +#include "A3XAI_handleDeath_land.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDeath_random.sqf b/A3XAI/compile/A3XAI_handleDeath_random.sqf new file mode 100644 index 0000000..965e0ba --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_random.sqf @@ -0,0 +1,24 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_victim","_killer","_groupIsEmpty","_trigger","_unitGroup"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; +_groupIsEmpty = _this select 3; + +if (_groupIsEmpty) then { + if (isDedicated) then { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; + [_trigger,true] spawn A3XAI_despawn_random; //force despawning even if players are present in trigger area. + } else { + A3XAI_despawnRandomGroup_PVS = _unitGroup; + publicVariableServer "A3XAI_despawnRandomGroup_PVS"; + }; +} else { + if ((A3XAI_enableFindKiller) && {(combatMode _unitGroup) isEqualTo "YELLOW"}) then { + 0 = [_killer,_unitGroup] spawn A3XAI_huntKiller; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_handleDeath_static.sqf b/A3XAI/compile/A3XAI_handleDeath_static.sqf new file mode 100644 index 0000000..bf267b6 --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_static.sqf @@ -0,0 +1,44 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_victim","_killer","_unitGroup","_trigger","_dummy","_groupIsEmpty"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; +_groupIsEmpty = _this select 3; + +_trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; + +if (_groupIsEmpty) then { + if (_trigger getVariable ["respawn",true]) then { + _respawnCount = _trigger getVariable ["respawnLimit",-1]; + if !(_respawnCount isEqualTo 0) then { + [0,_trigger,_unitGroup] call A3XAI_addRespawnQueue; //If there are still respawns possible... respawn the group + } else { + if (isDedicated) then { + _trigger setVariable ["permadelete",true]; //deny respawn and delete trigger on next despawn. + } else { + A3XAI_setPermaDeleteSpawn_PVS = _unitGroup; + publicVariableServer "A3XAI_setPermaDeleteSpawn_PVS"; + }; + }; + } else { + if (isDedicated) then { + _nul = _trigger spawn A3XAI_deleteCustomSpawn; + } else { + A3XAI_deleteCustomSpawn_PVS = _unitGroup; + publicVariableServer "A3XAI_deleteCustomSpawn_PVS"; + }; + }; +} else { + if ((A3XAI_enableFindKiller) && {(combatMode _unitGroup) isEqualTo "YELLOW"}) then { + 0 = [_killer,_unitGroup] spawn A3XAI_huntKiller; + }; + if (!(_trigger getVariable ["respawn",true])) then { + _maxUnits = _trigger getVariable ["maxUnits",[0,0]]; //Reduce maximum AI for spawn trigger for each AI killed for non-respawning spawns. + _maxUnits set [0,(_maxUnits select 0) - 1]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: MaxUnits variable for group %1 set to %2.",_unitGroup,_maxUnits];}; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_handleDeath_staticcustom.sqf b/A3XAI/compile/A3XAI_handleDeath_staticcustom.sqf new file mode 100644 index 0000000..f7c43bb --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_staticcustom.sqf @@ -0,0 +1 @@ +#include "A3XAI_handleDeath_static.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_handleDeath_vehiclecrew.sqf b/A3XAI/compile/A3XAI_handleDeath_vehiclecrew.sqf new file mode 100644 index 0000000..e6ac39c --- /dev/null +++ b/A3XAI/compile/A3XAI_handleDeath_vehiclecrew.sqf @@ -0,0 +1,2 @@ +#include "A3XAI_handleDeath_static.sqf" + diff --git a/A3XAI/compile/A3XAI_handle_death_UV.sqf b/A3XAI/compile/A3XAI_handle_death_UV.sqf new file mode 100644 index 0000000..f970a19 --- /dev/null +++ b/A3XAI/compile/A3XAI_handle_death_UV.sqf @@ -0,0 +1,45 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_victim","_killer","_unitGroup","_unitType","_launchWeapon","_launchAmmo","_groupIsEmpty","_unitsAlive","_vehicle","_groupSize","_newGroupSize","_fnc_deathHandler","_unitLevel","_bodyPos","_bodyPosEmpty"]; + +_victim = _this select 0; +_killer = _this select 1; + +if (_victim getVariable ["deathhandled",false]) exitWith {}; +_victim setVariable ["deathhandled",true]; + +_vehicle = (vehicle _victim); +_unitGroup = (group _victim); + +{_victim removeAllEventHandlers _x} count ["Killed","HandleDamage","Local","Hit"]; +_victim setDamage 1; + +//Check number of units alive, preserve group immediately if empty. +_unitsAlive = ({alive _x} count (units _unitGroup)); +_groupIsEmpty = if (_unitsAlive isEqualTo 0) then {_unitGroup call A3XAI_protectGroup; true} else {false}; + +//Retrieve group type +_unitType = _unitGroup getVariable ["unitType",""]; +_unitLevel = _unitGroup getVariable ["unitLevel",0]; + +//Update group size counter +_groupSize = (_unitGroup getVariable ["GroupSize",0]); +if (_groupSize > 0) then { + _newGroupSize = (_groupSize - 1); + _unitGroup setVariable ["GroupSize",_newGroupSize]; +}; + +if !(isNull _victim) then { + if (isDedicated) then { + // _victim setVariable ["A3XAI_deathTime",diag_tickTime]; //Delete ASAP + _victim setVariable ["A3XAI_deathTime",diag_tickTime]; + } else { + A3XAI_registerDeath_PVS = [_victim,_killer,_unitGroup]; + publicVariableServer "A3XAI_registerDeath_PVS"; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 AI unit %2 killed by %3, %4 units left alive in group %5.",_unitType,_victim,_killer,_unitsAlive,_unitGroup];}; +} else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI unit %1 killed by %2 is null.",_victim,_killer];}; +}; + +_victim diff --git a/A3XAI/compile/A3XAI_hasLOS.sqf b/A3XAI/compile/A3XAI_hasLOS.sqf new file mode 100644 index 0000000..4fbde29 --- /dev/null +++ b/A3XAI/compile/A3XAI_hasLOS.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_begin", "_end", "_ignore1", "_ignore2"]; + +_begin = _this select 0; +_end = _this select 1; +_ignore1 = [_this,2,objNull] call A3XAI_param; +_ignore2 = [_this,3,objNull] call A3XAI_param; + +if ((typeName _begin) == "OBJECT") then { + _begin = getPosASL _begin; + _begin set [2,(_begin select 2) + 1.7]; +}; + +if ((typeName _end) == "OBJECT") then { + _end = getPosASL _end; + _end set [2,(_end select 2) + 1.7]; +}; + +!(lineIntersects [_begin,_end,_ignore1,_ignore2]) \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_HCMonitor.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_HCMonitor.sqf new file mode 100644 index 0000000..7b9472f --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_HCMonitor.sqf @@ -0,0 +1,27 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_currentTime", "_monitorReport", "_getUptime", "_currentSec", "_outHour", "_outMin", "_outSec", "_uptime"]; + +_currentTime = diag_tickTime; +_monitorReport = _currentTime; + +_getUptime = { + private ["_currentSec","_outSec","_outMin","_outHour"]; + _currentSec = diag_tickTime; + _outHour = floor (_currentSec/3600); + _outMin = floor ((_currentSec - (_outHour*3600))/60); + _outSec = floor (_currentSec - (_outHour*3600) - (_outMin*60)); + + [_outHour,_outMin,_outSec] +}; + +while {true} do { + _currentTime = diag_tickTime; + + if ((A3XAI_monitorReportRate > 0) && {((_currentTime - _monitorReport) > A3XAI_monitorReportRate)}) then { + _uptime = [] call _getUptime; + diag_log format ["A3XAI Monitor: Uptime: %1:%2:%3. FPS: %4. HC Groups: %5.",_uptime select 0, _uptime select 1, _uptime select 2,round(diag_fps),A3XAI_HCGroupsCount]; + _monitorReport = _currentTime; + }; + uiSleep 30; +}; diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_addHunterGroup.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_addHunterGroup.sqf new file mode 100644 index 0000000..76fa75e --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_addHunterGroup.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_targetPlayer"]; + +_unitGroup = _this select 0; +_targetPlayer = _this select 1; + +_unitGroup setVariable ["targetPlayer",_targetPlayer]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_addNewGroup.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_addNewGroup.sqf new file mode 100644 index 0000000..4485c35 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_addNewGroup.sqf @@ -0,0 +1,65 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_anchor", "_unitType", "_groupSize", "_functionCall", "_check", "_lootPool","_miscData1","_miscData2","_anchorType"]; + +_unitGroup = _this select 0; +_unitLevel = _this select 1; +_anchor = _this select 2; +_unitType = _this select 3; +_groupSize = _this select 4; +_lootPool = _this select 5; +_miscData1 = if ((count _this) > 6) then {_this select 6}; +_miscData2 = if ((count _this) > 7) then {_this select 7}; + +call { + _anchorType = (typeName _anchor); + if (_anchorType isEqualTo "ARRAY") exitWith { + _anchor = TRIGGER_OBJECT createVehicleLocal _anchor; + _unitGroup setVariable ["trigger",_anchor]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Created group trigger for %1 group %2.",_unitType,_unitGroup];}; + }; + if (_anchorType isEqualTo "OBJECT") exitWith { + _unitGroup setVariable ["assignedVehicle",_anchor]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 group %2 has assigned vehicle %3 (%4).",_unitType,_unitGroup,_anchor,(typeOf _anchor)];}; + }; + _anchor = objNull; +}; + +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setVariable ["GroupSize",_groupSize]; + +[_unitGroup,_unitType] call A3XAI_setUnitType; +// _unitGroup call A3XAI_initNoAggroStatus; + +if !(_lootPool isEqualTo []) then {_unitGroup setVariable ["LootPool",_lootPool];}; + +if !(isNil "_miscData1") then { + call { + if (_unitType isEqualTo "dynamic") exitWith { + _unitGroup setVariable ["targetplayer",_miscData1]; + _anchor setVariable ["targetplayer",_miscData1]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 group %2 has target player %3.",_unitType,_unitGroup,_miscData1];}; + }; + }; +}; + +if (!isNil "_miscData2") then { + +}; + +_functionCall = missionNamespace getVariable ["A3XAI_handle"+_unitType,{false}]; +_check = _unitGroup call _functionCall; + +if (A3XAI_debugLevel > 0) then { + if (_check) then { + diag_log format ["A3XAI Debug: HC received new group from server: %1. Processing new group with function %2.",_unitGroup,("A3XAI_handle"+_unitType)]; + } else { + diag_log format ["A3XAI Debug: Function %1 not found.","A3XAI_handle"+_unitType] + }; +}; + +0 = [_unitGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; + +A3XAI_HCGroupsCount = A3XAI_HCGroupsCount + 1; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_airReinforcementDetection.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_airReinforcementDetection.sqf new file mode 100644 index 0000000..f2d71d9 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_airReinforcementDetection.sqf @@ -0,0 +1,28 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_unitType", "_groupSize", "_vehicle"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Waiting for reinforcement group %1 ready state.",_unitGroup];}; +waitUntil {uiSleep 10; diag_log format ["Debug: Group %1 behavior is %2, combat mode %3.",_unitGroup,(behaviour (leader _unitGroup)),(combatMode _unitGroup)]; !((behaviour (leader _unitGroup)) isEqualTo "CARELESS")}; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 has now entered ready state.",_unitGroup];}; + +while {((behaviour (leader _unitGroup)) in ["AWARE","COMBAT"]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0}} do { + if (local _unitGroup) then { + _vehiclePos = getPosATL _vehicle; + _vehiclePos set [2,0]; + _nearUnits = _vehiclePos nearEntities [[PLAYER_UNITS,"LandVehicle"],250]; + if ((count _nearUnits) > 5) then {_nearUnits resize 5}; + { + if ((isPlayer _x) && {(_unitGroup knowsAbout _x) < 3}) then { + _unitGroup reveal [_x,3]; + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[31+(floor (random 5)),[name (leader _unitGroup)]]] call A3XAI_radioSend; + }; + }; + } forEach _nearUnits; + }; + uiSleep 15; +}; diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_cleanupReinforcementHC.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_cleanupReinforcementHC.sqf new file mode 100644 index 0000000..3f4c058 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_cleanupReinforcementHC.sqf @@ -0,0 +1,13 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_unitGroup setVariable ["GroupSize",-1]; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Releasing ownership of reinforcement group %1 to server.",_unitGroup];}; + +true diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_createGroupTriggerObject.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_createGroupTriggerObject.sqf new file mode 100644 index 0000000..824a869 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_createGroupTriggerObject.sqf @@ -0,0 +1,12 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_trigger","_triggerPos"]; +_unitGroup = _this select 0; +_triggerPos = _this select 1; + +_trigger = TRIGGER_OBJECT createVehicleLocal _triggerPos; +_unitGroup setVariable ["trigger",_trigger]; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Created group trigger object for %1 at %2.",_unitGroup,_triggerPos];}; + +_trigger \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleair.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleair.sqf new file mode 100644 index 0000000..7320fa3 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleair.sqf @@ -0,0 +1,24 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_vehicle"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addVehAirEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5, %6",_unitGroup,_unitLevel,_vehicle,(assignedDriver _vehicle),_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleair_reinforce.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleair_reinforce.sqf new file mode 100644 index 0000000..ed2bef8 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleair_reinforce.sqf @@ -0,0 +1,26 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_patrolParams"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addVehAirEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +[_unitGroup,_vehicle] spawn A3XAI_airReinforcementDetection; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5, %6",_unitGroup,_unitLevel,_vehicle,(assignedDriver _vehicle),_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleaircustom.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleaircustom.sqf new file mode 100644 index 0000000..c3fc231 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleaircustom.sqf @@ -0,0 +1,24 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_patrolParams"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addVehAirEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5, %6",_unitGroup,_unitLevel,_vehicle,(assignedDriver _vehicle),_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handledynamic.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handledynamic.sqf new file mode 100644 index 0000000..7e70583 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handledynamic.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_unitGroup call A3XAI_requestGroupVars; + +//diag_log format ["Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_trigger,_unitType,_groupSize]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleland.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleland.sqf new file mode 100644 index 0000000..a14127b --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleland.sqf @@ -0,0 +1,25 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_vehicle"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = assignedVehicle (leader _unitGroup); +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addLandVehEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_vehicle,_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlelandcustom.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlelandcustom.sqf new file mode 100644 index 0000000..8d69a2e --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlelandcustom.sqf @@ -0,0 +1,25 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_patrolParams"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = assignedVehicle (leader _unitGroup); +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addLandVehEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_vehicle,_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlerandom.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlerandom.sqf new file mode 100644 index 0000000..7e70583 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlerandom.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_unitGroup call A3XAI_requestGroupVars; + +//diag_log format ["Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_trigger,_unitType,_groupSize]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlestatic.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlestatic.sqf new file mode 100644 index 0000000..7e70583 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlestatic.sqf @@ -0,0 +1,20 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUnitEH; +} forEach (units _unitGroup); + +_unitGroup call A3XAI_requestGroupVars; + +//diag_log format ["Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_trigger,_unitType,_groupSize]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlestaticcustom.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlestaticcustom.sqf new file mode 100644 index 0000000..1b0d6bc --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlestaticcustom.sqf @@ -0,0 +1 @@ +#include "A3XAI_handlestatic.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleuav.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleuav.sqf new file mode 100644 index 0000000..195efda --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleuav.sqf @@ -0,0 +1,24 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_vehicle"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUVUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addUAVEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5, %6",_unitGroup,_unitLevel,_vehicle,(assignedDriver _vehicle),_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleugv.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleugv.sqf new file mode 100644 index 0000000..1a89ae5 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handleugv.sqf @@ -0,0 +1,25 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_vehicle"]; + +_unitGroup = _this; + +//_unitLevel = _unitGroup getVariable ["unitLevel",1]; +//_trigger = _unitGroup getVariable ["trigger",objNull]; +//_unitType = _unitGroup getVariable ["unitType","unknown"]; +//_groupSize = _unitGroup getVariable ["GroupSize",-1]; + +{ + _x call A3XAI_addUVUnitEH; +} forEach (units _unitGroup); + +_vehicle = assignedVehicle (leader _unitGroup); +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3XAI_addUGVEH; +_vehicle call A3XAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_vehicle,_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlevehiclecrew.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlevehiclecrew.sqf new file mode 100644 index 0000000..1b0d6bc --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_handlevehiclecrew.sqf @@ -0,0 +1 @@ +#include "A3XAI_handlestatic.sqf" \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_requestGroupVars.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_requestGroupVars.sqf new file mode 100644 index 0000000..bec74a4 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_requestGroupVars.sqf @@ -0,0 +1,3 @@ +A3XAI_getGroupTriggerVars_PVS = _this; +publicVariableServer "A3XAI_getGroupTriggerVars_PVS"; +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_setBehaviorHC.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setBehaviorHC.sqf new file mode 100644 index 0000000..298431a --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setBehaviorHC.sqf @@ -0,0 +1,18 @@ +private ["_unitGroup","_mode"]; +_unitGroup = _this select 0; +_mode = _this select 1; + +call { + if (_mode isEqualTo 0) exitWith { + _unitGroup setBehaviour "CARELESS"; + {_x doWatch objNull} forEach (units _unitGroup); + _unitGroup setVariable ["EnemiesIgnored",true]; + true + }; + if (_mode isEqualTo 1) exitWith { + _unitGroup setBehaviour "AWARE"; + _unitGroup setVariable ["EnemiesIgnored",false]; + true + }; + false +}; diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_setCurrentWaypointHC.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setCurrentWaypointHC.sqf new file mode 100644 index 0000000..893e288 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setCurrentWaypointHC.sqf @@ -0,0 +1,5 @@ +private ["_unitGroup","_waypointIndex"]; +_unitGroup = _this select 0; +_waypointIndex = _this select 1; +_unitGroup setCurrentWaypoint [_unitGroup,_waypointIndex]; +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_setGroupTriggerVars.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setGroupTriggerVars.sqf new file mode 100644 index 0000000..d405189 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setGroupTriggerVars.sqf @@ -0,0 +1,35 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_arrayData", "_unitGroup", "_trigger"]; +_arrayData = _this; +_unitGroup = _arrayData select 0; + +/* +if !(local _unitGroup) then { + diag_log format ["Debug: Server sent group trigger variables for remote group %1.",_unitGroup]; +}; +*/ + +_trigger = _unitGroup getVariable "trigger"; + +if (isNil "_trigger") exitWith {diag_log format ["A3XAI Error: Group %1 has undefined trigger.",_unitGroup];}; + +//Remove array headers (Group reference) +_arrayData deleteAt 0; + +{ + _trigger setVariable [_x,_arrayData select _forEachIndex]; + //diag_log format ["Debug: Group %1 variable %2 has value %3.",_unitGroup,_x,_arrayData select _forEachIndex]; +} forEach [ + "GroupArray", + "patrolDist", + "unitLevel", + "unitLevelEffective", + "maxUnits", + "spawnChance", + "spawnType", + "respawn", + "permadelete" +]; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Set group trigger variables for group %1. Success check: %2.",_unitGroup,_trigger isKindOf TRIGGER_OBJECT];}; diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_setLoadoutVariables_HC.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setLoadoutVariables_HC.sqf new file mode 100644 index 0000000..842f413 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_setLoadoutVariables_HC.sqf @@ -0,0 +1,93 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitType", "_unitGroup", "_loadout", "_weapon", "_magazine", "_useLaunchers", "_maxLaunchers", "_unitLevel", "_launchWeapon", "_launchAmmo", "_useGL"]; + +_unitGroup = _this select 0; +_unitType = _this select 1; +_unitLevel = _this select 2; + +if !(_unitType in ["uav","ugv"]) then { + _useGL = if !(A3XAI_levelRequiredGL isEqualTo -1) then {_unitLevel >= A3XAI_levelRequiredGL} else {false}; + { + _loadout = _x getVariable "loadout"; + if (isNil "_loadout") then { + _x setVariable ["loadout",[[],[]]]; + _loadout = _x getVariable "loadout"; + _primaryWeapon = primaryWeapon _x; + _secondaryWeapon = secondaryWeapon _x; + _handgunWeapon = handgunWeapon _x; + + if !(_primaryWeapon isEqualTo "") then { + (_loadout select 0) pushBack _primaryWeapon; + _primaryWeaponMagazine = getArray (configFile >> "CfgWeapons" >> _primaryWeapon >> "magazines") select 0; + (_loadout select 1) pushBack _primaryWeaponMagazine; + } else { + if !(_handgunWeapon isEqualTo "") then { + (_loadout select 0) pushBack _handgunWeapon; + _handgunWeaponMagazine = getArray (configFile >> "CfgWeapons" >> _handgunWeapon >> "magazines") select 0; + (_loadout select 1) pushBack _handgunWeaponMagazine; + }; + }; + + if !(_secondaryWeapon isEqualTo "") then { + (_loadout select 0) pushBack _secondaryWeapon; + _secondaryWeaponMagazine = getArray (configFile >> "CfgWeapons" >> _secondaryWeapon >> "magazines") select 0; + (_loadout select 1) pushBack _secondaryWeaponMagazine; + }; + + if ((getNumber (configFile >> "CfgMagazines" >> ((_loadout select 1) select 0) >> "count")) < 6) then {_x setVariable ["extraMag",true]}; + + if (_useGL) then { + _weaponMuzzles = getArray(configFile >> "cfgWeapons" >> ((_loadout select 0) select 0) >> "muzzles"); + if ((count _weaponMuzzles) > 1) then { + _GLWeapon = _weaponMuzzles select 1; + _GLMagazines = (getArray (configFile >> "CfgWeapons" >> ((_loadout select 0) select 0) >> _GLWeapon >> "magazines")); + if (GRENADE_AMMO_3RND in _GLMagazines) then { + (_loadout select 0) pushBack _GLWeapon; + (_loadout select 1) pushBack GRENADE_AMMO_3RND; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Modified unit %1 loadout to %2.",_x,_loadout];}; + } else { + if (GRENADE_AMMO_1RND in _GLMagazines) then { + (_loadout select 0) pushBack _GLWeapon; + (_loadout select 1) pushBack GRENADE_AMMO_1RND; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Modified unit %1 loadout to %2.",_x,_loadout];}; + } + }; + }; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 Unit %2 loadout: %3. unitLevel %4.",_unitType,_x,_x getVariable ["loadout",[]],_unitLevel];}; + }; + } forEach (units _unitGroup); +}; + +if (A3XAI_enableDebugMarkers) then { + { + _nul = _x spawn { + waitUntil {sleep 5; ((local _this) or {!(alive _this)})}; + _unitMarker = str (_this); + _unitGroup = group _this; + while {alive _this} do { + if (local _this) then { + _unitPos = getPosATL _this; + if ((leader _unitGroup) isEqualTo _this) then { + (format ["%1_Lead",_unitGroup]) setMarkerPos _unitPos; + _color = call { + _combatMode = (combatMode _unitGroup); + if (_combatMode isEqualTo "YELLOW") exitWith {"ColorBlack"}; + if (_combatMode isEqualTo "RED") exitWith {"ColorRed"}; + if (_combatMode isEqualTo "BLUE") exitWith {"ColorBlue"}; + "ColorBlack" + }; + (format ["%1_Lead",_unitGroup]) setMarkerColor _color; + (format ["%1_WP",_unitGroup]) setMarkerPos (getWPPos [_unitGroup,(currentWaypoint _unitGroup)]); + }; + _unitMarker setMarkerPos _unitPos; + }; + uiSleep 10; + }; + }; + } forEach (units _unitGroup); +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupLootPoolHC.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupLootPoolHC.sqf new file mode 100644 index 0000000..62974be --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupLootPoolHC.sqf @@ -0,0 +1,9 @@ +private ["_unitGroup","_lootPool"]; +_unitGroup = _this select 0; +_lootPool = _this select 1; + +_unitGroup setVariable ["LootPool",_lootPool]; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Updated group %1 loot pool to %2.",_unitGroup,_lootPool];}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupSizeAuto.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupSizeAuto.sqf new file mode 100644 index 0000000..e9546c3 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupSizeAuto.sqf @@ -0,0 +1,12 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_groupSize"]; + +_unitGroup = _this; + +_groupSize = {alive _x} count (units _unitGroup); +_unitGroup setVariable ["GroupSize",_groupSize]; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Updated GroupSize variable for group %1 to %2",_unitGroup,_groupSize];}; + +_groupSize diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupSizeManual.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupSizeManual.sqf new file mode 100644 index 0000000..e0762b4 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateGroupSizeManual.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_groupSize"]; + +_unitGroup = _this select 0; +_groupSize = _this select 1; + +_unitGroup setVariable ["GroupSize",_groupSize]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateServerLoot.sqf b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateServerLoot.sqf new file mode 100644 index 0000000..35d3d70 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient/A3XAI_updateServerLoot.sqf @@ -0,0 +1,3 @@ +A3XAI_updateGroupLoot_PVS = _this; +publicVariableServer "A3XAI_updateGroupLoot_PVS"; +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient_both/A3XAI_setAggroStatusRemoteGroup.sqf b/A3XAI/compile/A3XAI_headlessclient_both/A3XAI_setAggroStatusRemoteGroup.sqf new file mode 100644 index 0000000..02cd4ad --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_both/A3XAI_setAggroStatusRemoteGroup.sqf @@ -0,0 +1,8 @@ +private ["_unitGroup","_status"]; + +_unitGroup = _this select 0; +_status = _this select 1; + +_unitGroup setVariable ["NoAggroStatus",_status]; + +true diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_HCListener.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_HCListener.sqf new file mode 100644 index 0000000..23ce047 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_HCListener.sqf @@ -0,0 +1,49 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_HCObject","_versionHC","_compatibleVersions","_positionHC","_useRemoteConfigs"]; +_HCObject = _this select 0; +_versionHC = _this select 1; +_useRemoteConfigs = _this select 2; + +A3XAI_HC_serverResponse = false; +if (((owner A3XAI_HCObject) isEqualTo 0) && {(typeOf _HCObject) isEqualTo "HeadlessClient_F"}) then { + _compatibleVersions = [configFile >> "CfgPatches" >> "A3XAI","compatibleHCVersions",[]] call BIS_fnc_returnConfigEntry; + if (_versionHC in _compatibleVersions) then { + A3XAI_HCObject = _HCObject; + A3XAI_HCObject allowDamage false; + A3XAI_HCObject enableSimulationGlobal false; + A3XAI_HCObject addEventHandler ["Local",{ + if (_this select 1) then { + private["_unit","_unitGroup"]; + A3XAI_HCIsConnected = false; + A3XAI_HCObjectOwnerID = 0; + A3XAI_HCObject = objNull; + _unit = _this select 0; + _unitGroup = (group _unit); + _unit removeAllEventHandlers "Local"; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Deleting disconnected headless client unit %1.",typeOf _unit];}; + deleteVehicle _unit; + deleteGroup _unitGroup; + }; + }]; + A3XAI_HCObjectOwnerID = (owner A3XAI_HCObject); + A3XAI_HCIsConnected = true; + A3XAI_HC_serverResponse = if (_useRemoteConfigs) then { + A3XAI_pushedHCVariables + } else { + true + }; + _positionHC = getPosATL A3XAI_HCObject; + if (({if (_positionHC in _x) exitWith {1}} count (nearestLocations [_positionHC,[BLACKLIST_OBJECT_GENERAL],BLACKLIST_AREA_HC_SIZE])) isEqualTo 0) then { + [_positionHC,TEMP_BLACKLIST_AREA_HC_SIZE] call A3XAI_createBlackListArea; + diag_log format ["[A3XAI] Created 750m radius blacklist area at HC position %1",_positionHC]; + }; + diag_log format ["[A3XAI] Headless client %1 (owner: %2) logged in successfully.",A3XAI_HCObject,A3XAI_HCObjectOwnerID]; + } else { + diag_log format ["[A3XAI] Headless client %1 (owner: %2) has wrong A3XAI version %3 (Compatible versions: %4).",_HCObject,owner _HCObject,_versionHC,_compatibleVersions]; + }; +} else { + diag_log format ["[A3XAI] Rejecting connection from HC %1. A headless client is already connected: %2. Client object type: %3.",(_this select 1),!((owner A3XAI_HCObject) isEqualTo 0),typeOf _HCObject]; +}; + +(owner _HCObject) publicVariableClient "A3XAI_HC_serverResponse"; diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_getGroupTriggerVars.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_getGroupTriggerVars.sqf new file mode 100644 index 0000000..3456a1d --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_getGroupTriggerVars.sqf @@ -0,0 +1,27 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_trigger", "_triggerVars", "_value"]; + +_unitGroup = _this; +_trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; +_triggerVars = [_unitGroup]; + +{ + _value = _trigger getVariable [_x select 0,_x select 1]; + _triggerVars pushBack _value; +} forEach [ + ["GroupArray",[]], + ["patrolDist",100], + ["unitLevel",1], + ["unitLevelEffective",1], + ["maxUnits",[0,0]], + ["spawnChance",0], + ["spawnType",""], + ["respawn",true], + ["permadelete",false] +]; + +A3XAI_sendGroupTriggerVars_PVC = _triggerVars; +A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_sendGroupTriggerVars_PVC"; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Retrieved group %1 trigger variables: %2",_unitGroup,_triggerVars];}; diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_protectRemoteGroup.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_protectRemoteGroup.sqf new file mode 100644 index 0000000..efafe8f --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_protectRemoteGroup.sqf @@ -0,0 +1,5 @@ +private ["_unitGroup","_dummy"]; +_unitGroup = _this select 0; +_dummy = _this select 1; +_unitGroup setVariable ["dummyUnit",_dummy]; +true diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_registerDeath.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_registerDeath.sqf new file mode 100644 index 0000000..ac0e02e --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_registerDeath.sqf @@ -0,0 +1,22 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_victim", "_killer", "_unitGroup", "_unitLevel","_groupSize","_newGroupSize"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; + +_victim setVariable ["A3XAI_deathTime",diag_tickTime]; +_unitLevel = (_unitGroup getVariable ["unitLevel",0]); + +_groupSize = (_unitGroup getVariable ["GroupSize",0]); +if (_groupSize > 0) then { + _newGroupSize = (_groupSize - 1); + _unitGroup setVariable ["GroupSize",_newGroupSize]; +}; + +if ((_unitGroup getVariable ["ReinforceAvailable",false]) && {isPlayer _killer} && {(missionNamespace getVariable [format ["A3XAI_airReinforcementSpawnChance%1",_unitLevel],0]) call A3XAI_chance}) then { + _unitGroup setVariable ["ReinforceAvailable",false]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 (Level %2) is calling reinforcements.",_unitGroup,_unitLevel];}; + _nul = [(getPosATL _victim),_killer,_unitLevel] spawn A3XAI_spawn_reinforcement; +}; diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_setBehavior.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_setBehavior.sqf new file mode 100644 index 0000000..298431a --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_setBehavior.sqf @@ -0,0 +1,18 @@ +private ["_unitGroup","_mode"]; +_unitGroup = _this select 0; +_mode = _this select 1; + +call { + if (_mode isEqualTo 0) exitWith { + _unitGroup setBehaviour "CARELESS"; + {_x doWatch objNull} forEach (units _unitGroup); + _unitGroup setVariable ["EnemiesIgnored",true]; + true + }; + if (_mode isEqualTo 1) exitWith { + _unitGroup setBehaviour "AWARE"; + _unitGroup setVariable ["EnemiesIgnored",false]; + true + }; + false +}; diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferGroupToHC.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferGroupToHC.sqf new file mode 100644 index 0000000..b7899de --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferGroupToHC.sqf @@ -0,0 +1,30 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_lootPool","_miscData","_miscData2","_groupData""_result","_vehicle"]; +_unitGroup = _this; + +if ((diag_tickTime - A3XAI_lastGroupTransfer) < 5) exitWith {false}; +A3XAI_lastGroupTransfer = diag_tickTime; + +_unitLevel = _unitGroup getVariable ["unitLevel",0]; +_trigger = _unitGroup getVariable ["trigger",nil]; +_unitType = _unitGroup getVariable ["unitType","unknown"]; +_groupSize = _unitGroup getVariable ["GroupSize",nil]; +_lootPool = _unitGroup getVariable ["LootPool",[]]; +_miscData = _unitGroup getVariable ["MiscData",nil]; +_miscData2 = _unitGroup getVariable ["MiscData2",nil]; +_vehicle = _unitGroup getVariable ["assignedVehicle",nil]; + +_groupData = [_unitGroup,_unitLevel,nil,_unitType,_groupSize,_lootPool]; + +if (!isNil "_trigger") then {_groupData set [2,(getPosATL _trigger)];}; +if (!isNil "_vehicle") then {_groupData set [2,_vehicle];}; +if (!isNil "_miscData") then {_groupData set [6,_miscData];}; +if (!isNil "_miscData2") then {_groupData set [7,_miscData2];}; + +A3XAI_transferGroup_PVC = _groupData; +A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_transferGroup_PVC"; + +_result = _unitGroup setGroupOwner A3XAI_HCObjectOwnerID; + +_result \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferGroupToServer.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferGroupToServer.sqf new file mode 100644 index 0000000..92cae3e --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferGroupToServer.sqf @@ -0,0 +1,7 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup"]; +_unitGroup = _this; +_unitGroup setGroupOwner 2; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferHunterGroupToHC.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferHunterGroupToHC.sqf new file mode 100644 index 0000000..8923a19 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_transferHunterGroupToHC.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_targetPlayer"]; + +_unitGroup = _this select 0; +_targetPlayer = _this select 1; + +A3XAI_sendHunterGroupHC = [_unitGroup,_targetPlayer]; +A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_sendHunterGroupHC"; + +true diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_updateGroupLootPool.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_updateGroupLootPool.sqf new file mode 100644 index 0000000..9dbc060 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_updateGroupLootPool.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_lootPool","_lootIndex"]; +_unitGroup = _this select 0; +_lootIndex = _this select 1; + +_lootPool = _unitGroup getVariable ["LootPool",[]]; +_lootPool deleteAt _lootIndex; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_updateGroupSizeServer.sqf b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_updateGroupSizeServer.sqf new file mode 100644 index 0000000..e0762b4 --- /dev/null +++ b/A3XAI/compile/A3XAI_headlessclient_server/A3XAI_updateGroupSizeServer.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_groupSize"]; + +_unitGroup = _this select 0; +_groupSize = _this select 1; + +_unitGroup setVariable ["GroupSize",_groupSize]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_heliDestroyed.sqf b/A3XAI/compile/A3XAI_heliDestroyed.sqf new file mode 100644 index 0000000..875032b --- /dev/null +++ b/A3XAI/compile/A3XAI_heliDestroyed.sqf @@ -0,0 +1,35 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitLevel"]; + +_vehicle = (_this select 0); +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (_vehicle getVariable ["VehicleDisabled",false]) exitWith {}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetIn","GetOut","Killed","Hit"]; +_vehicle setVariable ["VehicleDisabled",true]; +if !((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then {[_vehicle,true] call A3XAI_respawnAIVehicle;}; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +if !(surfaceIsWater (getPosASL _vehicle)) then { + _unitLevel = _unitGroup getVariable ["unitLevel",1]; + [_unitGroup,"aircrashed"] call A3XAI_setUnitType; //Recategorize group as "aircrashed" to prevent AI inventory from being cleared since death is considered suicide. + { + if (alive _x) then { + //_x action ["eject",_vehicle]; + _x call A3XAI_ejectParachute; + _nul = [_x,objNull] call A3XAI_handleDeathEvent; + 0 = [_x,_unitLevel] spawn A3XAI_generateLootOnDeath; + }; + } count (units _unitGroup); +}; + +_unitGroup setVariable ["GroupSize",-1]; +if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; +true diff --git a/A3XAI/compile/A3XAI_heliDetection.sqf b/A3XAI/compile/A3XAI_heliDetection.sqf new file mode 100644 index 0000000..dd24d7f --- /dev/null +++ b/A3XAI/compile/A3XAI_heliDetection.sqf @@ -0,0 +1,70 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_detectBase","_vehicle","_canParaDrop","_detectStartPos"]; +_unitGroup = _this select 0; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +if (_unitGroup getVariable ["EnemiesIgnored",false]) then {[_unitGroup,"Default"] call A3XAI_forceBehavior}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 %2 detection started.",_unitGroup,(typeOf (_vehicle))];}; + +_detectStartPos = getPosATL _vehicle; +_canParaDrop = ((diag_tickTime - (_unitGroup getVariable ["HeliLastParaDrop",-A3XAI_paraDropCooldown])) > A3XAI_paraDropCooldown); +_vehicle flyInHeight (FLYINHEIGHT_AIR_SEARCHING_BASE + (random FLYINHEIGHT_AIR_SEARCHING_VARIANCE)); +_unitGroup setVariable ["IsDetecting",true]; + +while {!(_vehicle getVariable ["VehicleDisabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}} do { + private ["_detected","_vehPos","_nearBlacklistAreas","_playerPos","_canReveal"]; + + _vehPos = getPosATL _vehicle; + _detected = _vehPos nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_AIR]; + + { + if !(isPlayer _x) then { + _detected deleteAt _forEachIndex; + }; + if (_forEachIndex > 4) exitWith {}; + } forEach _detected; + + _nearBlacklistAreas = if (_detected isEqualTo []) then {[]} else {nearestLocations [_vehPos,[BLACKLIST_OBJECT_GENERAL],1500]}; + + if (_canParaDrop) then { + { + _playerPos = getPosATL _x; + if ((isPlayer _x) && {isNull (objectParent _x)} && {({if (_playerPos in _x) exitWith {1}} count _nearBlacklistAreas) isEqualTo 0}) exitWith { + _unitGroup setVariable ["HeliLastParaDrop",diag_tickTime]; + _nul = [_unitGroup,_vehicle,_x] spawn A3XAI_heliParaDrop; + _canParaDrop = false; + }; + uiSleep 0.1; + } forEach _detected; + } else { + _canReveal = ((combatMode _unitGroup) in ["YELLOW","RED"]); + { + _playerPos = getPosATL _x; + if ((isPlayer _x) && {isNull (objectParent _x)} && {({if (_playerPos in _x) exitWith {1}} count _nearBlacklistAreas) isEqualTo 0}) then { + if (_canReveal && {(_unitGroup knowsAbout _x) < 2}) then { + _heliAimPos = aimPos _vehicle; + _playerEyePos = eyePos _x; + if (((lineIntersectsSurfaces [_heliAimPos,_playerEyePos,_vehicle,_x,true,1]) isEqualTo []) && {A3XAI_airDetectChance call A3XAI_chance}) then { + _unitGroup reveal [_x,2.5]; + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[31+(floor (random 5)),[name (leader _unitGroup)]]] call A3XAI_radioSend; + }; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; + }; + + if (((_vehicle distance2D _detectStartPos) > DETECT_LENGTH_AIR_2D) or {_vehicle getVariable ["VehicleDisabled",false]}) exitWith {}; + uiSleep 15; +}; + +_unitGroup setVariable ["IsDetecting",false]; +_vehicle flyInHeight (FLYINHEIGHT_AIR_PATROLLING_BASE + (random FLYINHEIGHT_AIR_PATROLLING_VARIANCE)); + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 %2 detection end.",_unitGroup,(typeOf (_vehicle))];}; diff --git a/A3XAI/compile/A3XAI_heliEvacuated.sqf b/A3XAI/compile/A3XAI_heliEvacuated.sqf new file mode 100644 index 0000000..f5b4727 --- /dev/null +++ b/A3XAI/compile/A3XAI_heliEvacuated.sqf @@ -0,0 +1,73 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_vehiclePos","_unitGroup","_driver"]; + +_vehicle = (_this select 0); +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (_vehicle getVariable ["VehicleDisabled",false]) exitWith {}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_vehicle setVariable ["VehicleDisabled",true]; + +if ((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then { + if (A3XAI_vehiclesAllowedForPlayers) then { + _vehicle call A3XAI_releaseVehicleAllow; + }; +} else { + [_vehicle,true] call A3XAI_respawnAIVehicle; +}; + +_vehiclePos = getPosATL _vehicle; + +if (isNil {_unitGroup getVariable "dummyUnit"}) then { + private ["_unitsAlive","_trigger","_unitLevel","_units","_waypointCount"]; + _unitLevel = _unitGroup getVariable ["unitLevel",1]; + _units = (units _unitGroup); + if (!(surfaceIsWater _vehiclePos) && {(_vehiclePos select 2) > PARACHUTE_HEIGHT_REQUIRED}) then { + _unitsAlive = { + if (alive _x) then { + _x call A3XAI_ejectParachute; + true + } else { + 0 = [_x,_unitLevel] spawn A3XAI_generateLootOnDeath; + false + }; + } count _units; + if (_unitsAlive > 0) then { + if (isDedicated) then { + [_unitGroup,_vehicle,2] call A3XAI_addVehicleGroup; + } else { + _vehiclePos set [2,0]; + [_unitGroup,_vehiclePos] call A3XAI_createGroupTriggerObject; + A3XAI_addVehicleGroup_PVS = [_unitGroup,_vehicle]; + publicVariableServer "A3XAI_addVehicleGroup_PVS"; + [_unitGroup,"vehiclecrew"] call A3XAI_setUnitType; + + }; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI %1 group %2 parachuted with %3 surviving units.",(typeOf _vehicle),_unitGroup,_unitsAlive];}; + } else { + [_unitGroup,"aircrashed"] call A3XAI_setUnitType; + { + _x call A3XAI_ejectParachute; + _nul = [_x,objNull] call A3XAI_handleDeathEvent; + 0 = [_x,_unitLevel] spawn A3XAI_generateLootOnDeath; + } forEach _units; + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI %1 group %2 parachuted with no surviving units.",(typeOf _vehicle),_unitGroup];}; + }; +} else { + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 executed on empty AI %2 group %3.",__FILE__,(typeOf _vehicle),_unitGroup];}; +}; + +true diff --git a/A3XAI/compile/A3XAI_heliLanded.sqf b/A3XAI/compile/A3XAI_heliLanded.sqf new file mode 100644 index 0000000..7f984fc --- /dev/null +++ b/A3XAI/compile/A3XAI_heliLanded.sqf @@ -0,0 +1,38 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_unitsAlive","_unitGroup","_vehiclePos"]; + +_vehicle = (_this select 0); +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["VehicleDisabled",false]) exitWith {}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_vehicle setVariable ["VehicleDisabled",true]; + +if ((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then { + if (A3XAI_vehiclesAllowedForPlayers) then { + _vehicle call A3XAI_releaseVehicleAllow; + }; +} else { + [_vehicle,true] call A3XAI_respawnAIVehicle; +}; + +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitsAlive = {alive _x} count (units _unitGroup); +if (_unitsAlive > 0) then { + if (isDedicated) then { + [_unitGroup,_vehicle,1] call A3XAI_addVehicleGroup; + } else { + _vehiclePos = getPosATL _vehicle; + _vehiclePos set [2,0]; + [_unitGroup,_vehiclePos] call A3XAI_createGroupTriggerObject; + A3XAI_addVehicleGroup_PVS = [_unitGroup,_vehicle]; + publicVariableServer "A3XAI_addVehicleGroup_PVS"; + [_unitGroup,"vehiclecrew"] call A3XAI_setUnitType; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 %2 landed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; +}; diff --git a/A3XAI/compile/A3XAI_heliParaDrop.sqf b/A3XAI/compile/A3XAI_heliParaDrop.sqf new file mode 100644 index 0000000..a224be3 --- /dev/null +++ b/A3XAI/compile/A3XAI_heliParaDrop.sqf @@ -0,0 +1,34 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_targetPlayer", "_cargoAvailable", "_vehiclePos"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_targetPlayer = _this select 2; + +_vehiclePos = getPosATL _vehicle; + +if (surfaceIsWater _vehiclePos) exitWith { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Group %1 cannot deploy paradrop group at %2 due to water position.",_unitGroup,_vehiclePos]; + }; +}; + +// _cargoAvailable = (_vehicle emptyPositions "cargo") min A3XAI_paraDropAmount; +_cargoAvailable = 1 max A3XAI_paraDropAmount; +if (_cargoAvailable > 0) then { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 group %2 has enough cargo positions for successful paradrop. Spawning new group.",typeOf _vehicle,_unitGroup];}; + + _nul = _vehicle spawn { + _this allowDamage false; + uiSleep 5; + _this allowDamage true; + }; + + if (isDedicated) then { + [_vehicle,_unitGroup,_cargoAvailable,_targetPlayer] call A3XAI_addParaGroup; + } else { + A3XAI_addParaGroup_PVS = [_vehicle,_unitGroup,_cargoAvailable,_targetPlayer]; + publicVariableServer "A3XAI_addParaGroup_PVS"; + }; +}; diff --git a/A3XAI/compile/A3XAI_heliStartPatrol.sqf b/A3XAI/compile/A3XAI_heliStartPatrol.sqf new file mode 100644 index 0000000..4f0c863 --- /dev/null +++ b/A3XAI/compile/A3XAI_heliStartPatrol.sqf @@ -0,0 +1,30 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_tooClose","_detectionWaypoint","_exitWaypoint","_vehicle","_dir","_locationSelected"]; +_unitGroup = _this select 0; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; + +_tooClose = true; +_locationSelected = [0,0,0]; + +while {_tooClose} do { + _locationSelected = (A3XAI_locationsAir call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _locationSelected) > NEXT_WP_DIST_AIR) then { + _tooClose = false; + } else { + uiSleep 0.1; + }; +}; + +_dir = [_locationSelected,_vehicle] call BIS_fnc_dirTo; +_detectionWaypoint = [_locationSelected,WP_POS_INGRESS_BASE_AIR + (random WP_POS_INGRESS_VARIANCE_AIR),_dir,1] call A3XAI_SHK_pos; +[_unitGroup,0] setWaypointPosition [_detectionWaypoint,0]; + +_dir = [_vehicle,_locationSelected] call BIS_fnc_dirTo; +_exitWaypoint = [_detectionWaypoint,WP_POS_EGRESS_BASE_AIR + (random WP_POS_EGRESS_VARIANCE_AIR),_dir,1] call A3XAI_SHK_pos; +[_unitGroup,2] setWaypointPosition [_detectionWaypoint,0]; + +_unitGroup setVariable ["SearchLength",(_detectionWaypoint distance2D _exitWaypoint)]; + +true diff --git a/A3XAI/compile/A3XAI_huntKiller.sqf b/A3XAI/compile/A3XAI_huntKiller.sqf new file mode 100644 index 0000000..e8587ec --- /dev/null +++ b/A3XAI/compile/A3XAI_huntKiller.sqf @@ -0,0 +1,149 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_targetPlayer","_startPos","_chaseDistance","_enableHCReady","_nearBlacklistedAreas","_targetPlayerPos","_originalSpeed"]; + +_targetPlayer = _this select 0; +_unitGroup = _this select 1; + +//Disable killer-finding for dynamic AI in hunting mode +if (_unitGroup getVariable ["seekActive",false]) exitWith {}; + +//If group is already pursuing player and target player has killed another group member, then extend pursuit time. +if (((_unitGroup getVariable ["pursuitTime",0]) > 0) && {((_unitGroup getVariable ["targetKiller",""]) isEqualTo (name _targetPlayer))}) exitWith { + _unitGroup setVariable ["pursuitTime",((_unitGroup getVariable ["pursuitTime",0]) + 20)]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Pursuit time +20 sec for Group %1 (Target: %2) to %3 seconds.",_unitGroup,name _targetPlayer,(_unitGroup getVariable ["pursuitTime",0]) - diag_tickTime]}; +}; + +_enableHCReady = false; +if (_unitGroup getVariable ["HC_Ready",false]) then { //If HC mode enabled and AI group is controlled by server, prevent it from being transferred to HC until hunting mode is over. + _unitGroup setVariable ["HC_Ready",false]; + _enableHCReady = true; +}; + +_startPos = _unitGroup getVariable ["trigger",(getPosASL (leader _unitGroup))]; +_chaseDistance = _unitGroup getVariable ["patrolDist",250]; +_targetPlayerPos = getPosATL _targetPlayer; +_nearBlacklistedAreas = nearestLocations [_targetPlayerPos,[BLACKLIST_OBJECT_GENERAL],1500]; +_originalSpeed = speedMode _unitGroup; + +_unitGroup setFormDir ([(leader _unitGroup),_targetPlayerPos] call BIS_fnc_dirTo); + +if ((_startPos distance _targetPlayerPos) < _chaseDistance) then { + private ["_leader","_ableToChase","_marker"]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 has entered pursuit state for 180 seconds. Target: %2.",_unitGroup,_targetPlayer];}; + + //Set pursuit timer + _unitGroup setVariable ["pursuitTime",diag_tickTime+180]; + _unitGroup setVariable ["targetKiller",name _targetPlayer]; + + if (_originalSpeed != "FULL") then { + _unitGroup setSpeedMode "FULL"; + }; + + if (A3XAI_enableDebugMarkers) then { + _markername = format ["%1 Target",_unitGroup]; + if (_markername in allMapMarkers) then {deleteMarker _markername; uiSleep 0.5;}; + _marker = createMarker [_markername,getPosASL _targetPlayer]; + _marker setMarkerText _markername; + _marker setMarkerType "mil_warning"; + _marker setMarkerColor "ColorRed"; + _marker setMarkerBrush "Solid"; + }; + + //Begin pursuit state. + _ableToChase = true; + while { + _ableToChase && + {isPlayer _targetPlayer} && + {alive _targetPlayer} && + {((_startPos distance _targetPlayer) < _chaseDistance)} && + {(vehicle _targetPlayer) isKindOf "Land"} + } do { + _targetPlayerPos = getPosATL _targetPlayer; + if (({_targetPlayerPos in _x} count _nearBlacklistedAreas) isEqualTo 0) then { + if ((_unitGroup knowsAbout _targetPlayer) < 4) then {_unitGroup reveal [_targetPlayer,4]}; + (units _unitGroup) doMove _targetPlayerPos; + }; + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: AI group %1 in pursuit state. Pursuit time remaining: %2 seconds.",_unitGroup,(_unitGroup getVariable ["pursuitTime",0]) - diag_tickTime];}; + + if ((A3XAI_enableRadioMessages) && {0.7 call A3XAI_chance}) then { + _leader = (leader _unitGroup); + if ((alive _leader) && {(_targetPlayer distance _leader) <= RECEIVE_DIST_RADIO_HUNTKILLER}) then { + _nearbyUnits = _targetPlayerPos nearEntities [[PLAYER_UNITS,"LandVehicle"],TRANSMIT_RANGE_RADIO_HUNTKILLER]; + if !(_nearbyUnits isEqualTo []) then { //Have at least 1 player to send a message to + if ((_unitGroup getVariable ["GroupSize",0]) > 1) then { //Have at least 1 AI unit to send a message from + _speechIndex = (floor (random 3)); + _radioSpeech = call { + if (_speechIndex isEqualTo 0) exitWith { + [1,[(name _leader),(name _targetPlayer)]] + }; + if (_speechIndex isEqualTo 1) exitWith { + [2,[(name _leader),(getText (configFile >> "CfgVehicles" >> (typeOf _targetPlayer) >> "displayName"))]] + }; + if (_speechIndex isEqualTo 2) exitWith { + [3,[(name _leader),round (_leader distance _targetPlayer)]] + }; + [0,[]] + }; + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,_radioSpeech] call A3XAI_radioSend; + }; + } count _nearbyUnits; + } else { + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,[0,[]]] call A3XAI_radioSend; + }; + } count _nearbyUnits; + }; + }; + }; + }; + if (A3XAI_enableDebugMarkers) then { + _marker setMarkerPos (getPosASL _targetPlayer); + }; + _ableToChase = ((!isNull _unitGroup) && {diag_tickTime < (_unitGroup getVariable ["pursuitTime",0])} && {(_unitGroup getVariable ["GroupSize",0]) > 0}); + if (_ableToChase) then {uiSleep 20}; + }; + + if !(isNull _unitGroup) then { + //End of pursuit state. Re-enable patrol state. + _unitGroup setVariable ["pursuitTime",0]; + _unitGroup setVariable ["targetKiller",""]; + //_unitGroup lockWP false; + + if ((_unitGroup getVariable ["GroupSize",0]) > 0) then { + _waypoints = (waypoints _unitGroup); + _unitGroup setCurrentWaypoint (_waypoints call A3XAI_selectRandom); + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Pursuit state ended for group %1. Returning to patrol state.",_unitGroup];}; + + if (A3XAI_enableRadioMessages) then { + _leader = (leader _unitGroup); + if ((alive _leader) && {(_targetPlayer distance _leader) <= RECEIVE_DIST_RADIO_HUNTKILLER} && {((_unitGroup getVariable ["GroupSize",0]) > 1)} && {isPlayer _targetPlayer}) then { + _radioText = if (alive _targetPlayer) then {4} else {5}; + _radioSpeech = [_radioText,[name (leader _unitGroup)]]; + _nearbyUnits = (getPosASL _targetPlayer) nearEntities [["LandVehicle",PLAYER_UNITS],TRANSMIT_RANGE_RADIO_HUNTKILLER]; + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,_radioSpeech] call A3XAI_radioSend; + }; + } count _nearbyUnits; + }; + }; + }; + + if ((speedMode _unitGroup) != _originalSpeed) then { + _unitGroup setSpeedMode _originalSpeed; + }; + + if (_enableHCReady) then { + _unitGroup setVariable ["HC_Ready",true]; + }; + }; + + if (A3XAI_enableDebugMarkers) then { + deleteMarker _marker; + }; +}; diff --git a/A3XAI/compile/A3XAI_hunterLocate.sqf b/A3XAI/compile/A3XAI_hunterLocate.sqf new file mode 100644 index 0000000..8fb5984 --- /dev/null +++ b/A3XAI/compile/A3XAI_hunterLocate.sqf @@ -0,0 +1,79 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_targetPlayer", "_waypoint", "_leader", "_nearbyUnits", "_index", "_radioSpeech", "_searchPos","_triggerPos","_nearBlacklistedAreas","_targetPos"]; + +_unitGroup = _this; + +_triggerPos = getPosATL (_unitGroup getVariable ["trigger",objNull]); +_targetPlayer = _unitGroup getVariable ["targetplayer",objNull]; + +try { + if !(isPlayer _targetPlayer) then { + throw "Target is not a player."; + }; + + _nearBlacklistedAreas = nearestLocations [_targetPlayer,[BLACKLIST_OBJECT_GENERAL],1500]; + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Hunter group %1 has target player %2 and anchor pos %3.",_unitGroup,_targetPlayer,_triggerPos];}; + + if ( + (isPlayer _targetPlayer) && + {alive _targetPlayer} && + {(vehicle _targetPlayer) isKindOf "Land"} && + {(_targetPlayer distance _triggerPos) < SEEK_RANGE_HUNTER} + ) then { + _waypoint = [_unitGroup,0]; + _targetPos = getPosATL _targetPlayer; + if (({_targetPos in _x} count _nearBlacklistedAreas) isEqualTo 0) then { + if (((getWPPos _waypoint) distance _targetPlayer) > 20) then { + _waypoint setWPPos _targetPos; + //diag_log format ["Debug: Hunter group %1 is seeking player %2 (position: %3).",_unitGroup,_targetPlayer,(getPosATL _targetPlayer)]; + } else { + _searchPos = [(getWPPos _waypoint),20+(random 20),(random 360),0] call A3XAI_SHK_pos; + _waypoint setWPPos _searchPos; + }; + }; + + if ((_unitGroup knowsAbout _targetPlayer) < 4) then {_unitGroup reveal [_targetPlayer,4]}; + _unitGroup setCurrentWaypoint _waypoint; + + if (A3XAI_enableRadioMessages) then { + _leader = (leader _unitGroup); + if ((_leader distance _targetPlayer) < RADIO_RECEPTION_RANGE) then { + _nearbyUnits = _targetPos nearEntities [["LandVehicle",PLAYER_UNITS],TRANSMIT_RANGE_RADIO_HUNTER]; + if !(_nearbyUnits isEqualTo []) then { + if ((count _nearbyUnits) > 10) then {_nearbyUnits resize 10;}; + if ((_unitGroup getVariable ["GroupSize",0]) > 1) then { + _index = (floor (random 4)); + _radioSpeech = call { + if (_index isEqualTo 0) exitWith {[11,[(name _leader),(name _targetPlayer)]]}; + if (_index isEqualTo 1) exitWith {[12,[(name _leader),(getText (configFile >> "CfgVehicles" >> (typeOf _targetPlayer) >> "displayName"))]]}; + if (_index isEqualTo 2) exitWith {[13,[(name _leader),round (_leader distance _targetPlayer)]]}; + if (_index > 2) exitWith {[0,[]]}; + [0,[]] + }; + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,_radioSpeech] call A3XAI_radioSend; + }; + } count _nearbyUnits; + } else { + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,[0,[]]] call A3XAI_radioSend; + }; + } count _nearbyUnits; + }; + }; + }; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Dynamic group %1 has located player %2.",_unitGroup,_targetPlayer];}; + } else { + throw "Hunt ended."; + }; +} catch { + [_unitGroup,0] setWaypointStatements ["true","if !(local this) exitWith {}; if ((random 1) < 0.50) then { group this setCurrentWaypoint [(group this), (floor (random (count (waypoints (group this)))))];};"]; + 0 = [_unitGroup,_triggerPos,PATROL_DIST_DYNAMIC] spawn A3XAI_BIN_taskPatrol; + if (A3XAI_enableHC && {isDedicated}) then {_unitGroup setVariable ["MiscData",nil];}; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Dynamic group %1 is patrolling area. (%2)",_unitGroup,_exception];}; +}; diff --git a/A3XAI/compile/A3XAI_initNoAggroStatus.sqf b/A3XAI/compile/A3XAI_initNoAggroStatus.sqf new file mode 100644 index 0000000..0250ac5 --- /dev/null +++ b/A3XAI/compile/A3XAI_initNoAggroStatus.sqf @@ -0,0 +1,38 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_object", "_inArea", "_noAggroRange","_leader","_assignedTarget"]; + +_unitGroup = _this; +_object = (leader _unitGroup); + +_noAggroRange = call { + private ["_unitType"]; + _unitType = _unitGroup getVariable ["unitType",""]; + if (_unitType isEqualTo "static") exitWith {NO_AGGRO_RANGE_MAN}; + if (_unitType isEqualTo "random") exitWith {NO_AGGRO_RANGE_MAN}; + if (_unitType isEqualTo "dynamic") exitWith {NO_AGGRO_RANGE_MAN}; + if (_unitType isEqualTo "air") exitWith {NO_AGGRO_RANGE_AIR}; + if (_unitType isEqualTo "land") exitWith {NO_AGGRO_RANGE_LAND}; + if (_unitType isEqualTo "uav") exitWith {NO_AGGRO_RANGE_UAV}; + if (_unitType isEqualTo "ugv") exitWith {NO_AGGRO_RANGE_UGV}; + if (_unitType isEqualTo "air_reinforce") exitWith {NO_AGGRO_RANGE_AIR}; + if (_unitType isEqualTo "vehiclecrew") exitWith {NO_AGGRO_RANGE_MAN}; + if (_unitType isEqualTo "staticcustom") exitWith {NO_AGGRO_RANGE_MAN}; + if (_unitType isEqualTo "aircustom") exitWith {NO_AGGRO_RANGE_AIR}; + if (_unitType isEqualTo "landcustom") exitWith {NO_AGGRO_RANGE_LAND}; + 900 +}; + +_leader = (leader _unitGroup); +_inArea = [_object,_noAggroRange] call A3XAI_checkInActiveNoAggroArea; //need to set range according to unit type. + +if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < _noAggroRange) then { //900: replace with engagement range + _inArea = [_assignedTarget,300] call A3XAI_checkInActiveNoAggroArea; + }; +}; + +[_unitGroup,_inArea] call A3XAI_setNoAggroStatus; + +true diff --git a/A3XAI/compile/A3XAI_initUVGroup.sqf b/A3XAI/compile/A3XAI_initUVGroup.sqf new file mode 100644 index 0000000..90dd94d --- /dev/null +++ b/A3XAI/compile/A3XAI_initUVGroup.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_UAVGroup", "_unitType", "_unitGroup"]; + +_UAVGroup = _this select 0; +_unitType = _this select 1; + +_unitGroup = grpNull; +if !((side _UAVGroup) isEqualTo A3XAI_side) then { + _unitGroup = [_unitType] call A3XAI_createGroup; + (units _UAVGroup) joinSilent _unitGroup; + deleteGroup _UAVGroup; +} else { + _unitGroup = _UAVGroup; + [_unitGroup,_unitType] call A3XAI_setUnitType; + A3XAI_activeGroups pushBack _unitGroup; +}; + +{ + _x call A3XAI_addUVUnitEH; +} forEach (units _unitGroup); + +_unitGroup; diff --git a/A3XAI/compile/A3XAI_initializeTrigger.sqf b/A3XAI/compile/A3XAI_initializeTrigger.sqf new file mode 100644 index 0000000..a3ec65d --- /dev/null +++ b/A3XAI/compile/A3XAI_initializeTrigger.sqf @@ -0,0 +1,82 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_trigger","_mode"]; + +_mode = _this select 0; +_trigger = _this select 1; + +if (_trigger getVariable ["initialized",false]) exitWith {}; + +_trigger setVariable ["isCleaning",false]; +_trigger setVariable ["GroupArray",(_this select 2)]; + +call { + if (_mode isEqualTo 0) exitWith { + //Static spawns + _trigger setVariable ["patrolDist",(_this select 3)]; + _trigger setVariable ["unitLevel",(_this select 4)]; + _trigger setVariable ["unitLevelEffective",(_this select 4)]; + _trigger setVariable ["locationArray",(_this select 5)]; + _trigger setVariable ["maxUnits",(_this select 6)]; + _trigger setVariable ["spawnChance",(_this select 7)]; + _trigger setVariable ["spawnType","static"]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Initialized static spawn at %1 (%2). GroupArray: %3, PatrolDist: %4. unitLevel: %5. %LocationArray %6 positions, MaxUnits %7, SpawnChance %8.",(_trigger getVariable ["TriggerText","Unknown Trigger"]),(getPosATL _trigger),(_this select 2),(_this select 3),(_this select 4),count (_this select 5),(_this select 6),(_this select 7)];}; + }; + if (_mode isEqualTo 1) exitWith { + //Dynamic spawns + _trigger setVariable ["patrolDist",(_this select 3)]; + _trigger setVariable ["unitLevel",(_this select 4)]; + _trigger setVariable ["unitLevelEffective",(_this select 4)]; + _trigger setVariable ["maxUnits",(_this select 5)]; + _trigger setVariable ["spawnChance",1000]; + _trigger setVariable ["spawnType","dynamic"]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Initialized dynamic spawn at %1 (%2). GroupArray: %3, PatrolDist: %4. unitLevel: %5, MaxUnits %6.",(_trigger getVariable ["TriggerText","Unknown Trigger"]),(getPosATL _trigger),(_this select 2),(_this select 3),(_this select 4),(_this select 5)];}; + }; + if (_mode isEqualTo 2) exitWith { + //Random spawns + _trigger setVariable ["patrolDist",(_this select 3)]; + _trigger setVariable ["unitLevel",(_this select 4)]; + _trigger setVariable ["unitLevelEffective",(_this select 4)]; + _trigger setVariable ["maxUnits",(_this select 5)]; + _trigger setVariable ["spawnChance",1000]; + _trigger setVariable ["spawnType","random"]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Initialized random spawn at %1 (%2). GroupArray: %3, PatrolDist: %4. unitLevel: %5, MaxUnits %6.",(_trigger getVariable ["TriggerText","Unknown Trigger"]),(getPosATL _trigger),(_this select 2),(_this select 3),(_this select 4),(_this select 5)];}; + }; + if (_mode isEqualTo 3) exitWith { + //Static spawns (custom) + _trigger setVariable ["patrolDist",(_this select 3)]; + _trigger setVariable ["unitLevel",(_this select 4)]; + _trigger setVariable ["unitLevelEffective",(_this select 4)]; + _trigger setVariable ["locationArray",(_this select 5)]; + _trigger setVariable ["maxUnits",(_this select 6)]; + _trigger setVariable ["spawnChance",1000]; + _trigger setVariable ["spawnType","staticcustom"]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Initialized custom spawn at %1 (%2). GroupArray: %3, PatrolDist: %4. unitLevel: %5. %LocationArray %6 positions, MaxUnits %7.",_trigger getVariable ["TriggerText","Unknown Trigger"],(getPosATL _trigger),(_this select 2),(_this select 3),(_this select 4),count (_this select 5),(_this select 6)];}; + }; + if (_mode isEqualTo 4) exitWith { + //Vehicle group + _trigger setVariable ["patrolDist",(_this select 3)]; + _trigger setVariable ["unitLevel",(_this select 4)]; + _trigger setVariable ["maxUnits",(_this select 5)]; + _trigger setVariable ["spawnChance",1]; + _trigger setVariable ["spawnType","vehiclecrew"]; + _trigger setVariable ["respawn",false]; + _trigger setVariable ["permadelete",true]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Initialized vehicle group at %1. GroupArray: %2, PatrolDist: %3. unitLevel: %4, MaxUnits %5.",_trigger getVariable ["TriggerText","Unknown Trigger"],(_this select 2),(_this select 3),(_this select 4),(_this select 5)];}; + }; + if (_mode isEqualTo 5) exitWith { + //Paradrop group + _trigger setVariable ["patrolDist",(_this select 3)]; + _trigger setVariable ["unitLevel",(_this select 4)]; + _trigger setVariable ["maxUnits",(_this select 5)]; + _trigger setVariable ["spawnChance",1]; + _trigger setVariable ["spawnType","vehiclecrew"]; + _trigger setVariable ["respawn",false]; + _trigger setVariable ["permadelete",true]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Initialized paradrop group at %1. GroupArray: %2, PatrolDist: %3. unitLevel: %4, MaxUnits %5.",_trigger getVariable ["TriggerText","Unknown Trigger"],(_this select 2),(_this select 3),(_this select 4),(_this select 5)];}; + }; +}; + +_trigger setVariable ["initialized",true]; + +true diff --git a/A3XAI/compile/A3XAI_markGroupUnitsForDeletion.sqf b/A3XAI/compile/A3XAI_markGroupUnitsForDeletion.sqf new file mode 100644 index 0000000..68944c0 --- /dev/null +++ b/A3XAI/compile/A3XAI_markGroupUnitsForDeletion.sqf @@ -0,0 +1,16 @@ +#include "\A3XAI\globaldefines.hpp" +//NOTE: Needs to be made HC-compatible + +private ["_unitGroup", "_respawn"]; + +_unitGroup = _this select 0; +_respawn = _this select 1; + +if (isDedicated) then { + _unitGroup setVariable ["RecycleGroup",_respawn]; + _unitGroup setVariable ["GroupSize",-1]; +} else { + diag_log format ["A3XAI Error: No HC handling implemented for %1",__FILE__]; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_moveToPosAndDeleteWP.sqf b/A3XAI/compile/A3XAI_moveToPosAndDeleteWP.sqf new file mode 100644 index 0000000..7be192b --- /dev/null +++ b/A3XAI/compile/A3XAI_moveToPosAndDeleteWP.sqf @@ -0,0 +1,9 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup"]; + +_unitGroup = _this; +deleteWaypoint [_unitGroup,(currentWaypoint _unitGroup)]; +_unitGroup setCurrentWaypoint ((waypoints _unitGroup) call BIS_fnc_selectRandom); + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_moveToPosAndPatrol.sqf b/A3XAI/compile/A3XAI_moveToPosAndPatrol.sqf new file mode 100644 index 0000000..65340e2 --- /dev/null +++ b/A3XAI/compile/A3XAI_moveToPosAndPatrol.sqf @@ -0,0 +1,14 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_currentWaypoint","_radius"]; + +_unitGroup = _this select 0; +_radius = _this select 1; + +_currentWaypoint = (currentWaypoint _unitGroup); +_pos = (waypointPosition _currentWaypoint); +deleteWaypoint [_unitGroup,_currentWaypoint]; +[_unitGroup,"Default"] call A3XAI_forceBehavior; +0 = [_unitGroup,_pos,_radius] spawn A3XAI_BIN_taskPatrol; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_noAggroAreaActivate.sqf b/A3XAI/compile/A3XAI_noAggroAreaActivate.sqf new file mode 100644 index 0000000..71c3876 --- /dev/null +++ b/A3XAI/compile/A3XAI_noAggroAreaActivate.sqf @@ -0,0 +1,13 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger"]; +_trigger = _this select 0; + +// if ((A3XAI_activeNoAggroAreas find _trigger) isEqualTo -1) then { +if !(_trigger in A3XAI_activeNoAggroAreas) then { + A3XAI_activeNoAggroAreas pushBack _trigger; + _trigger setTriggerArea [NO_AGGRO_AREA_EXPANDED_SIZE,NO_AGGRO_AREA_EXPANDED_SIZE,0,false]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Player entered no-aggro area at %1",(getPosATL _trigger)];}; +}; + +true diff --git a/A3XAI/compile/A3XAI_noAggroAreaDeactivate.sqf b/A3XAI/compile/A3XAI_noAggroAreaDeactivate.sqf new file mode 100644 index 0000000..c87d907 --- /dev/null +++ b/A3XAI/compile/A3XAI_noAggroAreaDeactivate.sqf @@ -0,0 +1,12 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger"]; +_trigger = _this select 0; + +if (_trigger in A3XAI_activeNoAggroAreas) then { + A3XAI_activeNoAggroAreas = A3XAI_activeNoAggroAreas - [_trigger]; + _trigger setTriggerArea [NO_AGGRO_AREA_SIZE,NO_AGGRO_AREA_SIZE,0,false]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Player exited no-aggro area at %1",(getPosATL _trigger)];}; +}; + +true diff --git a/A3XAI/compile/A3XAI_noAggroAreaToggle.sqf b/A3XAI/compile/A3XAI_noAggroAreaToggle.sqf new file mode 100644 index 0000000..4e2160d --- /dev/null +++ b/A3XAI/compile/A3XAI_noAggroAreaToggle.sqf @@ -0,0 +1,31 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_object", "_nearNoAggroAreas", "_inNoAggroArea", "_objectPos","_combatMode"]; + +_unitGroup = _this select 0; +_inNoAggroArea = _this select 1; + +_combatMode = (combatMode _unitGroup); + +if (_inNoAggroArea) then { + if !(_unitGroup call A3XAI_getNoAggroStatus) then { + [_unitGroup,true] call A3XAI_setNoAggroStatus; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 set no-aggro status ON.",_unitGroup];}; + }; + if (_combatMode != "BLUE") then { + [_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; + //if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 in no-aggro zone.",_unitGroup];}; + } +} else { + if (_unitGroup call A3XAI_getNoAggroStatus) then { + [_unitGroup,false] call A3XAI_setNoAggroStatus; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 set no-aggro status OFF.",_unitGroup];}; + }; + if (_combatMode isEqualTo "BLUE") then { + [_unitGroup,"Default"] call A3XAI_forceBehavior; + [_unitGroup,false] call A3XAI_setNoAggroStatus; + //if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 exited no-aggro zone.",_unitGroup];}; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_param.sqf b/A3XAI/compile/A3XAI_param.sqf new file mode 100644 index 0000000..7802e2b --- /dev/null +++ b/A3XAI/compile/A3XAI_param.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_array", "_index", "_default", "_return", "_value"]; + +_array = _this select 0; +_index = _this select 1; +_default = _this select 2; + +_return = -1; +if ((count _array) > _index) then { + _value = _array select _index; + if ((typeName _value) isEqualTo (typeName _default)) then { + _return = _value; + } else { + _return = _default; + }; +} else { + _return = _default; +}; + +//diag_log format ["Debug: Array: %1, Return: %2",_this,_return]; + +_return diff --git a/A3XAI/compile/A3XAI_posInBuilding.sqf b/A3XAI/compile/A3XAI_posInBuilding.sqf new file mode 100644 index 0000000..92e4070 --- /dev/null +++ b/A3XAI/compile/A3XAI_posInBuilding.sqf @@ -0,0 +1,7 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_comparePos"]; +if ((count _this) isEqualTo 2) then {_this set [2,0]}; +_comparePos = [(_this select 0),(_this select 1),(_this select 2) + 20]; + +(lineIntersects[_this, _comparePos]) \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_processRespawn.sqf b/A3XAI/compile/A3XAI_processRespawn.sqf new file mode 100644 index 0000000..258cc14 --- /dev/null +++ b/A3XAI/compile/A3XAI_processRespawn.sqf @@ -0,0 +1,131 @@ +#include "\A3XAI\globaldefines.hpp" + +waitUntil {uiSleep 3; diag_tickTime > A3XAI_nextRespawnTime}; + +A3XAI_respawnActive = true; //First respawn is now being processed, so deny subsequent attempts to modify the initial wait time. +A3XAI_queueActive = nil; +A3XAI_nextRespawnTime = nil; + +while {(count A3XAI_respawnQueue) > 0} do { + private ["_minDelay","_delay"]; + + _minDelay = -1; + //Remove expired entries before proceeding. + { + if (((typeName (_x select 3)) isEqualTo "GROUP") && {(isNull (_x select 3))}) then { + A3XAI_respawnQueue deleteAt _forEachIndex; + }; + } forEach A3XAI_respawnQueue; + + if (A3XAI_currentFPS > A3XAI_minFPS) then { + //Begin examining queue entries. + { + _currentRespawn = (A3XAI_respawnQueue select _forEachIndex); + _timeToRespawn = _currentRespawn select 0; + //If enough time has passed to respawn the group. + if (diag_tickTime > _timeToRespawn) then { + _mode = _currentRespawn select 1; + call { + if (_mode isEqualTo 0) exitWith { + //Infantry AI respawn + _trigger = _currentRespawn select 2; + _unitGroup = _currentRespawn select 3; + _grpArray = _trigger getVariable ["GroupArray",[]]; + if ((_unitGroup in _grpArray) && {((_unitGroup getVariable ["GroupSize",0]) < 1)}) then { + if (_trigger in A3XAI_activePlayerAreas) then { + //Trigger is active, so respawn the group + _maxUnits = _trigger getVariable ["maxUnits",[1,0]]; + _respawned = [_unitGroup,_trigger,_maxUnits] call A3XAI_respawnGroup; + if ((A3XAI_debugLevel > 0) && {!_respawned}) then {diag_log format ["A3XAI Debug: No units were respawned for group %1 at %2. Group %1 reinserted into respawn queue.",_unitGroup,(_trigger getVariable ["TriggerText","Unknown Trigger"])];}; + } else { + //Trigger is inactive (despawned or deleted) so clean up group instead + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Spawn area %1 has already been despawned. Cleaning up group %2.",_trigger getVariable ["TriggerText","Unknown Trigger"],_unitGroup]}; + _unitGroup call A3XAI_deleteGroup; + if (!isNull _trigger) then { + _trigger setVariable ["GroupArray",_grpArray - [grpNull]]; + }; + }; + }; + }; + if (_mode isEqualTo 1) exitWith { + //Custom vehicle AI respawn + _respawnParams = _currentRespawn select 2; + _nul = _respawnParams spawn A3XAI_spawnVehicleCustom; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Respawning custom AI vehicle patrol with params %1",((A3XAI_respawnQueue select _forEachIndex) select 2)]}; + }; + if (_mode isEqualTo 2) exitWith { + //Vehicle AI patrol respawn + _vehicleTypeOld = _currentRespawn select 2; + if (_vehicleTypeOld isKindOf "Air") then { //Air-type vehicle AI patrol respawn + A3XAI_heliTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3XAI_heliTypesUsable)); + _vehicleTypeNew = A3XAI_heliTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3XAI_spawnVehiclePatrol; + A3XAI_heliTypesUsable deleteAt _index; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Respawning AI air vehicle type patrol %1.",_vehicleTypeNew]}; + } else { + if (_vehicleTypeOld isKindOf "LandVehicle") then { //Land-type vehicle AI patrol respawn + A3XAI_vehTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3XAI_vehTypesUsable)); + _vehicleTypeNew = A3XAI_vehTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3XAI_spawnVehiclePatrol; + A3XAI_vehTypesUsable deleteAt _index; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Respawning AI land vehicle patrol type %1.",_vehicleTypeNew]}; + }; + }; + }; + if (_mode isEqualTo 3) exitWith { + //UAV/UGV respawn + _vehicleTypeOld = _currentRespawn select 2; + if (_vehicleTypeOld isKindOf "Air") then { + A3XAI_UAVTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3XAI_UAVTypesUsable)); + _vehicleTypeNew = A3XAI_UAVTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3XAI_spawn_UV_patrol; + A3XAI_UAVTypesUsable deleteAt _index; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Respawning UAV patrol %1.",_vehicleTypeNew]}; + } else { + if (_vehicleTypeOld isKindOf "LandVehicle") then { + A3XAI_UGVTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3XAI_UGVTypesUsable)); + _vehicleTypeNew = A3XAI_UGVTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3XAI_spawn_UV_patrol; + A3XAI_UGVTypesUsable deleteAt _index; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Respawning UGV patrol type %1.",_vehicleTypeNew]}; + }; + }; + }; + + diag_log format ["A3XAI Error: Invalid respawn mode %1 for group %2!",_mode,_unitGroup]; + }; + A3XAI_respawnQueue deleteAt _forEachIndex; + uiSleep TIME_PER_RESPAWN_PROCESSING; + } else { + //Find shortest delay to next group respawn. + _delay = ((_timeToRespawn - diag_tickTime) max TIME_PER_RESPAWN_PROCESSING); + //diag_log format ["DEBUG :: Comparing new respawn time %1 with previous %2.",_delay,_minDelay]; + if (_minDelay > 0) then { + //If next delay time is smaller than the current minimum delay, use it instead. + if (_delay < _minDelay) then { + _minDelay = _delay; + //diag_log format ["DEBUG :: Found shorter respawn interval: %1 seconds.",_minDelay]; + }; + } else { + //Initialize minimum delay to first found delay. + _minDelay = _delay; + //diag_log format ["DEBUG :: Set respawn interval to %1 seconds.",_minDelay]; + }; + }; + } forEach A3XAI_respawnQueue; + } else { + diag_log format ["A3XAI Debug: %1: Current server FPS %2 is lower than minimum threshold of %3.",__FILE__,A3XAI_currentFPS,A3XAI_minFPS]; + _minDelay = 30; + }; + if (_minDelay > -1) then { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 groups left in respawn queue. Next group is scheduled to respawn in %2 seconds.",(count A3XAI_respawnQueue),_minDelay];}; + uiSleep _minDelay; + }; +}; + +A3XAI_respawnActive = nil; +if (A3XAI_debugLevel > 0) then {diag_log "A3XAI Debug: Respawn queue is empty. Exiting respawn handler.";}; diff --git a/A3XAI/compile/A3XAI_protectGroup.sqf b/A3XAI/compile/A3XAI_protectGroup.sqf new file mode 100644 index 0000000..dce79f6 --- /dev/null +++ b/A3XAI/compile/A3XAI_protectGroup.sqf @@ -0,0 +1,21 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_dummy"]; //_this = group + +_dummy = _this getVariable ["dummyUnit",objNull]; + +if (isNull _dummy) then { + _dummy = _this createUnit ["Logic",[0,0,0],[],0,"FORM"]; + [_dummy] joinSilent _this; + _this setVariable ["dummyUnit",_dummy]; + if !(isDedicated) then { + A3XAI_protectGroup_PVS = [_unitGroup,_dummy]; + publicVariableServer "A3XAI_protectGroup_PVS"; + }; + + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Spawned 1 dummy AI unit to preserve group %1.",_this];}; +} else { + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Group %1 already has a dummy unit assigned.",_this];}; +}; + +_dummy \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_protectObject.sqf b/A3XAI/compile/A3XAI_protectObject.sqf new file mode 100644 index 0000000..7fd5c6b --- /dev/null +++ b/A3XAI/compile/A3XAI_protectObject.sqf @@ -0,0 +1,8 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_objectMonitor","_object","_index"]; +_object = _this; + +_index = A3XAI_monitoredObjects pushBack _object; + +_index \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_purgeUnitGear.sqf b/A3XAI/compile/A3XAI_purgeUnitGear.sqf new file mode 100644 index 0000000..d7f9835 --- /dev/null +++ b/A3XAI/compile/A3XAI_purgeUnitGear.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +removeAllWeapons _this; +removeAllItems _this; +removeAllAssignedItems _this; +removeGoggles _this; +removeHeadgear _this; +removeBackpack _this; +removeUniform _this; +removeVest _this; \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_radioSend.sqf b/A3XAI/compile/A3XAI_radioSend.sqf new file mode 100644 index 0000000..78f1102 --- /dev/null +++ b/A3XAI/compile/A3XAI_radioSend.sqf @@ -0,0 +1,12 @@ +#include "\A3XAI\globaldefines.hpp" + +A3XAI_SMS = (_this select 1); + +if (isDedicated) then { + (owner (_this select 0)) publicVariableClient "A3XAI_SMS"; +} else { + A3XAI_SMS_PVS = [(_this select 0),A3XAI_SMS]; + publicVariableServer "A3XAI_SMS_PVS"; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_recycleGroup.sqf b/A3XAI/compile/A3XAI_recycleGroup.sqf new file mode 100644 index 0000000..e641541 --- /dev/null +++ b/A3XAI/compile/A3XAI_recycleGroup.sqf @@ -0,0 +1,87 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_unitType", "_trigger", "_respawn", "_vehicleType"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; +_vehicleType = _this select 2; + +if (_unitGroup getVariable ["isRecycling",false]) exitWith {}; +_unitGroup setVariable ["isRecycling",true]; + +_unitGroup setCombatMode "BLUE"; +[_unitGroup,true] call A3XAI_setNoAggroStatus; + +_unitType = _unitGroup getVariable ["unitType",""]; +_respawn = true; + +// diag_log format ["%1: %2",__FILE__,_this]; + +if (isDedicated) then { + if !(isNull _vehicle) then { + _vehicle setVariable ["DeleteVehicle",true]; + _vehicle setVariable ["VehicleDisabled",true]; + }; + + call { + if (_unitType isEqualTo "static") exitWith { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; //This block is non-HC compatible. + [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + }; + if (_unitType isEqualTo "random") exitWith { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; + [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + }; + if (_unitType isEqualTo "dynamic") exitWith { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; + [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + }; + if (_unitType isEqualTo "air") exitWith { + [2,_vehicleType,true] call A3XAI_addRespawnQueue; + A3XAI_curHeliPatrols = A3XAI_curHeliPatrols - 1; + }; + if (_unitType isEqualTo "land") exitWith { + [2,_vehicleType,true] call A3XAI_addRespawnQueue; + A3XAI_curLandPatrols = A3XAI_curLandPatrols - 1; + }; + if (_unitType isEqualTo "uav") exitWith { + [3,_vehicleType,true] call A3XAI_addRespawnQueue; + A3XAI_curUAVPatrols = A3XAI_curUAVPatrols - 1; + }; + if (_unitType isEqualTo "ugv") exitWith { + [3,_vehicleType,true] call A3XAI_addRespawnQueue; + A3XAI_curUGVPatrols = A3XAI_curUGVPatrols - 1; + }; + if (_unitType isEqualTo "air_reinforce") exitWith { + _respawn = false; + }; + if (_unitType isEqualTo "infantry_reinforce") exitWith { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; //This block is non-HC compatible. + 0 = [_trigger] spawn A3XAI_despawn_static; + _respawn = false; + }; + if (_unitType isEqualTo "vehiclecrew") exitWith { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; //This block is non-HC compatible. + 0 = [_trigger] spawn A3XAI_despawn_static; + _respawn = false; + }; + if (_unitType isEqualTo "staticcustom") exitWith { + _trigger = _unitGroup getVariable ["trigger",A3XAI_defaultTrigger]; //This block is non-HC compatible. + [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + }; + if (_unitType isEqualTo "aircustom") exitWith { + [1,_vehicleType,true] call A3XAI_addRespawnQueue; + A3XAI_curHeliPatrols = A3XAI_curHeliPatrols - 1; + }; + if (_unitType isEqualTo "landcustom") exitWith { + [1,_vehicleType,true] call A3XAI_addRespawnQueue; + A3XAI_curLandPatrols = A3XAI_curLandPatrols - 1; + }; + }; +} else { + diag_log format ["A3XAI Error: No HC handling implemented for %1",__FILE__]; +}; + +[_unitGroup,_respawn] call A3XAI_markGroupUnitsForDeletion; + +true diff --git a/A3XAI/compile/A3XAI_reinforce_begin.sqf b/A3XAI/compile/A3XAI_reinforce_begin.sqf new file mode 100644 index 0000000..683b943 --- /dev/null +++ b/A3XAI/compile/A3XAI_reinforce_begin.sqf @@ -0,0 +1,100 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_waypoint", "_vehicle", "_endTime", "_vehiclePos", "_nearUnits", "_vehPos", "_despawnPos", "_reinforcePos","_vehicleArmed","_paraDrop","_reinforceTime","_unitLevel","_canDeploy"]; + +_unitGroup = _this; + + +if (!hasInterface && !isDedicated) exitWith {diag_log format ["Error: %1 executed on headless client.",__FILE__];}; +if !((typeName _unitGroup) isEqualTo "GROUP") exitWith {diag_log format ["A3XAI Error: Invalid group %1 provided to %2.",_unitGroup,__FILE__];}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_vehicleArmed = ((({_x call A3XAI_checkIsWeapon} count (weapons _vehicle)) > 0) || {({_x call A3XAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) > 0} || {(_vehicle call A3XAI_countVehicleGunners) > 0}); +_reinforcePos = _unitGroup getVariable ["ReinforcePos",[0,0,0]]; +_canDeploy = true; + +if (_vehicleArmed) then { + _unitLevel = _unitGroup getVariable ["unitLevel",0]; + _canDeploy = (missionNamespace getVariable [format ["A3XAI_airReinforceDeployChance%1",_unitLevel],0.00]) call A3XAI_chance; +}; + +if (_canDeploy) then { + _paraDrop = [_unitGroup,_vehicle,objNull] spawn A3XAI_heliParaDrop; + waitUntil {uiSleep 0.1; scriptDone _paraDrop}; +} else { + _waypoint = [_unitGroup,0]; + _waypoint setWaypointStatements ["true",""]; + + //Original: + _waypoint setWaypointType "SAD"; + _waypoint setWaypointBehaviour "AWARE"; + _waypoint setWaypointCombatMode "RED"; + + if (local _unitGroup) then { + _unitGroup setCurrentWaypoint _waypoint; + } else { + A3XAI_setCurrentWaypoint_PVC = _waypoint; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_setCurrentWaypoint_PVC"; + }; + + _reinforceTime = missionNamespace getVariable [format ["A3XAI_airReinforcementDuration%1",_unitGroup getVariable ["unitLevel",0]],0]; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 is now reinforcing for %2 seconds.",_unitGroup,_reinforceTime];}; + + _unitGroup setSpeedMode "LIMITED"; + _endTime = diag_tickTime + _reinforceTime; + while {(diag_tickTime < _endTime) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {(_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce"}} do { + if (local _unitGroup) then { + _vehiclePos = getPosATL _vehicle; + _vehiclePos set [2,0]; + _nearUnits = _vehiclePos nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_AIR_REINFORCE]; + + { + if !(isPlayer _x) then { + _nearUnits deleteAt _forEachIndex; + }; + if (_forEachIndex > 4) exitWith {}; + } forEach _nearUnits; + + { + if ((isPlayer _x) && {(_unitGroup knowsAbout _x) < 3} && {(lineIntersectsSurfaces [(aimPos _vehicle),(eyePos _x),_vehicle,_x,true,1]) isEqualTo []}) then { + _unitGroup reveal [_x,3]; + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[31+(floor (random 5)),[name (leader _unitGroup)]]] call A3XAI_radioSend; + }; + }; + } forEach _nearUnits; + }; + uiSleep 10; + }; + _unitGroup setSpeedMode "NORMAL"; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 reinforcement timer complete.",_unitGroup];}; +}; + +if (((_unitGroup getVariable ["GroupSize",-1]) < 1) or {!((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce")}) exitWith { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Group %1 (type: %2) is no longer reinforcing, reinforce timer exited.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"])];}; +}; + +_vehPos = (getPosATL _vehicle); +_despawnPos = [_vehPos,AIR_REINFORCE_DESPAWN_DIST,random(360),1] call A3XAI_SHK_pos; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 Vehicle %2 (pos %3) assigned despawn pos %4.",_unitGroup,(typeOf _vehicle),_vehPos,_despawnPos];}; + +_waypoint = _unitGroup addWaypoint [_despawnPos,0]; +_waypoint setWaypointType "MOVE"; +_waypoint setWaypointBehaviour "CARELESS"; +if (_vehicleArmed) then {_waypoint setWaypointCombatMode "BLUE";}; +if (local _unitGroup) then { + _unitGroup setCurrentWaypoint _waypoint; +} else { + A3XAI_setCurrentWaypoint_PVC = _waypoint; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_setCurrentWaypoint_PVC"; +}; + +waitUntil {uiSleep 15; (((getPosATL _vehicle) distance2D _vehPos) > 1200) or {!(alive _vehicle)}}; + +_unitGroup call A3XAI_cleanupReinforcementGroup; +A3XAI_reinforcedPositions = A3XAI_reinforcedPositions - _reinforcePos; + +true diff --git a/A3XAI/compile/A3XAI_releaseVehicleAllow.sqf b/A3XAI/compile/A3XAI_releaseVehicleAllow.sqf new file mode 100644 index 0000000..4679b7d --- /dev/null +++ b/A3XAI/compile/A3XAI_releaseVehicleAllow.sqf @@ -0,0 +1,37 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_object","_vehicleClass"]; + +_object = _this; + +_vehicleClass = [configFile >> "CfgVehicles" >> (typeOf _object),"vehicleClass",""] call BIS_fnc_returnConfigEntry; +if ((toLower _vehicleClass) isEqualTo "autonomous") exitWith {}; + +_object removeAllEventHandlers "GetIn"; + +if (isDedicated) then { + _object call A3XAI_generateVehicleLoot; + _object addEventHandler ["GetIn",{ + if (isPlayer (_this select 2)) then { + (_this select 0) call A3XAI_releaseVehicleNow; + }; + }]; +}; + +if (local _object) then { + _object lock 1; + _object enableCopilot true; + _object enableRopeAttach true; + _object setFuel (random 1); + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Released AI vehicle %1 at %2 for player access.",(typeOf _object),(getPosATL _object)];}; +} else { + if (isDedicated && {(owner _object) isEqualTo A3XAI_HCObjectOwnerID}) then { + A3XAI_requestVehicleRelease_PVC = _object; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_requestVehicleRelease_PVC"; + } else { + diag_log format ["A3XAI Error: AI vehicle %1 is remote object but not owned by HC (OwnerID: %2, HC ID: %3).",_object,(owner _object),A3XAI_HCObjectOwnerID]; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_releaseVehicleNow.sqf b/A3XAI/compile/A3XAI_releaseVehicleNow.sqf new file mode 100644 index 0000000..50dedc5 --- /dev/null +++ b/A3XAI/compile/A3XAI_releaseVehicleNow.sqf @@ -0,0 +1,18 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_object"]; + +_object = _this; + +_object removeAllEventHandlers "GetIn"; +_object setVariable ["A3XAI_deathTime",nil]; +_object setVariable ["DeleteVehicle",false]; +_object addMPEventHandler ["MPKilled",{ + (_this select 0) call A3XAI_vehMPKilled; +}]; + +_object call A3XAI_addToExternalObjectMonitor; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Player entered AI %1 at %2. Vehicle A3XAI_deathTime variable set to nil.",(typeOf _object),(getPosATL _object)];}; + +true diff --git a/A3XAI/compile/A3XAI_reloadVehicleTurrets.sqf b/A3XAI/compile/A3XAI_reloadVehicleTurrets.sqf new file mode 100644 index 0000000..ecbf936 --- /dev/null +++ b/A3XAI/compile/A3XAI_reloadVehicleTurrets.sqf @@ -0,0 +1,27 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_reloadedTurrets","_vehicleTurrets"]; + +_vehicle = _this; + +_reloadedTurrets = false; + +_vehicleTurrets = allTurrets [_vehicle,false]; +{ + private ["_turretWeapons","_turretMagazine","_turretAmmoCount","_turretMagazines","_turretPath"]; + _turretWeapons = _vehicle weaponsTurret _x; + if !(_turretWeapons isEqualTo []) then { + _turretMagazines = _vehicle magazinesTurret _x; + _turretPath = _x; + { + _turretAmmoCount = _vehicle magazineTurretAmmo [_x,_turretPath]; + if (_turretAmmoCount isEqualTo 0) then { + _vehicle removeMagazinesTurret [_x, _turretPath]; + _vehicle addMagazineTurret [_x,_turretPath]; + if !(_reloadedTurrets) then {_reloadedTurrets = true}; + }; + } forEach _turretMagazines; + }; +} forEach _vehicleTurrets; + +_reloadedTurrets \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_relocateAutonomous.sqf b/A3XAI/compile/A3XAI_relocateAutonomous.sqf new file mode 100644 index 0000000..b050bcf --- /dev/null +++ b/A3XAI/compile/A3XAI_relocateAutonomous.sqf @@ -0,0 +1,61 @@ +#include "\A3XAI\globaldefines.hpp" + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_vehicleType = (typeOf _vehicle); +_isAirVehicle = (_vehicleType isKindOf "Air"); +_vehiclePosition = []; +_spawnMode = "NONE"; +_keepLooking = true; + +call { + if (([configFile >> "CfgVehicles" >> _vehicleType,"vehicleClass",""] call BIS_fnc_returnConfigEntry) != "Autonomous") exitWith {_error = true}; + if (_vehicleType isKindOf "Air") exitWith { + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + (random((getMarkerSize "A3XAI_centerMarker") select 0)),random(360),1] call A3XAI_SHK_pos; + _vehiclePosition set [2,200]; + _spawnMode = "FLY"; + }; + if (_vehicleType isKindOf "LandVehicle") exitWith { + while {_keepLooking} do { + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + random((getMarkerSize "A3XAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3XAI_SHK_pos; + if ((count _vehiclePosition) > 1) then { + if ({isPlayer _x} count (_vehiclePosition nearEntities [[PLAYER_UNITS,"AllVehicles"], PLAYER_DISTANCE_SPAWN_AUTONOMOUS]) isEqualTo 0) then { + _keepLooking = false; //Found road position, stop searching + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to find road position to spawn AI %1. Retrying in 15 seconds.",_vehicleType]}; + uiSleep 15; + }; + }; + }; +}; + +_vehicle setPos _vehiclePosition; + +call { + if (_vehicle isKindOf "Plane") exitWith { + _direction = (random 360); + _velocity = velocity _vehicle; + _vehicle setDir _direction; + _vehicle setVelocity [(_velocity select 1)*sin _direction - (_velocity select 0)*cos _direction, (_velocity select 0)*sin _direction + (_velocity select 1)*cos _direction, _velocity select 2]; + }; + if (_vehicle isKindOf "Helicopter") exitWith { + _vehicle setDir (random 360); + }; + if (_vehicle isKindOf "LandVehicle") exitWith { + _nearRoads = _vehiclePosition nearRoads 100; + if !(_nearRoads isEqualTo []) then { + _nextRoads = roadsConnectedTo (_nearRoads select 0); + if !(_nextRoads isEqualTo []) then { + _direction = [_vehicle,(_nextRoads select 0)] call BIS_fnc_relativeDirTo; + _vehicle setDir _direction; + //diag_log format ["Debug: Reoriented vehicle %1 to direction %2.",_vehicle,_direction]; + }; + } else { + _vehicle setDir (random 360); + }; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_relocateVehicleGroup.sqf b/A3XAI/compile/A3XAI_relocateVehicleGroup.sqf new file mode 100644 index 0000000..bc565d1 --- /dev/null +++ b/A3XAI/compile/A3XAI_relocateVehicleGroup.sqf @@ -0,0 +1,64 @@ +#include "\A3XAI\globaldefines.hpp" + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_vehicleType = (typeOf _vehicle); +_isAirVehicle = (_vehicleType isKindOf "Air"); +_vehiclePosition = []; +_spawnMode = "NONE"; +_keepLooking = true; +_isStandard = (((_unitGroup getVariable ["unitType",""]) find "custom") isEqualTo -1); + +if (_isStandard) then { + call { + if (_vehicleType isKindOf "Air") exitWith { + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + (random((getMarkerSize "A3XAI_centerMarker") select 0)),random(360),1] call A3XAI_SHK_pos; + _vehiclePosition set [2,200]; + _spawnMode = "FLY"; + }; + if (_vehicleType isKindOf "LandVehicle") exitWith { + while {_keepLooking} do { + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + random((getMarkerSize "A3XAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3XAI_SHK_pos; + if ((count _vehiclePosition) > 1) then { + if ({isPlayer _x} count (_vehiclePosition nearEntities [[PLAYER_UNITS,"AllVehicles"], PLAYER_DISTANCE_SPAWN_AUTONOMOUS]) isEqualTo 0) then { + _keepLooking = false; //Found road position, stop searching + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to find road position to spawn AI %1. Retrying in 15 seconds.",_vehicleType]}; + uiSleep 15; + }; + }; + }; + }; +} else { +}; + +_vehicle setPos _vehiclePosition; + +call { + if (_vehicle isKindOf "Plane") exitWith { + _direction = (random 360); + _velocity = velocity _vehicle; + _vehicle setDir _direction; + _vehicle setVelocity [(_velocity select 1)*sin _direction - (_velocity select 0)*cos _direction, (_velocity select 0)*sin _direction + (_velocity select 1)*cos _direction, _velocity select 2]; + }; + if (_vehicle isKindOf "Helicopter") exitWith { + _vehicle setDir (random 360); + }; + if (_vehicle isKindOf "LandVehicle") exitWith { + _nearRoads = _vehiclePosition nearRoads 100; + if !(_nearRoads isEqualTo []) then { + _nextRoads = roadsConnectedTo (_nearRoads select 0); + if !(_nextRoads isEqualTo []) then { + _direction = [_vehicle,(_nextRoads select 0)] call BIS_fnc_relativeDirTo; + _vehicle setDir _direction; + //diag_log format ["Debug: Reoriented vehicle %1 to direction %2.",_vehicle,_direction]; + }; + } else { + _vehicle setDir (random 360); + }; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_removeExplosive.sqf b/A3XAI/compile/A3XAI_removeExplosive.sqf new file mode 100644 index 0000000..40110fc --- /dev/null +++ b/A3XAI/compile/A3XAI_removeExplosive.sqf @@ -0,0 +1,43 @@ +#include "\A3XAI\globaldefines.hpp" + +if ((typeName _this) != "OBJECT") exitWith {}; + +private ["_vehicleWeapons","_cursorAim","_missileMags","_vehicleMags","_vehicleTurrets"]; + +_vehicleWeapons = +(weapons _this); +_vehicleMags = +(magazines _this); +_vehicleTurrets = allTurrets [_this,false]; +if !([-1] in _vehicleTurrets) then {_vehicleTurrets pushBack [-1];}; + +{ + private ["_ammo","_explosiveRating"]; + _ammo = [configFile >> "CfgMagazines" >> _x,"ammo",""] call BIS_fnc_returnConfigEntry; + _explosiveRating = [configFile >> "CfgAmmo" >> _ammo,"explosive",0] call BIS_fnc_returnConfigEntry; + if (_explosiveRating > AI_VEHICLEWEAPON_EXPLOSIVERATING_LIMIT) then { + _this removeMagazines _x; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Removed explosive magazine %1 from vehicle %2.",_x,(typeOf _this)];}; + }; +} forEach _vehicleMags; + +{ + private ["_currentTurret","_turretWeapons"]; + _turretWeapons = _this weaponsTurret _x; + //diag_log format ["DEBUG WEAPONS: %1",_turretWeapons]; + _currentTurret = _x; + { + private ["_currentTurretWeapon","_turretMags"]; + _currentTurretWeapon = _x; + _turretMags = _this magazinesTurret _currentTurret; + { + private ["_ammo","_explosiveRating"]; + _ammo = [configFile >> "CfgMagazines" >> _x,"ammo",""] call BIS_fnc_returnConfigEntry; + _explosiveRating = [configFile >> "CfgAmmo" >> _ammo,"explosive",0] call BIS_fnc_returnConfigEntry; + if (_explosiveRating > AI_VEHICLEWEAPON_EXPLOSIVERATING_LIMIT) then { + _this removeMagazinesTurret [_x,_currentTurret]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Removed explosive magazine %1 from vehicle %2.",_x,(typeOf _this)];}; + }; + } forEach _turretMags; + } forEach _turretWeapons; +} forEach _vehicleTurrets; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_respawnAIVehicle.sqf b/A3XAI/compile/A3XAI_respawnAIVehicle.sqf new file mode 100644 index 0000000..f7aebe5 --- /dev/null +++ b/A3XAI/compile/A3XAI_respawnAIVehicle.sqf @@ -0,0 +1,41 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle", "_vehicleType", "_spawnParams","_release"]; + +_vehicle = _this select 0; +_release = _this select 1; + +if (isNull _vehicle) exitWith {diag_log format ["Error: %1 attempted to respawn null vehicle",__FILE__];}; + +if (isDedicated) then { + _vehicleType = (typeOf _vehicle); + _spawnParams = _vehicle getVariable ["spawnParams",[]]; + _vehicleClass = [configFile >> "CfgVehicles" >> _vehicleType,"vehicleClass",""] call BIS_fnc_returnConfigEntry; + if ((_vehicleClass) != "autonomous") then { + if (_spawnParams isEqualTo []) then { + [2,_vehicleType] call A3XAI_addRespawnQueue; + } else { + if (_spawnParams select 4) then { + [1,_spawnParams] call A3XAI_addRespawnQueue; + }; + }; + if (_vehicleType isKindOf "Air") then {A3XAI_curHeliPatrols = A3XAI_curHeliPatrols - 1} else {A3XAI_curLandPatrols = A3XAI_curLandPatrols - 1}; + } else { + [3,_vehicleType] call A3XAI_addRespawnQueue; + if (_vehicleType isKindOf "Air") then {A3XAI_curUAVPatrols = A3XAI_curUAVPatrols - 1} else {A3XAI_curUGVPatrols = A3XAI_curUGVPatrols - 1}; + }; + _vehicle setVariable ["A3XAI_deathTime",diag_tickTime]; //mark vehicle for cleanup +} else { + A3XAI_respawnVehicle_PVS = [_vehicle,_release]; + publicVariableServer "A3XAI_respawnVehicle_PVS"; +}; + +if (A3XAI_vehiclesAllowedForPlayers && {alive _vehicle} && {_release}) then { + _vehicle call A3XAI_releaseVehicleAllow; +} else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Respawning AI %1.",(typeOf _vehicle)]}; +}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","Killed","GetOut","Local","Hit"]; + +true diff --git a/A3XAI/compile/A3XAI_respawnGroup.sqf b/A3XAI/compile/A3XAI_respawnGroup.sqf new file mode 100644 index 0000000..981997e --- /dev/null +++ b/A3XAI/compile/A3XAI_respawnGroup.sqf @@ -0,0 +1,93 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_trigger","_patrolDist","_spawnPositions","_spawnPos","_startTime","_maxUnits","_totalAI","_aiGroup","_unitLevel","_unitLevelEffective", "_checkPos","_spawnRadius", +"_unitType","_spawnChance","_playerDistNoLOS","_playerDistWithLOS","_isStandardGroup"]; + +_startTime = diag_tickTime; + +_unitGroup = _this select 0; +_trigger = _this select 1; +_maxUnits = _this select 2; + +_patrolDist = _trigger getVariable ["patrolDist",150]; +_unitLevel = _trigger getVariable ["unitLevel",1]; +_unitLevelEffective = _trigger getVariable ["unitLevelEffective",1]; +_spawnPositions = _trigger getVariable ["locationArray",[]]; + +//Check unit type +_unitType = _unitGroup getVariable ["unitType",""]; +if (_unitType isEqualTo "") then { + _unitType = _trigger getVariable ["spawnType",""]; + [_unitGroup,_unitType] call A3XAI_setUnitType; +}; + +_isStandardGroup = ((_unitType find "custom") isEqualTo -1); +_playerDistWithLOS = if (_isStandardGroup) then {PLAYER_DISTANCE_WITH_LOS_STATIC} else {PLAYER_DISTANCE_WITH_LOS_STATIC_CUSTOM}; +_playerDistNoLOS = if (_isStandardGroup) then {PLAYER_DISTANCE_NO_LOS_STATIC} else {PLAYER_DISTANCE_NO_LOS_STATIC_CUSTOM}; + +_totalAI = 0; +_spawnPos = []; +_checkPos = false; +_spawnChance = ((_trigger getVariable ["spawnChance",1]) * A3XAI_spawnChanceMultiplier); + +if (_spawnChance call A3XAI_chance) then { + _totalAI = ((_maxUnits select 0) + round(random (_maxUnits select 1))); + if !(_spawnPositions isEqualTo []) then { + _spawnPos = _spawnPositions call A3XAI_findSpawnPos; + } else { + _checkPos = true; + _attempts = 0; + _continue = true; + _spawnRadius = _patrolDist; + + while {_continue && {(_attempts < 3)}} do { + _spawnPosSelected = [(getPosATL _trigger),random (_spawnRadius),random(360),0] call A3XAI_SHK_pos; + _spawnPosSelASL = ATLToASL _spawnPosSelected; + if ((count _spawnPosSelected) isEqualTo 2) then {_spawnPosSelected set [2,0];}; + if ( + !((_spawnPosSelASL) call A3XAI_posInBuilding) && + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPosSelected select 0),(_spawnPosSelected select 1),(_spawnPosSelASL select 2) + 1.7],_x] call A3XAI_hasLOS) or ((_x distance _spawnPosSelected) < _playerDistNoLOS)}) exitWith {1}} count (_spawnPosSelected nearEntities [[PLAYER_UNITS,"LandVehicle"],_playerDistWithLOS])) isEqualTo 0} + ) then { + _spawnPos = _spawnPosSelected; + _continue = false; + } else { + _attempts = _attempts + 1; + _spawnRadius = _spawnRadius + 25; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Unable to find suitable spawn position. (attempt %1/3).",_attempts];}; + }; + }; + }; +}; + +if ((_totalAI < 1) or {_spawnPos isEqualTo []}) exitWith { + //_unitGroup setVariable ["GroupSize",0]; + [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + false +}; + +_aiGroup = [_totalAI,_unitGroup,_unitType,_spawnPos,_trigger,_unitLevelEffective,"LIMITED",_checkPos] call A3XAI_spawnGroup; +if (isNull _unitGroup) then {diag_log format ["A3XAI Error: Respawned group at %1 was null group. New group reassigned: %2.",_trigger getVariable ["TriggerText","Unknown Trigger"],_aiGroup]; _unitGroup = _aiGroup}; +//if (_unitLevel != _unitLevelEffective) then {_trigger setVariable ["unitLevelEffective",_unitLevel]}; //Reset unitLevel after respawning promoted group +if (_patrolDist > 1) then { + if ((count (waypoints _unitGroup)) > 1) then { + _unitGroup setCurrentWaypoint ((waypoints _unitGroup) call A3XAI_selectRandom); + } else { + _nul = [_unitGroup,(getPosATL _trigger),_patrolDist] spawn A3XAI_BIN_taskPatrol; + }; +} else { + [_unitGroup, 0] setWaypointType "GUARD"; +}; + +[_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + +if (_unitType in A3XAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +if (A3XAI_enableDebugMarkers) then { + _nul = [_trigger,str(_trigger)] call A3XAI_addMapMarker; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: %1 AI units respawned for group %2 (unitLevel %3) at %4 in %5 seconds.",_totalAI,_unitGroup,_unitLevelEffective,(_trigger getVariable ["TriggerText","Unknown Trigger"]),diag_tickTime - _startTime];}; + +true diff --git a/A3XAI/compile/A3XAI_returnNoAggroAreaActive.sqf b/A3XAI/compile/A3XAI_returnNoAggroAreaActive.sqf new file mode 100644 index 0000000..109daf4 --- /dev/null +++ b/A3XAI/compile/A3XAI_returnNoAggroAreaActive.sqf @@ -0,0 +1,17 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_noAggroArea", "_objectPos"]; + +_objectPos = _this select 0; +_noAggroRange = [_this,1,900] call A3XAI_param; + +_noAggroArea = objNull; +if ((typeName _this) isEqualTo "OBJECT") then {_objectPos = getPosATL _this}; + +{ + if (((position _x) distance2D _objectPos) < _noAggroRange) exitWith { + _noAggroArea = _x; + }; +} forEach A3XAI_activeNoAggroAreas; + +_noAggroArea diff --git a/A3XAI/compile/A3XAI_returnNoAggroAreaAll.sqf b/A3XAI/compile/A3XAI_returnNoAggroAreaAll.sqf new file mode 100644 index 0000000..3bcf82a --- /dev/null +++ b/A3XAI/compile/A3XAI_returnNoAggroAreaAll.sqf @@ -0,0 +1,17 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_noAggroArea", "_objectPos"]; + +_objectPos = _this select 0; +_noAggroRange = [_this,1,900] call A3XAI_param; + +_noAggroArea = objNull; +if ((typeName _this) isEqualTo "OBJECT") then {_objectPos = getPosATL _this}; + +{ + if (((position _x) distance2D _objectPos) < _noAggroRange) exitWith { + _noAggroArea = _x; + }; +} forEach A3XAI_noAggroAreas; + +_noAggroArea diff --git a/A3XAI/compile/A3XAI_secureVehicle.sqf b/A3XAI/compile/A3XAI_secureVehicle.sqf new file mode 100644 index 0000000..e4367e7 --- /dev/null +++ b/A3XAI/compile/A3XAI_secureVehicle.sqf @@ -0,0 +1,18 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_object"]; + +_object = _this; + +_object addEventHandler ["GetIn",{ + if (isPlayer (_this select 2)) then { + (_this select 2) action ["getOut",(_this select 0)]; + if ((_this select 0) getVariable ["VehicleDisabled",false]) then {deleteVehicle (_this select 0);}; + }; +}]; + +_object setVehicleLock "LOCKEDPLAYER"; +_object enableCopilot false; +_object enableRopeAttach false; + +true diff --git a/A3XAI/compile/A3XAI_selectRandom.sqf b/A3XAI/compile/A3XAI_selectRandom.sqf new file mode 100644 index 0000000..02859ac --- /dev/null +++ b/A3XAI/compile/A3XAI_selectRandom.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +//scriptName "Functions\arrays\fn_selectRandom.sqf"; +/************************************************************ + Random Select + By Andrew Barron + +Parameters: array + +This returns a randomly selected element from the passed array. + +Example: [1,2,3] call BIS_fnc_selectRandom +Returns: 1, 2, or 3 +************************************************************/ + +private "_ret"; + +_ret = count _this; //number of elements in the array +if (_ret > 0) then { + _ret = floor (random _ret); //floor it first + _ret = _this select _ret; //get the element, return it +}; +_ret \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_sendExileMessage.sqf b/A3XAI/compile/A3XAI_sendExileMessage.sqf new file mode 100644 index 0000000..fb0a050 --- /dev/null +++ b/A3XAI/compile/A3XAI_sendExileMessage.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_messageName","_messageParameters","_player"]; +_player = _this select 0; +_messageName = _this select 1; +_messageParameters = _this select 2; + +_publicMessage = [_messageName, _messageParameters]; +_publicMessage remoteExecCall ["ExileClient_system_network_dispatchIncomingMessage",(owner _player)]; + +true diff --git a/A3XAI/compile/A3XAI_sendKillResponse.sqf b/A3XAI/compile/A3XAI_sendKillResponse.sqf new file mode 100644 index 0000000..c1da0bd --- /dev/null +++ b/A3XAI/compile/A3XAI_sendKillResponse.sqf @@ -0,0 +1,129 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_killer", "_victim", "_killerRespectPoints", "_fragAttributes", "_killerPlayerUID", "_lastKillAt", "_vehicleKiller", "_killStack", "_distance", "_distanceBonus", "_overallRespectChange", "_newKillerScore", "_killMessage", "_newKillerFrags","_collision"]; + +_killer = _this select 0; +_victim = _this select 1; +_collision = _this select 2; + +_fragAttributes = []; +_killerPlayerUID = getPlayerUID _killer; +_vehicleKiller = (vehicle _killer); + +{ + if ((getPlayerUID _x) isEqualTo _killerPlayerUID) exitWith { + _killer = _x; + }; +} forEach (crew _vehicleKiller); + +if (A3XAI_enableRespectRewards) then { + _killerRespectPoints = []; + + if (_vehicleKiller isEqualTo _killer) then { + if (currentWeapon _killer isEqualTo "Exile_Melee_Axe") then { + if (A3XAI_respectHumiliation > 0) then { + _fragAttributes pushBack "Humiliation"; + _killerRespectPoints pushBack ["HUMILIATION", A3XAI_respectHumiliation]; + }; + } else { + if (A3XAI_respectFragged > 0) then { + //_fragAttributes pushBack "ENEMY FRAGGED"; + _fragAttributes pushBack "ENEMY AI FRAGGED"; + //_killerRespectPoints pushBack ["ENEMY FRAGGED", A3XAI_respectFragged]; + _killerRespectPoints pushBack ["ENEMY AI FRAGGED", A3XAI_respectFragged]; + }; + }; + } else { + if (_collision) then { + call { + if (_vehicleKiller isKindOf "ParachuteBase") exitWith { + if (A3XAI_respectChute > 0) then { + _fragAttributes pushBack "Chute > Chopper"; + _killerRespectPoints pushBack ["CHUTE > CHOPPER", A3XAI_respectChute]; + }; + }; + if (_vehicleKiller isKindOf "Air") exitWith { + if (A3XAI_respectBigBird > 0) then { + _fragAttributes pushBack "Big Bird"; + _killerRespectPoints pushBack ["BIG BIRD", A3XAI_respectBigBird]; + }; + }; + if (A3XAI_respectRoadkill > 0) then { + _fragAttributes pushBack "Road Kill"; + _killerRespectPoints pushBack ["Road Kill", A3XAI_respectRoadkill]; + }; + }; + } else { + if (A3XAI_respectLetItRain > 0) then { + _fragAttributes pushBack "Let it Rain"; + _killerRespectPoints pushBack ["LET IT RAIN", A3XAI_respectLetItRain]; + }; + }; + }; + + _lastKillAt = _killer getVariable ["A3XAI_LastKillAt", 0]; + _killStack = _killer getVariable ["A3XAI_KillStack", 0]; + if ((diag_tickTime - _lastKillAt) < (getNumber (configFile >> "CfgSettings" >> "Respect" >> "Bonus" >> "killStreakTimeout"))) then { + if (A3XAI_respectKillstreak > 0) then { + _killStack = _killStack + 1; + _fragAttributes pushBack (format ["%1x Kill Streak", _killStack]); + _killerRespectPoints pushBack [(format ["%1x KILL STREAK", _killStack]), _killStack * A3XAI_respectKillstreak]; + }; + + } else { + _killStack = 1; + }; + _killer setVariable ["A3XAI_KillStack", _killStack]; + _killer setVariable ["A3XAI_LastKillAt", diag_tickTime]; + + _distance = floor (_victim distance _killer); + _fragAttributes pushBack (format ["%1m Distance", _distance]); + _distanceBonus = ((floor (_distance / 100)) * A3XAI_respectPer100m); + if (_distanceBonus > 0) then { + _killerRespectPoints pushBack [(format ["%1m RANGE BONUS", _distance]), _distanceBonus]; + }; + + _overallRespectChange = 0; + { + _overallRespectChange = _overallRespectChange + (_x select 1); + } forEach _killerRespectPoints; + + if (_overallRespectChange > 0) then { + _newKillerScore = _killer getVariable ["ExileScore", 0]; + _newKillerScore = _newKillerScore + _overallRespectChange; + _killer setVariable ["ExileScore", _newKillerScore]; + format["setAccountScore:%1:%2", _newKillerScore,_killerPlayerUID] call ExileServer_system_database_query_fireAndForget; + [_killer, "showFragRequest", [_killerRespectPoints]] call A3XAI_sendExileMessage; + }; + + //["systemChatRequest", [_killMessage]] call ExileServer_system_network_send_broadcast; //To-do: Non-global version + _newKillerFrags = _killer getVariable ["ExileKills", 0]; + _killer setVariable ["ExileKills", _newKillerFrags + 1]; + format["addAccountKill:%1", _killerPlayerUID] call ExileServer_system_database_query_fireAndForget; + + _killer call ExileServer_object_player_sendStatsUpdate; +}; + + +if (A3XAI_enableDeathMessages) then { + _killMessage = format ["%1 was killed by %2", _victim getVariable ["bodyName","Bandit"], (name _killer)]; + + if !(_fragAttributes isEqualTo []) then { + _killMessage = _killMessage + " ("; + { + if (_forEachIndex > 0) then { + _killMessage = _killMessage + ", "; + }; + _killMessage = _killMessage + _x; + } forEach _fragAttributes; + _killMessage = _killMessage + ")"; + }; + + { + if (isPlayer _x) then { + [_x, "systemChatRequest", [_killMessage]] call A3XAI_sendExileMessage; + }; + } count (units (group _killer)); +}; + +true diff --git a/A3XAI/compile/A3XAI_setFirstWPPos.sqf b/A3XAI/compile/A3XAI_setFirstWPPos.sqf new file mode 100644 index 0000000..e70b06c --- /dev/null +++ b/A3XAI/compile/A3XAI_setFirstWPPos.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_position","_waypoint","_result"]; +_unitGroup = _this select 0; +_position = _this select 1; +_result = false; + +if !(surfaceIsWater _position) then { + _waypoint = [_unitGroup,0]; + _waypoint setWaypointType "MOVE"; + _waypoint setWaypointCompletionRadius 40; + _waypoint setWaypointTimeout [3,4,5]; + _waypoint setWPPos _position; + if (local _unitGroup) then { + _unitGroup setCurrentWaypoint _waypoint; + } else { + A3XAI_setCurrentWaypoint_PVC = _waypoint; + A3XAI_HCObjectOwnerID publicVariableClient "A3XAI_setCurrentWaypoint_PVC"; + }; + _result = true; +}; + +_result \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_setLoadoutVariables.sqf b/A3XAI/compile/A3XAI_setLoadoutVariables.sqf new file mode 100644 index 0000000..d97a8a0 --- /dev/null +++ b/A3XAI/compile/A3XAI_setLoadoutVariables.sqf @@ -0,0 +1,114 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitType", "_unitGroup", "_loadout", "_weapon", "_magazine", "_useLaunchers", "_maxLaunchers", "_unitLevel", "_launchWeapon", "_launchAmmo","_launchersAdded"]; + +_unitGroup = _this select 0; +_unitType = _this select 1; +_unitLevel = _this select 2; + +if (_unitGroup getVariable ["VariablesSet",false]) exitWith {}; + +if !(_unitType in ["uav","ugv"]) then { + _useLaunchers = if !(A3XAI_levelRequiredLauncher isEqualTo -1) then {((count A3XAI_launcherTypes) > 0) && {(_unitLevel >= A3XAI_levelRequiredLauncher)}} else {false}; + _maxLaunchers = if (_useLaunchers) then {A3XAI_launchersPerGroup min _unitLevel} else {0}; + _launchersAdded = 0; + + _unitGroup setVariable ["LootPool",[]]; + _unitGroup spawn A3XAI_generateLootPool; + + //Set up individual group units + { + _loadout = _x getVariable "loadout"; + if (isNil "_loadout") then { + _weapon = primaryWeapon _x; + _magazine = getArray (configFile >> "CfgWeapons" >> _weapon >> "magazines") select 0; + _loadout = [[_weapon],[_magazine]]; + _x setVariable ["loadout",_loadout]; + + if (_launchersAdded < _maxLaunchers) then { + _launchWeapon = A3XAI_launcherTypes call A3XAI_selectRandom; + _launchAmmo = getArray (configFile >> "CfgWeapons" >> _launchWeapon >> "magazines") select 0; + if (_x canAdd _launchAmmo) then { + _x addMagazine _launchAmmo; + _x addWeapon _launchWeapon; + (_loadout select 1) pushBack _launchAmmo; + (_loadout select 0) pushBack _launchWeapon; + _launchersAdded = _launchersAdded + 1; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Successfully added weapon %1 and ammo %2 to unit %3.",_launchWeapon,_launchAmmo,_x];}; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to add weapon %1 and ammo %2 to unit %3.",_launchWeapon,_launchAmmo,_x];}; + }; + }; + }; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 Unit %2 loadout: %3. unitLevel %4.",_unitType,_x,_x getVariable ["loadout",[]],_unitLevel];}; + } forEach (units _unitGroup); +}; + +if (A3XAI_enableDebugMarkers) then { + _groupLeadMarker = format ["%1_Lead",_unitGroup]; + if (_groupLeadMarker in allMapMarkers) then { + deleteMarker _groupLeadMarker + }; //Delete the previous marker if it wasn't deleted for some reason. + _groupLeadMarker = createMarker [_groupLeadMarker, (leader _unitGroup)]; + _groupLeadMarker setMarkerType "mil_warning"; + _groupLeadMarker setMarkerBrush "Solid"; + + if (isNull _vehicle) then { + _groupLeadMarker setMarkerText format ["%1 (AI L%2)",_unitGroup,_unitLevel]; + } else { + _groupLeadMarker setMarkerText format ["%1 (AI L%2 %3)",_unitGroup,_unitLevel,(typeOf (vehicle (leader _unitGroup)))]; + }; + + _groupWPMarker = format ["%1_WP",_unitGroup]; + if (_groupWPMarker in allMapMarkers) then { + deleteMarker _groupWPMarker; + }; //Delete the previous marker if it wasn't deleted for some reason. + _groupWPMarker = createMarker [_groupWPMarker,(getWPPos [_unitGroup,(currentWaypoint _unitGroup)])]; + _groupWPMarker setMarkerText format ["%1 Waypoint",_unitGroup]; + _groupWPMarker setMarkerType "Waypoint"; + _groupWPMarker setMarkerColor "ColorBlue"; + _groupWPMarker setMarkerBrush "Solid"; + + [_unitGroup] spawn { + _unitGroup = _this select 0; + { + _markname = str(_x); + if (_markname in allMapMarkers) then { + deleteMarker _markname; + }; + _mark = createMarker [_markname, _x]; + _mark setMarkerShape "ELLIPSE"; + _mark setMarkerType "Dot"; + _mark setMarkerColor "ColorRed"; + _mark setMarkerBrush "SolidBorder"; + _nul = _x spawn { + _markername = str (_this); + _unitGroup = group _this; + while {alive _this} do { + if (local _this) then { + _unitPos = getPosATL _this; + if ((leader _unitGroup) isEqualTo _this) then { + (format ["%1_Lead",_unitGroup]) setMarkerPos _unitPos; + _color = call { + _combatMode = (combatMode _unitGroup); + if (_combatMode isEqualTo "YELLOW") exitWith {"ColorBlack"}; + if (_combatMode isEqualTo "RED") exitWith {"ColorRed"}; + if (_combatMode isEqualTo "BLUE") exitWith {"ColorBlue"}; + "ColorBlack" + }; + (format ["%1_Lead",_unitGroup]) setMarkerColor _color; + (format ["%1_WP",_unitGroup]) setMarkerPos (getWPPos [_unitGroup,(currentWaypoint _unitGroup)]); + }; + _markername setMarkerPos _unitPos; + }; + uiSleep 10; + }; + deleteMarker _markername; + }; + } forEach (units _unitGroup); + }; +}; + +_unitGroup setVariable ["VariablesSet",true]; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_setMarkerType.sqf b/A3XAI/compile/A3XAI_setMarkerType.sqf new file mode 100644 index 0000000..69a19e1 --- /dev/null +++ b/A3XAI/compile/A3XAI_setMarkerType.sqf @@ -0,0 +1,26 @@ +#include "\A3XAI\globaldefines.hpp" + +/* +private ["_trigger", "_objectString", "_mapMarkerArray"]; + +_trigger = _this select 0; +_objectString = _this select 1; + +if !(isNull _trigger) then { + if !(_objectString in allMapMarkers) then { + _objectString = createMarker [_objectString, _trigger]; + _objectString setMarkerType "mil_warning"; + _objectString setMarkerBrush "Solid"; + _mapMarkerArray = missionNamespace getVariable ["A3XAI_mapMarkerArray",[]]; + _mapMarkerArray pushBack _objectString; + }; + + _objectString setMarkerText "STATIC TRIGGER (ACTIVE)"; + _objectString setMarkerColor "ColorRed"; +}; + +// diag_log format ["%1 %2",__FILE__,_this]; + +*/ + +true diff --git a/A3XAI/compile/A3XAI_setNoAggroStatus.sqf b/A3XAI/compile/A3XAI_setNoAggroStatus.sqf new file mode 100644 index 0000000..3f59048 --- /dev/null +++ b/A3XAI/compile/A3XAI_setNoAggroStatus.sqf @@ -0,0 +1,10 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_status"]; + +_unitGroup = _this select 0; +_status = _this select 1; + +_unitGroup setVariable ["NoAggroStatus",_status]; + +_status diff --git a/A3XAI/compile/A3XAI_setRandomWaypoint.sqf b/A3XAI/compile/A3XAI_setRandomWaypoint.sqf new file mode 100644 index 0000000..71bc3da --- /dev/null +++ b/A3XAI/compile/A3XAI_setRandomWaypoint.sqf @@ -0,0 +1,11 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_currentWaypoint", "_allWaypoints", "_selectedWaypoint"]; + +_unitGroup = _this select 0; +_currentWaypoint = (currentWaypoint _unitGroup); +_allWaypoints = (waypoints _unitGroup) - [_unitGroup,_currentWaypoint]; +_selectedWaypoint = _allWaypoints call A3XAI_selectRandom; +_unitGroup setCurrentWaypoint _selectedWaypoint; + +_selectedWaypoint diff --git a/A3XAI/compile/A3XAI_setSkills.sqf b/A3XAI/compile/A3XAI_setSkills.sqf new file mode 100644 index 0000000..d5ec118 --- /dev/null +++ b/A3XAI/compile/A3XAI_setSkills.sqf @@ -0,0 +1,12 @@ +#include "\A3XAI\globaldefines.hpp" + +private["_unit","_unitLevel","_skillSeed","_skillArray","_skillTypeArray"]; +_unit = _this select 0; +_unitLevel = _this select 1; +_skillArray = missionNamespace getVariable ["A3XAI_skill"+str(_unitLevel),[ ["aimingAccuracy",0.05,0.10],["aimingShake",0.40,0.50],["aimingSpeed",0.40,0.50],["spotDistance",0.40,0.50],["spotTime",0.40,0.50],["courage",0.40,0.50],["reloadSpeed",0.40,0.50],["commanding",0.40,0.50],["general",0.40,0.50]]]; +_skillSeed = (random 1); +_skillTypeArray = ["aimingAccuracy","aimingShake","aimingSpeed","spotDistance","spotTime","courage","reloadSpeed","commanding","general"]; +{ + _unit setSkill [_skillTypeArray select _forEachIndex,((_x select 1) + random ((_x select 2)-(_x select 1))) min 1]; + //_unit setSkill [_skillTypeArray select _forEachIndex,linearConversion [0,1,_skillSeed,(_x select 1),((_x select 2) min 1),true]]; +} forEach _skillArray; diff --git a/A3XAI/compile/A3XAI_setSpawnParams.sqf b/A3XAI/compile/A3XAI_setSpawnParams.sqf new file mode 100644 index 0000000..67ae97f --- /dev/null +++ b/A3XAI/compile/A3XAI_setSpawnParams.sqf @@ -0,0 +1,22 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_spawnParams","_result"]; + +_trigger = _this select 0; +_spawnParams = _this select 1; + +_result = true; +if ((count _spawnParams) isEqualTo 4) then { + _trigger setVariable ["minAI",_spawnParams select 0]; + _trigger setVariable ["addAI",_spawnParams select 1]; + _trigger setVariable ["unitLevel",_spawnParams select 2]; + _trigger setVariable ["spawnChance",_spawnParams select 3]; +} else { + _trigger setVariable ["minAI",1]; + _trigger setVariable ["addAI",1]; + _trigger setVariable ["unitLevel",1]; + _trigger setVariable ["spawnChance",1]; + _result = false; +}; + +_result diff --git a/A3XAI/compile/A3XAI_setUnitType.sqf b/A3XAI/compile/A3XAI_setUnitType.sqf new file mode 100644 index 0000000..825476f --- /dev/null +++ b/A3XAI/compile/A3XAI_setUnitType.sqf @@ -0,0 +1,32 @@ +#include "\A3XAI\globaldefines.hpp" + +/* + _fnc_execEveryLoop = _dataArray select 0; + _fnc_checkUnits = _dataArray select 1; + _fnc_generateLoot = _dataArray select 2; + _fnc_vehicleAmmoFuelCheck = _dataArray select 3; + _fnc_antistuck = _dataArray select 4; +*/ + +private ["_unitGroup", "_unitType", "_groupVariables", "_dataArray", "_stuckCheckTime"]; + +_unitGroup = _this select 0; +_unitType =_this select 1; + +_unitGroup setVariable ["unitType",_unitType]; +_groupVariables = _unitGroup getVariable "GroupVariables"; + +_dataArray = [_unitGroup,_unitType] call A3XAI_getLocalFunctions; +_stuckCheckTime = _unitType call A3XAI_getAntistuckTime; +_dataArray pushBack _stuckCheckTime; + +if (isNil "_groupVariables") then { + _unitGroup setVariable ["GroupVariables",[]]; + _groupVariables = _unitGroup getVariable "GroupVariables"; + _groupVariables append _dataArray; +} else { + _groupVariables deleteRange [0,(count _groupVariables)]; + _groupVariables append _dataArray; +}; + +_groupVariables diff --git a/A3XAI/compile/A3XAI_setVehicleRegrouped.sqf b/A3XAI/compile/A3XAI_setVehicleRegrouped.sqf new file mode 100644 index 0000000..0f811e4 --- /dev/null +++ b/A3XAI/compile/A3XAI_setVehicleRegrouped.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup"]; + +_unitGroup = _this; + +if !(_unitGroup getVariable ["regrouped",false]) then { + _unitGroup setVariable ["regrouped",true]; + if !(isDedicated) then { + A3XAI_setVehicleRegrouped_PVS = _unitGroup; + publicVariableServer "A3XAI_setVehicleRegrouped_PVS"; + }; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_setup_randomspawns.sqf b/A3XAI/compile/A3XAI_setup_randomspawns.sqf new file mode 100644 index 0000000..7b7f0da --- /dev/null +++ b/A3XAI/compile/A3XAI_setup_randomspawns.sqf @@ -0,0 +1,64 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_maxRandomSpawns","_attempts","_trigPos","_trigger","_objectString","_marker"]; + +_maxRandomSpawns = _this; + +if (isNil SERVER_STARTED_INDICATOR) then { + private ["_expireTime"]; + _expireTime = diag_tickTime + SERVER_START_TIMEOUT; + waitUntil {uiSleep 3; (!isNil SERVER_STARTED_INDICATOR) or {diag_tickTime > _expireTime}}; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Attempting to place %1 random spawns on the map...",_maxRandomSpawns];}; + +for "_i" from 1 to _maxRandomSpawns do { + _attempts = 0; + _posCheckFail = true; + _trigPos = []; + while { + (_posCheckFail && {_attempts < MAX_RANDOMSPAWN_RETRY_ATTEMPTS}) + } do { + _attempts = _attempts + 1; + _trigPos = if (_attempts < MAX_RANDOMSPAWN_RETRY_ATTEMPTS) then { + _randomLocation = (A3XAI_locations call A3XAI_selectRandom) select 1; + [_randomLocation,CREATE_RANDOM_SPAWN_DIST_BASE+(random CREATE_RANDOM_SPAWN_DIST_VARIANCE),(random 360),0] call A3XAI_SHK_pos + } else { + ["A3XAI_centerMarker",0] call A3XAI_SHK_pos + }; + + _posCheckFail = ( + (({if (_trigPos in _x) exitWith {1}} count (nearestLocations [_trigPos,[BLACKLIST_OBJECT_GENERAL,BLACKLIST_OBJECT_RANDOM],1500])) > 0) || //Position not in blacklisted area + {({if ((_trigPos distance2D _x) < (TRIGGER_SIZE_NORMAL_DOUBLED + A3XAI_distanceBetweenRandomSpawns)) exitWith {1}} count A3XAI_randomTriggerArray) > 0} || //Not too close to another random spawn. + {!((_trigPos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])} //Position not blocked by a jammer + ); + if (_posCheckFail && {_attempts < MAX_RANDOMSPAWN_RETRY_ATTEMPTS}) then {uiSleep 0.25}; + }; + + if !(_posCheckFail) then { + _trigger = TRIGGER_OBJECT createVehicleLocal _trigPos; + _spawnParams = _trigPos call A3XAI_getSpawnParams; + _result = [_trigger,_spawnParams] call A3XAI_setSpawnParams; + _location = [_trigPos,TEMP_BLACKLIST_AREA_RANDOM_SIZE] call A3XAI_createBlackListAreaRandom; + _trigger setVariable ["triggerLocation",_location]; + [_trigger,"A3XAI_randomTriggerArray",true] call A3XAI_updateSpawnCount; + if (A3XAI_enableDebugMarkers) then { + _objectString = str(_trigger); + _marker = createMarker[_objectString,_trigPos]; + _marker setMarkerShape "ELLIPSE"; + _marker setMarkerType "Flag"; + _marker setMarkerBrush "SOLID"; + _marker setMarkerSize [TRIGGER_SIZE_RANDOM, TRIGGER_SIZE_RANDOM]; + _marker setMarkerColor "ColorYellow"; + _marker setMarkerAlpha 0.6; + _trigger setVariable ["MarkerName",_objectString]; + A3XAI_mapMarkerArray set [(count A3XAI_mapMarkerArray),_marker]; + }; + _trigger setVariable ["TriggerText",format ["Random Spawn at %1",(mapGridPosition _trigger)]]; + _trigger setVariable ["timestamp",diag_tickTime]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Random spawn %1 of %2 placed at %3 with params %4 (Attempts: %5).",_i,_maxRandomSpawns,_trigPos,_spawnParams,_attempts];}; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: Could not find suitable location to place random spawn %1 of %2.",_i,_maxRandomSpawns];}; + }; + uiSleep 3; +}; diff --git a/A3XAI/compile/A3XAI_spawnGroup.sqf b/A3XAI/compile/A3XAI_spawnGroup.sqf new file mode 100644 index 0000000..3e1a6ac --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnGroup.sqf @@ -0,0 +1,76 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_totalAI","_spawnPos","_unitGroup","_trigger","_attempts","_baseDist","_dummy","_unitLevel","_checkPos","_moveMode"]; + + +_totalAI = _this select 0; +_unitGroup = _this select 1; +_unitType = _this select 2; +_spawnPos = _this select 3; +_trigger = _this select 4; +_unitLevel = [_this,5,1] call A3XAI_param; +_moveMode = [_this,6,"LIMITED"] call A3XAI_param; +_checkPos = [_this,7,false] call A3XAI_param; + +if (_checkPos) then { //If provided position requires checking... + _pos = []; + _attempts = 0; + _baseDist = 15; + + while {(_pos isEqualTo []) && {(_attempts < 3)}} do { + _pos = _spawnPos findEmptyPosition [0.5,_baseDist,SPACE_FOR_OBJECT]; + if !(_pos isEqualTo []) then { + _pos = _pos isFlatEmpty [0,0,0.75,5,0,false,objNull]; + }; + + _attempts = (_attempts + 1); + if (_pos isEqualTo []) then { + if (_attempts < 3) then { + _baseDist = (_baseDist + 15); + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Found spawn position at %1 meters away at position %2 after %3 attempts.",(_pos distance _spawnPos),_pos,_attempts]}; + _spawnPos = _pos; + }; + }; +}; + +_spawnPos set [2,0]; + +if (({if (isPlayer _x) exitWith {1}} count (_spawnPos nearEntities [[PLAYER_UNITS,"LandVehicle"],PLAYER_DISTANCE_SPAWN_AIGROUP])) isEqualTo 1) exitWith { + grpNull +}; + +if (isNull _unitGroup) then { + _unitGroup = [_unitType] call A3XAI_createGroup; +}; + +for "_i" from 1 to (_totalAI max 1) do { + private ["_unit"]; + _unit = [_unitGroup,_unitLevel,_spawnPos,true] call A3XAI_createUnit; +}; + +//Delete dummy if it exists, and clear group's "dummy" variable. +_dummy = _unitGroup getVariable "dummyUnit"; +if (!isNil "_dummy") then { + deleteVehicle _dummy; + _unitGroup setVariable ["dummyUnit",nil]; + if (A3XAI_debugLevel > 1) then {diag_log format["A3XAI Debug: Deleted 1 dummy unit for group %1.",_unitGroup];}; +}; + +_unitGroup selectLeader ((units _unitGroup) select 0); +_unitGroup setVariable ["trigger",_trigger]; +_unitGroup setVariable ["GroupSize",_totalAI]; +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setFormDir (random 360); +_unitGroup setSpeedMode _moveMode; +_unitGroup setCombatMode "YELLOW"; +_unitGroup allowFleeing 0; + +0 = [_unitGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; //start group-level manager + +if (_unitType in A3XAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +_unitGroup diff --git a/A3XAI/compile/A3XAI_spawnInfantryCustom.sqf b/A3XAI/compile/A3XAI_spawnInfantryCustom.sqf new file mode 100644 index 0000000..cd79579 --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnInfantryCustom.sqf @@ -0,0 +1,76 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_totalAI", "_patrolDist", "_trigger", "_unitLevel", "_grpArray", "_triggerPos", "_maxUnits", "_attempts", "_continue", "_spawnPos", "_spawnPosSelected", "_unitGroup","_spawnRadius"]; + +_startTime = diag_tickTime; + +_totalAI = _this select 0; +//_this select 1; +_patrolDist = _this select 2; +_trigger = _this select 3; +_unitLevel = _this select 4; + +_grpArray = _trigger getVariable ["GroupArray",[]]; +if !(_grpArray isEqualTo []) exitWith {if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Active groups found at custom spawn %1. Exiting spawn script.",(_trigger getVariable ["TriggerText","Unknown Trigger"])];};}; + +_trigger setTriggerArea [TRIGGER_SIZE_EXPANDED,TRIGGER_SIZE_EXPANDED,0,false]; +_triggerPos = getPosATL _trigger; + +_startTime = diag_tickTime; + +if !(_trigger getVariable ["respawn",true]) then { + _maxUnits = _trigger getVariable ["maxUnits",[0,0]]; + if !(_maxUnits isEqualTo [0,0]) then {_totalAI = (_maxUnits select 0)}; //Retrieve AI amount if it was updated from initial value (for non-respawning custom spawns only) +}; + +_attempts = 0; +_continue = true; +_spawnPos = []; +_spawnRadius = _patrolDist; +while {_continue && {(_attempts < 3)}} do { + _spawnPosSelected = [_triggerPos,random (_patrolDist),random(360),0] call A3XAI_SHK_pos; + _spawnPosSelASL = ATLToASL _spawnPosSelected; + if ((count _spawnPosSelected) isEqualTo 2) then {_spawnPosSelected set [2,0];}; + if ( + !(_spawnPosSelASL call A3XAI_posInBuilding) && + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPosSelected select 0),(_spawnPosSelected select 1),(_spawnPosSelASL select 2) + 1.7],_x] call A3XAI_hasLOS) or ((_x distance _spawnPosSelected) < PLAYER_DISTANCE_NO_LOS_STATIC_CUSTOM)}) exitWith {1}} count (_spawnPosSelected nearEntities [[PLAYER_UNITS,"LandVehicle"],PLAYER_DISTANCE_WITH_LOS_STATIC_CUSTOM])) isEqualTo 0} + ) then { + _spawnPos = _spawnPosSelected; + _continue = false; + } else { + _attempts = _attempts + 1; + _spawnRadius = _spawnRadius + 25; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Unable to find suitable spawn position. (attempt %1/3).",_attempts];}; + }; +}; + +_unitGroup = grpNull; +try { + if !(_spawnPos isEqualTo []) then { + _unitGroup = [_totalAI,_unitGroup,"staticcustom",_spawnPos,_trigger,_unitLevel,"LIMITED",true] call A3XAI_spawnGroup; + if (isNull _unitGroup) then { + throw format ["A3XAI Debug: Custom static group spawn position too close to a player at %1. Spawn cancelled.",_spawnPos]; + }; + if (_patrolDist > 1) then { + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3XAI_BIN_taskPatrol; + } else { + [_unitGroup, 0] setWaypointType "HOLD"; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format["A3XAI Debug: Spawned a group of %1 units in %2 seconds at custom spawn %3.",_totalAI,(diag_tickTime - _startTime),(_trigger getVariable ["TriggerText","Unknown Trigger"])];}; + } else { + throw format["A3XAI Debug: Unable to find suitable spawn position at custom spawn %1.",(_trigger getVariable ["TriggerText","Unknown Trigger"])]; + }; +} catch { + _unitGroup = ["staticcustom",true] call A3XAI_createGroup; + _unitGroup setVariable ["GroupSize",0]; + _unitGroup setVariable ["trigger",_trigger]; + [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + if (A3XAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +_grpArray pushBack _unitGroup; + +_unitGroup diff --git a/A3XAI/compile/A3XAI_spawnUnits_dynamic.sqf b/A3XAI/compile/A3XAI_spawnUnits_dynamic.sqf new file mode 100644 index 0000000..9f0b6fb --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnUnits_dynamic.sqf @@ -0,0 +1,93 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_patrolDist","_trigger","_totalAI","_unitGroup","_targetPlayer","_playerPos","_playerDir","_spawnPos","_spawnPosASL","_startTime","_behavior","_spawnDist","_triggerLocation"]; + + +_startTime = diag_tickTime; + +_patrolDist = _this select 0; +_trigger = _this select 1; +_minAI = _this select 2; +_addAI = _this select 3; +_unitLevel = _this select 4; + +_targetPlayer = _trigger getVariable ["targetplayer",objNull]; +if (isNull _targetPlayer) exitWith { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Cancelling dynamic spawn for target player. Reason: Player does not exist (logged out?).",name _targetPlayer]}; + _nul = _trigger call A3XAI_cancelDynamicSpawn; + + false +}; + +_playerPos = getPosATL _targetPlayer; +_playerDir = getDir _targetPlayer; +_spawnDist = (SPAWN_DISTANCE_BASE_DYNAMICRANDOM + random (SPAWN_DISTANCE_EXTRA_DYNAMICRANDOM)); +_spawnPos = [_playerPos,_spawnDist,[(_playerDir-SPAWN_DIRECTION_VARIANCE_DYNAMIC),(_playerDir+SPAWN_DIRECTION_VARIANCE_DYNAMIC)],0] call A3XAI_SHK_pos; +_spawnPosASL = ATLToASL _spawnPos; +if ((count _spawnPos) isEqualTo 2) then {_spawnPos set [2,0];}; +_triggerLocation = _trigger getVariable ["triggerLocation",locationNull]; + +try { + if ( + (surfaceIsWater _spawnPos) or + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPos select 0),(_spawnPos select 1),(_spawnPosASL select 2) + 1.7],_x] call A3XAI_hasLOS) or ((_x distance _spawnPos) < PLAYER_DISTANCE_NO_LOS_DYNAMIC)}) exitWith {1}} count (_spawnPos nearEntities [[PLAYER_UNITS,"LandVehicle"],PLAYER_DISTANCE_WITH_LOS_DYNAMIC])) > 0} or + {({if (_spawnPos in _x) exitWith {1}} count ((nearestLocations [_spawnPos,[BLACKLIST_OBJECT_GENERAL,BLACKLIST_OBJECT_DYNAMIC],1500]) - [_triggerLocation])) > 0} or + {!((_spawnPos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])} + ) then { + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Cancelling dynamic spawn for target player %1...",name _targetPlayer]; + diag_log format ["DEBUG: Position is water: %1",(surfaceIsWater _spawnPos)]; + diag_log format ["DEBUG: Player nearby: %1",({isPlayer _x} count (_spawnPos nearEntities [[PLAYER_UNITS,"LandVehicle"],200])) > 0]; + diag_log format ["DEBUG: Location is blacklisted: %1",({_spawnPos in _x} count ((nearestLocations [_spawnPos,[BLACKLIST_OBJECT_GENERAL,BLACKLIST_OBJECT_DYNAMIC],1000]) - [_triggerLocation])) > 0]; + diag_log format ["DEBUG: No jammer nearby: %1.",((_spawnPos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])]; + }; + throw format ["A3XAI Debug: Canceling dynamic spawn for target player %1. Possible reasons: Spawn position has water, player nearby, or is blacklisted.",name _targetPlayer]; + }; + + _totalAI = ((_minAI + floor (random (_addAI + 1))) max 1); + _unitGroup = [_totalAI,grpNull,"dynamic",_spawnPos,_trigger,_unitLevel,"FULL",true] call A3XAI_spawnGroup; + + if (isNull _unitGroup) then { + throw format ["A3XAI Debug: Dynamic group spawn position too close to a player at %1. Spawn cancelled.",_spawnPos]; + }; + + //Set group variables + _unitGroup setBehaviour "AWARE"; + + //Begin hunting player or patrolling area + _behavior = if (A3XAI_spawnHunterChance call A3XAI_chance) then { + _unitGroup reveal [_targetPlayer,4]; + 0 = [_unitGroup,_patrolDist,_targetPlayer,getPosATL _trigger] spawn A3XAI_startHunting; + "HUNT PLAYER" + } else { + if ((_spawnPos distance2D _playerPos) < DUMBFIRE_AI_DISTANCE) then { + [_unitGroup,_playerPos] call A3XAI_setFirstWPPos; + }; + 0 = [_unitGroup,_playerPos,_patrolDist,"LIMITED"] spawn A3XAI_BIN_taskPatrol; + "PATROL AREA" + }; + if (A3XAI_debugLevel > 0) then { + diag_log format["A3XAI Debug: Spawned 1 new AI groups of %1 units each in %2 seconds at %3 using behavior mode %4. Distance from target: %5 meters.",_totalAI,(diag_tickTime - _startTime),(mapGridPosition _trigger),_behavior,_spawnDist]; + }; + + if (!(_trigger getVariable ["initialized",false])) then { + 0 = [1,_trigger,[_unitGroup],_patrolDist,_unitLevel,[_minAI,_addAI]] call A3XAI_initializeTrigger; //set dynamic trigger variables and create dynamic area blacklist + }; + + [_trigger,"A3XAI_dynamicTriggerArray",true] call A3XAI_updateSpawnCount; + + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; //Dark orange: Activated trigger + }; + }; +} catch { + _nul = _trigger call A3XAI_cancelDynamicSpawn; + if (A3XAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +true diff --git a/A3XAI/compile/A3XAI_spawnUnits_random.sqf b/A3XAI/compile/A3XAI_spawnUnits_random.sqf new file mode 100644 index 0000000..94db668 --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnUnits_random.sqf @@ -0,0 +1,120 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_patrolDist","_trigger","_totalAI","_minAI","_addAI","_unitLevel","_unitGroup","_playerPos","_spawnPos","_startTime","_baseDist","_extraDist","_spawnDist","_thisList" ,"_spawnPosSelected","_spawnChance","_firstActualPlayer","_moveMode"]; + + +_startTime = diag_tickTime; + +_minAI = _this select 0; //Mandatory minimum number of AI units to spawn +_addAI = _this select 1; //Maximum number of additional AI units to spawn +_patrolDist = _this select 2; //Patrol radius from trigger center. +_trigger = _this select 3; +_thisList = _this select 4; +_spawnChance = if ((count _this) > 5) then {_this select 5} else {1}; +_unitLevel = if ((count _this) > 6) then {_this select 6} else {1}; //(Optional) Select the item probability table to use +_numGroups = if ((count _this) > 7) then {_this select 7} else {1}; //(Optional) Number of groups of x number of units each to spawn + +_result = true; +try { + _spawnChance = (_spawnChance * A3XAI_spawnChanceMultiplier); + if (_spawnChance call A3XAI_chance) then { + _baseDist = SPAWN_DISTANCE_DEFAULT_RANDOM; + _extraDist = 0; + _playerPos = [0,0,0]; + _firstActualPlayer = objNull; + + { + if ((isPlayer _x) && {(typeOf _x) in [PLAYER_UNITS]}) exitWith { + _playerPos = getPosASL _x; + _firstActualPlayer = _x; + _triggerLocation = _trigger getVariable ["triggerLocation",locationNull]; + if ( + (({if (_playerPos in _x) exitWith {1}} count ((nearestLocations [_playerPos,[BLACKLIST_OBJECT_GENERAL,BLACKLIST_OBJECT_RANDOM],1500]) - [_triggerLocation])) isEqualTo 0) && + {!(surfaceIsWater _playerPos)} && + {((_playerPos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])} + ) then { + _trigger setPosASL _playerPos; + _triggerLocation setPosition _playerPos; + _baseDist = SPAWN_DISTANCE_BASE_DYNAMICRANDOM; + _extraDist = SPAWN_DISTANCE_EXTRA_DYNAMICRANDOM; + if (A3XAI_enableDebugMarkers) then { + str (_trigger) setMarkerPos _playerPos; + }; + }; + }; + } forEach _thisList; + + if (isNull _firstActualPlayer) then { + //diag_log "CASE 1"; + throw format ["A3XAI Debug: No players of type %1 found.",PLAYER_UNITS]; + }; + + _triggerPos = getPosATL _trigger; + _nearAttempts = 0; + _spawnPos = []; + while {(_spawnPos isEqualTo []) && {_nearAttempts < 4}} do { + _spawnPosSelected = [_triggerPos,(_baseDist + (random _extraDist)),(random 360),0] call A3XAI_SHK_pos; + _spawnPosSelASL = ATLToASL _spawnPosSelected; + if ((count _spawnPosSelected) isEqualTo 2) then {_spawnPosSelected set [2,0];}; + if ( + ({if ((isPlayer _x) && {([eyePos _x,[(_spawnPosSelected select 0),(_spawnPosSelected select 1),(_spawnPosSelASL select 2) + 1.7],_x] call A3XAI_hasLOS) or ((_x distance _spawnPosSelected) < PLAYER_DISTANCE_NO_LOS_RANDOM)}) exitWith {1}} count (_spawnPosSelected nearEntities [[PLAYER_UNITS,"LandVehicle"], PLAYER_DISTANCE_WITH_LOS_RANDOM]) isEqualTo 0) && + {!(surfaceIsWater _spawnPosSelected)} && + {((_spawnPosSelected nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo [])} + ) then { + _spawnPos = _spawnPosSelected; + }; + _nearAttempts = _nearAttempts + 1; + }; + + //diag_log format ["DEBUG: Nearby units: %1",_spawnPos nearEntities [["CAManBase"],200]]; + + if !(_spawnPos isEqualTo []) then { + _totalAI = ((_minAI + floor (random (_addAI + 1))) max 1); + _unitGroup = [_totalAI,grpNull,"random",_spawnPos,_trigger,_unitLevel,"FULL",true] call A3XAI_spawnGroup; + + if (isNull _unitGroup) then { + throw format ["A3XAI Debug: Random group spawn position too close to a player at %1. Spawn cancelled.",_spawnPos]; + }; + + _unitGroup setBehaviour "AWARE"; + + if ((_spawnPos distance2D _playerPos) < DUMBFIRE_AI_DISTANCE) then { + [_unitGroup,_playerPos] call A3XAI_setFirstWPPos; + }; + 0 = [_unitGroup,_triggerPos,_patrolDist,"LIMITED"] spawn A3XAI_BIN_taskPatrol; + + if (A3XAI_debugLevel > 0) then { + diag_log format["A3XAI Debug: Spawned 1 new AI groups of %1 units each in %2 seconds at %3 using %4 attempts (Random Spawn).",_totalAI,(diag_tickTime - _startTime),(mapGridPosition _trigger),_nearAttempts]; + }; + + if (!(_trigger getVariable ["initialized",false])) then { + 0 = [2,_trigger,[_unitGroup],_patrolDist,_unitLevel,[_minAI,_addAI]] call A3XAI_initializeTrigger; + }; + + if (A3XAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; //Dark orange: Activated trigger + }; + }; + + true + } else { + //diag_log "CASE 2"; + throw format ["A3XAI Debug: Conditional checks failed for random spawn at %1. Canceling spawn.",(mapGridPosition _trigger)]; + }; + } else { + //diag_log "CASE 3"; + throw format ["A3XAI Debug: Probability check failed for random spawn at %1. Canceling spawn.",(mapGridPosition _trigger)]; + + }; +} catch { + _nul = _trigger call A3XAI_cancelRandomSpawn; + if (A3XAI_debugLevel > 0) then { + diag_log _exception; + }; + _result = false; +}; + +_result diff --git a/A3XAI/compile/A3XAI_spawnUnits_static.sqf b/A3XAI/compile/A3XAI_spawnUnits_static.sqf new file mode 100644 index 0000000..d52539e --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnUnits_static.sqf @@ -0,0 +1,82 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_minAI","_addAI","_patrolDist","_trigger","_unitLevel","_numGroups","_grpArray","_triggerPos","_startTime","_totalSpawned","_groupsActive","_spawnChance","_result"]; + +_minAI = _this select 0; //Mandatory minimum number of AI units to spawn +_addAI = _this select 1; //Maximum number of additional AI units to spawn +_patrolDist = _this select 2; //Patrol radius from trigger center. +_trigger = _this select 3; //The trigger calling this script. +_unitLevel = if ((count _this) > 4) then {_this select 4} else {1}; //(Optional) Select the item probability table to use +_numGroups = if ((count _this) > 5) then {_this select 5} else {1}; //(Optional) Number of groups of x number of units each to spawn + +_startTime = diag_tickTime; + +_grpArray = _trigger getVariable ["GroupArray",[]]; +_groupsActive = count _grpArray; +_triggerPos = getPosATL _trigger; +_result = true; + +//If trigger already has defined spawn points, then reuse them instead of recalculating new ones. +_locationArray = _trigger getVariable ["locationArray",[]]; +_totalSpawned = 0; + +//Spawn groups +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Trigger %1 is spawning units...",_trigger getVariable ["TriggerText","Unknown Spawn"]]}; +for "_j" from 1 to (_numGroups - _groupsActive) do { + private ["_unitGroup","_spawnPos","_totalAI"]; + _totalAI = 0; + _spawnPos = []; + _spawnChance = ((_trigger getVariable ["spawnChance",1]) * A3XAI_spawnChanceMultiplier); + if ((_trigger getVariable ["spawnChance",1]) call A3XAI_chance) then { + _totalAI = ((_minAI + round(random _addAI)) min MAX_UNITS_PER_STATIC_SPAWN); + _spawnPos = if !(_locationArray isEqualTo []) then {_locationArray call A3XAI_findSpawnPos} else {[(getPosATL _trigger),random (_patrolDist),random(360),0] call A3XAI_SHK_pos}; + }; + + //If non-zero unit amount and valid spawn position, spawn group, otherwise add it to respawn queue. + _unitGroup = grpNull; + try { + if ((count _spawnPos) > 1) then { + if (_totalAI > 0) then { + _unitGroup = [_totalAI,_unitGroup,"static",_spawnPos,_trigger,_unitLevel] call A3XAI_spawnGroup; + if (isNull _unitGroup) then { + throw [true, format ["A3XAI Debug: No units spawned for static spawn at %1. Added group to respawn queue with fast mode.",(_trigger getVariable ["TriggerText","Unknown Spawn"])]]; + }; + _totalSpawned = _totalSpawned + _totalAI; + if (_patrolDist > 1) then { + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3XAI_BIN_taskPatrol; + } else { + [_unitGroup, 0] setWaypointType "GUARD"; + }; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Spawned group %1 (unitLevel: %2) with %3 units.",_unitGroup,_unitLevel,_totalAI];}; + } else { + throw [false, format ["A3XAI Debug: No units spawned for static spawn at %1.",(_trigger getVariable ["TriggerText","Unknown Spawn"])]]; + }; + } else { + throw [true, format ["A3XAI Debug: Unable to find spawn position for static spawn at %1. Added group to respawn queue with fast mode.",(_trigger getVariable ["TriggerText","Unknown Spawn"])]]; + }; + } catch { + private ["_result","_logText"]; + + _result = _exception select 0; //true: no lockout (position-based failure), false: lockout (probability-based failure) + _logText = _exception select 1; + + if (_result) then{ + _unitGroup = ["static",true] call A3XAI_createGroup; + _unitGroup setVariable ["GroupSize",0]; + _unitGroup setVariable ["trigger",_trigger]; + 0 = [0,_trigger,_unitGroup,true] call A3XAI_addRespawnQueue; + }; + if (A3XAI_debugLevel > 1) then {diag_log _logText;}; + }; + + if !(isNull _unitGroup) then { + _grpArray pushBack _unitGroup; + }; +}; + +if (A3XAI_debugLevel > 0) then { + diag_log format["A3XAI Debug: Spawned %1 new AI groups (%2 units total) in %3 seconds at %4.",_numGroups,_totalSpawned,(diag_tickTime - _startTime),(_trigger getVariable ["TriggerText","Unknown Spawn"])]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Trigger %1 group array updated to: %2.",_trigger getVariable ["TriggerText","Unknown Spawn"],_trigger getVariable "GroupArray"]}; +}; + +_result diff --git a/A3XAI/compile/A3XAI_spawnVehicleCustom.sqf b/A3XAI/compile/A3XAI_spawnVehicleCustom.sqf new file mode 100644 index 0000000..f71a5f0 --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnVehicleCustom.sqf @@ -0,0 +1,155 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_marker","_vehicleType","_unitLevel","_unitGroup","_driver","_vehicle","_gunnerSpots","_spawnPos","_patrolDist","_isAirVehicle","_unitType","_vehiclePosition","_maxUnits","_maxCargoUnits","_maxGunnerUnits","_keepLooking","_gunnersAdded","_velocity","_direction"]; + +_spawnName = _this select 0; +_spawnPos = _this select 1; +_vehicleType = _this select 2; +_patrolDist = _this select 3; +_maxUnits = _this select 4; +_unitLevel = _this select 5; + +_maxCargoUnits = _maxUnits select 0; +_maxGunnerUnits = _maxUnits select 1; +_isAirVehicle = (_vehicleType isKindOf "Air"); +_vehiclePosition = []; +_roadSearching = 1; //A3XAI_SHK_pos will search for roads, and return random position if none found. +_waterPosAllowed = 0; //do not allow water position for land vehicles. +_spawnMode = "NONE"; + +if (_isAirVehicle) then { + _roadSearching = 0; //No need to search for road positions for air vehicles + _waterPosAllowed = 1; //Allow water position for air vehicles + _spawnMode = "FLY"; //set flying mode for air vehicles + _vehiclePosition set [2,200]; //spawn air vehicles in air + _spawnPos set [2,200]; //set marker height in air + if !(_maxCargoUnits isEqualTo 0) then {_maxCargoUnits = 0}; //disable cargo units for air vehicles +}; + +_keepLooking = true; +_waitTime = 10; +while {_keepLooking} do { + _vehiclePosition = [_spawnPos,random _patrolDist,random(360),_waterPosAllowed,[_roadSearching,200]] call A3XAI_SHK_pos; + if (({if (isPlayer _x) exitWith {1}} count (_vehiclePosition nearEntities [[PLAYER_UNITS,"AllVehicles"],PLAYER_DISTANCE_SPAWN_AIVEHICLE])) isEqualTo 0) then { + _keepLooking = false; //safe area found, continue to spawn the vehicle and crew + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Waiting %1 seconds for area at %2 to have no players nearby to spawn custom AI vehicle %3.",_waitTime,_marker,_vehicleType]}; + uiSleep _waitTime; //wait a while before checking spawn area again. Scaling wait time from 10-30 seconds. + _waitTime = ((_waitTime + 5) min 60); + }; +}; + +_unitType = if (_isAirVehicle) then {"aircustom"} else {"landcustom"}; +_unitGroup = [_unitType] call A3XAI_createGroup; +_driver = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, _spawnMode]; +_driver moveInDriver _vehicle; + +_vehicle call A3XAI_protectObject; +_vehicle call A3XAI_secureVehicle; +_vehicle call A3XAI_clearVehicleCargo; + +call { + if (_vehicle isKindOf "Plane") exitWith { + _direction = (random 360); + _velocity = velocity _vehicle; + _vehicle setDir _direction; + _vehicle setVelocity [(_velocity select 1)*sin _direction - (_velocity select 0)*cos _direction, (_velocity select 0)*sin _direction + (_velocity select 1)*cos _direction, _velocity select 2]; + }; + if (_vehicle isKindOf "Helicopter") exitWith { + _vehicle setDir (random 360); + }; + if (_vehicle isKindOf "LandVehicle") exitWith { + _nearRoads = _vehiclePosition nearRoads 100; + if !(_nearRoads isEqualTo []) then { + _nextRoads = roadsConnectedTo (_nearRoads select 0); + if !(_nextRoads isEqualTo []) then { + _direction = [_vehicle,(_nextRoads select 0)] call BIS_fnc_relativeDirTo; + _vehicle setDir _direction; + //diag_log format ["Debug: Reoriented vehicle %1 to direction %2.",_vehicle,_direction]; + }; + } else { + _vehicle setDir (random 360); + }; + }; +}; + +//Set variables +_vehicle setVariable ["unitGroup",_unitGroup]; + +//Determine vehicle type and add needed eventhandlers +if (_isAirVehicle) then { + _vehicle call A3XAI_addVehAirEH; +} else { + _vehicle call A3XAI_addLandVehEH; +}; +_vehicle allowCrewInImmobile (!_isAirVehicle); +_vehicle setUnloadInCombat [!_isAirVehicle,false]; + +_nvg = _driver call A3XAI_addTempNVG; +_driver assignAsDriver _vehicle; +_driver setVariable ["isDriver",true]; +_unitGroup selectLeader _driver; + +if (_isAirVehicle) then {_vehicle flyInHeight FLYINHEIGHT_AIR_CUSTOM}; + +_gunnersAdded = [_unitGroup,_unitLevel,_vehicle,_maxGunnerUnits] call A3XAI_addVehicleGunners; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Spawned %1 gunner units for %2 vehicle %3.",_gunnersAdded,_unitGroup,_vehicleType];}; + +_cargoSpots = _vehicle emptyPositions "cargo"; +for "_i" from 0 to ((_cargoSpots min _maxCargoUnits) - 1) do { + _cargo = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + _nvg = _cargo call A3XAI_addTempNVG; + _cargo assignAsCargo _vehicle; + _cargo moveInCargo [_vehicle,_i]; +}; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Spawned %1 cargo units for %2 vehicle %3.",(_cargoSpots min _maxCargoUnits),_unitGroup,_vehicleType]}; + +_unitGroup setBehaviour "AWARE"; +_unitGroup setCombatMode "YELLOW"; +_unitGroup setSpeedMode "FULL"; +_unitGroup allowFleeing 0; + +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setVariable ["assignedVehicle",_vehicle]; +_unitGroup setVariable ["spawnParams",_this]; +[_unitGroup,0] setWaypointPosition [_spawnPos,0]; //Move group's initial waypoint position away from [0,0,0] (initial spawn position). +(units _unitGroup) allowGetIn true; + +if (_isAirVehicle) then { + if (A3XAI_removeExplosiveAmmo) then { + _result = _vehicle call A3XAI_removeExplosive; //Remove missile weaponry for air vehicles + }; + + if ((({_x call A3XAI_checkIsWeapon} count (weapons _vehicle)) isEqualTo 0) && {({_x call A3XAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) isEqualTo 0} && {_gunnersAdded isEqualTo 0}) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: AI group %1 air vehicle %2 set to Careless behavior mode",_unitGroup,_vehicleType];}; + }; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_curHeliPatrols = A3XAI_curHeliPatrols + 1; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Custom AI helicopter crew group %1 is now active and patrolling.",_unitGroup];}; + }; +} else { + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_curLandPatrols = A3XAI_curLandPatrols + 1; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Custom AI land vehicle crew group %1 is now active and patrolling.",_unitGroup];}; + }; +}; + +0 = [_unitGroup,_spawnPos,_patrolDist,false] spawn A3XAI_BIN_taskPatrol; +0 = [_unitGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; + +if (A3XAI_enableHC && {_unitType in A3XAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (_unitType in A3XAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created custom vehicle spawn at %1 with vehicle type %2 with %3 crew units.",_spawnName,_vehicleType,(count (units _unitGroup))]}; + +true diff --git a/A3XAI/compile/A3XAI_spawnVehiclePatrol.sqf b/A3XAI/compile/A3XAI_spawnVehiclePatrol.sqf new file mode 100644 index 0000000..466fa55 --- /dev/null +++ b/A3XAI/compile/A3XAI_spawnVehiclePatrol.sqf @@ -0,0 +1,197 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicleType", "_maxCargoUnits", "_maxGunnerUnits", "_unitLevel", "_isAirVehicle", "_vehiclePosition", "_spawnMode", "_keepLooking", "_error", "_unitType", "_unitGroup", "_driver", "_vehicle", "_direction", "_velocity", "_nearRoads", "_nextRoads", "_gunnersAdded", "_cargoSpots", "_cargo", "_waypoint", "_result", "_rearm","_combatMode","_behavior","_waypointCycle"]; + +_vehicleType = _this; + +_maxCargoUnits = 0; +_maxGunnerUnits = 0; +_unitLevel = 0; +_isAirVehicle = (_vehicleType isKindOf "Air"); +_vehiclePosition = []; +_spawnMode = "NONE"; +_keepLooking = true; +_error = false; + +call { + if (_vehicleType isKindOf "Air") exitWith { + //Note: no cargo units for air vehicles + _maxGunnerUnits = A3XAI_airGunnerUnits; + _unitLevel = "airvehicle" call A3XAI_getUnitLevel; + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + (random((getMarkerSize "A3XAI_centerMarker") select 0)),random(360),1] call A3XAI_SHK_pos; + _vehiclePosition set [2,200]; + _spawnMode = "FLY"; + }; + if (_vehicleType isKindOf "StaticWeapon") exitWith {_error = true}; + if (_vehicleType isKindOf "Ship") exitWith {_error = true}; + if (_vehicleType isKindOf "LandVehicle") exitWith { + _maxGunnerUnits = A3XAI_landGunnerUnits; + _maxCargoUnits = A3XAI_landCargoUnits; + _unitLevel = "landvehicle" call A3XAI_getUnitLevel; + while {_keepLooking} do { + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + random((getMarkerSize "A3XAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3XAI_SHK_pos; + if ((count _vehiclePosition) > 1) then { + if ({isPlayer _x} count (_vehiclePosition nearEntities [[PLAYER_UNITS,"AllVehicles"], PLAYER_DISTANCE_SPAWN_AIVEHICLE]) isEqualTo 0) then { + _keepLooking = false; //Found road position, stop searching + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to find road position to spawn AI %1. Retrying in 30 seconds.",_vehicleType]}; + uiSleep 30; //Couldnt find road, search again in 30 seconds. + }; + }; + }; + _error = true; +}; + +if (_error) exitWith {diag_log format ["A3XAI Error: %1 attempted to spawn unsupported vehicle type %2.",__FILE__,_vehicleType]}; + +_unitType = if (_isAirVehicle) then {"air"} else {"land"}; +_unitGroup = [_unitType] call A3XAI_createGroup; +_driver = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, _spawnMode]; +_driver moveInDriver _vehicle; + +_vehicle call A3XAI_protectObject; +_vehicle call A3XAI_secureVehicle; +_vehicle call A3XAI_clearVehicleCargo; + +call { + if (_vehicle isKindOf "Plane") exitWith { + _direction = (random 360); + _velocity = velocity _vehicle; + _vehicle setDir _direction; + _vehicle setVelocity [(_velocity select 1)*sin _direction - (_velocity select 0)*cos _direction, (_velocity select 0)*sin _direction + (_velocity select 1)*cos _direction, _velocity select 2]; + }; + if (_vehicle isKindOf "Helicopter") exitWith { + _vehicle setDir (random 360); + }; + if (_vehicle isKindOf "LandVehicle") exitWith { + _nearRoads = _vehiclePosition nearRoads 100; + if !(_nearRoads isEqualTo []) then { + _nextRoads = roadsConnectedTo (_nearRoads select 0); + if !(_nextRoads isEqualTo []) then { + _direction = [_vehicle,(_nextRoads select 0)] call BIS_fnc_relativeDirTo; + _vehicle setDir _direction; + //diag_log format ["Debug: Reoriented vehicle %1 to direction %2.",_vehicle,_direction]; + }; + } else { + _vehicle setDir (random 360); + }; + }; +}; + +//Set variables +_vehicle setVariable ["unitGroup",_unitGroup]; + +//Determine vehicle type and add needed eventhandlers +if (_isAirVehicle) then { + _vehicle call A3XAI_addVehAirEH; +} else { + _vehicle call A3XAI_addLandVehEH; +}; + +_vehicle allowCrewInImmobile (!_isAirVehicle); +_vehicle setUnloadInCombat [!_isAirVehicle,false]; + +//Setup group and crew +_nvg = _driver call A3XAI_addTempNVG; +_driver assignAsDriver _vehicle; +_driver setVariable ["isDriver",true]; +_unitGroup selectLeader _driver; + +_gunnersAdded = [_unitGroup,_unitLevel,_vehicle,_maxGunnerUnits] call A3XAI_addVehicleGunners; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Spawned %1 gunner units for %2 vehicle %3.",_gunnersAdded,_unitGroup,_vehicleType];}; + +_cargoSpots = _vehicle emptyPositions "cargo"; +for "_i" from 0 to ((_cargoSpots min _maxCargoUnits) - 1) do { + _cargo = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + _nvg = _cargo call A3XAI_addTempNVG; + _cargo assignAsCargoIndex [_vehicle,_i]; + _cargo moveInCargo _vehicle; +}; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Spawned %1 cargo units for %2 vehicle %3.",(_cargoSpots min _maxCargoUnits),_unitGroup,_vehicleType]}; + +_unitGroup setBehaviour "AWARE"; +_unitGroup setCombatMode "YELLOW"; +_unitGroup allowFleeing 0; + +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(units _unitGroup) allowGetIn true; + +_combatMode = (combatMode _unitGroup); +_behavior = (behaviour (leader _unitGroup)); + +if (_isAirVehicle) then { + if (A3XAI_removeExplosiveAmmo) then { + _result = _vehicle call A3XAI_removeExplosive; //Remove missile weaponry for air vehicles + }; + + if ((({_x call A3XAI_checkIsWeapon} count (weapons _vehicle)) isEqualTo 0) && {({_x call A3XAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) isEqualTo 0} && {_gunnersAdded isEqualTo 0}) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: AI group %1 air vehicle %2 set to Careless behavior mode",_unitGroup,_vehicleType];}; + }; + + [_unitGroup,0] setWPPos _vehiclePosition; + [_unitGroup,0] setWaypointType "MOVE"; + [_unitGroup,0] setWaypointTimeout [0.5,0.5,0.5]; + [_unitGroup,0] setWaypointCompletionRadius 200; + // [_unitGroup,0] setWaypointStatements ["true","if !(local this) exitWith {}; [(group this)] spawn A3XAI_heliDetection;"]; + [_unitGroup,0] setWaypointStatements ["true","if !(local this) exitWith {};"]; + [_unitGroup,0] setWaypointCombatMode _combatMode; + [_unitGroup,0] setWaypointBehaviour _behavior; + [_unitGroup,0] setWaypointSpeed "FULL"; + + _waypoint = _unitGroup addWaypoint [_vehiclePosition,0]; + _waypoint setWaypointType "MOVE"; + _waypoint setWaypointTimeout [3,6,9]; + _waypoint setWaypointCompletionRadius 150; + _waypoint setWaypointStatements ["true","if !(local this) exitWith {}; [(group this)] spawn A3XAI_heliStartPatrol;"]; + _waypoint setWaypointCombatMode _combatMode; + _waypoint setWaypointBehaviour _behavior; + _waypoint setWaypointSpeed "LIMITED"; + + _waypointCycle = _unitGroup addWaypoint [_vehiclePosition, 0]; + _waypointCycle setWaypointType "CYCLE"; + _waypointCycle setWaypointCompletionRadius 150; + + _unitGroup setVariable ["HeliLastParaDrop",diag_tickTime - A3XAI_paraDropCooldown]; + _vehicle flyInHeight (FLYINHEIGHT_AIR_PATROLLING_BASE + (random FLYINHEIGHT_AIR_PATROLLING_VARIANCE)); + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_curHeliPatrols = A3XAI_curHeliPatrols + 1; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created AI helicopter crew group %1 is now active and patrolling.",_unitGroup];}; + }; +} else { + //Set initial waypoint and begin patrol + [_unitGroup,0] setWaypointType "MOVE"; + [_unitGroup,0] setWaypointTimeout [5,10,15]; + [_unitGroup,0] setWaypointCompletionRadius 150; + [_unitGroup,0] setWaypointStatements ["true","if !(local this) exitWith {}; [(group this)] spawn A3XAI_vehStartPatrol;"]; + [_unitGroup,0] setWaypointCombatMode _combatMode; + [_unitGroup,0] setWaypointBehaviour _behavior; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_curLandPatrols = A3XAI_curLandPatrols + 1; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: AI land vehicle crew group %1 is now active and patrolling.",_unitGroup];}; + }; +}; + +[_unitGroup,0] setWaypointPosition [_vehiclePosition,0]; +_unitGroup setCurrentWaypoint [_unitGroup,0]; + +_rearm = [_unitGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; //start group-level manager + +if (A3XAI_enableHC && {_unitType in A3XAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (_unitType in A3XAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Created AI vehicle patrol at %1 with vehicle type %2 with %3 crew units.",_vehiclePosition,_vehicleType,(count (units _unitGroup))]}; + +true diff --git a/A3XAI/compile/A3XAI_spawn_UV_patrol.sqf b/A3XAI/compile/A3XAI_spawn_UV_patrol.sqf new file mode 100644 index 0000000..347329b --- /dev/null +++ b/A3XAI/compile/A3XAI_spawn_UV_patrol.sqf @@ -0,0 +1,161 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicleType", "_maxGunnerUnits", "_unitLevel", "_isAirVehicle", "_vehiclePosition", "_spawnMode", "_keepLooking", "_error", "_unitType", "_unitGroup", "_driver", "_vehicle", "_direction", "_velocity", "_nearRoads", "_nextRoads", "_detectionStatement", "_patrolStatement", "_gunnersAdded", "_waypoint", "_rearm","_combatMode","_behavior","_waypointCycle"]; + +_vehicleType = _this; + +_maxGunnerUnits = 5; +_unitLevel = 0; +_isAirVehicle = (_vehicleType isKindOf "Air"); +_vehiclePosition = []; +_spawnMode = "NONE"; +_keepLooking = true; +_error = false; + +call { + if (([configFile >> "CfgVehicles" >> _vehicleType,"vehicleClass",""] call BIS_fnc_returnConfigEntry) != "Autonomous") exitWith {_error = true}; + if (_vehicleType isKindOf "Air") exitWith { + //Note: no cargo units for air vehicles + _unitLevel = "uav" call A3XAI_getUnitLevel; + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + (random((getMarkerSize "A3XAI_centerMarker") select 0)),random(360),1] call A3XAI_SHK_pos; + _vehiclePosition set [2,200]; + _spawnMode = "FLY"; + }; + if (_vehicleType isKindOf "StaticWeapon") exitWith {_error = true}; + if (_vehicleType isKindOf "LandVehicle") exitWith { + _unitLevel = "ugv" call A3XAI_getUnitLevel; + while {_keepLooking} do { + _vehiclePosition = [(getMarkerPos "A3XAI_centerMarker"),300 + random((getMarkerSize "A3XAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3XAI_SHK_pos; + if ((count _vehiclePosition) > 1) then { + if ({isPlayer _x} count (_vehiclePosition nearEntities [[PLAYER_UNITS,"AllVehicles"], PLAYER_DISTANCE_SPAWN_AUTONOMOUS]) isEqualTo 0) then { + _keepLooking = false; //Found road position, stop searching + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Unable to find road position to spawn AI %1. Retrying in 30 seconds.",_vehicleType]}; + uiSleep 30; //Couldnt find road, search again in 30 seconds. + }; + }; + }; + _error = true; +}; + +if (_error) exitWith {diag_log format ["A3XAI Error: %1 attempted to spawn unsupported vehicle type %2.",__FILE__,_vehicleType]}; + +_unitType = if (_isAirVehicle) then {"uav"} else {"ugv"}; + +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, _spawnMode]; +createVehicleCrew _vehicle; +_vehicle setAutonomous true; +_vehicle lockDriver true; +_unitGroup = [(group ((crew _vehicle) select 0)),_unitType] call A3XAI_initUVGroup; + +_vehicle call A3XAI_protectObject; +_vehicle call A3XAI_secureVehicle; +_vehicle call A3XAI_clearVehicleCargo; + +call { + if (_vehicle isKindOf "Plane") exitWith { + _direction = (random 360); + _velocity = velocity _vehicle; + _vehicle setDir _direction; + _vehicle setVelocity [(_velocity select 1)*sin _direction - (_velocity select 0)*cos _direction, (_velocity select 0)*sin _direction + (_velocity select 1)*cos _direction, _velocity select 2]; + }; + if (_vehicle isKindOf "Helicopter") exitWith { + _vehicle setDir (random 360); + }; + if (_vehicle isKindOf "LandVehicle") exitWith { + _nearRoads = _vehiclePosition nearRoads 100; + if !(_nearRoads isEqualTo []) then { + _nextRoads = roadsConnectedTo (_nearRoads select 0); + if !(_nextRoads isEqualTo []) then { + _direction = [_vehicle,(_nextRoads select 0)] call BIS_fnc_relativeDirTo; + _vehicle setDir _direction; + //diag_log format ["Debug: Reoriented vehicle %1 to direction %2.",_vehicle,_direction]; + }; + } else { + _vehicle setDir (random 360); + }; + }; +}; + +//Set variables +_vehicle setVariable ["unitGroup",_unitGroup]; + +//Determine vehicle type and add needed eventhandlers +if (_isAirVehicle) then { + _vehicle call A3XAI_addUAVEH; +} else { + _vehicle call A3XAI_addUGVEH; +}; + +_unitGroup setBehaviour "AWARE"; +_unitGroup setCombatMode "YELLOW"; +_unitGroup setSpeedMode "NORMAL"; +_unitGroup allowFleeing 0; + +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(units _unitGroup) allowGetIn true; + +if (_isAirVehicle) then { + // _detectionStatement = "if !(local this) exitWith {}; [(group this)] spawn A3XAI_UAVDetection;"; + _detectionStatement = "if !(local this) exitWith {};"; + _patrolStatement = "if !(local this) exitWith {}; [(group this)] spawn A3XAI_UAVStartPatrol;"; + _vehicle flyInHeight (FLYINHEIGHT_UAV_PATROLLING_BASE + random (FLYINHEIGHT_UAV_PATROLLING_VARIANCE)); + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_curUAVPatrols = A3XAI_curUAVPatrols + 1; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: UAV group %1 is now active and patrolling.",_unitGroup];}; + }; +} else { + // _detectionStatement = "if !(local this) exitWith {}; [(group this)] spawn A3XAI_UGVDetection;"; + _detectionStatement = "if !(local this) exitWith {};"; + _patrolStatement = "if !(local this) exitWith {}; [(group this)] spawn A3XAI_UGVStartPatrol;"; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_curUGVPatrols = A3XAI_curUGVPatrols + 1; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: UGV group %1 is now active and patrolling.",_unitGroup];}; + }; +}; + +[_unitGroup,"Nonhostile"] call A3XAI_forceBehavior; +_combatMode = (combatMode _unitGroup); +_behavior = (behaviour (leader _unitGroup)); + +[_unitGroup,0] setWPPos _vehiclePosition; +[_unitGroup,0] setWaypointType "MOVE"; +[_unitGroup,0] setWaypointTimeout [0.5,0.5,0.5]; +[_unitGroup,0] setWaypointCompletionRadius 200; +[_unitGroup,0] setWaypointStatements ["true",_detectionStatement]; +[_unitGroup,0] setWaypointCombatMode _combatMode; +[_unitGroup,0] setWaypointBehaviour _behavior; + +_waypoint = _unitGroup addWaypoint [_vehiclePosition,0]; +_waypoint setWaypointType "MOVE"; +_waypoint setWaypointTimeout [3,6,9]; +_waypoint setWaypointCompletionRadius 150; +_waypoint setWaypointStatements ["true",_patrolStatement]; +_waypoint setWaypointCombatMode _combatMode; +_waypoint setWaypointBehaviour _behavior; + +_waypointCycle = _unitGroup addWaypoint [_vehiclePosition, 0]; +_waypointCycle setWaypointType "CYCLE"; +_waypointCycle setWaypointCompletionRadius 150; + +if (_isAirVehicle) then { + [_unitGroup,0] setWaypointSpeed "FULL"; + [_unitGroup,1] setWaypointSpeed "LIMITED"; +}; + +[_unitGroup,0] setWaypointPosition [_vehiclePosition,0]; +_unitGroup setCurrentWaypoint [_unitGroup,0]; + +_rearm = [_unitGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; //start group-level manager + +if (A3XAI_enableHC && {_unitType in A3XAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Created AI vehicle patrol at %1 with vehicle type %2 with %3 crew units.",_vehiclePosition,_vehicleType,(count (units _unitGroup))]}; + +true diff --git a/A3XAI/compile/A3XAI_spawn_reinforcement.sqf b/A3XAI/compile/A3XAI_spawn_reinforcement.sqf new file mode 100644 index 0000000..8806223 --- /dev/null +++ b/A3XAI/compile/A3XAI_spawn_reinforcement.sqf @@ -0,0 +1,119 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_destPos", "_unitLevel", "_maxGunnerUnits", "_vehiclePosition", "_error", "_unitGroup", "_driver", "_vehicleType", "_vehicle", "_direction", "_velocity", "_nvg", "_gunnersAdded", "_cargoSpots", "_cargo", "_result", +"_rearm","_targetPlayer","_unitType","_vehicleDescription"]; + +A3XAI_activeReinforcements = A3XAI_activeReinforcements - [grpNull]; +if ((count A3XAI_activeReinforcements) >= A3XAI_maxAirReinforcements) exitWith { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Maximum number of active air reinforcements reached (%1).",A3XAI_activeReinforcements];}; +}; + +_destPos = _this select 0; +_targetPlayer = _this select 1; +_unitLevel = _this select 2; + +if (({(_destPos distance2D _x) < AIR_REINFORCE_DIST_BETWEEN_LOCATIONS} count A3XAI_reinforcedPositions) > 0) exitWith { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Another AI reinforcement is active within 1000m of location %1, reinforce request cancelled.",_destPos];}; +}; + +A3XAI_reinforcedPositions pushBack _destPos; + +_error = false; +_maxGunnerUnits = A3XAI_airGunnerUnits; +_vehiclePosition = [_destPos,AIR_REINFORCE_SPAWN_DIST_BASE + (random(AIR_REINFORCE_SPAWN_DIST_VARIANCE)),random(360),1] call A3XAI_SHK_pos; +_vehiclePosition set [2,200]; + +_unitType = "air_reinforce"; +_unitGroup = [_unitType] call A3XAI_createGroup; +_driver = [_unitGroup,_unitLevel,[0,0,0]] call A3XAI_createUnit; + +_vehicleType = A3XAI_airReinforcementVehicles call A3XAI_selectRandom; +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, "FLY"]; +_driver moveInDriver _vehicle; + +_vehicle call A3XAI_protectObject; +_vehicle call A3XAI_secureVehicle; +_vehicle call A3XAI_clearVehicleCargo; +_vehicle call A3XAI_addVehAirEH; + +call { + if (_vehicle isKindOf "Plane") exitWith { + _direction = (random 360); + _velocity = velocity _vehicle; + _vehicle setDir _direction; + _vehicle setVelocity [(_velocity select 1)*sin _direction - (_velocity select 0)*cos _direction, (_velocity select 0)*sin _direction + (_velocity select 1)*cos _direction, _velocity select 2]; + }; + if (_vehicle isKindOf "Helicopter") exitWith { + _vehicle setDir (random 360); + }; + _error = true; +}; + +if (_error) exitWith {diag_log format ["A3XAI Error: Selected reinforcement vehicle %1 is non-Air type!",_vehicleType];}; + +//Set variables +_vehicle setVariable ["unitGroup",_unitGroup]; +_vehicle allowCrewInImmobile false; +_vehicle setUnloadInCombat [false,false]; + +//Setup group and crew +_nvg = _driver call A3XAI_addTempNVG; +_driver assignAsDriver _vehicle; +_driver setVariable ["isDriver",true]; +_unitGroup selectLeader _driver; + +_gunnersAdded = [_unitGroup,_unitLevel,_vehicle,_maxGunnerUnits] call A3XAI_addVehicleGunners; +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Spawned %1 gunner units for %2 vehicle %3.",_gunnersAdded,_unitGroup,_vehicleType];}; + +_unitGroup setSpeedMode "NORMAL"; +_unitGroup allowFleeing 0; + +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setVariable ["assignedVehicle",_vehicle]; +_unitGroup setVariable ["ReinforcePos",_destPos]; +(units _unitGroup) allowGetIn true; + +_vehicle flyInHeight FLYINHEIGHT_AIR_REINFORCE; + +if (A3XAI_removeExplosiveAmmo) then { + _result = _vehicle call A3XAI_removeExplosive; //Remove missile weaponry for air vehicles +}; + +if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3XAI_activeReinforcements pushBack _unitGroup; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created AI air reinforcement group %1 is now active and patrolling.",_unitGroup];}; +}; + +//Set initial waypoint and begin patrol +[_unitGroup,0] setWPPos _destPos; +[_unitGroup,0] setWaypointType "MOVE"; +[_unitGroup,0] setWaypointTimeout [0.5,0.5,0.5]; +[_unitGroup,0] setWaypointCompletionRadius 200; +[_unitGroup,0] setWaypointStatements ["true","if (isServer) then {(group this) spawn A3XAI_reinforce_begin};"]; +[_unitGroup,0] setWaypointBehaviour "CARELESS"; +[_unitGroup,0] setWaypointCombatMode "BLUE"; +_unitGroup setCurrentWaypoint [_unitGroup,0]; + +_rearm = [_unitGroup,_unitLevel] spawn A3XAI_addGroupManagerSingle; //start group-level manager + +if (A3XAI_enableHC && {_unitType in A3XAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (A3XAI_enableRadioMessages && {!((owner _targetPlayer) isEqualTo 0)}) then { + private ["_targetPlayerVehicleCrew","_radioText"]; + if ((_targetPlayer distance _destPos) < AIR_REINFORCE_RADIO_DIST) then { + _targetPlayerVehicleCrew = (crew (vehicle _targetPlayer)); + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count _targetPlayerVehicleCrew) > 0) then { + _vehicleDescription = format ["%1 %2",[configFile >> "CfgVehicles" >> _vehicleType,"displayName","patrol"] call BIS_fnc_returnConfigEntry,[configFile >> "CfgVehicles" >> _vehicleType,"textSingular","helicopter"] call BIS_fnc_returnConfigEntry]; + _radioText = [20,[_vehicleDescription]]; + { + [_x,_radioText] call A3XAI_radioSend; + } forEach _targetPlayerVehicleCrew; + }; + }; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created AI air reinforcement at %1 with vehicle type %2 with %3 crew units. Distance from destination: %4.",_vehiclePosition,_vehicleType,(count (units _unitGroup)),(_destPos distance _vehiclePosition)]}; + +true diff --git a/A3XAI/compile/A3XAI_startHunting.sqf b/A3XAI/compile/A3XAI_startHunting.sqf new file mode 100644 index 0000000..bf9f641 --- /dev/null +++ b/A3XAI/compile/A3XAI_startHunting.sqf @@ -0,0 +1,49 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup", "_triggerPos", "_targetPlayer", "_nearbyUnits", "_waypoint", "_patrolDist","_targetPos"]; + +_unitGroup = _this select 0; +_patrolDist = _this select 1; +_targetPlayer = _this select 2; +_triggerPos = _this select 3; + +_targetPos = getPosATL _targetPlayer; +if ((isPlayer _targetPlayer) && {(vehicle _targetPlayer) isKindOf "Land"}) then { + if (A3XAI_enableRadioMessages) then { + //diag_log "DEBUG: Sending radio static"; + if ((_unitGroup getVariable ["GroupSize",0]) > 0) then { + _nearbyUnits = _targetPlayer nearEntities [["LandVehicle",PLAYER_UNITS],TRANSMIT_RANGE_RADIO_HUNTKILLER]; + if ((count _nearbyUnits) > 10) then {_nearbyUnits resize 10;}; + { + if (isPlayer _x) then { + if (({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0) then { + [_x,[0,[]]] call A3XAI_radioSend; + }; + } + } count _nearbyUnits; + }; + }; + + _unitGroup setSpeedMode "FULL"; + + _waypoint = [_unitGroup,0]; //Group will move to waypoint index 0 (first waypoint). + _waypoint setWaypointType "MOVE"; + _waypoint setWaypointCompletionRadius 50; + _waypoint setWaypointTimeout [3, 3, 3]; + _waypoint setWaypointSpeed "FULL"; + _waypoint setWPPos _targetPos; + _waypoint setWaypointStatements ["true","if !(local this) exitWith {}; (group this) spawn A3XAI_hunterLocate;"]; + // _waypoint setWaypointSpeed "FULL"; + + _unitGroup setCurrentWaypoint _waypoint; + + _unitGroup setVariable ["targetplayer",_targetPlayer]; + if (A3XAI_enableHC && {"dynamic" in A3XAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; + _unitGroup setVariable ["MiscData",_targetPlayer]; + }; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Dynamic group %1 is now hunting player %2.",_unitGroup,_targetPlayer];}; +} else { + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3XAI_BIN_taskPatrol; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Dynamic group %1 is patrolling area.",_unitGroup];}; +}; diff --git a/A3XAI/compile/A3XAI_updGroupCount.sqf b/A3XAI/compile/A3XAI_updGroupCount.sqf new file mode 100644 index 0000000..04dfd25 --- /dev/null +++ b/A3XAI/compile/A3XAI_updGroupCount.sqf @@ -0,0 +1,15 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_isNewGroup"]; +_unitGroup = _this select 0; +_isNewGroup = _this select 1; + +if (isNull _unitGroup) exitWith {false}; + +if (_isNewGroup) then { + A3XAI_activeGroups pushBack _unitGroup; +} else { + A3XAI_activeGroups = A3XAI_activeGroups - [_unitGroup,grpNull]; +}; + +true \ No newline at end of file diff --git a/A3XAI/compile/A3XAI_updateSpawnCount.sqf b/A3XAI/compile/A3XAI_updateSpawnCount.sqf new file mode 100644 index 0000000..610ce28 --- /dev/null +++ b/A3XAI/compile/A3XAI_updateSpawnCount.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_trigger","_arrayString","_triggerArray","_action"]; +_trigger = _this select 0; +_arrayString = _this select 1; +_action = _this select 2; + +_triggerArray = missionNamespace getVariable [_arrayString,[]]; +if (!isNull _trigger) then { + if (_action) then { + if !(_trigger in _triggerArray) then { + _triggerArray pushBack _trigger; + }; + } else { + _triggerArray = _triggerArray - [_trigger]; + }; +}; + +if (objNull in _triggerArray) then { + _triggerArray = _triggerArray - [objNull]; +}; + +missionNamespace setVariable [_arrayString,_triggerArray]; diff --git a/A3XAI/compile/A3XAI_vehCrewRegroup.sqf b/A3XAI/compile/A3XAI_vehCrewRegroup.sqf new file mode 100644 index 0000000..86e82dc --- /dev/null +++ b/A3XAI/compile/A3XAI_vehCrewRegroup.sqf @@ -0,0 +1,43 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_vehicle","_loadWP","_loadWPCond","_unit","_regroupPos","_waypointCycle"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +if ((count (waypoints _unitGroup)) > 1) exitWith { + if (isNull (driver _vehicle)) then { + [_unitGroup,1] setWaypointPosition [(getPosATL _vehicle),0]; + _unitGroup setCurrentWaypoint [_unitGroup,1]; + }; +}; + +_unit = objNull; +{ + if (isNull (objectParent _x)) exitWith {_unit = _x}; +} forEach (units _unitGroup); + +if (isNull _unit) exitWith {_unitGroup call A3XAI_setVehicleRegrouped}; //If no units outside of vehicle, consider crew regrouped and exit script + +_regroupPos = if (isNull (driver _vehicle)) then { + (getPosATL _vehicle) +} else { + ([_vehicle,_unit,(_unit distance _vehicle)/2] call A3XAI_getPosBetween) +}; + +_loadWP = _unitGroup addWaypoint [_regroupPos,0]; +_loadWP setWaypointType "LOAD"; +_loadWPCond = "_vehicle = (group this) getVariable ['assignedVehicle',objNull]; ({_x isEqualTo (vehicle _x)} count (assignedCargo _vehicle)) isEqualTo 0"; +_loadWP setWaypointStatements [_loadWPCond,"if !(local this) exitWith {}; (group this) spawn A3XAI_vehCrewRegroupComplete;"]; + +_waypointCycle = _unitGroup addWaypoint [_regroupPos, 0]; +_waypointCycle setWaypointType "CYCLE"; +_waypointCycle setWaypointStatements ["true","if !(local this) exitWith {}; _unitGroup = (group this); deleteWaypoint [_unitGroup,2]; deleteWaypoint [_unitGroup,1];"]; +_waypointCycle setWaypointCompletionRadius 150; + +_loadWP setWaypointCompletionRadius 10; +_unitGroup setCurrentWaypoint _loadWP; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Regroup order issued for AI group %1 to vehicle %2. Check WP count: %3.",_unitGroup,typeOf _vehicle,(count (waypoints _unitGroup))];}; + +true diff --git a/A3XAI/compile/A3XAI_vehCrewRegroupComplete.sqf b/A3XAI/compile/A3XAI_vehCrewRegroupComplete.sqf new file mode 100644 index 0000000..f1cde9d --- /dev/null +++ b/A3XAI/compile/A3XAI_vehCrewRegroupComplete.sqf @@ -0,0 +1,16 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_vehicle","_vehiclePos"]; + +_unitGroup = _this; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +if (isNull _vehicle) exitWith {diag_log format ["A3XAI Error: Group %1 has null vehicle.",_unitGroup];}; +_vehiclePos = getPosATL _vehicle; +[_unitGroup,0] setWaypointPosition [_vehiclePos,0]; +[_unitGroup,2] setWaypointPosition [_vehiclePos,0]; +_unitGroup call A3XAI_setVehicleRegrouped; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Regroup completed for vehicle group %1.",_unitGroup];}; + +true diff --git a/A3XAI/compile/A3XAI_vehDestroyed.sqf b/A3XAI/compile/A3XAI_vehDestroyed.sqf new file mode 100644 index 0000000..0c87bb3 --- /dev/null +++ b/A3XAI/compile/A3XAI_vehDestroyed.sqf @@ -0,0 +1,46 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitsAlive","_vehiclePos"]; + +_vehicle = _this select 0; +_release = _this select 1; + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["VehicleDisabled",false]) exitWith {}; +_vehicle setVariable ["VehicleDisabled",true]; +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; +[_vehicle,_release] call A3XAI_respawnAIVehicle; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitsAlive = {alive _x} count (units _unitGroup); + +if (_unitsAlive > 0) then { + { + if !((gunner _vehicle) isEqualTo _x) then { + unassignVehicle _x; + }; + } forEach (units _unitGroup); + (units _unitGroup) allowGetIn false; + + if (isDedicated) then { + [_unitGroup,_vehicle] call A3XAI_addVehicleGroup; + } else { + _vehiclePos = getPosATL _vehicle; + _vehiclePos set [2,0]; + [_unitGroup,_vehiclePos] call A3XAI_createGroupTriggerObject; + A3XAI_addVehicleGroup_PVS = [_unitGroup,_vehicle]; + publicVariableServer "A3XAI_addVehicleGroup_PVS"; + [_unitGroup,"vehiclecrew"] call A3XAI_setUnitType; + }; + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: AI land vehicle patrol group %1 was converted to vehiclecrew type.",_unitGroup];}; +} else { + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3XAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3XAI_updateGroupSize_PVS"; + }; +}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; diff --git a/A3XAI/compile/A3XAI_vehMPKilled.sqf b/A3XAI/compile/A3XAI_vehMPKilled.sqf new file mode 100644 index 0000000..aae3e11 --- /dev/null +++ b/A3XAI/compile/A3XAI_vehMPKilled.sqf @@ -0,0 +1,14 @@ +#include "\A3XAI\globaldefines.hpp" + +if !(isServer) exitWith {false}; + +private ["_vehicle"]; + +_vehicle = _this; + +if !(isServer) exitWith {false}; +if (isNull _vehicle) exitWith {}; + +_vehicle setVariable ["A3XAI_deathTime",diag_tickTime]; + +true diff --git a/A3XAI/compile/A3XAI_vehStartPatrol.sqf b/A3XAI/compile/A3XAI_vehStartPatrol.sqf new file mode 100644 index 0000000..22e3356 --- /dev/null +++ b/A3XAI/compile/A3XAI_vehStartPatrol.sqf @@ -0,0 +1,23 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_unitGroup","_tooClose","_locationSelected"]; +_unitGroup = _this select 0; + +_tooClose = true; +_locationSelected = [0,0,0]; + +while {_tooClose} do { + _locationSelected = (A3XAI_locationsLand call A3XAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _locationSelected) > 300) then { + _tooClose = false; + }; +}; + +_locationSelected = [_locationSelected,random(300),random(360),0,[1,300]] call A3XAI_SHK_pos; +[_unitGroup,0] setWPPos _locationSelected; +[_unitGroup,0] setWaypointCompletionRadius 150; +if ((count (waypoints _unitGroup)) isEqualTo 1) then { + _unitGroup setCurrentWaypoint [_unitGroup,0]; +}; + +if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Set %1 waypoint position to %2.",_unitGroup,_locationSelected];}; diff --git a/A3XAI/config.cpp b/A3XAI/config.cpp new file mode 100644 index 0000000..b7640ca --- /dev/null +++ b/A3XAI/config.cpp @@ -0,0 +1,112 @@ +class CfgPatches { + class A3XAI { + units[] = {}; + weapons[] = {}; + requiredVersion = 0.1; + A3XAIVersion = "3"; + compatibleConfigVersions[] = {"3"}; + compatibleHCVersions[] = {"3"}; + requiredAddons[] = {"exile_client"}; + }; + class A3XAI_HC { + units[] = {}; + weapons[] = {}; + requiredVersion = 0.1; + A3XAI_HCVersion = "3"; + requiredAddons[] = {"exile_client"}; + }; +}; + +class CfgFunctions { + class A3XAI { + class A3XAI_Server { + file = "A3XAI"; + + class A3XAI_init { + preInit = 1; + }; + }; + }; + + class ExileClient { + class Bootstrap { + file = "\A3XAI\init\client"; + + class preInit { + preInit = 1; + }; + + class postInit { + postInit = 1; + }; + }; + }; + + class ExileServer { + class Bootstrap { + file = "\A3XAI\init\server"; + + class preInit { + preInit = 1; + }; + + class postInit { + postInit = 1; + }; + }; + }; +}; + +class CfgNonAIVehicles { + class A3XAI_EmptyDetector { + displayName="A3XAI_EmptyDetector"; + icon = "\a3\Ui_f\data\IGUI\Cfg\IslandMap\iconSensor_ca.paa"; + model = ""; + scope = public; + selectionFabric = "latka"; + simulation="detector"; + }; +}; + +class CfgLocationTypes { + class A3XAI_BlacklistedArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3XAI Blacklist Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; + class A3XAI_NoAggroArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3XAI No-Aggro Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; + class A3XAI_RandomSpawnArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3XAI Random Spawn Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; + class A3XAI_DynamicSpawnArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3XAI Dynamic Spawn Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; +}; diff --git a/A3XAI/fn_A3XAI_init.sqf b/A3XAI/fn_A3XAI_init.sqf new file mode 100644 index 0000000..5fdee2a --- /dev/null +++ b/A3XAI/fn_A3XAI_init.sqf @@ -0,0 +1,8 @@ +if (hasInterface) exitWith {}; + +if (isDedicated) then { + [] call compile preprocessFileLineNumbers "\A3XAI\init\A3XAI_initserver.sqf"; +} else { + [] call compile preprocessFileLineNumbers "\A3XAI\init\A3XAI_inithc.sqf"; +}; + diff --git a/A3XAI/globaldefines.hpp b/A3XAI/globaldefines.hpp new file mode 100644 index 0000000..6620787 --- /dev/null +++ b/A3XAI/globaldefines.hpp @@ -0,0 +1,207 @@ +//Advanced Configuration File (For development purposes) +//Do not edit unless you fully understand the consequences of the change. +//Improper editing may stop A3EAI/A3XAI from functioning properly or at all. + + +//Classnames +#define PARACHUTE_OBJECT "Steerable_Parachute_F" +#define RADIO_ITEM "ItemRadio" //A3XAI +//#define RADIO_ITEM "EpochRadio0" //A3EAI +#define PLAYER_UNITS "Exile_Unit_Player" //A3XAI +//#define PLAYER_UNITS "Epoch_Male_F","Epoch_Female_F" //A3EAI +#define LOOT_HOLDER_CLASS "GroundWeaponHolder" //A3XAI +//#define LOOT_HOLDER_CLASS "WH_Loot" //A3EAI +#define DEFAULT_UNIT_CLASSNAME "i_survivor_F" +#define SPACE_FOR_OBJECT "Land_Coil_F" +#define PLOTPOLE_OBJECT "Exile_Construction_Flag_Static" //A3XAI +//#define PLOTPOLE_OBJECT "PlotPole_EPOCH" //A3EAI +#define GRENADE_AMMO_3RND "3Rnd_HE_Grenade_shell" +#define GRENADE_AMMO_1RND "1Rnd_HE_Grenade_shell" +#define FIRST_AID_ITEM_AI "FirstAidKit" +#define FIRST_AID_ITEM_PLAYER "Exile_Item_InstaDoc" //A3XAI +//#define FIRST_AID_ITEM_PLAYER "FAK" //A3EAI +#define NVG_ITEM_AI "NVGoggles_OPFOR" +#define NVG_ITEM_PLAYER "NVGoggles" //A3XAI +// #define NVG_ITEM_PLAYER "NVG_EPOCH" //A3EAI +// #define DEFAULT_UNIFORM_ITEM "U_Test1_uniform" //A3EAI +// #define DEFAULT_VEST_ITEM_MALE "V_41_EPOCH" //A3EAI +// #define DEFAULT_VEST_ITEM_FEMALE "V_F41_EPOCH" //A3EAI +#define BLACKLIST_OBJECT_GENERAL "A3XAI_BlacklistedArea" //A3XAI +#define BLACKLIST_OBJECT_NOAGGRO "A3XAI_NoAggroArea" //A3XAI +#define BLACKLIST_OBJECT_RANDOM "A3XAI_RandomSpawnArea" //A3XAI +#define BLACKLIST_OBJECT_DYNAMIC "A3XAI_DynamicSpawnArea" //A3XAI +#define TRIGGER_OBJECT "A3XAI_EmptyDetector" //A3XAI +#define SENSOR_OBJECT "A3XAI_EmptyDetector" //A3XAI +// #define BLACKLIST_OBJECT_GENERAL "A3EAI_BlacklistedArea" //A3EAI +// #define BLACKLIST_OBJECT_NOAGGRO "A3EAI_NoAggroArea" //A3EAI +// #define BLACKLIST_OBJECT_RANDOM "A3EAI_RandomSpawnArea" //A3EAI +// #define BLACKLIST_OBJECT_DYNAMIC "A3EAI_DynamicSpawnArea" //A3EAI +// #define TRIGGER_OBJECT "A3EAI_EmptyDetector" //A3EAI + +//Probabilities +#define DYNAMIC_CHANCE_ADJUST_UNARMED -0.15 +#define DYNAMIC_CHANCE_ADJUST_WEAKENED 0.05 +#define DYNAMIC_CHANCE_ADJUST_LOOTING 0.05 +#define DYNAMIC_CHANCE_ADJUST_WEALTHY 0.10 + +//Distances +#define NEAREST_ENEMY_RANGE 350 +#define NEAREST_ENEMY_AIR 350 +#define NEAREST_ENEMY_INFANTRY 250 +#define NEAREST_ENEMY_LAND 300 +#define NEAREST_ENEMY_RANGE_ANTISTUCK 300 +#define PATROL_DISTANCE_BUFFER 100 +#define PLOTPOLE_RADIUS 300 +#define TRANSMIT_RANGE_RADIO_HUNTER 50 +#define SEEK_RANGE_HUNTER 450 +#define TRANSMIT_RANGE_RADIO_HUNTKILLER 50 +#define RECEIVE_DIST_RADIO_HUNTKILLER 200 +#define PLAYER_DISTANCE_SPAWN_AUTONOMOUS 300 +#define PLAYER_DISTANCE_SPAWN_AIGROUP 100 +#define PLAYER_DISTANCE_SPAWN_AIVEHICLE 200 +#define PLAYER_DISTANCE_NO_LOS_STATIC 200 +#define PLAYER_DISTANCE_WITH_LOS_STATIC 300 +#define PLAYER_DISTANCE_NO_LOS_STATIC_CUSTOM 100 +#define PLAYER_DISTANCE_WITH_LOS_STATIC_CUSTOM 200 +#define PLAYER_DISTANCE_NO_LOS_DYNAMIC 200 +#define PLAYER_DISTANCE_WITH_LOS_DYNAMIC 400 +#define PLAYER_DISTANCE_NO_LOS_RANDOM 200 +#define PLAYER_DISTANCE_WITH_LOS_RANDOM 400 +#define PLAYER_DISTANCE_WITH_LOS_ANTISTUCK 300 +#define NO_AGGRO_AREA_SIZE 200 //A3XAI +#define SPAWN_DIST_FROM_NO_AGGRO_AREA 100 //A3XAI +//#define NO_AGGRO_AREA_SIZE 300 //A3EAI +#define NO_AGGRO_AREA_EXPANDED_SIZE 400 //A3XAI +//#define NO_AGGRO_AREA_EXPANDED_SIZE 400 //A3EAI +#define BLACKLIST_AREA_SIZE 750 //A3XAI +//#define BLACKLIST_AREA_SIZE 750 //A3EAI +#define BLACKLIST_AREA_HC_SIZE 750 +#define TRIGGER_SIZE_SMALL 600 +#define TRIGGER_SIZE_NORMAL 650 +#define TRIGGER_SIZE_EXPANDED 750 +#define TRIGGER_SIZE_NORMAL_DOUBLED 1300 +#define TRIGGER_SIZE_STATIC 700 +#define TRIGGER_SIZE_RANDOM 650 +#define TRIGGER_SIZE_DYNAMIC 650 +#define DUMBFIRE_AI_DISTANCE 400 +#define SPAWN_DISTANCE_BASE_DYNAMICRANDOM 200 +#define SPAWN_DISTANCE_EXTRA_DYNAMICRANDOM 100 +#define SPAWN_DISTANCE_DEFAULT_RANDOM 300 +#define SPAWN_DIRECTION_VARIANCE_DYNAMIC 80 +#define TEMP_BLACKLIST_AREA_RANDOM_SIZE 650 +#define TEMP_BLACKLIST_AREA_DYNAMIC_SIZE 650 +#define TEMP_BLACKLIST_AREA_HC_SIZE 750 +#define CREATE_RANDOM_SPAWN_DIST_BASE 300 +#define CREATE_RANDOM_SPAWN_DIST_VARIANCE 450 +#define STATIC_SPAWN_OBJECT_RANGE 250 +#define STATIC_SPAWN_MIN_PATROL_RADIUS 125 +#define STATIC_SPAWN_MAX_PATROL_RADIUS 300 +#define STATIC_SPAWN_OBJECT_SIZE_REQ 15 +#define PATROL_DIST_PARAGROUP 100 +#define PATROL_DIST_VEHICLEGROUP 75 +#define PATROL_DIST_DYNAMIC 150 +#define PATROL_DIST_RANDOM 150 +#define ANTISTUCK_LAND_MIN_WP_DIST 300 +#define ANTISTUCK_LAND_WP_DIST_BASE 50 +#define ANTISTUCK_LAND_WP_DIST_VARIANCE 900 +#define ANTISTUCK_AIR_MIN_WP_DIST 300 +#define ANTISTUCK_AIR_WP_DIST_BASE 50 +#define ANTISTUCK_AIR_WP_DIST_VARIANCE 900 +#define ANTISTUCK_MIN_TRAVEL_DIST_AIR 750 +#define ANTISTUCK_MIN_TRAVEL_DIST_LAND 10 +#define ANTISTUCK_MIN_TRAVEL_DIST_INFANTRY 5 +#define NEXT_WP_DIST_UAV 300 +#define NEXT_WP_DIST_AIR 300 +#define WP_POS_INGRESS_BASE_UAV 100 +#define WP_POS_INGRESS_VARIANCE_UAV 350 +#define WP_POS_EGRESS_BASE_UAV 300 +#define WP_POS_EGRESS_VARIANCE_UAV 100 +#define WP_POS_INGRESS_BASE_AIR 100 +#define WP_POS_INGRESS_VARIANCE_AIR 700 +#define WP_POS_EGRESS_BASE_AIR 600 +#define WP_POS_EGRESS_VARIANCE_AIR 100 +#define DETECT_RANGE_UGV 300 +#define DETECT_RANGE_UAV 500 +#define DETECT_RANGE_AIR 500 +#define DETECT_RANGE_AIR_REINFORCE 500 +#define DETECT_RANGE_AIR_CUSTOM 500 +#define DETECT_LENGTH_UGV_2D 600 +#define DETECT_LENGTH_UAV_2D 1200 +#define DETECT_LENGTH_AIR_2D 1200 +#define BEGIN_DETECT_DIST_UGV 750 +#define BEGIN_DETECT_DIST_UAV 1000 +#define BEGIN_DETECT_DIST_AIR 1000 +#define FLYINHEIGHT_UAV_SEARCHING_BASE 125 +#define FLYINHEIGHT_UAV_SEARCHING_VARIANCE 25 +#define FLYINHEIGHT_UAV_PATROLLING_BASE 200 +#define FLYINHEIGHT_UAV_PATROLLING_VARIANCE 25 +#define FLYINHEIGHT_AIR_SEARCHING_BASE 125 +#define FLYINHEIGHT_AIR_SEARCHING_VARIANCE 25 +#define FLYINHEIGHT_AIR_PATROLLING_BASE 200 +#define FLYINHEIGHT_AIR_PATROLLING_VARIANCE 25 +#define FLYINHEIGHT_AIR_CUSTOM 125 +#define FLYINHEIGHT_AIR_REINFORCE 125 +#define AIR_REINFORCE_DIST_BETWEEN_LOCATIONS 1000 +#define AIR_REINFORCE_SPAWN_DIST_BASE 2000 +#define AIR_REINFORCE_SPAWN_DIST_VARIANCE 750 +#define AIR_REINFORCE_RADIO_DIST 300 +#define AIR_REINFORCE_DESPAWN_DIST 2000 +#define RADIO_RECEPTION_RANGE 250 +#define PARACHUTE_HEIGHT_REQUIRED 50 +#define REGROUP_VEHICLEGROUP_DIST 175 +#define DYNAMIC_LOOTING_DISTANCE 25 +#define NO_AGGRO_RANGE_MAN 500 +#define NO_AGGRO_RANGE_AIR 900 +#define NO_AGGRO_RANGE_LAND 500 +#define NO_AGGRO_RANGE_UAV 900 //Comment: UAVs have hilariously large range. Unlikely long enough distance, better than nothing. +#define NO_AGGRO_RANGE_UGV 500 +#define NO_AGGRO_RANGE_DEFAULT 900 + +//Amounts +#define STATIC_SPAWN_MIN_SPAWN_POINTS 6 +#define MAX_UNITS_PER_STATIC_SPAWN 5 +#define MAX_RANDOMSPAWN_RETRY_ATTEMPTS 2 +#define DYNAMIC_RICH_PLAYER_AMOUNT 5000 +#define DYNAMIC_WEAK_PLAYER_HEALTH 0.4 +#define INFINITY 3.4028235e38 + +//Timing +#define TIME_PER_RESPAWN_PROCESSING 5 +#define MONITOR_CLEANDEAD_FREQ 300 +//#define MONITOR_VEHICLECLEANUP_FREQ 60 //A3EAI +#define MONITOR_LOCATIONCLEANUP_FREQ 300 +#define MONITOR_CHECK_RANDSPAWN_FREQ 300 +#define RANDSPAWN_EXPIRY_TIME 900 +#define SIDECHECK_TIME 1200 +#define MONITOR_UPDATE_PLAYER_COUNT_FREQ 60 +#define DYNSPAWNMGR_SLEEP_DELAY 300 +// #define KRYPTO_CLEANUP_FREQ 900 //A3EAI +#define SERVER_START_TIMEOUT 300 +#define TRIGGER_TIMEOUT_STATIC 10, 10, 10 +#define TRIGGER_TIMEOUT_PARAGROUP 3, 3, 3 +#define TRIGGER_TIMEOUT_VEHICLEGROUP 3, 3, 3 +#define TRIGGER_TIMEOUT_RANDOM 3, 3, 3 +#define TRIGGER_TIMEOUT_DYNAMIC 3, 3, 3 +#define TRIGGER_TIMEOUT_STATICCUSTOM 3, 3, 3 +#define TRIGGER_TIMEOUT_NOAGGROAREA 0, 0, 0 +#define DEFENSIVE_AGGRESSION_TIME 120 +#define ADD_RESPAWN_FAST_TIME 60 +#define ANTISTUCK_CHECK_TIME_INFANTRY 60 +#define ANTISTUCK_CHECK_TIME_AIR 300 +#define ANTISTUCK_CHECK_TIME_LAND 120 +#define LOOT_PULL_RATE 30 +#define CHECK_VEHICLE_AMMO_FUEL_TIME 180 +#define STATIC_SPAWN_CHECK_FREQ 60 +#define RANDOM_SPAWN_CHECK_FREQ 75 +#define DYNAMIC_SPAWN_CHECK_FREQ 90 + +//Other definitions +#define PLAYER_GROUP_SIDE resistance //A3XAI +//#define PLAYER_GROUP_SIDE1 west //A3EAI +//#define PLAYER_GROUP_SIDE2 east //A3EAI +//#define AI_GROUP_SIDE resistance //A3EAI +#define AI_VEHICLEWEAPON_EXPLOSIVERATING_LIMIT 0.9 +#define EXTERNAL_OBJECT_MONITOR_NAME "ExileSimulationMonitoredVehicles" //A3XAI +#define SERVER_STARTED_INDICATOR "PublicServerIsLoaded" //A3XAI +//#define SERVER_STARTED_INDICATOR "EPOCH_BuildingSlotCount" //A3EAI + diff --git a/A3XAI/init/A3XAI_HCFunctions.sqf b/A3XAI/init/A3XAI_HCFunctions.sqf new file mode 100644 index 0000000..cd20657 --- /dev/null +++ b/A3XAI/init/A3XAI_HCFunctions.sqf @@ -0,0 +1,32 @@ +diag_log "[A3XAI] Compiling A3XAI HC functions."; + +//HC-only +A3XAI_setGroupTriggerVars = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_setGroupTriggerVars.sqf",A3XAI_directory]; +A3XAI_handlestatic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handlestatic.sqf",A3XAI_directory]; +A3XAI_handlestaticcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handlestaticcustom.sqf",A3XAI_directory]; +A3XAI_handleland = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handleland.sqf",A3XAI_directory]; +A3XAI_handleair = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handleair.sqf",A3XAI_directory]; +A3XAI_handleaircustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handleaircustom.sqf",A3XAI_directory]; +A3XAI_handleair_reinforce = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handleair_reinforce.sqf",A3XAI_directory]; +A3XAI_handlelandcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handlelandcustom.sqf",A3XAI_directory]; +A3XAI_handledynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handledynamic.sqf",A3XAI_directory]; +A3XAI_handlerandom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handlerandom.sqf",A3XAI_directory]; +A3XAI_handleuav = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handleuav.sqf",A3XAI_directory]; +A3XAI_handleugv = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handleugv.sqf",A3XAI_directory]; +A3XAI_handlevehiclecrew = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_handlevehiclecrew.sqf",A3XAI_directory]; +A3XAI_addNewGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_addNewGroup.sqf",A3XAI_directory]; +A3XAI_addHunterGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_addHunterGroup.sqf",A3XAI_directory]; +A3XAI_updateGroupSizeAuto = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_updateGroupSizeAuto.sqf",A3XAI_directory]; +A3XAI_updateGroupSizeManual = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_updateGroupSizeManual.sqf",A3XAI_directory]; +A3XAI_airReinforcementDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_airReinforcementDetection.sqf",A3XAI_directory]; +A3XAI_cleanupReinforcementHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_cleanupReinforcementHC.sqf",A3XAI_directory]; +A3XAI_setLoadoutVariables_HC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_setLoadoutVariables_HC.sqf",A3XAI_directory]; +A3XAI_createGroupTriggerObject = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_createGroupTriggerObject.sqf",A3XAI_directory]; +A3XAI_requestGroupVars = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_requestGroupVars.sqf",A3XAI_directory]; +A3XAI_updateServerLoot = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_updateServerLoot.sqf",A3XAI_directory]; +A3XAI_updateGroupLootPoolHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_updateGroupLootPoolHC.sqf",A3XAI_directory]; +A3XAI_setBehaviorHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient\A3XAI_setBehaviorHC.sqf",A3XAI_directory]; + +diag_log "[A3XAI] A3XAI HC functions loaded."; + +true diff --git a/A3XAI/init/A3XAI_HC_PVEH.sqf b/A3XAI/init/A3XAI_HC_PVEH.sqf new file mode 100644 index 0000000..7873391 --- /dev/null +++ b/A3XAI/init/A3XAI_HC_PVEH.sqf @@ -0,0 +1,14 @@ +"A3XAI_transferGroup_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_addNewGroup;diag_log format ["Debug: %1",_this];}; +"A3XAI_sendGroupTriggerVars_PVC" addPublicVariableEventHandler {_nul = (_this select 1) spawn A3XAI_setGroupTriggerVars;diag_log format ["Debug: %1",_this];}; +"A3XAI_updateGroupLoot_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_updateGroupLootPoolHC;diag_log format ["Debug: %1",_this];}; +"A3XAI_sendHunterGroup_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_addHunterGroup;diag_log format ["Debug: %1",_this];}; +"A3XAI_updateGroupSizeManual_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_updateGroupSizeManual;diag_log format ["Debug: %1",_this];}; +"A3XAI_updateGroupSizeAuto_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_updateGroupSizeAuto;diag_log format ["Debug: %1",_this];}; +"A3XAI_setCurrentWaypoint_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_setCurrentWaypointHC;diag_log format ["Debug: %1",_this];}; +"A3XAI_cleanupReinforcement_PVC" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_cleanupReinforcementHC;diag_log format ["Debug: %1",_this];}; +"A3XAI_setBehavior_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_setBehaviorHC;diag_log format ["Debug: %1",_this];}; +"A3XAI_requestVehicleRelease_PVC" addPublicVariableEventHandler {(_this select 1) call A3XAI_releaseVehicleAllow;diag_log format ["Debug: %1",_this];}; + +diag_log "[A3XAI] A3XAI HC PVEHs loaded."; + +true diff --git a/A3XAI/init/A3XAI_ServerHC_PVEH.sqf b/A3XAI/init/A3XAI_ServerHC_PVEH.sqf new file mode 100644 index 0000000..055a9bb --- /dev/null +++ b/A3XAI/init/A3XAI_ServerHC_PVEH.sqf @@ -0,0 +1,25 @@ +"A3XAI_respawnGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_addRespawnQueue;diag_log format ["Debug: %1",_this];}; +"A3XAI_sendKillResponse_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_sendKillResponse;diag_log format ["Debug: %1",_this];}; +"A3XAI_despawnRandomGroup_PVS" addPublicVariableEventHandler {private ["_trigger"];_trigger = (_this select 1) getVariable ["trigger",objNull];[_trigger,true] spawn A3XAI_despawn_random;diag_log format ["Debug: %1",_this];}; +"A3XAI_despawnDynamicGroup_PVS" addPublicVariableEventHandler {private ["_trigger"];_trigger = (_this select 1) getVariable ["trigger",objNull];[_trigger,true] spawn A3XAI_despawn_dynamic;diag_log format ["Debug: %1",_this];}; +"A3XAI_respawnVehicle_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_respawnAIVehicle;diag_log format ["Debug: %1",_this];}; +"A3XAI_addVehicleGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_addVehicleGroup;diag_log format ["Debug: %1",_this];}; +"A3XAI_addParaGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_addParaGroup;diag_log format ["Debug: %1",_this];}; +"A3XAI_transferGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_HCGroupToServer;diag_log format ["Debug: %1",_this];}; +"A3XAI_SMS_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_radioSend;diag_log format ["Debug: %1",_this];}; +"A3XAI_getGroupTriggerVars_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_getGroupTriggerVars;diag_log format ["Debug: %1",_this];}; +"A3XAI_registerDeath_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_registerDeath;diag_log format ["Debug: %1",_this];}; +"A3XAI_updateGroupLoot_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_updateGroupLootPool;diag_log format ["Debug: %1",_this];}; +"A3XAI_protectGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_protectRemoteGroup;diag_log format ["Debug: %1",_this];}; +"A3XAI_updateGroupSize_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_updateGroupSizeServer;diag_log format ["Debug: %1",_this];}; +"A3XAI_HCLogin_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_HCListener;diag_log format ["Debug: %1",_this];}; +"A3XAI_updateReinforcePlaces_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_updateReinforcePlaces;diag_log format ["Debug: %1",_this];}; +"A3XAI_setPermaDeleteSpawn_PVS" addPublicVariableEventHandler {private ["_trigger"];_trigger = (_this select 1) getVariable ["trigger",objNull];_trigger setVariable ["permadelete",true];diag_log format ["Debug: %1",_this];}; +"A3XAI_deleteCustomSpawn_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_deleteCustomSpawn;diag_log format ["Debug: %1",_this];}; +"A3XAI_setDriverUnit_PVS" addPublicVariableEventHandler {(_this select 1) setVariable ["isDriver",true];diag_log format ["Debug: %1",_this];}; +"A3XAI_setVehicleRegrouped_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_setVehicleRegrouped;diag_log format ["Debug: %1",_this];}; +"A3XAI_spawnReinforcements_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_spawn_reinforcement;diag_log format ["Debug: %1",_this];}; +"A3XAI_setBehavior_PVS" addPublicVariableEventHandler {(_this select 1) call A3XAI_setBehavior;diag_log format ["Debug: %1",_this];}; +"A3XAI_generateLootOnDeath_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3XAI_generateLootOnDeath;diag_log format ["Debug: %1",_this];}; + +diag_log "[A3XAI] Serverside PVEHs loaded."; diff --git a/A3XAI/init/A3XAI_ServerHC_functions.sqf b/A3XAI/init/A3XAI_ServerHC_functions.sqf new file mode 100644 index 0000000..f4fe06c --- /dev/null +++ b/A3XAI/init/A3XAI_ServerHC_functions.sqf @@ -0,0 +1 @@ +diag_log "[A3XAI] Compiling A3XAI HC functions."; //Server-only A3XAI_transferGroupToHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_transferGroupToHC.sqf",A3XAI_directory]; A3XAI_HCGroupToServer = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_transferGroupToServer.sqf",A3XAI_directory]; A3XAI_getGroupTriggerVars = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_getGroupTriggerVars.sqf",A3XAI_directory]; A3XAI_updateGroupLootPool = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_updateGroupLootPool.sqf",A3XAI_directory]; A3XAI_HCListener = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_HCListener.sqf",A3XAI_directory]; A3XAI_updateGroupSizeServer = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_updateGroupSizeServer.sqf",A3XAI_directory]; A3XAI_registerDeath = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_registerDeath.sqf",A3XAI_directory]; A3XAI_protectRemoteGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_protectRemoteGroup.sqf",A3XAI_directory]; A3XAI_setBehavior = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_headlessclient_server\A3XAI_setBehavior.sqf",A3XAI_directory]; diag_log "[A3XAI] A3XAI HC functions compiled."; \ No newline at end of file diff --git a/A3XAI/init/A3XAI_custom_loader.sqf b/A3XAI/init/A3XAI_custom_loader.sqf new file mode 100644 index 0000000..342cf73 --- /dev/null +++ b/A3XAI/init/A3XAI_custom_loader.sqf @@ -0,0 +1,9 @@ +#include "\A3XAI\globaldefines.hpp" + +//Do not edit this file + +waitUntil {uiSleep 3; !isNil "A3XAI_locations_ready"}; + +call compile preprocessFileLineNumbers "A3XAI_config\A3XAI_custom_defs.sqf"; + +A3XAI_customSpawnsReady = true; diff --git a/A3XAI/init/A3XAI_functions.sqf b/A3XAI/init/A3XAI_functions.sqf new file mode 100644 index 0000000..9f0af8f --- /dev/null +++ b/A3XAI/init/A3XAI_functions.sqf @@ -0,0 +1,195 @@ +/* + A3XAI Functions + +*/ + +diag_log "[A3XAI] Compiling A3XAI functions."; + +call compile preprocessFile format ["%1\SHK_pos\A3XAI_SHK_pos_init.sqf",A3XAI_directory]; + +//A3XAI_behavior +A3XAI_BIN_taskPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_BIN_taskPatrol.sqf",A3XAI_directory]; +A3XAI_customHeliDetect = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_customHeliDetect.sqf",A3XAI_directory]; +A3XAI_heliDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_heliDetection.sqf",A3XAI_directory]; +A3XAI_heliStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_heliStartPatrol.sqf",A3XAI_directory]; +A3XAI_hunterLocate = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_hunterLocate.sqf",A3XAI_directory]; +A3XAI_huntKiller = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_huntKiller.sqf",A3XAI_directory]; +A3XAI_reinforce_begin = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_reinforce_begin.sqf",A3XAI_directory]; +A3XAI_vehCrewRegroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_vehCrewRegroup.sqf",A3XAI_directory]; +A3XAI_vehCrewRegroupComplete = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_vehCrewRegroupComplete.sqf",A3XAI_directory]; +A3XAI_vehStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_vehStartPatrol.sqf",A3XAI_directory]; +A3XAI_UAVStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_UAVStartPatrol.sqf",A3XAI_directory]; +A3XAI_UAVDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_UAVDetection.sqf",A3XAI_directory]; +A3XAI_UGVStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_UGVStartPatrol.sqf",A3XAI_directory]; +A3XAI_UGVDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_UGVDetection.sqf",A3XAI_directory]; +A3XAI_areaSearching = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_areaSearching.sqf",A3XAI_directory]; +A3XAI_startHunting = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_startHunting.sqf",A3XAI_directory]; +A3XAI_defensiveAggression = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_defensiveAggression.sqf",A3XAI_directory]; +A3XAI_forceBehavior = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_forceBehavior.sqf",A3XAI_directory]; + +//A3XAI_unit_events +A3XAI_handle_death_UV = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handle_death_UV.sqf",A3XAI_directory]; +A3XAI_handleDamageHeli = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDamageHeli.sqf",A3XAI_directory]; +A3XAI_handleDamageVeh = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDamageVeh.sqf",A3XAI_directory]; +A3XAI_handleDamageUnit = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDamageUnit.sqf",A3XAI_directory]; +A3XAI_handleDamageUGV = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDamageUGV.sqf",A3XAI_directory]; +A3XAI_handleDeath_air = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_air.sqf",A3XAI_directory]; +A3XAI_handleDeath_air_reinforce = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_air_reinforce.sqf",A3XAI_directory]; +A3XAI_handleDeath_aircrashed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_aircrashed.sqf",A3XAI_directory]; +A3XAI_handleDeath_aircustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_aircustom.sqf",A3XAI_directory]; +A3XAI_handleDeath_dynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_dynamic.sqf",A3XAI_directory]; +A3XAI_handleDeath_generic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_generic.sqf",A3XAI_directory]; +A3XAI_handleDeath_land = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_land.sqf",A3XAI_directory]; +A3XAI_handleDeath_landcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_landcustom.sqf",A3XAI_directory]; +A3XAI_handleDeath_random = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_random.sqf",A3XAI_directory]; +A3XAI_handleDeath_static = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_static.sqf",A3XAI_directory]; +A3XAI_handleDeath_staticcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_staticcustom.sqf",A3XAI_directory]; +A3XAI_handleDeath_vehiclecrew = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeath_vehiclecrew.sqf",A3XAI_directory]; +A3XAI_handleDeathEvent = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_handleDeathEvent.sqf",A3XAI_directory]; +A3XAI_heliDestroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_heliDestroyed.sqf",A3XAI_directory]; +A3XAI_heliEvacuated = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_heliEvacuated.sqf",A3XAI_directory]; +A3XAI_heliLanded = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_heliLanded.sqf",A3XAI_directory]; +A3XAI_heliParaDrop = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_heliParaDrop.sqf",A3XAI_directory]; +A3XAI_UAV_destroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_UAV_destroyed.sqf",A3XAI_directory]; +A3XAI_UGV_destroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_UGV_destroyed.sqf",A3XAI_directory]; +A3XAI_vehDestroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_vehDestroyed.sqf",A3XAI_directory]; +A3XAI_vehMPKilled = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_vehMPKilled.sqf",A3XAI_directory]; +A3XAI_ejectParachute = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_ejectParachute.sqf",A3XAI_directory]; + +//A3XAI_unit_spawning +A3XAI_addRespawnQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addRespawnQueue.sqf",A3XAI_directory]; +A3XAI_addVehicleGunners = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addVehicleGunners.sqf",A3XAI_directory]; +A3XAI_cancelDynamicSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_cancelDynamicSpawn.sqf",A3XAI_directory]; +A3XAI_cancelRandomSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_cancelRandomSpawn.sqf",A3XAI_directory]; +A3XAI_create_UV_unit = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_create_UV_unit.sqf",A3XAI_directory]; +A3XAI_createCustomSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createCustomSpawn.sqf",A3XAI_directory]; +A3XAI_createUnit = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createUnit.sqf",A3XAI_directory]; +A3XAI_despawn_dynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_despawn_dynamic.sqf",A3XAI_directory]; +A3XAI_despawn_random = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_despawn_random.sqf",A3XAI_directory]; +A3XAI_despawn_static = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_despawn_static.sqf",A3XAI_directory]; +A3XAI_despawnUniversal = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_despawnUniversal.sqf",A3XAI_directory]; +A3XAI_processRespawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_processRespawn.sqf",A3XAI_directory]; +A3XAI_respawnGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_respawnGroup.sqf",A3XAI_directory]; +A3XAI_setup_randomspawns = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setup_randomspawns.sqf",A3XAI_directory]; +A3XAI_spawn_reinforcement = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawn_reinforcement.sqf",A3XAI_directory]; +A3XAI_spawn_UV_patrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawn_UV_patrol.sqf",A3XAI_directory]; +A3XAI_spawnGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnGroup.sqf",A3XAI_directory]; +A3XAI_spawnInfantryCustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnInfantryCustom.sqf",A3XAI_directory]; +A3XAI_spawnUnits_dynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnUnits_dynamic.sqf",A3XAI_directory]; +A3XAI_spawnUnits_random = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnUnits_random.sqf",A3XAI_directory]; +A3XAI_spawnUnits_static = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnUnits_static.sqf",A3XAI_directory]; +A3XAI_spawnVehicleCustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnVehicleCustom.sqf",A3XAI_directory]; +A3XAI_spawnVehiclePatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_spawnVehiclePatrol.sqf",A3XAI_directory]; +A3XAI_addVehicleGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addVehicleGroup.sqf",A3XAI_directory]; +A3XAI_addParaGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addParaGroup.sqf",A3XAI_directory]; +A3XAI_respawnAIVehicle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_respawnAIVehicle.sqf",A3XAI_directory]; +A3XAI_cleanupReinforcementGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_cleanupReinforcementGroup.sqf",A3XAI_directory]; + +//A3XAI_utilities +A3XAI_addItem = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addItem.sqf",A3XAI_directory]; +A3XAI_addLandVehEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addLandVehEH.sqf",A3XAI_directory]; +A3XAI_addMapMarker = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addMapMarker.sqf",A3XAI_directory]; +A3XAI_addTempNVG = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addTempNVG.sqf",A3XAI_directory]; +A3XAI_addTemporaryWaypoint = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addTemporaryWaypoint.sqf",A3XAI_directory]; +A3XAI_addToExternalObjectMonitor = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addToExternalObjectMonitor.sqf",A3XAI_directory]; +A3XAI_addUAVEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addUAVEH.sqf",A3XAI_directory]; +A3XAI_addUGVEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addUGVEH.sqf",A3XAI_directory]; +A3XAI_addUnitEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addUnitEH.sqf",A3XAI_directory]; +A3XAI_addUVUnitEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addUVUnitEH.sqf",A3XAI_directory]; +A3XAI_addVehAirEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addVehAirEH.sqf",A3XAI_directory]; +A3XAI_chance = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_chance.sqf",A3XAI_directory]; +A3XAI_checkClassname = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkClassname.sqf",A3XAI_directory]; +A3XAI_checkInNoAggroArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkInNoAggroArea.sqf",A3XAI_directory]; +A3XAI_checkInActiveNoAggroArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkInActiveNoAggroArea.sqf",A3XAI_directory]; +A3XAI_checkInActiveStaticSpawnArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkInActiveStaticSpawnArea.sqf",A3XAI_directory]; +A3XAI_checkIsWeapon = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkIsWeapon.sqf",A3XAI_directory]; +A3XAI_clearVehicleCargo = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_clearVehicleCargo.sqf",A3XAI_directory]; +A3XAI_countVehicleGunners = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_countVehicleGunners.sqf",A3XAI_directory]; +A3XAI_createBlackListArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createBlackListArea.sqf",A3XAI_directory]; +A3XAI_createBlackListAreaDynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createBlackListAreaDynamic.sqf",A3XAI_directory]; +A3XAI_createBlackListAreaRandom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createBlackListAreaRandom.sqf",A3XAI_directory]; +A3XAI_createNoAggroArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createNoAggroArea.sqf",A3XAI_directory]; +A3XAI_createBlacklistAreaQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createBlacklistAreaQueue.sqf",A3XAI_directory]; +A3XAI_createCustomInfantryQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createCustomInfantryQueue.sqf",A3XAI_directory]; +A3XAI_createCustomInfantrySpawnQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createCustomInfantrySpawnQueue.sqf",A3XAI_directory]; +A3XAI_createCustomVehicleQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createCustomVehicleQueue.sqf",A3XAI_directory]; +A3XAI_createGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createGroup.sqf",A3XAI_directory]; +A3XAI_createInfantryQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createInfantryQueue.sqf",A3XAI_directory]; +A3XAI_createRandomInfantrySpawnQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createRandomInfantrySpawnQueue.sqf",A3XAI_directory]; +A3XAI_createTriggerArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_createTriggerArea.sqf",A3XAI_directory]; +A3XAI_deleteGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_deleteGroup.sqf",A3XAI_directory]; +A3XAI_markGroupUnitsForDeletion = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_markGroupUnitsForDeletion.sqf",A3XAI_directory]; +A3XAI_deleteCustomSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_deleteCustomSpawn.sqf",A3XAI_directory]; +A3XAI_findSpawnPos = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_findSpawnPos.sqf",A3XAI_directory]; +A3XAI_fixStuckGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_fixStuckGroup.sqf",A3XAI_directory]; +A3XAI_getNoAggroStatus = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getNoAggroStatus.sqf",A3XAI_directory]; +A3XAI_getSafePosReflected = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getSafePosReflected.sqf",A3XAI_directory]; +A3XAI_getSpawnParams = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getSpawnParams.sqf",A3XAI_directory]; +A3XAI_getUnitLevel = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getUnitLevel.sqf",A3XAI_directory]; +A3XAI_getWeapon = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getWeapon.sqf",A3XAI_directory]; +A3XAI_hasLOS = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_hasLOS.sqf",A3XAI_directory]; +A3XAI_initializeTrigger = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_initializeTrigger.sqf",A3XAI_directory]; +A3XAI_initNoAggroStatus = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_initNoAggroStatus.sqf",A3XAI_directory]; +A3XAI_initUVGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_initUVGroup.sqf",A3XAI_directory]; +A3XAI_moveToPosAndDeleteWP = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_moveToPosAndDeleteWP.sqf",A3XAI_directory]; +A3XAI_moveToPosAndPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_moveToPosAndPatrol.sqf",A3XAI_directory]; +A3XAI_noAggroAreaToggle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_noAggroAreaToggle.sqf",A3XAI_directory]; +A3XAI_noAggroAreaActivate = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_noAggroAreaActivate.sqf",A3XAI_directory]; +A3XAI_noAggroAreaDeactivate = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_noAggroAreaDeactivate.sqf",A3XAI_directory]; +A3XAI_param = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_param.sqf",A3XAI_directory]; +A3XAI_posInBuilding = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_posInBuilding.sqf",A3XAI_directory]; +A3XAI_protectGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_protectGroup.sqf",A3XAI_directory]; +A3XAI_protectObject = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_protectObject.sqf",A3XAI_directory]; +A3XAI_purgeUnitGear = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_purgeUnitGear.sqf",A3XAI_directory]; +A3XAI_radioSend = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_radioSend.sqf",A3XAI_directory]; +A3XAI_recycleGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_recycleGroup.sqf",A3XAI_directory]; +A3XAI_releaseVehicleAllow = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_releaseVehicleAllow.sqf",A3XAI_directory]; +A3XAI_releaseVehicleNow = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_releaseVehicleNow.sqf",A3XAI_directory]; +A3XAI_reloadVehicleTurrets = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_reloadVehicleTurrets.sqf",A3XAI_directory]; +A3XAI_removeExplosive = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_removeExplosive.sqf",A3XAI_directory]; +A3XAI_returnNoAggroAreaAll = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_returnNoAggroAreaAll.sqf",A3XAI_directory]; +A3XAI_returnNoAggroAreaActive = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_returnNoAggroAreaActive.sqf",A3XAI_directory]; +A3XAI_secureVehicle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_secureVehicle.sqf",A3XAI_directory]; +A3XAI_sendKillResponse = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_sendKillResponse.sqf",A3XAI_directory]; +A3XAI_setFirstWPPos = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setFirstWPPos.sqf",A3XAI_directory]; +A3XAI_setNoAggroStatus = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setNoAggroStatus.sqf",A3XAI_directory]; +A3XAI_setSkills = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setSkills.sqf",A3XAI_directory]; +A3XAI_setVehicleRegrouped = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setVehicleRegrouped.sqf",A3XAI_directory]; +A3XAI_updateSpawnCount = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_updateSpawnCount.sqf",A3XAI_directory]; +// A3XAI_updGroupCount = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_updGroupCount.sqf",A3XAI_directory]; +A3XAI_selectRandom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_selectRandom.sqf",A3XAI_directory]; +A3XAI_setRandomWaypoint = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setRandomWaypoint.sqf",A3XAI_directory]; +A3XAI_setSpawnParams = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setSpawnParams.sqf",A3XAI_directory]; +A3XAI_getPosBetween = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getPosBetween.sqf",A3XAI_directory]; +A3XAI_debugMarkerLocation = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_debugMarkerLocation.sqf",A3XAI_directory]; +A3XAI_sendExileMessage = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_sendExileMessage.sqf",A3XAI_directory]; + +//Group functions +A3XAI_getLocalFunctions = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getLocalFunctions.sqf",A3XAI_directory]; +A3XAI_getAntistuckTime = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_getAntistuckTime.sqf",A3XAI_directory]; +A3XAI_setLoadoutVariables = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setLoadoutVariables.sqf",A3XAI_directory]; +A3XAI_setUnitType = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_setUnitType.sqf",A3XAI_directory]; +A3XAI_execEveryLoop_air = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_execEveryLoop_air.sqf",A3XAI_directory]; +A3XAI_execEveryLoop_infantry = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_execEveryLoop_infantry.sqf",A3XAI_directory]; +A3XAI_execEveryLoop_vehicle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_execEveryLoop_vehicle.sqf",A3XAI_directory]; +A3XAI_execEveryLoop_ugv = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_execEveryLoop_ugv.sqf",A3XAI_directory]; +A3XAI_execEveryLoop_uav = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_execEveryLoop_uav.sqf",A3XAI_directory]; +A3XAI_checkGroupUnits = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkGroupUnits.sqf",A3XAI_directory]; +A3XAI_generateGroupLoot = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_generateGroupLoot.sqf",A3XAI_directory]; +A3XAI_generateVehicleLoot = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_generateVehicleLoot.sqf",A3XAI_directory]; +A3XAI_checkAmmoFuel = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_checkAmmoFuel.sqf",A3XAI_directory]; +A3XAI_antistuck_air = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_antistuck_air.sqf",A3XAI_directory]; +A3XAI_antistuck_aircustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_antistuck_aircustom.sqf",A3XAI_directory]; +A3XAI_antistuck_generic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_antistuck_generic.sqf",A3XAI_directory]; +A3XAI_antistuck_land = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_antistuck_land.sqf",A3XAI_directory]; +A3XAI_antistuck_uav = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_antistuck_uav.sqf",A3XAI_directory]; +A3XAI_antistuck_ugv = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_antistuck_ugv.sqf",A3XAI_directory]; +A3XAI_generateLootPool = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_generateLootPool.sqf",A3XAI_directory]; +A3XAI_generateLootOnDeath = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_generateLootOnDeath.sqf",A3XAI_directory]; +A3XAI_generateLoadout = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_generateLoadout.sqf",A3XAI_directory]; +A3XAI_addGroupManagerSingle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addGroupManagerSingle.sqf",A3XAI_directory]; +A3XAI_addGroupManangerGlobal = compileFinal preprocessFileLineNumbers format ["%1\compile\A3XAI_addGroupManangerGlobal.sqf",A3XAI_directory]; + +diag_log "[A3XAI] A3XAI functions compiled."; + +true diff --git a/A3XAI/init/A3XAI_initHC.sqf b/A3XAI/init/A3XAI_initHC.sqf new file mode 100644 index 0000000..0ca7389 --- /dev/null +++ b/A3XAI/init/A3XAI_initHC.sqf @@ -0,0 +1,147 @@ +#include "\A3XAI\globaldefines.hpp" + +if (hasInterface || isDedicated || !isNil "A3XAI_HC_isActive") exitWith {}; + +_startTime = diag_tickTime; + +A3XAI_HC_isActive = true; +A3XAI_directory = "A3XAI"; +A3XAI_HCPlayerLoggedIn = false; +A3XAI_HCGroupsCount = 0; +A3XAI_enableHC = true; +A3XAI_settingsReady = false; + +//Create reference marker to act as boundary for spawning AI air/land vehicles. +_worldName = (toLower worldName); +_markerInfo = call { + { + if (_worldName isEqualTo (_x select 0)) exitWith { + [_x select 1,_x select 2] + }; + } forEach [ + //worldName, center position, landmass radius + ["altis",[15834.2,15787.8,0],12000], + ["australia",[21966.2,22728.5,0],15000], + ["bootcamp_acr",[1938.24,1884.16,0],1800], + ["caribou",[3938.9722, 4195.7417],3500], + ["chernarus",[7652.9634, 7870.8076],5500], + ["chernarus_summer",[6669.88,9251.68,0],6000], + ["desert_e",[1034.26,1022.18,0],1000], + ["esseker",[6206.94,5920.05,0],5700], + ["fallujah",[5139.8008, 4092.6797],4000], + ["fdf_isle1_a",[10771.362, 8389.2568],2750], + ["intro",[2914.44,2771.61,0],900], + ["isladuala",[4945.3438, 4919.6616],4000], + ["lingor",[5166.5581, 5108.8301],4500], + ["mbg_celle2",[6163.52, 6220.3984],6000], + ["mountains_acr",[3223.09,3242.13,0],3100], + ["namalsk",[5880.1313, 8889.1045],3000], + ["napf",[10725.096, 9339.918],8500], + ["oring",[5191.1069, 5409.1938],4750], + ["panthera2",[5343.6953, 4366.2534],3500], + ["porto",[2641.45,2479.77,0],800], + ["sara",[12693.104, 11544.386],6250], + ["saralite",[5357.5,5000.67,0],3000], + ["sara_dbe1",[11995.3,11717.9,0],7000], + ["sauerland",[12270.443, 13632.132],17500], + ["smd_sahrani_a2",[12693.104, 11544.386],6250], + ["stratis",[3937.6,4774.51,0],3000], + ["takistan",[6368.2764, 6624.2744],6000], + ["tavi",[10887.825, 11084.657],8500], + ["trinity",[7183.8403, 7067.4727],5300], + ["utes",[3519.8037, 3703.0649],1000], + ["woodland_acr",[3884.41,3896.44,0],3700], + ["zargabad",[3917.6201, 3800.0376],2000], + [_worldname,getArray(configFile >> "CfgWorlds" >> worldName >> "centerPosition"),worldSize/2] + ]; +}; +_centerMarker = createMarkerLocal ["A3XAI_centerMarker",_markerInfo select 0]; +_centerMarker setMarkerSizeLocal [_markerInfo select 1,_markerInfo select 1]; + +_nul = [] spawn { + _versionKey = [configFile >> "CfgPatches" >> "A3XAI_HC","A3XAI_HCVersion","0"] call BIS_fnc_returnConfigEntry; + diag_log format ["[A3XAI] Initializing A3XAI HC build %1 using base path %2.",_versionKey,A3XAI_directory]; + + A3XAI_enableDebugMarkers = (([missionConfigFile >> "CfgDeveloperOptions","enableDebugMarkers",0] call BIS_fnc_returnConfigEntry) isEqualTo 1); + _readOverrideFile = (([missionConfigFile >> "CfgDeveloperOptions","readOverrideFile",0] call BIS_fnc_returnConfigEntry) isEqualTo 1); + _serverDir = [missionConfigFile >> "CfgDeveloperOptions","headlessClientDir","@A3XAI_HC"] call BIS_fnc_returnConfigEntry; + _useRemoteConfigs = (([missionConfigFile >> "CfgDeveloperOptions","useRemoteConfigs",1] call BIS_fnc_returnConfigEntry) isEqualTo 1); //Using local configs not currently supported. + _debugLevelHC = [missionConfigFile >> "CfgDeveloperOptions","useDebugLevel",0] call BIS_fnc_returnConfigEntry; + + if ((!_useRemoteConfigs) && {!isFilePatchingEnabled}) then {_useRemoteConfigs = true;}; + + diag_log "[A3XAI] Waiting for HC player object setup to be completed."; + + waitUntil {uiSleep 2; player == player}; + if !((typeOf player) isEqualTo "HeadlessClient_F") exitWith { + diag_log format ["A3XAI Error: Headless client assigned to wrong player slot. Player Type: %1. Expected: HeadlessClient_F",(typeOf player)]; + }; + + A3XAI_HCObject = player; + A3XAI_HCObjectGroup = (group player); + A3XAI_HCObject allowDamage false; + + diag_log "[A3XAI] Attempting to connect to A3XAI server..."; + A3XAI_HCLogin_PVS = [A3XAI_HCObject,_versionKey,_useRemoteConfigs]; + publicVariableServer "A3XAI_HCLogin_PVS"; + _loginStart = diag_tickTime; + waitUntil {uiSleep 1; ((!isNil "A3XAI_HC_serverResponse") or {(diag_tickTime - _loginStart) > 60})}; + + if (isNil "A3XAI_HC_serverResponse") exitWith { + diag_log "[A3XAI] Headless client connection timed out after 60 seconds of no response from server."; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: HC Object %1. Group %2",A3XAI_HCObject,A3XAI_HCObjectGroup];}; + {deleteVehicle _x} forEach (units A3XAI_HCObjectGroup); + deleteGroup A3XAI_HCObjectGroup; + endMission "END1"; + }; + + _isConnectionRejected = call { + _responseType = (typeName A3XAI_HC_serverResponse); + if (_responseType == "ARRAY") exitWith { + (A3XAI_HC_serverResponse isEqualTo []) + }; + if (_responseType == "BOOL") exitWith { + !(A3XAI_HC_serverResponse) + }; + true + }; + + if (_isConnectionRejected) exitWith { + diag_log "[A3XAI] Headless client connection unsuccessful. HC authorization request rejected (incorrect HC version?)."; + {deleteVehicle _x} forEach (units A3XAI_HCObjectGroup); + deleteGroup A3XAI_HCObjectGroup; + endMission "END1"; + }; + + _configCheck = if (_useRemoteConfigs) then { + call compile preprocessFileLineNumbers format ["%1\init\loadSettingsHC.sqf",A3XAI_directory] + } else { + //Not currently supported + }; + if (isNil "_configCheck") exitWith {diag_log "A3XAI Critical Error: Configuration file not successfully loaded. Stopping startup procedure.";}; + + if (_debugLevelHC > 0) then {A3XAI_debugLevel = _debugLevelHC;}; + if (_readOverrideFile && {isFilePatchingEnabled}) then {call compile preprocessFileLineNumbers format ["%1\A3XAI_settings_override.sqf",_serverDir];}; + + diag_log "[A3XAI] Headless client connection successful. HC authorization request granted."; + + //Load internal use variables + call compile preprocessFileLineNumbers format ["%1\init\variables.sqf",A3XAI_directory]; + + //Load A3XAI functions and A3XAI HC functions + diag_log "[A3XAI] Compiling functions..."; + _check = call compile preprocessFileLineNumbers "A3XAI\init\A3XAI_HCFunctions.sqf"; + if (isNil "_check") exitWith {diag_log "A3XAI Critical Error: HC functions not successfully loaded. Stopping startup procedure.";}; + _check = call compile preprocessFileLineNumbers "A3XAI\init\A3XAI_functions.sqf"; + if (isNil "_check") exitWith {diag_log "A3XAI Critical Error: Functions not successfully loaded. Stopping startup procedure.";}; + _check = call compile preprocessFileLineNumbers "A3XAI\init\A3XAI_HC_PVEH.sqf"; + if (isNil "_check") exitWith {diag_log "A3XAI Critical Error: PublicVariable EHs not successfully loaded. Stopping startup procedure.";}; + + diag_log format ["[A3XAI] A3XAI HC started with Debug Level: %1.",A3XAI_debugLevel]; + + //Build location list + _setupLocations = [] execVM format ['%1\scripts\setup_locations.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.5; scriptDone _setupLocations}; + + _serverMonitor = [] execVM format ['%1\compile\A3XAI_headlessclient\A3XAI_HCMonitor.sqf',A3XAI_directory]; +}; diff --git a/A3XAI/init/A3XAI_initserver.sqf b/A3XAI/init/A3XAI_initserver.sqf new file mode 100644 index 0000000..f8c2401 --- /dev/null +++ b/A3XAI/init/A3XAI_initserver.sqf @@ -0,0 +1,110 @@ +#include "\A3XAI\globaldefines.hpp" + +/* + A3XAI Server Initialization File + + Description: Handles startup process for A3XAI. Does not contain any values intended for modification. +*/ + +if (hasInterface || !isDedicated ||!isNil "A3XAI_isActive") exitWith {}; + +_startTime = diag_tickTime; + +A3XAI_isActive = true; + +private ["_startTime","_worldname","_allUnits","_configCheck","_functionsCheck","_readOverrideFile","_reportDirectoryName","_configVersion","_coreVersion","_compatibleVersions"]; + +A3XAI_directory = "A3XAI"; //PREFIX + +_coreVersion = [configFile >> "CfgPatches" >> "A3XAI","A3XAIVersion",""] call BIS_fnc_returnConfigEntry; +_configVersion = [configFile >> "CfgPatches" >> "A3XAI_config","A3XAIVersion",""] call BIS_fnc_returnConfigEntry; +_compatibleVersions = [configFile >> "CfgPatches" >> "A3XAI","compatibleConfigVersions",[]] call BIS_fnc_returnConfigEntry; +_serverDir = [missionConfigFile >> "CfgDeveloperOptions","serverDir","@A3XAI"] call BIS_fnc_returnConfigEntry; +_readOverrideFile = (([missionConfigFile >> "CfgDeveloperOptions","readOverrideFile",0] call BIS_fnc_returnConfigEntry) isEqualTo 1); +_reportDirectoryName = (([missionConfigFile >> "CfgDeveloperOptions","reportDirectoryName",0] call BIS_fnc_returnConfigEntry) isEqualTo 1); +A3XAI_enableDebugMarkers = (([missionConfigFile >> "CfgDeveloperOptions","enableDebugMarkers",0] call BIS_fnc_returnConfigEntry) isEqualTo 1); + +if (_reportDirectoryName) then { + diag_log format ["Debug: File is [%1]",__FILE__]; +}; + +if !(_configVersion in _compatibleVersions) exitWith { + diag_log format ["A3XAI Error: Incompatible A3XAI core and config pbo versions. Core: %1. Config: %2. Please update both A3XAI.pbo and A3XAI_config.pbo.",_coreVersion,_configVersion]; +}; + +//Report A3XAI version to RPT log +diag_log format ["[A3XAI] Initializing A3XAI version %1 using base path %2.",[configFile >> "CfgPatches" >> "A3XAI","A3XAIVersion","error - unknown version"] call BIS_fnc_returnConfigEntry,A3XAI_directory]; + +//Load A3XAI functions +_functionsCheck = call compile preprocessFileLineNumbers format ["%1\init\A3XAI_functions.sqf",A3XAI_directory]; +if (isNil "_functionsCheck") exitWith {diag_log "A3XAI Critical Error: Functions not successfully loaded. Stopping startup procedure.";}; + +//Load A3XAI settings +_configCheck = call compile preprocessFileLineNumbers format ["%1\init\loadSettings.sqf",A3XAI_directory]; +if (isNil "_configCheck") exitWith {diag_log "A3XAI Critical Error: Configuration file not successfully loaded. Stopping startup procedure.";}; + +//Load custom A3XAI settings file. +if ((_readOverrideFile) && {isFilePatchingEnabled}) then {call compile preprocessFileLineNumbers format ["%1\A3XAI_settings_override.sqf",_serverDir];}; + +//Create reference marker to act as boundary for spawning AI air/land vehicles. +_worldname = (toLower worldName); +_markerInfo = call { + { + if (_worldname isEqualTo (_x select 0)) exitWith { + [_x select 1,_x select 2] + }; + } forEach [ + //worldName, center position, landmass radius + ["altis",[15834.2,15787.8,0],12000], + ["australia",[21966.2,22728.5,0],15000], + ["bootcamp_acr",[1938.24,1884.16,0],1800], + ["caribou",[3938.9722, 4195.7417],3500], + ["chernarus",[7652.9634, 7870.8076],5500], + ["chernarus_summer",[6669.88,9251.68,0],6000], + ["desert_e",[1034.26,1022.18,0],1000], + ["esseker",[6206.94,5920.05,0],5700], + ["fallujah",[5139.8008, 4092.6797],4000], + ["fdf_isle1_a",[10771.362, 8389.2568],2750], + ["intro",[2914.44,2771.61,0],900], + ["isladuala",[4945.3438, 4919.6616],4000], + ["lingor",[5166.5581, 5108.8301],4500], + ["mbg_celle2",[6163.52, 6220.3984],6000], + ["mountains_acr",[3223.09,3242.13,0],3100], + ["namalsk",[5880.1313, 8889.1045],3000], + ["napf",[10725.096, 9339.918],8500], + ["oring",[5191.1069, 5409.1938],4750], + ["panthera2",[5343.6953, 4366.2534],3500], + ["porto",[2641.45,2479.77,0],800], + ["sara",[12693.104, 11544.386],6250], + ["saralite",[5357.5,5000.67,0],3000], + ["sara_dbe1",[11995.3,11717.9,0],7000], + ["sauerland",[12270.443, 13632.132],17500], + ["smd_sahrani_a2",[12693.104, 11544.386],6250], + ["stratis",[3937.6,4774.51,0],3000], + ["takistan",[6368.2764, 6624.2744],6000], + ["tavi",[10887.825, 11084.657],8500], + ["trinity",[7183.8403, 7067.4727],5300], + ["utes",[3519.8037, 3703.0649],1000], + ["woodland_acr",[3884.41,3896.44,0],3700], + ["zargabad",[3917.6201, 3800.0376],2000], + [_worldname,getArray(configFile >> "CfgWorlds" >> worldName >> "centerPosition"),worldSize/2] + ]; +}; +_centerMarker = createMarkerLocal ["A3XAI_centerMarker",_markerInfo select 0]; +_centerMarker setMarkerSizeLocal [_markerInfo select 1,_markerInfo select 1]; + +//Set side relations only if needed +_allUnits = +allUnits; +if (({if ((side _x) isEqualTo PLAYER_GROUP_SIDE) exitWith {1}} count _allUnits) isEqualTo 0) then {createCenter PLAYER_GROUP_SIDE}; +if (({if ((side _x) isEqualTo A3XAI_side) exitWith {1}} count _allUnits) isEqualTo 0) then {createCenter A3XAI_side}; +if !((PLAYER_GROUP_SIDE getFriend A3XAI_side) isEqualTo 0) then {PLAYER_GROUP_SIDE setFriend [A3XAI_side, 0]}; +if !((A3XAI_side getFriend PLAYER_GROUP_SIDE) isEqualTo 0) then {A3XAI_side setFriend [PLAYER_GROUP_SIDE, 0]}; +if !((A3XAI_side getFriend A3XAI_side) isEqualTo 1) then {A3XAI_side setFriend [A3XAI_side, 1]}; + +//Continue loading required A3XAI script files +[] execVM format ['%1\init\A3XAI_post_init.sqf',A3XAI_directory]; + +//Report A3XAI startup settings to RPT log +diag_log format ["[A3XAI] A3XAI settings: Debug Level: %1. WorldName: %2. VerifyClassnames: %3. VerifySettings: %4.",A3XAI_debugLevel,_worldname,A3XAI_verifyClassnames,A3XAI_verifySettings]; +diag_log format ["[A3XAI] AI spawn settings: Static: %1. Dynamic: %2. Random: %3. Air: %4. Land: %5. UAV: %6. UGV: %7.",A3XAI_enableStaticSpawns,!(A3XAI_maxDynamicSpawns isEqualTo 0),!(A3XAI_maxRandomSpawns isEqualTo 0),!(A3XAI_maxAirPatrols isEqualTo 0),!(A3XAI_maxLandPatrols isEqualTo 0),!(A3XAI_maxUAVPatrols isEqualTo 0),!(A3XAI_maxUGVPatrols isEqualTo 0)]; +diag_log format ["[A3XAI] A3XAI loading completed in %1 seconds.",(diag_tickTime - _startTime)]; diff --git a/A3XAI/init/A3XAI_post_init.sqf b/A3XAI/init/A3XAI_post_init.sqf new file mode 100644 index 0000000..69232cf --- /dev/null +++ b/A3XAI/init/A3XAI_post_init.sqf @@ -0,0 +1,161 @@ +#include "\A3XAI\globaldefines.hpp" + +/* + A3XAI Startup + + Description: Handles post-initialization tasks + +*/ + +if (A3XAI_debugLevel > 0) then {diag_log "A3XAI Debug: A3XAI Startup is running required script files..."}; + +call compile preprocessFileLineNumbers format ["%1\init\variables.sqf",A3XAI_directory]; +call compile preprocessFileLineNumbers format ["%1\init\variables_precalculated.sqf",A3XAI_directory]; + +if (A3XAI_enableHC) then { + [] call compile preprocessFileLineNumbers format ["%1\init\A3XAI_ServerHC_functions.sqf",A3XAI_directory]; + [] call compile preprocessFileLineNumbers format ["%1\init\A3XAI_ServerHC_PVEH.sqf",A3XAI_directory]; + diag_log "[A3XAI] A3XAI is now listening for headless client connection."; +}; + +//Create default trigger object if AI is spawned without trigger object specified (ie: for custom vehicle AI groups) +_nul = [] spawn { + A3XAI_defaultTrigger = TRIGGER_OBJECT createVehicleLocal ([configFile >> "CfgWorlds" >> worldName,"centerPosition",[0,0,0]] call BIS_fnc_returnConfigEntry); + A3XAI_defaultTrigger enableSimulation false; + A3XAI_defaultTrigger setVariable ["isCleaning",true]; + A3XAI_defaultTrigger setVariable ["patrolDist",100]; + A3XAI_defaultTrigger setVariable ["unitLevel",1]; + A3XAI_defaultTrigger setVariable ["unitLevelEffective",1]; + A3XAI_defaultTrigger setVariable ["locationArray",[]]; + A3XAI_defaultTrigger setVariable ["maxUnits",[0,0]]; + A3XAI_defaultTrigger setVariable ["GroupSize",0]; + A3XAI_defaultTrigger setVariable ["initialized",true]; + A3XAI_defaultTrigger setVariable ["spawnChance",0]; + A3XAI_defaultTrigger setVariable ["spawnType",""]; + A3XAI_defaultTrigger setVariable ["TriggerText","Default Trigger Object"]; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Default trigger check result: %1",[!(isNull A3XAI_defaultTrigger),(typeOf A3XAI_defaultTrigger),(getPosASL A3XAI_defaultTrigger)]]}; +}; + +[ + //Input variable - Gradechances array, Output variable - Gradeindices array + ["A3XAI_levelChancesAir","A3XAI_levelIndicesAir"], + ["A3XAI_levelChancesLand","A3XAI_levelIndicesLand"], + ["A3XAI_levelChancesUAV","A3XAI_levelIndicesUAV"], + ["A3XAI_levelChancesUGV","A3XAI_levelIndicesUGV"], + ["A3XAI_useWeaponChance0","A3XAI_weaponTypeIndices0"], + ["A3XAI_useWeaponChance1","A3XAI_weaponTypeIndices1"], + ["A3XAI_useWeaponChance2","A3XAI_weaponTypeIndices2"], + ["A3XAI_useWeaponChance3","A3XAI_weaponTypeIndices3"] +] call compile preprocessFileLineNumbers format ["%1\scripts\buildWeightedTables.sqf",A3XAI_directory]; + +if (A3XAI_verifyClassnames) then { + A3XAI_tableChecklist = ["A3XAI_pistolList0","A3XAI_rifleList0","A3XAI_machinegunList0","A3XAI_sniperList0","A3XAI_pistolList1","A3XAI_rifleList1","A3XAI_machinegunList1","A3XAI_sniperList1", + "A3XAI_pistolList2","A3XAI_rifleList2","A3XAI_machinegunList2","A3XAI_sniperList2","A3XAI_headgearTypes0","A3XAI_headgearTypes1","A3XAI_headgearTypes2","A3XAI_headgearTypes3", + "A3XAI_pistolList3","A3XAI_rifleList3","A3XAI_machinegunList3","A3XAI_sniperList3","A3XAI_backpackTypes0","A3XAI_backpackTypes1","A3XAI_backpackTypes2","A3XAI_backpackTypes3", + "A3XAI_pistolList","A3XAI_rifleList","A3XAI_machinegunList","A3XAI_sniperList","A3XAI_foodLoot","A3XAI_MiscLoot1","A3XAI_MiscLoot2","A3XAI_airReinforcementVehicles","A3XAI_uniformTypes0", + "A3XAI_uniformTypes1","A3XAI_uniformTypes2","A3XAI_uniformTypes3","A3XAI_launcherTypes", + "A3XAI_vestTypes0","A3XAI_vestTypes1","A3XAI_vestTypes2","A3XAI_vestTypes3"]; +}; + + +if (A3XAI_generateDynamicUniforms) then { + _skinlist = [] execVM format ['%1\scripts\A3XAI_buildUniformList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _skinlist}; +}; + +//Build weapon classname tables +if (A3XAI_generateDynamicWeapons) then { + _weaponlist = [] execVM format ['%1\scripts\A3XAI_buildWeaponList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _weaponlist}; +}; + +//Build backpack classname tables +if (A3XAI_generateDynamicBackpacks) then { + _backpacklist = [] execVM format ['%1\scripts\A3XAI_buildBackpackList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _backpacklist}; +}; + +//Build vest classname tables +if (A3XAI_generateDynamicVests) then { + _vestlist = [] execVM format ['%1\scripts\A3XAI_buildVestList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _vestlist}; +}; + +//Build headgear classname tables +if (A3XAI_generateDynamicHeadgear) then { + _headgearlist = [] execVM format ['%1\scripts\A3XAI_buildHeadgearList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _headgearlist}; +}; + +//Build food classname tables (1) +if (A3XAI_generateDynamicFood) then { + _foodlist = [] execVM format ['%1\scripts\A3XAI_buildFoodList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _foodlist}; +}; + +//Build generic loot classname tables (1) +if (A3XAI_generateDynamicLoot) then { + _lootlist = [] execVM format ['%1\scripts\A3XAI_buildLootList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _lootlist}; +}; + +if (A3XAI_generateDynamicMedical) then { + _lootlist = [] execVM format ['%1\scripts\A3XAI_buildMedicalList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _lootlist}; +}; + +if (A3XAI_generateDynamicOptics) then { + _weaponScopes = [] execVM format ['%1\scripts\A3XAI_buildScopeList.sqf',A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _weaponScopes}; +}; + +//Check classname tables if enabled +if (A3XAI_verifyClassnames) then { + _verifyClassnames = [] execVM format ["%1\scripts\verifyClassnames.sqf",A3XAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _verifyClassnames}; +}; + +if (A3XAI_enableHC && {A3XAI_waitForHC}) then { + diag_log "[A3XAI] Waiting for headless client to connect. A3XAI post-initialization process paused."; + waitUntil {uiSleep 5; A3XAI_HCIsConnected}; + diag_log format ["[A3XAI] Headless client connected with owner ID %1. A3XAI post-initialization process continuing.",A3XAI_HCObjectOwnerID]; +}; + +A3XAI_classnamesVerified = true; + +//Build map location list. +_setupLocations = [] execVM format ['%1\scripts\setup_locations.sqf',A3XAI_directory]; + +//Set up auto-generated static spawns +if (A3XAI_enableStaticSpawns) then { + if (A3XAI_useManualStaticSpawns && {!(A3XAI_manualStaticSpawnLocations isEqualTo [])}) then { + _staticSpawns = [] execVM format ["%1\scripts\A3XAI_generateStaticSpawnsManual.sqf",A3XAI_directory]; //sqf to be created + } else { + _staticSpawns = [] execVM format ["%1\scripts\A3XAI_generateStaticSpawnsAuto.sqf",A3XAI_directory]; + }; +}; + +//Start dynamic spawn manager +if !(A3XAI_maxDynamicSpawns isEqualTo 0) then { + _dynManagerV2 = [] execVM format ['%1\scripts\dynamicSpawn_manager.sqf',A3XAI_directory]; +}; + +//Start universal spawn manager +_spawnManager = [] execVM format ['%1\scripts\A3XAI_spawnManagerUniversal.sqf',A3XAI_directory]; + +//Set up vehicle patrols +if ((A3XAI_maxAirPatrols > 0) or {(A3XAI_maxLandPatrols > 0)}) then { + _vehicles = [] execVM format ['%1\scripts\setup_veh_patrols.sqf',A3XAI_directory]; +}; + +//Load custom definitions file +if (A3XAI_loadCustomFile) then { + if (isClass (configFile >> "CfgA3XAISettings")) then { + _customLoader = [] execVM format ["%1\init\A3XAI_custom_loader.sqf",A3XAI_directory]; //0.1.8 + } else { + diag_log "A3XAI Error: Could not load A3XAI_config.pbo. Unable to load custom definitions."; + }; +}; + +//Load A3XAI server monitor +_serverMonitor = [] execVM format ['%1\scripts\A3XAI_serverMonitor.sqf',A3XAI_directory]; diff --git a/A3XAI/init/client/fn_postinit.sqf b/A3XAI/init/client/fn_postinit.sqf new file mode 100644 index 0000000..1a24655 --- /dev/null +++ b/A3XAI/init/client/fn_postinit.sqf @@ -0,0 +1,15 @@ +/** + * Exile Mod + * www.exilemod.com + * © 2015 Exile Mod Team + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + */ + +if (!hasInterface && {!isDedicated}) exitWith {diag_log format ["A3XAI blocked execution of %1.",__FILE__];}; + +call compile preprocessFileLineNumbers "exile_client\bootstrap\fn_postInit.sqf"; +//diag_log "Debug: Executing Exile client post init."; + +true \ No newline at end of file diff --git a/A3XAI/init/client/fn_preInit.sqf b/A3XAI/init/client/fn_preInit.sqf new file mode 100644 index 0000000..74ab744 --- /dev/null +++ b/A3XAI/init/client/fn_preInit.sqf @@ -0,0 +1,15 @@ +/** + * Exile Mod + * www.exilemod.com + * © 2015 Exile Mod Team + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + */ + +if (!hasInterface && {!isDedicated}) exitWith {diag_log format ["A3XAI blocked execution of %1.",__FILE__];}; + +call compile preprocessFileLineNumbers "exile_client\bootstrap\fn_preInit.sqf"; +//diag_log "Debug: Executing Exile client post init."; + +true \ No newline at end of file diff --git a/A3XAI/init/client/fn_preStart.sqf b/A3XAI/init/client/fn_preStart.sqf new file mode 100644 index 0000000..a9baa9c --- /dev/null +++ b/A3XAI/init/client/fn_preStart.sqf @@ -0,0 +1,2 @@ + +true \ No newline at end of file diff --git a/A3XAI/init/loadSettings.sqf b/A3XAI/init/loadSettings.sqf new file mode 100644 index 0000000..38d118e --- /dev/null +++ b/A3XAI/init/loadSettings.sqf @@ -0,0 +1,410 @@ +_startTime = diag_tickTime; + +A3XAI_pushedHCVariables = []; + +_fnc_getConfigValue = { + private ["_variableName", "_defaultValue", "_configValue", "_returnValue", "_type", "_string"]; + + _variableName = _x select 0; + _defaultValue = _x select 1; + + _returnValue = _defaultValue; + if (isClass (configFile >> "CfgA3XAISettings")) then { + _configValue = configFile >> "CfgA3XAISettings" >> _variableName; + _returnValue = call { + _type = (typeName _defaultValue); + + if (_type isEqualTo "SCALAR") exitWith { + if (isNumber _configValue) then { + getNumber _configValue + } else { + diag_log format ["[A3XAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + }; + + if ((_type isEqualTo "BOOL") && {isNumber _configValue}) exitWith { + if (isNumber _configValue) then { + (getNumber _configValue) isEqualTo 1 + } else { + diag_log format ["[A3XAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + }; + + if ((_type isEqualTo "ARRAY") && {isArray _configValue}) exitWith { + if (isArray _configValue) then { + getArray _configValue + } else { + diag_log format ["[A3XAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + }; + + if ((_type isEqualTo "STRING") && {isText _configValue}) exitWith { + if (isText _configValue) then { + getText _configValue + } else { + diag_log format ["[A3XAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + }; + + if ((_type isEqualTo "SIDE") && {isText _configValue}) exitWith { + _string = getText _configValue; + call { + if (_string == "east") exitWith {east}; + if (_string == "west") exitWith {west}; + if (_string == "resistance") exitWith {resistance}; + if (_string == "civilian") exitWith {civilian}; + diag_log format ["[A3XAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + }; + + _defaultValue + }; + }; + _returnValue +}; + +{ + private ["_variableName", "_defaultValue", "_HCPushable", "_variableValue"]; + _variableName = _x select 0; + _defaultValue = _x select 1; + _HCPushable = [_x,2,false] call A3XAI_param; + + _variableValue = [_variableName,_defaultValue] call _fnc_getConfigValue; + missionNamespace setVariable [format ["A3XAI_%1",_variableName],_variableValue]; + diag_log format ["Debug: %1 = %2",_variableName,_variableValue]; + if (_HCPushable) then { + A3XAI_pushedHCVariables pushBack [_variableName,_variableValue]; + //diag_log format ["Debug: Found HC variable %1:%2",_variableName,_variableValue]; + }; +} forEach [ + ["debugLevel",0,true], + ["monitorReportRate",300,true], + ["verifyClassnames",true], + ["verifySettings",true], + ["cleanupDelay",900], + ["loadCustomFile",true], + ["minFPS",0], + ["groupManageMode",0], + ["enableHC",false,true], + ["waitForHC",false], + ["itemPriceLimit",2000], + ["generateDynamicWeapons",true], + ["dynamicWeaponBlacklist",[]], + ["generateDynamicOptics",true], + ["dynamicOpticsBlacklist",[]], + ["generateDynamicUniforms",true], + ["dynamicUniformBlacklist",[]], + ["generateDynamicBackpacks",true], + ["dynamicBackpackBlacklist",[]], + ["generateDynamicVests",true], + ["dynamicVestBlacklist",[]], + ["generateDynamicHeadgear",true], + ["dynamicHeadgearBlacklist",[]], + ["generateDynamicFood",true], + ["dynamicFoodBlacklist",[]], + ["generateDynamicMedical",true], + ["dynamicMedicalBlacklist",[]], + ["generateDynamicLoot",true], + ["dynamicLootBlacklist",[]], + ["enableRadioMessages",false,true], + ["side",east], + ["playerCountThreshold",10], + ["upwardsChanceScaling",true], + ["chanceScalingThreshold",0.50], + ["minAI_village",1], + ["addAI_village",1], + ["unitLevel_village",0], + ["spawnChance_village",0.40], + ["minAI_city",1], + ["addAI_city",2], + ["unitLevel_city",1], + ["spawnChance_city",0.60], + ["minAI_capitalCity",2], + ["addAI_capitalCity",1], + ["unitLevel_capitalCity",1], + ["spawnChance_capitalCity",0.70], + ["minAI_remoteArea",1], + ["addAI_remoteArea",2], + ["unitLevel_remoteArea",2], + ["spawnChance_remoteArea",0.80], + ["minAI_wilderness",1], + ["addAI_wilderness",1], + ["unitLevel_wilderness",2], + ["spawnChance_wilderness",0.50], + ["tempBlacklistTime",1200], + ["enableDeathMessages",false,true], + ["enableFindKiller",true,true], + ["enableTempNVGs",false], + ["levelRequiredGL",2,true], + ["levelRequiredLauncher",-1,true], + ["launcherTypes",[],true], + ["launchersPerGroup",1,true], + ["enableHealing",true], + ["removeExplosiveAmmo",true], + ["noCollisionDamage",true,true], + ["roadKillPenalty",true,true], + ["traderAreaLocations",[],true], + ["enableStaticSpawns",true], + ["enableExtraStaticSpawns",true], + ["useManualStaticSpawns",false], + ["manualStaticSpawnLocations",[]], + ["respawnTimeMin",300], + ["respawnTimeMax",600], + ["despawnWait",60], + ["staticLockoutTime",300], + ["respawnLimit_village",-1], + ["respawnLimit_city",-1], + ["respawnLimit_capitalCity",-1], + ["respawnLimit_remoteArea",-1], + ["maxDynamicSpawns",15], + ["timePerDynamicSpawn",900], + ["purgeLastDynamicSpawnTime",3600], + ["spawnHunterChance",0.60], + ["despawnDynamicSpawnTime",60], + ["maxRandomSpawns",-1], + ["despawnRandomSpawnTime",60], + ["distanceBetweenRandomSpawns",600], + ["vehicleDespawnTime",600], + ["vehiclesAllowedForPlayers",false], + ["waypointBlacklistAir",[],true], + ["waypointBlacklistLand",[],true], + ["maxAirPatrols",0], + ["levelChancesAir",[0.00,0.50,0.35,0.15]], + ["respawnAirMinTime",600], + ["respawnAirMaxTime",900], + ["airVehicleList",[ + ["B_Heli_Light_01_armed_F",5], + ["B_Heli_Attack_01_F",2] + ]], + ["airGunnerUnits",2], + ["airDetectChance",0.80,true], + ["paradropChance",0.50,true], + ["paradropCooldown",1800,true], + ["paradropAmount",3,true], + ["maxLandPatrols",0], + ["levelChancesLand",[0.00,0.50,0.35,0.15]], + ["respawnLandMinTime",600], + ["respawnLandMaxTime",900], + ["landVehicleList",[ + ["Exile_Car_Hatchback_Rusty1",5], + ["Exile_Car_Hatchback_Rusty2",5], + ["Exile_Car_Hatchback_Rusty3",5], + ["Exile_Car_Hatchback_Sport_Red",5], + ["Exile_Car_SUV_Red",5], + ["Exile_Car_Offroad_Rusty1",5], + ["Exile_Car_Offroad_Rusty2",5], + ["Exile_Car_Offroad_Rusty3",5], + ["Exile_Car_Offroad_Repair_Civillian",5], + ["Exile_Car_Offroad_Armed_Guerilla01",5], + ["Exile_Car_Strider",5], + ["Exile_Car_Hunter",5], + ["Exile_Car_Ifrit",5], + ["Exile_Car_Van_Black",5], + ["Exile_Car_Van_Box_Black",5], + ["Exile_Car_Van_Fuel_Black",5], + ["Exile_Car_Zamak",5], + ["Exile_Car_Tempest",5], + ["Exile_Car_HEMMT",5] + ]], + ["landGunnerUnits",2], + ["landCargoUnits",3], + ["maxAirReinforcements",5], + ["airReinforcementVehicles",[ + "B_Heli_Transport_01_F", + "B_Heli_Light_01_armed_F" + ]], + ["airReinforcementSpawnChance0",0.00], + ["airReinforcementSpawnChance1",0.10], + ["airReinforcementSpawnChance2",0.20], + ["airReinforcementSpawnChance3",0.30], + ["airReinforcementAllowedFor",["static","dynamic","random"]], + ["airReinforceDeployChance0",0.60], + ["airReinforceDeployChance1",0.70], + ["airReinforceDeployChance2",0.80], + ["airReinforceDeployChance3",0.90], + ["airReinforcementDuration0",120], + ["airReinforcementDuration1",180], + ["airReinforcementDuration2",240], + ["airReinforcementDuration3",300], + ["maxUAVPatrols",0], + ["UAVList",[ + ["I_UAV_02_CAS_F",5], + ["I_UAV_02_F",5], + ["B_UAV_02_CAS_F",5], + ["B_UAV_02_F",5], + ["O_UAV_02_CAS_F",5], + ["O_UAV_02_F",5] + ]], + ["levelChancesUAV",[0.35,0.50,0.15,0.00]], + ["respawnUAVMinTime",600], + ["respawnUAVMaxTime",900], + ["UAVCallReinforceCooldown",1800,true], + ["UAVDetectChance",0.80,true], + ["maxUGVPatrols",0], + ["UGVList",[ + ["I_UGV_01_rcws_F",5], + ["B_UGV_01_rcws_F",5], + ["O_UGV_01_rcws_F",5] + ]], + ["levelChancesUGV",[0.35,0.50,0.15,0.00]], + ["respawnUGVMinTime",600], + ["respawnUGVMaxTime",900], + ["UGVCallReinforceCooldown",1800,true], + ["UGVDetectChance",0.80,true], + ["skill0",[ + ["aimingAccuracy",0.05,0.10], + ["aimingShake",0.30,0.50], + ["aimingSpeed",0.30,0.50], + ["spotDistance",0.30,0.50], + ["spotTime",0.30,0.50], + ["courage",0.30,0.50], + ["reloadSpeed",0.30,0.50], + ["commanding",0.30,0.50], + ["general",0.30,0.50] + ]], + ["skill1",[ + ["aimingAccuracy",0.05,0.10], + ["aimingShake",0.40,0.60], + ["aimingSpeed",0.40,0.60], + ["spotDistance",0.40,0.60], + ["spotTime",0.40,0.60], + ["courage",0.40,0.60], + ["reloadSpeed",0.40,0.60], + ["commanding",0.40,0.60], + ["general",0.40,0.60] + ]], + ["skill2",[ + ["aimingAccuracy",0.15,0.20], + ["aimingShake",0.50,0.70], + ["aimingSpeed",0.50,0.70], + ["spotDistance",0.50,0.70], + ["spotTime",0.50,0.70], + ["courage",0.50,0.70], + ["reloadSpeed",0.50,0.70], + ["commanding",0.50,0.70], + ["general",0.50,0.70] + ]], + ["skill3",[ + ["aimingAccuracy",0.20,0.25], + ["aimingShake",0.60,0.80], + ["aimingSpeed",0.60,0.80], + ["spotDistance",0.60,0.80], + ["spotTime",0.60,0.80], + ["courage",0.60,0.80], + ["reloadSpeed",0.60,0.80], + ["commanding",0.60,0.80], + ["general",0.60,0.80] + ]], + ["addBackpackChance0",0.60], + ["addBackpackChance1",0.70], + ["addBackpackChance2",0.80], + ["addBackpackChance3",0.90], + ["addVestChance0",0.60], + ["addVestChance1",0.70], + ["addVestChance2",0.80], + ["addVestChance3",0.90], + ["addHeadgearChance0",0.60], + ["addHeadgearChance1",0.70], + ["addHeadgearChance2",0.80], + ["addHeadgearChance3",0.90], + ["useWeaponChance0",[0.20,0.80,0.00,0.00]], + ["useWeaponChance1",[0.00,0.90,0.05,0.05]], + ["useWeaponChance2",[0.00,0.80,0.10,0.10]], + ["useWeaponChance3",[0.00,0.70,0.15,0.15]], + ["opticsChance0",0.00], + ["opticsChance1",0.30], + ["opticsChance2",0.60], + ["opticsChance3",0.90], + ["pointerChance0",0.00], + ["pointerChance1",0.30], + ["pointerChance2",0.60], + ["pointerChance3",0.90], + ["muzzleChance0",0.00], + ["muzzleChance1",0.30], + ["muzzleChance2",0.60], + ["muzzleChance3",0.90], + ["underbarrelChance0",0.00], + ["underbarrelChance1",0.30], + ["underbarrelChance2",0.60], + ["underbarrelChance3",0.90], + ["foodLootCount",2], + ["miscLootCount",2], + ["medicalLootCount",1], + ["weaponLootVehicleCount",6], + ["ammoLootPerWeapon",3], + ["foodLootVehicleCount",6], + ["miscLootVehicleCount",6], + ["medicalLootVehicleCount",3], + ["lootPullChance0",0.20,true], + ["lootPullChance1",0.40,true], + ["lootPullChance2",0.60,true], + ["lootPullChance3",0.80,true], + ["enableRespectRewards",true], + ["respectHumiliation",300], + ["respectFragged",100], + ["respectChute",600], + ["respectBigBird",600], + ["respectRoadkill",500], + ["respectLetItRain",150], + ["respectKillstreak",50], + ["respectPer100m",10], + ["uniformTypes0",["U_C_Journalist","U_C_Poloshirt_blue","U_C_Poloshirt_burgundy","U_C_Poloshirt_salmon","U_C_Poloshirt_stripped","U_C_Poloshirt_tricolour","U_C_Poor_1","U_C_Poor_2","U_C_Poor_shorts_1","U_C_Scientist","U_OrestesBody","U_Rangemaster","U_NikosAgedBody","U_NikosBody","U_Competitor","U_B_CombatUniform_mcam","U_B_CombatUniform_mcam_tshirt","U_B_CombatUniform_mcam_vest","U_B_CombatUniform_mcam_worn","U_B_CTRG_1","U_B_CTRG_2","U_B_CTRG_3","U_I_CombatUniform","U_I_CombatUniform_shortsleeve","U_I_CombatUniform_tshirt","U_I_OfficerUniform","U_O_CombatUniform_ocamo","U_O_CombatUniform_oucamo","U_O_OfficerUniform_ocamo","U_B_SpecopsUniform_sgg","U_O_SpecopsUniform_blk","U_O_SpecopsUniform_ocamo","U_I_G_Story_Protagonist_F","U_C_HunterBody_grn","U_IG_Guerilla1_1","U_IG_Guerilla2_1","U_IG_Guerilla2_2","U_IG_Guerilla2_3","U_IG_Guerilla3_1","U_BG_Guerilla2_1","U_IG_Guerilla3_2","U_BG_Guerrilla_6_1","U_BG_Guerilla1_1","U_BG_Guerilla2_2","U_BG_Guerilla2_3","U_BG_Guerilla3_1"]], + ["uniformTypes1",["U_C_Journalist","U_C_Poloshirt_blue","U_C_Poloshirt_burgundy","U_C_Poloshirt_salmon","U_C_Poloshirt_stripped","U_C_Poloshirt_tricolour","U_C_Poor_1","U_C_Poor_2","U_C_Poor_shorts_1","U_C_Scientist","U_OrestesBody","U_Rangemaster","U_NikosAgedBody","U_NikosBody","U_Competitor","U_B_CombatUniform_mcam","U_B_CombatUniform_mcam_tshirt","U_B_CombatUniform_mcam_vest","U_B_CombatUniform_mcam_worn","U_B_CTRG_1","U_B_CTRG_2","U_B_CTRG_3","U_I_CombatUniform","U_I_CombatUniform_shortsleeve","U_I_CombatUniform_tshirt","U_I_OfficerUniform","U_O_CombatUniform_ocamo","U_O_CombatUniform_oucamo","U_O_OfficerUniform_ocamo","U_B_SpecopsUniform_sgg","U_O_SpecopsUniform_blk","U_O_SpecopsUniform_ocamo","U_I_G_Story_Protagonist_F","U_C_HunterBody_grn","U_IG_Guerilla1_1","U_IG_Guerilla2_1","U_IG_Guerilla2_2","U_IG_Guerilla2_3","U_IG_Guerilla3_1","U_BG_Guerilla2_1","U_IG_Guerilla3_2","U_BG_Guerrilla_6_1","U_BG_Guerilla1_1","U_BG_Guerilla2_2","U_BG_Guerilla2_3","U_BG_Guerilla3_1"]], + ["uniformTypes2",["U_C_Journalist","U_C_Poloshirt_blue","U_C_Poloshirt_burgundy","U_C_Poloshirt_salmon","U_C_Poloshirt_stripped","U_C_Poloshirt_tricolour","U_C_Poor_1","U_C_Poor_2","U_C_Poor_shorts_1","U_C_Scientist","U_OrestesBody","U_Rangemaster","U_NikosAgedBody","U_NikosBody","U_Competitor","U_B_CombatUniform_mcam","U_B_CombatUniform_mcam_tshirt","U_B_CombatUniform_mcam_vest","U_B_CombatUniform_mcam_worn","U_B_CTRG_1","U_B_CTRG_2","U_B_CTRG_3","U_I_CombatUniform","U_I_CombatUniform_shortsleeve","U_I_CombatUniform_tshirt","U_I_OfficerUniform","U_O_CombatUniform_ocamo","U_O_CombatUniform_oucamo","U_O_OfficerUniform_ocamo","U_B_SpecopsUniform_sgg","U_O_SpecopsUniform_blk","U_O_SpecopsUniform_ocamo","U_I_G_Story_Protagonist_F","U_C_HunterBody_grn","U_IG_Guerilla1_1","U_IG_Guerilla2_1","U_IG_Guerilla2_2","U_IG_Guerilla2_3","U_IG_Guerilla3_1","U_BG_Guerilla2_1","U_IG_Guerilla3_2","U_BG_Guerrilla_6_1","U_BG_Guerilla1_1","U_BG_Guerilla2_2","U_BG_Guerilla2_3","U_BG_Guerilla3_1"]], + ["uniformTypes3",["U_C_Journalist","U_C_Poloshirt_blue","U_C_Poloshirt_burgundy","U_C_Poloshirt_salmon","U_C_Poloshirt_stripped","U_C_Poloshirt_tricolour","U_C_Poor_1","U_C_Poor_2","U_C_Poor_shorts_1","U_C_Scientist","U_OrestesBody","U_Rangemaster","U_NikosAgedBody","U_NikosBody","U_Competitor","U_B_CombatUniform_mcam","U_B_CombatUniform_mcam_tshirt","U_B_CombatUniform_mcam_vest","U_B_CombatUniform_mcam_worn","U_B_CTRG_1","U_B_CTRG_2","U_B_CTRG_3","U_I_CombatUniform","U_I_CombatUniform_shortsleeve","U_I_CombatUniform_tshirt","U_I_OfficerUniform","U_O_CombatUniform_ocamo","U_O_CombatUniform_oucamo","U_O_OfficerUniform_ocamo","U_B_SpecopsUniform_sgg","U_O_SpecopsUniform_blk","U_O_SpecopsUniform_ocamo","U_I_G_Story_Protagonist_F","U_C_HunterBody_grn","U_IG_Guerilla1_1","U_IG_Guerilla2_1","U_IG_Guerilla2_2","U_IG_Guerilla2_3","U_IG_Guerilla3_1","U_BG_Guerilla2_1","U_IG_Guerilla3_2","U_BG_Guerrilla_6_1","U_BG_Guerilla1_1","U_BG_Guerilla2_2","U_BG_Guerilla2_3","U_BG_Guerilla3_1"]], + ["pistolList",["hgun_ACPC2_F","hgun_P07_F","hgun_Pistol_heavy_01_F","hgun_Pistol_heavy_02_F","hgun_Rook40_F"]], + ["rifleList",["arifle_Katiba_C_F","arifle_Katiba_F","arifle_Katiba_GL_F","arifle_Mk20_F","arifle_Mk20_GL_F","arifle_Mk20_GL_plain_F","arifle_Mk20_plain_F","arifle_Mk20C_F","arifle_Mk20C_plain_F","arifle_MX_Black_F","arifle_MX_F","arifle_MX_GL_Black_F","arifle_MX_GL_F","arifle_MXC_Black_F","arifle_MXC_F","arifle_SDAR_F","arifle_TRG20_F","arifle_TRG21_F","arifle_TRG21_GL_F"]], + ["machinegunList",["arifle_MX_SW_Black_F","arifle_MX_SW_F","LMG_Mk200_F","LMG_Zafir_F","MMG_01_hex_F","MMG_01_tan_F","MMG_02_black_F","MMG_02_camo_F","MMG_02_sand_F"]], + ["sniperList",["arifle_MXM_Black_F","arifle_MXM_F","srifle_DMR_01_F","srifle_DMR_02_camo_F","srifle_DMR_02_F","srifle_DMR_02_sniper_F","srifle_DMR_03_F","srifle_DMR_03_khaki_F","srifle_DMR_03_multicam_F","srifle_DMR_03_tan_F","srifle_DMR_03_woodland_F","srifle_DMR_04_F","srifle_DMR_04_Tan_F","srifle_DMR_05_blk_F","srifle_DMR_05_hex_F","srifle_DMR_05_tan_f","srifle_DMR_06_camo_F","srifle_DMR_06_olive_F","srifle_EBR_F","srifle_GM6_camo_F","srifle_GM6_F","srifle_LRR_camo_F","srifle_LRR_F"]], + ["weaponOpticsList",["optic_NVS","optic_SOS","optic_LRPS","optic_AMS","optic_AMS_khk","optic_AMS_snd","optic_KHS_blk","optic_KHS_hex","optic_KHS_old","optic_KHS_tan","optic_DMS","optic_Arco","optic_Hamr","Elcan_Exile","Elcan_reflex_Exile","optic_MRCO","optic_Holosight","optic_Holosight_smg","optic_Aco","optic_ACO_grn","optic_Aco_smg","optic_ACO_grn_smg","optic_Yorris","optic_MRD"]], + ["backpackTypes0",["B_AssaultPack_blk","B_AssaultPack_cbr","B_AssaultPack_dgtl","B_AssaultPack_khk","B_AssaultPack_mcamo","B_AssaultPack_rgr","B_AssaultPack_sgg","B_Bergen_blk","B_Bergen_mcamo","B_Bergen_rgr","B_Bergen_sgg","B_Carryall_cbr","B_Carryall_khk","B_Carryall_mcamo","B_Carryall_ocamo","B_Carryall_oli","B_Carryall_oucamo","B_FieldPack_blk","B_FieldPack_cbr","B_FieldPack_ocamo","B_FieldPack_oucamo","B_HuntingBackpack","B_Kitbag_cbr","B_Kitbag_mcamo","B_Kitbag_sgg","B_OutdoorPack_blk","B_OutdoorPack_blu","B_OutdoorPack_tan","B_TacticalPack_blk","B_TacticalPack_mcamo","B_TacticalPack_ocamo","B_TacticalPack_oli","B_TacticalPack_rgr"]], + ["backpackTypes1",["B_AssaultPack_blk","B_AssaultPack_cbr","B_AssaultPack_dgtl","B_AssaultPack_khk","B_AssaultPack_mcamo","B_AssaultPack_rgr","B_AssaultPack_sgg","B_Bergen_blk","B_Bergen_mcamo","B_Bergen_rgr","B_Bergen_sgg","B_Carryall_cbr","B_Carryall_khk","B_Carryall_mcamo","B_Carryall_ocamo","B_Carryall_oli","B_Carryall_oucamo","B_FieldPack_blk","B_FieldPack_cbr","B_FieldPack_ocamo","B_FieldPack_oucamo","B_HuntingBackpack","B_Kitbag_cbr","B_Kitbag_mcamo","B_Kitbag_sgg","B_OutdoorPack_blk","B_OutdoorPack_blu","B_OutdoorPack_tan","B_TacticalPack_blk","B_TacticalPack_mcamo","B_TacticalPack_ocamo","B_TacticalPack_oli","B_TacticalPack_rgr"]], + ["backpackTypes2",["B_AssaultPack_blk","B_AssaultPack_cbr","B_AssaultPack_dgtl","B_AssaultPack_khk","B_AssaultPack_mcamo","B_AssaultPack_rgr","B_AssaultPack_sgg","B_Bergen_blk","B_Bergen_mcamo","B_Bergen_rgr","B_Bergen_sgg","B_Carryall_cbr","B_Carryall_khk","B_Carryall_mcamo","B_Carryall_ocamo","B_Carryall_oli","B_Carryall_oucamo","B_FieldPack_blk","B_FieldPack_cbr","B_FieldPack_ocamo","B_FieldPack_oucamo","B_HuntingBackpack","B_Kitbag_cbr","B_Kitbag_mcamo","B_Kitbag_sgg","B_OutdoorPack_blk","B_OutdoorPack_blu","B_OutdoorPack_tan","B_TacticalPack_blk","B_TacticalPack_mcamo","B_TacticalPack_ocamo","B_TacticalPack_oli","B_TacticalPack_rgr"]], + ["backpackTypes3",["B_AssaultPack_blk","B_AssaultPack_cbr","B_AssaultPack_dgtl","B_AssaultPack_khk","B_AssaultPack_mcamo","B_AssaultPack_rgr","B_AssaultPack_sgg","B_Bergen_blk","B_Bergen_mcamo","B_Bergen_rgr","B_Bergen_sgg","B_Carryall_cbr","B_Carryall_khk","B_Carryall_mcamo","B_Carryall_ocamo","B_Carryall_oli","B_Carryall_oucamo","B_FieldPack_blk","B_FieldPack_cbr","B_FieldPack_ocamo","B_FieldPack_oucamo","B_HuntingBackpack","B_Kitbag_cbr","B_Kitbag_mcamo","B_Kitbag_sgg","B_OutdoorPack_blk","B_OutdoorPack_blu","B_OutdoorPack_tan","B_TacticalPack_blk","B_TacticalPack_mcamo","B_TacticalPack_ocamo","B_TacticalPack_oli","B_TacticalPack_rgr"]], + ["vestTypes0",["V_Press_F","V_Rangemaster_belt","V_TacVest_blk","V_TacVest_blk_POLICE","V_TacVest_brn","V_TacVest_camo","V_TacVest_khk","V_TacVest_oli","V_TacVestCamo_khk","V_TacVestIR_blk","V_I_G_resistanceLeader_F","V_BandollierB_blk","V_BandollierB_cbr","V_BandollierB_khk","V_BandollierB_oli","V_BandollierB_rgr","V_Chestrig_blk","V_Chestrig_khk","V_Chestrig_oli","V_Chestrig_rgr","V_HarnessO_brn","V_HarnessO_gry","V_HarnessOGL_brn","V_HarnessOGL_gry","V_HarnessOSpec_brn","V_HarnessOSpec_gry","V_PlateCarrier1_blk","V_PlateCarrier1_rgr","V_PlateCarrier2_rgr","V_PlateCarrier3_rgr","V_PlateCarrierGL_blk","V_PlateCarrierGL_mtp","V_PlateCarrierGL_rgr","V_PlateCarrierH_CTRG","V_PlateCarrierIA1_dgtl","V_PlateCarrierIA2_dgtl","V_PlateCarrierIAGL_dgtl","V_PlateCarrierIAGL_oli","V_PlateCarrierL_CTRG","V_PlateCarrierSpec_blk","V_PlateCarrierSpec_mtp","V_PlateCarrierSpec_rgr"]], + ["vestTypes1",["V_Press_F","V_Rangemaster_belt","V_TacVest_blk","V_TacVest_blk_POLICE","V_TacVest_brn","V_TacVest_camo","V_TacVest_khk","V_TacVest_oli","V_TacVestCamo_khk","V_TacVestIR_blk","V_I_G_resistanceLeader_F","V_BandollierB_blk","V_BandollierB_cbr","V_BandollierB_khk","V_BandollierB_oli","V_BandollierB_rgr","V_Chestrig_blk","V_Chestrig_khk","V_Chestrig_oli","V_Chestrig_rgr","V_HarnessO_brn","V_HarnessO_gry","V_HarnessOGL_brn","V_HarnessOGL_gry","V_HarnessOSpec_brn","V_HarnessOSpec_gry","V_PlateCarrier1_blk","V_PlateCarrier1_rgr","V_PlateCarrier2_rgr","V_PlateCarrier3_rgr","V_PlateCarrierGL_blk","V_PlateCarrierGL_mtp","V_PlateCarrierGL_rgr","V_PlateCarrierH_CTRG","V_PlateCarrierIA1_dgtl","V_PlateCarrierIA2_dgtl","V_PlateCarrierIAGL_dgtl","V_PlateCarrierIAGL_oli","V_PlateCarrierL_CTRG","V_PlateCarrierSpec_blk","V_PlateCarrierSpec_mtp","V_PlateCarrierSpec_rgr"]], + ["vestTypes2",["V_Press_F","V_Rangemaster_belt","V_TacVest_blk","V_TacVest_blk_POLICE","V_TacVest_brn","V_TacVest_camo","V_TacVest_khk","V_TacVest_oli","V_TacVestCamo_khk","V_TacVestIR_blk","V_I_G_resistanceLeader_F","V_BandollierB_blk","V_BandollierB_cbr","V_BandollierB_khk","V_BandollierB_oli","V_BandollierB_rgr","V_Chestrig_blk","V_Chestrig_khk","V_Chestrig_oli","V_Chestrig_rgr","V_HarnessO_brn","V_HarnessO_gry","V_HarnessOGL_brn","V_HarnessOGL_gry","V_HarnessOSpec_brn","V_HarnessOSpec_gry","V_PlateCarrier1_blk","V_PlateCarrier1_rgr","V_PlateCarrier2_rgr","V_PlateCarrier3_rgr","V_PlateCarrierGL_blk","V_PlateCarrierGL_mtp","V_PlateCarrierGL_rgr","V_PlateCarrierH_CTRG","V_PlateCarrierIA1_dgtl","V_PlateCarrierIA2_dgtl","V_PlateCarrierIAGL_dgtl","V_PlateCarrierIAGL_oli","V_PlateCarrierL_CTRG","V_PlateCarrierSpec_blk","V_PlateCarrierSpec_mtp","V_PlateCarrierSpec_rgr"]], + ["vestTypes3",["V_Press_F","V_Rangemaster_belt","V_TacVest_blk","V_TacVest_blk_POLICE","V_TacVest_brn","V_TacVest_camo","V_TacVest_khk","V_TacVest_oli","V_TacVestCamo_khk","V_TacVestIR_blk","V_I_G_resistanceLeader_F","V_BandollierB_blk","V_BandollierB_cbr","V_BandollierB_khk","V_BandollierB_oli","V_BandollierB_rgr","V_Chestrig_blk","V_Chestrig_khk","V_Chestrig_oli","V_Chestrig_rgr","V_HarnessO_brn","V_HarnessO_gry","V_HarnessOGL_brn","V_HarnessOGL_gry","V_HarnessOSpec_brn","V_HarnessOSpec_gry","V_PlateCarrier1_blk","V_PlateCarrier1_rgr","V_PlateCarrier2_rgr","V_PlateCarrier3_rgr","V_PlateCarrierGL_blk","V_PlateCarrierGL_mtp","V_PlateCarrierGL_rgr","V_PlateCarrierH_CTRG","V_PlateCarrierIA1_dgtl","V_PlateCarrierIA2_dgtl","V_PlateCarrierIAGL_dgtl","V_PlateCarrierIAGL_oli","V_PlateCarrierL_CTRG","V_PlateCarrierSpec_blk","V_PlateCarrierSpec_mtp","V_PlateCarrierSpec_rgr"]], + ["headgearTypes0",["H_Cap_blk","H_Cap_blk_Raven","H_Cap_blu","H_Cap_brn_SPECOPS","H_Cap_grn","H_Cap_headphones","H_Cap_khaki_specops_UK","H_Cap_oli","H_Cap_press","H_Cap_red","H_Cap_tan","H_Cap_tan_specops_US","H_Watchcap_blk","H_Watchcap_camo","H_Watchcap_khk","H_Watchcap_sgg","H_MilCap_blue","H_MilCap_dgtl","H_MilCap_mcamo","H_MilCap_ocamo","H_MilCap_oucamo","H_MilCap_rucamo","H_Bandanna_camo","H_Bandanna_cbr","H_Bandanna_gry","H_Bandanna_khk","H_Bandanna_khk_hs","H_Bandanna_mcamo","H_Bandanna_sgg","H_Bandanna_surfer","H_Booniehat_dgtl","H_Booniehat_dirty","H_Booniehat_grn","H_Booniehat_indp","H_Booniehat_khk","H_Booniehat_khk_hs","H_Booniehat_mcamo","H_Booniehat_tan","H_Hat_blue","H_Hat_brown","H_Hat_camo","H_Hat_checker","H_Hat_grey","H_Hat_tan","H_StrawHat","H_StrawHat_dark","H_Beret_02","H_Beret_blk","H_Beret_blk_POLICE","H_Beret_brn_SF","H_Beret_Colonel","H_Beret_grn","H_Beret_grn_SF","H_Beret_ocamo","H_Beret_red","H_Shemag_khk","H_Shemag_olive","H_Shemag_olive_hs"]], + ["headgearTypes1",["H_Cap_blk","H_Cap_blk_Raven","H_Cap_blu","H_Cap_brn_SPECOPS","H_Cap_grn","H_Cap_headphones","H_Cap_khaki_specops_UK","H_Cap_oli","H_Cap_press","H_Cap_red","H_Cap_tan","H_Cap_tan_specops_US","H_Watchcap_blk","H_Watchcap_camo","H_Watchcap_khk","H_Watchcap_sgg","H_MilCap_blue","H_MilCap_dgtl","H_MilCap_mcamo","H_MilCap_ocamo","H_MilCap_oucamo","H_MilCap_rucamo","H_Bandanna_camo","H_Bandanna_cbr","H_Bandanna_gry","H_Bandanna_khk","H_Bandanna_khk_hs","H_Bandanna_mcamo","H_Bandanna_sgg","H_Bandanna_surfer","H_Booniehat_dgtl","H_Booniehat_dirty","H_Booniehat_grn","H_Booniehat_indp","H_Booniehat_khk","H_Booniehat_khk_hs","H_Booniehat_mcamo","H_Booniehat_tan","H_Hat_blue","H_Hat_brown","H_Hat_camo","H_Hat_checker","H_Hat_grey","H_Hat_tan","H_StrawHat","H_StrawHat_dark","H_Beret_02","H_Beret_blk","H_Beret_blk_POLICE","H_Beret_brn_SF","H_Beret_Colonel","H_Beret_grn","H_Beret_grn_SF","H_Beret_ocamo","H_Beret_red","H_Shemag_khk","H_Shemag_olive","H_Shemag_olive_hs"]], + ["headgearTypes2",["H_Cap_blk","H_Cap_blk_Raven","H_Cap_blu","H_Cap_brn_SPECOPS","H_Cap_grn","H_Cap_headphones","H_Cap_khaki_specops_UK","H_Cap_oli","H_Cap_press","H_Cap_red","H_Cap_tan","H_Cap_tan_specops_US","H_Watchcap_blk","H_Watchcap_camo","H_Watchcap_khk","H_Watchcap_sgg","H_MilCap_blue","H_MilCap_dgtl","H_MilCap_mcamo","H_MilCap_ocamo","H_MilCap_oucamo","H_MilCap_rucamo","H_Bandanna_camo","H_Bandanna_cbr","H_Bandanna_gry","H_Bandanna_khk","H_Bandanna_khk_hs","H_Bandanna_mcamo","H_Bandanna_sgg","H_Bandanna_surfer","H_Booniehat_dgtl","H_Booniehat_dirty","H_Booniehat_grn","H_Booniehat_indp","H_Booniehat_khk","H_Booniehat_khk_hs","H_Booniehat_mcamo","H_Booniehat_tan","H_Hat_blue","H_Hat_brown","H_Hat_camo","H_Hat_checker","H_Hat_grey","H_Hat_tan","H_StrawHat","H_StrawHat_dark","H_Beret_02","H_Beret_blk","H_Beret_blk_POLICE","H_Beret_brn_SF","H_Beret_Colonel","H_Beret_grn","H_Beret_grn_SF","H_Beret_ocamo","H_Beret_red","H_Shemag_khk","H_Shemag_olive","H_Shemag_olive_hs"]], + ["headgearTypes3",["H_Cap_blk","H_Cap_blk_Raven","H_Cap_blu","H_Cap_brn_SPECOPS","H_Cap_grn","H_Cap_headphones","H_Cap_khaki_specops_UK","H_Cap_oli","H_Cap_press","H_Cap_red","H_Cap_tan","H_Cap_tan_specops_US","H_Watchcap_blk","H_Watchcap_camo","H_Watchcap_khk","H_Watchcap_sgg","H_MilCap_blue","H_MilCap_dgtl","H_MilCap_mcamo","H_MilCap_ocamo","H_MilCap_oucamo","H_MilCap_rucamo","H_Bandanna_camo","H_Bandanna_cbr","H_Bandanna_gry","H_Bandanna_khk","H_Bandanna_khk_hs","H_Bandanna_mcamo","H_Bandanna_sgg","H_Bandanna_surfer","H_Booniehat_dgtl","H_Booniehat_dirty","H_Booniehat_grn","H_Booniehat_indp","H_Booniehat_khk","H_Booniehat_khk_hs","H_Booniehat_mcamo","H_Booniehat_tan","H_Hat_blue","H_Hat_brown","H_Hat_camo","H_Hat_checker","H_Hat_grey","H_Hat_tan","H_StrawHat","H_StrawHat_dark","H_Beret_02","H_Beret_blk","H_Beret_blk_POLICE","H_Beret_brn_SF","H_Beret_Colonel","H_Beret_grn","H_Beret_grn_SF","H_Beret_ocamo","H_Beret_red","H_Shemag_khk","H_Shemag_olive","H_Shemag_olive_hs"]], + ["foodLoot",["Exile_Item_GloriousKnakworst","Exile_Item_SausageGravy","Exile_Item_ChristmasTinner","Exile_Item_BBQSandwich","Exile_Item_Surstromming","Exile_Item_Catfood","Exile_Item_PlasticBottleFreshWater","Exile_Item_Beer","Exile_Item_Energydrink"]], + ["miscLoot",["Exile_Item_Rope","Exile_Item_DuctTape","Exile_Item_ExtensionCord","Exile_Item_FuelCanisterEmpty","Exile_Item_JunkMetal","Exile_Item_LightBulb","Exile_Item_MetalBoard","Exile_Item_MetalPole","Exile_Item_CamoTentKit","Exile_Item_InstaDoc"]], + ["medicalLoot",["Exile_Item_InstaDoc","Exile_Item_Bandage","Exile_Item_Vishpirin"]], + ["toolsList0",[["Exile_Item_XM8",0.90],["ItemCompass",0.50],["ItemMap",0.50],["ItemGPS",0.05],["ItemRadio",0.05]]], + ["toolsList1",[["Exile_Item_XM8",0.90],["ItemCompass",0.60],["ItemMap",0.60],["ItemGPS",0.10],["ItemRadio",0.10]]], + ["toolsList2",[["Exile_Item_XM8",0.90],["ItemCompass",0.70],["ItemMap",0.70],["ItemGPS",0.15],["ItemRadio",0.15]]], + ["toolsList3",[["Exile_Item_XM8",0.90],["ItemCompass",0.80],["ItemMap",0.80],["ItemGPS",0.20],["ItemRadio",0.20]]], + ["gadgetsList0",[["binocular",0.40],["NVGoggles",0.05]]], + ["gadgetsList1",[["binocular",0.50],["NVGoggles",0.10]]], + ["gadgetsList2",[["binocular",0.60],["NVGoggles",0.15]]], + ["gadgetsList3",[["binocular",0.70],["NVGoggles",0.20]]] +]; + +if (A3XAI_verifySettings) then { + if !(A3XAI_unitLevel_capitalCity in [0,1,2,3]) then {diag_log format ["[A3XAI] Error found in variable A3XAI_unitLevel_capitalCity, resetting to default value."]; A3XAI_unitLevel_capitalCity = 1}; + if !(A3XAI_unitLevel_city in [0,1,2,3]) then {diag_log format ["[A3XAI] Error found in variable A3XAI_unitLevel_city, resetting to default value."]; A3XAI_unitLevel_city = 1}; + if !(A3XAI_unitLevel_village in [0,1,2,3]) then {diag_log format ["[A3XAI] Error found in variable A3XAI_unitLevel_village, resetting to default value."]; A3XAI_unitLevel_village = 0}; + if !(A3XAI_unitLevel_remoteArea in [0,1,2,3]) then {diag_log format ["[A3XAI] Error found in variable A3XAI_unitLevel_remoteArea, resetting to default value."]; A3XAI_unitLevel_remoteArea = 2}; + if !(A3XAI_unitLevel_wilderness in [0,1,2,3]) then {diag_log format ["[A3XAI] Error found in variable A3XAI_unitLevel_remoteArea, resetting to default value."]; A3XAI_unitLevel_wilderness = 2}; + if !((count A3XAI_levelChancesAir) isEqualTo 4) then {diag_log format ["[A3XAI] Error found in variable A3XAI_levelChancesAir, resetting to default value."]; A3XAI_levelChancesAir = [0.00,0.50,0.35,0.15]}; + if !((count A3XAI_levelChancesLand) isEqualTo 4) then {diag_log format ["[A3XAI] Error found in variable A3XAI_levelChancesLand, resetting to default value."]; A3XAI_levelChancesAir = [0.00,0.50,0.35,0.15]}; + if !((count A3XAI_useWeaponChance0) isEqualTo 4) then {diag_log format ["[A3XAI] Error found in variable A3XAI_useWeaponChance0, resetting to default value."]; A3XAI_useWeaponChance0 = [0.20,0.80,0.00,0.00]}; + if !((count A3XAI_useWeaponChance1) isEqualTo 4) then {diag_log format ["[A3XAI] Error found in variable A3XAI_useWeaponChance1, resetting to default value."]; A3XAI_useWeaponChance1 = [0.00,0.90,0.05,0.05]}; + if !((count A3XAI_useWeaponChance2) isEqualTo 4) then {diag_log format ["[A3XAI] Error found in variable A3XAI_useWeaponChance2, resetting to default value."]; A3XAI_useWeaponChance2 = [0.00,0.80,0.10,0.10]}; + if !((count A3XAI_useWeaponChance3) isEqualTo 4) then {diag_log format ["[A3XAI] Error found in variable A3XAI_useWeaponChance3, resetting to default value."]; A3XAI_useWeaponChance3 = [0.00,0.70,0.15,0.15]}; + if ("air_reinforce" in A3XAI_airReinforcementAllowedFor) then {A3XAI_airReinforcementAllowedFor = A3XAI_airReinforcementAllowedFor - ["air_reinforce"]}; + if ("uav" in A3XAI_airReinforcementAllowedFor) then {A3XAI_airReinforcementAllowedFor = A3XAI_airReinforcementAllowedFor - ["uav"]}; + if ("ugv" in A3XAI_airReinforcementAllowedFor) then {A3XAI_airReinforcementAllowedFor = A3XAI_airReinforcementAllowedFor - ["ugv"]}; + if !(A3XAI_side in [east,west]) then {diag_log format ["[A3XAI] Error found in variable A3XAI_side. User defined value: %1. Acceptable values: east, west",A3XAI_side]; A3XAI_side = east;}; +}; + +diag_log format ["[A3XAI] Loaded all A3XAI settings in %1 seconds.",(diag_tickTime - _startTime)]; + +true diff --git a/A3XAI/init/loadSettingsHC.sqf b/A3XAI/init/loadSettingsHC.sqf new file mode 100644 index 0000000..dfedd11 --- /dev/null +++ b/A3XAI/init/loadSettingsHC.sqf @@ -0,0 +1,15 @@ +_startTime = diag_tickTime; + +if (A3XAI_HC_serverResponse isEqualTo []) exitWith {}; + +{ + _variableName = _x select 0; + _variableValue = _x select 1; + + missionNamespace setVariable [format ["A3XAI_%1",_variableName],_variableValue]; + diag_log format ["Debug: %1:%2",_variableName,_variableValue]; +} forEach A3XAI_HC_serverResponse; + +diag_log format ["[A3XAI] Loaded all A3XAI settings in %1 seconds.",(diag_tickTime - _startTime)]; + +true diff --git a/A3XAI/init/server/fn_postinit.sqf b/A3XAI/init/server/fn_postinit.sqf new file mode 100644 index 0000000..49db0ec --- /dev/null +++ b/A3XAI/init/server/fn_postinit.sqf @@ -0,0 +1,17 @@ +/** + * Exile Mod + * www.exilemod.com + * © 2015 Exile Mod Team + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + */ + +if (!hasInterface && {!isDedicated}) exitWith {diag_log format ["A3XAI blocked execution of %1.",__FILE__];}; + +if (isDedicated) then { + call compile preprocessFileLineNumbers "exile_server\bootstrap\fn_postInit.sqf"; + //diag_log "Debug: Executing Exile server post init."; +}; + +true \ No newline at end of file diff --git a/A3XAI/init/server/fn_preInit.sqf b/A3XAI/init/server/fn_preInit.sqf new file mode 100644 index 0000000..20a7244 --- /dev/null +++ b/A3XAI/init/server/fn_preInit.sqf @@ -0,0 +1,17 @@ +/** + * Exile Mod + * www.exilemod.com + * © 2015 Exile Mod Team + * + * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. + */ + +if (!hasInterface && {!isDedicated}) exitWith {diag_log format ["A3XAI blocked execution of %1.",__FILE__];}; + +if (isDedicated) then { + call compile preprocessFileLineNumbers "exile_server\bootstrap\fn_preInit.sqf"; + //diag_log "Debug: Executing Exile server post init."; +}; + +true \ No newline at end of file diff --git a/A3XAI/init/variables.sqf b/A3XAI/init/variables.sqf new file mode 100644 index 0000000..68dae1b --- /dev/null +++ b/A3XAI/init/variables.sqf @@ -0,0 +1,56 @@ +//Set internal-use variables +A3XAI_unitLevels = [0,1,2,3]; +A3XAI_unitLevelsAll = A3XAI_unitLevels; +A3XAI_curHeliPatrols = 0; //Current number of active air patrols +A3XAI_curLandPatrols = 0; //Current number of active land patrols +A3XAI_curUAVPatrols = 0; +A3XAI_curUGVPatrols = 0; +A3XAI_dynamicTriggerArray = []; //List of all generated dynamic triggers. +A3XAI_staticTriggerArray = []; //List of all static triggers (Used for statistics only, not used for spawning/despawning) +A3XAI_respawnQueue = []; //Queue of AI groups that require respawning. Group ID is removed from queue after it is respawned. +A3XAI_areaBlacklists = []; //Queue of temporary dynamic spawn area blacklists for deletion +A3XAI_checkedClassnames = [[],[],[]]; //Classnames verified - Weapons/Magazines/Vehicles +A3XAI_invalidClassnames = [[],[],[]]; //Classnames known as invalid - Weapons/Magazines/Vehicles +A3XAI_monitoredObjects = []; //used to cleanup AI vehicles that may not be destroyed. +A3XAI_locations = []; +A3XAI_locationsAir = []; +A3XAI_locationsLand = []; +A3XAI_heliTypesUsable = []; +A3XAI_vehTypesUsable = []; +A3XAI_UAVTypesUsable = []; +A3XAI_UGVTypesUsable = []; +A3XAI_randomTriggerArray = []; +A3XAI_mapMarkerArray = []; +A3XAI_weaponTypeIndices0 = []; +A3XAI_weaponTypeIndices1 = []; +A3XAI_weaponTypeIndices2 = []; +A3XAI_weaponTypeIndices3 = []; +A3XAI_failedDynamicSpawns = []; +A3XAI_HCObject = objNull; +A3XAI_HCIsConnected = false; +A3XAI_HCObjectOwnerID = 0; +A3XAI_activeGroupAmount = 0; +A3XAI_staticInfantrySpawnQueue = []; +A3XAI_customBlacklistQueue = []; +A3XAI_customInfantrySpawnQueue = []; +A3XAI_createCustomSpawnQueue = []; +A3XAI_customVehicleSpawnQueue = []; +A3XAI_randomInfantrySpawnQueue = []; +A3XAI_activeReinforcements = []; +A3XAI_reinforcedPositions = []; +A3XAI_spawnChanceMultiplier = 1; +A3XAI_HCAllowedTypes = ["static", "dynamic", "random", "air", "land", "staticcustom", "aircustom", "landcustom", "vehiclecrew", "air_reinforce", "uav", "ugv", "infantry_reinforce"]; +A3XAI_lastGroupTransfer = 0; +A3XAI_automatedUnitTypes = ["uav","ugv"]; +A3XAI_noAggroAreas = []; +A3XAI_activeNoAggroAreas = []; +A3XAI_currentFPS = diag_fps; +A3XAI_activePlayerAreas = []; //List of *active* static trigger objects (used for spawning/despawning), not for statistics +A3XAI_activeGroups = []; +A3XAI_activeLocalGroups = []; +A3XAI_activeHCGroups = []; +A3XAI_activeVehicles = []; +A3XAI_activeLocalVehicles = []; +A3XAI_activeHCVehicles = []; +A3XAI_managedGroups = []; +A3XAI_staticSpawnObjects = []; //List of all static trigger objects (used for spawning/despawning, not for statistics). diff --git a/A3XAI/init/variables_precalculated.sqf b/A3XAI/init/variables_precalculated.sqf new file mode 100644 index 0000000..854d697 --- /dev/null +++ b/A3XAI/init/variables_precalculated.sqf @@ -0,0 +1,6 @@ +//Set internal-use variables +A3XAI_respawnTimeVariance = (abs (A3XAI_respawnTimeMax - A3XAI_respawnTimeMin)); +A3XAI_respawnTimeVarAir = (abs (A3XAI_respawnAirMaxTime - A3XAI_respawnAirMinTime)); +A3XAI_respawnTimeVarLand = (abs (A3XAI_respawnLandMaxTime - A3XAI_respawnLandMinTime)); +A3XAI_respawnTimeVarUAV = (abs (A3XAI_respawnUAVMaxTime - A3XAI_respawnUAVMinTime)); +A3XAI_respawnTimeVarUGV = (abs (A3XAI_respawnUGVMaxTime - A3XAI_respawnUGVMinTime)); diff --git a/A3XAI/scripts/A3XAI_buildBackpackList.sqf b/A3XAI/scripts/A3XAI_buildBackpackList.sqf new file mode 100644 index 0000000..e34256f --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildBackpackList.sqf @@ -0,0 +1,67 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_items", "_backpacksLevel0", "_backpacksLevel1", "_backpacksLevel2", "_backpacksLevel3", "_itemLevel"]; + +_startTime = diag_tickTime; + +_items = [missionConfigFile >> "CfgTraderCategories" >> "Backpacks","items",[]] call BIS_fnc_returnConfigEntry; + +if !(A3XAI_dynamicBackpackBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicBackpackBlacklist; +}; + +_backpacksLevel0 = []; +_backpacksLevel1 = []; +_backpacksLevel2 = []; +_backpacksLevel3 = []; + +if !(_items isEqualTo []) then { + { + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice < A3XAI_itemPriceLimit) then { + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _backpacksLevel0 pushBack _x; + _backpacksLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 2) exitWith { + _backpacksLevel1 pushBack _x; + _backpacksLevel2 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _backpacksLevel2 pushBack _x; + _backpacksLevel3 pushBack _x; + }; + + _backpacksLevel0 pushBack _x; + _backpacksLevel1 pushBack _x; + _backpacksLevel2 pushBack _x; + _backpacksLevel3 pushBack _x; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; + } forEach _items; + + if !(_backpacksLevel0 isEqualTo []) then {A3XAI_backpackTypes0 = _backpacksLevel0} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_backpackTypes0. Classnames from A3XAI_config.sqf used instead.";}; + if !(_backpacksLevel1 isEqualTo []) then {A3XAI_backpackTypes1 = _backpacksLevel1} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_backpackTypes1. Classnames from A3XAI_config.sqf used instead.";}; + if !(_backpacksLevel2 isEqualTo []) then {A3XAI_backpackTypes2 = _backpacksLevel2} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_backpackTypes2. Classnames from A3XAI_config.sqf used instead.";}; + if !(_backpacksLevel3 isEqualTo []) then {A3XAI_backpackTypes3 = _backpacksLevel3} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_backpackTypes3. Classnames from A3XAI_config.sqf used instead.";}; + + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 backpack classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_backpackTypes0: %1",A3XAI_backpackTypes0]; + diag_log format ["A3XAI Debug: Contents of A3XAI_backpackTypes1: %1",A3XAI_backpackTypes1]; + diag_log format ["A3XAI Debug: Contents of A3XAI_backpackTypes2: %1",A3XAI_backpackTypes2]; + diag_log format ["A3XAI Debug: Contents of A3XAI_backpackTypes3: %1",A3XAI_backpackTypes3]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate backpack classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicBackpackBlacklist = nil; +A3XAI_dynamicBackpackLevels = nil; diff --git a/A3XAI/scripts/A3XAI_buildFoodList.sqf b/A3XAI/scripts/A3XAI_buildFoodList.sqf new file mode 100644 index 0000000..32cae29 --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildFoodList.sqf @@ -0,0 +1,40 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_food", "_drink", "_startTime", "_foodList", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_itemList", "_itemInfo", "_itemBias", "_itemType", "_item","_itemClass"]; + +_startTime = diag_tickTime; + +_loot0 = [missionConfigFile >> "CfgTraderCategories" >> "Food","items",[]] call BIS_fnc_returnConfigEntry; +_loot1 = [missionConfigFile >> "CfgTraderCategories" >> "Drinks","items",[]] call BIS_fnc_returnConfigEntry; + +_items = []; +{ + _items append _x; +} forEach [_loot0,_loot1]; + +if !(A3XAI_dynamicFoodBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicFoodBlacklist; +}; + +{ + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice > A3XAI_itemPriceLimit) then { + _items deleteAt _forEachIndex; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; +} forEach _items; + +if !(_items isEqualTo []) then { + A3XAI_foodLoot = _items; + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 food classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_foodLoot: %1",A3XAI_foodLoot]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate food classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicFoodBlacklist = nil; diff --git a/A3XAI/scripts/A3XAI_buildHeadgearList.sqf b/A3XAI/scripts/A3XAI_buildHeadgearList.sqf new file mode 100644 index 0000000..0de909b --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildHeadgearList.sqf @@ -0,0 +1,68 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_items", "_headgearsLevel0", "_headgearsLevel1", "_headgearsLevel2", "_headgearsLevel3", "_itemLevel"]; + +_startTime = diag_tickTime; + +_items = [missionConfigFile >> "CfgTraderCategories" >> "Headgear","items",[]] call BIS_fnc_returnConfigEntry; + +if !(A3XAI_dynamicHeadgearBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicHeadgearBlacklist; +}; + +_headgearsLevel0 = []; +_headgearsLevel1 = []; +_headgearsLevel2 = []; +_headgearsLevel3 = []; + +if !(_items isEqualTo []) then { + { + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice < A3XAI_itemPriceLimit) then { + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _headgearsLevel0 pushBack _x; + _headgearsLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 2) exitWith { + _headgearsLevel1 pushBack _x; + _headgearsLevel2 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _headgearsLevel2 pushBack _x; + _headgearsLevel3 pushBack _x; + }; + + _headgearsLevel0 pushBack _x; + _headgearsLevel1 pushBack _x; + _headgearsLevel2 pushBack _x; + _headgearsLevel3 pushBack _x; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; + + } forEach _items; + + if !(_headgearsLevel0 isEqualTo []) then {A3XAI_headgearTypes0 = _headgearsLevel0} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_headgearTypes0. Classnames from A3XAI_config.sqf used instead.";}; + if !(_headgearsLevel1 isEqualTo []) then {A3XAI_headgearTypes1 = _headgearsLevel1} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_headgearTypes1. Classnames from A3XAI_config.sqf used instead.";}; + if !(_headgearsLevel2 isEqualTo []) then {A3XAI_headgearTypes2 = _headgearsLevel2} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_headgearTypes2. Classnames from A3XAI_config.sqf used instead.";}; + if !(_headgearsLevel3 isEqualTo []) then {A3XAI_headgearTypes3 = _headgearsLevel3} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_headgearTypes3. Classnames from A3XAI_config.sqf used instead.";}; + + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 headgear classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_headgearTypes0: %1",A3XAI_headgearTypes0]; + diag_log format ["A3XAI Debug: Contents of A3XAI_headgearTypes1: %1",A3XAI_headgearTypes1]; + diag_log format ["A3XAI Debug: Contents of A3XAI_headgearTypes2: %1",A3XAI_headgearTypes2]; + diag_log format ["A3XAI Debug: Contents of A3XAI_headgearTypes3: %1",A3XAI_headgearTypes3]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate headgear classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicHeadgearBlacklist = nil; +A3XAI_dynamicHeadgearLevels = nil; diff --git a/A3XAI/scripts/A3XAI_buildLootList.sqf b/A3XAI/scripts/A3XAI_buildLootList.sqf new file mode 100644 index 0000000..cc68279 --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildLootList.sqf @@ -0,0 +1,43 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_lootListLarge1", "_lootListLarge2", "_lootListLarge", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_item", "_itemSubClass", "_itemList", "_itemInfo", "_itemBias", "_itemType"]; + +_startTime = diag_tickTime; + +_loot0 = [missionConfigFile >> "CfgTraderCategories" >> "Hardware","items",[]] call BIS_fnc_returnConfigEntry; +_loot1 = [missionConfigFile >> "CfgTraderCategories" >> "Smokes","items",[]] call BIS_fnc_returnConfigEntry; +// _loot2 = [missionConfigFile >> "CfgTraderCategories" >> "Tools","items",[]] call BIS_fnc_returnConfigEntry; +// _loot3 = [missionConfigFile >> "CfgTraderCategories" >> "FirstAid","items",[]] call BIS_fnc_returnConfigEntry; + +_items = []; + +{ + _items append _x; +} forEach [_loot0,_loot1]; + +if !(A3XAI_dynamicLootBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicLootBlacklist; +}; + +{ + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice > A3XAI_itemPriceLimit) then { + _items deleteAt _forEachIndex; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; +} forEach _items; + +if !(_items isEqualTo []) then { + A3XAI_MiscLoot = _items; + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 generic loot classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_MiscLoot: %1",A3XAI_MiscLoot]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate loot classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicLootBlacklist = nil; diff --git a/A3XAI/scripts/A3XAI_buildMedicalList.sqf b/A3XAI/scripts/A3XAI_buildMedicalList.sqf new file mode 100644 index 0000000..939f294 --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildMedicalList.sqf @@ -0,0 +1,40 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_list0", "_items", "_itemPrice"]; + +_startTime = diag_tickTime; + +_list0 = [missionConfigFile >> "CfgTraderCategories" >> "FirstAid","items",[]] call BIS_fnc_returnConfigEntry; + +_items = []; + +{ + _items append _x; +} forEach [_list0]; + +if !(A3XAI_dynamicMedicalBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicMedicalBlacklist; +}; + +{ + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice > A3XAI_itemPriceLimit) then { + _items deleteAt _forEachIndex; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; +} forEach _items; + +if !(_items isEqualTo []) then { + A3XAI_medicalLoot = _items; + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 medical classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_medicalLoot: %1",A3XAI_medicalLoot]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate medical classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicMedicalBlacklist = nil; diff --git a/A3XAI/scripts/A3XAI_buildScopeList.sqf b/A3XAI/scripts/A3XAI_buildScopeList.sqf new file mode 100644 index 0000000..9eb50f8 --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildScopeList.sqf @@ -0,0 +1,33 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_scopeList", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_itemList", "_itemInfo", "_itemBias", "_itemType", "_item","_itemClass"]; + +_startTime = diag_tickTime; + +_items = [missionConfigFile >> "CfgTraderCategories" >> "OpticAttachments","items",[]] call BIS_fnc_returnConfigEntry; + +if !(A3XAI_dynamicOpticsBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicOpticsBlacklist; +}; + +{ + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice > A3XAI_itemPriceLimit) then { + _items deleteAt _forEachIndex; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; +} forEach _items; + +if !(_items isEqualTo []) then { + A3XAI_weaponOpticsList = _items; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Generated %1 weapon optics classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_weaponOpticsList: %1",A3XAI_weaponOpticsList]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate weapon optics classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicOpticsBlacklist = nil; diff --git a/A3XAI/scripts/A3XAI_buildUniformList.sqf b/A3XAI/scripts/A3XAI_buildUniformList.sqf new file mode 100644 index 0000000..7369792 --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildUniformList.sqf @@ -0,0 +1,67 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_items", "_uniformsLevel0", "_uniformsLevel1", "_uniformsLevel2", "_uniformsLevel3", "_itemLevel"]; + +_startTime = diag_tickTime; + +_items = [missionConfigFile >> "CfgTraderCategories" >> "Uniforms","items",[]] call BIS_fnc_returnConfigEntry; + +if !(A3XAI_dynamicUniformBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicUniformBlacklist; +}; + +_uniformsLevel0 = []; +_uniformsLevel1 = []; +_uniformsLevel2 = []; +_uniformsLevel3 = []; + +if !(_items isEqualTo []) then { + { + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice < A3XAI_itemPriceLimit) then { + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _uniformsLevel0 pushBack _x; + _uniformsLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 2) exitWith { + _uniformsLevel1 pushBack _x; + _uniformsLevel2 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _uniformsLevel2 pushBack _x; + _uniformsLevel3 pushBack _x; + }; + + _uniformsLevel0 pushBack _x; + _uniformsLevel1 pushBack _x; + _uniformsLevel2 pushBack _x; + _uniformsLevel3 pushBack _x; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; + } forEach _items; + + if !(_uniformsLevel0 isEqualTo []) then {A3XAI_uniformTypes0 = _uniformsLevel0} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_uniformTypes0. Classnames from A3XAI_config.sqf used instead.";}; + if !(_uniformsLevel1 isEqualTo []) then {A3XAI_uniformTypes1 = _uniformsLevel1} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_uniformTypes1. Classnames from A3XAI_config.sqf used instead.";}; + if !(_uniformsLevel2 isEqualTo []) then {A3XAI_uniformTypes2 = _uniformsLevel2} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_uniformTypes2. Classnames from A3XAI_config.sqf used instead.";}; + if !(_uniformsLevel3 isEqualTo []) then {A3XAI_uniformTypes3 = _uniformsLevel3} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_uniformTypes3. Classnames from A3XAI_config.sqf used instead.";}; + + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 uniform classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_uniformTypes0: %1",A3XAI_uniformTypes0]; + diag_log format ["A3XAI Debug: Contents of A3XAI_uniformTypes1: %1",A3XAI_uniformTypes1]; + diag_log format ["A3XAI Debug: Contents of A3XAI_uniformTypes2: %1",A3XAI_uniformTypes2]; + diag_log format ["A3XAI Debug: Contents of A3XAI_uniformTypes3: %1",A3XAI_uniformTypes3]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate uniform classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicUniformBlacklist = nil; +A3XAI_dynamicUniformLevels = nil; diff --git a/A3XAI/scripts/A3XAI_buildVestList.sqf b/A3XAI/scripts/A3XAI_buildVestList.sqf new file mode 100644 index 0000000..7ceff24 --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildVestList.sqf @@ -0,0 +1,66 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_items", "_vestsLevel0", "_vestsLevel1", "_vestsLevel2", "_vestsLevel3", "_itemLevel"]; + +_startTime = diag_tickTime; + +_items = [missionConfigFile >> "CfgTraderCategories" >> "Vests","items",[]] call BIS_fnc_returnConfigEntry; + +if !(A3XAI_dynamicVestBlacklist isEqualTo []) then { + _items = _items - A3XAI_dynamicVestBlacklist; +}; + +_vestsLevel0 = []; +_vestsLevel1 = []; +_vestsLevel2 = []; +_vestsLevel3 = []; + +if !(_items isEqualTo []) then { + { + _itemPrice = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "price"); + if (_itemPrice < A3XAI_itemPriceLimit) then { + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _vestsLevel0 pushBack _x; + _vestsLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 2) exitWith { + _vestsLevel1 pushBack _x; + _vestsLevel2 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _vestsLevel2 pushBack _x; + _vestsLevel3 pushBack _x; + }; + + _vestsLevel0 pushBack _x; + _vestsLevel1 pushBack _x; + _vestsLevel2 pushBack _x; + _vestsLevel3 pushBack _x; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_x,A3XAI_itemPriceLimit];}; + }; + } forEach _items; + + if !(_vestsLevel0 isEqualTo []) then {A3XAI_vestTypes0 = _vestsLevel0} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_vestTypes0. Classnames from A3XAI_config.sqf used instead.";}; + if !(_vestsLevel1 isEqualTo []) then {A3XAI_vestTypes1 = _vestsLevel1} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_vestTypes1. Classnames from A3XAI_config.sqf used instead.";}; + if !(_vestsLevel2 isEqualTo []) then {A3XAI_vestTypes2 = _vestsLevel2} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_vestTypes2. Classnames from A3XAI_config.sqf used instead.";}; + if !(_vestsLevel3 isEqualTo []) then {A3XAI_vestTypes3 = _vestsLevel3} else {diag_log "A3XAI Error: Could not dynamically generate A3XAI_vestTypes3. Classnames from A3XAI_config.sqf used instead.";}; + + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Generated %1 vest classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3XAI_debugLevel > 1) then { + diag_log format ["A3XAI Debug: Contents of A3XAI_vestTypes0: %1",A3XAI_vestTypes0]; + diag_log format ["A3XAI Debug: Contents of A3XAI_vestTypes1: %1",A3XAI_vestTypes1]; + diag_log format ["A3XAI Debug: Contents of A3XAI_vestTypes2: %1",A3XAI_vestTypes2]; + diag_log format ["A3XAI Debug: Contents of A3XAI_vestTypes3: %1",A3XAI_vestTypes3]; + }; + }; +} else { + diag_log "A3XAI Error: Could not dynamically generate vest classname list. Classnames from A3XAI_config.sqf used instead."; +}; + +//Cleanup global vars +A3XAI_dynamicVestBlacklist = nil; diff --git a/A3XAI/scripts/A3XAI_buildWeaponList.sqf b/A3XAI/scripts/A3XAI_buildWeaponList.sqf new file mode 100644 index 0000000..19f773a --- /dev/null +++ b/A3XAI/scripts/A3XAI_buildWeaponList.sqf @@ -0,0 +1,196 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime", "_checkWeapon", "_magazineTypes", "_cursorAim", "_ammo", "_ammoHit", "_buildWeaponList", "_pistolList", "_rifleList", "_machinegunList", "_sniperList"]; + +_startTime = diag_tickTime; + +if (isNil "A3XAI_dynamicWeaponBlacklist") then {A3XAI_dynamicWeaponBlacklist = [];}; + +_checkWeapon = +{ + private ["_magazineTypes","_ammo","_ammoMaxRange","_ammoHit"]; + if ((typeName _this) != "STRING") exitWith {false}; + if (_this in A3XAI_dynamicWeaponBlacklist) exitWith {false}; + _magazineTypes = [configFile >> "CfgWeapons" >> _this,"magazines",[]] call BIS_fnc_returnConfigEntry; + if (_magazineTypes isEqualTo []) exitWith {false}; + _cursorAim = [configFile >> "CfgWeapons" >> _this,"cursorAim","throw"] call BIS_fnc_returnConfigEntry; + if (_cursorAim isEqualTo "throw") exitWith {false}; + _ammo = [configFile >> "CfgMagazines" >> (_magazineTypes select 0),"ammo",""] call BIS_fnc_returnConfigEntry; + if (_ammo isEqualTo "") exitWith {false}; + _ammoHit = [configFile >> "CfgAmmo" >> _ammo,"hit",0] call BIS_fnc_returnConfigEntry; + if (_ammoHit isEqualTo 0) exitWith {false}; + _weaponPrice = [configFile >> "CfgExileArsenal" >> _this,"price",0] call BIS_fnc_returnConfigEntry; + if (_weaponPrice > A3XAI_itemPriceLimit) exitWith {if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Item %1 exceeds price limit of %2.",_this,A3XAI_itemPriceLimit];}; false}; + true +}; + +_buildWeaponList = { + private ["_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_weapon", "_itemClass", "_itemList"]; + _items = []; + + { + if (_x call _checkWeapon) then { + _items pushBack _x; + }; + } forEach _this; + + _items +}; + +_pistolList = [missionConfigFile >> "CfgTraderCategories" >> "Pistols","items",[]] call BIS_fnc_returnConfigEntry; +_rifleList = [missionConfigFile >> "CfgTraderCategories" >> "AssaultRifles","items",[]] call BIS_fnc_returnConfigEntry; +_machinegunList = [missionConfigFile >> "CfgTraderCategories" >> "LightMachineGuns","items",[]] call BIS_fnc_returnConfigEntry; +_sniperList = [missionConfigFile >> "CfgTraderCategories" >> "SniperRifles","items",[]] call BIS_fnc_returnConfigEntry; +_submachinegunList = [missionConfigFile >> "CfgTraderCategories" >> "SubMachineGuns","items",[]] call BIS_fnc_returnConfigEntry; +_rifleList append _submachinegunList; + +_pistolList = _pistolList call _buildWeaponList; +_rifleList = _rifleList call _buildWeaponList; +_machinegunList = _machinegunList call _buildWeaponList; +_sniperList = _sniperList call _buildWeaponList; + +_pistolsLevel0 = []; +_pistolsLevel1 = []; +_pistolsLevel2 = []; +_pistolsLevel3 = []; + +_riflesLevel0 = []; +_riflesLevel1 = []; +_riflesLevel2 = []; +_riflesLevel3 = []; + +_machinegunsLevel0 = []; +_machinegunsLevel1 = []; +_machinegunsLevel2 = []; +_machinegunsLevel3 = []; + +_snipersLevel0 = []; +_snipersLevel1 = []; +_snipersLevel2 = []; +_snipersLevel3 = []; + +{ + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _pistolsLevel0 pushBack _x; + _pistolsLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _pistolsLevel2 pushBack _x; + _pistolsLevel3 pushBack _x; + }; + + _pistolsLevel0 pushBack _x; + _pistolsLevel1 pushBack _x; + _pistolsLevel2 pushBack _x; + _pistolsLevel3 pushBack _x; + }; +} forEach _pistolList; + +if !(_pistolsLevel0 isEqualTo []) then {A3XAI_pistolList0 = _pistolsLevel0} else {A3XAI_pistolList0 = A3XAI_pistolList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_pistolList0. Classnames from A3XAI_config.sqf used instead.";}; +if !(_pistolsLevel1 isEqualTo []) then {A3XAI_pistolList1 = _pistolsLevel1} else {A3XAI_pistolList1 = A3XAI_pistolList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_pistolList1. Classnames from A3XAI_config.sqf used instead.";}; +if !(_pistolsLevel2 isEqualTo []) then {A3XAI_pistolList2 = _pistolsLevel2} else {A3XAI_pistolList2 = A3XAI_pistolList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_pistolList2. Classnames from A3XAI_config.sqf used instead.";}; +if !(_pistolsLevel3 isEqualTo []) then {A3XAI_pistolList3 = _pistolsLevel3} else {A3XAI_pistolList3 = A3XAI_pistolList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_pistolList3. Classnames from A3XAI_config.sqf used instead.";}; + +{ + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _riflesLevel0 pushBack _x; + _riflesLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _riflesLevel2 pushBack _x; + _riflesLevel3 pushBack _x; + }; + + _riflesLevel0 pushBack _x; + _riflesLevel1 pushBack _x; + _riflesLevel2 pushBack _x; + _riflesLevel3 pushBack _x; + }; +} forEach _rifleList; + +if !(_riflesLevel0 isEqualTo []) then {A3XAI_rifleList0 = _riflesLevel0} else {A3XAI_rifleList0 = A3XAI_rifleList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_rifleList0. Classnames from A3XAI_config.sqf used instead.";}; +if !(_riflesLevel1 isEqualTo []) then {A3XAI_rifleList1 = _riflesLevel1} else {A3XAI_rifleList1 = A3XAI_rifleList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_rifleList1. Classnames from A3XAI_config.sqf used instead.";}; +if !(_riflesLevel2 isEqualTo []) then {A3XAI_rifleList2 = _riflesLevel2} else {A3XAI_rifleList2 = A3XAI_rifleList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_rifleList2. Classnames from A3XAI_config.sqf used instead.";}; +if !(_riflesLevel3 isEqualTo []) then {A3XAI_rifleList3 = _riflesLevel3} else {A3XAI_rifleList3 = A3XAI_rifleList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_rifleList3. Classnames from A3XAI_config.sqf used instead.";}; + +{ + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _machinegunsLevel0 pushBack _x; + _machinegunsLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _machinegunsLevel2 pushBack _x; + _machinegunsLevel3 pushBack _x; + }; + + _machinegunsLevel0 pushBack _x; + _machinegunsLevel1 pushBack _x; + _machinegunsLevel2 pushBack _x; + _machinegunsLevel3 pushBack _x; + }; +} forEach _machinegunList; + +if !(_machinegunsLevel0 isEqualTo []) then {A3XAI_machinegunList0 = _machinegunsLevel0} else {A3XAI_machinegunList0 = A3XAI_machinegunList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_machinegunList0. Classnames from A3XAI_config.sqf used instead.";}; +if !(_machinegunsLevel1 isEqualTo []) then {A3XAI_machinegunList1 = _machinegunsLevel1} else {A3XAI_machinegunList1 = A3XAI_machinegunList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_machinegunList1. Classnames from A3XAI_config.sqf used instead.";}; +if !(_machinegunsLevel2 isEqualTo []) then {A3XAI_machinegunList2 = _machinegunsLevel2} else {A3XAI_machinegunList2 = A3XAI_machinegunList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_machinegunList2. Classnames from A3XAI_config.sqf used instead.";}; +if !(_machinegunsLevel3 isEqualTo []) then {A3XAI_machinegunList3 = _machinegunsLevel3} else {A3XAI_machinegunList3 = A3XAI_machinegunList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_machinegunList3. Classnames from A3XAI_config.sqf used instead.";}; + +{ + _itemLevel = getNumber(missionConfigFile >> "CfgExileArsenal" >> _x >> "quality"); + call { + if (_itemLevel isEqualTo 1) exitWith { + _snipersLevel0 pushBack _x; + _snipersLevel1 pushBack _x; + }; + if (_itemLevel isEqualTo 3) exitWith { + _snipersLevel2 pushBack _x; + _snipersLevel3 pushBack _x; + }; + + _snipersLevel0 pushBack _x; + _snipersLevel1 pushBack _x; + _snipersLevel2 pushBack _x; + _snipersLevel3 pushBack _x; + }; +} forEach _sniperList; + +if !(_snipersLevel0 isEqualTo []) then {A3XAI_sniperList0 = _snipersLevel0} else {A3XAI_sniperList0 = A3XAI_sniperList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_sniperList0. Classnames from A3XAI_config.sqf used instead.";}; +if !(_snipersLevel1 isEqualTo []) then {A3XAI_sniperList1 = _snipersLevel1} else {A3XAI_sniperList1 = A3XAI_sniperList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_sniperList1. Classnames from A3XAI_config.sqf used instead.";}; +if !(_snipersLevel2 isEqualTo []) then {A3XAI_sniperList2 = _snipersLevel2} else {A3XAI_sniperList2 = A3XAI_sniperList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_sniperList2. Classnames from A3XAI_config.sqf used instead.";}; +if !(_snipersLevel3 isEqualTo []) then {A3XAI_sniperList3 = _snipersLevel3} else {A3XAI_sniperList3 = A3XAI_sniperList; diag_log "A3XAI Error: Could not dynamically generate A3XAI_sniperList3. Classnames from A3XAI_config.sqf used instead.";}; + +if (A3XAI_debugLevel > 0) then { + if (A3XAI_debugLevel > 1) then { + //Display finished weapon arrays + if (!isNil "A3XAI_pistolList0") then {diag_log format ["Contents of A3XAI_pistolList0: %1",A3XAI_pistolList0];}; + if (!isNil "A3XAI_rifleList0") then {diag_log format ["Contents of A3XAI_rifleList0: %1",A3XAI_rifleList0];}; + if (!isNil "A3XAI_machinegunList0") then {diag_log format ["Contents of A3XAI_machinegunList0: %1",A3XAI_machinegunList0];}; + if (!isNil "A3XAI_sniperList0") then {diag_log format ["Contents of A3XAI_sniperList0: %1",A3XAI_sniperList0];}; + + if (!isNil "A3XAI_pistolList1") then {diag_log format ["Contents of A3XAI_pistolList1: %1",A3XAI_pistolList1];}; + if (!isNil "A3XAI_rifleList1") then {diag_log format ["Contents of A3XAI_rifleList1: %1",A3XAI_rifleList1];}; + if (!isNil "A3XAI_machinegunList1") then {diag_log format ["Contents of A3XAI_machinegunList1: %1",A3XAI_machinegunList1];}; + if (!isNil "A3XAI_sniperList1") then {diag_log format ["Contents of A3XAI_sniperList1: %1",A3XAI_sniperList1];}; + + if (!isNil "A3XAI_pistolList2") then {diag_log format ["Contents of A3XAI_pistolList2: %1",A3XAI_pistolList2];}; + if (!isNil "A3XAI_rifleList2") then {diag_log format ["Contents of A3XAI_rifleList2: %1",A3XAI_rifleList2];}; + if (!isNil "A3XAI_machinegunList2") then {diag_log format ["Contents of A3XAI_machinegunList2: %1",A3XAI_machinegunList2];}; + if (!isNil "A3XAI_sniperList2") then {diag_log format ["Contents of A3XAI_sniperList2: %1",A3XAI_sniperList2];}; + + if (!isNil "A3XAI_pistolList3") then {diag_log format ["Contents of A3XAI_pistolList3: %1",A3XAI_pistolList3];}; + if (!isNil "A3XAI_rifleList3") then {diag_log format ["Contents of A3XAI_rifleList3: %1",A3XAI_rifleList3];}; + if (!isNil "A3XAI_machinegunList3") then {diag_log format ["Contents of A3XAI_machinegunList3: %1",A3XAI_machinegunList3];}; + if (!isNil "A3XAI_sniperList3") then {diag_log format ["Contents of A3XAI_sniperList3: %1",A3XAI_sniperList3];}; + }; + diag_log format ["A3XAI Debug: Weapon classname tables created in %1 seconds.",(diag_tickTime - _startTime)]; +}; + +//Clean up global vars +A3XAI_dynamicWeaponBlacklist = nil; + diff --git a/A3XAI/scripts/A3XAI_generateStaticSpawnsAuto.sqf b/A3XAI/scripts/A3XAI_generateStaticSpawnsAuto.sqf new file mode 100644 index 0000000..c169fec --- /dev/null +++ b/A3XAI/scripts/A3XAI_generateStaticSpawnsAuto.sqf @@ -0,0 +1,112 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_expireTime", "_startTime", "_cfgWorldName"]; + +_expireTime = diag_tickTime + SERVER_START_TIMEOUT; +waitUntil {uiSleep 3; !isNil "A3XAI_locations_ready" && {(!isNil SERVER_STARTED_INDICATOR) or {diag_tickTime > _expireTime}}}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 is generating static spawns.",__FILE__];}; + +_startTime = diag_tickTime; +_cfgWorldName = configFile >> "CfgWorlds" >> worldName >> "Names"; + +{ + private ["_placeName","_placePos","_placeType","_trigger"]; + _placeName = _x select 0; + _placePos = _x select 1; + _placeType = _x select 2; + _trigger = _x select 3; + + try { + if (isNull _trigger) then { + throw format ["A3XAI Debug: Static spawn not created at %1 due to null trigger object.",_placeName]; + }; + if (surfaceIsWater _placePos) then { + throw format ["A3XAI Debug: Static spawn not created at %1 due to water position.",_placeName]; + }; + + if !((_placePos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo []) then { + throw format ["A3XAI Debug: Static spawn not created at %1 due to nearby Frequency Jammer.",_placeName]; + }; + + _nearbldgs = _placePos nearObjects ["HouseBase",STATIC_SPAWN_OBJECT_RANGE]; + _nearBlacklistedAreas = nearestLocations [_placePos,[BLACKLIST_OBJECT_GENERAL],1500]; + _spawnPoints = 0; + _aiCount = [0,0]; + _unitLevel = 0; + _radiusA = getNumber (_cfgWorldName >> (_x select 0) >> "radiusA"); + _radiusB = getNumber (_cfgWorldName >> (_x select 0) >> "radiusB"); + _patrolRadius = (((_radiusA min _radiusB) max STATIC_SPAWN_MIN_PATROL_RADIUS) min STATIC_SPAWN_MAX_PATROL_RADIUS); + _spawnChance = 0; + _respawnLimit = -1; + + { + _objType = (typeOf _x); + _objPos = (getPosATL _x); + if (!(surfaceIsWater _objPos) && {(sizeOf _objType) > STATIC_SPAWN_OBJECT_SIZE_REQ}) then { + if ((({_objPos in _x} count _nearBlacklistedAreas) > 0) or {([_objPos,SPAWN_DIST_FROM_NO_AGGRO_AREA] call A3XAI_checkInNoAggroArea)}) then { + if ((_objPos distance2D _placePos) < _patrolRadius) then { + throw format ["A3XAI Debug: Static spawn not created at %1. A spawn position is within %2 m of a blacklisted area.",_placeName,_objPos distance2D _placePos]; + } else { + _nearbldgs deleteAt _forEachIndex; + }; + } else { + _spawnPoints = _spawnPoints + 1; + }; + } else { + _nearbldgs deleteAt _forEachIndex; + }; + } forEach _nearbldgs; + + if (_spawnPoints < 6) then { + if (A3XAI_enableExtraStaticSpawns) then { + _nearbldgs = []; + } else { + throw format ["A3XAI Debug: Static spawn not created at %1. Acceptable positions: %2, Total: %3",_placeName,_spawnPoints,(count _nearbldgs)]; + }; + }; + + call { + if (_placeType isEqualTo "namecitycapital") exitWith { + _aiCount = [A3XAI_minAI_capitalCity,A3XAI_addAI_capitalCity]; + _unitLevel = A3XAI_unitLevel_capitalCity; + _spawnChance = A3XAI_spawnChance_capitalCity; + _respawnLimit = A3XAI_respawnLimit_capitalCity; + }; + if (_placeType isEqualTo "namecity") exitWith { + _aiCount = [A3XAI_minAI_city,A3XAI_addAI_city]; + _unitLevel = A3XAI_unitLevel_city; + _spawnChance = A3XAI_spawnChance_city; + _respawnLimit = A3XAI_respawnLimit_city; + }; + if (_placeType isEqualTo "namevillage") exitWith { + _aiCount = [A3XAI_minAI_village,A3XAI_addAI_village]; + _unitLevel = A3XAI_unitLevel_village; + _spawnChance = A3XAI_spawnChance_village; + _respawnLimit = A3XAI_respawnLimit_village; + }; + if (_placeType isEqualTo "namelocal") exitWith { + _aiCount = [A3XAI_minAI_remoteArea,A3XAI_addAI_remoteArea]; + _unitLevel = A3XAI_unitLevel_remoteArea; + _spawnChance = A3XAI_spawnChance_remoteArea; + _respawnLimit = A3XAI_respawnLimit_remoteArea; + }; + }; + + if ((_spawnChance <= 0) or {(_aiCount isEqualTo [0,0])}) then { + throw format ["A3XAI Debug: Static spawn not created at %1. Spawn chance zero or AI count zero.",_placeName]; + }; + + _trigger setVariable ["respawnLimit",_respawnLimit]; + _trigger setVariable ["respawnLimitOriginal",_respawnLimit]; + _trigger setVariable ["A3XAI_static_spawn",true]; + 0 = [0,_trigger,[],_patrolRadius,_unitLevel,_nearbldgs,_aiCount,_spawnChance] call A3XAI_initializeTrigger; + A3XAI_staticSpawnObjects pushBack _trigger; + } catch { + if (A3XAI_debugLevel > 0) then {diag_log _exception;}; + }; + if ((_forEachIndex % 5) isEqualTo 0) then {uiSleep 0.25;}; +// } forEach A3XAI_locations; +} forEach A3XAI_locations; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 has finished generating %2 static spawns in %3 seconds.",__FILE__,(count A3XAI_staticSpawnObjects),(diag_tickTime - _startTime)];}; diff --git a/A3XAI/scripts/A3XAI_generateStaticSpawnsManual.sqf b/A3XAI/scripts/A3XAI_generateStaticSpawnsManual.sqf new file mode 100644 index 0000000..5038153 --- /dev/null +++ b/A3XAI/scripts/A3XAI_generateStaticSpawnsManual.sqf @@ -0,0 +1,103 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_expireTime", "_startTime", "_cfgWorldName"]; + +_expireTime = diag_tickTime + SERVER_START_TIMEOUT; +waitUntil {uiSleep 3; !isNil "A3XAI_locations_ready" && {(!isNil SERVER_STARTED_INDICATOR) or {diag_tickTime > _expireTime}}}; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 is generating static spawns.",__FILE__];}; + +_startTime = diag_tickTime; +_cfgWorldName = configFile >> "CfgWorlds" >> worldName >> "Names"; + +{ + _placePos = _x; + + try { + if ((typeName _placePos) != "ARRAY") then { + throw format ["A3XAI Debug: %1 unable to create static spawn at non-array position %2",__FILE__,_placePos]; + }; + if ((count _placePos) < 2) then { + throw format ["A3XAI Debug: %1 unable to create static spawn at position %1, position has fewer than 2 elements.",__FILE__,_placePos]; + }; + if (surfaceIsWater _placePos) then { + throw format ["A3XAI Debug: Static spawn not created at %1 due to water position.",_placePos]; + }; + if !((_placePos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo []) then { + throw format ["A3XAI Debug: Static spawn not created at %1 due to nearby Frequency Jammer.",_placePos]; + }; + + _nearbldgs = _placePos nearObjects ["HouseBase",STATIC_SPAWN_OBJECT_RANGE]; + _nearBlacklistedAreas = nearestLocations [_placePos,[BLACKLIST_OBJECT_GENERAL],1500]; + _spawnPoints = 0; + _respawnLimit = -1; + _spawnParams = _placePos call A3XAI_getSpawnParams; + _aiCount = [_spawnParams select 0,_spawnParams select 1]; + _unitLevel = _spawnParams select 2; + _spawnChance = _spawnParams select 3; + _placeName = _spawnParams select 4; + _placeType = _spawnParams select 5; + _radiusA = getNumber (_cfgWorldName >> _placeName >> "radiusA"); + _radiusB = getNumber (_cfgWorldName >> _placeName >> "radiusB"); + _patrolRadius = (((_radiusA min _radiusB) max STATIC_SPAWN_MIN_PATROL_RADIUS) min STATIC_SPAWN_MAX_PATROL_RADIUS); + + { + _objType = (typeOf _x); + _objPos = (getPosATL _x); + if (!(surfaceIsWater _objPos) && {(sizeOf _objType) > STATIC_SPAWN_OBJECT_SIZE_REQ}) then { + if ((({_objPos in _x} count _nearBlacklistedAreas) > 0) or {([_objPos,SPAWN_DIST_FROM_NO_AGGRO_AREA] call A3XAI_checkInNoAggroArea)}) then { + if ((_objPos distance2D _placePos) < _patrolRadius) then { + throw format ["A3XAI Debug: Static spawn not created at %1. A spawn position is within %2 m of a blacklisted area.",_placePos,_objPos distance2D _placePos]; + } else { + _nearbldgs deleteAt _forEachIndex; + }; + } else { + _spawnPoints = _spawnPoints + 1; + }; + } else { + _nearbldgs deleteAt _forEachIndex; + }; + } forEach _nearbldgs; + + if (_spawnPoints < 6) then { + if (A3XAI_enableExtraStaticSpawns) then { + _nearbldgs = []; + } else { + throw format ["A3XAI Debug: Static spawn not created at %1. Acceptable positions: %2, Total: %3",_placePos,_spawnPoints,(count _nearbldgs)]; + }; + }; + + _trigger = [_placePos,_placeName] call A3XAI_createTriggerArea; + _result = [_trigger,_spawnParams] call A3XAI_setSpawnParams; + + if ((_spawnChance <= 0) or {(_aiCount isEqualTo [0,0])}) then { + throw format ["A3XAI Debug: Static spawn not created at %1. Spawn chance zero or AI count zero.",_placeName]; + }; + + call { + if (_placeType isEqualTo "namecitycapital") exitWith { + _respawnLimit = A3XAI_respawnLimit_capitalCity; + }; + if (_placeType isEqualTo "namecity") exitWith { + _respawnLimit = A3XAI_respawnLimit_city; + }; + if (_placeType isEqualTo "namevillage") exitWith { + _respawnLimit = A3XAI_respawnLimit_village; + }; + if (_placeType isEqualTo "namelocal") exitWith { + _respawnLimit = A3XAI_respawnLimit_remoteArea; + }; + }; + + _trigger setVariable ["respawnLimit",_respawnLimit]; + _trigger setVariable ["respawnLimitOriginal",_respawnLimit]; + _trigger setVariable ["A3XAI_static_spawn",true]; + 0 = [0,_trigger,[],_patrolRadius,_unitLevel,_nearbldgs,_aiCount,_spawnChance] call A3XAI_initializeTrigger; + A3XAI_staticSpawnObjects pushBack _trigger; + } catch { + if (A3XAI_debugLevel > 0) then {diag_log _exception;}; + }; + if ((_forEachIndex % 5) isEqualTo 0) then {uiSleep 0.25;}; +} forEach A3XAI_manualStaticSpawnLocations; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 has finished generating %2 static spawns in %3 seconds.",__FILE__,(count A3XAI_staticSpawnObjects),(diag_tickTime - _startTime)];}; diff --git a/A3XAI/scripts/A3XAI_serverMonitor.sqf b/A3XAI/scripts/A3XAI_serverMonitor.sqf new file mode 100644 index 0000000..b6c1632 --- /dev/null +++ b/A3XAI/scripts/A3XAI_serverMonitor.sqf @@ -0,0 +1,258 @@ +#include "\A3XAI\globaldefines.hpp" + +if (A3XAI_debugLevel > 0) then {diag_log "A3XAI Server Monitor will start in 60 seconds."}; + +//Initialize timer variables +_currentTime = diag_tickTime; +_cleanDead = _currentTime; +_monitorReport = _currentTime; +_deleteObjects = _currentTime; +_dynLocations = _currentTime; +_checkRandomSpawns = _currentTime - MONITOR_CHECK_RANDSPAWN_FREQ; +_sideCheck = _currentTime; +_playerCountTime = _currentTime - MONITOR_UPDATE_PLAYER_COUNT_FREQ; + +//Setup variables +_currentPlayerCount = 0; +_lastPlayerCount = 0; +_multiplierLowPlayers = 0; +_multiplierHighPlayers = 0; +_maxSpawnChancePlayers = (A3XAI_playerCountThreshold max 1); + +if (A3XAI_upwardsChanceScaling) then { + _multiplierLowPlayers = A3XAI_chanceScalingThreshold; + _multiplierHighPlayers = 1; +} else { + _multiplierLowPlayers = 1; + _multiplierHighPlayers = A3XAI_chanceScalingThreshold; +}; + +A3XAI_externalObjectMonitor = missionNamespace getVariable [EXTERNAL_OBJECT_MONITOR_NAME,[]]; + +//Local functions +_getUptime = { + private ["_currentSec","_outSec","_outMin","_outHour"]; + _currentSec = diag_tickTime; + _outHour = (floor (_currentSec/3600)); + _outMin = (floor ((_currentSec - (_outHour*3600))/60)); + _outSec = (floor (_currentSec - (_outHour*3600) - (_outMin*60))); + + _outHour = str (_outHour); + _outMin = str (_outMin); + _outSec = str (_outSec); + + if ((count _outHour) isEqualTo 1) then {_outHour = format ["0%1",_outHour];}; + if ((count _outMin) isEqualTo 1) then {_outMin = format ["0%1",_outMin];}; + if ((count _outSec) isEqualTo 1) then {_outSec = format ["0%1",_outSec];}; + + [_outHour,_outMin,_outSec] +}; + +_fnc_purgeAndDelete = { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Purging a %1 from A3XAI_monitoredObjects.",(typeOf _this)];}; + {_this removeAllEventHandlers _x} count ["Killed","HandleDamage","GetIn","GetOut","Fired","Local","Hit"]; + _this removeAllMPEventHandlers "MPKilled"; + _index = A3XAI_monitoredObjects find _this; + if (_index > -1) then {A3XAI_monitoredObjects deleteAt _index;}; + _index = A3XAI_externalObjectMonitor find _this; + if (_index > -1) then {A3XAI_externalObjectMonitor deleteAt _index;}; + deleteVehicle _this; + true +}; + +//Script handles +_cleanupMain = scriptNull; +_cleanupLocations = scriptNull; +_cleanupRandomSpawns = scriptNull; + +_canKillCleanupMain = false; +_canKillCleanupLocations = false; +_canKillRandomSpawns = false; + +uiSleep 60; + +while {true} do { + //Main cleanup loop + _currentTime = diag_tickTime; + A3XAI_currentFPS = diag_fps; + + if ((_currentTime - _cleanDead) > MONITOR_CLEANDEAD_FREQ) then { + if (scriptDone _cleanupMain) then { + if (_canKillCleanupMain) then {_canKillCleanupMain = false;}; + _cleanupMain = [_currentTime,_fnc_purgeAndDelete] spawn { + _currentTime = _this select 0; + _fnc_purgeAndDelete = _this select 1; + + //Clean abandoned AI vehicles + { + call { + if (_x isKindOf "i_survivor_F") exitWith { + if !(alive _x) then { + _deathTime = _x getVariable "A3XAI_deathTime"; + if (!isNil "_deathTime") then { + if ((_currentTime - _deathTime) > A3XAI_cleanupDelay) then { + if (({isPlayer _x} count (_x nearEntities [[PLAYER_UNITS,"Air","LandVehicle"],30])) isEqualTo 0) then { + _x call _fnc_purgeAndDelete; + }; + }; + } else { + _x setVariable ["A3XAI_deathTime",_currentTime]; + }; + }; + }; + if (_x isKindOf "I_UAV_AI") exitWith { + if !(alive _x) then { + _x call _fnc_purgeAndDelete; + }; + }; + if (_x isKindOf "AllVehicles") exitWith { + _deathTime = _x getVariable "A3XAI_deathTime"; + if (!isNil "_deathTime") then { + if ((_currentTime - _deathTime) > A3XAI_vehicleDespawnTime) then { + if (({isPlayer _x} count (_x nearEntities [[PLAYER_UNITS,"Air","LandVehicle"],60])) isEqualTo 0) then { + if (({alive _x} count (crew _x)) isEqualTo 0) then { + { + if !(alive _x) then { + _x call _fnc_purgeAndDelete; + }; + } forEach (crew _x); + _x call _fnc_purgeAndDelete; + }; + }; + }; + } else { + if !(alive _x) then { + _x setVariable ["A3XAI_deathTime",_currentTime]; + }; + }; + }; + }; + uiSleep 0.025; + } count A3XAI_monitoredObjects; + }; + } else { + if (_canKillCleanupMain) then { + terminate _cleanupMain; + diag_log "A3XAI terminated previous cleanupMain thread."; + } else { + _canKillCleanupMain = true; + diag_log "A3XAI marked current cleanupMain thread for termination."; + }; + }; + _cleanDead = _currentTime; + }; + + //Main location cleanup loop + if ((_currentTime - _dynLocations) > MONITOR_LOCATIONCLEANUP_FREQ) then { + if (scriptDone _cleanupLocations) then { + if (_canKillCleanupLocations) then {_canKillCleanupLocations = false;}; + _cleanupLocations = [_currentTime] spawn { + _currentTime = _this select 0; + A3XAI_areaBlacklists = A3XAI_areaBlacklists - [locationNull]; + { + _deletetime = _x getVariable "deletetime"; + if (isNil "_deleteTime") then {_deleteTime = _currentTime}; //since _x getVariable ["deletetime",_currentTime] gives an error... + //diag_log format ["DEBUG: CurrentTime: %1. Delete Time: %2",_currentTime,_deletetime]; + if (_currentTime > _deletetime) then { + deleteLocation _x; + A3XAI_areaBlacklists deleteAt _forEachIndex; + }; + uiSleep 0.025; + } forEach A3XAI_areaBlacklists; + }; + } else { + if (_canKillCleanupLocations) then { + terminate _cleanupLocations; + diag_log "A3XAI terminated previous cleanupLocations thread."; + } else { + _canKillCleanupLocations = true; + diag_log "A3XAI marked current cleanupLocations thread for termination."; + }; + }; + _dynLocations = _currentTime; + }; + + if ((_currentTime - _checkRandomSpawns) > MONITOR_CHECK_RANDSPAWN_FREQ) then { + if (scriptDone _cleanupRandomSpawns) then { + if (_canKillRandomSpawns) then {_canKillRandomSpawns = false;}; + _cleanupRandomSpawns = [_currentTime] spawn { + _currentTime = _this select 0; + A3XAI_randomTriggerArray = A3XAI_randomTriggerArray - [objNull]; + { + if (((_x getVariable ["GroupArray",[]]) isEqualTo []) && {(_currentTime - (_x getVariable ["timestamp",_currentTime])) > RANDSPAWN_EXPIRY_TIME}) then { + _triggerLocation = _x getVariable ["triggerLocation",locationNull]; + deleteLocation _triggerLocation; + if (A3XAI_enableDebugMarkers) then { + _marker = _x getVariable ["MarkerName",""]; + deleteMarker _marker; + }; + deleteVehicle _x; + A3XAI_randomTriggerArray deleteAt _forEachIndex; + }; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; + } forEach A3XAI_randomTriggerArray; + _spawnsAvailable = A3XAI_maxRandomSpawns - (count A3XAI_randomTriggerArray); + if (_spawnsAvailable > 0) then { + _nul = _spawnsAvailable spawn A3XAI_setup_randomspawns; + }; + }; + } else { + if (_canKillRandomSpawns) then { + terminate _cleanupRandomSpawns; + diag_log "A3XAI terminated previous cleanupRandomSpawns thread."; + } else { + _canKillRandomSpawns = true; + diag_log "A3XAI marked current cleanupRandomSpawns thread for termination."; + }; + }; + _checkRandomSpawns = _currentTime; + }; + + if ((_currentTime - _playerCountTime) > MONITOR_UPDATE_PLAYER_COUNT_FREQ) then { + _currentPlayerCount = ({alive _x} count allPlayers); + if (A3XAI_HCIsConnected) then {_currentPlayerCount = _currentPlayerCount - 1}; + if !(_lastPlayerCount isEqualTo _currentPlayerCount) then { + A3XAI_spawnChanceMultiplier = linearConversion [1, _maxSpawnChancePlayers, _currentPlayerCount, _multiplierLowPlayers, _multiplierHighPlayers, true]; + _lastPlayerCount = _currentPlayerCount; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Updated A3XAI_spawnChanceMultiplier to %1",A3XAI_spawnChanceMultiplier];}; + }; + _playerCountTime = _currentTime; + }; + + //Check for unwanted side modifications + if ((_currentTime - _sideCheck) > SIDECHECK_TIME) then { + if !((PLAYER_GROUP_SIDE getFriend A3XAI_side) isEqualTo 0) then {PLAYER_GROUP_SIDE setFriend [A3XAI_side, 0]}; + if !((A3XAI_side getFriend PLAYER_GROUP_SIDE) isEqualTo 0) then {A3XAI_side setFriend [PLAYER_GROUP_SIDE, 0]}; + if !((A3XAI_side getFriend A3XAI_side) isEqualTo 1) then {A3XAI_side setFriend [A3XAI_side, 1]}; + _sideCheck = _currentTime; + }; + + if (A3XAI_enableDebugMarkers) then { + { + if (_x in allMapMarkers) then { + _x setMarkerPos (getMarkerPos _x); + } else { + A3XAI_mapMarkerArray set [_forEachIndex,""]; + }; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; + } forEach A3XAI_mapMarkerArray; + A3XAI_mapMarkerArray = A3XAI_mapMarkerArray - [""]; + }; + + A3XAI_activeGroups = A3XAI_activeGroups - [grpNull]; + _activeGroupAmount = format ["%1/%2",{(_x getVariable ["GroupSize",0]) > 0} count A3XAI_activeGroups,count A3XAI_activeGroups]; + if (A3XAI_debugLevel > 1) then { + _allAIGroups = format [" (Total: %1)",{(side _x) isEqualTo A3XAI_side} count allGroups]; + _activeGroupAmount = _activeGroupAmount + _allAIGroups; + }; + + //Report statistics to RPT log + if ((A3XAI_monitorReportRate > 0) && {((_currentTime - _monitorReport) > A3XAI_monitorReportRate)}) then { + _uptime = [] call _getUptime; + diag_log format ["[A3XAI Monitor] [Uptime:%1:%2:%3][FPS:%4][Groups:%5][Respawn:%6][HC:%7]",_uptime select 0, _uptime select 1, _uptime select 2,round A3XAI_currentFPS,_activeGroupAmount,(count A3XAI_respawnQueue),A3XAI_HCIsConnected]; + diag_log format ["[A3XAI Monitor] [Static:%1][Dynamic:%2][Random:%3][Air:%4][Land:%5][UAV:%6][UGV:%7]",(count A3XAI_staticTriggerArray),(count A3XAI_dynamicTriggerArray),(count A3XAI_randomTriggerArray),A3XAI_curHeliPatrols,A3XAI_curLandPatrols,A3XAI_curUAVPatrols,A3XAI_curUGVPatrols]; + _monitorReport = _currentTime; + }; + + uiSleep 30; +}; diff --git a/A3XAI/scripts/A3XAI_spawnManagerUniversal.sqf b/A3XAI/scripts/A3XAI_spawnManagerUniversal.sqf new file mode 100644 index 0000000..cb4a36c --- /dev/null +++ b/A3XAI/scripts/A3XAI_spawnManagerUniversal.sqf @@ -0,0 +1,161 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_currentTime", "_staticSpawnTime", "_randomSpawnTime", "_dynamicSpawnTime", "_trigger", "_spawnTriggered", +"_noGroupsSpawned", "_aiCount", "_minAI", "_maxAI", "_patrolDist", "_unitLevel", "_numGroups", "_result", "_trigger", +"_nearbyEntities", "_spawnChance"]; + +//Timers +_currentTime = diag_tickTime; +_staticSpawnTime = _currentTime; +_randomSpawnTime = _currentTime; +_dynamicSpawnTime = _currentTime; + +diag_log "Starting universal spawn manager."; + +while {true} do { + _currentTime = diag_tickTime; + + if ((_currentTime - _staticSpawnTime) > STATIC_SPAWN_CHECK_FREQ) then { + diag_log "Debug: Checking static spawn areas."; + { + _scriptHandle = [_x,_forEachIndex] spawn { + _trigger = _this select 0; + _index = _this select 1; + call { + if (isNull _trigger) exitWith { + A3XAI_staticSpawnObjects deleteAt _index; + }; + + _spawnTriggered = (({if (isPlayer _x) exitWith {1}} count (_trigger nearEntities [[PLAYER_UNITS,"LandVehicle","Air"],TRIGGER_SIZE_STATIC])) isEqualTo 1); + _noGroupsSpawned = (_trigger getVariable ["GroupArray",[]] isEqualTo []); + + if (_spawnTriggered && {_noGroupsSpawned}) exitWith { + //If spawn area triggered and no groups spawned yet... spawn a group + if (A3XAI_currentFPS > A3XAI_minFPS) then { + diag_log format ["Lockoutvar: %1, Lockoutcalc: %2, Lockouttime: %3",(_trigger getVariable ["LockoutTime",-A3XAI_staticLockoutTime]),(diag_tickTime - (_trigger getVariable ["LockoutTime",-A3XAI_staticLockoutTime])),A3XAI_staticLockoutTime]; + if ((diag_tickTime - (_trigger getVariable ["LockoutTime",-A3XAI_staticLockoutTime])) > A3XAI_staticLockoutTime) then { + _aiCount = _trigger getVariable ["maxUnits",[1,0]]; + _patrolDist = _trigger getVariable ["patrolDist",125]; + _unitLevel = _trigger getVariable ["unitLevel",1]; + _numGroups = 1; + _result = [_aiCount select 0,_aiCount select 1,_patrolDist,_trigger,_unitLevel,_numGroups] call A3XAI_spawnUnits_static; + if (_result) then { + [_trigger,"A3XAI_staticTriggerArray",true] call A3XAI_updateSpawnCount; + A3XAI_activePlayerAreas pushBackUnique _trigger; + if ("LockoutTime" in allVariables _trigger) then { + _trigger setVariable ["LockoutTime",nil]; + }; + if (A3XAI_enableDebugMarkers) then { + _nul = [_trigger,str(_trigger)] call A3XAI_addMapMarker; + _marker = str(_trigger); + _marker setMarkerColor "ColorRed"; + _marker setMarkerAlpha 0.9; + // diag_log format ["Debug: Marker String %1",(str _trigger)]; + }; + } else { + _trigger setVariable ["LockoutTime",diag_tickTime]; + diag_log format ["Lockout timer for static spawn %1 set to %2 seconds.",(_trigger getVariable ["TriggerText","Unknown Spawn"]),A3XAI_staticLockoutTime]; + }; + } else { + diag_log format ["Static spawn %1 is still under lockdown timer.",_trigger getVariable ["TriggerText","Unknown Spawn"]]; + }; + } else { + diag_log "Server FPS below required FPS for spawning AI groups."; + }; + }; + if (!_spawnTriggered && {!_noGroupsSpawned}) exitWith { + //If spawn area is not triggered and groups exist... despawn spawned groups + _result = [_trigger] call A3XAI_despawnUniversal; + if (_result) then { + [_trigger,"A3XAI_staticTriggerArray",false] call A3XAI_updateSpawnCount; + A3XAI_activePlayerAreas = A3XAI_activePlayerAreas - [_trigger,objNull]; + }; + }; + }; + }; + waitUntil {uiSleep 0.1; scriptDone _scriptHandle}; + _staticSpawnTime = _currentTime; + } forEach A3XAI_staticSpawnObjects; + }; + + if ((_currentTime - _randomSpawnTime) > RANDOM_SPAWN_CHECK_FREQ) then { + diag_log "Debug: Checking random spawn areas."; + { + _scriptHandle = [_x,_forEachIndex] spawn { + _trigger = _this select 0; + _index = _this select 1; + call { + if (isNull _trigger) exitWith { + A3XAI_randomTriggerArray deleteAt _index; + }; + + _nearbyEntities = (_trigger nearEntities [[PLAYER_UNITS,"LandVehicle","Air"],TRIGGER_SIZE_RANDOM]); + _spawnTriggered = (({if (isPlayer _x) exitWith {1}} count _nearbyEntities) isEqualTo 1); + _noGroupsSpawned = (_trigger getVariable ["GroupArray",[]] isEqualTo []); + + if (_spawnTriggered && {_noGroupsSpawned}) exitWith { + //If spawn area triggered and no groups spawned yet... spawn a group + if ((A3XAI_currentFPS > A3XAI_minFPS) && {!(_trigger getVariable ["LockedOut",false])}) then { + _aiCount = _trigger getVariable ["maxUnits",[1,0]]; + _patrolDist = _trigger getVariable ["patrolDist",125]; + _unitLevel = _trigger getVariable ["unitLevel",1]; + _numGroups = 1; + _spawnChance = _trigger getVariable ["SpawnChance",1]; + _result = [_aiCount select 0,_aiCount select 1,_patrolDist,_trigger,_nearbyEntities,_spawnChance,_unitLevel,_numGroups] call A3XAI_spawnUnits_random; //Check if input is correct + if (_result) then { + [_trigger,"A3XAI_randomTriggerArray",true] call A3XAI_updateSpawnCount; + _trigger setVariable ["LockedOut",true]; + if (A3XAI_enableDebugMarkers) then { + _marker = str(_trigger); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; + diag_log format ["Debug: Marker String %1",(str _trigger)]; + }; + } else { + A3XAI_randomTriggerArray deleteAt _index; + }; + } else { + diag_log "Server FPS below required FPS for spawning AI groups."; + }; + }; + if (!_spawnTriggered && {!_noGroupsSpawned}) exitWith { + //If spawn area is not triggered and groups exist... despawn spawned groups + _result = [_trigger] call A3XAI_despawnUniversal; + if (_result) then { + [_trigger,"A3XAI_randomTriggerArray",false] call A3XAI_updateSpawnCount; + }; + }; + }; + }; + waitUntil {uiSleep 0.1; scriptDone _scriptHandle}; + _randomSpawnTime = _currentTime; + } forEach A3XAI_randomTriggerArray; + }; + + if ((_currentTime - _dynamicSpawnTime) > DYNAMIC_SPAWN_CHECK_FREQ) then { + diag_log "Debug: Checking dynamic spawn areas."; + { + _scriptHandle = [_x,_forEachIndex] spawn { + _trigger = _this select 0; + _index = _this select 1; + call { + if (isNull _trigger) exitWith { + A3XAI_dynamicTriggerArray deleteAt _index; + }; + + _spawnTriggered = (({if (isPlayer _x) exitWith {1}} count (_trigger nearEntities [[PLAYER_UNITS,"LandVehicle","Air"],TRIGGER_SIZE_DYNAMIC])) isEqualTo 1); + if !(_spawnTriggered) then { + _result = [_trigger] call A3XAI_despawnUniversal; + if (_result) then { + [_trigger,"A3XAI_dynamicTriggerArray",false] call A3XAI_updateSpawnCount; + }; + }; + }; + }; + waitUntil {uiSleep 0.1; scriptDone _scriptHandle}; + _dynamicSpawnTime = _currentTime; + } forEach A3XAI_dynamicTriggerArray; + }; + + uiSleep 30; +}; diff --git a/A3XAI/scripts/buildWeightedTables.sqf b/A3XAI/scripts/buildWeightedTables.sqf new file mode 100644 index 0000000..acdecee --- /dev/null +++ b/A3XAI/scripts/buildWeightedTables.sqf @@ -0,0 +1,63 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_startTime","_getWeightedIndices"]; + +_startTime = diag_tickTime; + +if ((typeName _this) != "ARRAY") exitWith {diag_log format ["%1 was given non-array input.",__FILE__];}; + +//Function exerpt from fn_selectRandomWeighted.sqf written by Joris-Jan van 't Land +_getWeightedIndices = { + private ["_array", "_weights","_index","_weighted","_i"]; + _array = _this select 0; + _weights = _this select 1; + + //Parameter validation. + if ((typeName _array) != (typeName [])) exitWith {debugLog "Log: [selectRandomWeighted] Array (0) must be an Array!"; nil}; + if ((typeName _weights) != (typeName [])) exitWith {debugLog "Log: [selectRandomWeighted] Weights (1) must be an Array!"; nil}; + if ((count _array) > (count _weights)) exitWith {debugLog "Log: [selectRandomWeighted] There must be at least as many elements in Weights (1) as there are in Array (0)!"; nil}; + + //Created weighted array of indices. + private ["_weighted"]; + _weighted = []; + for "_i" from 0 to ((count _weights) - 1) do + { + private ["_weight"]; + _weight = _weights select _i; + + //Ensure the weight is a Number. + //If it's not, set weight to 0 to exclude it. + if ((typeName _weight) != (typeName 0)) then {debugLog "Log: [selectRandomWeighted] Weights should be Numbers; weight set to 0!"; _weight = 0}; + + //The weight should be a Number between 0 and 1. + if (_weight < 0) then {debugLog "Log: [selectRandomWeighted] Weights should be more than or equal to 0; weight set to 0!"; _weight = 0}; + //if (_weight > 1) then {debugLog "Log: [selectRandomWeighted] Weights should be less than or equal to 1; weight set to 1!"; _weight = 1}; + + //Normalize the weight for a precision of hundreds. + _weight = round(_weight * 100); + + for "_k" from 0 to (_weight - 1) do + { + //_weighted = _weighted + [_i]; + //_weighted set [count _weighted,_i]; + _weighted pushBack _i; + }; + }; + + _weighted +}; + +{ + private ["_weightedTable","_gradeChances"]; + _gradeChances = missionNamespace getVariable (_x select 0); + if !(isNil "_gradeChances") then { + _weightedTable = [A3XAI_unitLevels,_gradeChances] call _getWeightedIndices; + missionNamespace setVariable [_x select 1,_weightedTable]; + missionNamespace setVariable [_x select 0,nil]; + }; +} count _this; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: A3XAI finished building weighted unitLevel tables in %1 seconds.",(diag_tickTime - _startTime)]}; + +true + diff --git a/A3XAI/scripts/dynamicSpawn_manager.sqf b/A3XAI/scripts/dynamicSpawn_manager.sqf new file mode 100644 index 0000000..841f794 --- /dev/null +++ b/A3XAI/scripts/dynamicSpawn_manager.sqf @@ -0,0 +1,154 @@ +#include "\A3XAI\globaldefines.hpp" + +if (A3XAI_debugLevel > 0) then {diag_log "Starting A3XAI Dynamic Spawn Manager in 1 minute.";}; +uiSleep 60; +if (A3XAI_debugLevel > 0) then {diag_log "A3XAI V3 Dynamic Spawn Manager started.";}; + +//Spawn manager database variables +_playerUID_DB = []; //Database of all collected playerUIDs +_lastSpawned_DB = []; //Database of timestamps for each corresponding playerUID +_lastOnline_DB = []; //Database of last online checks + +while {true} do { + try { + _allPlayers = []; //Do not edit + _playerCount = 0; + _currentTime = diag_tickTime; + { + if ((isPlayer _x) && {((typeOf _x) in [PLAYER_UNITS])}) then { + _playerUID = getPlayerUID _x; + if ((_playerUID select [0,2]) != "HC") then { + _playerIndex = _playerUID_DB find _playerUID; + if (_playerIndex > -1) then { + _lastSpawned = _lastSpawned_DB select _playerIndex; + _timePassed = (_currentTime - _lastSpawned); + if (_timePassed > A3XAI_timePerDynamicSpawn) then { + if ((_currentTime - (_lastOnline_DB select _playerIndex)) < A3XAI_purgeLastDynamicSpawnTime) then { + _allPlayers pushBack _x; + _playerCount = _playerCount + 1; + //diag_log format ["DEBUG: Player %1 added to current cycle dynamic spawn list.",_x]; + }; + _lastOnline_DB set [_playerIndex,_currentTime]; + } else { + if (_playerUID in A3XAI_failedDynamicSpawns) then { + _allPlayers pushBack _x; + _playerCount = _playerCount + 1; + //diag_log format ["DEBUG: Player %1 added to current cycle dynamic spawn list.",_x]; + A3XAI_failedDynamicSpawns = A3XAI_failedDynamicSpawns - [_playerUID]; + }; + }; + } else { + _playerUID_DB pushBack _playerUID; + _lastSpawned_DB pushBack _currentTime - DYNSPAWNMGR_SLEEP_DELAY; + _lastOnline_DB pushBack _currentTime; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 added to dynamic spawn playerUID database.",_x];}; + }; + //diag_log format ["DEBUG: Found a player at %1 (%2).",mapGridPosition _x,name _x]; + }; + }; + uiSleep 0.05; + } count allPlayers; + + if (_allPlayers isEqualTo []) then { + throw "A3XAI Debug: No players online. Dynamic spawn manager is entering waiting state."; + }; + + if (A3XAI_currentFPS < A3XAI_minFPS) then { + throw format ["A3XAI Debug: Current server FPS %1 is lower than minimum threshold of %2. Dynamic spawn manager is entering waiting state.",A3XAI_currentFPS,A3XAI_minFPS]; + }; + + _activeDynamicSpawns = (count A3XAI_dynamicTriggerArray); + _maxSpawnsPossible = (_playerCount min A3XAI_maxDynamicSpawns); //Can't have more spawns than players (doesn't count current number of dynamic spawns) + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Preparing to create %1 dynamic spawns (Players: %2, Dynamic Spawns: %3).",(_maxSpawnsPossible - _activeDynamicSpawns),_playerCount,_activeDynamicSpawns];}; + + while {_allPlayers = _allPlayers - [objNull]; (((_maxSpawnsPossible - _activeDynamicSpawns) > 0) && {!(_allPlayers isEqualTo [])})} do { //_spawns: Have we created enough spawns? _allPlayers: Are there enough players to create spawns for? + _time = diag_tickTime; + _player = _allPlayers call A3XAI_selectRandom; + _playerUID = (getPlayerUID _player); + if (alive _player) then { + _playername = name _player; + _index = _playerUID_DB find _playerUID; + _playerPos = getPosATL _player; + _spawnParams = _playerPos call A3XAI_getSpawnParams; + _spawnChance = _spawnParams select 3; + _chanceModifier = 1.00; + call { + if (_spawnChance isEqualTo 0) exitWith {}; + if (_player getVariable ["ExileIsBambi",false]) exitWith { + _spawnChance = 0; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is bambi. Dynamic spawn chance set to 0.",_player];}; + }; + if ((vehicle _player) isKindOf "Air") exitWith { + _spawnChance = 0; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is in Air vehicle. Dynamic spawn chance set to 0.",_player];}; + }; + if (({if (_playerPos in _x) exitWith {1}} count (nearestLocations [_playerPos,[BLACKLIST_OBJECT_GENERAL,BLACKLIST_OBJECT_DYNAMIC],1500])) > 0) exitWith { + _spawnChance = 0; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is in blacklisted area. Dynamic spawn chance set to 0.",_player];}; + }; + if !((_playerPos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo []) exitWith { + _spawnChance = 0; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is near pole object. Dynamic spawn chance set to 0.",_player];}; + }; + if (currentWeapon _player isEqualTo "") then { + _chanceModifier = _chanceModifier + DYNAMIC_CHANCE_ADJUST_UNARMED; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is unarmed. Unadjusted dynamic spawn chance set to %2.",_player,_chanceModifier];}; + }; + if ((damage _player) > DYNAMIC_WEAK_PLAYER_HEALTH) then { + _chanceModifier = _chanceModifier + DYNAMIC_CHANCE_ADJUST_WEAKENED; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is weakened. Unadjusted dynamic spawn chance set to %2.",_player,_chanceModifier];}; + }; + if !((_playerPos nearObjects [LOOT_HOLDER_CLASS,DYNAMIC_LOOTING_DISTANCE]) isEqualTo []) then { + _chanceModifier = _chanceModifier + DYNAMIC_CHANCE_ADJUST_LOOTING; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is looting. Unadjusted dynamic spawn chance set to %2.",_player,_chanceModifier];}; + }; + if ((_player getVariable ["ExileMoney",0]) > DYNAMIC_RICH_PLAYER_AMOUNT) then { + _chanceModifier = _chanceModifier + DYNAMIC_CHANCE_ADJUST_WEALTHY; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Player %1 is wealthy. Unadjusted dynamic spawn chance set to %2.",_player,_chanceModifier];}; + }; + if (_chanceModifier < 0) then {_chanceModifier = 0;}; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Dynamic spawn probabilities for %1: Base: %2, Modifier: %3, A3XAI_spawnChanceMultiplier: %4",_player,_spawnChance,_chanceModifier,A3XAI_spawnChanceMultiplier];}; + _spawnChance = (_spawnChance * _chanceModifier * A3XAI_spawnChanceMultiplier); + }; + if (_spawnChance call A3XAI_chance) then { + _lastSpawned_DB set [_index,diag_tickTime]; + _trigger = TRIGGER_OBJECT createVehicleLocal _playerPos; + _location = [_playerPos,TEMP_BLACKLIST_AREA_DYNAMIC_SIZE] call A3XAI_createBlackListAreaDynamic; + _trigger setVariable ["triggerLocation",_location]; + _trigger setVariable ["TriggerText",(format ["Dynamic Spawn (Triggered by: %1)",_playername])]; + _trigger setVariable ["targetplayer",_player]; + _trigger setVariable ["targetplayerUID",_playerUID]; + if (A3XAI_enableDebugMarkers) then { + _marker = str(_trigger); + if (_marker in allMapMarkers) then {deleteMarker _marker}; + _marker = createMarker[_marker,(getPosASL _trigger)]; + _marker setMarkerShape "ELLIPSE"; + _marker setMarkerType "Flag"; + _marker setMarkerBrush "SOLID"; + _marker setMarkerSize [TRIGGER_SIZE_DYNAMIC, TRIGGER_SIZE_DYNAMIC]; + _marker setMarkerAlpha 0; + _trigger setVariable ["MarkerName",_marker]; + }; + 0 = [PATROL_DIST_DYNAMIC,_trigger,_spawnParams select 0,_spawnParams select 1,_spawnParams select 2] call A3XAI_spawnUnits_dynamic; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created dynamic trigger at %1 with params %2. Triggered by player: %3.",(mapGridPosition _trigger),_spawnParams,_playername];}; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Dynamic spawn probability check failed for player %1 (Probability: %2).",_playername,_spawnChance];}; + }; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Cancel dynamic spawn check for player %1 (Reason: Player not in suitable state).",_player]}; + }; + _allPlayers = _allPlayers - [_player]; + _activeDynamicSpawns = _activeDynamicSpawns + 1; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Processed a spawning probability check in %1 seconds.",diag_tickTime - _time]}; + uiSleep 5; + }; + } catch { + if (A3XAI_debugLevel > 1) then { + diag_log _exception; + }; + }; + + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Dynamic spawn manager is sleeping for %1 seconds.",DYNSPAWNMGR_SLEEP_DELAY];}; + uiSleep DYNSPAWNMGR_SLEEP_DELAY; +}; diff --git a/A3XAI/scripts/setup_locations.sqf b/A3XAI/scripts/setup_locations.sqf new file mode 100644 index 0000000..e905e51 --- /dev/null +++ b/A3XAI/scripts/setup_locations.sqf @@ -0,0 +1,121 @@ +#include "\A3XAI\globaldefines.hpp" + +private ["_cfgWorldName","_startTime","_allPlaces","_allLocations","_traderCityPositions" +,"_traderCityMarkers","_createTriggerArea"]; + +_startTime = diag_tickTime; +_allPlaces = []; +_allLocations = []; +_cfgWorldName = configFile >> "CfgWorlds" >> worldName >> "Names"; +_createTriggerArea = (A3XAI_enableStaticSpawns && {!A3XAI_useManualStaticSpawns or {A3XAI_manualStaticSpawnLocations isEqualTo []}}); + +for "_i" from 0 to ((count _cfgWorldName) -1) do { + _allPlaces set [(count _allPlaces),configName (_cfgWorldName select _i)]; + //diag_log format ["DEBUG :: Added location %1 to allPlaces array.",configName (_cfgWorldName select _i)]; +}; + +//Add user-specified blacklist areas +{ + A3XAI_waypointBlacklistAir set [_forEachIndex,(toLower _x)]; //Ensure case-insensitivity + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created AI vehicle waypoint blacklist at %1.",_x];}; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; +} forEach A3XAI_waypointBlacklistAir; + +{ + A3XAI_waypointBlacklistLand set [_forEachIndex,(toLower _x)]; //Ensure case-insensitivity + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created AI vehicle waypoint blacklist at %1.",_x];}; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; +} forEach A3XAI_waypointBlacklistLand; + +//Set up trader city blacklist areas +_traderCityPositions = []; +call { + { + if ((triggerStatements _x) isEqualTo ["(vehicle player) in thisList","call ExileClient_object_player_event_onEnterSafezone","call ExileClient_object_player_event_onLeaveSafezone"]) then { + _traderCityPositions pushBack [getPosATL _x, (triggerArea _x) select 0]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Found trader safezone at %1",getPosATL _x];}; + }; + } forEach (allMissionObjects "EmptyDetector"); + + if (_traderCityPositions isEqualTo []) then { + { + if ((markerType _x) isEqualTo "ExileTraderZone") then { + _traderCityPositions pushBack [getMarkerPos _x, (markerSize _x) select 0]; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Found trader marker %1",_x];}; + }; + } forEach allMapMarkers; + }; + + { + call { + if ((typeName _x) != "ARRAY") exitWith {diag_log "A3XAI Error: Non-array value found in A3XAI_traderAreaLocations";}; + if ((count _x) < 2) exitWith {diag_log "A3XAI Error: Array value with fewer than 2 elements found in A3XAI_traderAreaLocations";}; + _traderCityPositions pushBack [_x, NO_AGGRO_AREA_SIZE]; //To do: Custom no-aggro area size + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Found trader location at %1",_x];}; + }; + } forEach A3XAI_traderAreaLocations; + + if (_traderCityPositions isEqualTo []) then { + diag_log "A3XAI Warning: Could not automatically detect trader safezones and no manually defined positions found in A3XAI_traderAreaLocations."; + }; +}; + +{ + _position = _x select 0; + _radius = _x select 1; + _location = [_position,_radius] call A3XAI_createNoAggroArea; + _location = [_position,BLACKLIST_AREA_SIZE] call A3XAI_createBlackListArea; + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Created %1m radius blacklist area and %2m radius no-aggro area at trader city position %3.",BLACKLIST_AREA_SIZE,_radius,_position];}; +} forEach _traderCityPositions; + +{ + _placeType = toLower (getText (_cfgWorldName >> _x >> "type")); + if (_placeType in ["namecitycapital","namecity","namevillage","namelocal"]) then { + _placeName = getText (_cfgWorldName >> _x >> "name"); + _placePos = getArray (_cfgWorldName >> _x >> "position"); + _isAllowedPos = (({(_placePos distance2D _x) < NO_AGGRO_AREA_SIZE} count A3XAI_noAggroAreas) isEqualTo 0); + if (_isAllowedPos) then { + _trigger = objNull; + if (_createTriggerArea) then { + _trigger = [_placePos,_placeName] call A3XAI_createTriggerArea; + }; + A3XAI_locations pushBack [_placeName,_placePos,_placeType,_trigger]; + if !(_placeName in A3XAI_waypointBlacklistAir) then {A3XAI_locationsAir pushBack [_placeName,_placePos,_placeType];}; + if !((_placeName in A3XAI_waypointBlacklistLand) && {!(surfaceIsWater _placePos)}) then {A3XAI_locationsLand pushBack [_placeName,_placePos,_placeType];}; + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: Added location %1 (type: %2, pos: %3) to location list.",_placeName,_placeType,_placePos];}; + } else { + if (A3XAI_debugLevel > 1) then {diag_log format ["A3XAI Debug: %1 not in allowed position. Blacklist (Air): %2, Blacklist (Land): %3. Trader: %4.",_placeName,!((toLower _placeName) in A3XAI_waypointBlacklistAir),!((toLower _placeName) in A3XAI_waypointBlacklistLand),(({(_placePos distance2D _x) < NO_AGGRO_AREA_SIZE} count A3XAI_noAggroAreas) isEqualTo 0)];}; + }; + _allLocations pushBack [_placeName,_placePos,_placeType]; + }; + if ((_forEachIndex % 10) isEqualTo 0) then {uiSleep 0.05}; +} forEach _allPlaces; + +//Auto-adjust random spawn limit +if (isDedicated && {A3XAI_maxRandomSpawns isEqualTo -1}) then { + A3XAI_maxRandomSpawns = ((round (0.125 * (count _allLocations)) min 20) max 5); + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Adjusted random spawn limit to %1",A3XAI_maxRandomSpawns];}; +}; + +if (A3XAI_locations isEqualTo []) then { + A3XAI_locations = +_allLocations; + if (A3XAI_debugLevel > 1) then {diag_log "A3XAI Debug: A3XAI_locations is empty, using _allLocations array instead.";}; +}; + +if (A3XAI_locationsAir isEqualTo []) then { + A3XAI_locationsAir = +_allLocations; + if (A3XAI_debugLevel > 1) then {diag_log "A3XAI Debug: A3XAI_locationsAir is empty, using _allLocations array instead.";}; +}; + +if (A3XAI_locationsLand isEqualTo []) then { + A3XAI_locationsLand = +_allLocations; + if (A3XAI_debugLevel > 1) then {diag_log "A3XAI Debug: A3XAI_locationsLand is empty, using _allLocations array instead.";}; +}; + +A3XAI_locations_ready = true; + +//Cleanup global vars +A3XAI_waypointBlacklistAir = nil; +A3XAI_waypointBlacklistLand = nil; + +if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Location configuration completed with %1 locations found in %2 seconds.",(count A3XAI_locations),(diag_tickTime - _startTime)]}; diff --git a/A3XAI/scripts/setup_veh_patrols.sqf b/A3XAI/scripts/setup_veh_patrols.sqf new file mode 100644 index 0000000..094289d --- /dev/null +++ b/A3XAI/scripts/setup_veh_patrols.sqf @@ -0,0 +1,161 @@ +#include "\A3XAI\globaldefines.hpp" +diag_log format ["Debug: FPS Limit %1",A3XAI_minFPS]; +waitUntil {uiSleep 0.3; (!isNil "A3XAI_locations_ready" && {!isNil "A3XAI_classnamesVerified"})}; + +if (A3XAI_maxAirPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3XAI_airVehicleList) - 1) do { + _currentElement = (A3XAI_airVehicleList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _heliType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_heliType,"vehicle"] call A3XAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3XAI_heliTypesUsable pushBack _heliType; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_heliType];}; + }; + } else { + diag_log "A3XAI Error: Non-array element found in A3XAI_airVehicleList. Please see default configuration file for proper format."; + }; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Assembled helicopter list: %1",A3XAI_heliTypesUsable];}; + + _maxHelis = (A3XAI_maxAirPatrols min (count A3XAI_heliTypesUsable)); + for "_i" from 1 to _maxHelis do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Air Patrol spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + _index = floor (random (count A3XAI_heliTypesUsable)); + _heliType = A3XAI_heliTypesUsable select _index; + _nul = _heliType spawn A3XAI_spawnVehiclePatrol; + A3XAI_heliTypesUsable deleteAt _index; + if (_i < _maxHelis) then {uiSleep 20}; + }; + }; + uiSleep 5; +}; + +if (A3XAI_maxLandPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3XAI_landVehicleList) - 1) do { + _currentElement = (A3XAI_landVehicleList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _vehType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_vehType,"vehicle"] call A3XAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3XAI_vehTypesUsable pushBack _vehType; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_vehType];}; + }; + } else { + diag_log "A3XAI Error: Non-array element found in A3XAI_landVehicleList. Please see default configuration file for proper format."; + }; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Assembled vehicle list: %1",A3XAI_vehTypesUsable];}; + + _maxVehicles = (A3XAI_maxLandPatrols min (count A3XAI_vehTypesUsable)); + for "_i" from 1 to _maxVehicles do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: Land Patrol spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + _index = floor (random (count A3XAI_vehTypesUsable)); + _vehType = A3XAI_vehTypesUsable select _index; + _nul = _vehType spawn A3XAI_spawnVehiclePatrol; + A3XAI_vehTypesUsable deleteAt _index; + if (_i < _maxVehicles) then {uiSleep 20}; + }; + }; +}; + +if (A3XAI_maxUAVPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3XAI_UAVList) - 1) do { + _currentElement = (A3XAI_UAVList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _vehType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_vehType,"vehicle"] call A3XAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3XAI_UAVTypesUsable pushBack _vehType; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_vehType];}; + }; + } else { + diag_log "A3XAI Error: Non-array element found in A3XAI_UAVList. Please see default configuration file for proper format."; + }; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Assembled UAV list: %1",A3XAI_UAVTypesUsable];}; + + _maxVehicles = (A3XAI_maxUAVPatrols min (count A3XAI_UAVTypesUsable)); + for "_i" from 1 to _maxVehicles do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: UAV Patrol spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + _index = floor (random (count A3XAI_UAVTypesUsable)); + _vehType = A3XAI_UAVTypesUsable select _index; + _vehicleClass = [configFile >> "CfgVehicles" >> _vehType,"vehicleClass",""] call BIS_fnc_returnConfigEntry; + _nul = _vehType spawn A3XAI_spawn_UV_patrol; + A3XAI_UAVTypesUsable deleteAt _index; + if (_i < _maxVehicles) then {uiSleep 20}; + }; + }; +}; + +if (A3XAI_maxUGVPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3XAI_UGVList) - 1) do { + _currentElement = (A3XAI_UGVList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _vehType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_vehType,"vehicle"] call A3XAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3XAI_UGVTypesUsable pushBack _vehType; + }; + } else { + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_vehType];}; + }; + } else { + diag_log "A3XAI Error: Non-array element found in A3XAI_UGVList. Please see default configuration file for proper format."; + }; + }; + + if (A3XAI_debugLevel > 0) then {diag_log format ["A3XAI Debug: Assembled UGV list: %1",A3XAI_UGVTypesUsable];}; + + _maxVehicles = (A3XAI_maxUGVPatrols min (count A3XAI_UGVTypesUsable)); + for "_i" from 1 to _maxVehicles do { + if (A3XAI_currentFPS < A3XAI_minFPS) then { + if (A3XAI_debugLevel > 0) then { + diag_log format ["A3XAI Debug: UGV Patrol spawning paused. Waiting for server FPS to be above minimum set value (Current: %1. Minimum: %2)",A3XAI_currentFPS,A3XAI_minFPS]; + }; + waitUntil {uiSleep 15; A3XAI_currentFPS > A3XAI_minFPS}; + }; + _index = floor (random (count A3XAI_UGVTypesUsable)); + _vehType = A3XAI_UGVTypesUsable select _index; + _nul = _vehType spawn A3XAI_spawn_UV_patrol; + A3XAI_UGVTypesUsable deleteAt _index; + if (_i < _maxVehicles) then {uiSleep 20}; + }; + }; +}; diff --git a/A3XAI/scripts/verifyClassnames.sqf b/A3XAI/scripts/verifyClassnames.sqf new file mode 100644 index 0000000..7a28a48 --- /dev/null +++ b/A3XAI/scripts/verifyClassnames.sqf @@ -0,0 +1,307 @@ +private["_verified","_errorFound","_startTime"]; + +_startTime = diag_tickTime; + +_verified = []; + +{ + _array = missionNamespace getVariable [_x,[]]; + _errorFound = false; + { + if !(_x in _verified) then { + call { + if ((isNil {_x}) or {!((typeName _x) isEqualTo "STRING")}) exitWith { + diag_log format ["A3XAI] Removing non-string item %1 from classname table.",_x]; + _array deleteAt _forEachIndex; + }; + if (isClass (configFile >> "CfgWeapons" >> _x)) exitWith { + if (((configName (inheritsFrom (configFile >> "CfgWeapons" >> _x))) isEqualTo "FakeWeapon") or {(getNumber (configFile >> "CfgWeapons" >> _x >> "scope")) isEqualTo 0}) then { + diag_log format ["[A3XAI] Removing invalid classname: %1.",_x]; + _array deleteAt _forEachIndex; + } else { + _verified pushBack _x; + }; + }; + if (isClass (configFile >> "CfgMagazines" >> _x)) exitWith { + if (((configName (inheritsFrom (configFile >> "CfgMagazines" >> _x))) isEqualTo "FakeMagazine") or {(getNumber (configFile >> "CfgMagazines" >> _x >> "scope")) isEqualTo 0}) then { + diag_log format ["[A3XAI] Removing invalid classname: %1.",_x]; + _array deleteAt _forEachIndex; + } else { + _verified pushBack _x; + }; + }; + if (isClass (configFile >> "CfgVehicles" >> _x)) exitWith { + if (((configName (inheritsFrom (configFile >> "CfgVehicles" >> _x))) isEqualTo "Banned") or {(getNumber (configFile >> "CfgVehicles" >> _x >> "scope")) isEqualTo 0}) then { + diag_log format ["[A3XAI] Removing invalid classname: %1.",_x]; + _array deleteAt _forEachIndex; + } else { + _verified pushBack _x; + }; + }; + diag_log format ["[A3XAI] Removing invalid classname: %1.",_x]; //Default case - if classname doesn't exist at all + _array deleteAt _forEachIndex; + }; + }; + } forEach _array; +} forEach A3XAI_tableChecklist; + +if (A3XAI_maxAirPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3XAI] Removing non-array type element from A3XAI_airVehicleList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3XAI] Array in A3XAI_airVehicleList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "Air")) then { + throw format ["[A3XAI] Removing non-Air type vehicle from A3XAI_airVehicleList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3XAI_airVehicleList deleteAt _forEachIndex; + }; + } forEach A3XAI_airVehicleList; +}; + +if (A3XAI_maxLandPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3XAI] Removing non-array type element from A3XAI_landVehicleList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3XAI] Array in A3XAI_landVehicleList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "LandVehicle")) then { + throw format ["[A3XAI] Removing non-LandVehicle type vehicle from A3XAI_landVehicleList array: %1.",(_x select 0)]; + }; + if (((_x select 0) isKindOf "StaticWeapon")) then { + throw format ["[A3XAI] Removing StaticWeapon type vehicle from A3XAI_landVehicleList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3XAI_landVehicleList deleteAt _forEachIndex; + }; + } forEach A3XAI_landVehicleList; +}; + +if (A3XAI_maxAirReinforcements > 0) then { + { + try { + if (!(_x isKindOf "Air")) then { + throw format ["[A3XAI] Removing non-Air type vehicle from A3XAI_airReinforcementVehicles array: %1.",_x]; + }; + } catch { + diag_log _exception; + A3XAI_airReinforcementVehicles deleteAt _forEachIndex; + }; + } forEach A3XAI_airReinforcementVehicles; +}; + +if (A3XAI_maxUAVPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3XAI] Removing non-array type element from A3XAI_UAVList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3XAI] Array in A3XAI_UAVList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "Air")) then { + throw format ["[A3XAI] Removing non-Air type vehicle from A3XAI_UAVList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3XAI_UAVList deleteAt _forEachIndex; + }; + } forEach A3XAI_UAVList; +}; + +if (A3XAI_maxUGVPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3XAI] Removing non-array type element from A3XAI_UGVList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3XAI] Array in A3XAI_UGVList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "LandVehicle")) then { + throw format ["[A3XAI] Removing non-LandVehicle type vehicle from A3XAI_UGVList array: %1.",(_x select 0)]; + }; + if (((_x select 0) isKindOf "StaticWeapon")) then { + throw format ["[A3XAI] Removing StaticWeapon type vehicle from A3XAI_UGVList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3XAI_UGVList deleteAt _forEachIndex; + }; + } forEach A3XAI_UGVList; +}; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3XAI] Removing invalid uniform classname from A3XAI_uniformTypes0 array: %1.",_x]; + A3XAI_uniformTypes0 deleteAt _forEachIndex; + }; +} forEach A3XAI_uniformTypes0; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3XAI] Removing invalid uniform classname from A3XAI_uniformTypes1 array: %1.",_x]; + A3XAI_uniformTypes1 deleteAt _forEachIndex; + }; +} forEach A3XAI_uniformTypes1; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3XAI] Removing invalid uniform classname from A3XAI_uniformTypes2 array: %1.",_x]; + A3XAI_uniformTypes2 deleteAt _forEachIndex; + }; +} forEach A3XAI_uniformTypes2; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3XAI] Removing invalid uniform classname from A3XAI_uniformTypes3 array: %1.",_x]; + A3XAI_uniformTypes3 deleteAt _forEachIndex; + }; +} forEach A3XAI_uniformTypes3; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 2) then { + diag_log format ["[A3XAI] Removing invalid pistol classname from A3XAI_pistolList array: %1.",_x]; + A3XAI_pistolList deleteAt _forEachIndex; + }; +} forEach A3XAI_pistolList; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 1) then { + diag_log format ["[A3XAI] Removing invalid rifle classname from A3XAI_rifleList array: %1.",_x]; + A3XAI_rifleList deleteAt _forEachIndex; + }; +} forEach A3XAI_rifleList; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 1) then { + diag_log format ["[A3XAI] Removing invalid machine gun classname from A3XAI_machinegunList array: %1.",_x]; + A3XAI_machinegunList deleteAt _forEachIndex; + }; +} forEach A3XAI_machinegunList; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 1) then { + diag_log format ["[A3XAI] Removing invalid sniper classname from A3XAI_sniperList array: %1.",_x]; + A3XAI_sniperList deleteAt _forEachIndex; + }; +} forEach A3XAI_sniperList; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 4) then { + diag_log format ["[A3XAI] Removing invalid launcher classname from A3XAI_launcherTypes array: %1.",_x]; + A3XAI_launcherTypes deleteAt _forEachIndex; + }; +} forEach A3XAI_launcherTypes; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo","mountAction",""] call BIS_fnc_returnConfigEntry) isEqualTo "MountOptic") then { + diag_log format ["[A3XAI] Removing invalid optics classname from A3XAI_weaponOpticsList array: %1.",_x]; + A3XAI_weaponOpticsList deleteAt _forEachIndex; + }; +} forEach A3XAI_weaponOpticsList; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3XAI] Removing invalid backpack classname from A3XAI_backpackTypes0 array: %1.",_x]; + A3XAI_backpackTypes0 deleteAt _forEachIndex; + }; +} forEach A3XAI_backpackTypes0; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3XAI] Removing invalid backpack classname from A3XAI_backpackTypes1 array: %1.",_x]; + A3XAI_backpackTypes1 deleteAt _forEachIndex; + }; +} forEach A3XAI_backpackTypes1; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3XAI] Removing invalid backpack classname from A3XAI_backpackTypes2 array: %1.",_x]; + A3XAI_backpackTypes2 deleteAt _forEachIndex; + }; +} forEach A3XAI_backpackTypes2; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3XAI] Removing invalid backpack classname from A3XAI_backpackTypes3 array: %1.",_x]; + A3XAI_backpackTypes3 deleteAt _forEachIndex; + }; +} forEach A3XAI_backpackTypes3; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3XAI] Removing invalid vest classname from A3XAI_vestTypes0 array: %1.",_x]; + A3XAI_vestTypes0 deleteAt _forEachIndex; + }; +} forEach A3XAI_vestTypes0; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3XAI] Removing invalid vest classname from A3XAI_vestTypes1 array: %1.",_x]; + A3XAI_vestTypes1 deleteAt _forEachIndex; + }; +} forEach A3XAI_vestTypes1; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3XAI] Removing invalid vest classname from A3XAI_vestTypes2 array: %1.",_x]; + A3XAI_vestTypes2 deleteAt _forEachIndex; + }; +} forEach A3XAI_vestTypes2; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3XAI] Removing invalid vest classname from A3XAI_vestTypes3 array: %1.",_x]; + A3XAI_vestTypes3 deleteAt _forEachIndex; + }; +} forEach A3XAI_vestTypes3; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3XAI] Removing invalid headgear classname from A3XAI_headgearTypes0 array: %1.",_x]; + A3XAI_headgearTypes0 deleteAt _forEachIndex; + }; +} forEach A3XAI_headgearTypes0; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3XAI] Removing invalid headgear classname from A3XAI_headgearTypes1 array: %1.",_x]; + A3XAI_headgearTypes1 deleteAt _forEachIndex; + }; +} forEach A3XAI_headgearTypes1; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3XAI] Removing invalid headgear classname from A3XAI_headgearTypes2 array: %1.",_x]; + A3XAI_headgearTypes2 deleteAt _forEachIndex; + }; +} forEach A3XAI_headgearTypes2; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3XAI] Removing invalid headgear classname from A3XAI_headgearTypes3 array: %1.",_x]; + A3XAI_headgearTypes3 deleteAt _forEachIndex; + }; +} forEach A3XAI_headgearTypes3; + +//Anticipate cases where all elements of an array are invalid +if (A3XAI_pistolList isEqualTo []) then {A3XAI_pistolList = ["hgun_ACPC2_F","hgun_P07_F","hgun_Pistol_heavy_01_F","hgun_Pistol_heavy_02_F","hgun_Rook40_F"]}; +if (A3XAI_rifleList isEqualTo []) then {A3XAI_rifleList = ["arifle_Katiba_C_F","arifle_Katiba_F","arifle_Katiba_GL_F","arifle_Mk20_F","arifle_Mk20_GL_F","arifle_Mk20_GL_plain_F","arifle_Mk20_plain_F","arifle_Mk20C_F","arifle_Mk20C_plain_F","arifle_MX_Black_F","arifle_MX_F","arifle_MX_GL_Black_F","arifle_MX_GL_F","arifle_MXC_Black_F","arifle_MXC_F","arifle_SDAR_F","arifle_TRG20_F","arifle_TRG21_F","arifle_TRG21_GL_F"]}; +if (A3XAI_machinegunList isEqualTo []) then {A3XAI_machinegunList = ["arifle_MX_SW_Black_F","arifle_MX_SW_F","LMG_Mk200_F","LMG_Zafir_F","MMG_01_hex_F","MMG_01_tan_F","MMG_02_black_F","MMG_02_camo_F","MMG_02_sand_F"]}; +if (A3XAI_sniperList isEqualTo []) then {A3XAI_sniperList = ["arifle_MXM_Black_F","arifle_MXM_F","srifle_DMR_01_F","srifle_DMR_02_camo_F","srifle_DMR_02_F","srifle_DMR_02_sniper_F","srifle_DMR_03_F","srifle_DMR_03_khaki_F","srifle_DMR_03_multicam_F","srifle_DMR_03_tan_F","srifle_DMR_03_woodland_F","srifle_DMR_04_F","srifle_DMR_04_Tan_F","srifle_DMR_05_blk_F","srifle_DMR_05_hex_F","srifle_DMR_05_tan_f","srifle_DMR_06_camo_F","srifle_DMR_06_olive_F","srifle_EBR_F","srifle_GM6_camo_F","srifle_GM6_F","srifle_LRR_camo_F","srifle_LRR_F"]}; +if (A3XAI_foodLoot isEqualTo []) then {A3XAI_foodLootCount = 0; A3XAI_foodLootVehicleCount = 0;}; +if (A3XAI_miscLoot isEqualTo []) then {A3XAI_miscLootCount = 0; A3XAI_miscLootVehicleCount = 0;}; +if (A3XAI_medicalLoot isEqualTo []) then {A3XAI_medicalLootCount = 0; A3XAI_medicalLootVehicleCount = 0;}; +if (A3XAI_airReinforcementVehicles isEqualTo []) then {A3XAI_maxAirReinforcements = 0; A3XAI_airReinforcementSpawnChance1 = 0; A3XAI_airReinforcementSpawnChance2 = 0; A3XAI_airReinforcementSpawnChance3 = 0;}; + +diag_log format ["[A3XAI] Verified %1 unique classnames in %2 seconds.",(count _verified),(diag_tickTime - _startTime)]; diff --git a/A3XAI_config/$PREFIX$ b/A3XAI_config/$PREFIX$ new file mode 100644 index 0000000..b55db03 --- /dev/null +++ b/A3XAI_config/$PREFIX$ @@ -0,0 +1 @@ +A3XAI_config \ No newline at end of file diff --git a/A3XAI_config/a3xai_custom_defs.sqf b/A3XAI_config/a3xai_custom_defs.sqf new file mode 100644 index 0000000..9ec1de3 --- /dev/null +++ b/A3XAI_config/a3xai_custom_defs.sqf @@ -0,0 +1,61 @@ +/* + A3XAI Custom Spawn Definitions File + + Instructions: + + Generate custom spawns and blacklist areas using the included A3XAI Editor Tool (Inside the "Editor Tool" folder of the A3XAI download package). + + Instructions on how to use the Editor Tool are located on the A3XAI Wikia page: http://A3XAI.wikia.com/wiki/A3XAI_Editor_Tool + + In order for A3XAI to load this file on startup, you must set in @A3XAI/A3XAI_config/config.cpp under the "A3XAI Settings" section: + + loadCustomFile = 1; + +//----------------------------Add your custom spawn and dynamic area blacklist definitions below this line ----------------------------*/ + +call { + if (worldName == "taviana") exitWith { + ["SectorB_1",[22246.4,19894.3,0.00143862],100,4,3,true,600] call A3XAI_createCustomInfantryQueue; + ["SectorB_2",[22353.9,19542.2,0.00143862],200,4,3,true,600] call A3XAI_createCustomInfantryQueue; + ["SectorB_3",[22500.5,20066,0.00143862],100,4,3,true,600] call A3XAI_createCustomInfantryQueue; + ["SectorB_4",[22767.7,19846.5,0.00143862],200,4,3,true,600] call A3XAI_createCustomInfantryQueue; + ["SectorB_5",[22880.7,19340.1,0.00143862],200,4,3,true,600] call A3XAI_createCustomInfantryQueue; + ["SectorB_6",[22448.9,19484.1,0.00143862],100,4,3,true,600] call A3XAI_createCustomInfantryQueue; + ["VishkovMilitary",[11193.4,15783.1,0.00144196],200,3,2,true,600] call A3XAI_createCustomInfantryQueue; + ["VishkovCivilian",[11429.7,15904.4,0.00144196],200,3,1,true,600] call A3XAI_createCustomInfantryQueue; + ["DubravkaCivilian",[11781.3,15581.2,0.00144196],200,3,1,true,600] call A3XAI_createCustomInfantryQueue; + ["StariGradCivilian",[11297.1,15430.2,0.00144196],175,3,1,true,600] call A3XAI_createCustomInfantryQueue; + ["BoriMilitary",[11670.5,14961.6,0.00144196],200,3,2,true,600] call A3XAI_createCustomInfantryQueue; + ["NinaMilitary",[12455.3,14983.6,0.00144196],175,3,2,true,600] call A3XAI_createCustomInfantryQueue; + }; + if (worldName == "namalsk") exitWith { + A3XAI_manualStaticSpawnLocations = [ + [4157.5698, 6636.252], + [3558.3298, 6664.4048], + [3940.0078, 7536.5967], + [4976.6655, 6619.644, 42.068932], + [4845.8853, 6201.1484, 0], + [4081.9597, 9224.0859, 2.600769], + [4690.1934, 8916.7041, 5.2387733], + [5781.0708, 9809.7734, -7.6293945e-006], + [6308.022, 9307.7139], + [5807.0181, 8676.9521, 12.870121], + [7293.3511, 7967.5581, 3.7465744], + [7694.8877, 7602.1001, 1.2397766e-005], + [7046.0806, 5808.7622], + [5986.1616, 6641.3848, -1.1444092e-005], + [4947.3247, 8158.6709, -3.8146973e-006], + [8199.6846, 10729.502], + [4829.1992, 10839.983, 2.8610229e-006], + [4498.3965, 11127.151, -1.2159348e-005], + [4407.7451, 10748.695], + [6784.3296, 11291.731, 5.5010681], + [7668.0474, 8760.8672, 12.067543], + [3173.5461, 7504.5532], + [6943.7725, 11424.083], + [5777.3901, 10784.223, 0], + [6719.9434, 11113.122, 20.099487] + ]; + }; +}; + diff --git a/A3XAI_config/config.cpp b/A3XAI_config/config.cpp new file mode 100644 index 0000000..3ed5f48 --- /dev/null +++ b/A3XAI_config/config.cpp @@ -0,0 +1,827 @@ +class CfgPatches { + class A3XAI_config { + units[] = {}; + weapons[] = {}; + requiredVersion = 0.1; + A3XAIVersion = "3"; + requiredAddons[] = {}; + }; +}; + +class CfgA3XAISettings { + + /* A3XAI Settings + --------------------------------------------------------------------------------------------------------------------*/ + + //Enable or disable event logging to the server RPT file (named arma3server_[date]_[time].rpt). Debug level setting. 0: No debug output, 1: Basic Debug output, 2: Detailed Debug output. (Default: 0) + //Debug output may help finding additional information about A3XAI's background behavior. This output is helpful when asking for help regarding bugs or unexpected behaviors. + debugLevel = 0; + + //Frequency of server monitor update to RPT log in seconds. The monitor periodically reports number of max/current AI units and dynamically spawned triggers into RPT log. (Default: 300, 0 = Disable reporting) + monitorReportRate = 300; + + //Enable or disable verification and error-correction of classname tables used by A3XAI. If invalid entries are found, they are removed and logged into the RPT log. + //If disabled, any invalid classnames will not be removed and clients may crash if AI bodies with invalid items are looted. Only disable if a previous scan shows no invalid classnames (Default: 1). + verifyClassnames = 1; + + //Enables checking of all A3XAI config settings. (Default: 1) + verifySettings = 1; + + //Minimum seconds to pass until each dead AI body or destroyed vehicle can be cleaned up by A3XAI's task scheduler. A3XAI will not clean up a body/vehicle if there is a player close by (Default: 900). + cleanupDelay = 900; + + //Enabled: A3XAI will load custom spawn/blacklist definitions file on startup (A3XAI_config.pbo >> custom_defs.sqf) (Default: 0) + loadCustomFile = 0; + + //Minimum server FPS to spawn/respawn AI (Default: 0) + minFPS = 0; //0 + + //0: A3XAI uses individual threads to manage each spawned AI group (more accurate ammo/fuel reloading). 1: A3XAI will use a single thread to manage all AI groups (better performance). (Default: 0) + groupManageMode = 0; + + + /* A3XAI HC Settings + --------------------------------------------------------------------------------------------------------------------*/ + + //Enables A3XAI headless client support. (Default: 0) + enableHC = 0; + + //If HC support enabled, A3XAI will pause during post-initialization until HC has successfully connected. (Default: 0) + //IMPORTANT: It is highly recommended to ensure that the HC is properly setup before enabling this option, otherwise A3XAI may be stuck waiting for HC to to connect. + waitForHC = 0; + + + /* Dynamic Classname Settings + + If a setting is disabled, A3XAI will use the corresponding classname table further below. See "AI clothing, weapon, loot, and equipment settings" section. + --------------------------------------------------------------------------------------------------------------------*/ + + //Purchase price limit for items generated by dynamic classname system as defined in CfgExileArsenal. Lowering this value may reduce variety of items carried by AI. (Default: 2000) + itemPriceLimit = 2000; + + //1: Generate AI weapons from Exile trader tables (Default) + //0: Weapons defined by pistolList, rifleList, machinegunList, sniperList + //dynamicWeaponBlacklist: Classnames of weapons to ignore from Exile trader tables + generateDynamicWeapons = 1; + dynamicWeaponBlacklist[] = {}; + + //1: Use Exile loot table data as whitelist for AI-usable weapon scopes (Default) + //0: Scopes defined by weaponOpticsList + //dynamicOpticsBlacklist: List of optics classnames to ignore from Exile trader tables. + generateDynamicOptics = 1; + dynamicOpticsBlacklist[] = {}; + + //1: Generate AI uniform types from Exile trader tables (Default) + //0: Uniforms defined by uniformTypes0, uniformTypes1, uniformTypes2, uniformTypes3 + //dynamicUniformBlacklist: List of uniform classnames to ignore from Exile trader tables. + generateDynamicUniforms = 1; + dynamicUniformBlacklist[] = {}; + + //1: Generate AI backpack types from Exile trader tables (Default) + //0: Backpacks defined by backpackTypes0, backpackTypes1, backpackTypes2, backpackTypes3 + //dynamicBackpackBlacklist: List of backpack classnames to ignore from Exile trader tables. + generateDynamicBackpacks = 1; + dynamicBackpackBlacklist[] = {}; + + //1: Generate AI backpack types from Exile trader tables (Default) + //0: Vests defined by vestTypes0, vestTypes1, vestTypes2, vestTypes3 + //dynamicVestBlacklist: List of vest classnames to ignore from Exile trader tables. + generateDynamicVests = 1; + dynamicVestBlacklist[] = {}; + + //1: Generate AI headgear types from Exile trader tables (Default) + //0: Headgear defined by headgearTypes0, headgearTypes1, headgearTypes2, headgearTypes3 + //dynamicHeadgearBlacklist: List of headgear classnames to ignore from Exile trader tables. + generateDynamicHeadgear = 1; + dynamicHeadgearBlacklist[] = {}; + + //1: Generate AI food types from Exile trader tables (Default) + //0: Food defined by foodLoot + //dynamicFoodBlacklist: List of food classnames to ignore from Exile trader tables. + generateDynamicFood = 1; + dynamicFoodBlacklist[] = {}; + + //1: Generate AI medical types from Exile trader tables (Default) + //0: Food defined by medical Loot + //dynamicMedicalBlacklist: List of medical classnames to ignore from Exile trader tables. + generateDynamicMedical = 1; + dynamicMedicalBlacklist[] = {}; + + //1: Generate AI generic loot types from Exile trader tables. Includes "Hardware", "Smoke", "Flare" -class items. (Default) + //0: Loot defined by MiscLoot + //dynamicLootBlacklist: List of loot classnames to ignore from Exile trader tables. + generateDynamicLoot = 1; + dynamicLootBlacklist[] = {}; + + + //Enable or disable radio message receiving. Players with radios or part of a group with at least one radio will be able to intercept some AI communications. (Default: 0) + enableRadioMessages = 0; + + + /* Shared AI Unit Settings. These settings affect all AI spawned unless noted otherwise. + --------------------------------------------------------------------------------------------------------------------*/ + + //Sets side/faction for AI spawned by A3XAI. If A3XAI units are hostile with AI spawned from other install addons, consider changing this setting. Acceptable sides: east or west (Default: east) + side = east; + + //Number of online players required for maximum (or minimum) AI spawn chance. Affects Static, Dynamic, Random AI spawns. (Default: 10) + playerCountThreshold = 10; + + //1: Spawn chance multiplier scales upwards from value defined by chanceScalingThreshold to 1.00. 0: Spawn chance multiplier scales downwards from 1.00 to chanceScalingThreshold. + upwardsChanceScaling = 1; + + //If upwardsChanceScaling is 1: Initial spawn chance multiplier. If upwardsChanceScaling is 0: Final spawn chance multiplier. (Default: 0.50) + chanceScalingThreshold = 0.50; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_village = 1; + addAI_village = 1; + unitLevel_village = 0; + spawnChance_village = 0.60; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_city = 1; + addAI_city = 2; + unitLevel_city = 1; + spawnChance_city = 0.70; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_capitalCity = 2; + addAI_capitalCity = 1; + unitLevel_capitalCity = 1; + spawnChance_capitalCity = 0.80; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_remoteArea = 2; + addAI_remoteArea = 1; + unitLevel_remoteArea = 2; + spawnChance_remoteArea = 0.90; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_wilderness = 1; + addAI_wilderness = 2; + unitLevel_wilderness = 1; + spawnChance_wilderness = 0.50; + + //(For dynamic and random spawns only) Defines amount of time to wait in seconds until cleaning up temporary blacklist area after dynamic/random spawn is deactivated (Default: 1200) + tempBlacklistTime = 1200; + + //Enable or disable AI death messages. Messages will be visible to all group members of player responsible for killing the AI unit. (Default: 1) + enableDeathMessages = 1; + + //If enabled, AI group will attempt to track down player responsible for killing a group member. (Default: 1) + enableFindKiller = 1; + + //If normal probability check for spawning NVGs fails, then give AI temporary NVGs during night hours. Temporary NVGs are unlootable and will be removed at death (Default: 0). + enableTempNVGs = 0; + + //Minimum AI unit level requirement to use underslung grenade launchers. Set to -1 to disable completely. (Default: 1) + levelRequiredGL = 1; + + //Minimum AI unit level requirement to use launcher weapons. Set to -1 to disable completely. Launchers are unlootable and will be removed at death. (Default: -1) + levelRequiredLauncher = -1; + + //List of launcher-type weapons that AI can use. Launchers are unlootable and will be removed at death. + launcherTypes[] = {"launch_NLAW_F","launch_RPG32_F","launch_B_Titan_F","launch_I_Titan_F","launch_O_Titan_F","launch_B_Titan_short_F","launch_I_Titan_short_F","launch_O_Titan_short_F"}; + + //Maximum number of launcher weapons allowed per group (Default: 1) + launchersPerGroup = 1; + + //Enable or disable AI self-healing. Level 0 AI cannot self-heal. Affects: All AI infantry units (Default: 1). + enableHealing = 1; + + //If enabled, A3XAI will remove all explosive ammo (missiles, rockets, bombs - but not HE rounds) from spawned AI air vehicles. (Default: 1) + //Affects: All AI air vehicle types (patrols/custom/reinforcement). Does not affect UAV/UGVs. + removeExplosiveAmmo = 1; + + //if enabled, AI units suffer no damage from vehicle collisions. (Default: 1) + noCollisionDamage = 1; + + //If enabled, AI killed by vehicle collisions will have their gear removed (Default: 1) + roadKillPenalty = 1; + + //Array of positions defining trader locations. AI will be non-hostile and damage immune around this area. + //Use this if your server is not using the standard Exile trader markers or sensors in mission.sqm + //For performance reasons, do not add locations other than actual trader positions to this array. + //Example: traderAreaLocations[] = {{2998.06,18175.5,0},{14600,16797.2,0},{23334.6,24188.9,0}}; + traderAreaLocations[] = {}; + + + /* Static Infantry AI Spawning Settings + + A3XAI will spawn an AI group at various named locations on the map if players are nearby. + --------------------------------------------------------------------------------------------------------------------*/ + + //Enable or disable static AI spawns. If enabled, AI spawn points will be generated in cities, towns, and other named areas. + //Enabled: A3XAI automatically generates static spawns at named locations on map with buildings nearby. Disabled: No static spawns will be generated. (Default: 1) + enableStaticSpawns = 1; + + //(Requires enableStaticSpawns = 1) 1: Enables additional static spawns at named locations on map that may not have buildings nearby. 0: No extra static spawns will be generated. (Default: 0) + enableExtraStaticSpawns = 0; + + //(Disables enableExtraStaticSpawns) 1: Use manually-defined static spawn locations instead of using automatic generation method. 0: Use automatic static spawn generation method. (Default: 0) + useManualStaticSpawns = 0; + + //(Requires useManualStaticSpawns = 1) Manually-defined static spawn locations. Example: manualStaticSpawnLocations[] = {{2998.06,18175.5,0},{14600,16797.2,0},{23334.6,24188.9,0}}; + manualStaticSpawnLocations[] = {}; + + //Set minimum and maximum wait time (seconds) to respawn an AI group after all units have been killed. Applies to both static AI and custom spawned AI (Default: Min 300, Max 600). + respawnTimeMin = 300; + respawnTimeMax = 600; + + //[Unused setting] Time to allow spawned AI units to exist in seconds before being despawned when no players are present in a trigger area. Applies to both static AI and custom spawned AI (Default: 120) + despawnWait = 60; + + //Minimum time to wait to retry spawn if spawn chance roll fails, in seconds (Default: 300) + staticLockoutTime = 300; + + //Respawn Limits. Set to -1 for unlimited respawns. (Default: -1 for each). + respawnLimit_village = -1; + respawnLimit_city = -1; + respawnLimit_capitalCity = -1; + respawnLimit_remoteArea = -1; + + + /* Dynamic Infantry AI Spawning Settings. Probabilities should add up to 1.00 + + A3XAI will create ambient threat in the area for each player by periodically spawning AI to create unexpected ambush encounters. These AI may occasionally seek out and hunt a player. + --------------------------------------------------------------------------------------------------------------------*/ + + //Upper limit of dynamic spawns on map at once. Set to 0 to disable dynamic spawns (Default: 15) + maxDynamicSpawns = 15; + + //Minimum time (in seconds) that must pass between dynamic spawns for each player (Default: 900) + timePerDynamicSpawn = 900; + + //Players offline for this amount of time (seconds) will have their last spawn timestamp reset (Default: 3600) + purgeLastDynamicSpawnTime = 3600; + + //Probability for dynamic AI to actively hunt a targeted player. If probability check fails, dynamic AI will patrol the area instead of hunting (Default: 0.66) + spawnHunterChance = 0.66; + + //Time to wait (seconds) before despawning all AI units in dynamic spawn area when no players are present. (Default: 120) + despawnDynamicSpawnTime = 60; + + + /* Random Infantry AI Spawning Settings + + A3XAI will create spawns that are randomly placed around the map and are periodically relocated. These spawns are preferentially created in named locations, but may be also created anywhere in the world. + --------------------------------------------------------------------------------------------------------------------*/ + + //Maximum number of placed random spawns on map. Set to -1 for A3XAI to automatically adjust spawn limit according to map size. Set to 0 to disable random spawns. (Default: -1) + maxRandomSpawns = -1; + + //Time to wait (seconds) before despawning all AI units in random spawn area when no players are present. (Default: 120) + despawnRandomSpawnTime = 60; + + //Minimum distance between a random spawn location and other random spawns. (Default: 0) + distanceBetweenRandomSpawns = 0; + + + /* Shared AI Vehicle Settings + + These settings affect the following AI vehicle patrol types: Air, Land, UAV, UGV + --------------------------------------------------------------------------------------------------------------------*/ + + //Time to wait (seconds) before despawning disabled/destroyed AI vehicles. If vehiclesAllowedForPlayers is enabled, this timer is cleared once a player enters the vehicle. (Default: 600) + vehicleDespawnTime = 600; + + //Enable player use of AI vehicles. Players must either disable the vehicle or kill all units of the group in order to access the vehicle. (Default: 0) + vehiclesAllowedForPlayers = 0; + + //Add name of location as displayed on map prevent AI vehicle patrols from travelling to these locations. Location names are case-sensitive. Note: Vehicles may still pass through these areas + //Example: waypointBlacklistAir[] = {"Aggelochori","Panochori","Zaros"}; + waypointBlacklistAir[] = {}; //Affects Air vehicles (including UAVs) + waypointBlacklistLand[] = {}; //Affects Land vehicles (including UGVs) + + + /* AI Air Vehicle patrol settings. + + IMPORTANT: UAVs (Unmanned aerial vehicles) are not supported by this function. UAV spawns can be configured in the "UAV Patrol Settings" section further below. + + A3XAI will create AI vehicle patrols that will randomly travel between different cities and towns, and engage any players encountered. + Helicopters with available cargo space may also occasionally deploy an AI group by parachute. + --------------------------------------------------------------------------------------------------------------------*/ + + //Global maximum number of active AI air vehicle patrols. Set at 0 to disable (Default: 0). + maxAirPatrols = 0; + + //Probability of spawning Level 0/1/2/3 AI air vehicle patrol spawns. Probabilities should add up to 1.00 + levelChancesAir[] = {0.00,0.50,0.35,0.15}; + + //Set minimum and maximum wait time in seconds to respawn an AI vehicle patrol after vehicle is destroyed or disabled. (Default: Min 600, Max 900). + respawnAirMinTime = 600; + respawnAirMaxTime = 900; + + //Classnames of air vehicle types to use, with the maximum amount of each type to spawn. + airVehicleList[] = { + {"B_Heli_Light_01_armed_F",5}, + {"B_Heli_Transport_01_F",5}, + {"B_Heli_Transport_03_F",2} + }; + + //Maximum number of gunner units per air vehicle. Limited by actual number of available gunner positions. (Default: 2) + //Affects: All AI air vehicle patrols, including custom and reinforcement. + airGunnerUnits = 2; + + //Probability of AI helicopter sucessfully detecting player if there is line-of-sight. AI helicopters will conduct a visual sweep upon arriving at each waypoint and some distance after leaving. (Default: 0.80) + //Affects: All AI air vehicle patrols, including custom and reinforcement. + airDetectChance = 0.80; + + //Probability of AI to deploy infantry units by parachute if players are nearby when helicopter is investigating a waypoint. (Default: 1.00) + //Affects: Air vehicle patrols. + paradropChance = 1.00; + + //Cooldown time for AI paradrop deployment in seconds. (Default: 1800). + //Affects: Air vehicle patrols. + paradropCooldown = 1800; + + //Number of infantry AI to paradrop if players are nearby when helicopter is investigating a waypoint, or if helicopter is reinforcing a dynamic AI spawn. Limited by number of cargo seats available in the vehicle. (Default: 3) + //Affects: Air vehicle patrols, air reinforcements. + paradropAmount = 3; + + + /* AI Land Vehicle patrol settings. These AI vehicles will randomly travel between different cities and towns. + + IMPORTANT: UGVs (Unmanned ground vehicles) are not supported by this function. UGV spawns can be configured in the "UGV Patrol Settings" section further below. + + A3XAI will create AI vehicle patrols that will randomly travel between different cities and towns, and engage any players encountered. + --------------------------------------------------------------------------------------------------------------------*/ + + //Global maximum number of active AI land vehicle patrols. Set at 0 to disable (Default: 0). + maxLandPatrols = 0; + + //Probability of spawning Level 0/1/2/3 AI land vehicle spawns. Probabilities should add up to 1.00 + levelChancesLand[] = {0.00,0.50,0.35,0.15}; + + //Set minimum and maximum wait time in seconds to respawn an AI vehicle patrol after vehicle is destroyed or disabled. (Default: Min 600, Max 900). + respawnLandMinTime = 600; + respawnLandMaxTime = 900; + + //Classnames of land vehicle types to use, with the maximum amount of each type to spawn. + landVehicleList[] = { + {"Exile_Car_Hatchback_Rusty1",5}, + {"Exile_Car_Hatchback_Rusty2",5}, + {"Exile_Car_Hatchback_Rusty3",5}, + {"Exile_Car_Hatchback_Sport_Red",5}, + {"Exile_Car_SUV_Red",5}, + {"Exile_Car_Offroad_Rusty1",5}, + {"Exile_Car_Offroad_Rusty2",5}, + {"Exile_Car_Offroad_Rusty3",5}, + {"Exile_Car_Offroad_Repair_Civillian",5}, + {"Exile_Car_Offroad_Armed_Guerilla01",5}, + {"Exile_Car_Strider",5}, + {"Exile_Car_Hunter",5}, + {"Exile_Car_Ifrit",5}, + {"Exile_Car_Van_Black",5}, + {"Exile_Car_Van_Box_Black",5}, + {"Exile_Car_Van_Fuel_Black",5}, + {"Exile_Car_Zamak",5}, + {"Exile_Car_Tempest",5}, + {"Exile_Car_HEMMT",5} + }; + + //Maximum number of gunner units per land vehicle. Limited by actual number of available gunner positions. (Default: 2) + landGunnerUnits = 2; + + //Maximum number of cargo units per land vehicle. Limited by actual number of available cargo positions. (Default: 3) + landCargoUnits = 3; + + + + /* AI Air Reinforcement Settings + + Allowed types of AI groups (defined by airReinforcementAllowedTypes) may call for temporary air reinforcements if a player kills one of their units. + Probability to summon reinforcements determined by airReinforcementSpawnChance, where is the level of the calling group. + Once summoned, armed reinforcement vehicles will remain in the area for a duration determined by airReinforcementDuration and engage nearby players. + Unarmed reinforcement vehicles will deploy a paradrop group and exit the area. + + --------------------------------------------------------------------------------------------------------------------*/ + + //Maximum allowed number of simultaneous active reinforcements (Default: 5) + maxAirReinforcements = 5; + + //Air vehicles to use as reinforcement vehicles. Default: {"B_Heli_Transport_01_F","B_Heli_Light_01_armed_F"} + //Armed air vehicles will detect and engage players within reinforcement area. Unarmed air vehicles will deploy an AI paradrop group. + airReinforcementVehicles[] = { + "B_Heli_Transport_01_F", + "B_Heli_Light_01_armed_F" + }; + + //Probability to spawn reinforcements for each AI level. + airReinforcementSpawnChance0 = 0.00; + airReinforcementSpawnChance1 = 0.10; + airReinforcementSpawnChance2 = 0.20; + airReinforcementSpawnChance3 = 0.30; + + //AI types permitted to summon reinforcements. Default: airReinforcementAllowedFor[] = {"static","dynamic","random"}; + //Usable AI types: "static", "dynamic", "random", "air", "land", "staticcustom", "aircustom", "landcustom", "vehiclecrew" + airReinforcementAllowedFor[] = {"static","dynamic","random"}; + + //Probability to deploy infantry AI. If chance roll fails, air vehicle will remain in area for duration defined by airReinforcementDuration0-3 and engage detected players + //Unarmed air vehicle will always have a 1.00 probability to deploy at least 1 infantry AI unit. + airReinforceDeployChance0 = 0.60; + airReinforceDeployChance1 = 0.70; + airReinforceDeployChance2 = 0.80; + airReinforceDeployChance3 = 0.90; + + //Maximum time for reinforcement for armed air vehicles in seconds. AI air vehicle will leave the area after this time if not destroyed. + airReinforcementDuration0 = 120; + airReinforcementDuration1 = 180; + airReinforcementDuration2 = 240; + airReinforcementDuration3 = 300; + + + /* UAV Patrol Settings + + IMPORTANT: UAV patrols are a feature in testing, and may undergo significant changes or possible removal in future versions. + + A3XAI can spawn UAVs that patrol between named locations, and deploy air reinforcements if players are found. + In order for air reinforcements to be deployed, maxAirReinforcements must be greater than zero and the current limit of air reinforcements has not been exceeded. + --------------------------------------------------------------------------------------------------------------------*/ + + //Global maximum number of active UAV patrols. Set at 0 to disable (Default: 0). + maxUAVPatrols = 0; + + //Classnames of UAV types to use, with the maximum amount of each type to spawn. + UAVList[] = { + {"I_UAV_02_CAS_F",5}, + {"I_UAV_02_F",5}, + {"B_UAV_02_CAS_F",5}, + {"B_UAV_02_F",5}, + {"O_UAV_02_CAS_F",5}, + {"O_UAV_02_F",5} + }; + + //Probability of spawning Level 0/1/2/3 UAV spawns. Probabilities should add up to 1.00 + levelChancesUAV[] = {0.35,0.50,0.15,0.00}; + + //Set minimum and maximum wait time in seconds to respawn a UAV after vehicle is destroyed or disabled. (Default: Min 600, Max 900). + respawnUAVMinTime = 600; + respawnUAVMaxTime = 900; + + //Cooldown required in between air reinforcement summons when detecting players. Value in seconds. (Default: 1800) + UAVCallReinforceCooldown = 1800; + + //Probability to successfully detect player if there is line-of-sight. If at least one player is detected, air reinforcements will be summoned to the area. (Default: 0.50) + UAVDetectChance = 0.80; + + + /* UGV Patrol Settings + + IMPORTANT: UGV patrols are a feature in testing, and may undergo significant changes or possible removal in future versions. + + A3XAI can spawn UGVs that patrol between named locations, and deploy air reinforcements if players are found. Damaged UGVs repair themselves over time if not engaging enemes. + In order for air reinforcements to be deployed, maxAirReinforcements must be greater than zero and the current limit of air reinforcements has not been exceeded. + --------------------------------------------------------------------------------------------------------------------*/ + + //Global maximum number of active UGV patrols. Set at 0 to disable (Default: 0). + maxUGVPatrols = 0; + + //Classnames of UGV types to use, with the maximum amount of each type to spawn. + UGVList[] = { + {"I_UGV_01_rcws_F",5}, + {"B_UGV_01_rcws_F",5}, + {"O_UGV_01_rcws_F",5} + }; + + //Probability of spawning Level 0/1/2/3 AI UGV spawns. Probabilities should add up to 1.00 + levelChancesUGV[] = {0.35,0.50,0.15,0.00}; + + //Set minimum and maximum wait time in seconds to respawn a UGV patrol after vehicle is destroyed or disabled. (Default: Min 600, Max 900). + respawnUGVMinTime = 600; + respawnUGVMaxTime = 900; + + //Cooldown required in between air reinforcement summons when detecting players. Value in seconds. (Default: 1800) + UGVCallReinforceCooldown = 1800; + + //Probability to successfully detect player if there is line-of-sight. If at least one player is detected, air reinforcements will be summoned to the area. (Default: 0.50) + UGVDetectChance = 0.80; + + + /* + AI skill settings. + + These settings affect all AI units spawned by A3XAI. + + Skill Level: Description + 0: Low-level AI found in villages + 1: Medium-level AI found in cities and capital cities + 2: High-level AI found in remote areas such as factories and military bases + 3: Expert-level AI. + + Valid skill range: 0.00 - 1.00. + Hint: For all skill types, higher number = better skill. For skill sub-type explanation, see: https://community.bistudio.com/wiki/AI_Sub-skills + */ + + //AI skill settings level 0 (Skill, Minimum skill, Maximum skill). Defaults: Accuracy 0.05-0.10, Others 0.30-0.50 + skill0[] = { + {"aimingAccuracy",0.05,0.10}, + {"aimingShake",0.30,0.50}, + {"aimingSpeed",0.30,0.50}, + {"spotDistance",0.30,0.50}, + {"spotTime",0.30,0.50}, + {"courage",0.30,0.50}, + {"reloadSpeed",0.30,0.50}, + {"commanding",0.30,0.50}, + {"general",0.30,0.50} + }; + + //AI skill settings level 1 (Skill, Minimum skill, Maximum skill). Defaults: Accuracy 0.10-0.15, Others 0.40-0.60 + skill1[] = { + {"aimingAccuracy",0.10,0.15}, + {"aimingShake",0.40,0.60}, + {"aimingSpeed",0.40,0.60}, + {"spotDistance",0.40,0.60}, + {"spotTime",0.40,0.60}, + {"courage",0.40,0.60}, + {"reloadSpeed",0.40,0.60}, + {"commanding",0.40,0.60}, + {"general",0.40,0.60} + }; + + //AI skill settings level 2 (Skill, Minimum skill, Maximum skill). Defaults: Accuracy 0.15-0.20, Others 0.50-0.70 + skill2[] = { + {"aimingAccuracy",0.15,0.20}, + {"aimingShake",0.50,0.70}, + {"aimingSpeed",0.50,0.70}, + {"spotDistance",0.50,0.70}, + {"spotTime",0.50,0.70}, + {"courage",0.50,0.70}, + {"reloadSpeed",0.50,0.70}, + {"commanding",0.50,0.70}, + {"general",0.50,0.70} + }; + + //AI skill settings level 3 (Skill, Minimum skill, Maximum skill). Defaults: Accuracy 0.20-0.25, Others 0.60-0.80 + skill3[] = { + {"aimingAccuracy",0.20,0.25}, + {"aimingShake",0.60,0.80}, + {"aimingSpeed",0.60,0.80}, + {"spotDistance",0.60,0.80}, + {"spotTime",0.60,0.80}, + {"courage",0.60,0.80}, + {"reloadSpeed",0.60,0.80}, + {"commanding",0.60,0.80}, + {"general",0.60,0.80} + }; + + + /* AI loadout probability settings. + --------------------------------------------------------------------------------------------------------------------*/ + + //Probabilities to equip backpack, according to AI level. + addBackpackChance0 = 0.60; + addBackpackChance1 = 0.70; + addBackpackChance2 = 0.80; + addBackpackChance3 = 0.90; + + //Probabilities to equip vest, according to AI level. + addVestChance0 = 0.60; + addVestChance1 = 0.70; + addVestChance2 = 0.80; + addVestChance3 = 0.90; + + //Probabilities to equip headgear, according to AI level. + addHeadgearChance0 = 0.60; + addHeadgearChance1 = 0.70; + addHeadgearChance2 = 0.80; + addHeadgearChance3 = 0.90; + + //Probabilities to equip level 0-3 AI with each weapon type. Order: {pistols, rifles, machineguns, sniper rifles}. Probabilities must add up to 1.00. + useWeaponChance0[] = {0.20,0.80,0.00,0.00}; + useWeaponChance1[] = {0.00,0.90,0.05,0.05}; + useWeaponChance2[] = {0.00,0.80,0.10,0.10}; + useWeaponChance3[] = {0.00,0.70,0.15,0.15}; + + //Probability to select a random optics attachment (ie: scopes) for level 0-3 AI + opticsChance0 = 0.00; + opticsChance1 = 0.30; + opticsChance2 = 0.60; + opticsChance3 = 0.90; + + //Probability to select a random pointer attachment (ie: flashlights) for level 0-3 AI + pointerChance0 = 0.00; + pointerChance1 = 0.30; + pointerChance2 = 0.60; + pointerChance3 = 0.90; + + //Probability to select a random muzzle attachment (ie: suppressors) for level 0-3 AI + muzzleChance0 = 0.00; + muzzleChance1 = 0.30; + muzzleChance2 = 0.60; + muzzleChance3 = 0.90; + + //Probability to select a random underbarrel attachment (ie: bipods) for level 0-3 AI + underbarrelChance0 = 0.00; + underbarrelChance1 = 0.30; + underbarrelChance2 = 0.60; + underbarrelChance3 = 0.90; + + + /* AI loot quantity settings (Infantry) + --------------------------------------------------------------------------------------------------------------------*/ + + //Maximum number of food loot items from foodLoot per infantry unit of each AI group. (Default: 2) + foodLootCount = 2; + + //Maximum number of items to select from miscLoot table per infantry unit of each AI group. (Default: 2) + miscLootCount = 2; + + //Maximum number of items to select from medicalLoot table per infantry unit of each AI group. (Default: 1) + medicalLootCount = 1; + + + /* AI loot quantity settings (Vehicle) + + Note: A3XAI_vehiclesAllowedForPlayers = 1; must be set in order to enable the settings in this section + --------------------------------------------------------------------------------------------------------------------*/ + + //Maximum number of weapons from pistolList, rifleList, machinegunList, sniperList found in vehicles recovered by players. (Default: 5) + weaponLootVehicleCount = 5; + + //Maximum number of magazines to generate for each weapon loot added to vehicle inventory (Default: 3) + ammoLootPerWeapon = 3; + + //Maximum number of food loot items from foodLoot found in vehicles recovered by players. (Default: 10) + foodLootVehicleCount = 10; + + //Maximum number of items to select from miscLoot found in vehicles recovered by players. (Default: 10) + miscLootVehicleCount = 10; + + //Maximum number of items to select from medicalLoot found in vehicles recovered by players. (Default: 5) + medicalLootVehicleCount = 5; + + + /* AI loot probability settings. AI loot is pre-generated into a pool for each unit and randomly pulled to units as time passes. + --------------------------------------------------------------------------------------------------------------------*/ + + //Probability to successfully pull a random item from loot pool for level 0-3 AI. Influences the rate at which loot items are added to units. + lootPullChance0 = 0.20; + lootPullChance1 = 0.40; + lootPullChance2 = 0.60; + lootPullChance3 = 0.80; + + + /* Respect rewards for AI kills. Note: This section only has effects if enableRespectRewards is enabled. + --------------------------------------------------------------------------------------------------------------------*/ + + //Enable Exile-style handling (ie: Respect rewards, kill-count tracking) for AI kills (Default: 1) + enableRespectRewards = 1; + + //Respect bonus for kills with Axe + respectHumiliation = 300; + + //Respect bonus for standard kills + respectFragged = 100; + + //Respect bonus for collision kills with parachute + respectChute = 600; + + //Respect bonus for collision kills with air vehicle + respectBigBird = 600; + + //Respect bonus for collision kills by vehicle driver + respectRoadkill = 500; + + //Respect bonus for kills with vehicle weapons + respectLetItRain = 150; + + //Respect bonus per kill streak + respectKillstreak = 50; + + //Respect bonus per 100m distance from target + respectPer100m = 10; + + + /* + AI skin, weapon, loot, and equipment settings + + Note: Some of the below tables may not be used by A3XAI if a dynamic classname setting is enabled. Check each section below for details. + */ + + //AI uniform classnames. Note: uniformTypes0-3 will not be read if generateDynamicUniforms is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + uniformTypes0[] = {"U_C_Journalist","U_C_Poloshirt_blue","U_C_Poloshirt_burgundy","U_C_Poloshirt_salmon","U_C_Poloshirt_stripped","U_C_Poloshirt_tricolour","U_C_Poor_1","U_C_Poor_2","U_C_Poor_shorts_1","U_C_Scientist"}; + uniformTypes1[] = {"U_B_CombatUniform_mcam_worn","U_B_CTRG_1","U_B_CTRG_2","U_B_CTRG_3","U_I_CombatUniform","U_I_CombatUniform_shortsleeve","U_I_CombatUniform_tshirt","U_I_OfficerUniform","U_O_CombatUniform_ocamo","U_O_CombatUniform_oucamo","U_C_HunterBody_grn","U_IG_Guerilla1_1","U_IG_Guerilla2_1","U_IG_Guerilla2_2","U_IG_Guerilla2_3","U_IG_Guerilla3_1","U_BG_Guerilla2_1","U_IG_Guerilla3_2","U_BG_Guerrilla_6_1","U_BG_Guerilla1_1","U_BG_Guerilla2_2","U_BG_Guerilla2_3","U_BG_Guerilla3_1","U_BG_leader","U_IG_leader","U_B_GhillieSuit","U_I_GhillieSuit","U_O_GhillieSuit","U_B_HeliPilotCoveralls","U_I_HeliPilotCoveralls","U_B_PilotCoveralls","U_I_pilotCoveralls"}; + uniformTypes2[] = {"U_OrestesBody","U_NikosAgedBody","U_NikosBody","U_B_CombatUniform_mcam","U_B_CTRG_1","U_B_CTRG_2","U_B_CTRG_3","U_O_OfficerUniform_ocamo","U_B_SpecopsUniform_sgg","U_O_SpecopsUniform_blk","U_O_SpecopsUniform_ocamo","U_I_G_Story_Protagonist_F","U_IG_Guerilla2_1","U_BG_Guerrilla_6_1","U_I_G_resistanceLeader_F","U_B_FullGhillie_ard","U_B_FullGhillie_lsh","U_B_FullGhillie_sard","U_B_GhillieSuit","U_I_FullGhillie_ard","U_I_FullGhillie_lsh","U_I_FullGhillie_sard","U_I_GhillieSuit","U_O_FullGhillie_ard","U_O_FullGhillie_lsh","U_O_FullGhillie_sard","U_O_GhillieSuit","U_I_Wetsuit","U_O_Wetsuit","U_B_Wetsuit","U_B_survival_uniform"}; + uniformTypes3[] = {"U_OrestesBody","U_NikosAgedBody","U_NikosBody","U_O_OfficerUniform_ocamo","U_B_SpecopsUniform_sgg","U_O_SpecopsUniform_blk","U_O_SpecopsUniform_ocamo","U_I_G_Story_Protagonist_F","U_I_G_resistanceLeader_F","U_B_FullGhillie_ard","U_B_FullGhillie_lsh","U_B_FullGhillie_sard","U_I_FullGhillie_ard","U_I_FullGhillie_lsh","U_I_FullGhillie_sard","U_O_FullGhillie_ard","U_O_FullGhillie_lsh","U_O_FullGhillie_sard","U_I_Wetsuit","U_O_Wetsuit","U_B_Wetsuit","U_B_survival_uniform"}; + + //AI weapon classnames. Note: pistolList, rifleList, machinegunList, sniperList will not be read if generateDynamicWeapons is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + pistolList[] = {"hgun_ACPC2_F","hgun_P07_F","hgun_Pistol_heavy_01_F","hgun_Pistol_heavy_02_F","hgun_Rook40_F"}; + rifleList[] = {"arifle_Katiba_C_F","arifle_Katiba_F","arifle_Katiba_GL_F","arifle_Mk20_F","arifle_Mk20_GL_F","arifle_Mk20_GL_plain_F","arifle_Mk20_plain_F","arifle_Mk20C_F","arifle_Mk20C_plain_F","arifle_MX_Black_F","arifle_MX_F","arifle_MX_GL_Black_F","arifle_MX_GL_F","arifle_MXC_Black_F","arifle_MXC_F","arifle_SDAR_F","arifle_TRG20_F","arifle_TRG21_F","arifle_TRG21_GL_F"}; + machinegunList[] = {"arifle_MX_SW_Black_F","arifle_MX_SW_F","LMG_Mk200_F","LMG_Zafir_F","MMG_01_hex_F","MMG_01_tan_F","MMG_02_black_F","MMG_02_camo_F","MMG_02_sand_F"}; + sniperList[] = {"arifle_MXM_Black_F","arifle_MXM_F","srifle_DMR_01_F","srifle_DMR_02_camo_F","srifle_DMR_02_F","srifle_DMR_02_sniper_F","srifle_DMR_03_F","srifle_DMR_03_khaki_F","srifle_DMR_03_multicam_F","srifle_DMR_03_tan_F","srifle_DMR_03_woodland_F","srifle_DMR_04_F","srifle_DMR_04_Tan_F","srifle_DMR_05_blk_F","srifle_DMR_05_hex_F","srifle_DMR_05_tan_f","srifle_DMR_06_camo_F","srifle_DMR_06_olive_F","srifle_EBR_F","srifle_GM6_camo_F","srifle_GM6_F","srifle_LRR_camo_F","srifle_LRR_F"}; + + //AI weapon scope attachment settings. Note: weaponOpticsList will not be read if generateDynamicOptics is enabled. + weaponOpticsList[] = {"optic_NVS","optic_SOS","optic_LRPS","optic_AMS","optic_AMS_khk","optic_AMS_snd","optic_KHS_blk","optic_KHS_hex","optic_KHS_old","optic_KHS_tan","optic_DMS","optic_Arco","optic_Hamr","Elcan_Exile","Elcan_reflex_Exile","optic_MRCO","optic_Holosight","optic_Holosight_smg","optic_Aco","optic_ACO_grn","optic_Aco_smg","optic_ACO_grn_smg","optic_Yorris","optic_MRD"}; + + //AI backpack types (for AI levels 0-3). Note: backpackTypes0-3 will not be read if generateDynamicBackpacks is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + backpackTypes0[] = {"B_AssaultPack_blk","B_AssaultPack_cbr","B_AssaultPack_dgtl","B_AssaultPack_khk","B_AssaultPack_mcamo","B_AssaultPack_rgr","B_AssaultPack_sgg","B_OutdoorPack_blk","B_OutdoorPack_blu","B_OutdoorPack_tan"}; + backpackTypes1[] = {"B_Bergen_blk","B_Bergen_mcamo","B_Bergen_rgr","B_Bergen_sgg","B_FieldPack_blk","B_FieldPack_cbr","B_FieldPack_ocamo","B_FieldPack_oucamo","B_OutdoorPack_blk","B_OutdoorPack_blu","B_OutdoorPack_tan","B_TacticalPack_blk","B_TacticalPack_mcamo","B_TacticalPack_ocamo","B_TacticalPack_oli","B_TacticalPack_rgr"}; + backpackTypes2[] = {"B_Bergen_blk","B_Bergen_mcamo","B_Bergen_rgr","B_Bergen_sgg","B_Carryall_cbr","B_Carryall_khk","B_Carryall_mcamo","B_Carryall_ocamo","B_Carryall_oli","B_Carryall_oucamo","B_FieldPack_blk","B_FieldPack_cbr","B_FieldPack_ocamo","B_FieldPack_oucamo","B_HuntingBackpack","B_Kitbag_cbr","B_Kitbag_mcamo","B_Kitbag_sgg"}; + backpackTypes3[] = {"B_Carryall_cbr","B_Carryall_khk","B_Carryall_mcamo","B_Carryall_ocamo","B_Carryall_oli","B_Carryall_oucamo","B_HuntingBackpack","B_Kitbag_cbr","B_Kitbag_mcamo","B_Kitbag_sgg"}; + + //AI vest types (for AI levels 0-3). Note: vestTypes0-3 will not be read if generateDynamicVests is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + vestTypes0[] = {"V_Press_F","V_Rangemaster_belt","V_BandollierB_blk","V_BandollierB_cbr","V_BandollierB_khk","V_BandollierB_oli","V_BandollierB_rgr","V_Chestrig_blk","V_Chestrig_khk","V_Chestrig_oli","V_Chestrig_rgr"}; + vestTypes1[] = {"V_Chestrig_blk","V_Chestrig_khk","V_Chestrig_oli","V_Chestrig_rgr","V_HarnessO_brn","V_HarnessO_gry","V_HarnessOGL_brn","V_HarnessOGL_gry","V_HarnessOSpec_brn","V_HarnessOSpec_gry","V_PlateCarrier1_blk","V_PlateCarrier1_rgr","V_PlateCarrier2_rgr","V_PlateCarrier3_rgr","V_PlateCarrierH_CTRG","V_PlateCarrierIA1_dgtl","V_PlateCarrierIA2_dgtl","V_PlateCarrierL_CTRG"}; + vestTypes2[] = {"V_TacVest_blk","V_TacVest_blk_POLICE","V_TacVest_brn","V_TacVest_camo","V_TacVest_khk","V_TacVest_oli","V_TacVestCamo_khk","V_TacVestIR_blk","V_I_G_resistanceLeader_F","V_PlateCarrier2_rgr","V_PlateCarrier3_rgr","V_PlateCarrierGL_blk","V_PlateCarrierGL_mtp","V_PlateCarrierGL_rgr","V_PlateCarrierH_CTRG","V_PlateCarrierIA1_dgtl","V_PlateCarrierIA2_dgtl","V_PlateCarrierIAGL_dgtl","V_PlateCarrierIAGL_oli","V_PlateCarrierL_CTRG","V_PlateCarrierSpec_blk","V_PlateCarrierSpec_mtp","V_PlateCarrierSpec_rgr"}; + vestTypes3[] = {"V_TacVest_blk_POLICE","V_PlateCarrierGL_blk","V_PlateCarrierGL_mtp","V_PlateCarrierGL_rgr","V_PlateCarrierIAGL_dgtl","V_PlateCarrierIAGL_oli","V_PlateCarrierSpec_blk","V_PlateCarrierSpec_mtp","V_PlateCarrierSpec_rgr"}; + + //AI head gear types. Note: headgearTypes0-3 will not be read if generateDynamicHeadgear is enabled. + headgearTypes0[] = {"H_Cap_blk","H_Cap_blk_Raven","H_Cap_blu","H_Cap_brn_SPECOPS","H_Cap_grn","H_Cap_headphones","H_Cap_khaki_specops_UK","H_Cap_oli","H_Cap_press","H_Cap_red","H_Cap_tan","H_Cap_tan_specops_US","H_Watchcap_blk","H_Watchcap_camo"}; + headgearTypes1[] = {"H_Beret_02","H_Beret_blk","H_Beret_blk_POLICE","H_Beret_brn_SF","H_Beret_grn","H_Beret_grn_SF","H_Beret_ocamo","H_Beret_red","H_Shemag_khk","H_Shemag_olive","H_Shemag_olive_hs","H_Shemag_tan"}; + headgearTypes2[] = {"H_Beret_Colonel","H_HelmetB","H_HelmetB_black","H_HelmetB_camo","H_HelmetB_desert","H_HelmetB_grass","H_HelmetB_paint","H_HelmetB_plain_blk","H_HelmetB_sand","H_HelmetB_snakeskin","H_HelmetLeaderO_ocamo","H_HelmetLeaderO_oucamo","H_HelmetO_ocamo","H_HelmetO_oucamo","H_HelmetSpecB","H_HelmetSpecB_blk","H_HelmetSpecB_paint1","H_HelmetSpecB_paint2","H_HelmetSpecO_blk","H_HelmetSpecO_ocamo","H_CrewHelmetHeli_B","H_CrewHelmetHeli_I","H_CrewHelmetHeli_O","H_PilotHelmetHeli_B","H_PilotHelmetHeli_I","H_PilotHelmetHeli_O"}; + headgearTypes3[] = {"H_Beret_Colonel","H_HelmetB_camo","H_HelmetLeaderO_ocamo","H_HelmetLeaderO_oucamo","H_HelmetO_ocamo","H_HelmetO_oucamo","H_HelmetSpecO_blk","H_HelmetSpecO_ocamo","H_PilotHelmetHeli_B","H_PilotHelmetHeli_I","H_PilotHelmetHeli_O"}; + + //AI glasses-slot items. (Currently unused) + glassesTypes0[] = {}; + glassesTypes1[] = {}; + glassesTypes2[] = {}; + glassesTypes3[] = {}; + + //AI goggle-slot items. (Currently unused) + goggleTypes0[] = {"NVGoggles"}; + goggleTypes1[] = {"NVGoggles"}; + goggleTypes2[] = {"NVGoggles"}; + goggleTypes3[] = {"NVGoggles"}; + + //AI binocular-slot items. (Currently unused) + binocularTypes0[] = {"binocular"}; + binocularTypes1[] = {"binocular"}; + binocularTypes2[] = {"binocular"}; + binocularTypes3[] = {"binocular"}; + + //AI map-slot items. (Currently unused) + mapTypes0[] = {"ItemMap"}; + mapTypes1[] = {"ItemMap"}; + mapTypes2[] = {"ItemMap"}; + mapTypes3[] = {"ItemMap"}; + + //AI device-slot items. (Currently unused) + deviceTypes0[] = {"ItemGPS"}; + deviceTypes1[] = {"ItemGPS"}; + deviceTypes2[] = {"ItemGPS"}; + deviceTypes3[] = {"ItemGPS"}; + + //AI compass-slot items. (Currently unused) + compassTypes0[] = {"ItemCompass"}; + compassTypes1[] = {"ItemCompass"}; + compassTypes2[] = {"ItemCompass"}; + compassTypes3[] = {"ItemCompass"}; + + //AI watch-slot items. (Currently unused) + watchTypes0[] = {"Exile_Item_XM8"}; + watchTypes1[] = {"Exile_Item_XM8"}; + watchTypes2[] = {"Exile_Item_XM8"}; + watchTypes3[] = {"Exile_Item_XM8"}; + + + //AI Food/Loot item types. + // Note: foodLoot will not be read if generateDynamicFood is enabled. + // Note: miscLoot will not be read if generateDynamicLoot is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + foodLoot[] = {"Exile_Item_GloriousKnakworst","Exile_Item_SausageGravy","Exile_Item_ChristmasTinner","Exile_Item_BBQSandwich","Exile_Item_Surstromming","Exile_Item_Catfood","Exile_Item_PlasticBottleFreshWater","Exile_Item_Beer","Exile_Item_Energydrink"}; + miscLoot[] = {"Exile_Item_Rope","Exile_Item_DuctTape","Exile_Item_ExtensionCord","Exile_Item_FuelCanisterEmpty","Exile_Item_JunkMetal","Exile_Item_LightBulb","Exile_Item_MetalBoard","Exile_Item_MetalPole","Exile_Item_CamoTentKit"}; + + + //AI Medical item types. + // Note: medicalLoot will not be read if generateMedicalFood is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + medicalLoot[] = {"Exile_Item_InstaDoc","Exile_Item_Bandage","Exile_Item_Vishpirin"}; + + + //AI toolbelt item types. Toolbelt items are added to AI inventory upon death. Format: [item classname, item probability] + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + toolsList0[] = { + {"Exile_Item_XM8",0.90},{"ItemCompass",0.30},{"ItemMap",0.30},{"ItemGPS",0.00},{"ItemRadio",0.00} + }; + toolsList1[] = { + {"Exile_Item_XM8",0.90},{"ItemCompass",0.50},{"ItemMap",0.50},{"ItemGPS",0.10},{"ItemRadio",0.10} + }; + toolsList2[] = { + {"Exile_Item_XM8",0.90},{"ItemCompass",0.70},{"ItemMap",0.70},{"ItemGPS",0.20},{"ItemRadio",0.20} + }; + toolsList3[] = { + {"Exile_Item_XM8",0.90},{"ItemCompass",0.90},{"ItemMap",0.90},{"ItemGPS",0.30},{"ItemRadio",0.30} + }; + + + //AI-useable toolbelt item types. These items are added to AI inventory at unit creation and may be used by AI. Format: {item classname, item probability} + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + gadgetsList0[] = { + {"binocular",0.30},{"NVGoggles",0.00} + }; + gadgetsList1[] = { + {"binocular",0.50},{"NVGoggles",0.10} + }; + gadgetsList2[] = { + {"binocular",0.70},{"NVGoggles",0.20} + }; + gadgetsList3[] = { + {"binocular",0.90},{"NVGoggles",0.30} + }; +}; diff --git a/README.md b/README.md index daa0fc0..3070d16 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ -# A3XAI Final +A3XAI - Final Version (Last edited: April 17, 2016) +===== + +Important +--- +This addon is no longer actively developed or supported in any way. Everything below this section is from the original Readme. +This was the last and final release of A3XAI for A3 servers that still run Exile, and was never released to the public. +There is ***absolutely no guarantee** that this version of A3XAI is bug or issue-free, especially after a year of non-development. Use at your own risk. + +Introduction +--- +A3XAI is a roaming/ambient AI spawning addon for ArmA 3 Exile mod (http://www.exilemod.com/). + +Installing A3XAI +--- +1. Download the latest A3XAI release from the A3XAI Releases page: https://github.com/dayzai/A3XAI/releases +2. Locate the .zip file you downloaded and extract it to a folder. +3. Open the extracted folder and open the Installation Package folder. Copy the contents of this folder (Addons, A3XAI_config.sqf, A3XAI_custom_defs.sqf) +4. Navigate to your server's Arma 3 folder and open the @ExileServer folder. +5. Paste the contents copied from Step 3 into this directory. When prompted to merge the "Addons" folder, click Yes. Note: No Exile files are being replaced or modified. This step merges the two Addon folders. +6. (Optional) Configure A3XAI settings by editing A3XAI_config.sqf. + +A3XAI Features +--- + +* Automatically-generated static AI spawns: A3XAI will spawn an AI group at various named locations on the map if players are nearby. +* Dynamic AI spawns: A3XAI will create ambient threat in the area for each player by periodically spawning AI to create unexpected ambush encounters. These AI may occasionally seek out and hunt a player. +* Random AI spawns: A3XAI will create spawns that are randomly placed around the map and are periodically relocated. These spawns are preferentially created in named locations, but may be also created anywhere in the world. +* Air and land vehicle AI patrols: AI patrol in vehicles around the map, looking for players to hunt down. Cars and trucks may roam the road, and helicopters (or jets) search the skies for players. Helicopters with available cargo space may also occasionally deploy an AI group by parachute. +* UAV and UGV patrols: Currently an experimental feature in testing. UAVs and UGVs patrol around the map, and if armed, will engage detected players. UAVs and UGVs may also call for AI reinforcements. +* Custom AI spawns: Users may also define custom infantry and vehicle AI spawns at specified locations. +* Exile-style Respect rewards: Players gain Respect rewards for killing AI, along with bonus points for "special" kills such as long-distance kills or kill-streaks. +* Adaptive classname system: A3XAI reads Exile's trader tables to find items that AI can use, such as weapons and equipment. Users may also choose to manually specify classnames to use instead. +* Error-checking ability: A3XAI checks config files for errors upon startup. If errors are found, A3XAI will use backup settings and continue operating as normal. +* Classname verification: A3XAI filters out invalid or banned classnames and prevents them from being used by AI. +* Universal map support: A3XAI supports any and every map for Arma 3 without changing any settings. +* Plug-and-play installation: Installing and upgrading A3XAI is a simple copy and paste job and does not require modifying any Exile files. +* Easy configuration: A single configuration file contains all settings for A3XAI. This config file is external to the A3XAI pbo, so configuration changes can be made without ever having to unpack or repack the pbo file. +* Headless Client support: Offload AI calculations from your dedicated server to a headless client to improve server performance. The A3XAI HC can be started/stopped/restarted any time without causing problems. + +Note: Headless clients are currently bugged in ArmA 3 1.50. It is recommended to only attempt using a headless client setup in ArmA 3 1.52+. + +Support A3XAI +--- +if you would like to support development of A3XAI, you can contribute to A3XAI with a donation by clicking on the Donate Icon below. Thank you for your support! + +[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9PESMPV4SQFDJ) + + +This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.