diff --git a/Server/@A3EAI/addons/a3eai/$PREFIX$ b/Server/@A3EAI/addons/a3eai/$PREFIX$ new file mode 100644 index 0000000..a9807f0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/$PREFIX$ @@ -0,0 +1 @@ +A3EAI \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/PboPrefix.txt b/Server/@A3EAI/addons/a3eai/PboPrefix.txt new file mode 100644 index 0000000..a9807f0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/PboPrefix.txt @@ -0,0 +1 @@ +A3EAI \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_findclosestposition.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_findclosestposition.sqf new file mode 100644 index 0000000..43018b9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getmarkercorners.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getmarkercorners.sqf new file mode 100644 index 0000000..1cac68b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getmarkershape.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getmarkershape.sqf new file mode 100644 index 0000000..e397c5e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getpos.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getpos.sqf new file mode 100644 index 0000000..d5c5fb4 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromcircle.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromcircle.sqf new file mode 100644 index 0000000..87269f8 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromellipse.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromellipse.sqf new file mode 100644 index 0000000..a39bfaf --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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 A3EAI_SHK_pos_fnc_rotatePosition; +}; + +_posX = _centerX + (_pos select 0); +_posY = _centerY + (_pos select 1); + +[_posX,_posY,0] diff --git a/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromrectangle.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromrectangle.sqf new file mode 100644 index 0000000..76ebd7d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromsquare.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_getposfromsquare.sqf new file mode 100644 index 0000000..c47df5b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isblacklisted.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isblacklisted.sqf new file mode 100644 index 0000000..ecb49b1 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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 A3EAI_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 A3EAI_SHK_pos_fnc_isSamePosition) then { + _return = true; + }; + +// Markers that have an area. +} else { + if (_shape in ["RECTANGLE","SQUARE"]) then { + private ["_corners"]; + _corners = _area call A3EAI_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 A3EAI_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 A3EAI_SHK_pos_fnc_isInRectangle; + } else { + if (_shape == "CIRCLE") then { + _return = [_pos,_area] call A3EAI_SHK_pos_fnc_isInCircle; + } else { + _return = [_pos,_area] call A3EAI_SHK_pos_fnc_isInEllipse; + }; + }; +}; + + +_return \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isincircle.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isincircle.sqf new file mode 100644 index 0000000..39616d9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isinellipse.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isinellipse.sqf new file mode 100644 index 0000000..cd597be --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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 A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isinrectangle.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_isinrectangle.sqf new file mode 100644 index 0000000..43d7d36 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_issameposition.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_issameposition.sqf new file mode 100644 index 0000000..32dbf4c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_rotateposition.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_fnc_rotateposition.sqf new file mode 100644 index 0000000..c0afaae --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_getpos.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_getpos.sqf new file mode 100644 index 0000000..2e6d054 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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 A3EAI_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 A3EAI_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 A3EAI_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 A3EAI_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/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_getposmarker.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_getposmarker.sqf new file mode 100644 index 0000000..0b844d6 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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 A3EAI_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 A3EAI_SHK_pos_fnc_getPosFromRectangle; + } else { + _pos = _area call A3EAI_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 A3EAI_SHK_pos_fnc_isBlacklisted) exitwith { + _exit = false; + }; + } foreach _blist; + }; +}; + +// Return position +_pos \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_init.sqf b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_SHK_pos_init.sqf new file mode 100644 index 0000000..6486631 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/SHK_pos/A3EAI_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\A3EAI_SHK_pos_init.sqf"; + + Actually getting the position: + pos = [parameters] call A3EAI_SHK_pos; +*/ +// Functions +A3EAI_SHK_pos_getPos = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_getpos.sqf",A3EAI_directory]; +A3EAI_SHK_pos_getPosMarker = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_getposmarker.sqf",A3EAI_directory]; + +// Sub functions +A3EAI_SHK_pos_fnc_findClosestPosition = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_findclosestposition.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getMarkerCorners = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getmarkercorners.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getMarkerShape = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getmarkershape.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getPos = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getpos.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getPosFromCircle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getposfromcircle.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getPosFromEllipse = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getposfromellipse.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getPosFromRectangle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getposfromrectangle.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_getPosFromSquare = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_getposfromsquare.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_isBlacklisted = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_isblacklisted.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_isInCircle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_isincircle.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_isInEllipse = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_isinellipse.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_isInRectangle = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_isinrectangle.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_isSamePosition = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_issameposition.sqf",A3EAI_directory]; +A3EAI_SHK_pos_fnc_rotatePosition = compileFinal preprocessFileLineNumbers format ["%1\shk_pos\A3EAI_SHK_pos_fnc_rotateposition.sqf",A3EAI_directory]; + +// Wrapper function +// Decide which function to call based on parameters. +A3EAI_SHK_pos = { + private ["_pos"]; + _pos = []; + + // Only marker is given as parameter + if (typename _this isEqualTo "STRING") then { + _pos = [_this] call A3EAI_SHK_pos_getPosMarker; + + // Parameter array + } else { + if (typename (_this select 0) isEqualTo "STRING") then { + _pos = _this call A3EAI_SHK_pos_getPosMarker; + } else { + _pos = _this call A3EAI_SHK_pos_getPos; + }; + }; + + // Return position + _pos +}; \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_BIN_taskPatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_BIN_taskPatrol.sqf new file mode 100644 index 0000000..497e7c5 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_BIN_taskPatrol.sqf @@ -0,0 +1,233 @@ +#include "\A3EAI\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 A3EAI (https://github.com/dayzai/A3EAI) +======================================================================================================================= +*/ + + +_unitGroup = _this select 0; +_pos = _this select 1; +_max_dist = _this select 2; +_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)); + +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 A3EAI_chance) then {_nul = [group this] call A3EAI_setRandomWaypoint;} else {_nul = [group this] spawn A3EAI_areaSearching;};",_randomizeChance]}; + if (_unitType isEqualTo "aircustom") exitWith {format ["if !(local this) exitWith {}; if (%1 call A3EAI_chance) then {_nul = [group this] call A3EAI_setRandomWaypoint;} else {_nul = [(assignedVehicle this),(group this)] spawn A3EAI_customHeliDetect;};",_randomizeChance]}; + format ["if !(local this) exitWith {}; if (%1 call A3EAI_chance) then {_nul = [group this] call A3EAI_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;}; + +_allowInNoAggroArea = ([_pos,NO_AGGRO_RANGE_MAN] call A3EAI_checkInNoAggroArea); +_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 {(25 + _slack)} else {(10 + _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}) or {([_wp_pos,NO_AGGRO_RANGE_MAN] call A3EAI_checkInNoAggroArea) or {_allowInNoAggroArea}}) then { + _retry = true; + _retryCount = 0; + _retryPos = []; + _newMaxDist = _max_dist; + while {_retry && {_retryCount < 2}} 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}) && (!([_wp_pos,NO_AGGRO_RANGE_MAN] call A3EAI_checkInNoAggroArea) or {_allowInNoAggroArea})) then { + _retry = false; + _wp_pos = _retryPos; + }; + }; + }; + + if !(_retry) then { + _a = 0 + (_wp_pos select 0); + _b = 0 + (_wp_pos select 1); + + if (_searchLoot) then { + ////////////////////////////////////////////////////////////////// + // 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(count _bldgpos != 0) then {_wp_pos = _bldgpos call A3EAI_selectRandom;}; + } else { + if (_isVehicle) then { + _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 = _wp_array + [_wp_pos]; + _wp_array pushBack _wp_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; + 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; +[_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; + +for "_i" from ((count (waypoints _unitGroup)) - 1) to 0 step -1 do { + if ((getWPPos [_unitGroup,_i]) isEqualTo [0,0,0]) then { + diag_log format ["A3EAI Error: Waypoint %1 is invalid position.",[_unitGroup,_i]]; + deleteWaypoint [_unitGroup,_i]; + }; +}; + +if (A3EAI_enableHC && {_unitType in A3EAI_HCAllowedTypes}) then {_unitGroup setVariable ["HC_Ready",true];}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UAVDetection.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UAVDetection.sqf new file mode 100644 index 0000000..19de21e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UAVDetection.sqf @@ -0,0 +1,62 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_canCall","_vehicle","_detectStartPos","_searchLength"]; +_unitGroup = _this select 0; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +if (_unitGroup getVariable ["EnemiesIgnored",false]) then {[_unitGroup,"Behavior_Reset"] call A3EAI_forceBehavior}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_canCall = true; +_searchLength = _unitGroup getVariable "SearchLength"; +if (isNil "_searchLength") then {_searchLength = (waypointPosition [_unitGroup,0]) distance2D (waypointPosition [_unitGroup,1]);}; +if (_vehicle isKindOf "Plane") then {_searchLength = _searchLength * 2;}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 %2 detection started with search length %3.",_unitGroup,(typeOf (_vehicle)),_searchLength];}; + +if ((diag_tickTime - (_unitGroup getVariable ["UVLastCall",-A3EAI_UAVCallReinforceCooldown])) > A3EAI_UAVCallReinforceCooldown) then { + _detectStartPos = getPosATL _vehicle; + _vehicle flyInHeight (FLYINHEIGHT_UAV_SEARCHING_BASE + (random FLYINHEIGHT_UAV_SEARCHING_VARIANCE)); + _unitGroup getVariable ["IsDetecting",true]; + + while {!(_vehicle getVariable ["vehicle_disabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}} do { + private ["_detected","_vehPos","_nearBlacklistAreas","_playerPos","_canReveal"]; + _vehPos = getPosATL _vehicle; + _canReveal = !((combatMode _unitGroup) isEqualTo "BLUE"); + _detected = (getPosATL _vehicle) nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_UAV]; + if ((count _detected) > 5) then {_detected resize 5}; + _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 []) && {A3EAI_UAVDetectChance call A3EAI_chance}) then { + if (_canCall) then { + if (isDedicated) then { + _nul = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]] spawn A3EAI_spawn_reinforcement; + } else { + A3EAI_spawnReinforcements_PVS = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]]; + publicVariableServer "A3EAI_spawnReinforcements_PVS"; + }; + _unitGroup setVariable ["UVLastCall",diag_tickTime]; + _canCall = false; + }; + if (_canReveal && {(_unitGroup knowsAbout _x) < 2}) then { + _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 A3EAI_radioSend; + }; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; + if (((_vehicle distance2D _detectStartPos) > _searchLength) or {_vehicle getVariable ["vehicle_disabled",false]}) exitWith {}; + uiSleep 15; + + _unitGroup getVariable ["IsDetecting",false]; + }; + + _vehicle flyInHeight (FLYINHEIGHT_UAV_PATROLLING_BASE + (random FLYINHEIGHT_UAV_PATROLLING_VARIANCE)); +}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 %2 detection end.",_unitGroup,(typeOf (_vehicle))];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UAVStartPatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UAVStartPatrol.sqf new file mode 100644 index 0000000..e287ae0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UAVStartPatrol.sqf @@ -0,0 +1,30 @@ +#include "\A3EAI\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 = (A3EAI_locationsAir call A3EAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance _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 A3EAI_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 A3EAI_SHK_pos; +[_unitGroup,2] setWaypointPosition [_detectionWaypoint,0]; + +_unitGroup setVariable ["SearchLength",(_detectionWaypoint distance _exitWaypoint)]; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 search length %2, waypoint position %3.",_unitGroup,_unitGroup getVariable "SearchLength",_locationSelected];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UGVDetection.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UGVDetection.sqf new file mode 100644 index 0000000..b3e90c0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UGVDetection.sqf @@ -0,0 +1,58 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_vehicle","_canCall"]; +_unitGroup = _this select 0; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +if (_unitGroup getVariable ["EnemiesIgnored",false]) then {[_unitGroup,"Behavior_Reset"] call A3EAI_forceBehavior}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_canCall = true; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 %2 detection start.",_unitGroup,(typeOf (_vehicle))];}; + +if ((diag_tickTime - (_unitGroup getVariable ["UVLastCall",-A3EAI_UGVCallReinforceCooldown])) > A3EAI_UGVCallReinforceCooldown) then { + _detectStartPos = getPosATL _vehicle; + _unitGroup getVariable ["IsDetecting",true]; + + while {!(_vehicle getVariable ["vehicle_disabled",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) isEqualTo "BLUE"); + _detectOrigin = [_startPos,0,getDir _vehicle,1] call A3EAI_SHK_pos; + _detected = _detectOrigin nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_UGV]; + if ((count _detected) > 5) then {_detected resize 5}; + _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 []) && {A3EAI_UGVDetectChance call A3EAI_chance}) then { + if (_canCall) then { + if (isDedicated) then { + _nul = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]] spawn A3EAI_spawn_reinforcement; + } else { + A3EAI_spawnReinforcements_PVS = [_playerPos,_x,_unitGroup getVariable ["unitLevel",0]]; + publicVariableServer "A3EAI_spawnReinforcements_PVS"; + }; + _unitGroup setVariable ["UVLastCall",diag_tickTime]; + _canCall = false; + }; + if (_canReveal && {(_unitGroup knowsAbout _x) < 2}) then { + _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 A3EAI_radioSend; + }; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; + if (((_vehicle distance2D _detectStartPos) > DETECT_LENGTH_UGV_2D) or {_vehicle getVariable ["vehicle_disabled",false]}) exitWith {}; + uiSleep 15; + }; + + _unitGroup getVariable ["IsDetecting",false]; +}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 %2 detection end.",_unitGroup,(typeOf (_vehicle))];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UGVStartPatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UGVStartPatrol.sqf new file mode 100644 index 0000000..add0509 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_UGVStartPatrol.sqf @@ -0,0 +1,23 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_tooClose","_locationSelected"]; +_unitGroup = _this select 0; + +_tooClose = true; +_locationSelected = [0,0,0]; + +while {_tooClose} do { + _locationSelected = (A3EAI_locationsLand call A3EAI_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 A3EAI_SHK_pos; +[_unitGroup,0] setWPPos _locationSelected; +[_unitGroup,1] setWPPos _locationSelected; +[_unitGroup,2] setWaypointPosition [_locationSelected,0]; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Set %1 waypoint position to %2.",_unitGroup,_locationSelected];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_areaSearching.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_areaSearching.sqf new file mode 100644 index 0000000..23bbefc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_areaSearching.sqf @@ -0,0 +1,31 @@ +#include "\A3EAI\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 A3EAI_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 A3EAI_addTemporaryWaypoint; +} forEach _searchPoints; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_customHeliDetect.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_customHeliDetect.sqf new file mode 100644 index 0000000..4e4254f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_customHeliDetect.sqf @@ -0,0 +1,34 @@ +#include "\A3EAI\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 ["vehicle_disabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}) then{ + _detectOrigin = [getPosATL _vehicle,0,getDir _vehicle,1] call A3EAI_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 []) && {A3EAI_airDetectChance call A3EAI_chance}) then { + //if (!(terrainIntersectASL [_heliAimPos,_playerAimPos]) && {!(lineIntersects [_heliAimPos,_playerAimPos,_vehicle,_x])} && {A3EAI_airDetectChance call A3EAI_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 A3EAI_radioSend; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; +}; + +_unitGroup setVariable ["IsDetecting",false]; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_defensiveAggression.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_defensiveAggression.sqf new file mode 100644 index 0000000..26f2dfb --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_defensiveAggression.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\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 A3EAI_getNoAggroStatus) exitWith {false}; + +if ((isPlayer _hitSource) && {_damage > 0.1} && {(combatMode _unitGroup isEqualTo "BLUE")}) then { + _aggroExpiry = diag_tickTime + DEFENSIVE_AGGRESSION_TIME; + _vehicle setVariable ["AggroTime",_aggroExpiry]; + [_unitGroup,"Behavior_Reset"] call A3EAI_forceBehavior; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Defensive aggression enabled for %1 %2",_unitGroup,(typeOf _vehicle)];}; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_forceBehavior.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_forceBehavior.sqf new file mode 100644 index 0000000..1b735b2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_forceBehavior.sqf @@ -0,0 +1,23 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_action", "_unitGroup"]; + +_action = _this select 1; +_unitGroup = _this select 0; + +if (_action isEqualTo "IgnoreEnemies") exitWith { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + {_x doWatch objNull} forEach (units _unitGroup); + + true +}; + +if (_action isEqualTo "Behavior_Reset") exitWith { + _unitGroup setBehaviour "AWARE"; + _unitGroup setCombatMode "YELLOW"; + + true +}; + +false \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_heliDetection.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_heliDetection.sqf new file mode 100644 index 0000000..1140f04 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_heliDetection.sqf @@ -0,0 +1,55 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_detectBase","_vehicle","_canParaDrop","_detectStartPos","_searchLength"]; +_unitGroup = _this select 0; + +if (_unitGroup getVariable ["IsDetecting",false]) exitWith {}; +if (_unitGroup getVariable ["EnemiesIgnored",false]) then {[_unitGroup,"Behavior_Reset"] call A3EAI_forceBehavior}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_searchLength = _unitGroup getVariable "SearchLength"; +if (isNil "_searchLength") then {_searchLength = (waypointPosition [_unitGroup,0]) distance2D (waypointPosition [_unitGroup,1]);}; +if (_vehicle isKindOf "Plane") then {_searchLength = _searchLength * 2;}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 %2 detection started with search length %3.",_unitGroup,(typeOf (_vehicle)),_searchLength];}; + +_detectStartPos = getPosATL _vehicle; +_canParaDrop = ((diag_tickTime - (_unitGroup getVariable ["HeliLastParaDrop",-A3EAI_paradropCooldown])) > A3EAI_paradropCooldown); +_vehicle flyInHeight (FLYINHEIGHT_AIR_SEARCHING_BASE + (random FLYINHEIGHT_AIR_SEARCHING_VARIANCE)); +_unitGroup getVariable ["IsDetecting",true]; + +while {!(_vehicle getVariable ["vehicle_disabled",false]) && {(_unitGroup getVariable ["GroupSize",-1]) > 0} && {local _unitGroup}} do { + private ["_detected","_vehPos","_nearBlacklistAreas","_playerPos","_canReveal"]; + _vehPos = getPosATL _vehicle; + _canReveal = !((combatMode _unitGroup) isEqualTo "BLUE"); + _detected = _vehPos nearEntities [[PLAYER_UNITS,"LandVehicle"],DETECT_RANGE_AIR]; + if ((count _detected) > 5) then {_detected resize 5}; + _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 (_canParaDrop) then { + _canParaDrop = false; + _unitGroup setVariable ["HeliLastParaDrop",diag_tickTime]; + _nul = [_unitGroup,_vehicle,_x] spawn A3EAI_heliParaDrop; + }; + if (_canReveal && {(_unitGroup knowsAbout _x) < 2}) then { + if (((lineIntersectsSurfaces [(aimPos _vehicle),(eyePos _x),_vehicle,_x,true,1]) isEqualTo []) && {A3EAI_airDetectChance call A3EAI_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 A3EAI_radioSend; + }; + }; + }; + }; + uiSleep 0.1; + } forEach _detected; + if (((_vehicle distance2D _detectStartPos) > _searchLength) or {_vehicle getVariable ["vehicle_disabled",false]}) exitWith {}; + uiSleep 15; + + _unitGroup getVariable ["IsDetecting",false]; +}; + +_vehicle flyInHeight (FLYINHEIGHT_AIR_PATROLLING_BASE + (random FLYINHEIGHT_AIR_PATROLLING_VARIANCE)); + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 %2 detection end.",_unitGroup,(typeOf (_vehicle))];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_heliStartPatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_heliStartPatrol.sqf new file mode 100644 index 0000000..4f3dc57 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_heliStartPatrol.sqf @@ -0,0 +1,30 @@ +#include "\A3EAI\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 = (A3EAI_locationsAir call A3EAI_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 A3EAI_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 A3EAI_SHK_pos; +[_unitGroup,2] setWaypointPosition [_detectionWaypoint,0]; + +_unitGroup setVariable ["SearchLength",(_detectionWaypoint distance2D _exitWaypoint)]; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 search length %2, waypoint position %3.",_unitGroup,_unitGroup getVariable "SearchLength",_locationSelected];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_huntKiller.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_huntKiller.sqf new file mode 100644 index 0000000..201063d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_huntKiller.sqf @@ -0,0 +1,141 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_targetPlayer","_startPos","_chaseDistance","_enableHCReady","_nearBlacklistedAreas","_targetPlayerPos"]; + +_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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]; + +_unitGroup setFormDir ([(leader _unitGroup),_targetPlayerPos] call BIS_fnc_dirTo); + +if ((_startPos distance _targetPlayerPos) < _chaseDistance) then { + private ["_leader","_ableToChase","_marker"]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 (A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: AI group %1 in pursuit state. Pursuit time remaining: %2 seconds.",_unitGroup,(_unitGroup getVariable ["pursuitTime",0]) - diag_tickTime];}; + + if ((A3EAI_enableRadioMessages) && {0.7 call A3EAI_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 A3EAI_radioSend; + }; + } count _nearbyUnits; + } else { + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,[0,[]]] call A3EAI_radioSend; + }; + } count _nearbyUnits; + }; + }; + }; + }; + if (A3EAI_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 A3EAI_selectRandom); + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Pursuit state ended for group %1. Returning to patrol state. (fn_findKiller)",_unitGroup];}; + + if (A3EAI_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 A3EAI_radioSend; + }; + } count _nearbyUnits; + }; + }; + }; + + if (_enableHCReady) then { + _unitGroup setVariable ["HC_Ready",true]; + }; + }; + + if (A3EAI_enableDebugMarkers) then { + deleteMarker _marker; + }; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_hunterLocate.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_hunterLocate.sqf new file mode 100644 index 0000000..b47c161 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_hunterLocate.sqf @@ -0,0 +1,80 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_SHK_pos; + _waypoint setWPPos _searchPos; + }; + }; + + if ((_unitGroup knowsAbout _targetPlayer) < 4) then {_unitGroup reveal [_targetPlayer,4]}; + _unitGroup setCurrentWaypoint _waypoint; + + if (A3EAI_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 A3EAI_radioSend; + }; + } count _nearbyUnits; + } else { + { + if ((isPlayer _x) && {({if (RADIO_ITEM in (assignedItems _x)) exitWith {1}} count (units (group _x))) > 0}) then { + [_x,[0,[]]] call A3EAI_radioSend; + }; + } count _nearbyUnits; + }; + }; + }; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 A3EAI_BIN_taskPatrol; + _unitGroup setSpeedMode "FULL"; + if (A3EAI_enableHC && {isDedicated}) then {_unitGroup setVariable ["MiscData",nil];}; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Dynamic group %1 is patrolling area. (%2)",_unitGroup,_exception];}; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_reinforce_begin.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_reinforce_begin.sqf new file mode 100644 index 0000000..36254f6 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_reinforce_begin.sqf @@ -0,0 +1,85 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_waypoint", "_vehicle", "_endTime", "_vehiclePos", "_nearUnits", "_vehPos", "_despawnPos", "_reinforcePos","_vehicleArmed","_paraDrop","_reinforceTime"]; + +_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 ["A3EAI Error: Invalid group %1 provided to %2.",_unitGroup,__FILE__];}; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +_vehicleArmed = ((({_x call A3EAI_checkIsWeapon} count (weapons _vehicle)) > 0) || {({_x call A3EAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) > 0} || {(_vehicle call A3EAI_countVehicleGunners) > 0}); +_reinforcePos = _unitGroup getVariable ["ReinforcePos",[0,0,0]]; + +if (_vehicleArmed) then { + _waypoint = [_unitGroup,0]; + _waypoint setWaypointStatements ["true",""]; + _waypoint setWaypointType "GUARD"; + _waypoint setWaypointBehaviour "AWARE"; + _waypoint setWaypointCombatMode "RED"; + + if (local _unitGroup) then { + _unitGroup setCurrentWaypoint _waypoint; + } else { + A3EAI_setCurrentWaypoint_PVC = _waypoint; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_setCurrentWaypoint_PVC"; + }; + + _reinforceTime = missionNamespace getVariable [format ["A3EAI_airReinforcementDuration%1",_unitGroup getVariable ["unitLevel",0]],0]; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 ((count _nearUnits) > 5) then {_nearUnits resize 5}; + { + 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 A3EAI_radioSend; + }; + }; + } forEach _nearUnits; + }; + uiSleep 15; + }; + _unitGroup setSpeedMode "NORMAL"; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 reinforcement timer complete.",_unitGroup];}; +} else { + _paraDrop = [_unitGroup,_vehicle,objNull] spawn A3EAI_heliParaDrop; + waitUntil {uiSleep 0.1; scriptDone _paraDrop}; +}; + +if (((_unitGroup getVariable ["GroupSize",-1]) < 1) or {!((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce")}) exitWith { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_SHK_pos; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 { + A3EAI_setCurrentWaypoint_PVC = _waypoint; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_setCurrentWaypoint_PVC"; +}; + +waitUntil {uiSleep 15; (((getPosATL _vehicle) distance2D _vehPos) > 1200) or {!(alive _vehicle)}}; + +_unitGroup call A3EAI_cleanupReinforcementGroup; +A3EAI_reinforcedPositions = A3EAI_reinforcedPositions - _reinforcePos; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_startHunting.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_startHunting.sqf new file mode 100644 index 0000000..c7ca2ab --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_startHunting.sqf @@ -0,0 +1,47 @@ +#include "\A3EAI\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 (A3EAI_enableRadioMessages) then { + //diag_log "DEBUG: Sending radio static"; + if ((_unitGroup getVariable ["GroupSize",0]) > 0) then { + _nearbyUnits = _targetPlayer nearEntities [["LandVehicle",PLAYER_UNITS],TRANSMIT_RANGE_RADIO_HUNTER]; + 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 A3EAI_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 setWPPos _targetPos; + _waypoint setWaypointStatements ["true","if !(local this) exitWith {}; (group this) spawn A3EAI_hunterLocate;"]; + + _unitGroup setCurrentWaypoint _waypoint; + + _unitGroup setVariable ["targetplayer",_targetPlayer]; + if (A3EAI_enableHC && {"dynamic" in A3EAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; + _unitGroup setVariable ["MiscData",_targetPlayer]; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Dynamic group %1 is now hunting player %2.",_unitGroup,_targetPlayer];}; +} else { + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3EAI_BIN_taskPatrol; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Dynamic group %1 is patrolling area.",_unitGroup];}; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehCrewRegroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehCrewRegroup.sqf new file mode 100644 index 0000000..5bf929d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehCrewRegroup.sqf @@ -0,0 +1,43 @@ +#include "\A3EAI\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 A3EAI_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 A3EAI_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 A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehCrewRegroupComplete.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehCrewRegroupComplete.sqf new file mode 100644 index 0000000..498ed4c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehCrewRegroupComplete.sqf @@ -0,0 +1,16 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_vehicle","_vehiclePos"]; + +_unitGroup = _this; + +_vehicle = _unitGroup getVariable ["assignedVehicle",objNull]; +if (isNull _vehicle) exitWith {diag_log format ["A3EAI Error: Group %1 has null vehicle.",_unitGroup];}; +_vehiclePos = getPosATL _vehicle; +[_unitGroup,0] setWaypointPosition [_vehiclePos,0]; +[_unitGroup,2] setWaypointPosition [_vehiclePos,0]; +_unitGroup call A3EAI_setVehicleRegrouped; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Regroup completed for vehicle group %1.",_unitGroup];}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehStartPatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehStartPatrol.sqf new file mode 100644 index 0000000..c01a701 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_behavior/A3EAI_vehStartPatrol.sqf @@ -0,0 +1,23 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_tooClose","_locationSelected"]; +_unitGroup = _this select 0; + +_tooClose = true; +_locationSelected = [0,0,0]; + +while {_tooClose} do { + _locationSelected = (A3EAI_locationsLand call A3EAI_selectRandom) select 1; + if (((waypointPosition [_unitGroup,0]) distance2D _locationSelected) > 300) then { + _tooClose = false; + }; +}; + +_locationSelected = [_locationSelected,random(300),random(360),0,[1,300]] call A3EAI_SHK_pos; +[_unitGroup,0] setWPPos _locationSelected; +[_unitGroup,0] setWaypointCompletionRadius 150; +if ((count (waypoints _unitGroup)) isEqualTo 1) then { + _unitGroup setCurrentWaypoint [_unitGroup,0]; +}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Set %1 waypoint position to %2.",_unitGroup,_locationSelected];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_addGroupManager.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_addGroupManager.sqf new file mode 100644 index 0000000..3d56b54 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_addGroupManager.sqf @@ -0,0 +1,236 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_unitType", "_unitTypeRef", "_vehicle", "_stuckCheckTime", "_groupLeadMarker", "_groupWPMarker", "_fncArray", "_fnc_execEveryLoop", "_fnc_checkUnits", +"_fnc_generateLoot", "_fnc_vehicleAmmoFuelCheck", "_fnc_antistuck", "_currentTime", "_managerStartTime", "_updateServerLoot", "_pullRate", "_markname", "_mark", "_markername", "_unitPos", +"_unitMarker", "_lootPool", "_result", "_debugStartTime"]; + +_unitGroup = _this select 0; +_unitLevel = _this select 1; + +if (_unitGroup getVariable ["isManaged",false]) exitWith {}; +_unitGroup setVariable ["isManaged",true]; + +_unitType = (_unitGroup getVariable ["unitType",""]); +_unitTypeRef = _unitType; +_vehicle = if (_unitType in ["air","land","aircustom","landcustom","air_reinforce","uav","ugv"]) then {_unitGroup getVariable ["assignedVehicle",(assignedVehicle (leader _unitGroup))]} else {objNull}; + +_unitGroup setVariable ["antistuckPos",(getWPPos [_unitGroup,(currentWaypoint _unitGroup)])]; +if (isNil {_unitGroup getVariable "GroupSize"}) then {_unitGroup setVariable ["GroupSize",(count (units _unitGroup))]}; +_stuckCheckTime = _unitType call A3EAI_getAntistuckTime; + +//set up debug variables +_groupLeadMarker = ""; +_groupWPMarker = ""; + +//Set up local functions +_fncArray = [_unitGroup,_unitType] call A3EAI_getLocalFunctions; +_fnc_execEveryLoop = _fncArray select 0; +_fnc_checkUnits = _fncArray select 1; +_fnc_generateLoot = _fncArray select 2; +_fnc_vehicleAmmoFuelCheck = _fncArray select 3; +_fnc_antistuck = _fncArray select 4; + +//Set up timer variables +_currentTime = diag_tickTime; +_managerStartTime = _currentTime; +_unitGroup setVariable ["lastRearmTime",_currentTime]; +_unitGroup setVariable ["antistuckTime",_currentTime]; +_unitGroup setVariable ["lootGenTime",_currentTime]; + +//Setup loot variables +_updateServerLoot = (A3EAI_enableHC && {!isDedicated}); +_pullRate = 30; + +if (isDedicated) then { + [_unitGroup,_unitType,_unitLevel] call A3EAI_setLoadoutVariables; + + if (A3EAI_enableDebugMarkers) then { + _groupLeadMarker = format ["%1_Lead",_unitGroup]; + if (_groupLeadMarker in allMapMarkers) then {deleteMarker _groupLeadMarker; uiSleep 0.5}; //Delete the previous marker if it wasn't deleted for some reason. + _groupLeadMarker = createMarker [_groupLeadMarker,getPosATL (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; uiSleep 0.5;}; //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; uiSleep 0.5}; + _mark = createMarker [_markname,getPosATL _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); + }; + }; + +} else { + waitUntil {uiSleep 0.25; (local _unitGroup)}; + [_unitGroup,_unitType,_unitLevel] call A3EAI_setLoadoutVariables_HC; + + if (A3EAI_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); + }; + + if (A3EAI_debugLevel > 1) then { + _lootPool = _unitGroup getVariable ["LootPool",[]]; + //diag_log format ["Debug: Found loot pool for group %1 from server: %2",_unitGroup,_lootPool]; + }; +}; + +//Main loop +while {(!isNull _unitGroup) && {(_unitGroup getVariable ["GroupSize",-1]) > 0}} do { + _unitType = (_unitGroup getVariable ["unitType",""]); + if !(_unitType isEqualTo _unitTypeRef) then { + _fncArray = [_unitGroup,_unitType] call A3EAI_getLocalFunctions; + _fnc_execEveryLoop = _fncArray select 0; + _fnc_checkUnits = _fncArray select 1; + _fnc_generateLoot = _fncArray select 2; + _fnc_vehicleAmmoFuelCheck = _fncArray select 3; + _fnc_antistuck = _fncArray select 4; + _stuckCheckTime = _unitType call A3EAI_getAntistuckTime; + + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Reassigned group %1 type from %2 to %3.",_unitGroup,_unitTypeRef,_unitType];}; + + _unitTypeRef = _unitType; + }; + + [_unitGroup,_vehicle] call _fnc_execEveryLoop; + + [_unitGroup] call _fnc_checkUnits; + + //Generate loot + if ((diag_tickTime - (_unitGroup getVariable ["lootGenTime",diag_tickTime])) > _pullRate) then { + [_unitGroup,_unitLevel] call _fnc_generateLoot; + }; + + //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 _fnc_vehicleAmmoFuelCheck; + }; + + //Antistuck + if ((diag_tickTime - (_unitGroup getVariable ["antistuckTime",diag_tickTime])) > _stuckCheckTime) then { + [_unitGroup,_vehicle,_stuckCheckTime] call _fnc_antistuck; + }; + + if (A3EAI_HCIsConnected && {_unitGroup getVariable ["HC_Ready",false]} && {(diag_tickTime - _managerStartTime) > 30}) then { + private ["_result"]; + _result = _unitGroup call A3EAI_transferGroupToHC; + if (_result) then { + waitUntil {sleep 1.5; (!(local _unitGroup) or {isNull _unitGroup})}; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Transferred ownership of %1 group %2 to HC %3.",_unitType,_unitGroup,A3EAI_HCObjectOwnerID];}; + waitUntil {sleep 15; ((local _unitGroup) or {isNull _unitGroup})}; + if ((_unitGroup getVariable ["GroupSize",-1]) > 0) then { + _currentTime = diag_tickTime; + _unitGroup call A3EAI_initNoAggroStatus; + _unitGroup setVariable ["lastRearmTime",_currentTime]; + _unitGroup setVariable ["antistuckTime",_currentTime]; + _unitGroup setVariable ["lootGenTime",_currentTime]; + }; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: %1 group %2 ownership was returned to server.",(_unitGroup getVariable ["unitType",_unitType]),_unitGroup];}; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Waiting to transfer %1 group %2 ownership to headless client (ID: %3).",_unitType,_unitGroup,A3EAI_HCObjectOwnerID];}; + }; + }; + + //diag_log format ["DEBUG: Group Manager cycle time for group %1: %2 seconds.",_unitGroup,(diag_tickTime - _debugStartTime)]; + if ((_unitGroup getVariable ["GroupSize",0]) > 0) then {uiSleep 15}; +}; + +if (A3EAI_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 { + A3EAI_transferGroup_PVS = _unitGroup; + publicVariableServer "A3EAI_transferGroup_PVS"; //Return ownership to server. + A3EAI_HCGroupsCount = A3EAI_HCGroupsCount - 1; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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]) isEqualTo -1) then { //GroupSize value of -1 marks group for deletion + if (!isNull _unitGroup) then { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Deleting %2 group %1.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"])]}; + _unitGroup call A3EAI_deleteGroup; + }; + }; +} else { + diag_log "A3EAI Error: An A3EAI-managed group was deleted unexpectedly!"; +}; + +if ((local _vehicle) && {isEngineOn _vehicle}) then { + _vehicle engineOn false; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_air.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_air.sqf new file mode 100644 index 0000000..b06178d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_air.sqf @@ -0,0 +1,37 @@ +#include "\A3EAI\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_AIR) or {[_checkPos,NO_AGGRO_RANGE_AIR] call A3EAI_checkInNoAggroArea}) && {_checkPos distance2D (_unitGroup getVariable ["antistuckPos",[0,0,0]]) < ANTISTUCK_MIN_TRAVEL_DIST_AIR} && {canMove _vehicle}) then { + _tooClose = true; + _wpSelect = []; + while {_tooClose} do { + _wpSelect = (A3EAI_locationsAir call A3EAI_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 A3EAI_SHK_pos; + [_unitGroup,0] setWPPos _wpSelect; + [_unitGroup,1] setWPPos _wpSelect; + if ((count (waypoints _unitGroup)) > 2) then {[_unitGroup,2] setWPPos _wpSelect;}; + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + _unitGroup setVariable ["antistuckPos",_wpSelect]; + _unitGroup setVariable ["antistuckTime",diag_tickTime + (_stuckCheckTime/2)]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_aircustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_aircustom.sqf new file mode 100644 index 0000000..b71efc5 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_aircustom.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\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 distance2D (_leader findNearestEnemy _vehicle)) > NEAREST_ENEMY_AIR) or {[_checkPos,NO_AGGRO_RANGE_AIR] call A3EAI_checkInNoAggroArea}) && {_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_generic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_generic.sqf new file mode 100644 index 0000000..14c3b2a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_generic.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\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 A3EAI_checkInNoAggroArea}) && ((_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 A3EAI_fixStuckGroup; + _unitGroup setCurrentWaypoint [_unitGroup,_nextWP]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_land.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_land.sqf new file mode 100644 index 0000000..fde77a1 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_land.sqf @@ -0,0 +1,35 @@ +#include "\A3EAI\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 A3EAI_checkInNoAggroArea}) && {((_unitGroup getVariable ["antistuckPos",[0,0,0]]) distance _checkPos) < ANTISTUCK_MIN_TRAVEL_DIST_LAND}) then { + if (canMove _vehicle) then { + [_unitGroup] call A3EAI_fixStuckGroup; + if ((count (waypoints _unitGroup)) isEqualTo 1) then { + [_unitGroup,0] setWaypointPosition [_checkPos,0]; + _unitGroup setCurrentWaypoint [_unitGroup,0]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 ["vehicle_disabled",false])) then { + [_vehicle] call A3EAI_vehDestroyed; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: AI vehicle %1 (Group: %2) is immobilized. Respawning vehicle patrol group. Damage: %3. WaterPos: %4.",(typeOf _vehicle),_unitGroup,(damage _vehicle),(surfaceIsWater _checkPos)];}; + if (A3EAI_enableDebugMarkers) then {_checkPos call A3EAI_debugMarkerLocation;}; + }; + }; +} else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_uav.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_uav.sqf new file mode 100644 index 0000000..0bf6626 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_uav.sqf @@ -0,0 +1,37 @@ +#include "\A3EAI\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_AIR) or {[_checkPos,NO_AGGRO_RANGE_UAV] call A3EAI_checkInNoAggroArea}) && {_checkPos distance2D (_unitGroup getVariable ["antistuckPos",[0,0,0]]) < ANTISTUCK_MIN_TRAVEL_DIST_AIR} && {canMove _vehicle}) then { + _tooClose = true; + _wpSelect = []; + while {_tooClose} do { + _wpSelect = (A3EAI_locationsAir call A3EAI_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 A3EAI_SHK_pos; + [_unitGroup,0] setWPPos _wpSelect; + [_unitGroup,1] setWPPos _wpSelect; + if ((count (waypoints _unitGroup)) > 2) then {[_unitGroup,2] setWPPos _wpSelect;}; + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + _unitGroup setVariable ["antistuckPos",_wpSelect]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Antistuck triggered for UAV %1 (Group: %2). Forcing next waypoint.",(typeOf _vehicle),_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_ugv.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_ugv.sqf new file mode 100644 index 0000000..ef3f4c2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_antistuck_ugv.sqf @@ -0,0 +1,34 @@ +#include "\A3EAI\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 A3EAI_checkInNoAggroArea}) && {((_unitGroup getVariable ["antistuckPos",[0,0,0]]) distance _checkPos) < ANTISTUCK_MIN_TRAVEL_DIST_LAND}) then { + if (canMove _vehicle) then { + [_unitGroup] call A3EAI_fixStuckGroup; + [_unitGroup,0] setWaypointPosition [_checkPos,0]; + [_unitGroup,1] setWaypointPosition [_checkPos,0]; + _unitGroup setCurrentWaypoint [_unitGroup,1]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 ["vehicle_disabled",false])) then { + [_vehicle] call A3EAI_UGV_destroyed; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: UGV %1 (Group: %2) is immobilized. Respawning UGV group. Damage: %3. WaterPos: %4.",(typeOf _vehicle),_unitGroup,(damage _vehicle),(surfaceIsWater _checkPos)];}; + if (A3EAI_enableDebugMarkers) then {_checkPos call A3EAI_debugMarkerLocation;}; + }; + }; +} else { + _unitGroup setVariable ["antistuckPos",_checkPos]; + _unitGroup setVariable ["antistuckTime",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_checkAmmoFuel.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_checkAmmoFuel.sqf new file mode 100644 index 0000000..ac501e3 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_checkAmmoFuel.sqf @@ -0,0 +1,13 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_unit", "_loadout", "_currentMagazines", "_magazine"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_result = _vehicle call A3EAI_reloadVehicleTurrets; //Rearms vehicle weapons/turrets individually +if ((A3EAI_debugLevel > 0) && {_result}) then {diag_log format ["A3EAI Debug: Reloaded ammo for group %1 %2.",_unitGroup,(typeOf _vehicle)];}; +if ((fuel _vehicle) < 0.50) then {_vehicle setFuel 1; if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Refueled group %1 %2.",_unitGroup,(typeOf _vehicle)];};}; +_unitGroup setVariable ["lastRearmTime",diag_tickTime]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_checkGroupUnits.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_checkGroupUnits.sqf new file mode 100644 index 0000000..a1441cc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_checkGroupUnits.sqf @@ -0,0 +1,45 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_addItem; + if (_unit getVariable ["extraMag",false]) then { + [_unit,_magazine] call A3EAI_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 A3EAI_addItem; + }; + }; + if (alive _unit) then {uiSleep 15}; + }; + }; + if (alive _unit) then { + _unit setVariable ["canCheckUnit",true]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Repacking unit manager for unit %1.",_unit];}; + }; + }; + }; + uiSleep 0.1; +} forEach (units _unitGroup); + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_air.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_air.sqf new file mode 100644 index 0000000..a5b852f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_air.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle","_inNoAggroArea","_result","_leader", "_assignedTarget"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_inArea = [_vehicle,NO_AGGRO_RANGE_AIR] call A3EAI_checkInNoAggroArea; + +if !(_inArea) then { + _leader = (leader _unitGroup); + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_AIR) then { //900: replace with engagement range + _inArea = [_assignedTarget,300] call A3EAI_checkInNoAggroArea; + }; +}; //To test! + +_result = [_unitGroup,_inArea] call A3EAI_noAggroAreaToggle; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_infantry.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_infantry.sqf new file mode 100644 index 0000000..91fe1d9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_infantry.sqf @@ -0,0 +1,34 @@ +#include "\A3EAI\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 A3EAI_checkInNoAggroArea; + +if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_MAN) then { //900: replace with engagement range + _inArea = [_assignedTarget,300] call A3EAI_checkInNoAggroArea; + }; +}; + +_result = [_unitGroup,_inArea] call A3EAI_noAggroAreaToggle; + +/* +if !(_inArea) then { + _trigger = _unitGroup getVariable "trigger"; + if !(isNil "_trigger") then { + _maxDistance = _unitGroup getVariable ["patrolDist",250]; + if ((_leader distance2D _trigger) > (_maxDistance + PATROL_DISTANCE_BUFFER)) then { + _randomWaypoint = _unitGroup call A3EAI_setRandomWaypoint; + (units _unitGroup) doMove (waypointPosition _randomWaypoint); + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 moved beyond allowed patrol radius, ordering group towards spawn center.",_unitGroup];}; + }; + }; +}; +*/ + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_uav.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_uav.sqf new file mode 100644 index 0000000..500091e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_uav.sqf @@ -0,0 +1,36 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_lastAggro","_result","_leader", "_assignedTarget"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +if (A3EAI_detectOnlyUAVs) then { + _lastAggro = _vehicle getVariable "AggroTime"; + if (isNil "AggroTime") then { + _lastAggro = 0; + _vehicle setVariable ["AggroTime",0]; + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + }; + + if ((combatMode _unitGroup) isEqualTo "YELLOW") then { + if ((_lastAggro > diag_tickTime) or {[_vehicle,NO_AGGRO_RANGE_UAV] call A3EAI_checkInNoAggroArea}) then { + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Reset Group %1 %2 UAV to non-hostile mode.",_unitGroup,(typeOf _vehicle)]}; + }; + }; +} else { + _leader = (leader _unitGroup); + _inArea = [_vehicle,NO_AGGRO_RANGE_UAV] call A3EAI_checkInNoAggroArea; + + if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_UAV) then { //900: replace with engagement range + _inArea = [_assignedTarget,300] call A3EAI_checkInNoAggroArea; + }; + }; + + _result = [_unitGroup,_inArea] call A3EAI_noAggroAreaToggle; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_ugv.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_ugv.sqf new file mode 100644 index 0000000..c9e6259 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_ugv.sqf @@ -0,0 +1,36 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_hitArray", "_hitName", "_hitPoint", "_lastRepaired","_result","_leader", "_assignedTarget"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +if (A3EAI_detectOnlyUGVs) then { + _lastAggro = _vehicle getVariable "AggroTime"; + if (isNil "AggroTime") then { + _lastAggro = 0; + _vehicle setVariable ["AggroTime",0]; + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + }; + + if ((combatMode _unitGroup) isEqualTo "YELLOW") then { + if ((_lastAggro > diag_tickTime) or {[_vehicle,NO_AGGRO_RANGE_UGV] call A3EAI_checkInNoAggroArea}) then { + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Reset Group %1 %2 UGV to non-hostile mode.",_unitGroup,(typeOf _vehicle)]}; + }; + }; +} else { + _leader = (leader _unitGroup); + _inArea = [_vehicle,NO_AGGRO_RANGE_UGV] call A3EAI_checkInNoAggroArea; + + if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_UGV) then { //900: replace with engagement range + _inArea = [_assignedTarget,300] call A3EAI_checkInNoAggroArea; + }; + }; + + _result = [_unitGroup,_inArea] call A3EAI_noAggroAreaToggle; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_vehicle.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_vehicle.sqf new file mode 100644 index 0000000..b0abfdf --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_execEveryLoop_vehicle.sqf @@ -0,0 +1,35 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_lastRegroupCheck","_inNoAggroArea","_inArea","_result","_leader", "_assignedTarget"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_leader = (leader _unitGroup); +_inArea = [_leader,NO_AGGRO_RANGE_LAND] call A3EAI_checkInNoAggroArea; + +if !(_inArea) then { + _assignedTarget = (assignedTarget (vehicle _leader)); + if ((_assignedTarget distance _leader) < NO_AGGRO_RANGE_LAND) then { //900: replace with engagement range + _inArea = [_assignedTarget,300] call A3EAI_checkInNoAggroArea; + }; +}; //To test! + +_result = [_unitGroup,_inArea] call A3EAI_noAggroAreaToggle; + +_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 A3EAI_vehCrewRegroup; + }; + + _vehicle setVariable ["LastRegroupCheck",diag_tickTime]; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateGroupLoot.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateGroupLoot.sqf new file mode 100644 index 0000000..ec54d8d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateGroupLoot.sqf @@ -0,0 +1,29 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_lootPool", "_updateServerLoot", "_pullChance", "_lootUnit", "_lootIndex", "_loot", "_unitLevel"]; + +_unitGroup = _this select 0; +_unitLevel = _this select 1; + +_lootPool = _unitGroup getVariable ["LootPool",[]]; +_updateServerLoot = (A3EAI_enableHC && {!isDedicated}); +_pullChance = missionNamespace getVariable [format ["A3EAI_lootPullChance%1",_unitLevel],0.40]; +if !(_lootPool isEqualTo []) then { + if (_pullChance call A3EAI_chance) then { + _lootUnit = (units _unitGroup) call A3EAI_selectRandom; + _lootIndex = floor (random (count _lootPool)); + _loot = _lootPool select _lootIndex; + if (alive _lootUnit) then { + if ([_lootUnit,_loot] call A3EAI_addItem) then { + _lootPool deleteAt _lootIndex; + if (_updateServerLoot) then { + [_unitGroup,_lootIndex] call A3EAI_updateServerLoot; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLoadout.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLoadout.sqf new file mode 100644 index 0000000..daefea9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLoadout.sqf @@ -0,0 +1,178 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unit", "_unitLevel", "_unitLevelInvalid", "_loadout", "_weaponSelected", "_unitLevelString", "_uniforms", "_backpacks", "_vests", "_headgears", "_magazine", "_uniformItem", "_backpackItem", "_vestItem", "_headgearItem", +"_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 ["A3EAI Error: Unit already has loadout! (%1)",__FILE__];}; + +if !(_unitLevel in A3EAI_unitLevelsAll) then { + _unitLevelInvalid = _unitLevel; + _unitLevel = A3EAI_unitLevels call A3EAI_selectRandom; + diag_log format ["A3EAI Error: Invalid unitLevel provided: %1. Generating new unitLevel value: %2. (%3)",_unitLevelInvalid,_unitLevel,__FILE__]; +}; + +_unit call A3EAI_purgeUnitGear; //Clear unwanted gear from unit first. + +_loadout = [[],[]]; +_weaponSelected = _unitLevel call A3EAI_getWeapon; +_unitLevelString = str (_unitLevel); + +_uniformChance = missionNamespace getVariable ["A3EAI_addUniformChance"+_unitLevelString,1.00]; +_uniformItem = DEFAULT_UNIFORM_ITEM; +if (_uniformChance call A3EAI_chance) then { + _uniforms = missionNamespace getVariable ["A3EAI_uniformTypes"+_unitLevelString,[]]; + if !(_uniforms isEqualTo []) then { + _uniformItem = _uniforms call A3EAI_selectRandom; + _unit forceAddUniform _uniformItem; + //diag_log format ["DEBUG: %1",_uniformItem]; + }; +} else { + _unit forceAddUniform DEFAULT_UNIFORM_ITEM; + //diag_log format ["DEBUG: %1",DEFAULT_UNIFORM_ITEM]; +}; + +_backpackChance = missionNamespace getVariable ["A3EAI_addBackpackChance"+_unitLevelString,1.00]; +if (_backpackChance call A3EAI_chance) then { + _backpacks = missionNamespace getVariable ["A3EAI_backpackTypes"+_unitLevelString,[]]; + if !(_backpacks isEqualTo []) then { + _backpackItem = _backpacks call A3EAI_selectRandom; + _unit addBackpack _backpackItem; + clearAllItemsFromBackpack _unit; + //diag_log format ["DEBUG: %1",_backpackItem]; + }; +}; + +_vestChance = missionNamespace getVariable ["A3EAI_addVestChance"+_unitLevelString,1.00]; +if (_vestChance call A3EAI_chance) then { + _vests = missionNamespace getVariable ["A3EAI_vestTypes"+_unitLevelString,[]]; + if !(_vests isEqualTo []) then { + _vestItem = _vests call A3EAI_selectRandom; + _unit addVest _vestItem; + //diag_log format ["DEBUG: %1",_vestItem]; + }; +} else { + _uniformClass = configName (configFile >> "CfgWeapons" >> _uniformItem >> "ItemInfo" >> "uniformClass"); + _isWoman = [configFile >> "CfgVehicles" >> _uniformClass,"woman",0] call BIS_fnc_returnConfigEntry; + _vestItem = if (_isWoman isEqualTo 0) then { + DEFAULT_VEST_ITEM_MALE + } else { + DEFAULT_VEST_ITEM_FEMALE + }; + _unit addVest _vestItem; + //diag_log format ["DEBUG: %1",_vestItem]; +}; + +_headgearChance = missionNamespace getVariable ["A3EAI_addHeadgearChance"+_unitLevelString,1.00]; +if (_headgearChance call A3EAI_chance) then { + _headgears = missionNamespace getVariable ["A3EAI_headgearTypes"+_unitLevelString,[]]; + if !(_headgears isEqualTo []) then { + _headgearItem = _headgears call A3EAI_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 !(A3EAI_levelRequiredGL isEqualTo -1) then {_unitLevel >= A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Modified unit %1 loadout to %2.",_unit,_loadout];}; + } + }; + }; +}; + +//Select weapon optics +_isRifle = ((getNumber (configFile >> "CfgWeapons" >> _weaponSelected >> "type")) isEqualTo 1); +if ((missionNamespace getVariable [("A3EAI_opticsChance"+_unitLevelString),3]) call A3EAI_chance) then { + _opticsList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "CowsSlot" >> "compatibleItems"); + if !(_opticsList isEqualTo []) then { + _opticsType = A3EAI_weaponOpticsList call A3EAI_selectRandom; + if (_opticsType in _opticsList) then { + if (_isRifle) then {_unit addPrimaryWeaponItem _opticsType} else {_unit addHandGunItem _opticsType}; + }; + }; +}; + +//Select weapon pointer +if ((missionNamespace getVariable [("A3EAI_pointerChance"+_unitLevelString),3]) call A3EAI_chance) then { + _pointersList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "PointerSlot" >> "compatibleItems"); + if !(_pointersList isEqualTo []) then { + _pointerType = _pointersList call A3EAI_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 [("A3EAI_muzzleChance"+_unitLevelString),3]) call A3EAI_chance) then { + _muzzlesList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "MuzzleSlot" >> "compatibleItems"); + if !(_muzzlesList isEqualTo []) then { + _muzzleType = _muzzlesList call A3EAI_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 [("A3EAI_underbarrelChance"+_unitLevelString),3]) call A3EAI_chance) then { + _underbarrelList = getArray (configFile >> "CfgWeapons" >> _weaponSelected >> "WeaponSlotsInfo" >> "UnderBarrelSlot" >> "compatibleItems"); + if !(_underbarrelList isEqualTo []) then { + _underbarrelType = _underbarrelList call A3EAI_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 ["A3EAI_gadgetsList"+_unitLevelString,[]]; +for "_i" from 0 to ((count _gadgetsArray) - 1) do { + if (((_gadgetsArray select _i) select 1) call A3EAI_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 (A3EAI_enableTempNVGs && {sunOrMoon < 1}) then { + _unit call A3EAI_addTempNVG; +}; + +//Give unit temporary first aid kits to allow self-healing (unit level 1+) +if (A3EAI_enableHealing) then { + for "_i" from 1 to (_unitLevel min 3) do { + [_unit,FIRST_AID_ITEM_AI] call A3EAI_addItem; + }; +}; + +_unit setVariable ["loadout",_loadout]; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Created loadout for unit %1 (unitLevel: %2): %3.",_unit,_unitLevel,_loadout];}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLootOnDeath.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLootOnDeath.sqf new file mode 100644 index 0000000..222e56d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLootOnDeath.sqf @@ -0,0 +1,66 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unit", "_unitLevel", "_weaponLoot", "_toolLoot", "_pistol", "_magazine", "_kryptoAmountMax", "_kryptoAmount", "_kryptoPos", "_kryptoDevice", "_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 (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Generating loot for AI unit with unitLevel %2.",_unit,_unitLevel];}; + +_weaponLoot = []; +_toolLoot = []; + +_loadout = _unit getVariable ["loadout",[[],[]]]; +_primaryWeapon = [_loadout select 0,0,""] call A3EAI_param; + +//Generate a pistol if one wasn't assigned with loadout script. +if ((getNumber (configFile >> "CfgWeapons" >> _primaryWeapon >> "type")) != 2) then { + _pistol = A3EAI_pistolList call A3EAI_selectRandom; + _magazine = getArray (configFile >> "CfgWeapons" >> _pistol >> "magazines") select 0; + _unit addMagazine _magazine; + _unit addWeapon _pistol; + if (A3EAI_debugLevel > 1) then { + _weaponLoot pushBack _pistol; + _weaponLoot pushBack _magazine + }; +}; + +//Generate Krypto +_kryptoAmountMax = missionNamespace getVariable ["A3EAI_kryptoAmount"+str(_unitLevel),0]; +_kryptoAmount = floor (random (_kryptoAmountMax + 1)); +if(_kryptoAmount > 0) then { + _kryptoPos = getPosATL _unit; + _kryptoDevice = createVehicle ["Land_MPS_EPOCH",_kryptoPos,[],1.5,"CAN_COLLIDE"]; + _kryptoDevice setVariable ["Crypto",_kryptoAmount,true]; + _kryptoDevice setVariable ["A3EAI_kryptoGenTime",diag_tickTime]; + A3EAI_kryptoObjects pushBack _kryptoDevice; + _kryptoPosEmpty = _kryptoPos findEmptyPosition [0,0.5,"Land_MPS_EPOCH"]; + if !(_kryptoPosEmpty isEqualTo []) then { + _kryptoDevice setPosATL _kryptoPosEmpty; + }; + _kryptoDevice setVelocity [0,0,0.25]; + if (A3EAI_kryptoPickupAssist > 0) then { + _kryptoPickup = [_kryptoDevice,_kryptoPos] call A3EAI_generateKryptoPickup; + }; +}; + +//Add tool items +_toolsArray = missionNamespace getVariable ["A3EAI_toolsList"+str(_unitLevel),[]]; +{ + _item = _x select 0; + if (((_x select 1) call A3EAI_chance) && {[_item,"weapon"] call A3EAI_checkClassname}) then { + _unit addWeapon _item; + if (A3EAI_debugLevel > 1) then { + _toolLoot pushBack _item; + }; + } +} forEach _toolsArray; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Generated loot for AI death: %1,%2,%3. Krypto: %4.",_weaponLoot,_toolLoot,_kryptoAmount];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLootPool.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLootPool.sqf new file mode 100644 index 0000000..23f13dc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_generateLootPool.sqf @@ -0,0 +1,73 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_lootPool", "_groupSize", "_unitType", "_lootUnit", "_unitGroup", "_lootItem"]; + +_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 { + //Add first aid kit to loot list + if (A3EAI_firstAidKitChance call A3EAI_chance) then { + _lootPool pushBack FIRST_AID_ITEM_PLAYER; + }; + + //Add food to loot list + for "_i" from 1 to A3EAI_foodLootCount do { + _lootPool pushBack (A3EAI_foodLoot call A3EAI_selectRandom); + }; + + //Add items to loot list + for "_i" from 1 to A3EAI_miscLootCount1 do { + _lootPool pushBack (A3EAI_MiscLoot1 call A3EAI_selectRandom); + }; + + //Add items to loot list + for "_i" from 1 to A3EAI_miscLootCount2 do { + _lootPool pushBack (A3EAI_MiscLoot2 call A3EAI_selectRandom); + }; + + sleep 0.5; + }; +} else { + //Generate loot all at once for dynamic AI + for "_i" from 1 to _groupSize do { + //Add first aid kit to loot list + if (A3EAI_firstAidKitChance call A3EAI_chance) then { + _lootUnit = (units _unitGroup) call A3EAI_selectRandom; + [_lootUnit,FIRST_AID_ITEM_PLAYER] call A3EAI_addItem; + }; + + //Add food to loot list + for "_i" from 1 to A3EAI_foodLootCount do { + _lootUnit = (units _unitGroup) call A3EAI_selectRandom; + _lootItem = (A3EAI_foodLoot call A3EAI_selectRandom); + [_lootUnit,_lootItem] call A3EAI_addItem; + }; + + //Add items to loot list + for "_i" from 1 to A3EAI_miscLootCount1 do { + _lootUnit = (units _unitGroup) call A3EAI_selectRandom; + _lootItem = (A3EAI_MiscLoot1 call A3EAI_selectRandom); + [_lootUnit,_lootItem] call A3EAI_addItem; + }; + + //Add items to loot list + for "_i" from 1 to A3EAI_miscLootCount2 do { + _lootUnit = (units _unitGroup) call A3EAI_selectRandom; + _lootItem = (A3EAI_MiscLoot2 call A3EAI_selectRandom); + [_lootUnit,_lootItem] call A3EAI_addItem; + }; + + sleep 0.5; + }; +}; + + +//Update local group loot pool +_unitGroup setVariable ["LootPool",_lootPool]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_getAntistuckTime.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_getAntistuckTime.sqf new file mode 100644 index 0000000..87cac9e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_getAntistuckTime.sqf @@ -0,0 +1,12 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_getLocalFunctions.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_getLocalFunctions.sqf new file mode 100644 index 0000000..45bec3a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_getLocalFunctions.sqf @@ -0,0 +1,116 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_unitType", "_fnc_execEveryLoop", "_fnc_checkUnits", "_fnc_generateLoot", "_fnc_vehicleAmmoFuelCheck", "_fnc_antistuck", "_noAggroRange"]; + +_unitGroup = _this select 0; +_unitType = _this select 1; + +call { + if (_unitType isEqualTo "static") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_infantry; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = A3EAI_antistuck_generic; + _noAggroRange = NO_AGGRO_RANGE_MAN; + }; + if (_unitType isEqualTo "random") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_infantry; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = A3EAI_antistuck_generic; + _noAggroRange = NO_AGGRO_RANGE_MAN; + }; + if (_unitType isEqualTo "dynamic") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_infantry; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = A3EAI_antistuck_generic; + _noAggroRange = NO_AGGRO_RANGE_MAN; + }; + if (_unitType isEqualTo "air") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_air; + _fnc_checkUnits = {}; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = A3EAI_checkAmmoFuel; + _fnc_antistuck = A3EAI_antistuck_air; + _noAggroRange = NO_AGGRO_RANGE_AIR; + }; + if (_unitType isEqualTo "land") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_vehicle; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = A3EAI_checkAmmoFuel; + _fnc_antistuck = A3EAI_antistuck_land; + _noAggroRange = NO_AGGRO_RANGE_LAND; + }; + if (_unitType isEqualTo "uav") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_uav; + _fnc_checkUnits = {}; + _fnc_generateLoot = {}; + _fnc_vehicleAmmoFuelCheck = A3EAI_checkAmmoFuel; + _fnc_antistuck = A3EAI_antistuck_uav; + _noAggroRange = NO_AGGRO_RANGE_UAV; + }; + if (_unitType isEqualTo "ugv") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_ugv; + _fnc_checkUnits = {}; + _fnc_generateLoot = {}; + _fnc_vehicleAmmoFuelCheck = A3EAI_checkAmmoFuel; + _fnc_antistuck = A3EAI_antistuck_ugv; + _noAggroRange = NO_AGGRO_RANGE_UGV; + }; + if (_unitType isEqualTo "air_reinforce") exitWith { + _fnc_execEveryLoop = {}; + _fnc_checkUnits = {}; + _fnc_generateLoot = {}; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = {}; + _noAggroRange = NO_AGGRO_RANGE_AIR; + }; + if (_unitType isEqualTo "vehiclecrew") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_infantry; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = A3EAI_antistuck_generic; + _noAggroRange = NO_AGGRO_RANGE_MAN; + }; + if (_unitType isEqualTo "staticcustom") exitWith { + _fnc_execEveryLoop = {}; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = A3EAI_antistuck_generic; + _noAggroRange = NO_AGGRO_RANGE_MAN; + }; + if (_unitType isEqualTo "aircustom") exitWith { + _fnc_execEveryLoop = {}; + _fnc_checkUnits = {}; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = A3EAI_checkAmmoFuel; + _fnc_antistuck = A3EAI_antistuck_aircustom; + _noAggroRange = NO_AGGRO_RANGE_AIR; + }; + if (_unitType isEqualTo "landcustom") exitWith { + _fnc_execEveryLoop = A3EAI_execEveryLoop_vehicle; + _fnc_checkUnits = A3EAI_checkGroupUnits; + _fnc_generateLoot = A3EAI_generateGroupLoot; + _fnc_vehicleAmmoFuelCheck = A3EAI_checkAmmoFuel; + _fnc_antistuck = A3EAI_antistuck_generic; + _noAggroRange = NO_AGGRO_RANGE_LAND; + }; + + _fnc_execEveryLoop = {}; + _fnc_checkUnits = {}; + _fnc_generateLoot = {}; + _fnc_vehicleAmmoFuelCheck = {}; + _fnc_antistuck = {}; + _noAggroRange = NO_AGGRO_RANGE_DEFAULT; + + diag_log format ["A3EAI Warning: Group functions for unit type %1 not found.",_unitType]; +}; + +[_fnc_execEveryLoop,_fnc_checkUnits,_fnc_generateLoot,_fnc_vehicleAmmoFuelCheck,_fnc_antistuck,_noAggroRange] \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_setLoadoutVariables.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_setLoadoutVariables.sqf new file mode 100644 index 0000000..2d68015 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_group_functions/A3EAI_setLoadoutVariables.sqf @@ -0,0 +1,47 @@ +#include "\A3EAI\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 !(_unitType in ["uav","ugv"]) then { + _useLaunchers = if !(A3EAI_levelRequiredLauncher isEqualTo -1) then {((count A3EAI_launcherTypes) > 0) && {(_unitLevel >= A3EAI_levelRequiredLauncher)}} else {false}; + _maxLaunchers = if (_useLaunchers) then {A3EAI_launchersPerGroup min _unitLevel} else {0}; + _launchersAdded = 0; + + _unitGroup setVariable ["LootPool",[]]; + _unitGroup spawn A3EAI_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 = A3EAI_launcherTypes call A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Successfully added weapon %1 and ammo %2 to unit %3.",_launchWeapon,_launchAmmo,_x];}; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Unable to add weapon %1 and ammo %2 to unit %3.",_launchWeapon,_launchAmmo,_x];}; + }; + }; + + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: %1 Unit %2 loadout: %3. unitLevel %4.",_unitType,_x,_x getVariable ["loadout",[]],_unitLevel];}; + } forEach (units _unitGroup); +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_HCMonitor.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_HCMonitor.sqf new file mode 100644 index 0000000..9e2c842 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_HCMonitor.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\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 ((A3EAI_monitorReportRate > 0) && {((_currentTime - _monitorReport) > A3EAI_monitorReportRate)}) then { + _uptime = [] call _getUptime; + diag_log format ["A3EAI Monitor: Uptime: %1:%2:%3. FPS: %4. HC Groups: %5.",_uptime select 0, _uptime select 1, _uptime select 2,round(diag_fps),A3EAI_HCGroupsCount]; + _monitorReport = _currentTime; + }; + uiSleep 30; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_addHunterGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_addHunterGroup.sqf new file mode 100644 index 0000000..38cacca --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_addHunterGroup.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_addNewGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_addNewGroup.sqf new file mode 100644 index 0000000..07374e9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_addNewGroup.sqf @@ -0,0 +1,65 @@ +#include "\A3EAI\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 = createTrigger [TRIGGER_OBJECT,_anchor,false]; + _unitGroup setVariable ["trigger",_anchor]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Created group trigger for %1 group %2.",_unitType,_unitGroup];}; + }; + if (_anchorType isEqualTo "OBJECT") exitWith { + _unitGroup setVariable ["assignedVehicle",_anchor]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: %1 group %2 has assigned vehicle %3 (%4).",_unitType,_unitGroup,_anchor,(typeOf _anchor)];}; + }; + _anchor = objNull; +}; + +_unitGroup setVariable ["unitLevel",_unitLevel]; +_unitGroup setVariable ["unitType",_unitType]; +_unitGroup setVariable ["GroupSize",_groupSize]; + +_unitGroup call A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: %1 group %2 has target player %3.",_unitType,_unitGroup,_miscData1];}; + }; + }; +}; + +if (!isNil "_miscData2") then { + +}; + +_functionCall = missionNamespace getVariable ["A3EAI_handle"+_unitType,{false}]; +_check = _unitGroup call _functionCall; + +if (A3EAI_debugLevel > 0) then { + if (_check) then { + diag_log format ["A3EAI Debug: HC received new group from server: %1. Processing new group with function %2.",_unitGroup,("A3EAI_handle"+_unitType)]; + } else { + diag_log format ["A3EAI Debug: Function %1 not found.","A3EAI_handle"+_unitType] + }; +}; + +0 = [_unitGroup,_unitLevel] spawn A3EAI_addGroupManager; + +A3EAI_HCGroupsCount = A3EAI_HCGroupsCount + 1; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_airReinforcementDetection.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_airReinforcementDetection.sqf new file mode 100644 index 0000000..40b5b50 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_airReinforcementDetection.sqf @@ -0,0 +1,28 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_unitType", "_groupSize", "_vehicle"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_radioSend; + }; + }; + } forEach _nearUnits; + }; + uiSleep 15; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_cleanupReinforcementHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_cleanupReinforcementHC.sqf new file mode 100644 index 0000000..86fd343 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_cleanupReinforcementHC.sqf @@ -0,0 +1,13 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +{_vehicle removeAllEventHandlers _x} count ["Killed","HandleDamage","GetIn","GetOut","Fired","Local","Hit"]; +_unitGroup setVariable ["GroupSize",-1]; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Releasing ownership of reinforcement group %1 to server.",_unitGroup];}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_createGroupTriggerObject.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_createGroupTriggerObject.sqf new file mode 100644 index 0000000..bd2aa12 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_createGroupTriggerObject.sqf @@ -0,0 +1,12 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_trigger","_triggerPos"]; +_unitGroup = _this select 0; +_triggerPos = _this select 1; + +_trigger = createTrigger [TRIGGER_OBJECT,_triggerPos,false]; +_unitGroup setVariable ["trigger",_trigger]; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Created group trigger object for %1 at %2.",_unitGroup,_triggerPos];}; + +_trigger \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleair.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleair.sqf new file mode 100644 index 0000000..e92483c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleair.sqf @@ -0,0 +1,24 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addVehAirEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleair_reinforce.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleair_reinforce.sqf new file mode 100644 index 0000000..3533dfd --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleair_reinforce.sqf @@ -0,0 +1,26 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addVehAirEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +[_unitGroup,_vehicle] spawn A3EAI_airReinforcementDetection; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleaircustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleaircustom.sqf new file mode 100644 index 0000000..0bb2f3e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleaircustom.sqf @@ -0,0 +1,24 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addVehAirEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handledynamic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handledynamic.sqf new file mode 100644 index 0000000..501dcdc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handledynamic.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_unitGroup call A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleland.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleland.sqf new file mode 100644 index 0000000..a2e7724 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleland.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = assignedVehicle (leader _unitGroup); +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addLandVehEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_vehicle,_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlelandcustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlelandcustom.sqf new file mode 100644 index 0000000..f99c2d7 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlelandcustom.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_vehicle = assignedVehicle (leader _unitGroup); +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addLandVehEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_vehicle,_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlerandom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlerandom.sqf new file mode 100644 index 0000000..501dcdc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlerandom.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_unitGroup call A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlestatic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlestatic.sqf new file mode 100644 index 0000000..501dcdc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlestatic.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +} forEach (units _unitGroup); + +_unitGroup call A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlestaticcustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlestaticcustom.sqf new file mode 100644 index 0000000..8d969c2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlestaticcustom.sqf @@ -0,0 +1 @@ +#include "A3EAI_handlestatic.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleuav.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleuav.sqf new file mode 100644 index 0000000..6d5fc7d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleuav.sqf @@ -0,0 +1,24 @@ +#include "\A3EAI\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 A3EAI_addUVUnitEH; +} forEach (units _unitGroup); + +_vehicle = _unitGroup getVariable ["assignedVehicle",assignedVehicle (leader _unitGroup)]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addUAVEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleugv.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleugv.sqf new file mode 100644 index 0000000..ab658dd --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handleugv.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\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 A3EAI_addUVUnitEH; +} forEach (units _unitGroup); + +_vehicle = assignedVehicle (leader _unitGroup); +_unitGroup setVariable ["assignedVehicle",_vehicle]; +(assignedDriver _vehicle) setVariable ["isDriver",true]; +_vehicle call A3EAI_addUGVEH; +_vehicle call A3EAI_secureVehicle; +_vehicle setVariable ["unitGroup",_unitGroup]; + +//if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 (Level: %2): %3, %4, %5",_unitGroup,_unitLevel,_vehicle,_unitType,_groupSize];}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlevehiclecrew.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlevehiclecrew.sqf new file mode 100644 index 0000000..8d969c2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_handlevehiclecrew.sqf @@ -0,0 +1 @@ +#include "A3EAI_handlestatic.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_requestGroupVars.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_requestGroupVars.sqf new file mode 100644 index 0000000..8b5f670 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_requestGroupVars.sqf @@ -0,0 +1,3 @@ +A3EAI_getGroupTriggerVars_PVS = _this; +publicVariableServer "A3EAI_getGroupTriggerVars_PVS"; +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setBehaviorHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setBehaviorHC.sqf new file mode 100644 index 0000000..298431a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setCurrentWaypointHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setCurrentWaypointHC.sqf new file mode 100644 index 0000000..893e288 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setGroupTriggerVars.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setGroupTriggerVars.sqf new file mode 100644 index 0000000..d37fdbd --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setGroupTriggerVars.sqf @@ -0,0 +1,35 @@ +#include "\A3EAI\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 ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Set group trigger variables for group %1. Success check: %2.",_unitGroup,_trigger isKindOf TRIGGER_OBJECT];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setLoadoutVariables_HC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setLoadoutVariables_HC.sqf new file mode 100644 index 0000000..669965a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_setLoadoutVariables_HC.sqf @@ -0,0 +1,61 @@ +#include "\A3EAI\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 !(A3EAI_levelRequiredGL isEqualTo -1) then {_unitLevel >= A3EAI_levelRequiredGL} else {false}; + { + _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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Modified unit %1 loadout to %2.",_x,_loadout];}; + } + }; + }; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 Unit %2 loadout: %3. unitLevel %4.",_unitType,_x,_x getVariable ["loadout",[]],_unitLevel];}; + } forEach (units _unitGroup); +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateGroupLootPoolHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateGroupLootPoolHC.sqf new file mode 100644 index 0000000..ac43501 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateGroupLootPoolHC.sqf @@ -0,0 +1,9 @@ +private ["_unitGroup","_lootPool"]; +_unitGroup = _this select 0; +_lootPool = _this select 1; + +_unitGroup setVariable ["LootPool",_lootPool]; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Updated group %1 loot pool to %2.",_unitGroup,_lootPool];}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateGroupSizeHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateGroupSizeHC.sqf new file mode 100644 index 0000000..0302f97 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateGroupSizeHC.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateServerLoot.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateServerLoot.sqf new file mode 100644 index 0000000..3d4282f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient/A3EAI_updateServerLoot.sqf @@ -0,0 +1,3 @@ +A3EAI_updateGroupLoot_PVS = _this; +publicVariableServer "A3EAI_updateGroupLoot_PVS"; +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_both/A3EAI_setAggroStatusRemoteGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_both/A3EAI_setAggroStatusRemoteGroup.sqf new file mode 100644 index 0000000..02cd4ad --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_both/A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_HCListener.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_HCListener.sqf new file mode 100644 index 0000000..3eb805e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_HCListener.sqf @@ -0,0 +1,49 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_HCObject","_versionHC","_compatibleVersions","_positionHC","_useRemoteConfigs"]; +_HCObject = _this select 0; +_versionHC = _this select 1; +_useRemoteConfigs = _this select 2; + +A3EAI_HC_serverResponse = false; +if (((owner A3EAI_HCObject) isEqualTo 0) && {(typeOf _HCObject) isEqualTo "HeadlessClient_F"}) then { + _compatibleVersions = [configFile >> "CfgPatches" >> "A3EAI","compatibleHCVersions",[]] call BIS_fnc_returnConfigEntry; + if (_versionHC in _compatibleVersions) then { + A3EAI_HCObject = _HCObject; + A3EAI_HCObject allowDamage false; + A3EAI_HCObject enableSimulationGlobal false; + A3EAI_HCObject addEventHandler ["Local",{ + if (_this select 1) then { + private["_unit","_unitGroup"]; + A3EAI_HCIsConnected = false; + A3EAI_HCObjectOwnerID = 0; + A3EAI_HCObject = objNull; + _unit = _this select 0; + _unitGroup = (group _unit); + _unit removeAllEventHandlers "Local"; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Deleting disconnected headless client unit %1.",typeOf _unit];}; + deleteVehicle _unit; + deleteGroup _unitGroup; + }; + }]; + A3EAI_HCObjectOwnerID = (owner A3EAI_HCObject); + A3EAI_HCIsConnected = true; + A3EAI_HC_serverResponse = if (_useRemoteConfigs) then { + A3EAI_pushedHCVariables + } else { + true + }; + _positionHC = getPosATL A3EAI_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 A3EAI_createBlackListArea; + diag_log format ["[A3EAI] Created 750m radius blacklist area at HC position %1",_positionHC]; + }; + diag_log format ["[A3EAI] Headless client %1 (owner: %2) logged in successfully.",A3EAI_HCObject,A3EAI_HCObjectOwnerID]; + } else { + diag_log format ["[A3EAI] Headless client %1 (owner: %2) has wrong A3EAI version %3 (Compatible versions: %4).",_HCObject,owner _HCObject,_versionHC,_compatibleVersions]; + }; +} else { + diag_log format ["[A3EAI] Rejecting connection from HC %1. A headless client is already connected: %2. Client object type: %3.",(_this select 1),!((owner A3EAI_HCObject) isEqualTo 0),typeOf _HCObject]; +}; + +(owner _HCObject) publicVariableClient "A3EAI_HC_serverResponse"; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_getGroupTriggerVars.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_getGroupTriggerVars.sqf new file mode 100644 index 0000000..bafa3d1 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_getGroupTriggerVars.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_trigger", "_triggerVars", "_value"]; + +_unitGroup = _this; +_trigger = _unitGroup getVariable ["trigger",A3EAI_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] +]; + +A3EAI_sendGroupTriggerVars_PVC = _triggerVars; +A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_sendGroupTriggerVars_PVC"; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Retrieved group %1 trigger variables: %2",_unitGroup,_triggerVars];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_protectRemoteGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_protectRemoteGroup.sqf new file mode 100644 index 0000000..efafe8f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_registerDeath.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_registerDeath.sqf new file mode 100644 index 0000000..f6e6684 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_registerDeath.sqf @@ -0,0 +1,22 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_victim", "_killer", "_unitGroup", "_unitLevel","_groupSize","_newGroupSize"]; + +_victim = _this select 0; +_killer = _this select 1; +_unitGroup = _this select 2; + +_victim setVariable ["A3EAI_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 ["A3EAI_airReinforcementSpawnChance%1",_unitLevel],0]) call A3EAI_chance}) then { + _unitGroup setVariable ["ReinforceAvailable",false]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 (Level %2) is calling reinforcements.",_unitGroup,_unitLevel];}; + _nul = [(getPosATL _victim),_killer,_unitLevel] spawn A3EAI_spawn_reinforcement; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_setBehavior.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_setBehavior.sqf new file mode 100644 index 0000000..298431a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferGroupToHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferGroupToHC.sqf new file mode 100644 index 0000000..4d72cea --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferGroupToHC.sqf @@ -0,0 +1,36 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_unitLevel", "_trigger", "_unitType", "_groupSize","_lootPool","_miscData","_miscData2","_groupData""_result","_vehicle"]; +_unitGroup = _this; + +if ((diag_tickTime - A3EAI_lastGroupTransfer) < 5) exitWith {false}; +A3EAI_lastGroupTransfer = diag_tickTime; + +/* +if (A3EAI_debugLevel > 1) then { + diag_log format ["%1 variables: %2",_unitGroup,(allVariables _unitGroup)]; +}; +*/ + +_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];}; + +A3EAI_transferGroup_PVC = _groupData; +A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_transferGroup_PVC"; + +_result = _unitGroup setGroupOwner A3EAI_HCObjectOwnerID; + +_result \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferGroupToServer.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferGroupToServer.sqf new file mode 100644 index 0000000..296c5e6 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferGroupToServer.sqf @@ -0,0 +1,7 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup"]; +_unitGroup = _this; +_unitGroup setGroupOwner 2; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferHunterGroupToHC.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferHunterGroupToHC.sqf new file mode 100644 index 0000000..d08c2de --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_transferHunterGroupToHC.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_targetPlayer"]; + +_unitGroup = _this select 0; +_targetPlayer = _this select 1; + +A3EAI_sendHunterGroupHC = [_unitGroup,_targetPlayer]; +A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_sendHunterGroupHC"; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_updateGroupLootPool.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_updateGroupLootPool.sqf new file mode 100644 index 0000000..d4ee940 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_updateGroupLootPool.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_updateGroupSizeServer.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_updateGroupSizeServer.sqf new file mode 100644 index 0000000..0302f97 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_headlessclient_server/A3EAI_updateGroupSizeServer.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_UAV_destroyed.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_UAV_destroyed.sqf new file mode 100644 index 0000000..0c40578 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_UAV_destroyed.sqf @@ -0,0 +1,21 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitLevel"]; + +_vehicle = (_this select 0); + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["vehicle_disabled",false]) exitWith {}; +_vehicle setVariable ["vehicle_disabled",true]; +{_vehicle removeAllEventHandlers _x} count ["Killed","HandleDamage","GetOut","Fired","Local","Hit"]; +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; +_vehicle call A3EAI_respawnAIVehicle; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitGroup setVariable ["GroupSize",-1]; +if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_UGV_destroyed.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_UGV_destroyed.sqf new file mode 100644 index 0000000..8b89151 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_UGV_destroyed.sqf @@ -0,0 +1,21 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitsAlive"]; + +_vehicle = (_this select 0); + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["vehicle_disabled",false]) exitWith {}; +_vehicle setVariable ["vehicle_disabled",true]; +{_vehicle removeAllEventHandlers _x} count ["Killed","HandleDamage","GetOut","Fired","Local","Hit"]; +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; +_vehicle call A3EAI_respawnAIVehicle; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitGroup setVariable ["GroupSize",-1]; +if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_ejectParachute.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_ejectParachute.sqf new file mode 100644 index 0000000..e5aba84 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_ejectParachute.sqf @@ -0,0 +1,13 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageHeli.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageHeli.sqf new file mode 100644 index 0000000..a780cfe --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageHeli.sqf @@ -0,0 +1,77 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_object","_hit","_damage","_source","_ammo","_partdamage","_durability","_unitGroup","_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 A3EAI_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) != AI_GROUP_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 A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageUGV.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageUGV.sqf new file mode 100644 index 0000000..f334adc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageUGV.sqf @@ -0,0 +1,22 @@ +#include "\A3EAI\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 A3EAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; + if !(isPlayer _source) exitWith {_damage = _hitPoint;}; + if ((_hit find "wheel") > -1) exitWith {_damage = _hitPoint;}; + }; +}; + +_damage \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageUnit.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageUnit.sqf new file mode 100644 index 0000000..9e22123 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageUnit.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\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 A3EAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; //No damage from any source when non-hostile + if ((side _source) isEqualTo AI_GROUP_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 (A3EAI_noCollisionDamage) exitWith {_damage = _hitPoint;}; + if ((_damage >= 0.9) && {_hit in ["","body","head"]} && {_hitPartIndex > -1}) exitWith {_object setVariable ["CollisionKilled",A3EAI_roadKillPenalty];}; + }; + }; + }; +}; + +_damage diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageVeh.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageVeh.sqf new file mode 100644 index 0000000..6404a96 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDamageVeh.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\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 A3EAI_getNoAggroStatus) exitWith {_damage = _hitPoint;}; + if ((side _source) isEqualTo AI_GROUP_SIDE) exitWith {_damage = _hitPoint;}; + if (((_hit find "wheel") > -1) && {_damage > 0.8} && {!(_object getVariable ["vehicle_disabled",false])}) exitWith { + [_object] call A3EAI_vehDestroyed; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: AI vehicle %1 (%2) is immobilized. Respawning vehicle patrol group.",_object,(typeOf _object)];}; + }; + }; +}; + +_damage \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeathEvent.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeathEvent.sqf new file mode 100644 index 0000000..dffcee2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeathEvent.sqf @@ -0,0 +1,108 @@ +#include "\A3EAI\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","GetIn","GetOut","Fired","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 A3EAI_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]; +}; + +_fnc_deathHandler = missionNamespace getVariable [format ["A3EAI_handleDeath_%1",_unitType],{diag_log format ["A3EAI Error: Death handler not found for unit type %1",_unitType];}]; +[_victim,_killer,_unitGroup,_groupIsEmpty] call _fnc_deathHandler; +//diag_log format ["Debug: Result %1",_result]; +if (_unitType in ["static","staticcustom","vehiclecrew","dynamic","random","air","aircustom","air_reinforce","land","landcustom"]) then { + 0 = [_victim,_killer,_unitGroup,_unitType,_unitsAlive] call A3EAI_handleDeath_generic; +} else { + call { + if (_unitType isEqualTo "aircrashed") exitWith {}; + if (_groupIsEmpty) then { + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; + }; + }; + }; +}; + +if !(isNull _victim) then { + { + if (_x in A3EAI_launcherTypes) exitWith { + if (_x in (weapons _victim)) then { + _victim removeWeapon _x; + } else { + _launcherItem = _x; + { + if (_launcherItem in weaponCargo _x) exitWith { + deleteVehicle _x; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Deleted WeaponHolderSimulated containing launcher %1.",_launcherItem];}; + }; + } forEach ((getPosATL _victim) nearObjects ["WeaponHolderSimulated",10]); + }; + }; + } forEach ((_victim getVariable ["loadout",[[],[]]]) select 0); + + { + if (_forEachIndex > 0) then { + _victim removeMagazines _x; + }; + } forEach ((_victim getVariable ["loadout",[[],[]]]) select 1); + + _victim removeItems FIRST_AID_ITEM_AI; + _victim removeWeapon NVG_ITEM_AI; + + if (_vehicle isEqualTo (_unitGroup getVariable ["assignedVehicle",objNull])) then { + _bodyPos = (getPosATL _victim); + _bodyPosEmpty = _bodyPos findEmptyPosition [0,1.5,DEFAULT_UNIT_CLASSNAME]; + if (_bodyPosEmpty isEqualTo []) then { + _victim setPosATL _bodyPos; + } else { + _victim setPosATL _bodyPosEmpty; + }; + _victim setVelocity [0,0,0.25]; + }; + + if (A3EAI_enableDeathMessages && {isPlayer _killer}) then { + _nul = [_killer,_victim] spawn A3EAI_sendKillMessage; + }; + + if (isDedicated) then { + if ((isPlayer _killer) && {_unitGroup getVariable ["ReinforceAvailable",false]} && {(missionNamespace getVariable [format ["A3EAI_airReinforcementSpawnChance%1",_unitLevel],0]) call A3EAI_chance}) then { + _unitGroup setVariable ["ReinforceAvailable",false]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 (Level %2) is calling reinforcements.",_unitGroup,_unitLevel];}; + _nul = [(getPosATL _victim),_killer,_unitLevel] spawn A3EAI_spawn_reinforcement; + }; + _victim setVariable ["A3EAI_deathTime",diag_tickTime]; + } else { + A3EAI_registerDeath_PVS = [_victim,_killer,_unitGroup]; + publicVariableServer "A3EAI_registerDeath_PVS"; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 AI unit %2 killed by %3, %4 units left alive in group %5.",_unitType,_victim,_killer,_unitsAlive,_unitGroup];}; +} else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: AI unit %1 killed by %2 is null.",_victim,_killer];}; +}; + +_victim diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_air.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_air.sqf new file mode 100644 index 0000000..eddf73c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_air.sqf @@ -0,0 +1,28 @@ +#include "\A3EAI\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 setVariable ["unitType","aircrashed"];}; + _parachuted = [_vehicle,_unitGroup] call A3EAI_heliEvacuated; + if (_parachuted) then { + _nul = _vehicle spawn { + _this setFuel 0; + _this setVehicleAmmo 0; + uiSleep 2.5; + _this setDamage 1; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: AI %1 pilot unit %2 was killed, ejecting surviving crew.",(typeOf _vehicle),(typeOf _victim)];}; + }; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_air_reinforce.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_air_reinforce.sqf new file mode 100644 index 0000000..bcbd50f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_air_reinforce.sqf @@ -0,0 +1 @@ +#include "A3EAI_handleDeath_air.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_aircrashed.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_aircrashed.sqf new file mode 100644 index 0000000..e69de29 diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_aircustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_aircustom.sqf new file mode 100644 index 0000000..bcbd50f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_aircustom.sqf @@ -0,0 +1 @@ +#include "A3EAI_handleDeath_air.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_dynamic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_dynamic.sqf new file mode 100644 index 0000000..4bcefdc --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_dynamic.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\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",A3EAI_defaultTrigger]; + [_trigger,true] spawn A3EAI_despawn_dynamic; //force despawning even if players are present in trigger area. + } else { + A3EAI_despawnDynamicGroup_PVS = _unitGroup; + publicVariableServer "A3EAI_despawnDynamicGroup_PVS"; + }; +} else { + if ((A3EAI_enableFindKiller) && {(combatMode _unitGroup) isEqualTo "YELLOW"}) then { + 0 = [_killer,_unitGroup] spawn A3EAI_huntKiller; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Killer-searching mode triggered for AI group %1.",_unitGroup];}; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_generic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_generic.sqf new file mode 100644 index 0000000..c6bcfeb --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_generic.sqf @@ -0,0 +1,38 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_victim","_killer","_unitGroup","_unitType","_unitsAlive","_inNoAggroArea"]; + +_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 (A3EAI_cleanupDelay isEqualTo 0) then { + throw format ["A3EAI Debug: Clearing gear for %1 (cleanupDelay = 0)",_victim]; + }; + if (_victim getVariable ["CollisionKilled",false]) then { + throw format ["A3EAI 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 A3EAI_generateLootOnDeath; + } else { + A3EAI_generateLootOnDeath_PVS = [_victim,_unitLevel]; + publicVariableServer "A3EAI_generateLootOnDeath_PVS"; + }; + } else { + if (_killer isEqualTo _victim) then { + throw format ["A3EAI Debug: %1 AI unit %2 was killed by non-player. Unit gear cleared.",_unitType,_victim]; + }; + }; +} catch { + _victim call A3EAI_purgeUnitGear; + if (A3EAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_land.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_land.sqf new file mode 100644 index 0000000..c8284a2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_land.sqf @@ -0,0 +1,67 @@ +#include "\A3EAI\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 call A3EAI_respawnAIVehicle; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: AI vehicle patrol destroyed, adding vehicle %1 to cleanup queue.",(typeOf _vehicle)];}; + }; + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_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 A3EAI_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 { + A3EAI_setDriverUnit_PVS = _newDriver; + publicVariableServer "A3EAI_setDriverUnit_PVS"; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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] call A3EAI_vehDestroyed; + }; + } forEach _groupUnits; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 vehicle %2 has single unit remaining. Adding patrol to respawn queue.",_unitGroup,(typeOf _vehicle)];}; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_landcustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_landcustom.sqf new file mode 100644 index 0000000..67192b2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_landcustom.sqf @@ -0,0 +1 @@ +#include "A3EAI_handleDeath_land.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_random.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_random.sqf new file mode 100644 index 0000000..aac46d8 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_random.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\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",A3EAI_defaultTrigger]; + [_trigger,true] spawn A3EAI_despawn_random; //force despawning even if players are present in trigger area. + } else { + A3EAI_despawnRandomGroup_PVS = _unitGroup; + publicVariableServer "A3EAI_despawnRandomGroup_PVS"; + }; +} else { + if ((A3EAI_enableFindKiller) && {(combatMode _unitGroup) isEqualTo "YELLOW"}) then { + 0 = [_killer,_unitGroup] spawn A3EAI_huntKiller; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Killer-searching mode triggered for AI group %1.",_unitGroup];}; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_static.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_static.sqf new file mode 100644 index 0000000..f6db868 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_static.sqf @@ -0,0 +1,45 @@ +#include "\A3EAI\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",A3EAI_defaultTrigger]; + +if (_groupIsEmpty) then { + if (_trigger getVariable ["respawn",true]) then { + _respawnCount = _trigger getVariable ["respawnLimit",-1]; + if !(_respawnCount isEqualTo 0) then { + [0,_trigger,_unitGroup] call A3EAI_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 { + A3EAI_setPermaDeleteSpawn_PVS = _unitGroup; + publicVariableServer "A3EAI_setPermaDeleteSpawn_PVS"; + }; + }; + } else { + if (isDedicated) then { + _nul = _trigger spawn A3EAI_deleteCustomSpawn; + } else { + A3EAI_deleteCustomSpawn_PVS = _unitGroup; + publicVariableServer "A3EAI_deleteCustomSpawn_PVS"; + }; + }; +} else { + if ((A3EAI_enableFindKiller) && {(combatMode _unitGroup) isEqualTo "YELLOW"}) then { + 0 = [_killer,_unitGroup] spawn A3EAI_huntKiller; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Killer-searching mode triggered for AI group %1.",_unitGroup];}; + }; + 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 (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: MaxUnits variable for group %1 set to %2.",_unitGroup,_maxUnits];}; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_staticcustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_staticcustom.sqf new file mode 100644 index 0000000..1fd439a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_staticcustom.sqf @@ -0,0 +1 @@ +#include "A3EAI_handleDeath_static.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_vehiclecrew.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_vehiclecrew.sqf new file mode 100644 index 0000000..1fd439a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handleDeath_vehiclecrew.sqf @@ -0,0 +1 @@ +#include "A3EAI_handleDeath_static.sqf" \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handle_death_UV.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handle_death_UV.sqf new file mode 100644 index 0000000..c7aa708 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_handle_death_UV.sqf @@ -0,0 +1,44 @@ +#include "\A3EAI\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 A3EAI_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 ["A3EAI_deathTime",diag_tickTime]; + } else { + A3EAI_registerDeath_PVS = [_victim,_killer,_unitGroup]; + publicVariableServer "A3EAI_registerDeath_PVS"; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 AI unit %2 killed by %3, %4 units left alive in group %5.",_unitType,_victim,_killer,_unitsAlive,_unitGroup];}; +} else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: AI unit %1 killed by %2 is null.",_victim,_killer];}; +}; + +_victim diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliDestroyed.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliDestroyed.sqf new file mode 100644 index 0000000..940dcfb --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliDestroyed.sqf @@ -0,0 +1,35 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitLevel"]; + +_vehicle = (_this select 0); +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (_vehicle getVariable ["vehicle_disabled",false]) exitWith {}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetIn","GetOut","Killed","Hit"]; +_vehicle setVariable ["vehicle_disabled",true]; +if !((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then {_vehicle call A3EAI_respawnAIVehicle;}; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +if !(surfaceIsWater (getPosASL _vehicle)) then { + _unitLevel = _unitGroup getVariable ["unitLevel",1]; + _unitGroup setVariable ["unitType","aircrashed"]; //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 A3EAI_ejectParachute; + _nul = [_x,objNull] call A3EAI_handleDeathEvent; + 0 = [_x,_unitLevel] spawn A3EAI_generateLootOnDeath; + }; + } count (units _unitGroup); +}; + +_unitGroup setVariable ["GroupSize",-1]; +if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliEvacuated.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliEvacuated.sqf new file mode 100644 index 0000000..726e768 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliEvacuated.sqf @@ -0,0 +1,65 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle","_vehiclePos","_unitGroup","_driver"]; + +_vehicle = (_this select 0); +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (_vehicle getVariable ["vehicle_disabled",false]) exitWith {}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_vehicle setVariable ["vehicle_disabled",true]; +if !((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then {_vehicle call A3EAI_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 A3EAI_ejectParachute; + true + } else { + 0 = [_x,_unitLevel] spawn A3EAI_generateLootOnDeath; + false + }; + } count _units; + if (_unitsAlive > 0) then { + if (isDedicated) then { + [_unitGroup,_vehicle] call A3EAI_addVehicleGroup; + } else { + _vehiclePos set [2,0]; + [_unitGroup,_vehiclePos] call A3EAI_createGroupTriggerObject; + A3EAI_addVehicleGroup_PVS = [_unitGroup,_vehicle]; + publicVariableServer "A3EAI_addVehicleGroup_PVS"; + _unitGroup setVariable ["unitType","vehiclecrew"]; + + }; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: AI %1 group %2 parachuted with %3 surviving units.",(typeOf _vehicle),_unitGroup,_unitsAlive];}; + } else { + _unitGroup setVariable ["unitType","aircrashed"]; + { + _x call A3EAI_ejectParachute; + _nul = [_x,objNull] call A3EAI_handleDeathEvent; + 0 = [_x,_unitLevel] spawn A3EAI_generateLootOnDeath; + } forEach _units; + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: AI %1 group %2 parachuted with no surviving units.",(typeOf _vehicle),_unitGroup];}; + }; +} else { + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 executed on empty AI %2 group %3.",__FILE__,(typeOf _vehicle),_unitGroup];}; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliLanded.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliLanded.sqf new file mode 100644 index 0000000..ec7e815 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliLanded.sqf @@ -0,0 +1,30 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle","_unitsAlive","_unitGroup","_vehiclePos"]; + +_vehicle = (_this select 0); +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["vehicle_disabled",false]) exitWith {}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","GetOut","Killed","Hit"]; +_vehicle setVariable ["vehicle_disabled",true]; +if !((_unitGroup getVariable ["unitType",""]) isEqualTo "air_reinforce") then {_vehicle call A3EAI_respawnAIVehicle;}; +if !(isNil {_unitGroup getVariable "dummyUnit"}) exitWith {}; + +_unitsAlive = {alive _x} count (units _unitGroup); +if (_unitsAlive > 0) then { + if (isDedicated) then { + [_unitGroup,_vehicle] call A3EAI_addVehicleGroup; + } else { + _vehiclePos = getPosATL _vehicle; + _vehiclePos set [2,0]; + [_unitGroup,_vehiclePos] call A3EAI_createGroupTriggerObject; + A3EAI_addVehicleGroup_PVS = [_unitGroup,_vehicle]; + publicVariableServer "A3EAI_addVehicleGroup_PVS"; + _unitGroup setVariable ["unitType","vehiclecrew"]; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 %2 landed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliParaDrop.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliParaDrop.sqf new file mode 100644 index 0000000..1ef95e4 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_heliParaDrop.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\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 {}; +_cargoAvailable = (_vehicle emptyPositions "cargo") min A3EAI_paradropAmount; +if (_cargoAvailable > 0) then { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_addParaGroup; + } else { + A3EAI_addParaGroup_PVS = [_vehicle,_unitGroup,_cargoAvailable,_targetPlayer]; + publicVariableServer "A3EAI_addParaGroup_PVS"; + }; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_vehDestroyed.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_vehDestroyed.sqf new file mode 100644 index 0000000..db906dd --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_events/A3EAI_vehDestroyed.sqf @@ -0,0 +1,45 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle","_unitGroup","_unitsAlive","_vehiclePos"]; + +_vehicle = (_this select 0); + +if (isNull _vehicle) exitWith {}; +if (_vehicle getVariable ["vehicle_disabled",false]) exitWith {}; +_vehicle setVariable ["vehicle_disabled",true]; +{_vehicle removeAllEventHandlers _x} count ["Killed","HandleDamage","GetOut","Fired","Local","Hit"]; +_unitGroup = _vehicle getVariable ["unitGroup",grpNull]; +_vehicle call A3EAI_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 A3EAI_addVehicleGroup; + } else { + _vehiclePos = getPosATL _vehicle; + _vehiclePos set [2,0]; + [_unitGroup,_vehiclePos] call A3EAI_createGroupTriggerObject; + A3EAI_addVehicleGroup_PVS = [_unitGroup,_vehicle]; + publicVariableServer "A3EAI_addVehicleGroup_PVS"; + _unitGroup setVariable ["unitType","vehiclecrew"]; + }; + + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: AI land vehicle patrol group %1 was converted to vehiclecrew type.",_unitGroup];}; +} else { + _unitGroup setVariable ["GroupSize",-1]; + if !(isDedicated) then { + A3EAI_updateGroupSize_PVS = [_unitGroup,-1]; + publicVariableServer "A3EAI_updateGroupSize_PVS"; + }; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Group %1 %2 destroyed at %3",_unitGroup,(typeOf _vehicle),mapGridPosition _vehicle];}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addParaGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addParaGroup.sqf new file mode 100644 index 0000000..387bd22 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addParaGroup.sqf @@ -0,0 +1,46 @@ +#include "\A3EAI\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 A3EAI_createGroup; + +for "_i" from 1 to _cargoAvailable do { + _unit = [_paraGroup,_unitLevel,[0,0,0]] call A3EAI_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 A3EAI_addTempNVG; +}; + +_unitsAlive = {alive _x} count (units _paraGroup); +_trigger = createTrigger [TRIGGER_OBJECT,_startPos,false]; +_trigger setTriggerArea [TRIGGER_SIZE_SMALL,TRIGGER_SIZE_SMALL,0,false]; +_trigger setTriggerActivation ["ANY", "PRESENT", true]; +_trigger setTriggerTimeout [TRIGGER_TIMEOUT_PARAGROUP, true]; +_trigger setTriggerText (format ["Heli AI Reinforcement %1",mapGridPosition _vehicle]); +_trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList != 0;","","0 = [thisTrigger] spawn A3EAI_despawn_static;"]; +0 = [5,_trigger,[_unitGroup],PATROL_DIST_PARAGROUP,_unitLevel,[_unitsAlive,0]] call A3EAI_initializeTrigger; + +_paraGroup setVariable ["GroupSize",_unitsAlive]; +_paraGroup setVariable ["trigger",_trigger]; + +[_trigger,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; +0 = [_trigger] spawn A3EAI_despawn_static; + +[_paraGroup,_startPos] call A3EAI_setFirstWPPos; +0 = [_paraGroup,_startPos,PATROL_DIST_PARAGROUP] spawn A3EAI_BIN_taskPatrol; +_rearm = [_paraGroup,_unitLevel] spawn A3EAI_addGroupManager; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addRespawnQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addRespawnQueue.sqf new file mode 100644 index 0000000..85332af --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addRespawnQueue.sqf @@ -0,0 +1,88 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isDedicated) then { + private ["_respawnSleep","_nextRespawnTime","_mode","_unitLevelEffective","_promoteChance","_trigger"]; + _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 + + if (isNull _trigger) then {_trigger = _unitGroup getVariable ["trigger",objNull];}; + _respawnSleep = _trigger getVariable ["respawnTime",(A3EAI_respawnTimeMin + (random A3EAI_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 + A3EAI_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Added group %1 to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",_unitGroup,(count A3EAI_respawnQueue),_respawnSleep];}; + }; + if (_mode isEqualTo 1) exitWith { + //Custom vehicle AI respawn + _spawnParams = _this select 1; //parameters used to call A3EAI_createVehicleSpawn + _respawnSleep = if ((count _spawnParams) > 5) then {_spawnParams select 5} else {600}; //calculate respawn time + + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3EAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_spawnParams]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Added custom AI vehicle %1 patrol to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",(_spawnParams select 1),(count A3EAI_respawnQueue),_respawnSleep];}; + }; + if (_mode isEqualTo 2) exitWith { + //Vehicle patrol AI respawn + _vehicleType = _this select 1; + _fastMode = if ((count _this) > 2) then {_this select 2} else {false}; //shorter wait time if retrying a spawn + + if (_vehicleType isKindOf "Air") then { + _respawnSleep = (A3EAI_respawnAirMinTime + random A3EAI_respawnTimeVarAir); + } else { + _respawnSleep = (A3EAI_respawnLandMinTime + random A3EAI_respawnTimeVarLand); + if (_fastMode) then {_respawnSleep = (_respawnSleep/4) max 180}; + }; + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3EAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_vehicleType]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Added AI vehicle patrol type %1 to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",_vehicleType,(count A3EAI_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 + + if (_vehicleType isKindOf "Air") then { + _respawnSleep = (A3EAI_respawnUAVMinTime + random A3EAI_respawnTimeVarUAV); + } else { + _respawnSleep = (A3EAI_respawnUGVMinTime + random A3EAI_respawnTimeVarUGV); + if (_fastMode) then {_respawnSleep = (_respawnSleep/4) max 180}; + }; + _nextRespawnTime = (diag_tickTime + _respawnSleep); //Determine time of next respawn + A3EAI_respawnQueue pushBack [diag_tickTime + _respawnSleep,_mode,_vehicleType]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Added unmanned vehicle patrol type %1 to respawn queue. Queue position %2. Wait Time %3 (respawnHandler)",_vehicleType,(count A3EAI_respawnQueue),_respawnSleep];}; + }; + }; + + if (!isNil "A3EAI_respawnActive") exitWith {}; //If the first respawn has already occured, no need to modify the initial wait time. + + if (!isNil "A3EAI_nextRespawnTime") then { + if (_nextRespawnTime < A3EAI_nextRespawnTime) then { //If the newest respawn is scheduled to happen sooner than the next closest respawn, reduce the initial wait time appropriately. + A3EAI_nextRespawnTime = _nextRespawnTime; //Time of next spawn + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Decreased time to next respawn to %1 seconds.",_respawnSleep];}; + }; + } else { + A3EAI_nextRespawnTime = _nextRespawnTime; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Time to first respawn set to %1 seconds.",_respawnSleep];}; + }; + + if (!isNil "A3EAI_queueActive") exitWith {}; + A3EAI_queueActive = true; //The respawn queue is established, so don't create another one until it's finished. + A3EAI_addRespawnQueueHandle = [] spawn A3EAI_processRespawn; +} else { + A3EAI_respawnGroup_PVS = _this; + publicVariableServer "A3EAI_respawnGroup_PVS"; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addVehicleGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addVehicleGroup.sqf new file mode 100644 index 0000000..eb0f9ce --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addVehicleGroup.sqf @@ -0,0 +1,80 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_vehicle", "_unitsAlive", "_unitLevel", "_trigger", "_rearm" ,"_pos", "_posReflected", "_leader"]; + +_unitGroup = _this select 0; +_vehicle = _this select 1; + +_leader = leader _unitGroup; +_pos = getPosATL _leader; +_pos set [2,0]; +_unitsAlive = {alive _x} count (units _unitGroup); + +try { + if (_unitsAlive isEqualTo 0) then { + throw format ["A3EAI 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 A3EAI_checkInNoAggroArea) then { + _pos = _pos call A3EAI_getSafePosReflected; + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + if !(_pos isEqualTo []) then { + _tempWP = [_unitGroup,_pos,format ["if !(local this) exitWith {}; [(group this),%1] call A3EAI_moveToPosAndPatrol;",PATROL_DIST_VEHICLEGROUP]] call A3EAI_addTemporaryWaypoint; + }; + } else { + _unitGroup setCombatMode "YELLOW"; + _unitGroup setBehaviour "AWARE"; + [_unitGroup,_pos] call A3EAI_setFirstWPPos; + 0 = [_unitGroup,_pos,PATROL_DIST_VEHICLEGROUP] spawn A3EAI_BIN_taskPatrol; + }; + + if (_pos isEqualTo []) then { + _unitGroup setVariable ["GroupSize",-1]; + if !(local _unitGroup) then { + A3EAI_updateGroupSize_PVC = [_unitGroup,-1]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_updateGroupSize_PVC"; + }; + + deleteVehicle _vehicle; + + throw format ["A3EAI Debug: Vehicle group %1 inside no-aggro area at %2. Deleting group.",_unitGroup,_pos]; + }; + + _unitLevel = _unitGroup getVariable ["unitLevel",1]; + _trigger = createTrigger [TRIGGER_OBJECT,_pos,false]; + _trigger setTriggerArea [TRIGGER_SIZE_SMALL,TRIGGER_SIZE_SMALL,0,false]; + _trigger setTriggerActivation ["ANY", "PRESENT", true]; + _trigger setTriggerTimeout [TRIGGER_TIMEOUT_VEHICLEGROUP, true]; + _trigger setTriggerText (format ["AI Vehicle Group %1",mapGridPosition _leader]); + _trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList != 0;","","0 = [thisTrigger] spawn A3EAI_despawn_static;"]; + 0 = [4,_trigger,[_unitGroup],PATROL_DIST_VEHICLEGROUP,_unitLevel,[_unitsAlive,0]] call A3EAI_initializeTrigger; + + _unitGroup setVariable ["GroupSize",_unitsAlive]; + _unitGroup setVariable ["unitType","vehiclecrew"]; + _unitGroup setVariable ["trigger",_trigger]; + + [_trigger,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; + 0 = [_trigger] spawn A3EAI_despawn_static; + + { + if (alive _x) then { + if ((_x getHit "legs") > 0) then {_x setHit ["legs",0]}; + unassignVehicle _x; + }; + } count (units _unitGroup); + + if !(local _unitGroup) then { + A3EAI_sendGroupTriggerVars_PVC = [_unitGroup,[_unitGroup],PATROL_DIST_VEHICLEGROUP,1,1,[_unitsAlive,0],0,"vehiclecrew",false,true]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_sendGroupTriggerVars_PVC"; + }; +} catch { + if (A3EAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addVehicleGunners.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addVehicleGunners.sqf new file mode 100644 index 0000000..8d5fdc4 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_addVehicleGunners.sqf @@ -0,0 +1,30 @@ +#include "\A3EAI\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 A3EAI_createUnit; + _gunner call A3EAI_addTempNVG; + _gunner assignAsTurret [_vehicle,_x]; + _gunner moveInTurret [_vehicle,_x]; + _gunnersAdded = _gunnersAdded + 1; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cancelDynamicSpawn.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cancelDynamicSpawn.sqf new file mode 100644 index 0000000..df3c85a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cancelDynamicSpawn.sqf @@ -0,0 +1,13 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_trigger"]; +_trigger = _this; + +A3EAI_dynTriggerArray = A3EAI_dynTriggerArray - [_trigger]; +_playerUID = _trigger getVariable "targetplayerUID"; +if (!isNil "_playerUID") then {A3EAI_failedDynamicSpawns pushBack _playerUID}; +if (A3EAI_enableDebugMarkers) then {deleteMarker str(_trigger)}; + +deleteVehicle _trigger; + +false diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cancelRandomSpawn.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cancelRandomSpawn.sqf new file mode 100644 index 0000000..794e574 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cancelRandomSpawn.sqf @@ -0,0 +1,13 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_trigger","_triggerLocation"]; +_trigger = _this; + +[_trigger,"A3EAI_randTriggerArray"] call A3EAI_updateSpawnCount; +if (A3EAI_enableDebugMarkers) then {deleteMarker (str _trigger)}; + +_triggerLocation = _trigger getVariable "triggerLocation"; +deleteLocation _triggerLocation; +deleteVehicle _trigger; + +false diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cleanupReinforcementGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cleanupReinforcementGroup.sqf new file mode 100644 index 0000000..d41e2ff --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_cleanupReinforcementGroup.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\globaldefines.hpp" + +_unitGroup = _this; + +if (!((typeName _unitGroup) isEqualTo "GROUP") || {isNull _unitGroup}) exitWith {diag_log format ["A3EAI 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 ["Killed","HandleDamage","GetIn","GetOut","Fired","Local","Hit"]; +{_x enableSimulationGlobal false;} forEach (units _unitGroup); +_unitGroup setVariable ["GroupSize",-1]; +if (A3EAI_HCIsConnected) then { + A3EAI_cleanupReinforcement_PVC = [_unitGroup,_vehicle]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_cleanupReinforcement_PVC"; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_createCustomSpawn.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_createCustomSpawn.sqf new file mode 100644 index 0000000..28bbc8f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_createCustomSpawn.sqf @@ -0,0 +1,44 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Creating custom spawn area with params %1.",_this];}; + + if !(_unitLevel in A3EAI_unitLevels) then {_unitLevel = 3;}; + + if !(surfaceIsWater _spawnPos) then { + _trigStatements = format ["0 = [%1,0,%2,thisTrigger,%3,%4] call A3EAI_createCustomInfantrySpawnQueue;",_totalAI,_patrolDist,_unitLevel,_respawnTime]; + _trigger = createTrigger [TRIGGER_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 setTriggerText _spawnName; + _trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList != 0;",_trigStatements,"0 = [thisTrigger] spawn A3EAI_despawn_static;"]; + _trigger setVariable ["respawn",_respawn]; + //_trigger setVariable ["spawnmarker",_spawnName]; + _trigger setVariable ["isCustom",true]; + if (_respawnTime > 0) then {_trigger setVariable ["respawnTime",_respawnTime];}; + + 0 = [3,_trigger,[],_patrolDist,_unitLevel,[],[_totalAI,0]] call A3EAI_initializeTrigger; + //diag_log format ["DEBUG: triggerstatements variable is %1",_trigger getVariable "triggerStatements"]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_createUnit.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_createUnit.sqf new file mode 100644 index 0000000..adf1541 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_createUnit.sqf @@ -0,0 +1,24 @@ +#include "\A3EAI\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 A3EAI_addUnitEH; +0 = [_unit, _unitLevel] call A3EAI_generateLoadout; // Assign unit loadout +0 = [_unit, _unitLevel] call A3EAI_setSkills; // Set AI skill +_unit enableFatigue false; +A3EAI_monitoredObjects pushBack _unit; + +if (_antistuck) then { + _unit setPosATL _spawnPos; + _unit setVelocity [0,0,0.5]; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: Spawned AI %1 with unitLevel %2 for group %3.",_unit,_unitLevel,_unitGroup];}; + +_unit \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_create_UV_unit.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_create_UV_unit.sqf new file mode 100644 index 0000000..85542d3 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_create_UV_unit.sqf @@ -0,0 +1,16 @@ +#include "\A3EAI\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 A3EAI_addUVUnitEH; +0 = [_unit, _unitLevel] call A3EAI_setSkills; // Set AI skill +A3EAI_monitoredObjects pushBack _unit; + +if (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Spawned UAV AI %1 with unitLevel %2 for group %3.",_unit,_unitLevel,_unitGroup];}; + +_unit \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_dynamic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_dynamic.sqf new file mode 100644 index 0000000..3937041 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_dynamic.sqf @@ -0,0 +1,82 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log "A3EAI 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 (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: All units of dynamic AI group spawned by trigger %1 have been killed. Starting force despawn in 30 seconds.",triggerText _trigger];}; + uiSleep 30; +} else { + if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: No players remain in %1. Deleting spawned AI in %2 seconds.",triggerText _trigger,A3EAI_despawnDynamicSpawnTime];}; + if (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorGreen"; + _marker setMarkerAlpha 0.7; //Light green: Active trigger awaiting despawn. + }; + }; + uiSleep A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Deleting group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3EAI_HCIsConnected) then { + A3EAI_updateGroupSize_PVC = [_x,-1]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_updateGroupSize_PVC"; + }; + } forEach _grpArray; + + //Remove dynamic trigger from global dyn trigger array and clean up trigger + [_trigger,"A3EAI_dynTriggerArray"] call A3EAI_updateSpawnCount; + if (A3EAI_enableDebugMarkers) then {deleteMarker str(_trigger)}; + + //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 + A3EAI_tempBlacklistTime)]; + A3EAI_areaBlacklists pushBack _triggerLocation; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Removing expired dynamic trigger at %1.",mapGridPosition _trigger];}; + deleteVehicle _trigger; + + true +} else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: A player has entered the trigger area at %1. Cancelling despawn.",(triggerText _trigger)];}; //Exit script if trigger has been reactivated since A3EAI_despawnDynamicSpawnTime seconds has passed. + _trigger setVariable ["isCleaning",false]; //Allow next despawn request. + _triggerStatements set [2,_deactStatements]; + _trigger setTriggerStatements _triggerStatements; + if (A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_random.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_random.sqf new file mode 100644 index 0000000..83033ef --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_random.sqf @@ -0,0 +1,82 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log "A3EAI 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 (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: All units of random AI group spawned by trigger %1 have been killed. Starting force despawn in 30 seconds.",triggerText _trigger];}; + uiSleep 30; +} else { + if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: No players remain in %1. Deleting spawned AI in %2 seconds.",triggerText _trigger,A3EAI_despawnRandomSpawnTime];}; + if (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorGreen"; + _marker setMarkerAlpha 0.7; //Light green: Active trigger awaiting despawn. + }; + }; + uiSleep A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Deleting group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3EAI_HCIsConnected) then { + A3EAI_updateGroupSize_PVC = [_x,-1]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_updateGroupSize_PVC"; + }; + } forEach _grpArray; + + //Remove random trigger from global dyn trigger array and clean up trigger + [_trigger,"A3EAI_randTriggerArray"] call A3EAI_updateSpawnCount; + if (A3EAI_enableDebugMarkers) then {deleteMarker str(_trigger)}; + + //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 + A3EAI_tempBlacklistTime)]; + A3EAI_areaBlacklists pushBack _triggerLocation; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Removing expired random trigger at %1.",mapGridPosition _trigger];}; + deleteVehicle _trigger; + + true +} else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: A player has entered the trigger area at %1. Cancelling despawn script.",(triggerText _trigger)];}; //Exit script if trigger has been reactivated since A3EAI_despawnRandomSpawnTime seconds has passed. + _trigger setVariable ["isCleaning",false]; //Allow next despawn request. + _triggerStatements set [2,_deactStatements]; + _trigger setTriggerStatements _triggerStatements; + if (A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_static.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_static.sqf new file mode 100644 index 0000000..2ef197c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_despawn_static.sqf @@ -0,0 +1,91 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Trigger %1 Group Array: %2. isCleaning: %3. In static trigger array: %4",triggerText _trigger,_grpArray,_isCleaning,(_trigger in A3EAI_staticTriggerArray)];}; +if (!(_trigger in A3EAI_staticTriggerArray) or {_isCleaning}) exitWith {if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Trigger %1 has a despawn script already running. Exiting despawn script.",triggerText _trigger];};}; + +_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 (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: No players remain in trigger area at %3. Deleting %1 AI groups in %2 seconds.",_grpCount, A3EAI_despawnWait,(triggerText _trigger)];}; + +if (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _tMarker = str (_this); + _tMarker setMarkerText "STATIC TRIGGER (DESPAWNING)"; + _tMarker setMarkerColor "ColorOrange"; + }; +}; + +if (({isNull _x} count _grpArray) < _grpCount) then {uiSleep A3EAI_despawnWait}; + +if (isNull _trigger) exitWith {[_trigger,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount}; + +if ((triggerActivated _trigger) && {({isNull _x} count _grpArray) < _grpCount}) exitWith { //Exit script if trigger has been reactivated since A3EAI_despawnWait seconds has passed. + _trigger setVariable ["isCleaning",false]; //Allow next despawn request. + _triggerStatements set [2,_deactStatements]; + _trigger setTriggerStatements _triggerStatements; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: A player has entered the trigger area at %1. Cancelling despawn script.",(triggerText _trigger)];}; + if (A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Despawning group %1 with %2 active units.",_x,(_x getVariable ["GroupSize",0])];}; + _x setVariable ["GroupSize",-1]; + if (A3EAI_HCIsConnected) then { + A3EAI_updateGroupSize_PVC = [_x,-1]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_updateGroupSize_PVC"; + }; + _grpArray set [_forEachIndex,grpNull]; + }; + }; +} forEach _grpArray; + +[_trigger,"A3EAI_staticTriggerArray"] call A3EAI_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 (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _tMarker = str (_this); + _tMarker setMarkerText "STATIC TRIGGER (INACTIVE)"; + _tMarker setMarkerColor "ColorGreen"; + }; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Despawned AI units at %1. Reset trigger's group array to: %2.",(triggerText _trigger),_trigger getVariable "GroupArray"];}; +} else { + if (A3EAI_enableDebugMarkers) then { + deleteMarker (str (_trigger)); + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Permanently deleting a static spawn at %1.",triggerText _trigger]}; + deleteVehicle _trigger; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_processRespawn.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_processRespawn.sqf new file mode 100644 index 0000000..96c628c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_processRespawn.sqf @@ -0,0 +1,124 @@ +#include "\A3EAI\globaldefines.hpp" + +waitUntil {uiSleep 3; diag_tickTime > A3EAI_nextRespawnTime}; + +A3EAI_respawnActive = true; //First respawn is now being processed, so deny subsequent attempts to modify the initial wait time. +A3EAI_queueActive = nil; +A3EAI_nextRespawnTime = nil; + +while {(count A3EAI_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 { + A3EAI_respawnQueue deleteAt _forEachIndex; + }; + } forEach A3EAI_respawnQueue; + + //Begin examining queue entries. + { + _currentRespawn = (A3EAI_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 ((triggerStatements _trigger select 1) isEqualTo "") then { + //Trigger is active, so respawn the group + _maxUnits = _trigger getVariable ["maxUnits",[1,0]]; + _respawned = [_unitGroup,_trigger,_maxUnits] call A3EAI_respawnGroup; + if ((A3EAI_debugLevel > 0) && {!_respawned}) then {diag_log format ["A3EAI Debug: No units were respawned for group %1 at %2. Group %1 reinserted into respawn queue.",_unitGroup,(triggerText _trigger)];}; + } else { + //Trigger is inactive (despawned or deleted) so clean up group instead + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Spawn area %1 has already been despawned. Cleaning up group %2.",triggerText _trigger,_unitGroup]}; + _unitGroup call A3EAI_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 A3EAI_spawnVehicleCustom; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Respawning custom AI vehicle patrol with params %1",((A3EAI_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 + A3EAI_heliTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3EAI_heliTypesUsable)); + _vehicleTypeNew = A3EAI_heliTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3EAI_spawnVehiclePatrol; + A3EAI_heliTypesUsable deleteAt _index; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Respawning AI air vehicle type patrol %1.",_vehicleTypeNew]}; + } else { + if (_vehicleTypeOld isKindOf "LandVehicle") then { //Land-type vehicle AI patrol respawn + A3EAI_vehTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3EAI_vehTypesUsable)); + _vehicleTypeNew = A3EAI_vehTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3EAI_spawnVehiclePatrol; + A3EAI_vehTypesUsable deleteAt _index; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 { + A3EAI_UAVTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3EAI_UAVTypesUsable)); + _vehicleTypeNew = A3EAI_UAVTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3EAI_spawn_UV_patrol; + A3EAI_UAVTypesUsable deleteAt _index; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Respawning UAV patrol %1.",_vehicleTypeNew]}; + } else { + if (_vehicleTypeOld isKindOf "LandVehicle") then { + A3EAI_UGVTypesUsable pushBack _vehicleTypeOld; + _index = floor (random (count A3EAI_UGVTypesUsable)); + _vehicleTypeNew = A3EAI_UGVTypesUsable select _index; + _nul = _vehicleTypeNew spawn A3EAI_spawn_UV_patrol; + A3EAI_UGVTypesUsable deleteAt _index; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Respawning UGV patrol type %1.",_vehicleTypeNew]}; + }; + }; + }; + }; + A3EAI_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 A3EAI_respawnQueue; + if (_minDelay > -1) then { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 groups left in respawn queue. Next group is scheduled to respawn in %2 seconds.",(count A3EAI_respawnQueue),_minDelay];}; + uiSleep _minDelay; + }; +}; + +A3EAI_respawnActive = nil; +if (A3EAI_debugLevel > 0) then {diag_log "A3EAI Debug: Respawn queue is empty. Exiting respawn handler. (respawnHandler)";}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_respawnAIVehicle.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_respawnAIVehicle.sqf new file mode 100644 index 0000000..2fff234 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_respawnAIVehicle.sqf @@ -0,0 +1,34 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle", "_vehicleType", "_spawnParams"]; + +_vehicle = _this; +if (isNull _vehicle) exitWith {diag_log format ["Error: %1 attempted to respawn null vehicle",__FILE__];}; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Respawning AI vehicle %1.",_vehicle]}; +if (isDedicated) then { + + _vehicleType = (typeOf _vehicle); + _spawnParams = _vehicle getVariable ["spawnParams",[]]; + _vehicleClass = [configFile >> "CfgVehicles" >> _vehicleType,"vehicleClass",""] call BIS_fnc_returnConfigEntry; + if !((toLower _vehicleClass) isEqualTo "autonomous") then { + if (_spawnParams isEqualTo []) then { + [2,_vehicleType] call A3EAI_addRespawnQueue; + } else { + if (_spawnParams select 4) then { + [1,_spawnParams] call A3EAI_addRespawnQueue; + }; + }; + if (_vehicleType isKindOf "Air") then {A3EAI_curHeliPatrols = A3EAI_curHeliPatrols - 1} else {A3EAI_curLandPatrols = A3EAI_curLandPatrols - 1}; + } else { + [3,_vehicleType] call A3EAI_addRespawnQueue; + if (_vehicleType isKindOf "Air") then {A3EAI_curUAVPatrols = A3EAI_curUAVPatrols - 1} else {A3EAI_curUGVPatrols = A3EAI_curUGVPatrols - 1}; + }; + _vehicle setVariable ["A3EAI_deathTime",diag_tickTime]; //mark vehicle for cleanup +} else { + A3EAI_respawnVehicle_PVS = _vehicle; + publicVariableServer "A3EAI_respawnVehicle_PVS"; +}; + +{_vehicle removeAllEventHandlers _x} count ["HandleDamage","Killed","GetOut","Local","Hit"]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_respawnGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_respawnGroup.sqf new file mode 100644 index 0000000..02d828d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_respawnGroup.sqf @@ -0,0 +1,91 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_trigger","_patrolDist","_spawnPositions","_spawnPos","_startTime","_maxUnits","_totalAI","_aiGroup","_unitLevel","_unitLevelEffective", "_checkPos","_spawnRadius", +"_unitType","_spawnChance","_playerDistNoLOS","_playerDistWithLOS","_isCustomGroup"]; + +_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 setVariable ["unitType",_unitType]; +}; + +_isCustomGroup = _trigger getVariable ["isCustom",false]; +_playerDistWithLOS = if (_isCustomGroup) then {PLAYER_DISTANCE_WITH_LOS_STATIC_CUSTOM} else {PLAYER_DISTANCE_WITH_LOS_STATIC}; +_playerDistNoLOS = if (_isCustomGroup) then {PLAYER_DISTANCE_NO_LOS_STATIC_CUSTOM} else {PLAYER_DISTANCE_NO_LOS_STATIC}; + +_totalAI = 0; +_spawnPos = []; +_checkPos = false; +_spawnChance = if (_isCustomGroup) then {_trigger getVariable ["spawnChance",1]} else {(_trigger getVariable ["spawnChance",1]) * A3EAI_spawnChanceMultiplier}; + +if (_spawnChance call A3EAI_chance) then { + _totalAI = ((_maxUnits select 0) + round(random (_maxUnits select 1))); + if !(_spawnPositions isEqualTo []) then { + _spawnPos = _spawnPositions call A3EAI_findSpawnPos; + } else { + _checkPos = true; + _attempts = 0; + _continue = true; + _spawnRadius = _patrolDist; + + while {_continue && {(_attempts < 3)}} do { + _spawnPosSelected = [(getPosATL _trigger),random (_spawnRadius),random(360),0] call A3EAI_SHK_pos; + _spawnPosSelASL = ATLToASL _spawnPosSelected; + if ((count _spawnPosSelected) isEqualTo 2) then {_spawnPosSelected set [2,0];}; + if ( + !((_spawnPosSelASL) call A3EAI_posInBuilding) && + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPosSelected select 0),(_spawnPosSelected select 1),(_spawnPosSelASL select 2) + 1.7],_x] call A3EAI_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 A3EAI_addRespawnQueue; + false +}; + +_aiGroup = [_totalAI,_unitGroup,_unitType,_spawnPos,_trigger,_unitLevelEffective,_checkPos] call A3EAI_spawnGroup; +if (isNull _unitGroup) then {diag_log format ["A3EAI Error: Respawned group at %1 was null group. New group reassigned: %2.",triggerText _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 A3EAI_selectRandom); + } else { + _nul = [_unitGroup,(getPosATL _trigger),_patrolDist] spawn A3EAI_BIN_taskPatrol; + }; +} else { + [_unitGroup, 0] setWaypointType "GUARD"; +}; + +if (_unitType in A3EAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +if (A3EAI_enableDebugMarkers) then { + _nul = _trigger call A3EAI_addMapMarker; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: %1 AI units respawned for group %2 (unitLevel %3) at %4 in %5 seconds (respawnBandits).",_totalAI,_unitGroup,_unitLevelEffective,(triggerText _trigger),diag_tickTime - _startTime];}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_setup_randomspawns.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_setup_randomspawns.sqf new file mode 100644 index 0000000..a9b72b2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_setup_randomspawns.sqf @@ -0,0 +1,67 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_maxRandomSpawns","_attempts","_trigPos","_trigger","_markername","_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 = (A3EAI_locations call A3EAI_selectRandom) select 1; + [_randomLocation,CREATE_RANDOM_SPAWN_DIST_BASE+(random CREATE_RANDOM_SPAWN_DIST_VARIANCE),(random 360),0] call A3EAI_SHK_pos + } else { + ["A3EAI_centerMarker",0] call A3EAI_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 + A3EAI_distanceBetweenRandomSpawns)) exitWith {1}} count A3EAI_randTriggerArray) > 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 { + _spawnParams = _trigPos call A3EAI_getSpawnParams; + _trigger = createTrigger [TRIGGER_OBJECT,_trigPos,false]; + _location = [_trigPos,TEMP_BLACKLIST_AREA_RANDOM_SIZE] call A3EAI_createBlackListAreaRandom; + _trigger setVariable ["triggerLocation",_location]; + [_trigger,"A3EAI_randTriggerArray"] call A3EAI_updateSpawnCount; + _onActStatements = format ["0 = [%1,thisTrigger,thisList,%2,%3,%4,%5] call A3EAI_createRandomInfantrySpawnQueue;",PATROL_DIST_RANDOM,_spawnParams select 0,_spawnParams select 1,_spawnParams select 2,_spawnParams select 3]; + _trigger setTriggerArea [TRIGGER_SIZE_SMALL,TRIGGER_SIZE_SMALL,0,false]; + _trigger setTriggerActivation ["ANY", "PRESENT", true]; + _trigger setTriggerTimeout [TRIGGER_TIMEOUT_RANDOM, true]; + _trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList != 0;",_onActStatements,"[thisTrigger] spawn A3EAI_despawn_random;"]; + if (A3EAI_enableDebugMarkers) then { + _markername = str(_trigger); + _marker = createMarker[_markername,_trigPos]; + _marker setMarkerShape "ELLIPSE"; + _marker setMarkerType "Flag"; + _marker setMarkerBrush "SOLID"; + _marker setMarkerSize [600, 600]; + _marker setMarkerColor "ColorYellow"; + _marker setMarkerAlpha 0.6; + A3EAI_mapMarkerArray set [(count A3EAI_mapMarkerArray),_marker]; + }; + _trigger setTriggerText format ["Random Spawn at %1",(mapGridPosition _trigger)]; + _trigger setVariable ["timestamp",diag_tickTime]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Random spawn %1 of %2 placed at %3 with params %4 (Attempts: %5).",_i,_maxRandomSpawns,_trigPos,_spawnParams,_attempts];}; + } else { + if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: Could not find suitable location to place random spawn %1 of %2.",_i,_maxRandomSpawns];}; + }; + uiSleep 3; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnGroup.sqf new file mode 100644 index 0000000..e74be7f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnGroup.sqf @@ -0,0 +1,75 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_totalAI","_spawnPos","_unitGroup","_trigger","_attempts","_baseDist","_dummy","_unitLevel","_checkPos"]; + + +_totalAI = _this select 0; +_unitGroup = _this select 1; +_unitType = _this select 2; +_spawnPos = _this select 3; +_trigger = _this select 4; +_unitLevel = _this select 5; +_checkPos = if ((count _this) > 6) then {_this select 6} else {false}; + +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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_createGroup; +}; + +for "_i" from 1 to (_totalAI max 1) do { + private ["_unit"]; + _unit = [_unitGroup,_unitLevel,_spawnPos,true] call A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format["A3EAI 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 "FULL"; +_unitGroup setCombatMode "YELLOW"; +_unitGroup allowFleeing 0; + +0 = [_unitGroup,_unitLevel] spawn A3EAI_addGroupManager; //start group-level manager + +if (_unitType in A3EAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +_unitGroup diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnInfantryCustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnInfantryCustom.sqf new file mode 100644 index 0000000..df96578 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnInfantryCustom.sqf @@ -0,0 +1,67 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_totalAI", "_patrolDist", "_trigger", "_unitLevel", "_grpArray", "_triggerPos", "_maxUnits", "_attempts", "_continue", "_spawnPos", "_spawnPosSelected", "_unitGroup", "_triggerStatements","_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Active groups found at custom spawn %1. Exiting spawn script.",(triggerText _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 A3EAI_SHK_pos; + _spawnPosSelASL = ATLToASL _spawnPosSelected; + if ((count _spawnPosSelected) isEqualTo 2) then {_spawnPosSelected set [2,0];}; + if ( + !(_spawnPosSelASL call A3EAI_posInBuilding) && + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPosSelected select 0),(_spawnPosSelected select 1),(_spawnPosSelASL select 2) + 1.7],_x] call A3EAI_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Unable to find suitable spawn position. (attempt %1/3).",_attempts];}; + }; +}; + +_unitGroup = grpNull; +if !(_spawnPos isEqualTo []) then { + _unitGroup = [_totalAI,_unitGroup,"staticcustom",_spawnPos,_trigger,_unitLevel,true] call A3EAI_spawnGroup; + if (_patrolDist > 1) then { + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3EAI_BIN_taskPatrol; + } else { + [_unitGroup, 0] setWaypointType "HOLD"; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: Spawned a group of %1 units in %2 seconds at custom spawn %3.",_totalAI,(diag_tickTime - _startTime),(triggerText _trigger)];}; +} else { + _unitGroup = ["staticcustom",true] call A3EAI_createGroup; + _unitGroup setVariable ["trigger",_trigger]; + _unitGroup setVariable ["GroupSize",0]; + [0,_trigger,_unitGroup,true] call A3EAI_addRespawnQueue; + if (A3EAI_debugLevel > 0) then {diag_log format["A3EAI Debug: Unable to find suitable spawn position at custom spawn %1.",(triggerText _trigger)];}; +}; + +_grpArray pushBack _unitGroup; + +_unitGroup diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_dynamic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_dynamic.sqf new file mode 100644 index 0000000..225139b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_dynamic.sqf @@ -0,0 +1,90 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_patrolDist","_trigger","_totalAI","_unitGroup","_targetPlayer","_playerPos","_playerDir","_spawnPos","_spawnPosASL","_startTime","_behavior","_triggerStatements","_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Cancelling dynamic spawn for target player. Reason: Player does not exist (logged out?).",name _targetPlayer]}; + _nul = _trigger call A3EAI_cancelDynamicSpawn; + + false +}; + +_trigger setTriggerArea [TRIGGER_SIZE_EXPANDED,TRIGGER_SIZE_EXPANDED,0,false]; //Expand trigger area to prevent players from quickly leaving and start respawn process immediately +_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 A3EAI_SHK_pos; +_spawnPosASL = ATLToASL _spawnPos; +if ((count _spawnPos) isEqualTo 2) then {_spawnPos set [2,0];}; +_triggerLocation = _trigger getVariable ["triggerLocation",locationNull]; + +if ( + (surfaceIsWater _spawnPos) or + {({if ((isPlayer _x) && {([eyePos _x,[(_spawnPos select 0),(_spawnPos select 1),(_spawnPosASL select 2) + 1.7],_x] call A3EAI_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 [])} +) exitWith { + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Canceling dynamic spawn for target player %1. Possible reasons: Spawn position has water, player nearby, or is blacklisted.",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 [])]; + }; + _nul = _trigger call A3EAI_cancelDynamicSpawn; + + false + +}; + +_totalAI = ((_minAI + floor (random (_addAI + 1))) max 1); +_unitGroup = [_totalAI,grpNull,"dynamic",_spawnPos,_trigger,_unitLevel,true] call A3EAI_spawnGroup; +//To do: Handle case where player suddenly within close proximity + +//Set group variables +_unitGroup setBehaviour "AWARE"; + +//Begin hunting player or patrolling area +_behavior = if (A3EAI_spawnHunterChance call A3EAI_chance) then { + _unitGroup reveal [_targetPlayer,4]; + 0 = [_unitGroup,_patrolDist,_targetPlayer,getPosATL _trigger] spawn A3EAI_startHunting; + "HUNT PLAYER" +} else { + if ((_spawnPos distance _playerPos) < DUMBFIRE_AI_DISTANCE) then { + [_unitGroup,_playerPos] call A3EAI_setFirstWPPos; + }; + 0 = [_unitGroup,_playerPos,_patrolDist] spawn A3EAI_BIN_taskPatrol; + "PATROL AREA" +}; +if (A3EAI_debugLevel > 0) then { + diag_log format["A3EAI 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]; +}; + +_triggerStatements = (triggerStatements _trigger); +if (!(_trigger getVariable ["initialized",false])) then { + 0 = [1,_trigger,[_unitGroup]] call A3EAI_initializeTrigger; //set dynamic trigger variables and create dynamic area blacklist + _trigger setVariable ["triggerStatements",+_triggerStatements]; +}; +//_triggerStatements set [1,""]; +_trigger setTriggerStatements _triggerStatements; +[_trigger,"A3EAI_dynTriggerArray"] call A3EAI_updateSpawnCount; + +if (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; //Dark orange: Activated trigger + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_random.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_random.sqf new file mode 100644 index 0000000..127c000 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_random.sqf @@ -0,0 +1,119 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_patrolDist","_trigger","_totalAI","_minAI","_addAI","_unitLevel","_unitGroup","_playerPos","_spawnPos","_startTime","_baseDist","_extraDist","_triggerStatements","_spawnDist","_thisList" ,"_spawnPosSelected","_spawnChance","_firstActualPlayer"]; + + +_startTime = diag_tickTime; + +_patrolDist = _this select 0; +_trigger = _this select 1; +_thisList = _this select 2; +_minAI = _this select 3; +_addAI = _this select 4; +_unitLevel = _this select 5; +_spawnChance = _this select 6; + +try { + _spawnChance = (_spawnChance * A3EAI_spawnChanceMultiplier); + if (_spawnChance call A3EAI_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; + _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 (A3EAI_enableDebugMarkers) then { + str (_trigger) setMarkerPos _playerPos; + }; + }; + }; + } forEach _thisList; + + if (isNull _firstActualPlayer) then { + throw format ["A3EAI Debug: No players of type %1 found.",PLAYER_UNITS] + }; + + _trigger setTriggerArea [TRIGGER_SIZE_EXPANDED,TRIGGER_SIZE_EXPANDED,0,false]; //Expand trigger area to prevent players from quickly leaving and start respawn process immediately + _triggerPos = getPosATL _trigger; + + _nearAttempts = 0; + _spawnPos = []; + while {(_spawnPos isEqualTo []) && {_nearAttempts < 4}} do { + _spawnPosSelected = [_triggerPos,(_baseDist + (random _extraDist)),(random 360),0] call A3EAI_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 A3EAI_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,true] call A3EAI_spawnGroup; + + if (isNull _unitGroup) then { + throw format ["A3EAI Debug: Random group spawn position too close to a player at %1. Spawn cancelled.",_spawnPos]; + }; + _unitGroup setBehaviour "AWARE"; + _unitGroup setSpeedMode "FULL"; + + if ((_spawnPos distance _playerPos) < DUMBFIRE_AI_DISTANCE) then { + [_unitGroup,_playerPos] call A3EAI_setFirstWPPos; + }; + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3EAI_BIN_taskPatrol; + + if (A3EAI_debugLevel > 0) then { + diag_log format["A3EAI 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]; + }; + + _triggerStatements = (triggerStatements _trigger); + if (!(_trigger getVariable ["initialized",false])) then { + 0 = [2,_trigger,[_unitGroup]] call A3EAI_initializeTrigger; + _trigger setVariable ["triggerStatements",+_triggerStatements]; + }; + _triggerStatements set [1,""]; + _trigger setTriggerStatements _triggerStatements; + + if (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; //Dark orange: Activated trigger + }; + }; + + true + } else { + throw format ["A3EAI Debug: Conditional checks failed for random spawn at %1. Canceling spawn.",(mapGridPosition _trigger)]; + }; + } else { + throw format ["A3EAI Debug: Probability check failed for random spawn at %1. Canceling spawn.",(mapGridPosition _trigger)]; + }; +} catch { + _nul = _trigger call A3EAI_cancelRandomSpawn; + if (A3EAI_debugLevel > 0) then { + diag_log _exception; + }; +}; + +true + diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_static.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_static.sqf new file mode 100644 index 0000000..9d865d7 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnUnits_static.sqf @@ -0,0 +1,70 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_minAI","_addAI","_patrolDist","_trigger","_unitLevel","_numGroups","_grpArray","_triggerPos","_startTime","_totalSpawned","_triggerStatements","_groupsActive","_spawnChance"]; + +_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. +//_positionArray = _this select 4; //Array of manually-defined spawn points (markers). If empty, nearby buildings are used as spawn points. +_unitLevel = if ((count _this) > 5) then {_this select 5} else {1}; //(Optional) Select the item probability table to use +_numGroups = if ((count _this) > 6) then {_this select 6} else {1}; //(Optional) Number of groups of x number of units each to spawn + +_startTime = diag_tickTime; + +_grpArray = _trigger getVariable ["GroupArray",[]]; +_groupsActive = count _grpArray; + +_trigger setTriggerArea [TRIGGER_SIZE_EXPANDED,TRIGGER_SIZE_EXPANDED,0,false]; //Expand trigger area to prevent players from quickly leaving and start respawn process immediately +_triggerPos = getPosATL _trigger; + +//If trigger already has defined spawn points, then reuse them instead of recalculating new ones. +_locationArray = _trigger getVariable ["locationArray",[]]; +_totalSpawned = 0; + +//Spawn groups +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Trigger %1 is spawning units...",triggerText _trigger]}; +for "_j" from 1 to (_numGroups - _groupsActive) do { + private ["_unitGroup","_spawnPos","_totalAI"]; + _totalAI = 0; + _spawnPos = []; + _spawnChance = ((_trigger getVariable ["spawnChance",1]) * A3EAI_spawnChanceMultiplier); + if ((_trigger getVariable ["spawnChance",1]) call A3EAI_chance) then { + _totalAI = ((_minAI + round(random _addAI)) min MAX_UNITS_PER_STATIC_SPAWN); + _spawnPos = if ((count _locationArray) > 0) then {_locationArray call A3EAI_findSpawnPos} else {[(getPosATL _trigger),random (_patrolDist),random(360),0] call A3EAI_SHK_pos}; + }; + + //If non-zero unit amount and valid spawn position, spawn group, otherwise add it to respawn queue. + _unitGroup = grpNull; + try { + if ((_totalAI > 0) && {(count _spawnPos) > 1}) then { + _unitGroup = [_totalAI,_unitGroup,"static",_spawnPos,_trigger,_unitLevel] call A3EAI_spawnGroup; + if (isNull _unitGroup) then { + throw format ["A3EAI Debug: No units spawned for static spawn at %1. Added group to respawn queue.",(triggerText _trigger)]; + }; + _totalSpawned = _totalSpawned + _totalAI; + if (_patrolDist > 1) then { + 0 = [_unitGroup,_triggerPos,_patrolDist] spawn A3EAI_BIN_taskPatrol; + } else { + [_unitGroup, 0] setWaypointType "GUARD"; + }; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Spawned group %1 (unitLevel: %2) with %3 units.",_unitGroup,_unitLevel,_totalAI];}; + } else { + throw format ["A3EAI Debug: No units spawned for group %1. Added group to respawn queue.",_unitGroup]; + }; + } catch { + _unitGroup = ["static",true] call A3EAI_createGroup; + _unitGroup setVariable ["GroupSize",0]; + _unitGroup setVariable ["trigger",_trigger]; + 0 = [0,_trigger,_unitGroup] call A3EAI_addRespawnQueue; + if (A3EAI_debugLevel > 1) then {diag_log _exception;}; + }; + _grpArray pushBack _unitGroup; +}; + +if (A3EAI_debugLevel > 0) then { + diag_log format["A3EAI Debug: Spawned %1 new AI groups (%2 units total) in %3 seconds at %4.",_numGroups,_totalSpawned,(diag_tickTime - _startTime),(triggerText _trigger)]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Trigger %1 group array updated to: %2.",triggerText _trigger,_trigger getVariable "GroupArray"]}; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnVehicleCustom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnVehicleCustom.sqf new file mode 100644 index 0000000..f656017 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnVehicleCustom.sqf @@ -0,0 +1,156 @@ +#include "\A3EAI\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; //A3EAI_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 A3EAI_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 A3EAI_createGroup; +_driver = [_unitGroup,_unitLevel,[0,0,0]] call A3EAI_createUnit; + +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, _spawnMode]; +_driver moveInDriver _vehicle; + +_vehicle call A3EAI_protectObject; +_vehicle call A3EAI_secureVehicle; +_vehicle call A3EAI_clearVehicleCargo; +_vehicle call A3EAI_randomizeVehicleColor; + +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 A3EAI_addVehAirEH; +} else { + _vehicle call A3EAI_addLandVehEH; +}; +_vehicle allowCrewInImmobile (!_isAirVehicle); +_vehicle setUnloadInCombat [!_isAirVehicle,false]; + +_nvg = _driver call A3EAI_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 A3EAI_addVehicleGunners; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_createUnit; + _nvg = _cargo call A3EAI_addTempNVG; + _cargo assignAsCargo _vehicle; + _cargo moveInCargo [_vehicle,_i]; +}; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_removeExplosiveAmmo) then { + _result = _vehicle call A3EAI_removeExplosive; //Remove missile weaponry for air vehicles + }; + + if ((({_x call A3EAI_checkIsWeapon} count (weapons _vehicle)) isEqualTo 0) && {({_x call A3EAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) isEqualTo 0} && {_gunnersAdded isEqualTo 0}) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: AI group %1 air vehicle %2 set to Careless behavior mode",_unitGroup,_vehicleType];}; + }; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_curHeliPatrols = A3EAI_curHeliPatrols + 1; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Custom AI helicopter crew group %1 is now active and patrolling.",_unitGroup];}; + }; +} else { + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_curLandPatrols = A3EAI_curLandPatrols + 1; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Custom AI land vehicle crew group %1 is now active and patrolling.",_unitGroup];}; + }; +}; + +0 = [_unitGroup,_spawnPos,_patrolDist,false] spawn A3EAI_BIN_taskPatrol; +0 = [_unitGroup,_unitLevel] spawn A3EAI_addGroupManager; + +if (A3EAI_enableHC && {_unitType in A3EAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (_unitType in A3EAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Created custom vehicle spawn at %1 with vehicle type %2 with %3 crew units.",_spawnName,_vehicleType,(count (units _unitGroup))]}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnVehiclePatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnVehiclePatrol.sqf new file mode 100644 index 0000000..9af8366 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawnVehiclePatrol.sqf @@ -0,0 +1,197 @@ +#include "\A3EAI\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 = A3EAI_airGunnerUnits; + _unitLevel = "airvehicle" call A3EAI_getUnitLevel; + _vehiclePosition = [(getMarkerPos "A3EAI_centerMarker"),300 + (random((getMarkerSize "A3EAI_centerMarker") select 0)),random(360),1] call A3EAI_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 = A3EAI_landGunnerUnits; + _maxCargoUnits = A3EAI_landCargoUnits; + _unitLevel = "landvehicle" call A3EAI_getUnitLevel; + while {_keepLooking} do { + _vehiclePosition = [(getMarkerPos "A3EAI_centerMarker"),300 + random((getMarkerSize "A3EAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 ["A3EAI Error: %1 attempted to spawn unsupported vehicle type %2.",__FILE__,_vehicleType]}; + +_unitType = if (_isAirVehicle) then {"air"} else {"land"}; +_unitGroup = [_unitType] call A3EAI_createGroup; +_driver = [_unitGroup,_unitLevel,[0,0,0]] call A3EAI_createUnit; + +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, _spawnMode]; +_driver moveInDriver _vehicle; + +_vehicle call A3EAI_protectObject; +_vehicle call A3EAI_secureVehicle; +_vehicle call A3EAI_clearVehicleCargo; +_vehicle call A3EAI_randomizeVehicleColor; + +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 A3EAI_addVehAirEH; +} else { + _vehicle call A3EAI_addLandVehEH; +}; + +_vehicle allowCrewInImmobile (!_isAirVehicle); +_vehicle setUnloadInCombat [!_isAirVehicle,false]; + +//Setup group and crew +_nvg = _driver call A3EAI_addTempNVG; +_driver assignAsDriver _vehicle; +_driver setVariable ["isDriver",true]; +_unitGroup selectLeader _driver; + +_gunnersAdded = [_unitGroup,_unitLevel,_vehicle,_maxGunnerUnits] call A3EAI_addVehicleGunners; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_createUnit; + _nvg = _cargo call A3EAI_addTempNVG; + _cargo assignAsCargoIndex [_vehicle,_i]; + _cargo moveInCargo _vehicle; +}; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_removeExplosiveAmmo) then { + _result = _vehicle call A3EAI_removeExplosive; //Remove missile weaponry for air vehicles + }; + + if ((({_x call A3EAI_checkIsWeapon} count (weapons _vehicle)) isEqualTo 0) && {({_x call A3EAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) isEqualTo 0} && {_gunnersAdded isEqualTo 0}) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_heliDetection;"]; + [_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 A3EAI_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 - A3EAI_paradropCooldown]; + _vehicle flyInHeight (FLYINHEIGHT_AIR_PATROLLING_BASE + (random FLYINHEIGHT_AIR_PATROLLING_VARIANCE)); + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_curHeliPatrols = A3EAI_curHeliPatrols + 1; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 A3EAI_vehStartPatrol;"]; + [_unitGroup,0] setWaypointCombatMode _combatMode; + [_unitGroup,0] setWaypointBehaviour _behavior; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_curLandPatrols = A3EAI_curLandPatrols + 1; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 A3EAI_addGroupManager; //start group-level manager + +if (A3EAI_enableHC && {_unitType in A3EAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (_unitType in A3EAI_airReinforcementAllowedFor) then { + _unitGroup setVariable ["ReinforceAvailable",true]; +}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Created AI vehicle patrol at %1 with vehicle type %2 with %3 crew units.",_vehiclePosition,_vehicleType,(count (units _unitGroup))]}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawn_UV_patrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawn_UV_patrol.sqf new file mode 100644 index 0000000..d2798ba --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawn_UV_patrol.sqf @@ -0,0 +1,177 @@ +#include "\A3EAI\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 A3EAI_getUnitLevel; + _vehiclePosition = [(getMarkerPos "A3EAI_centerMarker"),300 + (random((getMarkerSize "A3EAI_centerMarker") select 0)),random(360),1] call A3EAI_SHK_pos; + _vehiclePosition set [2,200]; + _spawnMode = "FLY"; + }; + if (_vehicleType isKindOf "StaticWeapon") exitWith {_error = true}; + if (_vehicleType isKindOf "LandVehicle") exitWith { + _unitLevel = "ugv" call A3EAI_getUnitLevel; + while {_keepLooking} do { + _vehiclePosition = [(getMarkerPos "A3EAI_centerMarker"),300 + random((getMarkerSize "A3EAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 ["A3EAI 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 A3EAI_initUVGroup; + +_vehicle call A3EAI_protectObject; +_vehicle call A3EAI_secureVehicle; +_vehicle call A3EAI_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 A3EAI_addUAVEH; +} else { + _vehicle call A3EAI_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 A3EAI_UAVDetection;"; + _patrolStatement = "if !(local this) exitWith {}; [(group this)] spawn A3EAI_UAVStartPatrol;"; + _vehicle flyInHeight (FLYINHEIGHT_UAV_PATROLLING_BASE + random (FLYINHEIGHT_UAV_PATROLLING_VARIANCE)); + + if (A3EAI_detectOnlyUAVs) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + }; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_curUAVPatrols = A3EAI_curUAVPatrols + 1; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: UAV group %1 is now active and patrolling.",_unitGroup];}; + }; +} else { + _detectionStatement = "if !(local this) exitWith {}; [(group this)] spawn A3EAI_UGVDetection;"; + _patrolStatement = "if !(local this) exitWith {}; [(group this)] spawn A3EAI_UGVStartPatrol;"; + + if (A3EAI_detectOnlyUGVs) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + }; + + if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_curUGVPatrols = A3EAI_curUGVPatrols + 1; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: UGV group %1 is now active and patrolling.",_unitGroup];}; + }; +}; + +if ((({_x call A3EAI_checkIsWeapon} count (weapons _vehicle)) isEqualTo 0) && {({_x call A3EAI_checkIsWeapon} count (_vehicle weaponsTurret [-1])) isEqualTo 0}) then { + _unitGroup setBehaviour "CARELESS"; + _unitGroup setCombatMode "BLUE"; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: AI group %1 UAV/UGV vehicle %2 set to Careless behavior mode",_unitGroup,_vehicleType];}; +}; + +_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] spawn A3EAI_UAVStartPatrol; +} else { + //[_unitGroup] spawn A3EAI_UGVStartPatrol; +}; + +[_unitGroup,0] setWaypointPosition [_vehiclePosition,0]; +_unitGroup setCurrentWaypoint [_unitGroup,0]; + +_rearm = [_unitGroup,_unitLevel] spawn A3EAI_addGroupManager; //start group-level manager + +if (A3EAI_enableHC && {_unitType in A3EAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Created AI vehicle patrol at %1 with vehicle type %2 with %3 crew units.",_vehiclePosition,_vehicleType,(count (units _unitGroup))]}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawn_reinforcement.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawn_reinforcement.sqf new file mode 100644 index 0000000..615a085 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_unit_spawning/A3EAI_spawn_reinforcement.sqf @@ -0,0 +1,119 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_destPos", "_unitLevel", "_maxGunnerUnits", "_vehiclePosition", "_error", "_unitGroup", "_driver", "_vehicleType", "_vehicle", "_direction", "_velocity", "_nvg", "_gunnersAdded", "_cargoSpots", "_cargo", "_result", "_rearm","_targetPlayer","_unitType","_vehicleDescription"]; + +A3EAI_activeReinforcements = A3EAI_activeReinforcements - [grpNull]; +if ((count A3EAI_activeReinforcements) >= A3EAI_maxAirReinforcements) exitWith { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Maximum number of active air reinforcements reached (%1).",A3EAI_activeReinforcements];}; +}; + +_destPos = _this select 0; +_targetPlayer = _this select 1; +_unitLevel = _this select 2; + +if (({(_destPos distance _x) < AIR_REINFORCE_DIST_BETWEEN_LOCATIONS} count A3EAI_reinforcedPositions) > 0) exitWith { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Another AI reinforcement is active within 1000m of location %1, reinforce request cancelled.",_destPos];}; +}; + +A3EAI_reinforcedPositions pushBack _destPos; + +_error = false; +_maxGunnerUnits = A3EAI_airGunnerUnits; +_vehiclePosition = [_destPos,AIR_REINFORCE_SPAWN_DIST_BASE + (random(AIR_REINFORCE_SPAWN_DIST_VARIANCE)),random(360),1] call A3EAI_SHK_pos; +_vehiclePosition set [2,200]; + +_unitType = "air_reinforce"; +_unitGroup = [_unitType] call A3EAI_createGroup; +_driver = [_unitGroup,_unitLevel,[0,0,0]] call A3EAI_createUnit; + +_vehicleType = A3EAI_airReinforcementVehicles call A3EAI_selectRandom; +_vehicle = createVehicle [_vehicleType, _vehiclePosition, [], 0, "FLY"]; +_driver moveInDriver _vehicle; + +_vehicle call A3EAI_protectObject; +_vehicle call A3EAI_secureVehicle; +_vehicle call A3EAI_clearVehicleCargo; +_vehicle call A3EAI_addVehAirEH; +_vehicle call A3EAI_randomizeVehicleColor; + +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 ["A3EAI 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 A3EAI_addTempNVG; +_driver assignAsDriver _vehicle; +_driver setVariable ["isDriver",true]; +_unitGroup selectLeader _driver; + +_gunnersAdded = [_unitGroup,_unitLevel,_vehicle,_maxGunnerUnits] call A3EAI_addVehicleGunners; +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_removeExplosiveAmmo) then { + _result = _vehicle call A3EAI_removeExplosive; //Remove missile weaponry for air vehicles +}; + +if ((!isNull _vehicle) && {!isNull _unitGroup}) then { + A3EAI_activeReinforcements pushBack _unitGroup; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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 A3EAI_reinforce_begin};"]; +[_unitGroup,0] setWaypointBehaviour "CARELESS"; +[_unitGroup,0] setWaypointCombatMode "BLUE"; +_unitGroup setCurrentWaypoint [_unitGroup,0]; + +_rearm = [_unitGroup,_unitLevel] spawn A3EAI_addGroupManager; //start group-level manager + +if (A3EAI_enableHC && {_unitType in A3EAI_HCAllowedTypes}) then { + _unitGroup setVariable ["HC_Ready",true]; +}; + +if (A3EAI_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 A3EAI_radioSend; + } forEach _targetPlayerVehicleCrew; + }; + }; +}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_activateKryptoPickup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_activateKryptoPickup.sqf new file mode 100644 index 0000000..88b22ef --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_activateKryptoPickup.sqf @@ -0,0 +1,39 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_playerList","_player", "_kryptoTrigger", "_cIndex", "_kryptoStored", "_kryptoObject", "_vars", "_kryptoToSend","_arrowObject"]; + +_playerList = _this select 0; +_kryptoTrigger = _this select 1; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Krypto pickup assist activated at %1.",(getPosATL _kryptoTrigger)];}; + +_player = objNull; +{ + if (isPlayer _x) exitWith { + _player = _x; + } +} forEach _playerList; + +if (isPlayer _player) then { + if !(isNull _kryptoTrigger) then { + _cIndex = EPOCH_customVars find "Crypto"; + _kryptoObject = _kryptoTrigger getVariable ["A3EAI_kryptoObject",objNull]; + _arrowObject = _kryptoTrigger getVariable ["A3EAI_arrowObject",objNull]; + _kryptoStored = _kryptoObject getVariable ["Crypto",0]; + + if (_kryptoStored > 0) then { + _vars = _player getVariable ["VARS", [] + EPOCH_defaultVars_SEPXVar]; + _kryptoToSend = (((_vars select _cIndex) + _kryptoStored) min 25000) max 0; + [["effectCrypto", _kryptoToSend], (owner _player)] call EPOCH_sendPublicVariableClient; + _vars set [_cIndex, _kryptoToSend]; + _player setVariable ["VARS", _vars]; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Krypto pickup assist generated %1 krypto for player %2.",_kryptoStored,_player];}; + }; + + deleteVehicle _kryptoObject; + deleteVehicle _arrowObject; + }; +}; +deleteVehicle _kryptoTrigger; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addItem.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addItem.sqf new file mode 100644 index 0000000..dbeb108 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addItem.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addLandVehEH.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addLandVehEH.sqf new file mode 100644 index 0000000..64def45 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addLandVehEH.sqf @@ -0,0 +1,8 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3EAI_vehDestroyed"]; +_this addEventHandler ["HandleDamage","_this call A3EAI_handleDamageVeh"]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addMapMarker.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addMapMarker.sqf new file mode 100644 index 0000000..f1a8626 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addMapMarker.sqf @@ -0,0 +1,19 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_mapMarkerArray","_objectString"]; +_mapMarkerArray = missionNamespace getVariable ["A3EAI_mapMarkerArray",[]]; +_objectString = str (_this); +if !(_objectString in _mapMarkerArray) then { //Determine if marker is new + if !(_objectString in allMapMarkers) then { + private ["_marker"]; + _marker = createMarker [_objectString, (getPosASL _this)]; + _marker setMarkerType "mil_circle"; + _marker setMarkerBrush "Solid"; + }; + _mapMarkerArray pushBack _objectString; + missionNamespace setVariable ["A3EAI_mapMarkerArray",_mapMarkerArray]; +}; +if (_this isKindOf TRIGGER_OBJECT) then { //Set marker as active + _objectString setMarkerText "STATIC TRIGGER (ACTIVE)"; + _objectString setMarkerColor "ColorRed"; +}; \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addTempNVG.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addTempNVG.sqf new file mode 100644 index 0000000..9bbb488 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addTempNVG.sqf @@ -0,0 +1,8 @@ +#include "\A3EAI\globaldefines.hpp" + +if (_this hasWeapon NVG_ITEM_PLAYER) exitWith {false}; +_this addWeapon NVG_ITEM_AI; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Generated temporary NVGs for AI %1.",_this];}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addTemporaryWaypoint.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addTemporaryWaypoint.sqf new file mode 100644 index 0000000..7a9f623 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addTemporaryWaypoint.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_waypoint","_pos","_wpStatements"]; + +_unitGroup = _this select 0; +_pos = _this select 1; +_wpStatements = if ((count _this) > 2) then {_this select 2} else {"if !(local this) exitWith {}; (group this) call A3EAI_moveToPosAndDeleteWP;"}; + +if !(_pos isEqualTo [0,0,0]) then { + _waypoint = _unitGroup addWaypoint [_pos,0]; + _waypoint setWaypointType "MOVE"; + _waypoint setWaypointCompletionRadius 30; + _waypoint setWaypointStatements ["true",_wpStatements]; + + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 assigned temporary waypoint at %2 with statements %3.",_unitGroup,_pos,_wpStatements];}; + + _waypoint +} else { + diag_log format ["A3EAI Error: Group %1 was assigned temporary waypoint at %2.",_unitGroup,_pos]; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUAVEH.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUAVEH.sqf new file mode 100644 index 0000000..af38bec --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUAVEH.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3EAI_UAV_destroyed"]; + +if (A3EAI_detectOnlyUAVs) then { + _this addEventHandler ["Hit","_this call A3EAI_defensiveAggression"]; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUGVEH.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUGVEH.sqf new file mode 100644 index 0000000..8edcdd6 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUGVEH.sqf @@ -0,0 +1,12 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3EAI_UGV_destroyed"]; +_this addEventHandler ["HandleDamage","_this call A3EAI_handleDamageUGV"]; + +if (A3EAI_detectOnlyUGVs) then { + _this addEventHandler ["Hit","_this call A3EAI_defensiveAggression"]; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUVUnitEH.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUVUnitEH.sqf new file mode 100644 index 0000000..e5a27a0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUVUnitEH.sqf @@ -0,0 +1,7 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3EAI_handle_death_UV;"]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUnitEH.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUnitEH.sqf new file mode 100644 index 0000000..4431db8 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addUnitEH.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +_this addEventHandler ["Killed","_this call A3EAI_handleDeathEvent;"]; +_this addEventHandler ["HandleDamage","_this call A3EAI_handleDamageUnit;"]; + +_this setVariable ["bodyName",(name _this)]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addVehAirEH.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addVehAirEH.sqf new file mode 100644 index 0000000..268f9c5 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_addVehAirEH.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\globaldefines.hpp" + +if (isNull _this) exitWith {}; + +if (isNil {_this getVariable "durability"}) then {_this setVariable ["durability",[0,0,0,0]];}; +_this addEventHandler ["Killed","_this call A3EAI_heliDestroyed"]; +_this addEventHandler ["GetOut","_this call A3EAI_heliLanded"]; +_this addEventHandler ["HandleDamage","_this call A3EAI_handleDamageHeli"]; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_chance.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_chance.sqf new file mode 100644 index 0000000..0676260 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_chance.sqf @@ -0,0 +1,4 @@ +private ["_result"]; +_result = ((random 1) < _this); + +_result \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkClassname.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkClassname.sqf new file mode 100644 index 0000000..179ba45 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkClassname.sqf @@ -0,0 +1,66 @@ +#include "\A3EAI\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 (A3EAI_checkedClassnames select 0)) then { + _result = true; + } else { + if (!(_classname in (A3EAI_invalidClassnames select 0))) then { + _config = "CfgWeapons"; + _banString = "bin\config.bin/CfgWeapons/FakeWeapon"; + _configIndex = 0; + }; + }; + }; + if (_checkType isEqualTo "magazine") exitWith { + if (_classname in (A3EAI_checkedClassnames select 1)) then { + _result = true; + } else { + if (!(_classname in (A3EAI_invalidClassnames select 0))) then { + _config = "CfgMagazines"; + _banString = "bin\config.bin/CfgMagazines/FakeMagazine"; + _configIndex = 1; + }; + }; + }; + if (_checkType isEqualTo "vehicle") exitWith { + if (_classname in (A3EAI_checkedClassnames select 2)) then { + _result = true; + } else { + if (!(_classname in (A3EAI_invalidClassnames select 0))) then { + _config = "CfgVehicles"; + _banString = "bin\config.bin/CfgVehicles/Banned"; + _configIndex = 2; + }; + }; + }; + diag_log format ["A3EAI 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 = A3EAI_checkedClassnames; + _result = true; + } else { + _classnameArray = A3EAI_invalidClassnames; + diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkInNoAggroArea.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkInNoAggroArea.sqf new file mode 100644 index 0000000..4641368 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkInNoAggroArea.sqf @@ -0,0 +1,17 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_inNoAggroArea", "_objectPos", "_noAggroDistance"]; + +_objectPos = _this select 0; +_noAggroRange = [_this,1,900] call A3EAI_param; + +if ((typeName _objectPos) isEqualTo "OBJECT") then {_objectPos = getPosATL _objectPos}; + +_inNoAggroArea = false; +{ + if (((position _x) distance2D _objectPos) < _noAggroRange) exitWith { + _inNoAggroArea = true; + }; +} count A3EAI_noAggroAreas; + +_inNoAggroArea diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkIsWeapon.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkIsWeapon.sqf new file mode 100644 index 0000000..aeca1af --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_checkIsWeapon.sqf @@ -0,0 +1,15 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_clearVehicleCargo.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_clearVehicleCargo.sqf new file mode 100644 index 0000000..e466f05 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_clearVehicleCargo.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_vehicle"]; +_vehicle = _this; + +clearWeaponCargoGlobal _vehicle; +clearMagazineCargoGlobal _vehicle; +clearItemCargoGlobal _vehicle; +clearBackpackCargoGlobal _vehicle; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_countVehicleGunners.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_countVehicleGunners.sqf new file mode 100644 index 0000000..f7c841a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_countVehicleGunners.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListArea.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListArea.sqf new file mode 100644 index 0000000..d19712b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListArea.sqf @@ -0,0 +1,8 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListAreaDynamic.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListAreaDynamic.sqf new file mode 100644 index 0000000..e30a002 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListAreaDynamic.sqf @@ -0,0 +1,8 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListAreaRandom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListAreaRandom.sqf new file mode 100644 index 0000000..28fe229 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlackListAreaRandom.sqf @@ -0,0 +1,8 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_pos","_size"]; + +_pos = _this select 0; +_size = _this select 1; + +createLocation [BLACKLIST_OBJECT_RANDOM,_pos,_size,_size] \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlacklistAreaQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlacklistAreaQueue.sqf new file mode 100644 index 0000000..f2417c0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createBlacklistAreaQueue.sqf @@ -0,0 +1,20 @@ +#include "\A3EAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; +if (A3EAI_customBlacklistQueue isEqualTo []) then { + A3EAI_customBlacklistQueue pushBack _this; + _blacklistQueue = [] spawn { + while {!(A3EAI_customBlacklistQueue isEqualTo [])} do { + _statement = (A3EAI_customBlacklistQueue select 0); + _blacklistName = _statement select 0; + _statement deleteAt 0; + if ((_statement select 1) > 1499) then {_statement set [1,1499];}; + _statement call A3EAI_createBlackListArea; + A3EAI_customBlacklistQueue deleteAt 0; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Creating blacklist area at %1 (pos: %2) with radius %3.",_blacklistName,_statement select 0,_statement select 1];}; + uiSleep 1; + }; + }; +} else { + A3EAI_customBlacklistQueue pushBack _this; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomInfantryQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomInfantryQueue.sqf new file mode 100644 index 0000000..0d874e5 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomInfantryQueue.sqf @@ -0,0 +1,15 @@ +#include "\A3EAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1 (%2).",__FILE__,_this]}; +if (A3EAI_createCustomSpawnQueue isEqualTo []) then { + A3EAI_createCustomSpawnQueue pushBack _this; + _infantryQueue = [] spawn { + while {!(A3EAI_createCustomSpawnQueue isEqualTo [])} do { + (A3EAI_createCustomSpawnQueue select 0) call A3EAI_createCustomSpawn; + A3EAI_createCustomSpawnQueue deleteAt 0; + uiSleep 1; + }; + }; +} else { + A3EAI_createCustomSpawnQueue pushBack _this; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomInfantrySpawnQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomInfantrySpawnQueue.sqf new file mode 100644 index 0000000..3c15310 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomInfantrySpawnQueue.sqf @@ -0,0 +1,47 @@ +#include "\A3EAI\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 (A3EAI_customInfantrySpawnQueue isEqualTo []) then { + A3EAI_customInfantrySpawnQueue pushBack _this; + _infantryQueue = [] spawn { + //uiSleep 0.5; + while {!(A3EAI_customInfantrySpawnQueue isEqualTo [])} do { + private ["_args","_trigger"]; + _args = (A3EAI_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,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; + 0 = _args call A3EAI_spawnInfantryCustom; + if (A3EAI_enableDebugMarkers) then {_nul = _trigger call A3EAI_addMapMarker;}; + uiSleep 1; + }; + A3EAI_customInfantrySpawnQueue deleteAt 0; + }; + }; + } else { + A3EAI_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,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; + if (A3EAI_enableDebugMarkers) then { + _nul = _trigger call A3EAI_addMapMarker; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Maximum number of groups already spawned at custom %1. Exiting spawn script.",(triggerText _trigger)];}; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomVehicleQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomVehicleQueue.sqf new file mode 100644 index 0000000..e3be398 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createCustomVehicleQueue.sqf @@ -0,0 +1,21 @@ +#include "\A3EAI\globaldefines.hpp" + +if !((typeName _this) isEqualTo "ARRAY") exitWith {diag_log format ["Error: Wrong arguments sent to %1.",__FILE__]}; +if (A3EAI_customVehicleSpawnQueue isEqualTo []) then { + A3EAI_customVehicleSpawnQueue pushBack _this; + _vehicleQueue = [] spawn { + if (isNil "EPOCH_server_setVToken") then {waitUntil {uiSleep 0.3; !(isNil "EPOCH_server_setVToken")};}; + while {!(A3EAI_customVehicleSpawnQueue isEqualTo [])} do { + _vehicleType = (A3EAI_customVehicleSpawnQueue select 0) select 2; + if (!(_vehicleType isKindOf "StaticWeapon") && {[_vehicleType,"vehicle"] call A3EAI_checkClassname}) then { + (A3EAI_customVehicleSpawnQueue select 0) call A3EAI_spawnVehicleCustom; + } else { + diag_log format ["A3EAI Error: %1 attempted to spawn unsupported vehicle type %2.",__FILE__,_vehicleType]; + }; + A3EAI_customVehicleSpawnQueue deleteAt 0; + uiSleep 2; + }; + }; +} else { + A3EAI_customVehicleSpawnQueue pushBack _this; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createGroup.sqf new file mode 100644 index 0000000..9974487 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createGroup.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_unitGroup","_protect","_unitType"]; +_unitType = _this select 0; + +_unitGroup = createGroup AI_GROUP_SIDE; +if ((count _this) > 1) then {_unitGroup call A3EAI_protectGroup}; +_unitGroup setVariable ["unitType",_unitType]; +[_unitGroup,true] call A3EAI_updGroupCount; + +_unitGroup \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createInfantryQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createInfantryQueue.sqf new file mode 100644 index 0000000..ed11570 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createInfantryQueue.sqf @@ -0,0 +1,47 @@ +#include "\A3EAI\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 (A3EAI_staticInfantrySpawnQueue isEqualTo []) then { + A3EAI_staticInfantrySpawnQueue pushBack _this; + _infantryQueue = [] spawn { + while {!(A3EAI_staticInfantrySpawnQueue isEqualTo [])} do { + private ["_args","_trigger"]; + _args = (A3EAI_staticInfantrySpawnQueue select 0); + _trigger = _args select 3; + if (triggerActivated _trigger) then { + _trigger setVariable ["isCleaning",false]; + _triggerStatements = (triggerStatements _trigger); + _triggerStatements set [1,""]; + _trigger setTriggerStatements _triggerStatements; + [_trigger,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; + 0 = _args call A3EAI_spawnUnits_static; + if (A3EAI_enableDebugMarkers) then {_nul = _trigger call A3EAI_addMapMarker;}; + uiSleep 3; + }; + A3EAI_staticInfantrySpawnQueue deleteAt 0; + }; + }; + } else { + A3EAI_staticInfantrySpawnQueue 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,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; + if (A3EAI_enableDebugMarkers) then { + _nul = _trigger call A3EAI_addMapMarker; + }; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Maximum number of groups already spawned at %1. Exiting spawn script.",(triggerText _trigger)];}; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createNoAggroArea.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createNoAggroArea.sqf new file mode 100644 index 0000000..41b4c89 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createNoAggroArea.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_pos","_size","_location"]; + +_pos = _this select 0; +_size = _this select 1; + +_location = createLocation [BLACKLIST_OBJECT_NOAGGRO,_pos,_size,_size]; +A3EAI_noAggroAreas pushBack _location; + +_location diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createRandomInfantrySpawnQueue.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createRandomInfantrySpawnQueue.sqf new file mode 100644 index 0000000..e8d697f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_createRandomInfantrySpawnQueue.sqf @@ -0,0 +1,38 @@ +#include "\A3EAI\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 (A3EAI_randomInfantrySpawnQueue isEqualTo []) then { + A3EAI_randomInfantrySpawnQueue pushBack _this; + _infantryQueue = [] spawn { + while {!(A3EAI_randomInfantrySpawnQueue isEqualTo [])} do { + private ["_args","_trigger"]; + _args = (A3EAI_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 A3EAI_spawnUnits_random; + if (A3EAI_enableDebugMarkers) then { + _marker = str(_trigger); + _marker setMarkerColor "ColorOrange"; + _marker setMarkerAlpha 0.9; + }; + uiSleep 3; + }; + A3EAI_randomInfantrySpawnQueue deleteAt 0; + }; + }; + } else { + A3EAI_randomInfantrySpawnQueue pushBack _this; + }; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_debugMarkerLocation.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_debugMarkerLocation.sqf new file mode 100644 index 0000000..b5be196 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_debugMarkerLocation.sqf @@ -0,0 +1,16 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_mapMarkerArray","_objectString"]; +_mapMarkerArray = missionNamespace getVariable ["A3EAI_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 ["A3EAI_mapMarkerArray",_mapMarkerArray]; +}; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_deleteCustomSpawn.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_deleteCustomSpawn.sqf new file mode 100644 index 0000000..e67e19a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_deleteCustomSpawn.sqf @@ -0,0 +1,34 @@ +#include "\A3EAI\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 (A3EAI_enableDebugMarkers) then {deleteMarker str(_trigger)}; +_trigger setTriggerStatements ["this","true","false"]; //Disable trigger from activating or deactivating while cleanup is performed +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Deleting custom-defined AI spawn %1 at %2 in 30 seconds.",triggerText _trigger, mapGridPosition _trigger];}; +uiSleep 30; +{ + _x setVariable ["GroupSize",-1]; + if (A3EAI_HCIsConnected) then { + A3EAI_updateGroupSize_PVC = [_x,-1]; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_updateGroupSize_PVC"; + }; +} forEach (_trigger getVariable ["GroupArray",[]]); +//deleteMarker (_trigger getVariable ["spawnmarker",""]); +[_trigger,"A3EAI_staticTriggerArray"] call A3EAI_updateSpawnCount; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Deleting custom-defined AI spawn %1 at %2.",triggerText _trigger, mapGridPosition _trigger];}; + +deleteVehicle _trigger; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_deleteGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_deleteGroup.sqf new file mode 100644 index 0000000..6523a9c --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_deleteGroup.sqf @@ -0,0 +1,14 @@ +#include "\A3EAI\globaldefines.hpp" + +[_this,false] call A3EAI_updGroupCount; + +{ + if (alive _x) then { + deleteVehicle _x; + } else { + [_x] joinSilent grpNull; + }; +} count (units _this); +deleteGroup _this; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_findSpawnPos.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_findSpawnPos.sqf new file mode 100644 index 0000000..04f31c2 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_findSpawnPos.sqf @@ -0,0 +1,31 @@ +#include "\A3EAI\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,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 A3EAI_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 (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Unable to find suitable spawn position. (attempt %1/%2).",_attempts,_maxAttempts];}; + }; +}; + +_spawnPos diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_fixStuckGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_fixStuckGroup.sqf new file mode 100644 index 0000000..2fee4a5 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_fixStuckGroup.sqf @@ -0,0 +1,69 @@ +#include "\A3EAI\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 A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Relocated stuck group %1 (%2) to new location %3m away.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"]),(_leaderPos distance _newPosEmpty)];}; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 "A3EAI_centerMarker"),300 + random((getMarkerSize "A3EAI_centerMarker") select 0),random(360),0,[2,750],[25,_vehicleType]] call A3EAI_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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_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 A3EAI_SHK_pos; + _x setPosATL _newUnitPos; + _x setVelocity [0,0,0.25]; + }; + } forEach (units _unitGroup); + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Relocated stuck group %1 (%2) to new location %3m away.",_unitGroup,(_unitGroup getVariable ["unitType","unknown"]),(_leaderPos distance _newPosEmpty)];}; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_generateKryptoPickup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_generateKryptoPickup.sqf new file mode 100644 index 0000000..2e09118 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_generateKryptoPickup.sqf @@ -0,0 +1,30 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_kryptoDevice","_kryptoPos","_kryptoPickup","_arrowObject"]; + +_kryptoDevice = _this select 0; +_kryptoPos = _this select 1; + +if (isNull _kryptoDevice) exitWith {}; + +_arrowObject = createVehicle ["Sign_Arrow_F",_kryptoPos,[],0,"CAN_COLLIDE"]; +_arrowObject attachTo [_kryptoDevice,[0,0,2]]; + +_kryptoPickup = createTrigger [TRIGGER_OBJECT,_kryptoPos,false]; +_kryptoPickup setTriggerArea [1, 1, 0, false]; +_kryptoPickup setTriggerActivation ["ANY", "PRESENT", true]; +_kryptoPickup setTriggerTimeout [5, 5, 5, true]; +_kryptoPickup setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList > 0;","[thisList,thisTrigger] call A3EAI_activateKryptoPickup;",""]; + +_kryptoPickup attachTo [_kryptoDevice,[0,0,0]]; + +_kryptoPickup setVariable ["A3EAI_kryptoGenTime",diag_tickTime]; +_kryptoPickup setVariable ["A3EAI_kryptoObject",_kryptoDevice]; +_kryptoPickup setVariable ["A3EAI_arrowObject",_arrowObject]; +_kryptoDevice setVariable ["A3EAI_kryptoArea",_kryptoPickup]; + +A3EAI_kryptoAreas pushBack _kryptoPickup; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Generated krypto pickup assist area at %1.",_kryptoPos];}; + +_kryptoPickup \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getNoAggroStatus.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getNoAggroStatus.sqf new file mode 100644 index 0000000..1ec6c37 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getNoAggroStatus.sqf @@ -0,0 +1,9 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_status"]; + +_unitGroup = _this; + +_status = _unitGroup getVariable ["NoAggroStatus",false]; + +_status diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getPosBetween.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getPosBetween.sqf new file mode 100644 index 0000000..c5189f1 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getPosBetween.sqf @@ -0,0 +1,15 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getSafePosReflected.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getSafePosReflected.sqf new file mode 100644 index 0000000..2792e23 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getSafePosReflected.sqf @@ -0,0 +1,22 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_originPos", "_noAggroArea", "_posReflected", "_locationPos", "_locationSize", "_direction"]; + +_originPos = _this; //origin + +if ((typeName _originPos) isEqualTo "OBJECT") then {_originPos = getPosATL _originPos}; + +_noAggroArea = locationNull; +_posReflected = []; + +_noAggroArea = _originPos call A3EAI_returnNoAggroArea; + +if !(isNull _noAggroArea) then { + _locationPos = locationPosition _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,NO_AGGRO_RANGE_LAND] call A3EAI_checkInNoAggroArea}) then {_posReflected = []}; +}; + +_posReflected diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getSpawnParams.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getSpawnParams.sqf new file mode 100644 index 0000000..9192333 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getSpawnParams.sqf @@ -0,0 +1,19 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_nearestLocations", "_nearestLocationType", "_spawnParams"]; + +_nearestLocations = nearestLocations [_this,["NameCityCapital","NameCity","NameVillage","NameLocal"],1000]; +_nearestLocationType = if !(_nearestLocations isEqualTo []) then { + type (_nearestLocations select 0); +} else { + ""; //Position not in range of any categorized location +}; +_spawnParams = call { + if (_nearestLocationType isEqualTo "NameCityCapital") exitWith {[A3EAI_minAI_capitalCity,A3EAI_addAI_capitalCity,A3EAI_unitLevel_capitalCity,A3EAI_spawnChance_capitalCity]}; + if (_nearestLocationType isEqualTo "NameCity") exitWith {[A3EAI_minAI_city,A3EAI_addAI_city,A3EAI_unitLevel_city,A3EAI_spawnChance_city]}; + if (_nearestLocationType isEqualTo "NameVillage") exitWith {[A3EAI_minAI_village,A3EAI_addAI_village,A3EAI_unitLevel_village,A3EAI_spawnChance_village]}; + if (_nearestLocationType isEqualTo "NameLocal") exitWith {[A3EAI_minAI_remoteArea,A3EAI_addAI_remoteArea,A3EAI_unitLevel_remoteArea,A3EAI_spawnChance_remoteArea]}; + [A3EAI_minAI_wilderness,A3EAI_addAI_wilderness,A3EAI_unitLevel_wilderness,A3EAI_spawnChance_wilderness] //Default +}; + +_spawnParams \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getUnitLevel.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getUnitLevel.sqf new file mode 100644 index 0000000..da0929a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getUnitLevel.sqf @@ -0,0 +1,14 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_indexWeighted","_unitLevelIndexTable"]; +_unitLevelIndexTable = _this; + +_indexWeighted = call { + if (_unitLevelIndexTable isEqualTo "airvehicle") exitWith {A3EAI_levelIndicesAir}; + if (_unitLevelIndexTable isEqualTo "landvehicle") exitWith {A3EAI_levelIndicesLand}; + if (_unitLevelIndexTable isEqualTo "uav") exitWith {A3EAI_levelIndicesUAV}; + if (_unitLevelIndexTable isEqualTo "ugv") exitWith {A3EAI_levelIndicesUGV}; + [0] +}; + +A3EAI_unitLevels select (_indexWeighted call A3EAI_selectRandom) \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getWeapon.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getWeapon.sqf new file mode 100644 index 0000000..960f7ff --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_getWeapon.sqf @@ -0,0 +1,12 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitLevel", "_weaponIndices", "_weaponList", "_weaponsList", "_weaponSelected"]; + +_unitLevel = _this; + +_weaponIndices = missionNamespace getVariable ["A3EAI_weaponTypeIndices"+str(_unitLevel),[0,1,2,3]]; +_weaponList = ["A3EAI_pistolList","A3EAI_rifleList","A3EAI_machinegunList","A3EAI_sniperList"] select (_weaponIndices call A3EAI_selectRandom); +_weaponsList = missionNamespace getVariable [_weaponList,A3EAI_rifleList]; +_weaponSelected = _weaponsList call A3EAI_selectRandom; + +_weaponSelected \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_hasLOS.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_hasLOS.sqf new file mode 100644 index 0000000..550c989 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_hasLOS.sqf @@ -0,0 +1,6 @@ +private ["_comparePos"]; +_begin = _this select 0; +_end = _this select 1; +_ignore = _this select 2; + +!(lineIntersects [_begin,_end,_ignore]) \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initNoAggroStatus.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initNoAggroStatus.sqf new file mode 100644 index 0000000..25b1b38 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initNoAggroStatus.sqf @@ -0,0 +1,37 @@ +#include "\A3EAI\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 A3EAI_checkInNoAggroArea; //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 A3EAI_checkInNoAggroArea; + }; +}; //To test! + +[_unitGroup,_inArea] call A3EAI_setNoAggroStatus; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initUVGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initUVGroup.sqf new file mode 100644 index 0000000..5d7b069 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initUVGroup.sqf @@ -0,0 +1,25 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_UAVGroup", "_unitType", "_unitGroup"]; + +_UAVGroup = _this select 0; +_unitType = _this select 1; + +_unitGroup = grpNull; +if !((side _UAVGroup) isEqualTo AI_GROUP_SIDE) then { + _unitGroup = [_unitType] call A3EAI_createGroup; + (units _UAVGroup) joinSilent _unitGroup; + deleteGroup _UAVGroup; + //diag_log format ["Debug: Created UV Group %1.",_unitGroup]; +} else { + _unitGroup = _UAVGroup; + _unitGroup setVariable ["unitType",_unitType]; + [_unitGroup,true] call A3EAI_updGroupCount; + //diag_log format ["Debug: Retained UV Group %1.",_unitGroup]; +}; + +{ + _x call A3EAI_addUVUnitEH; +} forEach (units _unitGroup); + +_unitGroup; diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initializeTrigger.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initializeTrigger.sqf new file mode 100644 index 0000000..3675d22 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_initializeTrigger.sqf @@ -0,0 +1,71 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_trigger","_mode"]; + +_mode = _this select 0; +_trigger = _this select 1; + +_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 (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Initialized static spawn at %1 (%2). GroupArray: %3, PatrolDist: %4. unitLevel: %5. %LocationArray %6 positions, MaxUnits %7, SpawnChance %8.",(triggerText _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 ["spawnType","dynamic"]; + if (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Initialized dynamic spawn at %1. GroupArray: %2.",triggerText _trigger,(_this select 2)];}; + }; + if (_mode isEqualTo 2) exitWith { + //Random spawns + _trigger setVariable ["spawnType","random"]; + if (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Initialized random spawn at %1. GroupArray: %2.",triggerText _trigger,(_this select 2)];}; + }; + 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",1]; + _trigger setVariable ["spawnType","staticcustom"]; + if (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Initialized custom spawn at %1. GroupArray: %2, PatrolDist: %3. unitLevel: %4. %LocationArray %5 positions, MaxUnits %6.",triggerText _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 (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Initialized vehicle group at %1. GroupArray: %2, PatrolDist: %3. unitLevel: %4, MaxUnits %5.",triggerText _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 (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Initialized paradrop group at %1. GroupArray: %2, PatrolDist: %3. unitLevel: %4, MaxUnits %5.",triggerText _trigger,(_this select 2),(_this select 3),(_this select 4),(_this select 5)];}; + }; +}; + +_trigger setVariable ["triggerStatements",+(triggerStatements _trigger)]; +_trigger setVariable ["initialized",true]; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_moveToPosAndDeleteWP.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_moveToPosAndDeleteWP.sqf new file mode 100644 index 0000000..f2ec8ef --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_moveToPosAndDeleteWP.sqf @@ -0,0 +1,9 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_moveToPosAndPatrol.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_moveToPosAndPatrol.sqf new file mode 100644 index 0000000..c0ef75b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_moveToPosAndPatrol.sqf @@ -0,0 +1,14 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_currentWaypoint","_radius"]; + +_unitGroup = _this select 0; +_radius = _this select 1; + +_currentWaypoint = (currentWaypoint _unitGroup); +_pos = (waypointPosition _currentWaypoint); +deleteWaypoint [_unitGroup,_currentWaypoint]; +[_unitGroup,"Behavior_Reset"] call A3EAI_forceBehavior; +0 = [_unitGroup,_pos,_radius] spawn A3EAI_BIN_taskPatrol; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_noAggroAreaToggle.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_noAggroAreaToggle.sqf new file mode 100644 index 0000000..8757565 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_noAggroAreaToggle.sqf @@ -0,0 +1,24 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_object", "_nearNoAggroAreas", "_inNoAggroArea", "_objectPos","_combatMode"]; + +_unitGroup = _this select 0; +_inNoAggroArea = _this select 1; + +_combatMode = (combatMode _unitGroup); + +if (_inNoAggroArea) then { + if (_combatMode != "BLUE") then { + [_unitGroup,"IgnoreEnemies"] call A3EAI_forceBehavior; + [_unitGroup,true] call A3EAI_setNoAggroStatus; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 in no-aggro zone.",_unitGroup];}; + }; +} else { + if (_combatMode isEqualTo "BLUE") then { + [_unitGroup,"Behavior_Reset"] call A3EAI_forceBehavior; + [_unitGroup,false] call A3EAI_setNoAggroStatus; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Group %1 exited no-aggro zone.",_unitGroup];}; + }; +}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_param.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_param.sqf new file mode 100644 index 0000000..6aed491 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_param.sqf @@ -0,0 +1,19 @@ +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 +}; + +_return diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_posInBuilding.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_posInBuilding.sqf new file mode 100644 index 0000000..318f1ef --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_posInBuilding.sqf @@ -0,0 +1,5 @@ +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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_protectGroup.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_protectGroup.sqf new file mode 100644 index 0000000..2ebf36a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_protectGroup.sqf @@ -0,0 +1,15 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_dummy"]; //_this = group + +_dummy = _this createUnit ["Logic",[0,0,0],[],0,"FORM"]; +[_dummy] joinSilent _this; +_this setVariable ["dummyUnit",_dummy]; +if !(isDedicated) then { + A3EAI_protectGroup_PVS = [_unitGroup,_dummy]; + publicVariableServer "A3EAI_protectGroup_PVS"; +}; + +if (A3EAI_debugLevel > 1) then {diag_log format["A3EAI Debug: Spawned 1 dummy AI unit to preserve group %1.",_this];}; + +_dummy \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_protectObject.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_protectObject.sqf new file mode 100644 index 0000000..90f6126 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_protectObject.sqf @@ -0,0 +1,9 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_objectMonitor","_object","_index"]; +_object = _this; + +_object call EPOCH_server_setVToken; +_index = A3EAI_monitoredObjects pushBack _object; + +_index \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_purgeUnitGear.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_purgeUnitGear.sqf new file mode 100644 index 0000000..393790e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_purgeUnitGear.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_radioSend.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_radioSend.sqf new file mode 100644 index 0000000..f1f5487 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_radioSend.sqf @@ -0,0 +1,12 @@ +#include "\A3EAI\globaldefines.hpp" + +A3EAI_SMS = (_this select 1); + +if (isDedicated) then { + (owner (_this select 0)) publicVariableClient "A3EAI_SMS"; +} else { + A3EAI_SMS_PVS = [(_this select 0),A3EAI_SMS]; + publicVariableServer "A3EAI_SMS_PVS"; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_randomizeVehicleColor.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_randomizeVehicleColor.sqf new file mode 100644 index 0000000..87f41ae --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_randomizeVehicleColor.sqf @@ -0,0 +1,18 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_init","_vehicleType"]; + +if !(_this isKindOf "AllVehicles") exitWith {false}; + +_vehicleType = (typeOf _this); +if ((_vehicleType find "_EPOCH") isEqualTo -1) exitWith {false}; //Execute only on _EPOCH vehicles + +_init = [configFile >> "CfgVehicles" >> _vehicleType >> "Eventhandlers","init",""] call BIS_fnc_returnConfigEntry; +if !((_init find "bis_fnc_initVehicle") isEqualTo -1) exitWith {false}; + +_this setVariable ["BIS_enableRandomization",true]; +[_this, "", [], false] call BIS_fnc_initVehicle; + +if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: %1 executed on AI %2.",__FILE__,_vehicleType]}; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_reloadVehicleTurrets.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_reloadVehicleTurrets.sqf new file mode 100644 index 0000000..b795499 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_reloadVehicleTurrets.sqf @@ -0,0 +1,27 @@ +#include "\A3EAI\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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_removeExplosive.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_removeExplosive.sqf new file mode 100644 index 0000000..ea5c70e --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_removeExplosive.sqf @@ -0,0 +1,43 @@ +#include "\A3EAI\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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_returnNoAggroArea.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_returnNoAggroArea.sqf new file mode 100644 index 0000000..164e2be --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_returnNoAggroArea.sqf @@ -0,0 +1,16 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_noAggroArea", "_objectPos"]; + +_objectPos = _this; + +_noAggroArea = locationNull; +if ((typeName _this) isEqualTo "OBJECT") then {_objectPos = getPosATL _this}; + +{ + if (_objectPos in _x) exitWith { + _noAggroArea = _x; + }; +} forEach A3EAI_noAggroAreas; + +_noAggroArea diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_secureVehicle.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_secureVehicle.sqf new file mode 100644 index 0000000..86c8dc0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_secureVehicle.sqf @@ -0,0 +1,19 @@ +#include "\A3EAI\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 ["vehicle_disabled",false]) then {deleteVehicle (_this select 0);}; + diag_log "Debug: Forced player out of an AI vehicle."; + }; +}]; + +_object setVehicleLock "LOCKEDPLAYER"; +_object enableCopilot false; +_object enableRopeAttach false; + +true diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_selectRandom.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_selectRandom.sqf new file mode 100644 index 0000000..1b52ceb --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_selectRandom.sqf @@ -0,0 +1,22 @@ +//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"; + +if(count _this > 0) then +{ + _ret = count _this; //number of elements in the array + _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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_sendKillMessage.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_sendKillMessage.sqf new file mode 100644 index 0000000..da61735 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_sendKillMessage.sqf @@ -0,0 +1,18 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_killer","_victim"]; +_killer = _this select 0; +_victim = _this select 1; + +if (isDedicated) then { + _victimName = _victim getVariable ["bodyName","Bandit"]; + { + if (isPlayer _x) then { + A3EAI_killMSG = _victimName; + (owner _x) publicVariableClient "A3EAI_killMSG"; + }; + } count (crew _killer); +} else { + A3EAI_killMsg_PVS = [_killer,_victim]; + publicVariableServer "A3EAI_killMsg_PVS"; +}; \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setFirstWPPos.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setFirstWPPos.sqf new file mode 100644 index 0000000..6ea5708 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setFirstWPPos.sqf @@ -0,0 +1,23 @@ +#include "\A3EAI\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 { + A3EAI_setCurrentWaypoint_PVC = _waypoint; + A3EAI_HCObjectOwnerID publicVariableClient "A3EAI_setCurrentWaypoint_PVC"; + }; + _result = true; +}; + +_result \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setNoAggroStatus.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setNoAggroStatus.sqf new file mode 100644 index 0000000..3fb174d --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setNoAggroStatus.sqf @@ -0,0 +1,10 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_status"]; + +_unitGroup = _this select 0; +_status = _this select 1; + +_unitGroup setVariable ["NoAggroStatus",_status]; + +_status diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setRandomWaypoint.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setRandomWaypoint.sqf new file mode 100644 index 0000000..f4da742 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setRandomWaypoint.sqf @@ -0,0 +1,11 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup", "_currentWaypoint", "_allWaypoints", "_selectedWaypoint"]; + +_unitGroup = _this select 0; +_currentWaypoint = (currentWaypoint _unitGroup); +_allWaypoints = (waypoints _unitGroup) - [_unitGroup,_currentWaypoint]; +_selectedWaypoint = _allWaypoints call A3EAI_selectRandom; +_unitGroup setCurrentWaypoint _selectedWaypoint; + +_selectedWaypoint diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setSkills.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setSkills.sqf new file mode 100644 index 0000000..0e68611 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setSkills.sqf @@ -0,0 +1,12 @@ +#include "\A3EAI\globaldefines.hpp" + +private["_unit","_unitLevel","_skillSeed","_skillArray","_skillTypeArray"]; +_unit = _this select 0; +_unitLevel = _this select 1; +_skillArray = missionNamespace getVariable ["A3EAI_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/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setVehicleRegrouped.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setVehicleRegrouped.sqf new file mode 100644 index 0000000..d4d6219 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_setVehicleRegrouped.sqf @@ -0,0 +1,15 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup"]; + +_unitGroup = _this; + +if !(_unitGroup getVariable ["regrouped",false]) then { + _unitGroup setVariable ["regrouped",true]; + if !(isDedicated) then { + A3EAI_setVehicleRegrouped_PVS = _unitGroup; + publicVariableServer "A3EAI_setVehicleRegrouped_PVS"; + }; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_updGroupCount.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_updGroupCount.sqf new file mode 100644 index 0000000..172285a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_updGroupCount.sqf @@ -0,0 +1,15 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_unitGroup","_isNewGroup"]; +_unitGroup = _this select 0; +_isNewGroup = _this select 1; + +if (isNull _unitGroup) exitWith {false}; + +if (_isNewGroup) then { + A3EAI_activeGroups pushBack _unitGroup; +} else { + A3EAI_activeGroups = A3EAI_activeGroups - [_unitGroup,grpNull]; +}; + +true \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_updateSpawnCount.sqf b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_updateSpawnCount.sqf new file mode 100644 index 0000000..b60a838 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/compile/A3EAI_utilities/A3EAI_updateSpawnCount.sqf @@ -0,0 +1,19 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_trigger","_arrayString","_triggerArray"]; +_trigger = _this select 0; +_arrayString = _this select 1; + +_triggerArray = missionNamespace getVariable [_arrayString,[]]; +if (!isNull _trigger) then { + if (_trigger in _triggerArray) then { + _triggerArray = _triggerArray - [_trigger,objNull]; + } else { + if ((triggerStatements _trigger select 1) isEqualTo "") then { + _triggerArray pushBack _trigger; + }; + }; +}; + +_triggerArray = _triggerArray - [objNull]; +missionNamespace setVariable [_arrayString,_triggerArray]; \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/config.cpp b/Server/@A3EAI/addons/a3eai/config.cpp new file mode 100644 index 0000000..10f9331 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/config.cpp @@ -0,0 +1,84 @@ +class CfgPatches { + class A3EAI { + units[] = {}; + weapons[] = {}; + requiredVersion = 0.1; + A3EAIVersion = "1.0.1a"; + compatibleConfigVersions[] = {"1.0.0","1.0.0a","1.0.0b","1.0.0c","1.0.0d","1.0.1","1.0.1a"}; + compatibleHCVersions[] = {}; //HC not currently supported since Epoch client code execution prevention does not currently work. + requiredAddons[] = {"a3_epoch_code"}; + }; + class A3EAI_HC { + units[] = {}; + weapons[] = {}; + requiredVersion = 0.1; + A3EAI_HCVersion = "Non-functional"; + requiredAddons[] = {"a3_epoch_code"}; + }; +}; + +class CfgFunctions { + class A3EAI { + class A3EAI_Server { + file = "A3EAI"; + + class A3EAI_init { + preInit = 1; + }; + }; + }; +}; + +class CfgNonAIVehicles { + class A3EAI_EmptyDetector { + displayName="A3EAI Trigger"; + icon = "\a3\Ui_f\data\IGUI\Cfg\IslandMap\iconSensor_ca.paa"; + model = ""; + scope = public; + selectionFabric = "latka"; + simulation="detector"; + }; +}; + +class CfgLocationTypes { + class A3EAI_BlacklistedArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3EAI Blacklist Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; + class A3EAI_NoAggroArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3EAI No-Aggro Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; + class A3EAI_RandomSpawnArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3EAI Random Spawn Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; + class A3EAI_DynamicSpawnArea { + color[] = {0.91,0,0,1}; + drawStyle = "name"; + font = "PuristaMedium"; + name = "A3EAI Dynamic Spawn Area"; + shadow = 1; + size = 15; + textSize = 0.05; + texture = ""; + }; +}; diff --git a/Server/@A3EAI/addons/a3eai/fn_A3EAI_init.sqf b/Server/@A3EAI/addons/a3eai/fn_A3EAI_init.sqf new file mode 100644 index 0000000..9d4f8bf --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/fn_A3EAI_init.sqf @@ -0,0 +1,8 @@ +if (hasInterface) exitWith {}; + +if (isDedicated) then { + [] call compile preprocessFileLineNumbers "\a3eai\init\a3eai_initserver.sqf"; +} else { + [] call compile preprocessFileLineNumbers "\a3eai\init\a3eai_inithc.sqf"; +}; + diff --git a/Server/@A3EAI/addons/a3eai/globaldefines.hpp b/Server/@A3EAI/addons/a3eai/globaldefines.hpp new file mode 100644 index 0000000..6bc1ae7 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/globaldefines.hpp @@ -0,0 +1,184 @@ +//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" +// #define NVG_ITEM_PLAYER "NVGoggles_OPFOR" //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 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 //new +#define PLAYER_DISTANCE_WITH_LOS_STATIC_CUSTOM 200 //new +#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 900 //A3XAI +#define NO_AGGRO_AREA_SIZE 300 //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 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_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 350 +#define WP_POS_EGRESS_BASE_AIR 300 +#define WP_POS_EGRESS_VARIANCE_AIR 100 +#define DETECT_RANGE_UGV 250 +#define DETECT_RANGE_UAV 300 +#define DETECT_RANGE_AIR 500 +#define DETECT_RANGE_AIR_REINFORCE 300 +#define DETECT_RANGE_AIR_CUSTOM 500 +#define DETECT_LENGTH_UGV_2D 300 +#define FLYINHEIGHT_UAV_SEARCHING_BASE 75 +#define FLYINHEIGHT_UAV_SEARCHING_VARIANCE 25 +#define FLYINHEIGHT_UAV_PATROLLING_BASE 125 +#define FLYINHEIGHT_UAV_PATROLLING_VARIANCE 25 +#define FLYINHEIGHT_AIR_SEARCHING_BASE 75 +#define FLYINHEIGHT_AIR_SEARCHING_VARIANCE 25 +#define FLYINHEIGHT_AIR_PATROLLING_BASE 125 +#define FLYINHEIGHT_AIR_PATROLLING_VARIANCE 25 +#define FLYINHEIGHT_AIR_CUSTOM 100 +#define FLYINHEIGHT_AIR_REINFORCE 100 +#define AIR_REINFORCE_DIST_BETWEEN_LOCATIONS 1000 +#define AIR_REINFORCE_SPAWN_DIST_BASE 1800 +#define AIR_REINFORCE_SPAWN_DIST_VARIANCE 900 +#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 1000 //Comment: UAVs have hilariously large range. Unlikely long enough distance, better than nothing. +#define NO_AGGRO_RANGE_UGV 500 + +//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 360 +#define MONITOR_CHECK_RANDSPAWN_FREQ 360 +#define RANDSPAWN_EXPIRY_TIME 1080 +#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 3, 3, 3 +#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 DEFENSIVE_AGGRESSION_TIME 120 +#define ADD_RESPAWN_FAST_TIME 30 +#define ANTISTUCK_CHECK_TIME_INFANTRY 60 +#define ANTISTUCK_CHECK_TIME_AIR 300 +#define ANTISTUCK_CHECK_TIME_LAND 120 + +//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 "PublicHiveIsLoaded" //A3XAI +#define SERVER_STARTED_INDICATOR "EPOCH_BuildingSlotCount" //A3EAI diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_HCFunctions.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_HCFunctions.sqf new file mode 100644 index 0000000..40985c8 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_HCFunctions.sqf @@ -0,0 +1,28 @@ +A3EAI_setGroupTriggerVars = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_setGroupTriggerVars.sqf",A3EAI_directory]; +A3EAI_handlestatic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handlestatic.sqf",A3EAI_directory]; +A3EAI_handlestaticcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handlestaticcustom.sqf",A3EAI_directory]; +A3EAI_handleland = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handleland.sqf",A3EAI_directory]; +A3EAI_handleair = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handleair.sqf",A3EAI_directory]; +A3EAI_handleaircustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handleaircustom.sqf",A3EAI_directory]; +A3EAI_handleair_reinforce = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handleair_reinforce.sqf",A3EAI_directory]; +A3EAI_handlelandcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handlelandcustom.sqf",A3EAI_directory]; +A3EAI_handledynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handledynamic.sqf",A3EAI_directory]; +A3EAI_handlerandom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handlerandom.sqf",A3EAI_directory]; +A3EAI_handleuav = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handleuav.sqf",A3EAI_directory]; +A3EAI_handleugv = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handleugv.sqf",A3EAI_directory]; +A3EAI_handlevehiclecrew = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_handlevehiclecrew.sqf",A3EAI_directory]; +A3EAI_addNewGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_addNewGroup.sqf",A3EAI_directory]; +A3EAI_addHunterGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_addHunterGroup.sqf",A3EAI_directory]; +A3EAI_updateGroupSizeHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_updateGroupSizeHC.sqf",A3EAI_directory]; +A3EAI_airReinforcementDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_airReinforcementDetection.sqf",A3EAI_directory]; +A3EAI_cleanupReinforcementHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_cleanupReinforcementHC.sqf",A3EAI_directory]; +A3EAI_setLoadoutVariables_HC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_setLoadoutVariables_HC.sqf",A3EAI_directory]; +A3EAI_createGroupTriggerObject = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_createGroupTriggerObject.sqf",A3EAI_directory]; +A3EAI_requestGroupVars = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_requestGroupVars.sqf",A3EAI_directory]; +A3EAI_updateServerLoot = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_updateServerLoot.sqf",A3EAI_directory]; +A3EAI_updateGroupLootPoolHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_updateGroupLootPoolHC.sqf",A3EAI_directory]; +A3EAI_setBehaviorHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient\A3EAI_setBehaviorHC.sqf",A3EAI_directory]; + +diag_log "[A3EAI] A3EAI HC functions loaded."; + +true diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_HC_PVEH.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_HC_PVEH.sqf new file mode 100644 index 0000000..a4a4cfa --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_HC_PVEH.sqf @@ -0,0 +1,12 @@ +"A3EAI_transferGroup_PVC" addPublicVariableEventHandler {(_this select 1) call A3EAI_addNewGroup;diag_log format ["Debug: %1",_this];}; +"A3EAI_sendGroupTriggerVars_PVC" addPublicVariableEventHandler {_nul = (_this select 1) spawn A3EAI_setGroupTriggerVars;diag_log format ["Debug: %1",_this];}; +"A3EAI_updateGroupLoot_PVC" addPublicVariableEventHandler {(_this select 1) call A3EAI_updateGroupLootPoolHC;diag_log format ["Debug: %1",_this];}; +"A3EAI_sendHunterGroup_PVC" addPublicVariableEventHandler {(_this select 1) call A3EAI_addHunterGroup;diag_log format ["Debug: %1",_this];}; +"A3EAI_updateGroupSize_PVC" addPublicVariableEventHandler {(_this select 1) call A3EAI_updateGroupSizeHC;diag_log format ["Debug: %1",_this];}; +"A3EAI_setCurrentWaypoint_PVC" addPublicVariableEventHandler {(_this select 1) call A3EAI_setCurrentWaypointHC;diag_log format ["Debug: %1",_this];}; +"A3EAI_cleanupReinforcement_PVC" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_cleanupReinforcementHC;diag_log format ["Debug: %1",_this];}; +"A3EAI_setBehavior_PVC" addPublicVariableEventHandler {(_this select 1) call A3EAI_setBehaviorHC;diag_log format ["Debug: %1",_this];}; + +diag_log "[A3EAI] A3EAI HC PVEHs loaded."; + +true diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_ServerHC_PVEH.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_ServerHC_PVEH.sqf new file mode 100644 index 0000000..b9e726a --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_ServerHC_PVEH.sqf @@ -0,0 +1,25 @@ +"A3EAI_respawnGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_addRespawnQueue;diag_log format ["Debug: %1",_this];}; +"A3EAI_killMsg_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_sendKillMessage;diag_log format ["Debug: %1",_this];}; +"A3EAI_despawnRandomGroup_PVS" addPublicVariableEventHandler {private ["_trigger"];_trigger = (_this select 1) getVariable ["trigger",objNull];[_trigger,true] spawn A3EAI_despawn_random;diag_log format ["Debug: %1",_this];}; +"A3EAI_despawnDynamicGroup_PVS" addPublicVariableEventHandler {private ["_trigger"];_trigger = (_this select 1) getVariable ["trigger",objNull];[_trigger,true] spawn A3EAI_despawn_dynamic;diag_log format ["Debug: %1",_this];}; +"A3EAI_respawnVehicle_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_respawnAIVehicle;diag_log format ["Debug: %1",_this];}; +"A3EAI_addVehicleGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_addVehicleGroup;diag_log format ["Debug: %1",_this];}; +"A3EAI_addParaGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_addParaGroup;diag_log format ["Debug: %1",_this];}; +"A3EAI_transferGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_HCGroupToServer;diag_log format ["Debug: %1",_this];}; +"A3EAI_SMS_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_radioSend;diag_log format ["Debug: %1",_this];}; +"A3EAI_getGroupTriggerVars_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_getGroupTriggerVars;diag_log format ["Debug: %1",_this];}; +"A3EAI_registerDeath_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_registerDeath;diag_log format ["Debug: %1",_this];}; +"A3EAI_updateGroupLoot_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_updateGroupLootPool;diag_log format ["Debug: %1",_this];}; +"A3EAI_protectGroup_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_protectRemoteGroup;diag_log format ["Debug: %1",_this];}; +"A3EAI_updateGroupSize_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_updateGroupSizeServer;diag_log format ["Debug: %1",_this];}; +"A3EAI_HCLogin_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_HCListener;diag_log format ["Debug: %1",_this];}; +"A3EAI_updateReinforcePlaces_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_updateReinforcePlaces;diag_log format ["Debug: %1",_this];}; +"A3EAI_setPermaDeleteSpawn_PVS" addPublicVariableEventHandler {private ["_trigger"];_trigger = (_this select 1) getVariable ["trigger",objNull];_trigger setVariable ["permadelete",true];diag_log format ["Debug: %1",_this];}; +"A3EAI_deleteCustomSpawn_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_deleteCustomSpawn;diag_log format ["Debug: %1",_this];}; +"A3EAI_setDriverUnit_PVS" addPublicVariableEventHandler {(_this select 1) setVariable ["isDriver",true];diag_log format ["Debug: %1",_this];}; +"A3EAI_setVehicleRegrouped_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_setVehicleRegrouped;diag_log format ["Debug: %1",_this];}; +"A3EAI_spawnReinforcements_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_spawn_reinforcement;diag_log format ["Debug: %1",_this];}; +"A3EAI_setBehavior_PVS" addPublicVariableEventHandler {(_this select 1) call A3EAI_setBehavior;diag_log format ["Debug: %1",_this];}; +"A3EAI_generateLootOnDeath_PVS" addPublicVariableEventHandler {(_this select 1) spawn A3EAI_generateLootOnDeath;diag_log format ["Debug: %1",_this];}; + +diag_log "[A3EAI] Serverside PVEHs loaded."; diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_ServerHC_functions.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_ServerHC_functions.sqf new file mode 100644 index 0000000..5b5d7ae --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_ServerHC_functions.sqf @@ -0,0 +1,13 @@ +diag_log "[A3EAI] Compiling A3EAI HC functions."; + +A3EAI_transferGroupToHC = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_transferGroupToHC.sqf",A3EAI_directory]; +A3EAI_HCGroupToServer = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_transferGroupToServer.sqf",A3EAI_directory]; +A3EAI_getGroupTriggerVars = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_getGroupTriggerVars.sqf",A3EAI_directory]; +A3EAI_updateGroupLootPool = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_updateGroupLootPool.sqf",A3EAI_directory]; +A3EAI_HCListener = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_HCListener.sqf",A3EAI_directory]; +A3EAI_updateGroupSizeServer = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_updateGroupSizeServer.sqf",A3EAI_directory]; +A3EAI_registerDeath = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_registerDeath.sqf",A3EAI_directory]; +A3EAI_protectRemoteGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_protectRemoteGroup.sqf",A3EAI_directory]; +A3EAI_setBehavior = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_headlessclient_server\A3EAI_setBehavior.sqf",A3EAI_directory]; + +diag_log "[A3EAI] A3EAI HC functions compiled."; \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_custom_loader.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_custom_loader.sqf new file mode 100644 index 0000000..308964f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_custom_loader.sqf @@ -0,0 +1,7 @@ +//Do not edit this file + +waitUntil {uiSleep 3; !isNil "A3EAI_locations_ready"}; + +call compile preprocessFileLineNumbers "A3EAI_config\A3EAI_custom_defs.sqf"; + +A3EAI_customSpawnsReady = true; diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_functions.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_functions.sqf new file mode 100644 index 0000000..4cf6b0b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_functions.sqf @@ -0,0 +1,180 @@ +/* + A3EAI Functions + +*/ + +diag_log "[A3EAI] Compiling A3EAI functions."; + +call compile preprocessFile format ["%1\SHK_pos\A3EAI_SHK_pos_init.sqf",A3EAI_directory]; + +//A3EAI_behavior +A3EAI_BIN_taskPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_BIN_taskPatrol.sqf",A3EAI_directory]; +A3EAI_customHeliDetect = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_customHeliDetect.sqf",A3EAI_directory]; +A3EAI_heliDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_heliDetection.sqf",A3EAI_directory]; +A3EAI_heliStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_heliStartPatrol.sqf",A3EAI_directory]; +A3EAI_hunterLocate = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_hunterLocate.sqf",A3EAI_directory]; +A3EAI_huntKiller = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_huntKiller.sqf",A3EAI_directory]; +A3EAI_reinforce_begin = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_reinforce_begin.sqf",A3EAI_directory]; +A3EAI_vehCrewRegroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_vehCrewRegroup.sqf",A3EAI_directory]; +A3EAI_vehCrewRegroupComplete = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_vehCrewRegroupComplete.sqf",A3EAI_directory]; +A3EAI_vehStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_vehStartPatrol.sqf",A3EAI_directory]; +A3EAI_UAVStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_UAVStartPatrol.sqf",A3EAI_directory]; +A3EAI_UAVDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_UAVDetection.sqf",A3EAI_directory]; +A3EAI_UGVStartPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_UGVStartPatrol.sqf",A3EAI_directory]; +A3EAI_UGVDetection = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_UGVDetection.sqf",A3EAI_directory]; +A3EAI_areaSearching = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_areaSearching.sqf",A3EAI_directory]; +A3EAI_startHunting = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_startHunting.sqf",A3EAI_directory]; +A3EAI_forceBehavior = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_forceBehavior.sqf",A3EAI_directory]; +A3EAI_defensiveAggression = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_behavior\A3EAI_defensiveAggression.sqf",A3EAI_directory]; + +//A3EAI_unit_events +A3EAI_handle_death_UV = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handle_death_UV.sqf",A3EAI_directory]; +A3EAI_handleDamageHeli = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDamageHeli.sqf",A3EAI_directory]; +A3EAI_handleDamageVeh = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDamageVeh.sqf",A3EAI_directory]; +A3EAI_handleDamageUnit = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDamageUnit.sqf",A3EAI_directory]; +A3EAI_handleDamageUGV = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDamageUGV.sqf",A3EAI_directory]; +A3EAI_handleDeath_air = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_air.sqf",A3EAI_directory]; +A3EAI_handleDeath_air_reinforce = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_air_reinforce.sqf",A3EAI_directory]; +A3EAI_handleDeath_aircrashed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_aircrashed.sqf",A3EAI_directory]; +A3EAI_handleDeath_aircustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_aircustom.sqf",A3EAI_directory]; +A3EAI_handleDeath_dynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_dynamic.sqf",A3EAI_directory]; +A3EAI_handleDeath_generic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_generic.sqf",A3EAI_directory]; +A3EAI_handleDeath_land = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_land.sqf",A3EAI_directory]; +A3EAI_handleDeath_landcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_landcustom.sqf",A3EAI_directory]; +A3EAI_handleDeath_random = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_random.sqf",A3EAI_directory]; +A3EAI_handleDeath_static = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_static.sqf",A3EAI_directory]; +A3EAI_handleDeath_staticcustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_staticcustom.sqf",A3EAI_directory]; +A3EAI_handleDeath_vehiclecrew = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeath_vehiclecrew.sqf",A3EAI_directory]; +A3EAI_handleDeathEvent = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_handleDeathEvent.sqf",A3EAI_directory]; +A3EAI_heliDestroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_heliDestroyed.sqf",A3EAI_directory]; +A3EAI_heliEvacuated = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_heliEvacuated.sqf",A3EAI_directory]; +A3EAI_heliLanded = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_heliLanded.sqf",A3EAI_directory]; +A3EAI_heliParaDrop = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_heliParaDrop.sqf",A3EAI_directory]; +A3EAI_UAV_destroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_UAV_destroyed.sqf",A3EAI_directory]; +A3EAI_UGV_destroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_UGV_destroyed.sqf",A3EAI_directory]; +A3EAI_vehDestroyed = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_vehDestroyed.sqf",A3EAI_directory]; +A3EAI_ejectParachute = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_events\A3EAI_ejectParachute.sqf",A3EAI_directory]; + +//A3EAI_unit_spawning +A3EAI_addRespawnQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_addRespawnQueue.sqf",A3EAI_directory]; +A3EAI_addVehicleGunners = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_addVehicleGunners.sqf",A3EAI_directory]; +A3EAI_cancelDynamicSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_cancelDynamicSpawn.sqf",A3EAI_directory]; +A3EAI_cancelRandomSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_cancelRandomSpawn.sqf",A3EAI_directory]; +A3EAI_create_UV_unit = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_create_UV_unit.sqf",A3EAI_directory]; +A3EAI_createCustomSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_createCustomSpawn.sqf",A3EAI_directory]; +A3EAI_createUnit = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_createUnit.sqf",A3EAI_directory]; +A3EAI_despawn_dynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_despawn_dynamic.sqf",A3EAI_directory]; +A3EAI_despawn_random = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_despawn_random.sqf",A3EAI_directory]; +A3EAI_despawn_static = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_despawn_static.sqf",A3EAI_directory]; +A3EAI_processRespawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_processRespawn.sqf",A3EAI_directory]; +A3EAI_respawnGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_respawnGroup.sqf",A3EAI_directory]; +A3EAI_setup_randomspawns = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_setup_randomspawns.sqf",A3EAI_directory]; +A3EAI_spawn_reinforcement = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawn_reinforcement.sqf",A3EAI_directory]; +A3EAI_spawn_UV_patrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawn_UV_patrol.sqf",A3EAI_directory]; +A3EAI_spawnGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnGroup.sqf",A3EAI_directory]; +A3EAI_spawnInfantryCustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnInfantryCustom.sqf",A3EAI_directory]; +A3EAI_spawnUnits_dynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnUnits_dynamic.sqf",A3EAI_directory]; +A3EAI_spawnUnits_random = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnUnits_random.sqf",A3EAI_directory]; +A3EAI_spawnUnits_static = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnUnits_static.sqf",A3EAI_directory]; +A3EAI_spawnVehicleCustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnVehicleCustom.sqf",A3EAI_directory]; +A3EAI_spawnVehiclePatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_spawnVehiclePatrol.sqf",A3EAI_directory]; +A3EAI_addVehicleGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_addVehicleGroup.sqf",A3EAI_directory]; +A3EAI_addParaGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_addParaGroup.sqf",A3EAI_directory]; +A3EAI_respawnAIVehicle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_respawnAIVehicle.sqf",A3EAI_directory]; +A3EAI_cleanupReinforcementGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_unit_spawning\A3EAI_cleanupReinforcementGroup.sqf",A3EAI_directory]; + +//A3EAI_utilities +A3EAI_activateKryptoPickup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_activateKryptoPickup.sqf",A3EAI_directory]; +A3EAI_addItem = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addItem.sqf",A3EAI_directory]; +A3EAI_addLandVehEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addLandVehEH.sqf",A3EAI_directory]; +A3EAI_addMapMarker = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addMapMarker.sqf",A3EAI_directory]; +A3EAI_addTempNVG = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addTempNVG.sqf",A3EAI_directory]; +A3EAI_addTemporaryWaypoint = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addTemporaryWaypoint.sqf",A3EAI_directory]; +A3EAI_addUAVEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addUAVEH.sqf",A3EAI_directory]; +A3EAI_addUGVEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addUGVEH.sqf",A3EAI_directory]; +A3EAI_addUnitEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addUnitEH.sqf",A3EAI_directory]; +A3EAI_addUVUnitEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addUVUnitEH.sqf",A3EAI_directory]; +A3EAI_addVehAirEH = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_addVehAirEH.sqf",A3EAI_directory]; +A3EAI_chance = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_chance.sqf",A3EAI_directory]; +A3EAI_checkClassname = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_checkClassname.sqf",A3EAI_directory]; +A3EAI_checkIsWeapon = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_checkIsWeapon.sqf",A3EAI_directory]; +A3EAI_checkInNoAggroArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_checkInNoAggroArea.sqf",A3EAI_directory]; +A3EAI_clearVehicleCargo = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_clearVehicleCargo.sqf",A3EAI_directory]; +A3EAI_countVehicleGunners = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_countVehicleGunners.sqf",A3EAI_directory]; +A3EAI_createBlackListArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createBlackListArea.sqf",A3EAI_directory]; +A3EAI_createBlackListAreaDynamic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createBlackListAreaDynamic.sqf",A3EAI_directory]; +A3EAI_createBlackListAreaRandom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createBlackListAreaRandom.sqf",A3EAI_directory]; +A3EAI_createBlacklistAreaQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createBlacklistAreaQueue.sqf",A3EAI_directory]; +A3EAI_createCustomInfantryQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createCustomInfantryQueue.sqf",A3EAI_directory]; +A3EAI_createCustomInfantrySpawnQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createCustomInfantrySpawnQueue.sqf",A3EAI_directory]; +A3EAI_createCustomVehicleQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createCustomVehicleQueue.sqf",A3EAI_directory]; +A3EAI_createGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createGroup.sqf",A3EAI_directory]; +A3EAI_createInfantryQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createInfantryQueue.sqf",A3EAI_directory]; +A3EAI_createNoAggroArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createNoAggroArea.sqf",A3EAI_directory]; +A3EAI_createRandomInfantrySpawnQueue = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_createRandomInfantrySpawnQueue.sqf",A3EAI_directory]; +A3EAI_deleteGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_deleteGroup.sqf",A3EAI_directory]; +A3EAI_deleteCustomSpawn = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_deleteCustomSpawn.sqf",A3EAI_directory]; +A3EAI_findSpawnPos = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_findSpawnPos.sqf",A3EAI_directory]; +A3EAI_fixStuckGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_fixStuckGroup.sqf",A3EAI_directory]; +A3EAI_generateKryptoPickup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_generateKryptoPickup.sqf",A3EAI_directory]; +A3EAI_getNoAggroStatus = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_getNoAggroStatus.sqf",A3EAI_directory]; +A3EAI_getSafePosReflected = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_getSafePosReflected.sqf",A3EAI_directory]; +A3EAI_getSpawnParams = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_getSpawnParams.sqf",A3EAI_directory]; +A3EAI_getUnitLevel = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_getUnitLevel.sqf",A3EAI_directory]; +A3EAI_getWeapon = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_getWeapon.sqf",A3EAI_directory]; +A3EAI_hasLOS = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_hasLOS.sqf",A3EAI_directory]; +A3EAI_initializeTrigger = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_initializeTrigger.sqf",A3EAI_directory]; +A3EAI_initNoAggroStatus = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_initNoAggroStatus.sqf",A3EAI_directory]; +A3EAI_initUVGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_initUVGroup.sqf",A3EAI_directory]; +A3EAI_moveToPosAndDeleteWP = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_moveToPosAndDeleteWP.sqf",A3EAI_directory]; +A3EAI_moveToPosAndPatrol = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_moveToPosAndPatrol.sqf",A3EAI_directory]; +A3EAI_noAggroAreaToggle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_noAggroAreaToggle.sqf",A3EAI_directory]; +A3EAI_param = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_param.sqf",A3EAI_directory]; +A3EAI_posInBuilding = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_posInBuilding.sqf",A3EAI_directory]; +A3EAI_protectGroup = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_protectGroup.sqf",A3EAI_directory]; +A3EAI_protectObject = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_protectObject.sqf",A3EAI_directory]; +A3EAI_purgeUnitGear = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_purgeUnitGear.sqf",A3EAI_directory]; +A3EAI_radioSend = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_radioSend.sqf",A3EAI_directory]; +A3EAI_randomizeVehicleColor = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_randomizeVehicleColor.sqf",A3EAI_directory]; +A3EAI_reloadVehicleTurrets = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_reloadVehicleTurrets.sqf",A3EAI_directory]; +A3EAI_removeExplosive = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_removeExplosive.sqf",A3EAI_directory]; +A3EAI_returnNoAggroArea = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_returnNoAggroArea.sqf",A3EAI_directory]; +A3EAI_secureVehicle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_secureVehicle.sqf",A3EAI_directory]; +A3EAI_sendKillMessage = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_sendKillMessage.sqf",A3EAI_directory]; +A3EAI_setFirstWPPos = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_setFirstWPPos.sqf",A3EAI_directory]; +A3EAI_setNoAggroStatus = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_setNoAggroStatus.sqf",A3EAI_directory]; +A3EAI_setSkills = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_setSkills.sqf",A3EAI_directory]; +A3EAI_setVehicleRegrouped = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_setVehicleRegrouped.sqf",A3EAI_directory]; +A3EAI_updateSpawnCount = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_updateSpawnCount.sqf",A3EAI_directory]; +A3EAI_updGroupCount = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_updGroupCount.sqf",A3EAI_directory]; +A3EAI_selectRandom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_selectRandom.sqf",A3EAI_directory]; +A3EAI_setRandomWaypoint = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_setRandomWaypoint.sqf",A3EAI_directory]; +A3EAI_getPosBetween = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_getPosBetween.sqf",A3EAI_directory]; +A3EAI_debugMarkerLocation = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_utilities\A3EAI_debugMarkerLocation.sqf",A3EAI_directory]; + +//Group functions +A3EAI_getLocalFunctions = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_getLocalFunctions.sqf",A3EAI_directory]; +A3EAI_getAntistuckTime = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_getAntistuckTime.sqf",A3EAI_directory]; +A3EAI_setLoadoutVariables = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_setLoadoutVariables.sqf",A3EAI_directory]; +A3EAI_execEveryLoop_air = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_execEveryLoop_air.sqf",A3EAI_directory]; +A3EAI_execEveryLoop_infantry = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_execEveryLoop_infantry.sqf",A3EAI_directory]; +A3EAI_execEveryLoop_vehicle = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_execEveryLoop_vehicle.sqf",A3EAI_directory]; +A3EAI_execEveryLoop_ugv = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_execEveryLoop_ugv.sqf",A3EAI_directory]; +A3EAI_execEveryLoop_uav = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_execEveryLoop_uav.sqf",A3EAI_directory]; +A3EAI_checkGroupUnits = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_checkGroupUnits.sqf",A3EAI_directory]; +A3EAI_generateGroupLoot = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_generateGroupLoot.sqf",A3EAI_directory]; +A3EAI_checkAmmoFuel = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_checkAmmoFuel.sqf",A3EAI_directory]; +A3EAI_antistuck_air = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_antistuck_air.sqf",A3EAI_directory]; +A3EAI_antistuck_aircustom = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_antistuck_aircustom.sqf",A3EAI_directory]; +A3EAI_antistuck_generic = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_antistuck_generic.sqf",A3EAI_directory]; +A3EAI_antistuck_land = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_antistuck_land.sqf",A3EAI_directory]; +A3EAI_antistuck_uav = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_antistuck_uav.sqf",A3EAI_directory]; +A3EAI_antistuck_ugv = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_antistuck_ugv.sqf",A3EAI_directory]; +A3EAI_generateLootPool = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_generateLootPool.sqf",A3EAI_directory]; +A3EAI_generateLootOnDeath = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_generateLootOnDeath.sqf",A3EAI_directory]; +A3EAI_generateLoadout = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_generateLoadout.sqf",A3EAI_directory]; +A3EAI_addGroupManager = compileFinal preprocessFileLineNumbers format ["%1\compile\A3EAI_group_functions\A3EAI_addGroupManager.sqf",A3EAI_directory]; + +diag_log "[A3EAI] A3EAI functions compiled."; + +true diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_initHC.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_initHC.sqf new file mode 100644 index 0000000..202f13f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_initHC.sqf @@ -0,0 +1,154 @@ +#include "\A3EAI\globaldefines.hpp" + +if (hasInterface || isDedicated || !isNil "A3EAI_HC_isActive") exitWith {}; + +_startTime = diag_tickTime; + +A3EAI_HC_isActive = true; +A3EAI_directory = "A3EAI"; +A3EAI_HCPlayerLoggedIn = false; +A3EAI_HCGroupsCount = 0; +A3EAI_enableHC = true; +A3EAI_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 ["A3EAI_centerMarker",_markerInfo select 0]; +_centerMarker setMarkerSizeLocal [_markerInfo select 1,_markerInfo select 1]; + +_nul = [] spawn { + _versionKey = [configFile >> "CfgPatches" >> "A3EAI_HC","A3EAI_HCVersion","0"] call BIS_fnc_returnConfigEntry; + diag_log format ["[A3EAI] Initializing A3EAI HC build %1 using base path %2.",_versionKey,A3EAI_directory]; + + A3EAI_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","@A3EAI_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 "[A3EAI] Waiting for HC player object setup to be completed."; + + waitUntil {uiSleep 2; player == player}; + if !((typeOf player) isEqualTo "HeadlessClient_F") exitWith { + diag_log format ["A3EAI Error: Headless client assigned to wrong player slot. Player Type: %1. Expected: HeadlessClient_F",(typeOf player)]; + }; + + A3EAI_HCObject = player; + A3EAI_HCObjectGroup = (group player); + A3EAI_HCObject allowDamage false; + + diag_log "[A3EAI] Attempting to connect to A3EAI server..."; + A3EAI_HCLogin_PVS = [A3EAI_HCObject,_versionKey,_useRemoteConfigs]; + publicVariableServer "A3EAI_HCLogin_PVS"; + _loginStart = diag_tickTime; + waitUntil {uiSleep 1; ((!isNil "A3EAI_HC_serverResponse") or {(diag_tickTime - _loginStart) > 60})}; + + if (isNil "A3EAI_HC_serverResponse") exitWith { + diag_log "[A3EAI] Headless client connection timed out after 60 seconds of no response from server."; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: HC Object %1. Group %2",A3EAI_HCObject,A3EAI_HCObjectGroup];}; + {deleteVehicle _x} forEach (units A3EAI_HCObjectGroup); + deleteGroup A3EAI_HCObjectGroup; + endMission "END1"; + }; + + _isConnectionRejected = call { + _responseType = (typeName A3EAI_HC_serverResponse); + if (_responseType == "ARRAY") exitWith { + (A3EAI_HC_serverResponse isEqualTo []) + }; + if (_responseType == "BOOL") exitWith { + !(A3EAI_HC_serverResponse) + }; + true + }; + + if (_isConnectionRejected) exitWith { + diag_log "[A3EAI] Headless client connection unsuccessful. HC authorization request rejected (incorrect HC version?)."; + {deleteVehicle _x} forEach (units A3EAI_HCObjectGroup); + deleteGroup A3EAI_HCObjectGroup; + endMission "END1"; + }; + + _configCheck = if (_useRemoteConfigs) then { + call compile preprocessFileLineNumbers format ["%1\init\loadSettingsHC.sqf",A3EAI_directory] + } else { + //Not currently supported + }; + if (isNil "_configCheck") exitWith {diag_log "A3EAI Critical Error: Configuration file not successfully loaded. Stopping startup procedure.";}; + + if (_debugLevelHC > 0) then {A3EAI_debugLevel = _debugLevelHC;}; + if (_readOverrideFile && {isFilePatchingEnabled}) then {call compile preprocessFileLineNumbers format ["%1\A3EAI_settings_override.sqf",_serverDir];}; + + diag_log "[A3EAI] Headless client connection successful. HC authorization request granted."; + + //Load internal use variables + call compile preprocessFileLineNumbers format ["%1\init\variables.sqf",A3EAI_directory]; + + //Load A3EAI functions and A3EAI HC functions + diag_log "[A3EAI] Compiling functions..."; + _check = call compile preprocessFileLineNumbers "A3EAI\init\A3EAI_HCFunctions.sqf"; + if (isNil "_check") exitWith {diag_log "A3EAI Critical Error: HC functions not successfully loaded. Stopping startup procedure.";}; + _check = call compile preprocessFileLineNumbers "A3EAI\init\A3EAI_functions.sqf"; + if (isNil "_check") exitWith {diag_log "A3EAI Critical Error: Functions not successfully loaded. Stopping startup procedure.";}; + _check = call compile preprocessFileLineNumbers "A3EAI\init\A3EAI_HC_PVEH.sqf"; + if (isNil "_check") exitWith {diag_log "A3EAI Critical Error: PublicVariable EHs not successfully loaded. Stopping startup procedure.";}; + + diag_log format ["[A3EAI] A3EAI HC started with Debug Level: %1.",A3EAI_debugLevel]; + + //Build location list + _setupLocations = [] execVM format ['%1\scripts\setup_locations.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.5; scriptDone _setupLocations}; + + _serverMonitor = [] execVM format ['%1\compile\A3EAI_headlessclient\A3EAI_HCMonitor.sqf',A3EAI_directory]; + + _nul = [] spawn { + while {true} do { + diag_log format ["HC is %1",(typeOf player)]; + uiSleep 5; + }; + }; +}; diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_initserver.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_initserver.sqf new file mode 100644 index 0000000..9816484 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_initserver.sqf @@ -0,0 +1,112 @@ +#include "\A3EAI\globaldefines.hpp" + +/* + A3EAI Server Initialization File + + Description: Handles startup process for A3EAI. Does not contain any values intended for modification. +*/ + +if (hasInterface || !isDedicated ||!isNil "A3EAI_isActive") exitWith {}; + +_startTime = diag_tickTime; + +A3EAI_isActive = true; + +private ["_startTime","_worldname","_allUnits","_configCheck","_functionsCheck","_readOverrideFile","_reportDirectoryName","_configVersion","_coreVersion","_compatibleVersions"]; + +A3EAI_directory = "A3EAI"; //PREFIX + +_coreVersion = [configFile >> "CfgPatches" >> "A3EAI","A3EAIVersion",""] call BIS_fnc_returnConfigEntry; +_configVersion = [configFile >> "CfgPatches" >> "A3EAI_config","A3EAIVersion",""] call BIS_fnc_returnConfigEntry; +_compatibleVersions = [configFile >> "CfgPatches" >> "A3EAI","compatibleConfigVersions",[]] call BIS_fnc_returnConfigEntry; +_serverDir = [missionConfigFile >> "CfgDeveloperOptions","serverDir","@A3EAI"] 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); +A3EAI_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 ["A3EAI Error: Incompatible A3EAI core and config pbo versions. Core: %1. Config: %2. Please update both A3EAI.pbo and A3EAI_config.pbo.",_coreVersion,_configVersion]; +}; + +//Report A3EAI version to RPT log +diag_log format ["[A3EAI] Initializing A3EAI version %1 using base path %2.",[configFile >> "CfgPatches" >> "A3EAI","A3EAIVersion","error - unknown version"] call BIS_fnc_returnConfigEntry,A3EAI_directory]; + +//Load A3EAI functions +_functionsCheck = call compile preprocessFileLineNumbers format ["%1\init\A3EAI_functions.sqf",A3EAI_directory]; +if (isNil "_functionsCheck") exitWith {diag_log "A3EAI Critical Error: Functions not successfully loaded. Stopping startup procedure.";}; + +//Load A3EAI settings +_configCheck = call compile preprocessFileLineNumbers format ["%1\init\loadSettings.sqf",A3EAI_directory]; +if (isNil "_configCheck") exitWith {diag_log "A3EAI Critical Error: Configuration file not successfully loaded. Stopping startup procedure.";}; + +//Load custom A3EAI settings file. +if ((_readOverrideFile) && {isFilePatchingEnabled}) then {call compile preprocessFileLineNumbers format ["%1\A3EAI_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 ["A3EAI_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_SIDE2) exitWith {1}} count _allUnits) isEqualTo 0) then {createCenter PLAYER_GROUP_SIDE2}; +if (({if ((side _x) isEqualTo PLAYER_GROUP_SIDE1) exitWith {1}} count _allUnits) isEqualTo 0) then {createCenter PLAYER_GROUP_SIDE1}; +if (({if ((side _x) isEqualTo AI_GROUP_SIDE) exitWith {1}} count _allUnits) isEqualTo 0) then {createCenter AI_GROUP_SIDE}; +if ((AI_GROUP_SIDE getFriend PLAYER_GROUP_SIDE1) > 0) then {AI_GROUP_SIDE setFriend [PLAYER_GROUP_SIDE1, 0]}; +if ((AI_GROUP_SIDE getFriend PLAYER_GROUP_SIDE2) > 0) then {AI_GROUP_SIDE setFriend [PLAYER_GROUP_SIDE2, 0]}; +if ((PLAYER_GROUP_SIDE2 getFriend AI_GROUP_SIDE) > 0) then {PLAYER_GROUP_SIDE2 setFriend [AI_GROUP_SIDE, 0]}; +if ((PLAYER_GROUP_SIDE1 getFriend AI_GROUP_SIDE) > 0) then {PLAYER_GROUP_SIDE1 setFriend [AI_GROUP_SIDE, 0]}; + +//Continue loading required A3EAI script files +[] execVM format ['%1\init\A3EAI_post_init.sqf',A3EAI_directory]; + +//Report A3EAI startup settings to RPT log +diag_log format ["[A3EAI] A3EAI settings: Debug Level: %1. WorldName: %2. VerifyClassnames: %3. VerifySettings: %4.",A3EAI_debugLevel,_worldname,A3EAI_verifyClassnames,A3EAI_verifySettings]; +diag_log format ["[A3EAI] AI spawn settings: Static: %1. Dynamic: %2. Random: %3. Air: %4. Land: %5. UAV: %6. UGV: %7.",A3EAI_enableStaticSpawns,!(A3EAI_maxDynamicSpawns isEqualTo 0),!(A3EAI_maxRandomSpawns isEqualTo 0),!(A3EAI_maxAirPatrols isEqualTo 0),!(A3EAI_maxLandPatrols isEqualTo 0),!(A3EAI_maxUAVPatrols isEqualTo 0),!(A3EAI_maxUGVPatrols isEqualTo 0)]; +diag_log format ["[A3EAI] A3EAI loading completed in %1 seconds.",(diag_tickTime - _startTime)]; diff --git a/Server/@A3EAI/addons/a3eai/init/A3EAI_post_init.sqf b/Server/@A3EAI/addons/a3eai/init/A3EAI_post_init.sqf new file mode 100644 index 0000000..915ec21 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/A3EAI_post_init.sqf @@ -0,0 +1,149 @@ +#include "\A3EAI\globaldefines.hpp" + +/* + A3EAI Startup + + Description: Handles post-initialization tasks + +*/ + +if (A3EAI_debugLevel > 0) then {diag_log "A3EAI Debug: A3EAI Startup is running required script files..."}; + +call compile preprocessFileLineNumbers format ["%1\init\variables.sqf",A3EAI_directory]; +call compile preprocessFileLineNumbers format ["%1\init\variables_precalculated.sqf",A3EAI_directory]; + +if (A3EAI_enableHC) then { + [] call compile preprocessFileLineNumbers format ["%1\init\A3EAI_ServerHC_functions.sqf",A3EAI_directory]; + [] call compile preprocessFileLineNumbers format ["%1\init\A3EAI_ServerHC_PVEH.sqf",A3EAI_directory]; + diag_log "[A3EAI] A3EAI 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 { + A3EAI_defaultTrigger = createTrigger [TRIGGER_OBJECT,[configFile >> "CfgWorlds" >> worldName,"centerPosition",[0,0,0]] call BIS_fnc_returnConfigEntry,false]; + A3EAI_defaultTrigger enableSimulation false; + A3EAI_defaultTrigger setVariable ["isCleaning",true]; + A3EAI_defaultTrigger setVariable ["patrolDist",100]; + A3EAI_defaultTrigger setVariable ["unitLevel",1]; + A3EAI_defaultTrigger setVariable ["unitLevelEffective",1]; + A3EAI_defaultTrigger setVariable ["locationArray",[]]; + A3EAI_defaultTrigger setVariable ["maxUnits",[0,0]]; + A3EAI_defaultTrigger setVariable ["GroupSize",0]; + A3EAI_defaultTrigger setVariable ["initialized",true]; + A3EAI_defaultTrigger setVariable ["spawnChance",0]; + A3EAI_defaultTrigger setVariable ["spawnType",""]; + A3EAI_defaultTrigger setTriggerText "Default Trigger Object"; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Default trigger check result: %1",[!(isNull A3EAI_defaultTrigger),(typeOf A3EAI_defaultTrigger),(getPosASL A3EAI_defaultTrigger)]]}; +}; + +[ + //Input variable - Gradechances array, Output variable - Gradeindices array + ["A3EAI_levelChancesAir","A3EAI_levelIndicesAir"], + ["A3EAI_levelChancesLand","A3EAI_levelIndicesLand"], + ["A3EAI_levelChancesUAV","A3EAI_levelIndicesUAV"], + ["A3EAI_levelChancesUGV","A3EAI_levelIndicesUGV"], + ["A3EAI_useWeaponChance0","A3EAI_weaponTypeIndices0"], + ["A3EAI_useWeaponChance1","A3EAI_weaponTypeIndices1"], + ["A3EAI_useWeaponChance2","A3EAI_weaponTypeIndices2"], + ["A3EAI_useWeaponChance3","A3EAI_weaponTypeIndices3"] +] call compile preprocessFileLineNumbers format ["%1\scripts\buildWeightedTables.sqf",A3EAI_directory]; + +if (A3EAI_verifyClassnames) then { + A3EAI_tableChecklist = ["A3EAI_pistolList","A3EAI_rifleList","A3EAI_machinegunList","A3EAI_sniperList","A3EAI_headgearTypes0","A3EAI_headgearTypes1","A3EAI_headgearTypes2","A3EAI_headgearTypes3", + "A3EAI_backpackTypes0","A3EAI_backpackTypes1","A3EAI_backpackTypes2","A3EAI_backpackTypes3","A3EAI_foodLoot","A3EAI_MiscLoot1","A3EAI_MiscLoot2","A3EAI_airReinforcementVehicles", + "A3EAI_uniformTypes0","A3EAI_uniformTypes1","A3EAI_uniformTypes2","A3EAI_uniformTypes3","A3EAI_launcherTypes","A3EAI_vestTypes0","A3EAI_vestTypes1","A3EAI_vestTypes2","A3EAI_vestTypes3"]; +}; + + +if (A3EAI_generateDynamicUniforms) then { + _skinlist = [] execVM format ['%1\scripts\A3EAI_buildUniformList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _skinlist}; +}; + +//Build weapon classname tables +if (A3EAI_generateDynamicWeapons) then { + _weaponlist = [] execVM format ['%1\scripts\A3EAI_buildWeaponList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _weaponlist}; +}; + +//Build backpack classname tables +if (A3EAI_generateDynamicBackpacks) then { + _backpacklist = [] execVM format ['%1\scripts\A3EAI_buildBackpackList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _backpacklist}; +}; + +//Build vest classname tables +if (A3EAI_generateDynamicVests) then { + _vestlist = [] execVM format ['%1\scripts\A3EAI_buildVestList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _vestlist}; +}; + +//Build headgear classname tables +if (A3EAI_generateDynamicHeadgear) then { + _headgearlist = [] execVM format ['%1\scripts\A3EAI_buildHeadgearList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _headgearlist}; +}; + +//Build food classname tables (1) +if (A3EAI_generateDynamicFood) then { + _foodlist = [] execVM format ['%1\scripts\A3EAI_buildFoodList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _foodlist}; +}; + +//Build generic loot classname tables (1) +if (A3EAI_generateDynamicLoot) then { + _lootlist = [] execVM format ['%1\scripts\A3EAI_buildLootList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _lootlist}; + + _lootlist2 = [] execVM format ['%1\scripts\A3EAI_buildLootLargeList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _lootlist2}; +}; + +if (A3EAI_generateDynamicOptics) then { + _weaponScopes = [] execVM format ['%1\scripts\A3EAI_buildScopeList.sqf',A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _weaponScopes}; +}; + +//Check classname tables if enabled +if (A3EAI_verifyClassnames) then { + _verifyClassnames = [] execVM format ["%1\scripts\verifyClassnames.sqf",A3EAI_directory]; + waitUntil {uiSleep 0.05; scriptDone _verifyClassnames}; +}; + +if (A3EAI_enableHC && {A3EAI_waitForHC}) then { + diag_log "[A3EAI] Waiting for headless client to connect. A3EAI post-initialization process paused."; + waitUntil {uiSleep 5; A3EAI_HCIsConnected}; + diag_log format ["[A3EAI] Headless client connected with owner ID %1. A3EAI post-initialization process continuing.",A3EAI_HCObjectOwnerID]; +}; + +A3EAI_classnamesVerified = true; + +//Build map location list. +_setupLocations = [] execVM format ['%1\scripts\setup_locations.sqf',A3EAI_directory]; + +//Set up auto-generated static spawns +if (A3EAI_enableStaticSpawns) then { + _staticSpawns = [] execVM format ["%1\scripts\generateStaticSpawns.sqf",A3EAI_directory]; +}; + +//Start dynamic spawn manager +if !(A3EAI_maxDynamicSpawns isEqualTo 0) then { + _dynManagerV2 = [] execVM format ['%1\scripts\dynamicSpawn_manager.sqf',A3EAI_directory]; +}; + +//Set up vehicle patrols +if ((A3EAI_maxAirPatrols > 0) or {(A3EAI_maxLandPatrols > 0)}) then { + _vehicles = [] execVM format ['%1\scripts\setup_veh_patrols.sqf',A3EAI_directory]; +}; + +//Load custom definitions file +if (A3EAI_loadCustomFile) then { + if (isClass (configFile >> "CfgA3EAISettings")) then { + _customLoader = [] execVM format ["%1\init\A3EAI_custom_loader.sqf",A3EAI_directory]; //0.1.8 + } else { + diag_log "A3EAI Error: Could not load A3EAI_config.pbo. Unable to load custom definitions."; + }; +}; + +//Load A3EAI server monitor +_serverMonitor = [] execVM format ['%1\scripts\A3EAI_serverMonitor.sqf',A3EAI_directory]; diff --git a/Server/@A3EAI/addons/a3eai/init/fn_emptyFunction.sqf b/Server/@A3EAI/addons/a3eai/init/fn_emptyFunction.sqf new file mode 100644 index 0000000..e69de29 diff --git a/Server/@A3EAI/addons/a3eai/init/fn_init.sqf b/Server/@A3EAI/addons/a3eai/init/fn_init.sqf new file mode 100644 index 0000000..e69de29 diff --git a/Server/@A3EAI/addons/a3eai/init/fn_postinit.sqf b/Server/@A3EAI/addons/a3eai/init/fn_postinit.sqf new file mode 100644 index 0000000..e69de29 diff --git a/Server/@A3EAI/addons/a3eai/init/loadSettings.sqf b/Server/@A3EAI/addons/a3eai/init/loadSettings.sqf new file mode 100644 index 0000000..243de02 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/loadSettings.sqf @@ -0,0 +1,360 @@ +_startTime = diag_tickTime; + +A3EAI_pushedHCVariables = []; + +_fnc_getConfigValue = { + private ["_variableName", "_defaultValue", "_configValue", "_returnValue", "_type", "_string"]; + + _variableName = _x select 0; + _defaultValue = _x select 1; + + if (isClass (configFile >> "CfgA3EAISettings")) then { + _configValue = configFile >> "CfgA3EAISettings" >> _variableName; + _returnValue = call { + _type = (typeName _defaultValue); + if ((_type isEqualTo "SCALAR") && {isNumber _configValue}) exitWith { + getNumber _configValue + }; + if ((_type isEqualTo "BOOL") && {isNumber _configValue}) exitWith { + (getNumber _configValue) isEqualTo 1 + }; + if ((_type isEqualTo "ARRAY") && {isArray _configValue}) exitWith { + getArray _configValue + }; + if ((_type isEqualTo "STRING") && {isText _configValue}) exitWith { + getText _configValue + }; + 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 ["[A3EAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + }; + diag_log format ["[A3EAI] Error found in setting %1, resetting to default value.",_variableName]; + _defaultValue + }; + } else { + diag_log format ["[A3EAI] Error: Setting %1 not found in config, resetting to default value.",_variableName]; + _returnValue = _defaultValue; + }; + _returnValue +}; + +{ + private ["_variableName", "_defaultValue", "_HCPushable", "_variableValue"]; + _variableName = _x select 0; + _defaultValue = _x select 1; + _HCPushable = [_x,2,false] call A3EAI_param; + + _variableValue = [_variableName,_defaultValue] call _fnc_getConfigValue; + missionNamespace setVariable [format ["A3EAI_%1",_variableName],_variableValue]; + if (_HCPushable) then { + A3EAI_pushedHCVariables pushBack [_variableName,_variableValue]; + //diag_log format ["Debug: Found HC variable %1:%2",_variableName,_variableValue]; + }; +} forEach [ + ["debugLevel",0,true], //HC Pushable + ["monitorReportRate",300,true], //HC Pushable + ["verifyClassnames",true], + ["verifySettings",true], + ["cleanupDelay",900], + ["loadCustomFile",true], + ["enableHC",false,true], //HC Pushable + ["waitForHC",false], + ["generateDynamicWeapons",true], + ["dynamicWeaponBlacklist",[]], + ["generateDynamicOptics",true], + ["dynamicOpticsBlacklist",[]], + ["generateDynamicUniforms",true], + ["dynamicUniformBlacklist",[]], + ["generateDynamicBackpacks",true], + ["dynamicBackpackBlacklist",[]], + ["generateDynamicVests",true], + ["dynamicVestBlacklist",[]], + ["generateDynamicHeadgear",true], + ["dynamicHeadgearBlacklist",[]], + ["generateDynamicFood",true], + ["dynamicFoodBlacklist",[]], + ["generateDynamicLoot",true], + ["dynamicLootBlacklist",[]], + ["enableRadioMessages",false,true], //HC Pushable + ["enableDeathMessages",false,true], //HC Pushable + ["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], + ["enableFindKiller",true,true], //HC Pushable + ["enableTempNVGs",false], + ["levelRequiredGL",2,true], //HC Pushable + ["levelRequiredLauncher",-1,true], //HC Pushable + ["launcherTypes",[],true], //HC Pushable + ["launchersPerGroup",1,true], //HC Pushable + ["enableHealing",true], + ["removeExplosiveAmmo",true], + ["noCollisionDamage",true,true], //HC Pushable + ["roadKillPenalty",true,true], //HC Pushable + ["enableStaticSpawns",true], + ["respawnTimeMin",300], + ["respawnTimeMax",600], + ["despawnWait",120], + ["respawnLimit_village",-1], + ["respawnLimit_city",-1], + ["respawnLimit_capitalCity",-1], + ["respawnLimit_remoteArea",-1], + ["maxDynamicSpawns",15], + ["timePerDynamicSpawn",900], + ["purgeLastDynamicSpawnTime",3600], + ["spawnHunterChance",0.60], + ["despawnDynamicSpawnTime",120], + ["maxRandomSpawns",-1], + ["despawnRandomSpawnTime",120], + ["distanceBetweenRandomSpawns",600], + ["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_Transport_01_F",5], + ["B_Heli_Transport_03_F",2] + ]], + ["airGunnerUnits",2], + ["airDetectChance",0.80,true], //HC Pushable + ["paradropChance",0.50,true], //HC Pushable + ["paradropCooldown",1800,true], //HC Pushable + ["paradropAmount",3,true], //HC Pushable + ["maxLandPatrols",0], + ["levelChancesLand",[0.00,0.50,0.35,0.15]], + ["respawnLandMinTime",600], + ["respawnLandMaxTime",900], + ["landVehicleList",[ + ["B_MRAP_01_EPOCH",5], + ["C_Van_01_box_EPOCH",5], + ["C_Van_01_transport_EPOCH",5], + ["C_Offroad_01_EPOCH",5], + ["C_Hatchback_02_EPOCH",5], + ["C_Hatchback_01_EPOCH",5], + ["C_SUV_01_EPOCH",5], + ["B_Truck_01_transport_EPOCH",5], + ["B_Truck_01_covered_EPOCH",5], + ["B_Truck_01_mover_EPOCH",5], + ["B_Truck_01_box_EPOCH",5], + ["O_Truck_02_covered_EPOCH",5], + ["O_Truck_02_transport_EPOCH",5], + ["O_Truck_03_covered_EPOCH",5], + ["O_Truck_02_box_EPOCH",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"]], + ["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], + ["detectOnlyUAVs",false,true], //HC Pushable + ["UAVCallReinforceCooldown",1800,true], //HC Pushable + ["UAVDetectChance",0.80,true], //HC Pushable + ["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], + ["detectOnlyUGVs",false,true], //HC Pushable + ["UGVCallReinforceCooldown",1800,true], //HC Pushable + ["UGVDetectChance",0.80,true], //HC Pushable + ["skill0",[ + ["aimingAccuracy",0.10,0.15], + ["aimingShake",0.50,0.59], + ["aimingSpeed",0.50,0.59], + ["spotDistance",0.50,0.59], + ["spotTime",0.50,0.59], + ["courage",0.50,0.59], + ["reloadSpeed",0.50,0.59], + ["commanding",0.50,0.59], + ["general",0.50,0.59] + ]], + ["skill1",[ + ["aimingAccuracy",0.15,0.20], + ["aimingShake",0.60,0.69], + ["aimingSpeed",0.60,0.69], + ["spotDistance",0.60,0.69], + ["spotTime",0.60,0.69], + ["courage",0.60,0.69], + ["reloadSpeed",0.60,0.69], + ["commanding",0.60,0.69], + ["general",0.60,0.69] + ]], + ["skill2",[ + ["aimingAccuracy",0.20,0.25], + ["aimingShake",0.70,0.85], + ["aimingSpeed",0.70,0.85], + ["spotDistance",0.70,0.85], + ["spotTime",0.70,0.85], + ["courage",0.70,0.85], + ["reloadSpeed",0.70,0.85], + ["commanding",0.70,0.85], + ["general",0.70,0.85] + ]], + ["skill3",[ + ["aimingAccuracy",0.25,0.30], + ["aimingShake",0.85,0.95], + ["aimingSpeed",0.85,0.95], + ["spotDistance",0.85,0.95], + ["spotTime",0.85,0.95], + ["courage",0.85,0.95], + ["reloadSpeed",0.85,0.95], + ["commanding",0.85,0.95], + ["general",0.85,0.95] + ]], + ["addUniformChance0",0.60], + ["addUniformChance1",0.70], + ["addUniformChance2",0.80], + ["addUniformChance3",0.90], + ["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], + ["kryptoAmount0",50], + ["kryptoAmount1",75], + ["kryptoAmount2",100], + ["kryptoAmount3",150], + ["kryptoPickupAssist",0], + ["foodLootCount",2], + ["miscLootCount1",2], + ["miscLootCount2",1], + ["firstAidKitChance",0.25], + ["lootPullChance0",0.20,true], //HC Pushable + ["lootPullChance1",0.40,true], //HC Pushable + ["lootPullChance2",0.60,true], //HC Pushable + ["lootPullChance3",0.80,true], //HC Pushable + ["uniformTypes0",["U_O_CombatUniform_ocamo", "U_O_GhillieSuit", "U_O_PilotCoveralls", "U_O_Wetsuit", "U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_C_Poloshirt_stripped", "U_C_Poloshirt_blue", "U_C_Poloshirt_burgundy", "U_C_Poloshirt_tricolour", "U_C_Poloshirt_salmon", "U_C_Poloshirt_redwhite", "U_C_Poor_1", "U_C_WorkerCoveralls", "U_C_Journalist", "U_C_Scientist", "U_OrestesBody", "U_Wetsuit_uniform", "U_Wetsuit_White", "U_Wetsuit_Blue", "U_Wetsuit_Purp", "U_Wetsuit_Camo", "U_CamoRed_uniform", "U_CamoBrn_uniform", "U_CamoBlue_uniform", "U_Camo_uniform", "U_ghillie1_uniform", "U_ghillie2_uniform", "U_ghillie3_uniform", "U_C_Driver_1", "U_C_Driver_2", "U_C_Driver_3", "U_C_Driver_4", "U_C_Driver_1_black", "U_C_Driver_1_blue", "U_C_Driver_1_green", "U_C_Driver_1_red", "U_C_Driver_1_white", "U_C_Driver_1_yellow", "U_C_Driver_1_orange", "U_C_Driver_1_red"]], + ["uniformTypes1",["U_O_CombatUniform_ocamo", "U_O_GhillieSuit", "U_O_PilotCoveralls", "U_O_Wetsuit", "U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_C_Poloshirt_stripped", "U_C_Poloshirt_blue", "U_C_Poloshirt_burgundy", "U_C_Poloshirt_tricolour", "U_C_Poloshirt_salmon", "U_C_Poloshirt_redwhite", "U_C_Poor_1", "U_C_WorkerCoveralls", "U_C_Journalist", "U_C_Scientist", "U_OrestesBody", "U_Wetsuit_uniform", "U_Wetsuit_White", "U_Wetsuit_Blue", "U_Wetsuit_Purp", "U_Wetsuit_Camo", "U_CamoRed_uniform", "U_CamoBrn_uniform", "U_CamoBlue_uniform", "U_Camo_uniform", "U_ghillie1_uniform", "U_ghillie2_uniform", "U_ghillie3_uniform", "U_C_Driver_1", "U_C_Driver_2", "U_C_Driver_3", "U_C_Driver_4", "U_C_Driver_1_black", "U_C_Driver_1_blue", "U_C_Driver_1_green", "U_C_Driver_1_red", "U_C_Driver_1_white", "U_C_Driver_1_yellow", "U_C_Driver_1_orange", "U_C_Driver_1_red"]], + ["uniformTypes2",["U_O_CombatUniform_ocamo", "U_O_GhillieSuit", "U_O_PilotCoveralls", "U_O_Wetsuit", "U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_C_Poloshirt_stripped", "U_C_Poloshirt_blue", "U_C_Poloshirt_burgundy", "U_C_Poloshirt_tricolour", "U_C_Poloshirt_salmon", "U_C_Poloshirt_redwhite", "U_C_Poor_1", "U_C_WorkerCoveralls", "U_C_Journalist", "U_C_Scientist", "U_OrestesBody", "U_Wetsuit_uniform", "U_Wetsuit_White", "U_Wetsuit_Blue", "U_Wetsuit_Purp", "U_Wetsuit_Camo", "U_CamoRed_uniform", "U_CamoBrn_uniform", "U_CamoBlue_uniform", "U_Camo_uniform", "U_ghillie1_uniform", "U_ghillie2_uniform", "U_ghillie3_uniform", "U_C_Driver_1", "U_C_Driver_2", "U_C_Driver_3", "U_C_Driver_4", "U_C_Driver_1_black", "U_C_Driver_1_blue", "U_C_Driver_1_green", "U_C_Driver_1_red", "U_C_Driver_1_white", "U_C_Driver_1_yellow", "U_C_Driver_1_orange", "U_C_Driver_1_red"]], + ["uniformTypes3",["U_O_CombatUniform_ocamo", "U_O_GhillieSuit", "U_O_PilotCoveralls", "U_O_Wetsuit", "U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_C_Poloshirt_stripped", "U_C_Poloshirt_blue", "U_C_Poloshirt_burgundy", "U_C_Poloshirt_tricolour", "U_C_Poloshirt_salmon", "U_C_Poloshirt_redwhite", "U_C_Poor_1", "U_C_WorkerCoveralls", "U_C_Journalist", "U_C_Scientist", "U_OrestesBody", "U_Wetsuit_uniform", "U_Wetsuit_White", "U_Wetsuit_Blue", "U_Wetsuit_Purp", "U_Wetsuit_Camo", "U_CamoRed_uniform", "U_CamoBrn_uniform", "U_CamoBlue_uniform", "U_Camo_uniform", "U_ghillie1_uniform", "U_ghillie2_uniform", "U_ghillie3_uniform", "U_C_Driver_1", "U_C_Driver_2", "U_C_Driver_3", "U_C_Driver_4", "U_C_Driver_1_black", "U_C_Driver_1_blue", "U_C_Driver_1_green", "U_C_Driver_1_red", "U_C_Driver_1_white", "U_C_Driver_1_yellow", "U_C_Driver_1_orange", "U_C_Driver_1_red"]], + ["pistolList",["hgun_Pistol_heavy_01_F","hgun_P07_F","hgun_Rook40_F","hgun_Pistol_heavy_02_F","1911_pistol_epoch","hgun_ACPC2_F","ruger_pistol_epoch"]], + ["rifleList",["AKM_EPOCH","sr25_epoch","arifle_Katiba_GL_F","arifle_Katiba_C_F","arifle_Katiba_F","arifle_MX_GL_F","arifle_MX_GL_Black_F","arifle_MXM_Black_F","arifle_MXC_Black_F","arifle_MX_Black_F","arifle_MXM_F","arifle_MXC_F","arifle_MX_F","l85a2_epoch","l85a2_pink_epoch","l85a2_ugl_epoch","m4a3_EPOCH","m16_EPOCH","m16Red_EPOCH","arifle_Mk20_GL_F","arifle_Mk20_GL_plain_F","arifle_Mk20C_F","arifle_Mk20C_plain_F","arifle_Mk20_F","arifle_Mk20_plain_F","arifle_TRG21_GL_F","arifle_TRG21_F","arifle_TRG20_F","arifle_SDAR_F","Rollins_F","SMG_01_F","SMG_02_F","hgun_PDW2000_F"]], + ["machinegunList",["LMG_Zafir_F","arifle_MX_SW_F","arifle_MX_SW_Black_F","LMG_Mk200_F","m249_EPOCH","m249Tan_EPOCH","MMG_01_hex_F","MMG_01_tan_F","MMG_02_camo_F","MMG_02_black_F","MMG_02_sand_F"]], + ["sniperList",["m107_EPOCH","m107Tan_EPOCH","srifle_DMR_02_F","srifle_DMR_02_camo_F","srifle_DMR_02_sniper_F","srifle_DMR_03_F","srifle_DMR_03_khaki_F","srifle_DMR_03_tan_F","srifle_DMR_03_multicam_F","srifle_DMR_03_woodland_F","srifle_DMR_03_spotter_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_LRR_F","srifle_GM6_F","srifle_DMR_01_F","M14_EPOCH","M14Grn_EPOCH","srifle_EBR_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_epoch","Elcan_reflex_epoch","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_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"]], + ["backpackTypes1",["B_AssaultPack_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"]], + ["backpackTypes2",["B_AssaultPack_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"]], + ["backpackTypes3",["B_AssaultPack_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"]], + ["vestTypes0",["V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"]], + ["vestTypes1",["V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"]], + ["vestTypes2",["V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"]], + ["vestTypes3",["V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"]], + ["headgearTypes0",["H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"]], + ["headgearTypes1",["H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"]], + ["headgearTypes2",["H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"]], + ["headgearTypes3",["H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"]], + ["foodLoot",["FoodSnooter","FoodWalkNSons","FoodBioMeat","ItemSodaOrangeSherbet","ItemSodaPurple","ItemSodaMocha","ItemSodaBurst","ItemSodaRbull","honey_epoch","emptyjar_epoch","sardines_epoch","meatballs_epoch","scam_epoch","sweetcorn_epoch","WhiskeyNoodle","ItemCoolerE"]], + ["miscLoot1",["PaintCanClear","PaintCanBlk","PaintCanBlu","PaintCanBrn","PaintCanGrn","PaintCanOra","PaintCanPur","PaintCanRed","PaintCanTeal","PaintCanYel","ItemDocument","ItemMixOil","emptyjar_epoch","emptyjar_epoch","FoodBioMeat","ItemSodaOrangeSherbet","ItemSodaPurple","ItemSodaMocha","ItemSodaBurst","ItemSodaRbull","sardines_epoch","meatballs_epoch","scam_epoch","sweetcorn_epoch","Towelette","Towelette","Towelette","Towelette","Towelette","HeatPack","HeatPack","HeatPack","ColdPack","ColdPack","VehicleRepair","CircuitParts","ItemCoolerE","ItemScraps","ItemScraps"]], + ["miscLoot2",["MortarBucket","MortarBucket","ItemCorrugated","CinderBlocks","jerrycan_epoch","jerrycan_epoch","VehicleRepair","VehicleRepair","CircuitParts"]], + ["toolsList0",[["ItemWatch",0.70],["ItemCompass",0.50],["ItemMap",0.50],["ItemGPS",0.05],["EpochRadio0",0.05]]], + ["toolsList1",[["ItemWatch",0.80],["ItemCompass",0.60],["ItemMap",0.60],["ItemGPS",0.10],["EpochRadio0",0.10]]], + ["toolsList2",[["ItemWatch",0.80],["ItemCompass",0.70],["ItemMap",0.70],["ItemGPS",0.15],["EpochRadio0",0.15]]], + ["toolsList3",[["ItemWatch",0.80],["ItemCompass",0.80],["ItemMap",0.80],["ItemGPS",0.20],["EpochRadio0",0.20]]], + ["gadgetsList0",[["binocular",0.40],["NVG_EPOCH",0.05]]], + ["gadgetsList1",[["binocular",0.50],["NVG_EPOCH",0.10]]], + ["gadgetsList2",[["binocular",0.60],["NVG_EPOCH",0.15]]], + ["gadgetsList3",[["binocular",0.70],["NVG_EPOCH",0.20]]] +]; + +if (A3EAI_verifySettings) then { + if !(A3EAI_unitLevel_capitalCity in [0,1,2,3]) then {diag_log format ["[A3EAI] Error found in variable A3EAI_unitLevel_capitalCity, resetting to default value."]; A3EAI_unitLevel_capitalCity = 1}; + if !(A3EAI_unitLevel_city in [0,1,2,3]) then {diag_log format ["[A3EAI] Error found in variable A3EAI_unitLevel_city, resetting to default value."]; A3EAI_unitLevel_city = 1}; + if !(A3EAI_unitLevel_village in [0,1,2,3]) then {diag_log format ["[A3EAI] Error found in variable A3EAI_unitLevel_village, resetting to default value."]; A3EAI_unitLevel_village = 0}; + if !(A3EAI_unitLevel_remoteArea in [0,1,2,3]) then {diag_log format ["[A3EAI] Error found in variable A3EAI_unitLevel_remoteArea, resetting to default value."]; A3EAI_unitLevel_remoteArea = 2}; + if !(A3EAI_unitLevel_wilderness in [0,1,2,3]) then {diag_log format ["[A3EAI] Error found in variable A3EAI_unitLevel_remoteArea, resetting to default value."]; A3EAI_unitLevel_wilderness = 2}; + if !((count A3EAI_levelChancesAir) isEqualTo 4) then {diag_log format ["[A3EAI] Error found in variable A3EAI_levelChancesAir, resetting to default value."]; A3EAI_levelChancesAir = [0.00,0.50,0.35,0.15]}; + if !((count A3EAI_levelChancesLand) isEqualTo 4) then {diag_log format ["[A3EAI] Error found in variable A3EAI_levelChancesLand, resetting to default value."]; A3EAI_levelChancesAir = [0.00,0.50,0.35,0.15]}; + if !((count A3EAI_useWeaponChance0) isEqualTo 4) then {diag_log format ["[A3EAI] Error found in variable A3EAI_useWeaponChance0, resetting to default value."]; A3EAI_useWeaponChance0 = [0.20,0.80,0.00,0.00]}; + if !((count A3EAI_useWeaponChance1) isEqualTo 4) then {diag_log format ["[A3EAI] Error found in variable A3EAI_useWeaponChance1, resetting to default value."]; A3EAI_useWeaponChance1 = [0.00,0.90,0.05,0.05]}; + if !((count A3EAI_useWeaponChance2) isEqualTo 4) then {diag_log format ["[A3EAI] Error found in variable A3EAI_useWeaponChance2, resetting to default value."]; A3EAI_useWeaponChance2 = [0.00,0.80,0.10,0.10]}; + if !((count A3EAI_useWeaponChance3) isEqualTo 4) then {diag_log format ["[A3EAI] Error found in variable A3EAI_useWeaponChance3, resetting to default value."]; A3EAI_useWeaponChance3 = [0.00,0.70,0.15,0.15]}; + if ("air_reinforce" in A3EAI_airReinforcementAllowedFor) then {A3EAI_airReinforcementAllowedFor = A3EAI_airReinforcementAllowedFor - ["air_reinforce"]}; + if ("uav" in A3EAI_airReinforcementAllowedFor) then {A3EAI_airReinforcementAllowedFor = A3EAI_airReinforcementAllowedFor - ["uav"]}; + if ("ugv" in A3EAI_airReinforcementAllowedFor) then {A3EAI_airReinforcementAllowedFor = A3EAI_airReinforcementAllowedFor - ["ugv"]}; +}; + +diag_log format ["[A3EAI] Loaded all A3EAI settings in %1 seconds.",(diag_tickTime - _startTime)]; + +true diff --git a/Server/@A3EAI/addons/a3eai/init/loadSettingsHC.sqf b/Server/@A3EAI/addons/a3eai/init/loadSettingsHC.sqf new file mode 100644 index 0000000..b9454f9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/loadSettingsHC.sqf @@ -0,0 +1,15 @@ +_startTime = diag_tickTime; + +if (A3EAI_HC_serverResponse isEqualTo []) exitWith {}; + +{ + _variableName = _x select 0; + _variableValue = _x select 1; + + missionNamespace setVariable [format ["A3EAI_%1",_variableName],_variableValue]; + diag_log format ["Debug: %1:%2",_variableName,_variableValue]; +} forEach A3EAI_HC_serverResponse; + +diag_log format ["[A3EAI] Loaded all A3EAI settings in %1 seconds.",(diag_tickTime - _startTime)]; + +true diff --git a/Server/@A3EAI/addons/a3eai/init/variables.sqf b/Server/@A3EAI/addons/a3eai/init/variables.sqf new file mode 100644 index 0000000..575862b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/variables.sqf @@ -0,0 +1,48 @@ +//Set internal-use variables +A3EAI_unitLevels = [0,1,2,3]; +A3EAI_unitLevelsAll = A3EAI_unitLevels; +A3EAI_curHeliPatrols = 0; //Current number of active air patrols +A3EAI_curLandPatrols = 0; //Current number of active land patrols +A3EAI_curUAVPatrols = 0; +A3EAI_curUGVPatrols = 0; +A3EAI_dynTriggerArray = []; //List of all generated dynamic triggers. +A3EAI_staticTriggerArray = []; //List of all static triggers +A3EAI_respawnQueue = []; //Queue of AI groups that require respawning. Group ID is removed from queue after it is respawned. +A3EAI_areaBlacklists = []; //Queue of temporary dynamic spawn area blacklists for deletion +A3EAI_checkedClassnames = [[],[],[]]; //Classnames verified - Weapons/Magazines/Vehicles +A3EAI_invalidClassnames = [[],[],[]]; //Classnames known as invalid - Weapons/Magazines/Vehicles +A3EAI_monitoredObjects = []; //used to cleanup AI vehicles that may not be destroyed. +A3EAI_activeGroups = []; +A3EAI_locations = []; +A3EAI_locationsAir = []; +A3EAI_locationsLand = []; +A3EAI_heliTypesUsable = []; +A3EAI_vehTypesUsable = []; +A3EAI_UAVTypesUsable = []; +A3EAI_UGVTypesUsable = []; +A3EAI_randTriggerArray = []; +A3EAI_mapMarkerArray = []; +A3EAI_weaponTypeIndices0 = []; +A3EAI_weaponTypeIndices1 = []; +A3EAI_weaponTypeIndices2 = []; +A3EAI_weaponTypeIndices3 = []; +A3EAI_failedDynamicSpawns = []; +A3EAI_HCObject = objNull; +A3EAI_HCIsConnected = false; +A3EAI_HCObjectOwnerID = 0; +A3EAI_activeGroupAmount = 0; +A3EAI_staticInfantrySpawnQueue = []; +A3EAI_customBlacklistQueue = []; +A3EAI_customInfantrySpawnQueue = []; +A3EAI_createCustomSpawnQueue = []; +A3EAI_customVehicleSpawnQueue = []; +A3EAI_randomInfantrySpawnQueue = []; +A3EAI_activeReinforcements = []; +A3EAI_reinforcedPositions = []; +A3EAI_spawnChanceMultiplier = 1; +A3EAI_HCAllowedTypes = ["static", "dynamic", "random", "air", "land", "staticcustom", "aircustom", "landcustom", "vehiclecrew", "air_reinforce", "uav", "ugv"]; +A3EAI_lastGroupTransfer = 0; +A3EAI_automatedUnitTypes = ["uav","ugv"]; +A3EAI_kryptoAreas = []; +A3EAI_kryptoObjects = []; +A3EAI_noAggroAreas = []; diff --git a/Server/@A3EAI/addons/a3eai/init/variables_precalculated.sqf b/Server/@A3EAI/addons/a3eai/init/variables_precalculated.sqf new file mode 100644 index 0000000..3dab583 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/init/variables_precalculated.sqf @@ -0,0 +1,6 @@ +//Set internal-use variables +A3EAI_respawnTimeVariance = (abs (A3EAI_respawnTimeMax - A3EAI_respawnTimeMin)); +A3EAI_respawnTimeVarAir = (abs (A3EAI_respawnAirMaxTime - A3EAI_respawnAirMinTime)); +A3EAI_respawnTimeVarLand = (abs (A3EAI_respawnLandMaxTime - A3EAI_respawnLandMinTime)); +A3EAI_respawnTimeVarUAV = (abs (A3EAI_respawnUAVMaxTime - A3EAI_respawnUAVMinTime)); +A3EAI_respawnTimeVarUGV = (abs (A3EAI_respawnUGVMaxTime - A3EAI_respawnUGVMinTime)); diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildBackpackList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildBackpackList.sqf new file mode 100644 index 0000000..b587310 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildBackpackList.sqf @@ -0,0 +1,55 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_backpackList", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_item", "_itemList", "_itemInfo", "_itemBias", "_itemType","_itemClass"]; + +_startTime = diag_tickTime; + +_backpackList = [configFile >> "CfgLootTable" >> "Backpack","items",[]] call BIS_fnc_returnConfigEntry; +_items = []; +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "backpack") exitWith { + _item = _itemClassInfo select 0; + _items pushBack _item; + }; + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemClass = _itemClassInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "backpack") then { + _item = _itemInfo select 0; + _items pushBack _item; + }; + } forEach _itemList; + }; + }; +} forEach _backpackList; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicBackpackBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicBackpackBlacklist; + }; + A3EAI_backpackTypes0 = _items; + A3EAI_backpackTypes1 = +_items; + A3EAI_backpackTypes2 = +_items; + A3EAI_backpackTypes3 = +_items; + if (A3EAI_debugLevel > 0) then { + diag_log format ["A3EAI Debug: Generated %1 backpack classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_backpackTypes0: %1",A3EAI_backpackTypes0]; + diag_log format ["A3EAI Debug: Contents of A3EAI_backpackTypes1: %1",A3EAI_backpackTypes1]; + diag_log format ["A3EAI Debug: Contents of A3EAI_backpackTypes2: %1",A3EAI_backpackTypes2]; + diag_log format ["A3EAI Debug: Contents of A3EAI_backpackTypes3: %1",A3EAI_backpackTypes3]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate backpack classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +A3EAI_dynamicBackpackBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildFoodList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildFoodList.sqf new file mode 100644 index 0000000..628b5db --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildFoodList.sqf @@ -0,0 +1,53 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_foodList", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_itemList", "_itemInfo", "_itemBias", "_itemType", "_item","_itemClass"]; + +_startTime = diag_tickTime; + +_foodList = [configFile >> "CfgLootTable" >> "Food","items",[]] call BIS_fnc_returnConfigEntry; +_items = []; +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemClass = _itemClassInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "magazine") then { + _item = _itemInfo select 0; + if (([configFile >> "CfgMagazines" >> _item,"interactText",""] call BIS_fnc_returnConfigEntry) in ["EAT","DRINK","CONSUME"]) then { + _items pushBack _item; + }; + }; + } forEach _itemList; + }; + if (_itemClassType isEqualTo "magazine") exitWith { + _item = _itemClassInfo select 0; + if (([configFile >> "CfgMagazines" >> _item,"interactText",""] call BIS_fnc_returnConfigEntry) in ["EAT","DRINK"]) then { + _items pushBack _item; + }; + }; + }; +} forEach _foodList; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicFoodBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicFoodBlacklist; + }; + A3EAI_foodLoot = _items; + if (A3EAI_debugLevel > 0) then { + diag_log format ["A3EAI Debug: Generated %1 food classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_foodLoot: %1",A3EAI_foodLoot]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate food classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +A3EAI_dynamicFoodBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildHeadgearList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildHeadgearList.sqf new file mode 100644 index 0000000..93ae479 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildHeadgearList.sqf @@ -0,0 +1,56 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_itemList", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_itemInfo", "_itemBias", "_itemType", "_item","_itemClass", "_headgearList"]; + +_startTime = diag_tickTime; + +_headgearList = [configFile >> "CfgLootTable" >> "Headgear","items",[]] call BIS_fnc_returnConfigEntry; +_items = []; + +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemClass = _itemClassInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "item") then { + _item = _itemInfo select 0; + _items pushBack _item; + }; + } forEach _itemList; + }; + if (_itemClassType isEqualTo "item") exitWith { + _item = _itemInfo select 0; + _items pushBack _item; + }; + }; +} forEach _headgearList; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicHeadgearBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicHeadgearBlacklist; + }; + A3EAI_headgearTypes0 = _items; + A3EAI_headgearTypes1 = +_items; + A3EAI_headgearTypes2 = +_items; + A3EAI_headgearTypes3 = +_items; + if (A3EAI_debugLevel > 0) then { + diag_log format ["A3EAI Debug: Generated %1 headgear classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_headgearTypes0: %1",A3EAI_headgearTypes0]; + diag_log format ["A3EAI Debug: Contents of A3EAI_headgearTypes1: %1",A3EAI_headgearTypes1]; + diag_log format ["A3EAI Debug: Contents of A3EAI_headgearTypes2: %1",A3EAI_headgearTypes2]; + diag_log format ["A3EAI Debug: Contents of A3EAI_headgearTypes3: %1",A3EAI_headgearTypes3]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate headgear classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +A3EAI_dynamicHeadgearBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildLootLargeList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildLootLargeList.sqf new file mode 100644 index 0000000..2f82798 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildLootLargeList.sqf @@ -0,0 +1,52 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_lootListLarge1", "_lootListLarge2", "_lootListLarge", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_item", "_itemSubClass", "_itemList", "_itemInfo", "_itemBias", "_itemType"]; + +_startTime = diag_tickTime; + +_lootListLarge1 = [configFile >> "CfgLootTable" >> "GenericLarge","items",[]] call BIS_fnc_returnConfigEntry; +_lootListLarge2 = [configFile >> "CfgLootTable" >> "GenericAuto","items",[]] call BIS_fnc_returnConfigEntry; +_lootListLarge = _lootListLarge1 + _lootListLarge2; + +_items = []; +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "magazine") exitWith { + _item = _itemClassInfo select 0; + _items pushBack _item; + }; + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemSubClass = _itemClassInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemSubClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "magazine") then { + _item = _itemInfo select 0; + _items pushBack _item; + }; + } forEach _itemList; + }; + }; +} forEach _lootListLarge; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicLootBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicLootBlacklist; + }; + A3EAI_MiscLoot2 = _items; + if (A3EAI_debugLevel > 0) then { + diag_log format ["A3EAI Debug: Generated %1 generic loot (large) classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_MiscLoot2: %1",A3EAI_MiscLoot2]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate loot (large) classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +A3EAI_dynamicLootBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildLootList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildLootList.sqf new file mode 100644 index 0000000..2b3ce46 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildLootList.sqf @@ -0,0 +1,51 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_lootList1", "_lootList2", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_item", "_itemSubClass", "_itemSubClasses", "_itemInfo", "_itemBias", "_itemType"]; + +_startTime = diag_tickTime; + +_lootList1 = [configFile >> "CfgLootTable" >> "Generic","items",[]] call BIS_fnc_returnConfigEntry; +_lootList2 = [configFile >> "CfgLootTable" >> "GenericBed","items",[]] call BIS_fnc_returnConfigEntry; +_lootList1 append _lootList2; + +_items = []; +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "magazine") exitWith { + _item = _itemClassInfo select 0; + _items pushBack _item; + }; + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemSubClass = _itemClassInfo select 0; + _itemSubClasses = [configFile >> "CfgLootTable" >> _itemSubClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "magazine") then { + _item = _itemInfo select 0; + _items pushBack _item; + }; + } forEach _itemSubClasses; + }; + }; +} forEach _lootList1; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicLootBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicLootBlacklist; + }; + A3EAI_MiscLoot1 = _items; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Generated %1 generic loot classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_MiscLoot1: %1",A3EAI_MiscLoot1]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate loot classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +//A3EAI_dynamicLootBlacklist is nil'ed in buildLootLargeList \ No newline at end of file diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildScopeList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildScopeList.sqf new file mode 100644 index 0000000..9d4fc11 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildScopeList.sqf @@ -0,0 +1,52 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_scopeList", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_itemList", "_itemInfo", "_itemBias", "_itemType", "_item","_itemClass"]; + +_startTime = diag_tickTime; + +_scopeList = [configFile >> "CfgLootTable" >> "Scopes","items",[]] call BIS_fnc_returnConfigEntry; +_items = []; +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemClass = _itemClassInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "item") then { + _item = _itemInfo select 0; + if !(isClass (configfile >> "CfgWeapons" >> _item >> "ItemInfo" >> "OpticsModes" >> "TWS")) then { + _items pushBack _item; + }; + }; + } forEach _itemList; + }; + if (_itemClassType isEqualTo "item") exitWith { + _item = _itemClassInfo select 0; + if !(isClass (configfile >> "CfgWeapons" >> _item >> "ItemInfo" >> "OpticsModes" >> "TWS")) then { + _items pushBack _item; + }; + }; + }; +} forEach _scopeList; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicOpticsBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicOpticsBlacklist; + }; + A3EAI_weaponOpticsList = _items; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Generated %1 weapon optics classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_weaponOpticsList: %1",A3EAI_weaponOpticsList]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate weapon optics classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +A3EAI_dynamicOpticsBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildUniformList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildUniformList.sqf new file mode 100644 index 0000000..280b343 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildUniformList.sqf @@ -0,0 +1,64 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_uniformTypes", "_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_itemClass", "_itemClasses", "_itemInfo", "_itemBias", "_itemType", "_item", "_itemSubClass", "_itemList", "_itemSubInfo", "_itemSubBias", "_itemSubType"]; + +_startTime = diag_tickTime; + +_uniformTypes = [configFile >> "CfgLootTable" >> "Uniforms","items",[""]] call BIS_fnc_returnConfigEntry; +_items = []; +{ + _itemClassInfo = _x select 0; + _itemClassBias = _x select 1; + _itemClassType = _itemClassInfo select 1; + if (_itemClassType isEqualTo "CfgLootTable") then { + _itemClass = _itemClassInfo select 0; + _itemClasses = [configFile >> "CfgLootTable" >> _itemClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + call { + if (_itemType isEqualTo "item") exitWith { + _item = _itemInfo select 0; + _items pushBack _item; + }; + if (_itemType isEqualTo "CfgLootTable") exitWith { + _itemSubClass = _itemInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemSubClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemSubInfo = _x select 0; + _itemSubBias = _x select 1; + _itemSubType = _itemSubInfo select 1; + if (_itemSubType isEqualTo "item") then { + _item = _itemSubInfo select 0; + _items pushBack _item; + }; + } forEach _itemList; + }; + }; + } forEach _itemClasses; + }; +} forEach _uniformTypes; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicUniformBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicUniformBlacklist; + }; + A3EAI_uniformTypes0 = _items; + A3EAI_uniformTypes1 = +_items; + A3EAI_uniformTypes2 = +_items; + A3EAI_uniformTypes3 = +_items; + if (A3EAI_debugLevel > 0) then { + diag_log format ["A3EAI Debug: Generated %1 uniform classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_uniformTypes0: %1",A3EAI_uniformTypes0]; + diag_log format ["A3EAI Debug: Contents of A3EAI_uniformTypes1: %1",A3EAI_uniformTypes1]; + diag_log format ["A3EAI Debug: Contents of A3EAI_uniformTypes2: %1",A3EAI_uniformTypes2]; + diag_log format ["A3EAI Debug: Contents of A3EAI_uniformTypes3: %1",A3EAI_uniformTypes3]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate uniform classname list. Classnames from A3EAI_config.sqf used instead."; +}; + +A3EAI_dynamicUniformBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildVestList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildVestList.sqf new file mode 100644 index 0000000..fe066e0 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildVestList.sqf @@ -0,0 +1,55 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_vestList", "_items", "_itemInfo", "_itemBias", "_itemType", "_item"]; + +_startTime = diag_tickTime; + +_vestList = [configFile >> "CfgLootTable" >> "Vests","items",[]] call BIS_fnc_returnConfigEntry; +_items = []; +{ + _itemInfo = _x select 0; + _itemBias = _x select 1; + _itemType = _itemInfo select 1; + call { + if (_itemType isEqualTo "item") exitWith { + _item = _itemInfo select 0; + _items pushBack _item; + }; + if (_itemType isEqualTo "CfgLootTable") exitWith { + _itemSubClass = _itemInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemSubClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemSubInfo = _x select 0; + _itemSubBias = _x select 1; + _itemSubType = _itemSubInfo select 1; + if (_itemSubType isEqualTo "item") then { + _item = _itemSubInfo select 0; + _items pushBack _item; + }; + } forEach _itemList; + }; + }; +} forEach _vestList; + +if !(_items isEqualTo []) then { + if !(A3EAI_dynamicVestBlacklist isEqualTo []) then { + _items = _items - A3EAI_dynamicVestBlacklist; + }; + A3EAI_vestTypes0 = _items; + A3EAI_vestTypes1 = +_items; + A3EAI_vestTypes2 = +_items; + A3EAI_vestTypes3 = +_items; + if (A3EAI_debugLevel > 0) then { + diag_log format ["A3EAI Debug: Generated %1 vest classnames in %2 seconds.",(count _items),diag_tickTime - _startTime]; + if (A3EAI_debugLevel > 1) then { + diag_log format ["A3EAI Debug: Contents of A3EAI_vestTypes0: %1",A3EAI_vestTypes0]; + diag_log format ["A3EAI Debug: Contents of A3EAI_vestTypes1: %1",A3EAI_vestTypes1]; + diag_log format ["A3EAI Debug: Contents of A3EAI_vestTypes2: %1",A3EAI_vestTypes2]; + diag_log format ["A3EAI Debug: Contents of A3EAI_vestTypes3: %1",A3EAI_vestTypes3]; + }; + }; +} else { + diag_log "A3EAI Error: Could not dynamically generate vest classname list. Classnames from A3EAI_config.sqf used instead."; +}; +A3EAI_dynamicVestBlacklist = nil; + diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildWeaponList.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildWeaponList.sqf new file mode 100644 index 0000000..f825e06 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_buildWeaponList.sqf @@ -0,0 +1,87 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_startTime", "_checkWeapon", "_magazineTypes", "_cursorAim", "_ammo", "_ammoHit", "_buildWeaponList", "_pistolList", "_rifleList", "_machinegunList", "_sniperList"]; + +_startTime = diag_tickTime; + +if (isNil "A3EAI_dynamicWeaponBlacklist") then {A3EAI_dynamicWeaponBlacklist = [];}; + +_checkWeapon = +{ + private ["_magazineTypes","_ammo","_ammoMaxRange","_ammoHit"]; + if ((typeName _this) != "STRING") exitWith {false}; + if (_this in A3EAI_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}; + true +}; + +_buildWeaponList = { + private ["_items", "_itemClassInfo", "_itemClassBias", "_itemClassType", "_weapon", "_itemClass", "_itemList"]; + _items = []; + + { + _itemClassInfo = (_x select 0); + _itemClassBias = (_x select 1); + _itemClassType = _itemClassInfo select 1; + call { + if (_itemClassType isEqualTo "weapon") exitWith { + _item = _itemClassInfo select 0; + if (_item call _checkWeapon) then { + _items pushBack _item; + }; + }; + if (_itemClassType isEqualTo "CfgLootTable") exitWith { + _itemClass = _itemClassInfo select 0; + _itemList = [configFile >> "CfgLootTable" >> _itemClass,"items",[]] call BIS_fnc_returnConfigEntry; + { + _itemInfo = (_x select 0); + _itemBias = (_x select 1); + _itemType = _itemInfo select 1; + if (_itemType isEqualTo "weapon") then { + _item = _itemInfo select 0; + if (_item call _checkWeapon) then { + _items pushBack _item; + }; + }; + } forEach _itemList; + }; + }; + } forEach _this; + + _items +}; + +_pistolList = [configFile >> "CfgLootTable" >> "Pistols","items",[]] call BIS_fnc_returnConfigEntry; +_rifleList = [configFile >> "CfgLootTable" >> "Rifle","items",[]] call BIS_fnc_returnConfigEntry; +_machinegunList = [configFile >> "CfgLootTable" >> "Machinegun","items",[]] call BIS_fnc_returnConfigEntry; +_sniperList = [configFile >> "CfgLootTable" >> "SniperRifle","items",[]] call BIS_fnc_returnConfigEntry; + +_pistolList = _pistolList call _buildWeaponList; +_rifleList = _rifleList call _buildWeaponList; +_machinegunList = _machinegunList call _buildWeaponList; +_sniperList = _sniperList call _buildWeaponList; + +if !(_pistolList isEqualTo []) then {A3EAI_pistolList = _pistolList} else {diag_log "A3EAI Error: Could not dynamically generate Pistol weapon classname list. Classnames from A3EAI_config.sqf used instead."}; +if !(_rifleList isEqualTo []) then {A3EAI_rifleList = _rifleList} else {diag_log "A3EAI Error: Could not dynamically generate Rifle weapon classname list. Classnames from A3EAI_config.sqf used instead."}; +if !(_machinegunList isEqualTo []) then {A3EAI_machinegunList = _machinegunList} else {diag_log "A3EAI Error: Could not dynamically Machinegun weapon classname list. Classnames from A3EAI_config.sqf used instead."}; +if !(_sniperList isEqualTo []) then {A3EAI_sniperList = _sniperList} else {diag_log "A3EAI Error: Could not dynamically generate Sniper weapon classname list. Classnames from A3EAI_config.sqf used instead."}; + +if (A3EAI_debugLevel > 0) then { + if (A3EAI_debugLevel > 1) then { + //Display finished weapon arrays + diag_log format ["Contents of A3EAI_pistolList: %1",A3EAI_pistolList]; + diag_log format ["Contents of A3EAI_rifleList: %1",A3EAI_rifleList]; + diag_log format ["Contents of A3EAI_machinegunList: %1",A3EAI_machinegunList]; + diag_log format ["Contents of A3EAI_sniperList: %1",A3EAI_sniperList]; + }; + diag_log format ["A3EAI Debug: Weapon classname tables created in %1 seconds.",(diag_tickTime - _startTime)]; +}; + +A3EAI_dynamicWeaponBlacklist = nil; diff --git a/Server/@A3EAI/addons/a3eai/scripts/A3EAI_serverMonitor.sqf b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_serverMonitor.sqf new file mode 100644 index 0000000..92c2f5f --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/A3EAI_serverMonitor.sqf @@ -0,0 +1,291 @@ +#include "\A3EAI\globaldefines.hpp" + +if (A3EAI_debugLevel > 0) then {diag_log "A3EAI 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 = (A3EAI_playerCountThreshold max 1); + +if (A3EAI_upwardsChanceScaling) then { + _multiplierLowPlayers = A3EAI_chanceScalingThreshold; + _multiplierHighPlayers = 1; +} else { + _multiplierLowPlayers = 1; + _multiplierHighPlayers = A3EAI_chanceScalingThreshold; +}; + +//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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Purging a %1 from A3EAI_monitoredObjects.",(typeOf _this)];}; + {_this removeAllEventHandlers _x} count ["Killed","HandleDamage","GetIn","GetOut","Fired","Local","Hit"]; + _index = A3EAI_monitoredObjects find _this; + if (_index > -1) then {A3EAI_monitoredObjects 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; + 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 "A3EAI_deathTime"; + if (!isNil "_deathTime") then { + if ((_currentTime - _deathTime) > A3EAI_cleanupDelay) then { + if (({isPlayer _x} count (_x nearEntities [[PLAYER_UNITS,"Air","LandVehicle"],30])) isEqualTo 0) then { + _x call _fnc_purgeAndDelete; + }; + }; + } else { + _x setVariable ["A3EAI_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 "A3EAI_deathTime"; + if (!isNil "_deathTime") then { + if ((_currentTime - _deathTime) > MONITOR_VEHICLECLEANUP_FREQ) 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 ["A3EAI_deathTime",_currentTime]; + }; + }; + }; + }; + uiSleep 0.025; + } count A3EAI_monitoredObjects; + + { + if (!isNull _x) then { + private ["_kryptoGenTime"]; + _kryptoGenTime = _x getVariable "A3EAI_kryptoGenTime"; + if (!isNil "_kryptoGenTime") then { + if ((_currentTime - _kryptoGenTime) > KRYPTO_CLEANUP_FREQ) then { + _kryptoArea = _x getVariable ["A3EAI_kryptoArea",objNull]; + if !(isNull _kryptoArea) then { + _index = A3EAI_kryptoAreas find _kryptoArea; + if (_index > -1) then {A3EAI_kryptoAreas deleteAt _index;}; + deleteVehicle _kryptoArea; + }; + deleteVehicle _x; + A3EAI_kryptoObjects deleteAt _forEachIndex; + }; + }; + }; + uiSleep 0.025; + } forEach A3EAI_kryptoObjects; + + { + if (!isNull _x) then { + private ["_kryptoObject"]; + _kryptoObject = _x getVariable "A3EAI_kryptoObject"; + if (!isNil "_kryptoObject") then { + call { + private ["_kryptoGenTime","_arrowObject"]; + if (isNull _kryptoObject) exitWith { + deleteVehicle (_x getVariable ["A3EAI_arrowObject",objNull]); + deleteVehicle _x; + A3EAI_kryptoAreas deleteAt _forEachIndex; + }; + if ((_currentTime - (_x getVariable ["A3EAI_kryptoGenTime",0])) > A3EAI_kryptoPickupAssist) exitWith { + deleteVehicle (_x getVariable ["A3EAI_arrowObject",objNull]); + deleteVehicle _x; + A3EAI_kryptoAreas deleteAt _forEachIndex; + }; + }; + } else { + A3EAI_kryptoAreas deleteAt _forEachIndex; + }; + }; + uiSleep 0.025; + } forEach A3EAI_kryptoAreas; + }; + } else { + if (_canKillCleanupMain) then { + terminate _cleanupMain; + diag_log "A3EAI terminated previous cleanupMain thread."; + } else { + _canKillCleanupMain = true; + diag_log "A3EAI 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; + A3EAI_areaBlacklists = A3EAI_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; + A3EAI_areaBlacklists deleteAt _forEachIndex; + }; + uiSleep 0.025; + } forEach A3EAI_areaBlacklists; + }; + } else { + if (_canKillCleanupLocations) then { + terminate _cleanupLocations; + diag_log "A3EAI terminated previous cleanupLocations thread."; + } else { + _canKillCleanupLocations = true; + diag_log "A3EAI 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; + A3EAI_randTriggerArray = A3EAI_randTriggerArray - [objNull]; + { + if ((((triggerStatements _x) select 1) != "") && {(_currentTime - (_x getVariable ["timestamp",_currentTime])) > RANDSPAWN_EXPIRY_TIME}) then { + _triggerLocation = _x getVariable ["triggerLocation",locationNull]; + deleteLocation _triggerLocation; + if (A3EAI_enableDebugMarkers) then {deleteMarker (str _x)}; + deleteVehicle _x; + A3EAI_randTriggerArray deleteAt _forEachIndex; + }; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; + } forEach A3EAI_randTriggerArray; + _spawnsAvailable = A3EAI_maxRandomSpawns - (count A3EAI_randTriggerArray); + if (_spawnsAvailable > 0) then { + _nul = _spawnsAvailable spawn A3EAI_setup_randomspawns; + }; + }; + } else { + if (_canKillRandomSpawns) then { + terminate _cleanupRandomSpawns; + diag_log "A3EAI terminated previous cleanupRandomSpawns thread."; + } else { + _canKillRandomSpawns = true; + diag_log "A3EAI marked current cleanupRandomSpawns thread for termination."; + }; + }; + _checkRandomSpawns = _currentTime; + }; + + if ((_currentTime - _playerCountTime) > MONITOR_UPDATE_PLAYER_COUNT_FREQ) then { + _currentPlayerCount = ({alive _x} count allPlayers); + if (A3EAI_HCIsConnected) then {_currentPlayerCount = _currentPlayerCount - 1}; + if !(_lastPlayerCount isEqualTo _currentPlayerCount) then { + A3EAI_spawnChanceMultiplier = linearConversion [1, _maxSpawnChancePlayers, _currentPlayerCount, _multiplierLowPlayers, _multiplierHighPlayers, true]; + _lastPlayerCount = _currentPlayerCount; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Updated A3EAI_spawnChanceMultiplier to %1",A3EAI_spawnChanceMultiplier];}; + }; + _playerCountTime = _currentTime; + }; + + //Check for unwanted side modifications + if ((_currentTime - _sideCheck) > SIDECHECK_TIME) then { + if !((AI_GROUP_SIDE getFriend PLAYER_GROUP_SIDE1) isEqualTo 0) then {AI_GROUP_SIDE setFriend [PLAYER_GROUP_SIDE1, 0]}; + if !((AI_GROUP_SIDE getFriend PLAYER_GROUP_SIDE2) isEqualTo 0) then {AI_GROUP_SIDE setFriend [PLAYER_GROUP_SIDE2, 0]}; + if !((PLAYER_GROUP_SIDE2 getFriend AI_GROUP_SIDE) isEqualTo 0) then {PLAYER_GROUP_SIDE2 setFriend [AI_GROUP_SIDE, 0]}; + if !((PLAYER_GROUP_SIDE1 getFriend AI_GROUP_SIDE) isEqualTo 0) then {PLAYER_GROUP_SIDE1 setFriend [AI_GROUP_SIDE, 0]}; + if !((AI_GROUP_SIDE getFriend AI_GROUP_SIDE) isEqualTo 1) then {AI_GROUP_SIDE setFriend [AI_GROUP_SIDE, 1]}; + _sideCheck = _currentTime; + }; + + if (A3EAI_enableDebugMarkers) then { + { + if (_x in allMapMarkers) then { + _x setMarkerPos (getMarkerPos _x); + } else { + A3EAI_mapMarkerArray set [_forEachIndex,""]; + }; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; + } forEach A3EAI_mapMarkerArray; + A3EAI_mapMarkerArray = A3EAI_mapMarkerArray - [""]; + }; + + A3EAI_activeGroups = A3EAI_activeGroups - [grpNull]; + _activeGroupAmount = format ["%1/%2",{(_x getVariable ["GroupSize",0]) > 0} count A3EAI_activeGroups,count A3EAI_activeGroups]; + + //Report statistics to RPT log + if ((A3EAI_monitorReportRate > 0) && {((_currentTime - _monitorReport) > A3EAI_monitorReportRate)}) then { + _uptime = [] call _getUptime; + diag_log format ["[A3EAI Monitor] [Uptime:%1:%2:%3][FPS:%4][Groups:%5][Respawn:%6][HC:%7]",_uptime select 0, _uptime select 1, _uptime select 2,round diag_fps,_activeGroupAmount,(count A3EAI_respawnQueue),A3EAI_HCIsConnected]; + diag_log format ["[A3EAI Monitor] [Static:%1][Dynamic:%2][Random:%3][Air:%4][Land:%5][UAV:%6][UGV:%7]",(count A3EAI_staticTriggerArray),(count A3EAI_dynTriggerArray),(count A3EAI_randTriggerArray),A3EAI_curHeliPatrols,A3EAI_curLandPatrols,A3EAI_curUAVPatrols,A3EAI_curUGVPatrols]; + _monitorReport = _currentTime; + }; + + uiSleep 30; +}; diff --git a/Server/@A3EAI/addons/a3eai/scripts/allowDamage_fix.sqf b/Server/@A3EAI/addons/a3eai/scripts/allowDamage_fix.sqf new file mode 100644 index 0000000..0fc2445 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/allowDamage_fix.sqf @@ -0,0 +1,21 @@ +#include "\A3EAI\globaldefines.hpp" + +if (!isNil "A3EAI_allowDamageFix_active") exitWith {}; +A3EAI_allowDamageFix_active = true; + +uiSleep 5; + +while {true} do { + private ["_playersModified"]; + _playersModified = { + if ((_x getVariable ["noDamageAllowed",true]) && {!(_x isKindOf "VirtualMan_EPOCH")}) then { + if (isPlayer _x) then { + _x allowDamage true; + _x setVariable ["noDamageAllowed",false]; + true + }; + }; + } count playableUnits; + if (_playersModified > 0) then {diag_log format ["DEBUG :: Applied allowDamage:true to %1 players.",_playersModified]}; + uiSleep 10; +}; diff --git a/Server/@A3EAI/addons/a3eai/scripts/buildWeightedTables.sqf b/Server/@A3EAI/addons/a3eai/scripts/buildWeightedTables.sqf new file mode 100644 index 0000000..0389a26 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/buildWeightedTables.sqf @@ -0,0 +1,63 @@ +#include "\A3EAI\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 = [A3EAI_unitLevels,_gradeChances] call _getWeightedIndices; + missionNamespace setVariable [_x select 1,_weightedTable]; + missionNamespace setVariable [_x select 0,nil]; + }; +} count _this; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: A3EAI finished building weighted unitLevel tables in %1 seconds.",(diag_tickTime - _startTime)]}; + +true + diff --git a/Server/@A3EAI/addons/a3eai/scripts/dynamicSpawn_manager.sqf b/Server/@A3EAI/addons/a3eai/scripts/dynamicSpawn_manager.sqf new file mode 100644 index 0000000..0932267 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/dynamicSpawn_manager.sqf @@ -0,0 +1,140 @@ +#include "\A3EAI\globaldefines.hpp" + +if (A3EAI_debugLevel > 0) then {diag_log "Starting A3EAI Dynamic Spawn Manager in 1 minute.";}; +uiSleep 60; +//uiSleep 30; //FOR DEBUGGING +if (A3EAI_debugLevel > 0) then {diag_log "A3EAI 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 { + if (({alive _x} count allPlayers) > 0) then { + _allPlayers = []; //Do not edit + _currentTime = diag_tickTime; + { + if ((isPlayer _x) && {((typeOf _x) in [PLAYER_UNITS])}) then { + _playerUID = getPlayerUID _x; + if !((_playerUID select [0,2]) isEqualTo "HC") then { + _playerIndex = _playerUID_DB find _playerUID; + if (_playerIndex > -1) then { + _lastSpawned = _lastSpawned_DB select _playerIndex; + _timePassed = (_currentTime - _lastSpawned); + if (_timePassed > A3EAI_timePerDynamicSpawn) then { + if ((_currentTime - (_lastOnline_DB select _playerIndex)) < A3EAI_purgeLastDynamicSpawnTime) then { + _allPlayers pushBack _x; + //diag_log format ["DEBUG: Player %1 added to current cycle dynamic spawn list.",_x]; + }; + _lastOnline_DB set [_playerIndex,_currentTime]; + } else { + if (_playerUID in A3EAI_failedDynamicSpawns) then { + _allPlayers pushBack _x; + //diag_log format ["DEBUG: Player %1 added to current cycle dynamic spawn list.",_x]; + A3EAI_failedDynamicSpawns = A3EAI_failedDynamicSpawns - [_playerUID]; + }; + }; + } else { + _playerUID_DB pushBack _playerUID; + _lastSpawned_DB pushBack _currentTime - DYNSPAWNMGR_SLEEP_DELAY; + _lastOnline_DB pushBack _currentTime; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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; + } forEach allPlayers; + + _activeDynamicSpawns = (count A3EAI_dynTriggerArray); + _playerCount = (count _allPlayers); + _maxSpawnsPossible = (_playerCount min A3EAI_maxDynamicSpawns); //Can't have more spawns than players (doesn't count current number of dynamic spawns) + + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 A3EAI_selectRandom; + _playerUID = (getPlayerUID _player); + if (alive _player) then { + _playername = name _player; + _index = _playerUID_DB find _playerUID; + _playerPos = getPosATL _player; + _spawnParams = _playerPos call A3EAI_getSpawnParams; + _spawnChance = _spawnParams select 3; + _chanceModifier = 1.00; + call { + if (_spawnChance isEqualTo 0) exitWith {}; + if ((vehicle _player) isKindOf "Air") exitWith { + _spawnChance = 0; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI 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 (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Player %1 is unarmed. Probability modifier set to %2.",_player,_chanceModifier];}; + }; + if ((damage _player) > DYNAMIC_WEAK_PLAYER_HEALTH) then { + _chanceModifier = _chanceModifier + DYNAMIC_CHANCE_ADJUST_WEAKENED; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Player %1 is weakened. Probability modifier set to %2.",_player,_chanceModifier];}; + }; + if !((_playerPos nearObjects [LOOT_HOLDER_CLASS,DYNAMIC_LOOTING_DISTANCE]) isEqualTo []) then { + _chanceModifier = _chanceModifier + DYNAMIC_CHANCE_ADJUST_LOOTING; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Player %1 is looting. Probability modifier set to %2.",_player,_chanceModifier];}; + }; + if (_chanceModifier < 0) then {_chanceModifier = 0;}; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Dynamic spawn probabilities for %1: Base: %2, Modifier: %3, A3EAI_spawnChanceMultiplier: %4",_player,_spawnChance,_chanceModifier,A3EAI_spawnChanceMultiplier];}; + _spawnChance = (_spawnChance * _chanceModifier * A3EAI_spawnChanceMultiplier); + }; + if (_spawnChance call A3EAI_chance) then { + _lastSpawned_DB set [_index,diag_tickTime]; + _trigger = createTrigger [TRIGGER_OBJECT,_playerPos,false]; + _location = [_playerPos,TEMP_BLACKLIST_AREA_DYNAMIC_SIZE] call A3EAI_createBlackListAreaDynamic; + _trigger setVariable ["triggerLocation",_location]; + _trigger setTriggerArea [TRIGGER_SIZE_SMALL,TRIGGER_SIZE_SMALL,0,false]; + _trigger setTriggerActivation ["ANY", "PRESENT", true]; + _trigger setTriggerTimeout [TRIGGER_TIMEOUT_DYNAMIC, true]; + _trigger setTriggerText (format ["Dynamic Spawn (Triggered by: %1)",_playername]); + _trigger setVariable ["targetplayer",_player]; + _trigger setVariable ["targetplayerUID",_playerUID]; + _trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList != 0;","", "[thisTrigger] spawn A3EAI_despawn_dynamic;"]; + if (A3EAI_enableDebugMarkers) then { + _nul = _trigger spawn { + _marker = str(_this); + if (_marker in allMapMarkers) then {deleteMarker _marker}; + _marker = createMarker[_marker,(getPosASL _this)]; + _marker setMarkerShape "ELLIPSE"; + _marker setMarkerType "Flag"; + _marker setMarkerBrush "SOLID"; + _marker setMarkerSize [600, 600]; + _marker setMarkerAlpha 0; + }; + }; + 0 = [PATROL_DIST_DYNAMIC,_trigger,_spawnParams select 0,_spawnParams select 1,_spawnParams select 2] call A3EAI_spawnUnits_dynamic; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Created dynamic trigger at %1 with params %2. Triggered by player: %3.",(mapGridPosition _trigger),_spawnParams,_playername];}; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Dynamic spawn probability check failed for player %1 (Probability: %2).",_playername,_spawnChance];}; + }; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Cancel dynamic spawn check for player %1 (Reason: Player not in suitable state).",_player]}; + }; + _allPlayers = _allPlayers - [_player]; + _activeDynamicSpawns = _activeDynamicSpawns + 1; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Processed a spawning probability check in %1 seconds.",diag_tickTime - _time]}; + uiSleep 5; + }; + } else { + if (A3EAI_debugLevel > 1) then {diag_log "A3EAI Debug: No players online. Dynamic spawn manager is entering waiting state.";}; + }; + + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Dynamic spawn manager is sleeping for %1 seconds.",DYNSPAWNMGR_SLEEP_DELAY];}; + uiSleep DYNSPAWNMGR_SLEEP_DELAY; +}; diff --git a/Server/@A3EAI/addons/a3eai/scripts/generateStaticSpawns.sqf b/Server/@A3EAI/addons/a3eai/scripts/generateStaticSpawns.sqf new file mode 100644 index 0000000..c9c92d7 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/generateStaticSpawns.sqf @@ -0,0 +1,106 @@ +#include "\A3EAI\globaldefines.hpp" + +private ["_expireTime", "_spawnsCreated", "_startTime", "_cfgWorldName"]; + +_expireTime = diag_tickTime + SERVER_START_TIMEOUT; +waitUntil {uiSleep 3; !isNil "A3EAI_locations_ready" && {(!isNil SERVER_STARTED_INDICATOR) or {diag_tickTime > _expireTime}}}; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: A3EAI is generating static spawns."];}; + +_spawnsCreated = 0; +_startTime = diag_tickTime; +_cfgWorldName = configFile >> "CfgWorlds" >> worldName >> "Names"; + +{ + private ["_placeName","_placePos","_placeType"]; + _placeName = _x select 0; + _placePos = _x select 1; + _placeType = _x select 2; + + try { + if (surfaceIsWater _placePos) then { + throw format ["A3EAI Debug: Static spawn not created at %1 due to water position.",_placeName]; + }; + + if !((_placePos nearObjects [PLOTPOLE_OBJECT,PLOTPOLE_RADIUS]) isEqualTo []) then { + throw format ["A3EAI 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; + + { + _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,NO_AGGRO_RANGE_MAN] call A3EAI_checkInNoAggroArea)}) then { + throw format ["A3EAI Debug: Static spawn not created at %1. A spawn position is within a blacklisted area.",_placeName]; + }; + _spawnPoints = _spawnPoints + 1; + } else { + _nearbldgs deleteAt _forEachIndex; + }; + } forEach _nearbldgs; + + if (_spawnPoints < 6) then { + throw format ["A3EAI Debug: Static spawn not created at %1. Acceptable positions: %2, Total: %3",_placeName,_spawnPoints,(count _nearbldgs)]; + }; + + _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; + + call { + if (_placeType isEqualTo "namecitycapital") exitWith { + _aiCount = [A3EAI_minAI_capitalCity,A3EAI_addAI_capitalCity]; + _unitLevel = A3EAI_unitLevel_capitalCity; + _spawnChance = A3EAI_spawnChance_capitalCity; + _respawnLimit = A3EAI_respawnLimit_capitalCity; + }; + if (_placeType isEqualTo "namecity") exitWith { + _aiCount = [A3EAI_minAI_city,A3EAI_addAI_city]; + _unitLevel = A3EAI_unitLevel_city; + _spawnChance = A3EAI_spawnChance_city; + _respawnLimit = A3EAI_respawnLimit_city; + }; + if (_placeType isEqualTo "namevillage") exitWith { + _aiCount = [A3EAI_minAI_village,A3EAI_addAI_village]; + _unitLevel = A3EAI_unitLevel_village; + _spawnChance = A3EAI_spawnChance_village; + _respawnLimit = A3EAI_respawnLimit_village; + }; + if (_placeType isEqualTo "namelocal") exitWith { + _aiCount = [A3EAI_minAI_remoteArea,A3EAI_addAI_remoteArea]; + _unitLevel = A3EAI_unitLevel_remoteArea; + _spawnChance = A3EAI_spawnChance_remoteArea; + _respawnLimit = A3EAI_respawnLimit_remoteArea; + }; + }; + + if ((_spawnChance <= 0) or {(_aiCount isEqualTo [0,0])}) then { + throw format ["A3EAI Debug: Static spawn not created at %1. Spawn chance zero or AI count zero.",_placeName]; + }; + + _trigger = createTrigger [TRIGGER_OBJECT, _placePos,false]; + _trigger setTriggerArea [TRIGGER_SIZE_NORMAL,TRIGGER_SIZE_NORMAL,0,false]; + _trigger setTriggerActivation ["ANY", "PRESENT", true]; + _trigger setTriggerTimeout [TRIGGER_TIMEOUT_STATIC, true]; + _trigger setTriggerText _placeName; + _statements = format ["0 = [%1,%2,%3,thisTrigger,[],%4] call A3EAI_createInfantryQueue;",_aiCount select 0,_aiCount select 1,_patrolRadius,_unitLevel]; + _trigger setTriggerStatements ["{if (isPlayer _x) exitWith {1}} count thisList > 0;", _statements, "0 = [thisTrigger] spawn A3EAI_despawn_static;"]; + _trigger setVariable ["respawnLimit",_respawnLimit]; + _trigger setVariable ["respawnLimitOriginal",_respawnLimit]; + 0 = [0,_trigger,[],_patrolRadius,_unitLevel,_nearbldgs,_aiCount,_spawnChance] call A3EAI_initializeTrigger; + _spawnsCreated = _spawnsCreated + 1; + } catch { + if (A3EAI_debugLevel > 0) then {diag_log _exception;}; + }; + if ((_forEachIndex % 5) isEqualTo 0) then {uiSleep 0.25;}; +} forEach A3EAI_locations; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 has finished generating %2 static spawns in %3 seconds.",__FILE__,_spawnsCreated,(diag_tickTime - _startTime)];}; diff --git a/Server/@A3EAI/addons/a3eai/scripts/setup_locations.sqf b/Server/@A3EAI/addons/a3eai/scripts/setup_locations.sqf new file mode 100644 index 0000000..c43f66b --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/setup_locations.sqf @@ -0,0 +1,91 @@ +#include "\A3EAI\globaldefines.hpp" + +/* + Reads from CfgWorlds config and extracts information about city/town names, positions, and types. + + Used to generate waypoint positions for AI vehicle patrols. +*/ + +private ["_cfgWorldName","_startTime","_allPlaces","_telePositions","_allLocations"]; + +_startTime = diag_tickTime; +_allPlaces = []; +_telePositions = []; +_allLocations = []; +_cfgWorldName = configFile >> "CfgWorlds" >> worldName >> "Names"; + +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 +{ + A3EAI_waypointBlacklistAir set [_forEachIndex,(toLower _x)]; //Ensure case-insensitivity + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Created AI vehicle waypoint blacklist at %1.",_x];}; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; +} forEach A3EAI_waypointBlacklistAir; + +{ + A3EAI_waypointBlacklistLand set [_forEachIndex,(toLower _x)]; //Ensure case-insensitivity + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Created AI vehicle waypoint blacklist at %1.",_x];}; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; +} forEach A3EAI_waypointBlacklistLand; + +//Set up trader city blacklist areas +{ + if ((nearestLocations [_x select 3,[BLACKLIST_OBJECT_GENERAL],30]) isEqualTo []) then { + _location = [_x select 3,BLACKLIST_AREA_SIZE] call A3EAI_createBlackListArea; + _telePositions pushBack (_x select 3); + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Created %1m radius blacklist area at %2 teleport destination (%3).",BLACKLIST_AREA_SIZE,_x select 0,_x select 3];}; + }; + if ((nearestLocations [_x select 3,[BLACKLIST_OBJECT_NOAGGRO],30]) isEqualTo []) then { + _location = [_x select 3,NO_AGGRO_AREA_SIZE] call A3EAI_createNoAggroArea; + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Created %1m radius no-aggro area at %2 teleport destination (%3).",NO_AGGRO_AREA_SIZE,_x select 0,_x select 3];}; + }; + if ((_forEachIndex % 3) isEqualTo 0) then {uiSleep 0.05}; +} forEach ([configFile >> "CfgEpoch" >> worldName,"telePos",[]] call BIS_fnc_returnConfigEntry); + +{ + _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 (getMarkerPos "respawn_west")) > BLACKLIST_AREA_SIZE) && {({(_x distance2D _placePos) < BLACKLIST_AREA_SIZE} count _telePositions) isEqualTo 0}); + if (_isAllowedPos) then { + A3EAI_locations pushBack [_placeName,_placePos,_placeType]; + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: Added location %1 (type: %2, pos: %3) to location list.",_placeName,_placeType,_placePos];}; + if !(_placeName in A3EAI_waypointBlacklistAir) then {A3EAI_locationsAir pushBack [_placeName,_placePos,_placeType];}; + if !((_placeName in A3EAI_waypointBlacklistLand) && {!(surfaceIsWater _placePos)}) then {A3EAI_locationsLand pushBack [_placeName,_placePos,_placeType];}; + } else { + if (A3EAI_debugLevel > 1) then {diag_log format ["A3EAI Debug: %1 not in allowed position. Blacklist (Air): %2, Blacklist (Land): %3, respawn_west: %4, telepos: %5.",_placeName,!((toLower _placeName) in A3EAI_waypointBlacklistAir),!((toLower _placeName) in A3EAI_waypointBlacklistLand),(_placePos distance2D (getMarkerPos "respawn_west")) > BLACKLIST_AREA_SIZE,({(_x distance2D _placePos) < BLACKLIST_AREA_SIZE} count _telePositions) 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 && {A3EAI_maxRandomSpawns isEqualTo -1}) then { + A3EAI_maxRandomSpawns = ((round (0.10 * (count _allLocations)) min 15) max 5); + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Adjusted random spawn limit to %1",A3EAI_maxRandomSpawns];}; +}; + +if (A3EAI_locations isEqualTo []) then { + A3EAI_locations = +_allLocations; + if (A3EAI_debugLevel > 1) then {diag_log "A3EAI Debug: A3EAI_locations is empty, using _allLocations array instead.";}; +}; + +if (A3EAI_locationsAir isEqualTo []) then { + A3EAI_locationsAir = +_allLocations; + if (A3EAI_debugLevel > 1) then {diag_log "A3EAI Debug: A3EAI_locationsAir is empty, using _allLocations array instead.";}; +}; + +if (A3EAI_locationsLand isEqualTo []) then { + A3EAI_locationsLand = +_allLocations; + if (A3EAI_debugLevel > 1) then {diag_log "A3EAI Debug: A3EAI_locationsLand is empty, using _allLocations array instead.";}; +}; + +A3EAI_locations_ready = true; + +if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Location configuration completed with %1 locations found in %2 seconds.",(count A3EAI_locations),(diag_tickTime - _startTime)]}; diff --git a/Server/@A3EAI/addons/a3eai/scripts/setup_veh_patrols.sqf b/Server/@A3EAI/addons/a3eai/scripts/setup_veh_patrols.sqf new file mode 100644 index 0000000..392c851 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/setup_veh_patrols.sqf @@ -0,0 +1,137 @@ +#include "\A3EAI\globaldefines.hpp" + +waitUntil {uiSleep 0.3; (!isNil "A3EAI_locations_ready" && {!isNil "A3EAI_classnamesVerified"} && {!(isNil "EPOCH_server_setVToken")})}; + +if (A3EAI_maxAirPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3EAI_airVehicleList) - 1) do { + _currentElement = (A3EAI_airVehicleList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _heliType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_heliType,"vehicle"] call A3EAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3EAI_heliTypesUsable pushBack _heliType; + }; + } else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_heliType];}; + }; + } else { + diag_log "A3EAI Error: Non-array element found in A3EAI_airVehicleList. Please see default configuration file for proper format."; + }; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Assembled helicopter list: %1",A3EAI_heliTypesUsable];}; + + _maxHelis = (A3EAI_maxAirPatrols min (count A3EAI_heliTypesUsable)); + for "_i" from 1 to _maxHelis do { + _index = floor (random (count A3EAI_heliTypesUsable)); + _heliType = A3EAI_heliTypesUsable select _index; + _nul = _heliType spawn A3EAI_spawnVehiclePatrol; + A3EAI_heliTypesUsable deleteAt _index; + if (_i < _maxHelis) then {uiSleep 20}; + }; + }; + uiSleep 5; +}; + +if (A3EAI_maxLandPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3EAI_landVehicleList) - 1) do { + _currentElement = (A3EAI_landVehicleList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _vehType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_vehType,"vehicle"] call A3EAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3EAI_vehTypesUsable pushBack _vehType; + }; + } else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_vehType];}; + }; + } else { + diag_log "A3EAI Error: Non-array element found in A3EAI_landVehicleList. Please see default configuration file for proper format."; + }; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Assembled vehicle list: %1",A3EAI_vehTypesUsable];}; + + _maxVehicles = (A3EAI_maxLandPatrols min (count A3EAI_vehTypesUsable)); + for "_i" from 1 to _maxVehicles do { + _index = floor (random (count A3EAI_vehTypesUsable)); + _vehType = A3EAI_vehTypesUsable select _index; + _nul = _vehType spawn A3EAI_spawnVehiclePatrol; + A3EAI_vehTypesUsable deleteAt _index; + if (_i < _maxVehicles) then {uiSleep 20}; + }; + }; +}; + +if (A3EAI_maxUAVPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3EAI_UAVList) - 1) do { + _currentElement = (A3EAI_UAVList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _vehType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_vehType,"vehicle"] call A3EAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3EAI_UAVTypesUsable pushBack _vehType; + }; + } else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_vehType];}; + }; + } else { + diag_log "A3EAI Error: Non-array element found in A3EAI_UAVList. Please see default configuration file for proper format."; + }; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Assembled UAV list: %1",A3EAI_UAVTypesUsable];}; + + _maxVehicles = (A3EAI_maxUAVPatrols min (count A3EAI_UAVTypesUsable)); + for "_i" from 1 to _maxVehicles do { + _index = floor (random (count A3EAI_UAVTypesUsable)); + _vehType = A3EAI_UAVTypesUsable select _index; + _vehicleClass = [configFile >> "CfgVehicles" >> _vehType,"vehicleClass",""] call BIS_fnc_returnConfigEntry; + _nul = _vehType spawn A3EAI_spawn_UV_patrol; + A3EAI_UAVTypesUsable deleteAt _index; + if (_i < _maxVehicles) then {uiSleep 20}; + }; + }; +}; + +if (A3EAI_maxUGVPatrols > 0) then { + _nul = [] spawn { + for "_i" from 0 to ((count A3EAI_UGVList) - 1) do { + _currentElement = (A3EAI_UGVList select _i); + if ((typeName _currentElement) isEqualTo "ARRAY") then { + _vehType = _currentElement select 0; + _amount = _currentElement select 1; + + if ([_vehType,"vehicle"] call A3EAI_checkClassname) then { + for "_j" from 1 to _amount do { + A3EAI_UGVTypesUsable pushBack _vehType; + }; + } else { + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: %1 attempted to spawn invalid vehicle type %2.",__FILE__,_vehType];}; + }; + } else { + diag_log "A3EAI Error: Non-array element found in A3EAI_UGVList. Please see default configuration file for proper format."; + }; + }; + + if (A3EAI_debugLevel > 0) then {diag_log format ["A3EAI Debug: Assembled UGV list: %1",A3EAI_UGVTypesUsable];}; + + _maxVehicles = (A3EAI_maxUGVPatrols min (count A3EAI_UGVTypesUsable)); + for "_i" from 1 to _maxVehicles do { + _index = floor (random (count A3EAI_UGVTypesUsable)); + _vehType = A3EAI_UGVTypesUsable select _index; + _nul = _vehType spawn A3EAI_spawn_UV_patrol; + A3EAI_UGVTypesUsable deleteAt _index; + if (_i < _maxVehicles) then {uiSleep 20}; + }; + }; +}; diff --git a/Server/@A3EAI/addons/a3eai/scripts/verifyClassnames.sqf b/Server/@A3EAI/addons/a3eai/scripts/verifyClassnames.sqf new file mode 100644 index 0000000..659f264 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai/scripts/verifyClassnames.sqf @@ -0,0 +1,352 @@ +#include "\A3EAI\globaldefines.hpp" + +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 ["A3EAI] Removing non-string item %1 from classname table.",_x]; + _array set [_forEachIndex,""]; + if (!_errorFound) then {_errorFound = true}; + }; + 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 ["[A3EAI] Removing invalid classname: %1.",_x]; + _array set [_forEachIndex,""]; + if (!_errorFound) then {_errorFound = true}; + } 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 ["[A3EAI] Removing invalid classname: %1.",_x]; + _array set [_forEachIndex,""]; + if (!_errorFound) then {_errorFound = true}; + } 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 ["[A3EAI] Removing invalid classname: %1.",_x]; + _array set [_forEachIndex,""]; + if (!_errorFound) then {_errorFound = true}; + } else { + _verified pushBack _x; + }; + }; + diag_log format ["[A3EAI] Removing invalid classname: %1.",_x]; //Default case - if classname doesn't exist at all + _array set [_forEachIndex,""]; + if (!_errorFound) then {_errorFound = true}; + }; + }; + } forEach _array; + if (_errorFound) then { + _array = _array - [""]; + missionNamespace setVariable [_x,_array]; + diag_log format ["[A3EAI] Contents of %1 failed verification. Invalid entries removed.",_x]; + //diag_log format ["DEBUG :: Corrected contents of %1: %2.",_x,_array]; + //diag_log format ["DEBUG :: Comparison check of %1: %2.",_x,missionNamespace getVariable [_x,[]]]; + }; +} forEach A3EAI_tableChecklist; + +if (A3EAI_maxAirPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3EAI] Removing non-array type element from A3EAI_airVehicleList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3EAI] Array in A3EAI_airVehicleList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "Air")) then { + throw format ["[A3EAI] Removing non-Air type vehicle from A3EAI_airVehicleList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3EAI_airVehicleList deleteAt _forEachIndex; + }; + } forEach A3EAI_airVehicleList; +}; + +if (A3EAI_maxLandPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3EAI] Removing non-array type element from A3EAI_landVehicleList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3EAI] Array in A3EAI_landVehicleList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "LandVehicle")) then { + throw format ["[A3EAI] Removing non-LandVehicle type vehicle from A3EAI_landVehicleList array: %1.",(_x select 0)]; + }; + if (((_x select 0) isKindOf "StaticWeapon")) then { + throw format ["[A3EAI] Removing StaticWeapon type vehicle from A3EAI_landVehicleList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3EAI_landVehicleList deleteAt _forEachIndex; + }; + } forEach A3EAI_landVehicleList; +}; + +if (A3EAI_maxAirReinforcements > 0) then { + { + try { + if (!(_x isKindOf "Air")) then { + throw format ["[A3EAI] Removing non-Air type vehicle from A3EAI_airReinforcementVehicles array: %1.",_x]; + }; + } catch { + diag_log _exception; + A3EAI_airReinforcementVehicles deleteAt _forEachIndex; + }; + } forEach A3EAI_airReinforcementVehicles; +}; + +if (A3EAI_maxUAVPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3EAI] Removing non-array type element from A3EAI_UAVList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3EAI] Array in A3EAI_UAVList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "Air")) then { + throw format ["[A3EAI] Removing non-Air type vehicle from A3EAI_UAVList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3EAI_UAVList deleteAt _forEachIndex; + }; + } forEach A3EAI_UAVList; +}; + +if (A3EAI_maxUGVPatrols > 0) then { + { + try { + if ((typeName _x) != "ARRAY") then { + throw format ["[A3EAI] Removing non-array type element from A3EAI_UGVList array: %1.",_x]; + }; + if ((count _x) < 2) then { + throw format ["[A3EAI] Array in A3EAI_UGVList has only one element: %1. 2 expected: {Classname, Amount}.",_x]; + }; + if (!((_x select 0) isKindOf "LandVehicle")) then { + throw format ["[A3EAI] Removing non-LandVehicle type vehicle from A3EAI_UGVList array: %1.",(_x select 0)]; + }; + if (((_x select 0) isKindOf "StaticWeapon")) then { + throw format ["[A3EAI] Removing StaticWeapon type vehicle from A3EAI_UGVList array: %1.",(_x select 0)]; + }; + } catch { + diag_log _exception; + A3EAI_UGVList deleteAt _forEachIndex; + }; + } forEach A3EAI_UGVList; +}; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3EAI] Removing invalid uniform classname from A3EAI_uniformTypes0 array: %1.",_x]; + A3EAI_uniformTypes0 set [_forEachIndex,""]; + }; +} forEach A3EAI_uniformTypes0; +if ("" in A3EAI_uniformTypes0) then {A3EAI_uniformTypes0 = A3EAI_uniformTypes0 - [""];}; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3EAI] Removing invalid uniform classname from A3EAI_uniformTypes1 array: %1.",_x]; + A3EAI_uniformTypes1 set [_forEachIndex,""]; + }; +} forEach A3EAI_uniformTypes1; +if ("" in A3EAI_uniformTypes1) then {A3EAI_uniformTypes1 = A3EAI_uniformTypes1 - [""];}; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3EAI] Removing invalid uniform classname from A3EAI_uniformTypes2 array: %1.",_x]; + A3EAI_uniformTypes2 set [_forEachIndex,""]; + }; +} forEach A3EAI_uniformTypes2; +if ("" in A3EAI_uniformTypes2) then {A3EAI_uniformTypes2 = A3EAI_uniformTypes2 - [""];}; + +{ + if (([configFile >> "CfgWeapons" >> _x >> "ItemInfo","uniformClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "") then { + diag_log format ["[A3EAI] Removing invalid uniform classname from A3EAI_uniformTypes3 array: %1.",_x]; + A3EAI_uniformTypes3 set [_forEachIndex,""]; + }; +} forEach A3EAI_uniformTypes3; +if ("" in A3EAI_uniformTypes3) then {A3EAI_uniformTypes3 = A3EAI_uniformTypes3 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 2) then { + diag_log format ["[A3EAI] Removing invalid pistol classname from A3EAI_pistolList array: %1.",_x]; + A3EAI_pistolList set [_forEachIndex,""]; + }; +} forEach A3EAI_pistolList; +if ("" in A3EAI_pistolList) then {A3EAI_pistolList = A3EAI_pistolList - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 1) then { + diag_log format ["[A3EAI] Removing invalid rifle classname from A3EAI_rifleList array: %1.",_x]; + A3EAI_rifleList set [_forEachIndex,""]; + }; +} forEach A3EAI_rifleList; +if ("" in A3EAI_rifleList) then {A3EAI_rifleList = A3EAI_rifleList - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 1) then { + diag_log format ["[A3EAI] Removing invalid machine gun classname from A3EAI_machinegunList array: %1.",_x]; + A3EAI_machinegunList set [_forEachIndex,""]; + }; +} forEach A3EAI_machinegunList; +if ("" in A3EAI_machinegunList) then {A3EAI_machinegunList = A3EAI_machinegunList - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 1) then { + diag_log format ["[A3EAI] Removing invalid sniper classname from A3EAI_sniperList array: %1.",_x]; + A3EAI_sniperList set [_forEachIndex,""]; + }; +} forEach A3EAI_sniperList; +if ("" in A3EAI_sniperList) then {A3EAI_sniperList = A3EAI_sniperList - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x,"type",-1] call BIS_fnc_returnConfigEntry) isEqualTo 4) then { + diag_log format ["[A3EAI] Removing invalid launcher classname from A3EAI_launcherTypes array: %1.",_x]; + A3EAI_launcherTypes set [_forEachIndex,""]; + }; +} forEach A3EAI_launcherTypes; +if ("" in A3EAI_launcherTypes) then {A3EAI_launcherTypes = A3EAI_launcherTypes - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo","mountAction",""] call BIS_fnc_returnConfigEntry) isEqualTo "MountOptic") then { + diag_log format ["[A3EAI] Removing invalid optics classname from A3EAI_weaponOpticsList array: %1.",_x]; + A3EAI_weaponOpticsList set [_forEachIndex,""]; + }; +} forEach A3EAI_weaponOpticsList; +if ("" in A3EAI_weaponOpticsList) then {A3EAI_weaponOpticsList = A3EAI_weaponOpticsList - [""];}; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3EAI] Removing invalid backpack classname from A3EAI_backpackTypes0 array: %1.",_x]; + A3EAI_backpackTypes0 set [_forEachIndex,""]; + }; +} forEach A3EAI_backpackTypes0; +if ("" in A3EAI_backpackTypes0) then {A3EAI_backpackTypes0 = A3EAI_backpackTypes0 - [""];}; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3EAI] Removing invalid backpack classname from A3EAI_backpackTypes1 array: %1.",_x]; + A3EAI_backpackTypes1 set [_forEachIndex,""]; + }; +} forEach A3EAI_backpackTypes1; +if ("" in A3EAI_backpackTypes1) then {A3EAI_backpackTypes1 = A3EAI_backpackTypes1 - [""];}; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3EAI] Removing invalid backpack classname from A3EAI_backpackTypes2 array: %1.",_x]; + A3EAI_backpackTypes2 set [_forEachIndex,""]; + }; +} forEach A3EAI_backpackTypes2; +if ("" in A3EAI_backpackTypes2) then {A3EAI_backpackTypes2 = A3EAI_backpackTypes2 - [""];}; + +{ + if !(([configFile >> "CfgVehicles" >> _x,"vehicleClass",""] call BIS_fnc_returnConfigEntry) isEqualTo "Backpacks") then { + diag_log format ["[A3EAI] Removing invalid backpack classname from A3EAI_backpackTypes3 array: %1.",_x]; + A3EAI_backpackTypes3 set [_forEachIndex,""]; + }; +} forEach A3EAI_backpackTypes3; +if ("" in A3EAI_backpackTypes3) then {A3EAI_backpackTypes3 = A3EAI_backpackTypes3 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3EAI] Removing invalid vest classname from A3EAI_vestTypes0 array: %1.",_x]; + A3EAI_vestTypes0 set [_forEachIndex,""]; + }; +} forEach A3EAI_vestTypes0; +if ("" in A3EAI_vestTypes0) then {A3EAI_vestTypes0 = A3EAI_vestTypes0 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3EAI] Removing invalid vest classname from A3EAI_vestTypes1 array: %1.",_x]; + A3EAI_vestTypes1 set [_forEachIndex,""]; + }; +} forEach A3EAI_vestTypes1; +if ("" in A3EAI_vestTypes1) then {A3EAI_vestTypes1 = A3EAI_vestTypes1 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3EAI] Removing invalid vest classname from A3EAI_vestTypes2 array: %1.",_x]; + A3EAI_vestTypes2 set [_forEachIndex,""]; + }; +} forEach A3EAI_vestTypes2; +if ("" in A3EAI_vestTypes2) then {A3EAI_vestTypes2 = A3EAI_vestTypes2 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Body","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitBody") then { + diag_log format ["[A3EAI] Removing invalid vest classname from A3EAI_vestTypes3 array: %1.",_x]; + A3EAI_vestTypes3 set [_forEachIndex,""]; + }; +} forEach A3EAI_vestTypes3; +if ("" in A3EAI_vestTypes3) then {A3EAI_vestTypes3 = A3EAI_vestTypes3 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3EAI] Removing invalid headgear classname from A3EAI_headgearTypes0 array: %1.",_x]; + A3EAI_headgearTypes0 set [_forEachIndex,""]; + }; +} forEach A3EAI_headgearTypes0; +if ("" in A3EAI_headgearTypes0) then {A3EAI_headgearTypes0 = A3EAI_headgearTypes0 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3EAI] Removing invalid headgear classname from A3EAI_headgearTypes1 array: %1.",_x]; + A3EAI_headgearTypes1 set [_forEachIndex,""]; + }; +} forEach A3EAI_headgearTypes1; +if ("" in A3EAI_headgearTypes1) then {A3EAI_headgearTypes1 = A3EAI_headgearTypes1 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[CfgWeapons] Removing invalid headgear classname from A3EAI_headgearTypes2 array: %1.",_x]; + A3EAI_headgearTypes2 set [_forEachIndex,""]; + }; +} forEach A3EAI_headgearTypes2; +if ("" in A3EAI_headgearTypes2) then {A3EAI_headgearTypes2 = A3EAI_headgearTypes2 - [""];}; + +{ + if !(([configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "HitpointsProtectionInfo" >> "Head","hitpointName",""] call BIS_fnc_returnConfigEntry) isEqualTo "HitHead") then { + diag_log format ["[A3EAI] Removing invalid headgear classname from A3EAI_headgearTypes3 array: %1.",_x]; + A3EAI_headgearTypes3 set [_forEachIndex,""]; + }; +} forEach A3EAI_headgearTypes3; +if ("" in A3EAI_headgearTypes3) then {A3EAI_headgearTypes3 = A3EAI_headgearTypes3 - [""];}; + +{ + if !(([configFile >> "CfgMagazines" >> _x,"interactText",""] call BIS_fnc_returnConfigEntry) in ["EAT","DRINK","CONSUME"]) then { + diag_log format ["[A3EAI] Removing invalid food classname from A3EAI_foodLoot array: %1.",_x]; + A3EAI_foodLoot set [_forEachIndex,""]; + }; +} forEach A3EAI_foodLoot; +if ("" in A3EAI_foodLoot) then {A3EAI_foodLoot = A3EAI_foodLoot - [""];}; + + +//Anticipate cases where all elements of an array are invalid +if (A3EAI_pistolList isEqualTo []) then {A3EAI_pistolList = ["hgun_Pistol_heavy_01_F","hgun_P07_F","hgun_Rook40_F","hgun_Pistol_heavy_02_F","1911_pistol_epoch","hgun_ACPC2_F","ruger_pistol_epoch"]}; +if (A3EAI_rifleList isEqualTo []) then {A3EAI_rifleList = ["AKM_EPOCH","sr25_epoch","arifle_Katiba_GL_F","arifle_Katiba_C_F","arifle_Katiba_F","arifle_MX_GL_F","arifle_MX_GL_Black_F","arifle_MXM_Black_F","arifle_MXC_Black_F","arifle_MX_Black_F","arifle_MXM_F","arifle_MXC_F","arifle_MX_F","l85a2_epoch","l85a2_pink_epoch","l85a2_ugl_epoch","m4a3_EPOCH","m16_EPOCH","m16Red_EPOCH","arifle_Mk20_GL_F","arifle_Mk20_GL_plain_F","arifle_Mk20C_F","arifle_Mk20C_plain_F","arifle_Mk20_F","arifle_Mk20_plain_F","arifle_TRG21_GL_F","arifle_TRG21_F","arifle_TRG20_F","arifle_SDAR_F","Rollins_F","SMG_01_F","SMG_02_F","hgun_PDW2000_F"]}; +if (A3EAI_machinegunList isEqualTo []) then {A3EAI_machinegunList = ["LMG_Zafir_F","arifle_MX_SW_F","arifle_MX_SW_Black_F","LMG_Mk200_F","m249_EPOCH","m249Tan_EPOCH","MMG_01_hex_F","MMG_01_tan_F","MMG_02_camo_F","MMG_02_black_F","MMG_02_sand_F"]}; +if (A3EAI_sniperList isEqualTo []) then {A3EAI_sniperList = ["m107_EPOCH","m107Tan_EPOCH","srifle_DMR_02_F","srifle_DMR_02_camo_F","srifle_DMR_02_sniper_F","srifle_DMR_03_F","srifle_DMR_03_khaki_F","srifle_DMR_03_tan_F","srifle_DMR_03_multicam_F","srifle_DMR_03_woodland_F","srifle_DMR_03_spotter_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_LRR_F","srifle_GM6_F","srifle_DMR_01_F","M14_EPOCH","M14Grn_EPOCH","srifle_EBR_F"]}; +if (A3EAI_foodLoot isEqualTo []) then {A3EAI_foodLootCount = 0}; +if (A3EAI_MiscLoot1 isEqualTo []) then {A3EAI_miscLootCount1 = 0}; +if (A3EAI_MiscLoot2 isEqualTo []) then {A3EAI_miscLootCount2 = 0}; +if (A3EAI_airReinforcementVehicles isEqualTo []) then {A3EAI_maxAirReinforcements = 0; A3EAI_airReinforcementSpawnChance1 = 0; A3EAI_airReinforcementSpawnChance2 = 0; A3EAI_airReinforcementSpawnChance3 = 0;}; + +diag_log format ["[A3EAI] Verified %1 unique classnames in %2 seconds.",(count _verified),(diag_tickTime - _startTime)]; diff --git a/Server/@A3EAI/addons/a3eai_config/a3eai_custom_defs.sqf b/Server/@A3EAI/addons/a3eai_config/a3eai_custom_defs.sqf new file mode 100644 index 0000000..dc48817 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai_config/a3eai_custom_defs.sqf @@ -0,0 +1,15 @@ +/* + A3EAI Custom Spawn Definitions File + + Instructions: + + Generate custom spawns and blacklist areas using the included A3EAI Editor Tool (Inside the "Editor Tool" folder of the A3EAI download package). + + Instructions on how to use the Editor Tool are located on the A3EAI Wikia page: http://a3eai.wikia.com/wiki/A3EAI_Editor_Tool + + In order for A3EAI to load this file on startup, you must set in @A3EAI/A3EAI_config/config.cpp under the "A3EAI Settings" section: + + loadCustomFile = 1; + +//----------------------------Add your custom spawn and dynamic area blacklist definitions below this line ----------------------------*/ + diff --git a/Server/@A3EAI/addons/a3eai_config/config.cpp b/Server/@A3EAI/addons/a3eai_config/config.cpp new file mode 100644 index 0000000..16908e9 --- /dev/null +++ b/Server/@A3EAI/addons/a3eai_config/config.cpp @@ -0,0 +1,700 @@ +class CfgPatches { + class A3EAI_config { + units[] = {}; + weapons[] = {}; + requiredVersion = 0.1; + A3EAIVersion = "1.0.1a"; + requiredAddons[] = {}; + }; +}; + +class CfgA3EAISettings { + + /* A3EAI 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 A3EAI'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 A3EAI. 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 A3EAI config settings. (Default: 1) + verifySettings = 1; + + //Minimum seconds to pass until each dead AI body or destroyed vehicle can be cleaned up by A3EAI's task scheduler. A3EAI will not clean up a body/vehicle if there is a player close by (Default: 900). + cleanupDelay = 18000; + + //Enabled: A3EAI will load custom spawn/blacklist definitions file on startup (A3EAI_config.pbo >> custom_defs.sqf) (Default: 0) + loadCustomFile = 0; + + + /* A3EAI HC Settings + --------------------------------------------------------------------------------------------------------------------*/ + + //Enables A3EAI headless client support. (Default: 0) + enableHC = 0; + + //If HC support enabled, A3EAI 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 A3EAI may be stuck waiting for HC to to connect. + waitForHC = 0; + + + /* Dynamic Classname Settings + + If a setting is disabled, A3EAI will use the corresponding classname table further below. See "AI clothing, weapon, loot, and equipment settings" section. + --------------------------------------------------------------------------------------------------------------------*/ + + //1: Generate AI weapons from Epoch loot tables (Default) + //0: Weapons defined by pistolList, rifleList, machinegunList, sniperList + //dynamicWeaponBlacklist: Classnames of weapons to ignore from Epoch loot tables + generateDynamicWeapons = 1; + dynamicWeaponBlacklist[] = {}; + + //1: Use Epoch loot table data as whitelist for AI-usable weapon scopes (Default) + //0: Scopes defined by weaponOpticsList + //dynamicOpticsBlacklist: List of optics classnames to ignore from Epoch loot tables. + generateDynamicOptics = 1; + dynamicOpticsBlacklist[] = {}; + + //1: Generate AI uniform types from Epoch loot tables (Default) + //0: Uniforms defined by uniformTypes0, uniformTypes1, uniformTypes2, uniformTypes3 + //dynamicUniformBlacklist: List of uniform classnames to ignore from Epoch loot tables. + generateDynamicUniforms = 0; + dynamicUniformBlacklist[] = {}; + + //1: Generate AI backpack types from Epoch loot tables (Default) + //0: Backpacks defined by backpackTypes0, backpackTypes1, backpackTypes2, backpackTypes3 + //dynamicBackpackBlacklist: List of backpack classnames to ignore from Epoch loot tables. + generateDynamicBackpacks = 1; + dynamicBackpackBlacklist[] = {}; + + //1: Generate AI backpack types from Epoch loot tables (Default) + //0: Vests defined by vestTypes0, vestTypes1, vestTypes2, vestTypes3 + //dynamicVestBlacklist: List of vest classnames to ignore from Epoch loot tables. + generateDynamicVests = 1; + dynamicVestBlacklist[] = {}; + + //1: Generate AI headgear types from Epoch loot tables (Default) + //0: Headgear defined by headgearTypes0, headgearTypes1, headgearTypes2, headgearTypes3 + //dynamicHeadgearBlacklist: List of headgear classnames to ignore from Epoch loot tables. + generateDynamicHeadgear = 1; + dynamicHeadgearBlacklist[] = {}; + + //1: Generate AI food types from Epoch loot tables (Default) + //0: Food defined by foodLoot + //dynamicFoodBlacklist: List of food classnames to ignore from Epoch loot tables. + generateDynamicFood = 1; + dynamicFoodBlacklist[] = {}; + + //1: Generate AI generic loot types from Epoch loot tables. (Default) + //0: Loot defined by MiscLoot + //dynamicLootBlacklist: List of loot classnames to ignore from Epoch loot tables. + generateDynamicLoot = 1; + dynamicLootBlacklist[] = {}; + + + /* A3EAI Client Addon features. These features require the A3EAI client addon to be installed in order to work. + --------------------------------------------------------------------------------------------------------------------*/ + + enableRadioMessages = 1; + enableDeathMessages = 1; + + + /* Shared AI Unit Settings. These settings affect all AI spawned unless noted otherwise. + --------------------------------------------------------------------------------------------------------------------*/ + + //Number of online players required for maximum (or minimum) AI spawn chance. Affects Static, Dynamic, Random AI spawns. (Default: 10) + playerCountThreshold = 1; + + //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.70; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_village = 2; + addAI_village = 8; + unitLevel_village = 1; + 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 = 3; + addAI_city = 6; + unitLevel_city = 2; + 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 = 8; + unitLevel_capitalCity = 3; + spawnChance_capitalCity = 0.70; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_remoteArea = 1; + addAI_remoteArea = 4; + unitLevel_remoteArea = 2; + spawnChance_remoteArea = 0.50; + + //(Static/Dynamic/Random Spawns) minAI: Minimum number of units. addAI: maximum number of additional units. unitLevel: Unit level (0-3) + minAI_wilderness = 2; + addAI_wilderness = 4; + unitLevel_wilderness = 1; + spawnChance_wilderness = 0.70; + + //(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; + + //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. + launcherTypes[] = {"launch_NLAW_F","launch_RPG32_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, A3EAI 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 = 0; + + //if enabled, AI units suffer no damage from vehicle collisions. (Default: 1) + noCollisionDamage = 0; + + //If enabled, AI killed by vehicle collisions will have their gear removed (Default: 1) + roadKillPenalty = 0; + + + /* Static Infantry AI Spawning Settings + + A3EAI 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: A3EAI automatically generates static spawns at named locations on map. Disabled: No static spawns will be generated. (Default: 1) + enableStaticSpawns = 1; + + //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; + + //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 = 120; + + //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 + + A3EAI 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.60) + spawnHunterChance = 0.60; + + //Time to wait (seconds) before despawning all AI units in dynamic spawn area when no players are present. (Default: 120) + despawnDynamicSpawnTime = 120; + + + /* Random Infantry AI Spawning Settings + + A3EAI 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 A3EAI 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 = 120; + + //Minimum distance between a random spawn location and other random spawns. (Default: 0) + distanceBetweenRandomSpawns = 300; + + + /* Shared AI Vehicle Settings + + These settings affect the following AI vehicle patrol types: Air, Land, UAV, UGV + --------------------------------------------------------------------------------------------------------------------*/ + + //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. + + A3EAI 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",1}, + {"B_Heli_Transport_01_F",1}, + {"B_Heli_Transport_03_F",1} + }; + + //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.60; + + //Probability of AI to deploy infantry units by parachute if players are nearby when helicopter is investigating a waypoint. (Default: 0.50) + //Affects: Air vehicle patrols. + paradropChance = 0.50; + + //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. + + A3EAI 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 = 10; + + //Probability of spawning Level 0/1/2/3 AI land vehicle spawns. Probabilities should add up to 1.00 + levelChancesLand[] = {0.10,0.30,0.40,0.20}; + + //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[] = { + {"B_MRAP_01_EPOCH",5}, + {"C_Van_01_box_EPOCH",5}, + {"C_Van_01_transport_EPOCH",5}, + {"C_Offroad_01_EPOCH",5}, + {"C_Hatchback_02_EPOCH",5}, + {"C_Hatchback_01_EPOCH",5}, + {"C_SUV_01_EPOCH",5}, + {"B_Truck_01_transport_EPOCH",5}, + {"B_Truck_01_covered_EPOCH",5}, + {"B_Truck_01_mover_EPOCH",5}, + {"B_Truck_01_box_EPOCH",5}, + {"O_Truck_02_covered_EPOCH",5}, + {"O_Truck_02_transport_EPOCH",5}, + {"O_Truck_03_covered_EPOCH",5}, + {"O_Truck_02_box_EPOCH",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 = 6; + + + /* 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 = 0; + + //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.20; + airReinforcementSpawnChance1 = 0.25; + airReinforcementSpawnChance2 = 0.30; + airReinforcementSpawnChance3 = 0.40; + + //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"}; + + //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. + + A3EAI 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 = 2; + + //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; + + //Set to '1' to set detection-only behavior (UAV will not directly engage enemies). (Default: 0) + detectOnlyUAVs = 1; + + //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. + + A3EAI 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; + + //Set to '1' to set detection-only behavior (UGV will not directly engage enemies). (Default: 0) + detectOnlyUGVs = 1; + + //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.60; + + + /* + AI skill settings. + + These settings affect all AI units spawned by A3EAI. + + 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.08}, + {"aimingShake",0.10,0.15}, + {"aimingSpeed",0.10,0.30}, + {"spotDistance",0.10,0.30}, + {"spotTime",0.05,0.15}, + {"courage",0.10,0.20}, + {"reloadSpeed",0.10,0.30}, + {"commanding",0.10,0.30}, + {"general",0.10,0.30} + }; + + //AI skill settings level 1 (Skill, Minimum skill, Maximum skill). Defaults: Accuracy 0.10-0.15, Others 0.40-0.60 + skill1[] = { + {"aimingAccuracy",0.08,0.15}, + {"aimingShake",0.20,0.40}, + {"aimingSpeed",0.20,0.40}, + {"spotDistance",0.20,0.40}, + {"spotTime",0.10,0.20}, + {"courage",0.20,0.40}, + {"reloadSpeed",0.20,0.40}, + {"commanding",0.20,0.40}, + {"general",0.20,0.40} + }; + + //AI skill settings level 2 (Skill, Minimum skill, Maximum skill). Defaults: Accuracy 0.15-0.20, Others 0.50-0.70 + skill2[] = { + {"aimingAccuracy",0.10,0.20}, + {"aimingShake",0.30,0.50}, + {"aimingSpeed",0.30,0.50}, + {"spotDistance",0.30,0.50}, + {"spotTime",0.10,0.40}, + {"courage",0.30,0.40}, + {"reloadSpeed",0.30,0.50}, + {"commanding",0.30,0.50}, + {"general",0.30,0.50} + }; + + //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.50,0.70}, + {"aimingSpeed",0.40,0.70}, + {"spotDistance",0.40,0.70}, + {"spotTime",0.40,0.70}, + {"courage",0.60,0.80}, + {"reloadSpeed",0.60,0.80}, + {"commanding",0.60,0.80}, + {"general",0.40,0.70} + }; + + + /* AI loadout probability settings. + --------------------------------------------------------------------------------------------------------------------*/ + + //Probabilities to equip uniform, according to AI level. + addUniformChance0 = 0.60; + addUniformChance1 = 0.70; + addUniformChance2 = 0.80; + addUniformChance3 = 0.90; + + //Probabilities to equip backpack, according to AI level. + addBackpackChance0 = 0.20; + addBackpackChance1 = 0.50; + addBackpackChance2 = 0.80; + addBackpackChance3 = 0.99; + + //Probabilities to equip vest, according to AI level. + addVestChance0 = 0.10; + addVestChance1 = 0.60; + addVestChance2 = 0.80; + addVestChance3 = 0.99; + + //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.60,0.40,0.00,0.00}; + useWeaponChance1[] = {0.20,0.78,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.10; + 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 + --------------------------------------------------------------------------------------------------------------------*/ + + //Maximum amount of Krypto generated for level 0-3 AI. Actual amount will be randomized up to the specified amount. + kryptoAmount0 = 50; + kryptoAmount1 = 75; + kryptoAmount2 = 100; + kryptoAmount3 = 150; + + //Krypto pickup assist time window in seconds. Players must be within 2 meters of a Krypto device for 5 seconds to pick up Krypto automatically. 0: Disabled (Default: 0) + //After this time limit, players must manually pick up any dropped Krypto. + kryptoPickupAssist = 0; + + //Maximum number of food loot items found on AI. (Default: 2) + foodLootCount = 2; + + //Maximum number of items to select from A3EAI_miscLoot1 (generic loot) table. (Default: 1) + miscLootCount1 = 1; + + //Maximum number of items to select from A3EAI_miscLoot2 (large generic loot) table. (Default: 1) + miscLootCount2 = 1; + + + /* AI loot probability settings. AI loot is pre-generated into a pool for each unit and randomly pulled to units as time passes. + --------------------------------------------------------------------------------------------------------------------*/ + + //Chance to add a single First Aid Kit to group loot pool per unit (Default: 0.25) + firstAidKitChance = 0.25; + + //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.10; + lootPullChance1 = 0.30; + lootPullChance2 = 0.60; + lootPullChance3 = 0.80; + + + /* + AI skin, weapon, loot, and equipment settings + + Note: Some of the below tables may not be used by A3EAI 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_O_PilotCoveralls", "U_O_Wetsuit", "U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_C_Poloshirt_stripped", "U_C_Poloshirt_blue", "U_C_Poloshirt_burgundy", "U_C_Poloshirt_tricolour", "U_C_Poloshirt_salmon", "U_C_Poloshirt_redwhite", "U_C_Poor_1", "U_C_WorkerCoveralls", "U_C_Journalist", "U_C_Scientist", "U_OrestesBody", "U_C_Driver_1", "U_C_Driver_2", "U_C_Driver_3", "U_C_Driver_4", "U_C_Driver_1_black", "U_C_Driver_1_blue", "U_C_Driver_1_green", "U_C_Driver_1_red", "U_C_Driver_1_white", "U_C_Driver_1_yellow", "U_C_Driver_1_orange", "U_C_Driver_1_red"}; + uniformTypes1[] = {"U_O_PilotCoveralls", "U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_C_Poloshirt_stripped", "U_C_Poloshirt_blue", "U_C_Poloshirt_burgundy", "U_C_Poloshirt_tricolour", "U_C_Poloshirt_salmon", "U_C_Poloshirt_redwhite", "U_C_Poor_1", "U_C_WorkerCoveralls", "U_C_Journalist", "U_C_Scientist", "U_OrestesBody", "U_C_Driver_1", "U_C_Driver_2", "U_C_Driver_3", "U_C_Driver_4", "U_C_Driver_1_black", "U_C_Driver_1_blue", "U_C_Driver_1_green", "U_C_Driver_1_red", "U_C_Driver_1_white", "U_C_Driver_1_yellow", "U_C_Driver_1_orange", "U_C_Driver_1_red"}; + uniformTypes2[] = {"U_OG_Guerilla1_1", "U_OG_Guerilla2_1", "U_OG_Guerilla2_3", "U_OG_Guerilla3_1", "U_OG_Guerilla3_2", "U_OG_leader", "U_CamoRed_uniform", "U_CamoBrn_uniform", "U_CamoBlue_uniform", "U_Camo_uniform", "U_ghillie1_uniform", "U_ghillie2_uniform", "U_ghillie3_uniform"}; + uniformTypes3[] = {"U_O_CombatUniform_ocamo", "U_O_GhillieSuit", "U_CamoRed_uniform", "U_CamoBrn_uniform", "U_CamoBlue_uniform", "U_Camo_uniform", "U_ghillie1_uniform", "U_ghillie2_uniform", "U_ghillie3_uniform", }; + + //AI weapon classnames. Note: pistolList, rifleList, machinegunList, sniperList will not be read if generateDynamicWeapons is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + pistolList[] = {"hgun_Pistol_heavy_01_F","hgun_P07_F","hgun_Rook40_F","hgun_Pistol_heavy_02_F","1911_pistol_epoch","hgun_ACPC2_F","ruger_pistol_epoch"}; + rifleList[] = {"AKM_EPOCH","sr25_epoch","arifle_Katiba_GL_F","arifle_Katiba_C_F","arifle_Katiba_F","arifle_MX_GL_F","arifle_MX_GL_Black_F","arifle_MXM_Black_F","arifle_MXC_Black_F","arifle_MX_Black_F","arifle_MXM_F","arifle_MXC_F","arifle_MX_F","l85a2_epoch","l85a2_pink_epoch","l85a2_ugl_epoch","m4a3_EPOCH","m16_EPOCH","m16Red_EPOCH","arifle_Mk20_GL_F","arifle_Mk20_GL_plain_F","arifle_Mk20C_F","arifle_Mk20C_plain_F","arifle_Mk20_F","arifle_Mk20_plain_F","arifle_TRG21_GL_F","arifle_TRG21_F","arifle_TRG20_F","arifle_SDAR_F","Rollins_F","SMG_01_F","SMG_02_F","hgun_PDW2000_F"}; + machinegunList[] = {"LMG_Zafir_F","arifle_MX_SW_F","arifle_MX_SW_Black_F","LMG_Mk200_F","m249_EPOCH","m249Tan_EPOCH","MMG_01_hex_F","MMG_01_tan_F","MMG_02_camo_F","MMG_02_black_F","MMG_02_sand_F"}; + sniperList[] = {"m107_EPOCH","m107Tan_EPOCH","srifle_DMR_02_F","srifle_DMR_02_camo_F","srifle_DMR_02_sniper_F","srifle_DMR_03_F","srifle_DMR_03_khaki_F","srifle_DMR_03_tan_F","srifle_DMR_03_multicam_F","srifle_DMR_03_woodland_F","srifle_DMR_03_spotter_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_LRR_F","srifle_GM6_F","srifle_DMR_01_F","M14_EPOCH","M14Grn_EPOCH","srifle_EBR_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_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"}; + backpackTypes1[] = {"B_AssaultPack_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"}; + backpackTypes2[] = {"B_AssaultPack_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"}; + backpackTypes3[] = {"B_AssaultPack_cbr", "B_AssaultPack_dgtl", "B_AssaultPack_khk", "B_AssaultPack_mcamo", "B_AssaultPack_ocamo", "B_AssaultPack_rgr", "B_AssaultPack_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_khk", "B_FieldPack_ocamo", "B_FieldPack_oli", "B_FieldPack_oucamo", "B_Kitbag_cbr", "B_Kitbag_mcamo", "B_Kitbag_rgr", "B_Kitbag_sgg", "B_Parachute", "B_TacticalPack_blk", "B_TacticalPack_mcamo", "B_TacticalPack_ocamo", "B_TacticalPack_oli", "B_TacticalPack_rgr", "smallbackpack_red_epoch", "smallbackpack_green_epoch", "smallbackpack_teal_epoch", "smallbackpack_pink_epoch"}; + + //AI vest types (for AI levels 0-3). Note: vestTypes0-3 will not be read if generateDynamicVests is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + vestTypes0[] = {"V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"}; + vestTypes1[] = {"V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"}; + vestTypes2[] = {"V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"}; + vestTypes3[] = {"V_1_EPOCH", "V_2_EPOCH", "V_3_EPOCH", "V_4_EPOCH", "V_5_EPOCH", "V_6_EPOCH", "V_7_EPOCH", "V_8_EPOCH", "V_9_EPOCH", "V_10_EPOCH", "V_11_EPOCH", "V_12_EPOCH", "V_13_EPOCH", "V_14_EPOCH", "V_15_EPOCH", "V_16_EPOCH", "V_17_EPOCH", "V_18_EPOCH", "V_19_EPOCH", "V_20_EPOCH", "V_21_EPOCH", "V_22_EPOCH", "V_23_EPOCH", "V_24_EPOCH", "V_25_EPOCH", "V_26_EPOCH", "V_27_EPOCH", "V_28_EPOCH", "V_29_EPOCH", "V_30_EPOCH", "V_31_EPOCH", "V_32_EPOCH", "V_33_EPOCH", "V_34_EPOCH", "V_35_EPOCH", "V_36_EPOCH", "V_37_EPOCH", "V_38_EPOCH", "V_39_EPOCH", "V_40_EPOCH"}; + + //AI head gear types. Note: headgearTypes0-3 will not be read if generateDynamicHeadgear is enabled. + headgearTypes0[] = {"H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"}; + headgearTypes1[] = {"H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"}; + headgearTypes2[] = {"H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"}; + headgearTypes3[] = {"H_1_EPOCH","H_2_EPOCH","H_3_EPOCH","H_4_EPOCH","H_5_EPOCH","H_6_EPOCH","H_7_EPOCH","H_8_EPOCH","H_9_EPOCH","H_10_EPOCH","H_11_EPOCH","H_12_EPOCH","H_13_EPOCH","H_14_EPOCH","H_15_EPOCH","H_16_EPOCH","H_17_EPOCH","H_18_EPOCH","H_19_EPOCH","H_20_EPOCH","H_21_EPOCH","H_22_EPOCH","H_23_EPOCH","H_24_EPOCH","H_25_EPOCH","H_26_EPOCH","H_27_EPOCH","H_28_EPOCH","H_29_EPOCH","H_30_EPOCH","H_31_EPOCH","H_32_EPOCH","H_33_EPOCH","H_34_EPOCH","H_35_EPOCH","H_36_EPOCH","H_37_EPOCH","H_38_EPOCH","H_39_EPOCH","H_40_EPOCH","H_41_EPOCH","H_42_EPOCH","H_43_EPOCH","H_44_EPOCH","H_45_EPOCH","H_46_EPOCH","H_47_EPOCH","H_48_EPOCH","H_49_EPOCH","H_50_EPOCH","H_51_EPOCH","H_52_EPOCH","H_53_EPOCH","H_54_EPOCH","H_55_EPOCH","H_56_EPOCH","H_57_EPOCH","H_58_EPOCH","H_59_EPOCH","H_60_EPOCH","H_61_EPOCH","H_62_EPOCH","H_63_EPOCH","H_64_EPOCH","H_65_EPOCH","H_66_EPOCH","H_67_EPOCH","H_68_EPOCH","H_69_EPOCH","H_70_EPOCH","H_71_EPOCH","H_72_EPOCH","H_73_EPOCH","H_74_EPOCH","H_75_EPOCH","H_76_EPOCH","H_77_EPOCH","H_78_EPOCH","H_79_EPOCH","H_80_EPOCH","H_81_EPOCH","H_82_EPOCH","H_83_EPOCH","H_84_EPOCH","H_85_EPOCH","H_86_EPOCH","H_87_EPOCH","H_88_EPOCH","H_89_EPOCH","H_90_EPOCH","H_91_EPOCH","H_92_EPOCH","H_93_EPOCH","H_94_EPOCH","H_95_EPOCH","H_96_EPOCH","H_97_EPOCH","H_98_EPOCH","H_99_EPOCH","H_100_EPOCH","H_101_EPOCH","H_102_EPOCH","H_103_EPOCH","H_104_EPOCH"}; + + + //AI Food/Loot item types. + // Note: foodLoot will not be read if generateDynamicFood is enabled. + // Note: miscLoot1/miscLoot2 will not be read if generateDynamicLoot is enabled. + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + foodLoot[] = {"FoodSnooter","FoodWalkNSons","FoodBioMeat","ItemSodaOrangeSherbet","ItemSodaPurple","ItemSodaMocha","ItemSodaBurst","ItemSodaRbull","honey_epoch","emptyjar_epoch","sardines_epoch","meatballs_epoch","scam_epoch","sweetcorn_epoch","WhiskeyNoodle","ItemCoolerE"}; + miscLoot1[] = {"PaintCanClear","PaintCanBlk","PaintCanBlu","PaintCanBrn","PaintCanGrn","PaintCanOra","PaintCanPur","PaintCanRed","PaintCanTeal","PaintCanYel","ItemDocument","ItemMixOil","emptyjar_epoch","emptyjar_epoch","FoodBioMeat","Towelette","Towelette","Towelette","Towelette","Towelette","HeatPack","HeatPack","HeatPack","ColdPack","ColdPack","VehicleRepair","CircuitParts","ItemCoolerE","ItemScraps","ItemScraps"}; + miscLoot2[] = {"MortarBucket","MortarBucket","ItemCorrugated","CinderBlocks","jerrycan_epoch","jerrycan_epoch","VehicleRepair","VehicleRepair","CircuitParts"}; + + + //AI toolbelt item types. Toolbelt items are added to AI inventory upon death. Format: {item classname, item probability} + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + toolsList0[] = { + {"ItemWatch",0.90},{"ItemCompass",0.30},{"ItemMap",0.30},{"ItemGPS",0.00},{"EpochRadio0",0.00} + }; + toolsList1[] = { + {"ItemWatch",0.90},{"ItemCompass",0.50},{"ItemMap",0.50},{"ItemGPS",0.10},{"EpochRadio0",0.10} + }; + toolsList2[] = { + {"ItemWatch",0.90},{"ItemCompass",0.70},{"ItemMap",0.70},{"ItemGPS",0.20},{"EpochRadio0",0.20} + }; + toolsList3[] = { + {"ItemWatch",0.90},{"ItemCompass",0.90},{"ItemMap",0.90},{"ItemGPS",0.30},{"EpochRadio0",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.50},{"NVG_EPOCH",0.00} + }; + gadgetsList1[] = { + {"binocular",0.50},{"NVG_EPOCH",0.10} + }; + gadgetsList2[] = { + {"binocular",0.70},{"NVG_EPOCH",0.20} + }; + gadgetsList3[] = { + {"binocular",0.90},{"NVG_EPOCH",0.30} + }; +}; diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_code/A3EAI_client_killMessage.sqf b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_code/A3EAI_client_killMessage.sqf new file mode 100644 index 0000000..7a0f8d7 --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_code/A3EAI_client_killMessage.sqf @@ -0,0 +1,10 @@ +private ["_message"]; + +_message = _this; +if ((typeName _message) isEqualTo "STRING") then { + systemChat _message; +} else { + diag_log format ["A3EAI Error: Kill message is non-string %1",_message]; +}; + +true \ No newline at end of file diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_code/A3EAI_client_radioMessage.sqf b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_code/A3EAI_client_radioMessage.sqf new file mode 100644 index 0000000..1c4354e --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_code/A3EAI_client_radioMessage.sqf @@ -0,0 +1,30 @@ +private ["_dialogueType", "_dialogueParams", "_paramCount", "_dialogueTextTemplate", "_dialogueTextFormat"]; + +_dialogueType = _this select 0; +_dialogueParams = _this select 1; + +if (((diag_tickTime - (missionNamespace getVariable ["A3EAI_client_lastRadioMessage",-10])) > 10) or {_dialogueType in [20,30,31,32,33,34,35,41,42,43,44,45,51,52,53,54,55]}) then { + _paramCount = (count _dialogueParams); + _dialogueTextTemplate = missionNamespace getVariable [format ["A3EAI_client_radioMessage%1",_dialogueType],""]; + _dialogueTextFormat = call { + if (_paramCount isEqualTo 0) exitWith { + _dialogueTextTemplate + }; + if (_paramCount isEqualTo 1) exitWith { + format [_dialogueTextTemplate,_dialogueParams select 0] + }; + if (_paramCount isEqualTo 2) exitWith { + format [_dialogueTextTemplate,_dialogueParams select 0,_dialogueParams select 1] + }; + "" + }; + if !(_dialogueTextFormat isEqualTo "") then { + systemChat _dialogueTextFormat; + if (A3EAI_client_radioSounds) then { + playSound [format ["UAV_0%1",(floor (random 5) + 1)],false]; + }; + A3EAI_client_lastRadioMessage = diag_tickTime; + }; +}; + +true \ No newline at end of file diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_config.sqf b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_config.sqf new file mode 100644 index 0000000..5de5731 --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_config.sqf @@ -0,0 +1,65 @@ +/* + A3EAI Client-side Addon Configuration File + +*/ + + +/* A3EAI Client Addon Settings +--------------------------------------------------------------------------------------------------------------------*/ + +//Enables use of client-side radio functions. A3EAI_radioMsgs must be set 'true' in A3EAI_config.sqf. +A3EAI_client_radio = true; + +//Enables death messages to be displayed to players who kill AI units. A3EAI_deathMessages must be set 'true' in A3EAI_config.sqf +A3EAI_client_deathMessages = true; + +//Enables sounds upon recieving radio messages. +A3EAI_client_radioSounds = true; + + +/* A3EAI Text String Settings +--------------------------------------------------------------------------------------------------------------------*/ + +//AI kill message +A3EAI_client_killMessage0 = "%1 was killed"; //%1: AI corpse name + +//AI radio static message +A3EAI_client_radioMessage0 = "[RADIO] Your radio is picking up a signal nearby."; //Message displayed when radio sound transmitted without AI dialogue. + +//AI radio messages (AI-killer dialogue) +A3EAI_client_radioMessage1 = "[RADIO] %1: %2 is in this area. Stay on alert!"; //%1: AI leader name, %2: Target player name +A3EAI_client_radioMessage2 = "[RADIO] %1: Target looks like a %2. Find them!"; //%1: AI leader name, %2: Target player type +A3EAI_client_radioMessage3 = "[RADIO] %1: Target's range is about %2 meters. Move in on that position!"; //%1: AI leader name, %2: Target player distance +A3EAI_client_radioMessage4 = "[RADIO] %1: Lost contact with target. Breaking off pursuit."; //%1: AI leader name +A3EAI_client_radioMessage5 = "[RADIO] %1: Target has been eliminated."; //%1: AI leader name + +//AI radio messages (Dynamic AI hunter dialogue) +A3EAI_client_radioMessage11 = "[RADIO] %1: %2 is somewhere in this location. Search the area!"; //%1: AI leader name, %2: Target player name +A3EAI_client_radioMessage12 = "[RADIO] %1: Target is a %2. Stay on alert!"; //%1: AI leader name, %2: Target player type +A3EAI_client_radioMessage13 = "[RADIO] %1: Target's distance is %2 meters. Move in to intercept!"; //%1: AI leader name, %2: Target player distance +A3EAI_client_radioMessage14 = "[RADIO] %1: We've lost contact with the target. Let's move out."; //%1: AI leader name +A3EAI_client_radioMessage15 = "[RADIO] %1: The target has been killed."; //%1: AI leader name + +//AI air patrol reinforcement warning message +A3EAI_client_radioMessage20 = "Warning: Hostile %1 inbound."; //%1: Air vehicle type + +//AI air patrol dialogue. Displayed when player is detected by air patrol. +A3EAI_client_radioMessage31 = "[RADIO] %1: Target spotted below. Engaging."; //%1: AI leader name +A3EAI_client_radioMessage32 = "[RADIO] %1: We've arrived at the location. Moving in on the target."; //%1: AI leader name +A3EAI_client_radioMessage33 = "[RADIO] %1: Thats's the one we're looking for. Take him out."; //%1: AI leader name +A3EAI_client_radioMessage34 = "[RADIO] %1: Located the target. Let's take him out."; //%1: AI leader name +A3EAI_client_radioMessage35 = "[RADIO] %1: Priority target confirmed. Proceeding to engage."; //%1: AI leader name + +//UAV patrol dialogue. Displayed when player is detected. +A3EAI_client_radioMessage41 = "[RADIO] %1 %2: Targets detected. Relaying position data."; //%1: UAV Group, %2: UAV Type +A3EAI_client_radioMessage42 = "[RADIO] %1 %2: Targets found at destination coordinates."; //%1: UAV Group, %2: UAV Type +A3EAI_client_radioMessage43 = "[RADIO] %1 %2: Movement detected. Targets selected."; //%1: UAV Group, %2: UAV Type +A3EAI_client_radioMessage44 = "[RADIO] %1 %2: Heat signatures confirmed. Designating targets."; //%1: UAV Group, %2: UAV Type +A3EAI_client_radioMessage45 = "[RADIO] %1 %2: Priority target located. Redirecting armed forces to target location."; //%1: UAV Group, %2: UAV Type + +//UGV patrol dialogue. Displayed when player is detected. +A3EAI_client_radioMessage51 = "[RADIO] %1 %2: Targets detected. Relaying position data."; //%1: UGV Group, %2: UGV Type +A3EAI_client_radioMessage52 = "[RADIO] %1 %2: Targets found at destination coordinates."; //%1: UGV Group, %2: UGV Type +A3EAI_client_radioMessage53 = "[RADIO] %1 %2: Movement detected. Targets selected."; //%1: UGV Group, %2: UGV Type +A3EAI_client_radioMessage54 = "[RADIO] %1 %2: Heat signatures confirmed. Designating targets."; ///%1: UGV Group, %2: UGV Type +A3EAI_client_radioMessage55 = "[RADIO] %1 %2: Priority target located. Redirecting armed forces to target location."; //%1: UGV Group, %2: UGV Type diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_functions.sqf b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_functions.sqf new file mode 100644 index 0000000..a45b58b --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_functions.sqf @@ -0,0 +1,2 @@ +A3EAI_client_radioMessage = compileFinal preprocessFileLineNumbers "A3EAI_Client\A3EAI_client_code\A3EAI_client_radioMessage.sqf"; +A3EAI_client_killMessage = compileFinal preprocessFileLineNumbers "A3EAI_Client\A3EAI_client_code\A3EAI_client_killMessage.sqf"; \ No newline at end of file diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_verifySettings.sqf b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_verifySettings.sqf new file mode 100644 index 0000000..20e6520 --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_verifySettings.sqf @@ -0,0 +1,43 @@ +_startTime = diag_tickTime; + +//Check value types +{ + _value = missionNamespace getVariable (_x select 0); + if ((isNil "_value") or {(typeName _value) != (typeName (_x select 1))}) then { + missionNamespace setVariable [(_x select 0),(_x select 1)]; + diag_log format ["[A3EAI] Error found in variable %1, resetting to default value.",(_x select 0)]; + }; +} forEach [ + ["A3EAI_client_radio",true], + ["A3EAI_client_radioSounds",true], + ["A3EAI_client_deathMessages",true], + ["A3EAI_client_radioMessage0","[RADIO] Your radio is picking up a signal nearby."], + ["A3EAI_client_radioMessage1","[RADIO] %1: %2 is in this area. Stay on alert!"], + ["A3EAI_client_radioMessage2","[RADIO] %1: Target looks like a %2. Find them!"], + ["A3EAI_client_radioMessage3","[RADIO] %1: Target's range is about %2 meters. Move in on that position!"], + ["A3EAI_client_radioMessage4","[RADIO] %1: Lost contact with target. Breaking off pursuit."], + ["A3EAI_client_radioMessage5","[RADIO] %1: Target has been eliminated."], + ["A3EAI_client_radioMessage11","[RADIO] %1: %2 is somewhere in this location. Search the area!"], + ["A3EAI_client_radioMessage12","[RADIO] %1: Target is a %2. Stay on alert!"], + ["A3EAI_client_radioMessage13","[RADIO] %1: Target's distance is %2 meters. Move in to intercept!"], + ["A3EAI_client_radioMessage14","[RADIO] %1: We've lost contact with the target. Let's move out."], + ["A3EAI_client_radioMessage15","[RADIO] %1: The target has been killed."], + ["A3EAI_client_radioMessage20","Warning: Hostile %1 inbound."], + ["A3EAI_client_radioMessage31","[RADIO] %1: Target spotted below. Engaging."], + ["A3EAI_client_radioMessage32","[RADIO] %1: We've arrived at the location. Moving in on the target."], + ["A3EAI_client_radioMessage33","[RADIO] %1: Thats's the one we're looking for. Take him out."], + ["A3EAI_client_radioMessage34","[RADIO] %1: Located the target. Let's take him out."], + ["A3EAI_client_radioMessage35","[RADIO] %1: Priority target confirmed. Proceeding to engage."], + ["A3EAI_client_radioMessage41","[RADIO] %1 %2: Targets detected. Relaying position data."], + ["A3EAI_client_radioMessage42","[RADIO] %1 %2: Targets found at destination coordinates."], + ["A3EAI_client_radioMessage43","[RADIO] %1 %2: Movement detected. Targets selected."], + ["A3EAI_client_radioMessage44","[RADIO] %1 %2: Heat signatures confirmed. Designating targets."], + ["A3EAI_client_radioMessage45","[RADIO] %1 %2: Priority target located. Redirecting armed forces to target location."], + ["A3EAI_client_radioMessage51","[RADIO] %1 %2: Targets detected. Relaying position data."], + ["A3EAI_client_radioMessage52","[RADIO] %1 %2: Targets found at destination coordinates."], + ["A3EAI_client_radioMessage53","[RADIO] %1 %2: Movement detected. Targets selected."], + ["A3EAI_client_radioMessage54","[RADIO] %1 %2: Heat signatures confirmed. Designating targets."], + ["A3EAI_client_radioMessage55","[RADIO] %1 %2: Priority target located. Redirecting armed forces to target location."] +]; + +diag_log format ["[A3EAI] Verified all A3EAI settings in %1 seconds.",(diag_tickTime - _startTime)]; diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_version.txt b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_version.txt new file mode 100644 index 0000000..8bd9bb8 --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_client_version.txt @@ -0,0 +1,6 @@ +/* + A3EAI Version Identifier File +*/ + +#define A3EAI_CLIENT_TYPE "A3EAI Client Addon" +#define A3EAI_CLIENT_VERSION "1.2.0" diff --git a/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_initclient.sqf b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_initclient.sqf new file mode 100644 index 0000000..9b99ae5 --- /dev/null +++ b/Server/mpmissions/epoch.Bornholm/A3EAI_Client/A3EAI_initclient.sqf @@ -0,0 +1,17 @@ +if (hasInterface) then{ + #include "A3EAI_client_version.txt"; + + call compile preprocessFileLineNumbers "A3EAI_Client\A3EAI_client_config.sqf"; + call compile preprocessFileLineNumbers "A3EAI_Client\A3EAI_client_verifySettings.sqf"; + call compile preprocessFileLineNumbers "A3EAI_Client\A3EAI_client_functions.sqf"; + + if (A3EAI_client_radio) then { + "A3EAI_SMS" addPublicVariableEventHandler {(_this select 1) call A3EAI_client_radioMessage; diag_log _this;}; + }; + + if (A3EAI_client_deathMessages) then { + "A3EAI_killMSG" addPublicVariableEventHandler {(_this select 1) call A3EAI_client_killMessage; diag_log _this;}; + }; + + diag_log format ["[A3EAI] Initialized %1 version %2. Radio enabled: %3. Kill messages: %4.",A3EAI_CLIENT_TYPE,A3EAI_CLIENT_VERSION,A3EAI_client_radio,A3EAI_client_deathMessages]; +}; diff --git a/Server/mpmissions/epoch.Bornholm/init.sqf b/Server/mpmissions/epoch.Bornholm/init.sqf index 2f2aff0..46cd3f0 100644 --- a/Server/mpmissions/epoch.Bornholm/init.sqf +++ b/Server/mpmissions/epoch.Bornholm/init.sqf @@ -1,7 +1,7 @@ // execVM "debug\blckClient.sqf"; -// #include "A3EAI_Client\A3EAI_initclient.sqf"; +#include "A3EAI_Client\A3EAI_initclient.sqf"; if hasInterface then { diff --git a/Server/restartserver_x64.bat b/Server/restartserver_x64.bat index c92a412..f30de18 100644 --- a/Server/restartserver_x64.bat +++ b/Server/restartserver_x64.bat @@ -19,7 +19,7 @@ timeout 2 :: RESTARTING THE ARMA 3 SERVER BE SURE TO EDIT THIS TO YOUR SERVER .EXE LOCATION -NOTE ALSO THIS IS WHERE YOU DEFINE WHERE YOU CONFIG.CFG IS echo Starting ARMA 3 Server... ::C:\Windows\System32\cmd.exe /C start "arma3" "d:\GameServers\Arma3\arma3epoch\Epochserver_x64.exe" "-mod=@Epoch;@bornholm;@CBA_A3;@CUP Units;@CUP Vehicles;@CUP Weapons;@Extended Base Objects For Epoch;" "-serverMod=@EpochHive;@A3EAI;@GMS;@Advanced Rappelling;@Advanced Sling Loading;@Advanced Towing" -config=F:\GameServers\Arma3\arma3epoch\sc\server.cfg -port=2302 -profiles=sc -cfg=F:\GameServers\Arma3\arma3epoch\sc\basic.cfg -name=sc -autoINIT -Loadmissiontomemory -C:\Windows\System32\cmd.exe /C start "arma3" "d:\GameServers\Arma3\arma3epoch\Epochserver_x64.exe" "-mod=@Epoch;@bornholm;" "-serverMod=@EpochHive;" -config=d:\GameServers\Arma3\arma3epoch\sc\server.cfg -port=2302 -profiles=sc -cfg=d:\GameServers\Arma3\arma3epoch\sc\basic.cfg -name=sc -autoINIT -Loadmissiontomemory +C:\Windows\System32\cmd.exe /C start "arma3" "d:\GameServers\Arma3\arma3epoch\Epochserver_x64.exe" "-mod=@Epoch;@bornholm;" "-serverMod=@EpochHive;@A3EAI;" -config=d:\GameServers\Arma3\arma3epoch\sc\server.cfg -port=2302 -profiles=sc -cfg=d:\GameServers\Arma3\arma3epoch\sc\basic.cfg -name=sc -autoINIT -Loadmissiontomemory echo ARMA 3 Server has started timeout 60 diff --git a/StageFilesToServer.bat b/StageFilesToServer.bat index a3a450a..b28ffc5 100644 --- a/StageFilesToServer.bat +++ b/StageFilesToServer.bat @@ -5,7 +5,7 @@ cd d:\Git_Repo\Arma3_Repo\Server set DEST=d:\GameServers\Arma3\arma3epoch\ REM A3EAI -:: copy /y .\@A3EAI\addons\*.pbo %DEST%\@A3EAI\addons +copy /y .\@A3EAI\addons\*.pbo %DEST%\@A3EAI\addons REM epochHive copy /y .\@epochhive\*.hpp %DEST%\@epochHive