summaryrefslogtreecommitdiffstats
path: root/Server/AI
diff options
context:
space:
mode:
authorhybrid <hybrid@hybridlabs.pro>2026-06-11 19:53:22 +0300
committerhybrid <hybrid@hybridlabs.pro>2026-06-11 19:53:22 +0300
commitc055d44dcdb87e8db49fb23e632fc086aa44cce6 (patch)
tree7ac98d865915930bebc86b36b512a859c31538e5 /Server/AI
downloada2wf_zargabad-c055d44dcdb87e8db49fb23e632fc086aa44cce6.tar.gz
a2wf_zargabad-c055d44dcdb87e8db49fb23e632fc086aa44cce6.tar.bz2
a2wf_zargabad-c055d44dcdb87e8db49fb23e632fc086aa44cce6.zip
init
Diffstat (limited to 'Server/AI')
-rw-r--r--Server/AI/AI_AddMultiplayerRespawnEH.sqf1
-rw-r--r--Server/AI/AI_AdvancedRespawn.sqf119
-rw-r--r--Server/AI/AI_Resistance.sqf17
-rw-r--r--Server/AI/AI_SquadRespawn.sqf104
-rw-r--r--Server/AI/AI_TLWPHandler.sqs39
-rw-r--r--Server/AI/AI_UpdateSupplyTruck.sqf24
-rw-r--r--Server/AI/AI_Upgrade.sqf32
-rw-r--r--Server/AI/Orders/AI_MoveTo.sqf17
-rw-r--r--Server/AI/Orders/AI_Patrol.sqf34
-rw-r--r--Server/AI/Orders/AI_TownPatrol.sqf71
-rw-r--r--Server/AI/Orders/AI_WPAdd.sqf39
-rw-r--r--Server/AI/Orders/AI_WPRemove.sqf6
12 files changed, 503 insertions, 0 deletions
diff --git a/Server/AI/AI_AddMultiplayerRespawnEH.sqf b/Server/AI/AI_AddMultiplayerRespawnEH.sqf
new file mode 100644
index 0000000..1308fb4
--- /dev/null
+++ b/Server/AI/AI_AddMultiplayerRespawnEH.sqf
@@ -0,0 +1 @@
+(leader _this) addMPEventHandler ['MPRespawn',{if (isServer && !(isPlayer (_this select 0))) then {_this Spawn AIAdvancedRespawn}}]; \ No newline at end of file
diff --git a/Server/AI/AI_AdvancedRespawn.sqf b/Server/AI/AI_AdvancedRespawn.sqf
new file mode 100644
index 0000000..a05caac
--- /dev/null
+++ b/Server/AI/AI_AdvancedRespawn.sqf
@@ -0,0 +1,119 @@
+/* Enhanced Respawn Management via Multiplayer Event Handler - Experimental */
+Private ['_autonomous','_availableSpawn','_buildings','_checks','_closestRespawn','_corpse','_deathLoc','_hq','_i','_isForcedRespawn','_mobileRespawns','_moveMode','_pos','_ran','_range','_rcm','_rd','_respawn','_respawnLoc','_respawnedUnit','_side','_sideText','_skip','_team','_update','_upgrades'];
+
+_respawnedUnit = _this select 0;
+_corpse = _this select 1;
+
+_deathLoc = getPos _corpse;
+_rd = 'WFBE_RESPAWNDELAY' Call GetNamespace;
+_rcm = 'WFBE_RESPAWNCAMPSMODE' Call GetNamespace;
+
+_side = side _respawnedUnit;
+//--- Ensure that the side is not civilian.
+if (_side == civilian) then {
+ if (_respawnedUnit isKindOf westSoldierBaseClass) then {_side = west};
+ if (_respawnedUnit isKindOf eastSoldierBaseClass) then {_side = east};
+};
+_sideText = str _side;
+_team = group _respawnedUnit;
+_respawn = (_team) Call GetTeamRespawn;
+_respawnLoc = objNull;
+
+diag_log format ["[WFBE (INFORMATION)][frameno:%7 | ticktime:%8] AI_AdvancedRespawn: AI Team Leader '%1' (Team '%2', Rank '%3', Type '%4', Side '%5') has respawned after dieing at %6",_respawnedUnit,_team,rank _respawnedUnit,typeOf _respawnedUnit,_side,_deathLoc,diag_frameno,diag_tickTime];
+
+//--- Remove previous EH.
+_respawnedUnit removeAllEventHandlers "Killed";
+Call Compile Format ["_respawnedUnit addEventHandler ['Killed',{[_this select 0,_this select 1,%1] Spawn UnitKilled}]",_side];
+
+//--- Place the leader on a 'safe' position.
+_respawnedUnit setPos getMarkerPos Format["%1TempRespawnMarker",_sideText];
+
+_availableSpawn = [];
+_isForcedRespawn = false;
+
+//--- Ensure that the ai is not forced to respawn.
+if (typeName _respawn == 'STRING') then {if (_respawn == "forceRespawn") then {_isForcedRespawn = true}};
+
+//--- Towns.
+if (_rcm > 0 && !_isForcedRespawn) then {
+ _availableSpawn = _availableSpawn + ([_deathLoc, _side] Call GetRespawnCamps);
+};
+
+_upgrades = (_sideText) Call GetSideUpgrades;
+
+//--- Mobile Respawn.
+if (paramMobileRespawn && !_isForcedRespawn) then {
+ _mobileRespawns = Format ["WFBE_%1AMBULANCES",_sideText] Call GetNamespace;
+ _range = (Format["WFBE_RESPAWNMOBILERANGE%1",(_upgrades select 7)] Call GetNamespace);
+ _checks = _deathLoc nearEntities[_mobileRespawns,_range];
+ if (count _checks > 0) then {
+ {if (alive _x) then {_availableSpawn = _availableSpawn + [_x]}} forEach _checks;
+ };
+};
+
+//--- Wait.
+_i = _rd;
+_skip = false;
+while {_i > 0} do {
+ if (isPlayer(_respawnedUnit) || !(alive _respawnedUnit)) exitWith {_skip = true};
+
+ _i = _i - 1;
+ sleep 1;
+};
+
+//--- Make sure that the AI didn't die or that a player hasn't replaced him before going any further.
+if !(_skip) then {
+ //--- Equip the AI.
+ _ran = 1 + round(random(2));
+ [_respawnedUnit,Format ["WFBE_%1LEADERWEAPONS%2%3",_sideText,_upgrades select 13,_ran] Call GetNamespace, Format ["WFBE_%1LEADERAMMO%2%3",_sideText,_upgrades select 13,_ran] Call GetNamespace] Call EquipLoadout;
+ _hq = (_sideText) Call GetSideHQ;
+ _buildings = (_sideText) Call GetSideStructures;
+
+ //--- Check whether AI has a spawn set or not.
+ _update = false;
+ switch (typeName _respawn) do {
+ case "STRING": {
+ //--- Not defined.
+ _update = true;
+ if (_isForcedRespawn) then {[_team,""] Call SetTeamRespawn};
+ };
+ case "OBJECT": {
+ //--- Defined.
+ _respawnLoc = _respawn;
+ if (!alive _respawn || isNull _respawn) then {
+ [_team, ""] Call SetTeamRespawn;
+ _update = true;
+ };
+ };
+ };
+
+ //--- Default respawn.
+ if (_update) then {
+ _respawnLoc = _hq;
+ if (count _buildings > 0) then {
+ _closestRespawn = [_hq,_buildings] Call SortByDistance;
+ _respawnLoc = _closestRespawn select 0;
+ };
+ };
+
+ //--- Alternative spawn location.
+ if (count _availableSpawn > 0) then {
+ _respawnLoc = _availableSpawn select (round(random((count _availableSpawn)-1)));
+ };
+
+ diag_log format ["[WFBE (INFORMATION)][frameno:%4 | ticktime:%5] AI_AdvancedRespawn: AI Team Leader %1 is respawning at: %2 (%3)",_respawnedUnit,typeOf _respawnLoc,_respawnLoc,diag_frameno,diag_tickTime];
+
+ _pos = [getPos _respawnLoc,20,30] Call GetRandomPosition;
+ _pos set [2,0];
+ _respawnedUnit setPos _pos;
+
+ //--- Assign fresh order if the AI is not on autonomous mode.
+ _autonomous = (_team) Call GetTeamAutonomous;
+ if !(_autonomous) then {
+ _moveMode = (_team) Call GetTeamMoveMode;
+ if (_moveMode == "towns") then {[_team,"resetTowns"] Call SetTeamMoveMode};
+ if (_moveMode == "move") then {[_team,"resetMove"] Call SetTeamMoveMode};
+ if (_moveMode == "patrol") then {[_team,"resetPatrol"] Call SetTeamMoveMode};
+ if (_moveMode == "defense") then {[_team,"resetDefense"] Call SetTeamMoveMode};
+ };
+}; \ No newline at end of file
diff --git a/Server/AI/AI_Resistance.sqf b/Server/AI/AI_Resistance.sqf
new file mode 100644
index 0000000..2fe1ce2
--- /dev/null
+++ b/Server/AI/AI_Resistance.sqf
@@ -0,0 +1,17 @@
+Private ["_action","_position","_range","_team"];
+_team = _this select 0;
+_position = _this select 1;
+_range = _this select 2;
+_action = _this select 3;
+
+switch (_action) do {
+ case "Patrol": {[_team,_position,_range] Call BIS_fnc_taskPatrol};
+ case "Defend": {
+ _team setFormation "STAG COLUMN";
+ _team setBehaviour "AWARE";
+ _team setSpeedMode "NORMAL";
+
+ [_team, true, [[_position, 'SAD', 40, 30, "", []]]] Call AIWPAdd;
+ };
+ case "CPatrol": {[_team,_position,_range] Spawn AIPatrol};
+}; \ No newline at end of file
diff --git a/Server/AI/AI_SquadRespawn.sqf b/Server/AI/AI_SquadRespawn.sqf
new file mode 100644
index 0000000..08a32a2
--- /dev/null
+++ b/Server/AI/AI_SquadRespawn.sqf
@@ -0,0 +1,104 @@
+Private ["_availableSpawn","_autonomous","_buildings","_checks","_closestRespawn","_deathLoc","_enemySide","_hq","_isForcedRespawn","_leader","_mobileRespawns","_moveMode","_pos","_ran","_range","_rcm'","_rd","_respawn","_respawnLoc","_side","_sideText","_team","_update","_upgrades"];
+_side = _this select 0;
+_team = _this select 1;
+_sideText = str _side;
+_deathLoc = objNull;
+_respawnLoc = objNull;
+
+_rd = 'WFBE_RESPAWNDELAY' Call GetNamespace;
+_rcm = 'WFBE_RESPAWNCAMPSMODE' Call GetNamespace;
+
+sleep (random 0.5);
+
+while {!gameOver} do {
+ if (isPlayer leader _team) exitWith {};
+ [str _side,'UnitsCreated',1] Call UpdateStatistics;
+ waitUntil {!alive leader _team || isPlayer leader _team};
+ _deathLoc = getPos (leader _team);
+ diag_log format ["[WFBE (INFORMATION)][frameno:%4 | ticktime:%5] AI_SquadRespawn: AI Team Leader '%1' (Team '%2') is dead at %3",leader _team,_team,_deathLoc,diag_frameno,diag_tickTime];
+ if (isPlayer leader _team) exitWith {};
+ waitUntil {alive leader _team || isPlayer leader _team};
+ if (isPlayer leader _team) exitWith {};
+
+ _respawn = (_team) Call GetTeamRespawn;
+
+ //--- Place the AI.
+ _leader = leader _team;
+ _leader removeAllEventHandlers "Killed";
+ Call Compile Format ["_leader addEventHandler ['Killed',{[_this select 0,_this select 1,%1] Spawn UnitKilled}]",_side];
+ _leader setPos getMarkerPos Format["%1TempRespawnMarker",_sideText];
+
+ _availableSpawn = [];
+ _isForcedRespawn = false;
+ if (typeName _respawn == 'STRING') then {if (_respawn == "forceRespawn") then {_isForcedRespawn = true}};
+
+ //--- Towns.
+ if (_rcm > 0 && !_isForcedRespawn) then {
+ _availableSpawn = _availableSpawn + ([_deathLoc, _side] Call GetRespawnCamps);
+ };
+
+ _upgrades = (_sideText) Call GetSideUpgrades;
+
+ //--- Mobile Respawn.
+ if (paramMobileRespawn && !_isForcedRespawn) then {
+ _mobileRespawns = Format ["WFBE_%1AMBULANCES",_sideText] Call GetNamespace;
+ _range = (Format["WFBE_RESPAWNMOBILERANGE%1",(_upgrades select 7)] Call GetNamespace);
+ _checks = _deathLoc nearEntities[_mobileRespawns,_range];
+ if (count _checks > 0) then {
+ {if (alive _x) then {_availableSpawn = _availableSpawn + [_x]}} forEach _checks;
+ };
+ };
+
+ sleep _rd;
+
+ //--- Equip the AI.
+ _ran = 1 + round(random(2));
+ [_leader,Format ["WFBE_%1LEADERWEAPONS%2%3",_sideText,_upgrades select 13,_ran] Call GetNamespace, Format ["WFBE_%1LEADERAMMO%2%3",_sideText,_upgrades select 13,_ran] Call GetNamespace] Call EquipLoadout;
+ _hq = (_sideText) Call GetSideHQ;
+ _buildings = (_sideText) Call GetSideStructures;
+
+ //--- Check whether AI has a spawn set or not.
+ _update = false;
+ switch (typeName _respawn) do {
+ case "STRING": {
+ _update = true;
+ if (_isForcedRespawn) then {[_team,""] Call SetTeamRespawn};
+ }; //--- Not defined.
+ case "OBJECT": {
+ _respawnLoc = _respawn;
+ if (!alive _respawn || isNull _respawn) then {
+ [_team, ""] Call SetTeamRespawn;
+ _update = true;
+ }; //--- Defined.
+ };
+ };
+
+ //--- Default respawn.
+ if (_update) then {
+ _respawnLoc = _hq;
+ if (count _buildings > 0) then {
+ _closestRespawn = [_hq,_buildings] Call SortByDistance;
+ _respawnLoc = _closestRespawn select 0;
+ };
+ };
+
+ //--- Alternative spawn location.
+ if (count _availableSpawn > 0) then {
+ _respawnLoc = _availableSpawn select (round(random((count _availableSpawn)-1)));
+ };
+
+ diag_log format ["[WFBE (INFORMATION)][frameno:%4 | ticktime:%5] AI_SquadRespawn: AI Team Leader %1 is respawning at: %2 (%3)",leader _team,typeOf _respawnLoc,_respawnLoc,diag_frameno,diag_tickTime];
+ _pos = [getPos _respawnLoc,20,30] Call GetRandomPosition;
+ _pos set [2,0];
+ _leader setPos _pos;
+
+ //--- Assign fresh order (tbc).
+ _autonomous = (_team) Call GetTeamAutonomous;
+ if !(_autonomous) then {
+ _moveMode = (_team) Call GetTeamMoveMode;
+ if (_moveMode == "towns") then {[_team,"resetTowns"] Call SetTeamMoveMode};
+ if (_moveMode == "move") then {[_team,"resetMove"] Call SetTeamMoveMode};
+ if (_moveMode == "patrol") then {[_team,"resetPatrol"] Call SetTeamMoveMode};
+ if (_moveMode == "defense") then {[_team,"resetDefense"] Call SetTeamMoveMode};
+ };
+}; \ No newline at end of file
diff --git a/Server/AI/AI_TLWPHandler.sqs b/Server/AI/AI_TLWPHandler.sqs
new file mode 100644
index 0000000..c94a6a8
--- /dev/null
+++ b/Server/AI/AI_TLWPHandler.sqs
@@ -0,0 +1,39 @@
+Private ["_group","_i","_timeSince","_unit","_units","_waitForMembers","_wpPos"]
+_group = _this select 0
+_wpPos = _this select 1
+
+_timeSince = 0
+
+if (vehicle (leader _group) isKindOf "Air") then {Goto "Exit";}
+
+#Main
+ ~5
+
+ _timeSince = _timeSince + 5
+
+ ;Check for the group's status.
+ _units = ((units _group) Call GetLiveUnits) - [leader _group]
+
+ ;Exit if the leader is the only member.
+ if (count _units < 2) then {Goto "Exit";}
+
+ _i = 0
+ _waitForMembers = false
+
+ ;Iterate through all members.
+ #Loop
+ _unit = _units select _i
+
+ if (_unit distance (leader _group) > 150 && !(_unit isKindOf 'Air')) then {_waitForMembers = true; if (_timeSince > 240) then {[vehicle _unit,getPos (leader _group),75] Call PlaceSafe;};}
+
+ _i = _i + 1
+
+ if (_i == count _units) then {if (_waitForMembers) then {Goto "Main";} else {Goto "Exit";}};
+
+ Goto "Loop"
+
+ Goto "Main"
+
+#Exit
+
+ Exit \ No newline at end of file
diff --git a/Server/AI/AI_UpdateSupplyTruck.sqf b/Server/AI/AI_UpdateSupplyTruck.sqf
new file mode 100644
index 0000000..9083ed7
--- /dev/null
+++ b/Server/AI/AI_UpdateSupplyTruck.sqf
@@ -0,0 +1,24 @@
+Private ["_AITrucks","_destinations","_driver","_full","_hq","_isDeployed","_maist","_side","_sideText","_team","_type","_vehicle"];
+_side = _this select 0;
+_sideText = str _side;
+_maist = 'WFBE_MAXAISUPPLYTRUCKS' Call GetNamespace;
+
+sleep ((Random 200) / 100);
+sleep ((Random 200) / 100);
+
+waitUntil {townInit};
+
+while {!gameOver} do {
+ sleep 60;
+ _AITrucks = Format ["WFBE_%1_AISupplyTrucks",_sideText] Call GetNamespace;
+ _full = false;
+ if (count _AITrucks >= _maist) then {_full = true};
+ if (!_full) then {
+ _isDeployed = (_sideText) Call GetSideHQDeployed;
+ _hq = (_sideText) Call GetSideHQ;
+ if ((_isDeployed)&&(alive _hq)&&(!isNull _hq)) then {
+ diag_log Format["[WFBE (INFORMATION)][frameno:%2 | ticktime:%3] AI_UpdateSupplyTruck: A %1 Supply truck has been created",_side,diag_frameno,diag_tickTime];
+ [_side] ExecFSM "Server\FSM\supplytruck.fsm";
+ };
+ };
+};
diff --git a/Server/AI/AI_Upgrade.sqf b/Server/AI/AI_Upgrade.sqf
new file mode 100644
index 0000000..dabac71
--- /dev/null
+++ b/Server/AI/AI_Upgrade.sqf
@@ -0,0 +1,32 @@
+Private ["_index","_index2","_name","_side","_sideText","_time","_upgrades","_upgrades_old"];
+_time = _this select 0;
+_index = _this select 1;
+_index2 = _this select 2;
+_name = _this select 3;
+_side = _this select 4;
+_sideText = Str _side;
+
+Call Compile Format ["WFBE_%1_Upgrading = true; publicVariable 'WFBE_%1_Upgrading';",_sideText];
+
+_upgrades_old = (_sideText) Call GetSideUpgrades;
+
+sleep _time;
+
+_upgrades = (_sideText) Call GetSideUpgrades;
+
+//--- Failover due to faulty pv.
+for '_i' from 0 to count _upgrades_old do {
+ if ((_upgrades_old select _i) != (_upgrades select _i)) then {_upgrades set [_i, (_upgrades_old select _i)]};
+};
+
+_upgrades set [_index2,_index + 1];
+Call Compile Format ["%1Upgrades = _upgrades; publicVariable '%1Upgrades';",_sideText];
+
+WFBE_LocalizeMessage = [_side,'CLTFNCLOCALIZEMESSAGE',['UpgradeComplete',_index2,(_index + 1)]];
+publicVariable 'WFBE_LocalizeMessage';
+if (isHostedServer) then {[_side,'CLTFNCLOCALIZEMESSAGE',['UpgradeComplete',_index2,(_index + 1)]] Spawn HandlePVF};
+[Format["WFBE_%1_Upgrading",_sideText],false,true] Call SetNamespace;
+
+["NewIntelAvailable","",_side,""] Spawn SideMessage;
+
+Call Compile Format ["WFBE_%1_Upgrading = false; publicVariable 'WFBE_%1_Upgrading';",_sideText];
diff --git a/Server/AI/Orders/AI_MoveTo.sqf b/Server/AI/Orders/AI_MoveTo.sqf
new file mode 100644
index 0000000..e372393
--- /dev/null
+++ b/Server/AI/Orders/AI_MoveTo.sqf
@@ -0,0 +1,17 @@
+private ["_destination","_mission","_radius","_team","_update"];
+_team = _this select 0;
+_destination = _this select 1;
+_mission = _this select 2;
+_radius = if (count _this > 3) then {_this select 3} else {30};
+
+_update = true;
+if (side _team == west || side _team == east) then {
+ _update = (_team) Call CanUpdateTeam;
+};
+
+//--- Override.
+if (_update) then {_team Call UpdateTeam};
+
+diag_log Format["[WFBE (INFORMATION)][frameno:%4 | ticktime:%5] AI_MoveTo: The %1 %2 Team is moving to %3",side _team,_team,_destination,diag_frameno,diag_tickTime];
+
+[_team,true,[[_destination, _mission, _radius, 20, "", []]]] Call AIWPAdd; \ No newline at end of file
diff --git a/Server/AI/Orders/AI_Patrol.sqf b/Server/AI/Orders/AI_Patrol.sqf
new file mode 100644
index 0000000..8cff904
--- /dev/null
+++ b/Server/AI/Orders/AI_Patrol.sqf
@@ -0,0 +1,34 @@
+Private ["_destination","_maxWaypoints","_pos","_radius","_rand1","_rand2","_team","_type","_update","_wps","_z"];
+_team = _this select 0;
+_destination = _this select 1;
+_radius = if (count _this > 2) then {_this select 2} else {30};
+if (typeName _destination == 'OBJECT') then {_destination = getPos _destination};
+
+_update = true;
+if (side _team == west || side _team == east) then {
+ _update = (_team) Call CanUpdateTeam;
+};
+
+//--- Override.
+if (_update) then {_team Call UpdateTeam};
+
+_maxWaypoints = 8;
+_wps = [];
+for [{_z=0},{_z<=_maxWaypoints},{_z=_z+1}] do {
+ _rand1 = (random _radius) - (random _radius);
+ _rand2 = (random _radius) - (random _radius);
+ _pos = [(_destination select 0)+_rand1,(_destination select 1)+_rand2,0];
+ _tries = 100;
+ while {surfaceIsWater _pos && _tries > 0} do {
+ _tries = _tries - 1;
+ _rand1 = (random _radius) - (random _radius);
+ _rand2 = (random _radius) - (random _radius);
+ _pos = [(_destination select 0)+_rand1,(_destination select 1)+_rand2,0];
+ };
+ _type = if (_z != _maxWaypoints) then {'MOVE'} else {'CYCLE'};
+ _wps = _wps + [[_pos,_type,35,40,"",[]]];
+};
+
+diag_log Format["[WFBE (INFORMATION)][frameno:%4 | ticktime:%5] AI_Patrol: The %1 %2 Team is patrolling at %3",side _team,_team,_destination,diag_frameno,diag_tickTime];
+
+[_team, true, _wps] Call AIWPAdd; \ No newline at end of file
diff --git a/Server/AI/Orders/AI_TownPatrol.sqf b/Server/AI/Orders/AI_TownPatrol.sqf
new file mode 100644
index 0000000..15122d3
--- /dev/null
+++ b/Server/AI/Orders/AI_TownPatrol.sqf
@@ -0,0 +1,71 @@
+/*
+ Author: Benny
+ Name: AI_TownPatrol.sqf
+ Parameters:
+ 0 - Team
+ 1 - Town
+ 2 - (Radius)
+ Description:
+ This file is called upon a resistance / occupation patrol, AI will randomly patrol in towns, including the camps and the depots.
+*/
+
+Private ['_camps','_insert','_insertObject','_insertStep','_maxWaypoints','_pos','_radius','_rand1','_rand2','_team','_town','_townPos','_type','_update','_usable','_wpcompletionRadius','_wpradius','_wps','_z'];
+_team = _this select 0;
+_town = _this select 1;
+_radius = if (count _this > 2) then {_this select 2} else {30};
+if (typeName _town != 'OBJECT') exitWith {diag_log Format ["[WFBE (ERROR)] AI_TownPatrol: Object expected, %1 given",_town]};
+if (isNull _team) exitWith {diag_log Format ["[WFBE (ERROR)] AI_TownPatrol: Null Groups cannot be used (Town: %1)",_town]};
+_townPos = getPos _town;
+
+_camps = _town getVariable 'camps';
+
+_usable = [_town] + _camps;
+_maxWaypoints = ('WFBE_TOWNPATROLHOPS' Call GetNamespace) + count(_usable);
+_wps = [];
+
+//--- Randomize the behaviours.
+if (random 100 > 50) then {_team setFormation "DIAMOND"} else {_team setFormation "STAG COLUMN"};
+if (random 100 > 50) then {_team setCombatMode "YELLOW"} else {_team setCombatMode "RED"};
+if (random 100 > 50) then {_team setBehaviour "AWARE"} else {_team setBehaviour "COMBAT"};
+if (random 100 > 50) then {_team setSpeedMode "NORMAL"} else {_team setSpeedMode "LIMITED"};
+
+//--- Dyn insert.
+_insertStep = if (count(_usable) != 0) then {floor(_maxWaypoints / count(_usable))} else {-1};
+_insert = _insertStep;
+_insertObject = objNull;
+_wpradius = -1;
+_wpcompletionRadius = -1;
+
+for [{_z=0},{_z<=_maxWaypoints},{_z=_z+1}] do {
+ if (_z == _insert && count _usable > 0) then {
+ _insert = _insert + _insertStep;
+ _insertObject = _usable select (round(random((count _usable)-1)));
+ _usable = _usable - [_insertObject];
+ };
+
+ if (isNull _insertObject) then {
+ _rand1 = random _radius;
+ _rand2 = random _radius;
+ _pos = [(_townPos select 0)+_rand1,(_townPos select 1)+_rand2,0];
+ _tries = 100;
+ while {surfaceIsWater _pos && _tries > 0} do {
+ _tries = _tries - 1;
+ _rand1 = random _radius;
+ _rand2 = random _radius;
+ _pos = [(_townPos select 0)+_rand1,(_townPos select 1)+_rand2,0];
+ };
+ _wpradius = 32;
+ _wpcompletionRadius = 44;
+ } else {
+ _pos = getPos _insertObject;
+ _wpradius = 35;
+ _wpcompletionRadius = 68;
+ };
+
+ _type = if (_z != _maxWaypoints) then {'MOVE'} else {'CYCLE'};
+ _wps = _wps + [[_pos,_type,_wpradius,_wpcompletionRadius, "", []]];
+};
+
+diag_log Format["[WFBE (INFORMATION)][frameno:%4 | ticktime:%5] AI_TownPatrol: The %1 %2 Team is patrolling the %3 town",side _team,_team,_town,diag_frameno,diag_tickTime];
+
+[_team, true, _wps] Call AIWPAdd; \ No newline at end of file
diff --git a/Server/AI/Orders/AI_WPAdd.sqf b/Server/AI/Orders/AI_WPAdd.sqf
new file mode 100644
index 0000000..ad04518
--- /dev/null
+++ b/Server/AI/Orders/AI_WPAdd.sqf
@@ -0,0 +1,39 @@
+/*
+ Author: Benny
+ Name: AI_WPAdd.sqf
+ Parameters:
+ 0 - Team
+ 1 - Clear (Remove WPs)
+ 2 - Waypoints (given in an Array)
+ Description:
+ This file is used to give a detailed WP system.
+ Exemple:
+ [_team, true, [[getPos _camp, 'MOVE', 10, 20, "", []],[[1560,2560,0], 'SAD', 50, 70, "", ["canComplete", "this sidechat 'lets roll'"]]...]] Call AddWP;
+*/
+
+Private ['_clear','_completionRadius','_position','_radius','_scripted','_statements','_team','_type','_waypoint','_waypoints','_WPCount'];
+_team = _this select 0;
+_clear = _this select 1;
+_waypoints = _this select 2;
+
+if (_clear) then {_team Call AIWPRemove};
+
+{
+ _position = _x select 0;
+ _type = _x select 1;
+ _radius = _x select 2;
+ _completionRadius = _x select 3;
+ _scripted = _x select 4;
+ _statements = _x select 5;
+ if (typeName _position == 'OBJECT') then {_position = getPos _position};
+
+ _WPCount = count (waypoints _team);
+
+ _team addWaypoint [_position,_radius];
+ [_team, _WPCount] setWaypointType _type;
+ [_team, _WPCount] setWaypointCompletionRadius _completionRadius;
+ if (_type == 'SCRIPTED') then {[_team, _WPCount] setWaypointScript _scripted};
+ if (count _statements > 0) then {[_team, _WPCount] setWaypointStatements [_statements select 0, _statements select 1]};
+
+ if (_WPCount == 0) then {_team setCurrentWaypoint [_team, _WPCount]};
+} forEach _waypoints; \ No newline at end of file
diff --git a/Server/AI/Orders/AI_WPRemove.sqf b/Server/AI/Orders/AI_WPRemove.sqf
new file mode 100644
index 0000000..a02a1e2
--- /dev/null
+++ b/Server/AI/Orders/AI_WPRemove.sqf
@@ -0,0 +1,6 @@
+Private ['_team','_z'];
+_team = _this;
+
+for [{_z = (count (waypoints _team))-1},{_z > -1},{_z = _z - 1}] do {
+ deleteWaypoint [_team, _z];
+}; \ No newline at end of file