Guest User

Untitled

a guest
Nov 10th, 2025
8
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 477.38 KB | None | 0 0
  1.  
  2.  
  3. ==================== START OF: AAF.sqf ====================
  4.  
  5. // REWORKED: Handles dynamic AAF patrol spawning.
  6. // Also manages initial AAF garrisons at strategic locations.
  7.  
  8. // AAF squads are now limited to only bandit compositions.
  9. AAF_SQUAD_COMPOSITIONS = [
  10. // Bandit squads
  11. ["I_C_Soldier_Bandit_2_F", "I_C_Soldier_Bandit_1_F", "I_C_Soldier_Bandit_4_F", "I_C_Soldier_Bandit_5_F", "I_C_Soldier_Bandit_3_F", "I_C_Soldier_Bandit_8_F"],
  12. ["I_C_Soldier_Bandit_6_F", "I_C_Soldier_Bandit_1_F", "I_C_Soldier_Bandit_4_F", "I_C_Soldier_Bandit_4_F", "I_C_Soldier_Bandit_2_F", "I_C_Soldier_Bandit_5_F"],
  13. ["I_C_Soldier_Bandit_7_F", "I_C_Soldier_Bandit_3_F", "I_C_Soldier_Bandit_4_F", "I_C_Soldier_Bandit_1_F", "I_C_Soldier_Bandit_2_F"]
  14. ];
  15.  
  16. // New function to make AAF groups patrol and hunt in their assigned location
  17. fnc_setAAFHuntPatrol = {
  18. params ["_group", "_centerPos"];
  19.  
  20. // Clear any existing waypoints from the group
  21. while {count (waypoints _group) > 0} do {
  22. deleteWaypoint ((waypoints _group) select 0);
  23. };
  24.  
  25. private _patrolRadius = 500; // Patrol radius around the location center
  26.  
  27. // Find a random safe position within the patrol radius
  28. private _patrolPos = [_centerPos, 50, _patrolRadius, 10, 0, 0.3, 0] call BIS_fnc_findSafePos;
  29. if (count _patrolPos == 0) then { _patrolPos = _centerPos; }; // Fallback to center if no safe pos found
  30.  
  31. // Create a "Search and Destroy" waypoint
  32. private _wp = _group addWaypoint [_patrolPos, 0];
  33. _wp setWaypointType "SAD";
  34. _wp setWaypointBehaviour "COMBAT";
  35. _wp setWaypointSpeed "NORMAL";
  36. _wp setWaypointCompletionRadius 100;
  37.  
  38. // When the waypoint is completed, call this function again to get a new random patrol point
  39. // This makes the group patrol its assigned area indefinitely.
  40. _wp setWaypointStatements ["true", format["
  41. private _group = group this;
  42. if (!isNull _group && count (units _group) > 0) then {
  43. [_group, %1] call fnc_setAAFHuntPatrol;
  44. };
  45. ", _centerPos]];
  46. };
  47.  
  48. // ==================== CHANGE START ====================
  49. // This function now specifically handles the INITIAL AAF patrol at mission start.
  50. fnc_spawnAAFForces = {
  51. // IMPROVED: Wait for mission_centralPoint to exist AND be valid (not default fallback)
  52. waitUntil {
  53. !isNil "mission_centralPoint" &&
  54. {
  55. private _pos = missionNamespace getVariable ["mission_centralPoint", [0,0,0]];
  56. !(_pos isEqualTo [0,0,0]) &&
  57. !(_pos isEqualTo [worldSize/2, worldSize/2, 0]) &&
  58. (_pos select 0) > 0 &&
  59. (_pos select 1) > 0
  60. }
  61. };
  62.  
  63. // IMPROVED: Additional delay to ensure base objects are fully initialized
  64. sleep 3; // Increased from 2 to 3 seconds
  65.  
  66. private _aafTargetSize = AAF_START_SIZE;
  67. if (_aafTargetSize <= 0) exitWith {};
  68.  
  69. private _centerPos = missionNamespace getVariable ["mission_centralPoint", [worldSize/2, worldSize/2, 0]];
  70.  
  71. // IMPROVED: Comprehensive position validation with multiple checks
  72. private _positionValid = true;
  73.  
  74. // Check 1: Not default fallback positions
  75. if (_centerPos isEqualTo [worldSize/2, worldSize/2, 0] || _centerPos isEqualTo [0,0,0]) then {
  76. _positionValid = false;
  77. diag_log format ["ERROR: AAF spawn position is default fallback: %1", _centerPos];
  78. };
  79.  
  80. // Check 2: Coordinates must be positive and within world bounds
  81. if ((_centerPos select 0) <= 0 || (_centerPos select 1) <= 0) then {
  82. _positionValid = false;
  83. diag_log format ["ERROR: AAF spawn position has invalid coordinates: %1", _centerPos];
  84. };
  85.  
  86. if ((_centerPos select 0) >= worldSize || (_centerPos select 1) >= worldSize) then {
  87. _positionValid = false;
  88. diag_log format ["ERROR: AAF spawn position exceeds world bounds: %1", _centerPos];
  89. };
  90.  
  91. // Check 3: Position must not be in water
  92. if (surfaceIsWater _centerPos) then {
  93. _positionValid = false;
  94. diag_log format ["ERROR: AAF spawn position is in water: %1", _centerPos];
  95. };
  96.  
  97. // IMPROVED: If position validation fails completely, abort and log
  98. if (!_positionValid) exitWith {
  99. diag_log format ["CRITICAL ERROR: AAF spawn aborted due to invalid mission_centralPoint: %1", _centerPos];
  100. systemChat "ERROR: AAF forces could not spawn - invalid stronghold position detected";
  101. };
  102.  
  103. // IMPROVED: Log the validated position being used
  104. diag_log format ["AAF spawning at validated mission_centralPoint: %1", _centerPos];
  105.  
  106. // Force Z to ground level for spawning (existing fix)
  107. _centerPos set [2, 0];
  108.  
  109. private _allPatrolGroups = [];
  110. private _spawnedCount = 0;
  111.  
  112. // Create a marker for the AAF Stronghold
  113. private _marker = createMarker ["aaf_stronghold_marker", _centerPos];
  114. _marker setMarkerType "mil_flag";
  115. _marker setMarkerColor "ColorGreen";
  116. _marker setMarkerText "AAF Stronghold";
  117.  
  118. while {_spawnedCount < _aafTargetSize} do {
  119. private _group = createGroup independent;
  120. private _squadComp = selectRandom AAF_SQUAD_COMPOSITIONS;
  121.  
  122. if ((_spawnedCount + count _squadComp) > _aafTargetSize) then {
  123. _squadComp = _squadComp select [0, _aafTargetSize - _spawnedCount];
  124. };
  125. if (count _squadComp == 0) then { break; };
  126.  
  127. for "_i" from 0 to (count _squadComp - 1) do {
  128. private _unitType = _squadComp select _i;
  129.  
  130. // Tighter spawn radius to keep units closer to the stronghold center.
  131. private _spawnPos = [_centerPos, 10, 75, 15, 0, 0.3, 0] call BIS_fnc_findSafePos;
  132.  
  133. // NEW: Robust sanity check and multi-stage fallback for spawn position.
  134. // This prevents units spawning far away if findSafePos fails or returns an invalid location.
  135. if (count _spawnPos == 0 || _spawnPos isEqualTo [0,0,0] || _spawnPos distance2D _centerPos > 200) then {
  136.  
  137. // STAGE 1 FALLBACK: Try again with a very tight radius.
  138. _spawnPos = [_centerPos, 0, 20, 5, 0, 0, 0] call BIS_fnc_findSafePos;
  139. diag_log format ["WARNING: findSafePos failed for AAF unit. Attempting fallback stage 1..."];
  140.  
  141. // STAGE 2 FALLBACK: If the tight search also fails, use a simple random offset as a last resort.
  142. if (count _spawnPos == 0 || _spawnPos isEqualTo [0,0,0]) then {
  143. _spawnPos = [
  144. (_centerPos select 0) + (random 20 - 10),
  145. (_centerPos select 1) + (random 20 - 10),
  146. 0
  147. ];
  148. diag_log format ["WARNING: Fallback stage 1 failed. Using final offset position: %1", _spawnPos];
  149. };
  150. };
  151.  
  152. private _unit = _group createUnit [_unitType, _spawnPos, [], 10, "NONE"];
  153. _unit setVariable ["isAAF", true, true];
  154. [_unit, 0.05, "REGULAR"] call fnc_enhanceIndividualAI;
  155. _spawnedCount = _spawnedCount + 1;
  156. };
  157.  
  158. // Make the newly created group patrol the central city area.
  159. [_group, _centerPos] call fnc_setAAFHuntPatrol;
  160.  
  161. _allPatrolGroups pushBack _group;
  162. };
  163.  
  164. independent setFriend [west, 0];
  165. independent setFriend [east, 0];
  166. west setFriend [independent, 0];
  167. east setFriend [independent, 0];
  168.  
  169. missionNamespace setVariable ["AAF_Groups", _allPatrolGroups, true];
  170.  
  171. // IMPROVED: Final confirmation log with unit count verification
  172. diag_log format ["AAF spawn complete: %1 units spawned at %2 in %3 groups", _spawnedCount, _centerPos, count _allPatrolGroups];
  173.  
  174. // IMPROVED: Verify all groups have units
  175. {
  176. private _groupUnits = units _x select {alive _x};
  177. if (count _groupUnits == 0) then {
  178. diag_log format ["WARNING: AAF group %1 is empty after spawn", groupId _x];
  179. };
  180. } forEach _allPatrolGroups;
  181. };
  182. // ===================== CHANGE END =====================
  183.  
  184. if (isServer) then {
  185. [] spawn {
  186. // FIXED: Wait for mission setup to complete first, then wait for player teams
  187. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj" && !isNil "mission_centralPoint"};
  188. waitUntil {({side _x in [west, east]} count allPlayers > 0)};
  189. sleep 5;
  190. [] call fnc_spawnAAFForces;
  191. };
  192. };
  193.  
  194. // ==================== CHANGE START ====================
  195. // NEW: Standalone server-side loop to apply foliage/vision checks to AAF units.
  196. // This gives them smarter engagement logic without adding all the complex behaviors from botai.sqf.
  197. if (isServer) then {
  198. [] spawn {
  199. // Wait for AAF forces to be initialized
  200. waitUntil {!isNil "AAF_Groups"};
  201.  
  202. while {true} do {
  203. // Get all alive AAF units on the map
  204. private _allAAFUnits = allUnits select {alive _x && (_x getVariable ["isAAF", false])};
  205.  
  206. {
  207. private _unit = _x;
  208. // AAF are hostile to both BLUFOR and OPFOR
  209. private _enemySides = [west, east];
  210.  
  211. // Check for nearby targets
  212. private _nearTargets = _unit nearTargets 300; // Check targets within 300m
  213.  
  214. {
  215. private _targetInfo = _x;
  216. private _target = _targetInfo select 4;
  217.  
  218. // Check if the target is a valid enemy
  219. if (alive _target && (side _target in _enemySides)) then {
  220. // Use the global function from botai.sqf to check for obstruction
  221. private _obstructionLevel = [_unit, _target] call fnc_checkFoliageObstruction;
  222.  
  223. // If view is obstructed, reduce the AI's knowledge of the target.
  224. // This prevents them from shooting through dense bushes and trees.
  225. if (_obstructionLevel > 0.1) then {
  226. (group _unit) reveal [_target, 1.5]; // 1.5 = Aware, but not 100% sure, won't fire
  227. } else {
  228. // If view is clear, ensure the AI has full knowledge to engage.
  229. (group _unit) reveal [_target, 4]; // 4 = Knows for sure, will fire
  230. };
  231. };
  232. } forEach _nearTargets;
  233.  
  234. } forEach _allAAFUnits;
  235.  
  236. // Check every 3-5 seconds to balance performance and responsiveness
  237. sleep (3 + random 2);
  238. };
  239. };
  240. };
  241. // ===================== CHANGE END =====================
  242.  
  243. ==================== END OF: AAF.sqf ====================
  244.  
  245.  
  246.  
  247. ==================== START OF: AIstuff.sqf ====================
  248.  
  249. // AIstuff.sqf
  250.  
  251. // AI Unit Pools
  252. BLUFOR_INFANTRY_POOL = [
  253. ["B_Soldier_TL_F", "B_Soldier_F", "B_Soldier_LAT_F", "B_Soldier_AA_F", "B_medic_F", "B_Soldier_AR_F"],
  254. ["B_Soldier_SL_F", "B_Soldier_F", "B_Soldier_AT_F", "B_Soldier_AA_F", "B_medic_F", "B_engineer_F"],
  255. ["B_Soldier_TL_F", "B_Soldier_F", "B_Soldier_LAT_F", "B_Soldier_AA_F", "B_HeavyGunner_F", "B_Soldier_A_F"],
  256. ["B_Soldier_TL_F", "B_Soldier_F", "B_Soldier_AT_F", "B_Soldier_AA_F", "B_Soldier_AR_F", "B_Soldier_A_F"],
  257. ["B_Soldier_SL_F", "B_Soldier_LAT_F", "B_Soldier_AA_F", "B_medic_F", "B_Soldier_AR_F", "B_engineer_F"]
  258. ];
  259. OPFOR_INFANTRY_POOL = [
  260. ["O_Soldier_TL_F", "O_Soldier_F", "O_Soldier_LAT_F", "O_Soldier_AA_F", "O_medic_F", "O_Soldier_AR_F"],
  261. ["O_Soldier_SL_F", "O_Soldier_F", "O_Soldier_AT_F", "O_Soldier_AA_F", "O_medic_F", "O_engineer_F"],
  262. ["O_Soldier_TL_F", "O_Soldier_F", "O_Soldier_LAT_F", "O_Soldier_AA_F", "O_HeavyGunner_F", "O_Soldier_A_F"],
  263. ["O_Soldier_TL_F", "O_Soldier_F", "O_Soldier_AT_F", "O_Soldier_AA_F", "O_Soldier_AR_F", "O_Soldier_A_F"],
  264. ["O_Soldier_SL_F", "O_Soldier_LAT_F", "O_Soldier_AA_F", "O_medic_F", "O_Soldier_AR_F", "O_engineer_F"]
  265. ];
  266. BLUFOR_SPECOPS_POOL = [
  267. ["B_recon_TL_F", "B_recon_medic_F", "B_recon_LAT_F", "B_recon_exp_F", "B_recon_M_F", "B_recon_F"],
  268. ["B_recon_TL_F", "B_recon_medic_F", "B_recon_LAT_F", "B_recon_JTAC_F", "B_recon_F", "B_recon_F"]
  269. ];
  270. OPFOR_SPECOPS_POOL = [
  271. ["O_recon_TL_F", "O_recon_medic_F", "O_recon_LAT_F", "O_recon_exp_F", "O_recon_M_F", "O_recon_F"],
  272. ["O_recon_TL_F", "O_recon_medic_F", "O_recon_LAT_F", "O_recon_JTAC_F", "O_recon_F", "O_recon_F"]
  273. ];
  274. BLUFOR_ELITE_POOL = [
  275. ["B_CTRG_Soldier_TL_tna_F", "B_CTRG_Soldier_AR_tna_F", "B_CTRG_Soldier_LAT_tna_F", "B_CTRG_Soldier_Medic_tna_F", "B_CTRG_Soldier_M_tna_F"]
  276. ];
  277. OPFOR_ELITE_POOL = [
  278. ["O_V_Soldier_TL_hex_F", "O_V_Soldier_M_hex_F", "O_V_Soldier_LAT_hex_F", "O_V_Soldier_Medic_hex_F", "O_V_Soldier_Exp_hex_F"]
  279. ];
  280. BLUFOR_UPGRADED_POOL = [
  281. ["B_Patrol_Soldier_TL_F", "B_Patrol_Soldier_AR_F", "B_Patrol_Medic_F", "B_Patrol_HeavyGunner_F", "B_Patrol_Soldier_M_F", "B_Patrol_Soldier_AT_F"],
  282. ["B_Patrol_Soldier_TL_F", "B_Patrol_Soldier_AR_F", "B_Patrol_Medic_F", "B_Patrol_Soldier_AR_F", "B_Patrol_Soldier_M_F", "B_Patrol_Soldier_AT_F"]
  283. ];
  284. OPFOR_UPGRADED_POOL = [
  285. ["O_R_Patrol_Soldier_TL_F", "O_R_Patrol_Soldier_AR2_F", "O_R_Patrol_Soldier_Medic", "O_R_Patrol_Soldier_AR_F", "O_R_Patrol_Soldier_M_F", "O_R_Patrol_Soldier_LAT_F"],
  286. ["O_R_Patrol_Soldier_TL_F", "O_R_Patrol_Soldier_AR_F", "O_R_Patrol_Soldier_Medic", "O_R_Patrol_Soldier_AR_F", "O_R_Patrol_Soldier_M_F", "O_R_Patrol_Soldier_LAT_F"]
  287. ];
  288.  
  289. // Vehicle Pools - Light Vehicles (APCs)
  290. BLUFOR_LIGHT_VEHICLE_POOL = [
  291. "B_APC_Wheeled_01_cannon_F" // AMV-7 Marshall
  292. ];
  293. OPFOR_LIGHT_VEHICLE_POOL = [
  294. "O_APC_Wheeled_02_rcws_v2_F" // MSE-3 Marid
  295. ];
  296.  
  297. // Vehicle Pools - Main Battle Tanks
  298. BLUFOR_TANK_POOL = [
  299. "B_MBT_01_TUSK_F" // M2A4 Slammer UP
  300. ];
  301. OPFOR_TANK_POOL = [
  302. "O_MBT_02_cannon_F" // T-100 Varsuk
  303. ];
  304.  
  305. // Vehicle Pools - Attack Helicopters
  306. BLUFOR_ATTACK_HELI_POOL = [
  307. "B_Heli_Attack_01_F" // AH-99 Blackfoot
  308. ];
  309. OPFOR_ATTACK_HELI_POOL = [
  310. "O_Heli_Attack_01_F" // Mi-48 Kajman
  311. ];
  312.  
  313.  
  314. // Vehicle tracking (runtime state variables)
  315. if (isNil "BLUFOR_ACTIVE_LIGHT_VEHICLES") then { BLUFOR_ACTIVE_LIGHT_VEHICLES = []; };
  316. if (isNil "OPFOR_ACTIVE_LIGHT_VEHICLES") then { OPFOR_ACTIVE_LIGHT_VEHICLES = []; };
  317. if (isNil "BLUFOR_ACTIVE_TANKS") then { BLUFOR_ACTIVE_TANKS = []; };
  318. if (isNil "OPFOR_ACTIVE_TANKS") then { OPFOR_ACTIVE_TANKS = []; };
  319. if (isNil "BLUFOR_ACTIVE_ATTACK_HELIS") then { BLUFOR_ACTIVE_ATTACK_HELIS = []; };
  320. if (isNil "OPFOR_ACTIVE_ATTACK_HELIS") then { OPFOR_ACTIVE_ATTACK_HELIS = []; };
  321.  
  322. // ==================== CHANGE START ====================
  323. // NEW: Finds a safe spawn position in a 600m radius around the base, avoiding enemies.
  324. fnc_getSafeSpawnPosition = {
  325. params ["_side"];
  326.  
  327. private _baseObj = if (_side == west) then { bluforSpawnObj } else { opforSpawnObj };
  328. if (isNil "_baseObj") exitWith {[0,0,0]}; // Should not happen
  329.  
  330. private _basePos = getPos _baseObj;
  331. private _enemySide = if (_side == west) then { east } else { west };
  332. private _spawnRadius = 500;
  333. private _enemyBuffer = 50; // Minimum distance from an enemy
  334.  
  335. private _finalPos = [];
  336.  
  337. // Try up to 20 times to find a suitable position
  338. for "_i" from 1 to 20 do {
  339. // Find a random, safe position within the base radius
  340. private _potentialPos = [_basePos, 10, _spawnRadius, 10, 0, 0.3, 0] call BIS_fnc_findSafePos;
  341.  
  342. if (count _potentialPos > 0) then {
  343. // Check for nearby enemies at the potential spawn point
  344. private _isNearEnemy = false;
  345. _nearby = _potentialPos nearEntities ["AllVehicles", _enemyBuffer];
  346.  
  347. // Use findIf for efficiency; it stops once an enemy is found
  348. if ((_nearby findIf {side _x == _enemySide && alive _x}) != -1) then {
  349. _isNearEnemy = true;
  350. };
  351.  
  352. // If no enemies are nearby, this position is valid
  353. if (!_isNearEnemy) then {
  354. _finalPos = _potentialPos;
  355. break; // Exit the loop
  356. };
  357. };
  358. };
  359.  
  360. // If a valid position was found, return it. Otherwise, fallback to the base center.
  361. if (count _finalPos > 0) then {
  362. _finalPos
  363. } else {
  364. _basePos
  365. }
  366. };
  367. // ===================== CHANGE END =====================
  368.  
  369. // AI Helper Functions
  370. fnc_updateStrengthTally = {
  371. params ["_entity", "_side"];
  372. if (!isServer) exitWith {};
  373.  
  374. private _unitStrength = 1; // Default strength
  375.  
  376. // Determine strength based on type
  377. if (_entity isKindOf "CAManBase") then {
  378. if (_entity getVariable ["isElite", false]) then { _unitStrength = 5; };
  379. if (_entity getVariable ["isSpecOps", false]) then { _unitStrength = 3; };
  380. if (_entity getVariable ["isSniper", false]) then { _unitStrength = 2; };
  381. } else {
  382. if (_entity isKindOf "Tank") then { _unitStrength = 15; };
  383. if (_entity isKindOf "APC") then { _unitStrength = 8; };
  384. if (_entity isKindOf "Air") then { _unitStrength = 20; };
  385. if (_entity isKindOf "Car" || _entity isKindOf "Ship") then { _unitStrength = 3; }; // For light vehicles etc.
  386. };
  387.  
  388. // Store the calculated strength on the unit/vehicle for later use (on death)
  389. _entity setVariable ["strengthValue", _unitStrength, true];
  390.  
  391. // Add the strength to the correct side's tally
  392. if (_side == west) then {
  393. BLUFOR_STRENGTH = BLUFOR_STRENGTH + _unitStrength;
  394. } else {
  395. OPFOR_STRENGTH = OPFOR_STRENGTH + _unitStrength;
  396. };
  397. };
  398.  
  399. fnc_markAsImportant = {
  400. params ["_entity"];
  401. _entity enableDynamicSimulation false;
  402. _entity setVariable ["gcImportant", true, true];
  403. if (_entity isKindOf "AllVehicles" && !(_entity isKindOf "CAManBase")) then {
  404. {_x enableDynamicSimulation false; _x setVariable ["gcImportant", true, true];} forEach (crew _entity);
  405. };
  406. };
  407.  
  408. fnc_getPatrolPosition = {
  409. params ["_centerPos", "_minRadius", "_maxRadius"];
  410.  
  411. // Validate centerPos is an array with at least 2 elements
  412. if (typeName _centerPos != "ARRAY" || count _centerPos < 2) exitWith {
  413. [0, 0, 0]
  414. };
  415.  
  416. private _searchRadius = _maxRadius;
  417. private _coverPositions = selectBestPlaces [_centerPos, _searchRadius, "forest + houses", 10, 5];
  418. private _landCoverPositions = _coverPositions select { !surfaceIsWater (_x select 0) };
  419. private _patrolPos = [];
  420. if (count _landCoverPositions > 0) then {
  421. _patrolPos = selectRandom _landCoverPositions select 0;
  422. } else {
  423. // Fallback to original method
  424. private _distance = _minRadius + (random (_maxRadius - _minRadius));
  425. private _direction = random 360;
  426. private _potentialPos = [(_centerPos select 0) + (_distance * cos _direction), (_centerPos select 1) + (_distance * sin _direction), 0];
  427. _patrolPos = [_potentialPos, 0, _maxRadius, 10, 0, 0.3, 0] call BIS_fnc_findSafePos;
  428. if (count _patrolPos == 0) then { _patrolPos = _centerPos; };
  429. };
  430. _patrolPos
  431. };
  432.  
  433. fnc_getBattleHotspot = {
  434. params ["_side"];
  435.  
  436. private _intel = if (_side == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  437. private _enemyBase = if (_side == west) then {getPos opforSpawnObj} else {getPos bluforSpawnObj};
  438. private _targetPos = _enemyBase; // Default target is enemy base
  439.  
  440. private _enemyPositions = [];
  441. {
  442. // Consider intel from the last 3 minutes (180s) to be relevant for a hotspot
  443. if (time - (_y get "time") < 180) then {
  444. _enemyPositions pushBack (_y get "position");
  445. };
  446. } forEach _intel;
  447.  
  448. // If we have recent intel, find the largest concentration of enemies
  449. if (count _enemyPositions > 0) then {
  450. // Use the globally available clustering function from highcommand.sqf
  451. private _clusters = [_enemyPositions, 400] call fnc_clusterPositions;
  452. if (count _clusters > 0) then {
  453. // Find the largest cluster
  454. _clusters = [_clusters, [], {count _x}, "DESCEND"] call BIS_fnc_sortBy;
  455. private _largestCluster = _clusters select 0;
  456.  
  457. // Calculate the center of the largest cluster to use as the target
  458. private _avgX = 0; private _avgY = 0;
  459. { _avgX = _avgX + (_x select 0); _avgY = _avgY + (_x select 1); } forEach _largestCluster;
  460. _targetPos = [_avgX / count _largestCluster, _avgY / count _largestCluster, 0];
  461. };
  462. };
  463.  
  464. _targetPos // Return the calculated hotspot or the enemy base as a fallback
  465. };
  466.  
  467. // ==================== OPTIMIZED FUNCTION ====================
  468. fnc_getRandomWaveType = {
  469. params ["_availablePoints", "_side"];
  470. private [
  471. "_waveHistory", "_affordableWaves", "_waveType", "_weight",
  472. "_canAfford", "_wasRecentlyUsed", "_totalWeight",
  473. "_random", "_currentWeight", "_selectedWave"
  474. ];
  475.  
  476. _waveHistory = if (_side == west) then {BLUFOR_LAST_WAVES} else {OPFOR_LAST_WAVES};
  477. _affordableWaves = [];
  478.  
  479. // --- OPTIMIZATION: Pre-calculate all vehicle counts once ---
  480. private _aliveLightVehicles = 0;
  481. private _aliveTanks = 0;
  482. private _aliveHelis = 0;
  483.  
  484. if (_side == west) then {
  485. _aliveLightVehicles = count (BLUFOR_ACTIVE_LIGHT_VEHICLES select {!isNull _x && alive _x});
  486. _aliveTanks = count (BLUFOR_ACTIVE_TANKS select {!isNull _x && alive _x});
  487. _aliveHelis = count (BLUFOR_ACTIVE_ATTACK_HELIS select {!isNull _x && alive _x});
  488. } else {
  489. _aliveLightVehicles = count (OPFOR_ACTIVE_LIGHT_VEHICLES select {!isNull _x && alive _x});
  490. _aliveTanks = count (OPFOR_ACTIVE_TANKS select {!isNull _x && alive _x});
  491. _aliveHelis = count (OPFOR_ACTIVE_ATTACK_HELIS select {!isNull _x && alive _x});
  492. };
  493. // --- END OPTIMIZATION ---
  494.  
  495. {
  496. _waveType = _x select 0;
  497. _weight = _x select 1;
  498. _canAfford = false;
  499.  
  500. switch (_waveType) do {
  501. case "infantry": {if (!isNil "INFANTRY_VALUE") then {_canAfford = (_availablePoints >= (2 * 6 * INFANTRY_VALUE));};};
  502. case "upgraded": {if (!isNil "UPGRADED_VALUE") then {_canAfford = (_availablePoints >= (2 * 6 * UPGRADED_VALUE));};};
  503. case "specops": {if (!isNil "SPECOPS_VALUE") then {_canAfford = (_availablePoints >= (2 * 6 * SPECOPS_VALUE));};};
  504. case "sniper": {if (!isNil "SNIPER_VALUE") then {_canAfford = (_availablePoints >= (2 * 2 * SNIPER_VALUE));};};
  505. case "elite": {if (!isNil "ELITE_VALUE") then {_canAfford = (_availablePoints >= (2 * 5 * ELITE_VALUE));};};
  506. case "light_vehicle": {
  507. if (!isNil "VEHICLE_VALUE" && !isNil "maxLightVehiclesPerSide") then {
  508. _canAfford = (time > 600) && (_availablePoints >= (2 * VEHICLE_VALUE)) && (_aliveLightVehicles < maxLightVehiclesPerSide);
  509. };
  510. };
  511. case "tank": {
  512. if (!isNil "TANK_VALUE" && !isNil "maxTanksPerSide") then {
  513. _canAfford = (time > 1200) && (_availablePoints >= (2 * TANK_VALUE)) && (_aliveTanks < maxTanksPerSide);
  514. };
  515. };
  516. case "attack_heli": {
  517. if (!isNil "ATTACK_HELI_VALUE" && !isNil "maxAttackHelisPerSide") then {
  518. _canAfford = (time > 1200) && (_availablePoints >= (2 * ATTACK_HELI_VALUE)) && (_aliveHelis < maxAttackHelisPerSide);
  519. };
  520. };
  521. };
  522.  
  523. if (_canAfford) then {
  524. _wasRecentlyUsed = _waveType in _waveHistory;
  525. if (_wasRecentlyUsed) then {
  526. _weight = _weight * 0.2;
  527. } else {
  528. _weight = _weight * 1.5;
  529. };
  530. _affordableWaves pushBack [_waveType, _weight];
  531. };
  532. } forEach WAVE_WEIGHTS;
  533.  
  534. if (count _affordableWaves == 0) exitWith {nil};
  535.  
  536. _totalWeight = 0;
  537. {_totalWeight = _totalWeight + (_x select 1);} forEach _affordableWaves;
  538.  
  539. _random = random _totalWeight;
  540. _currentWeight = 0;
  541. _selectedWave = "infantry";
  542.  
  543. {
  544. _currentWeight = _currentWeight + (_x select 1);
  545. if (_random <= _currentWeight) exitWith { _selectedWave = _x select 0; };
  546. } forEach _affordableWaves;
  547.  
  548. _selectedWave
  549. };
  550.  
  551. // AI Patrol Functions
  552. fnc_createPatrolWaypoints = {
  553. params ["_group", "_enemyBasePos", ["_behavior", "AWARE"], ["_formation", "LINE"]];
  554.  
  555. while {count (waypoints _group) > 0} do {
  556. deleteWaypoint ((waypoints _group) select 0);
  557. };
  558.  
  559. // First waypoint: Move closer to enemy base (reduced minimum distance from 500 to 200)
  560. private _patrolPos = [_enemyBasePos, 200, PATROL_RADIUS] call fnc_getPatrolPosition;
  561.  
  562. private _wp = _group addWaypoint [_patrolPos, 50];
  563. _wp setWaypointType "MOVE";
  564. _wp setWaypointStatements ["true", "{_x setUnitPos 'UP';} forEach units group this;"];
  565. _wp setWaypointBehaviour _behavior;
  566. _wp setWaypointSpeed "NORMAL";
  567. _wp setWaypointFormation _formation;
  568. _wp setWaypointCombatMode "YELLOW";
  569.  
  570. // Add enemy base position directly as a SAD waypoint
  571. _wp = _group addWaypoint [_enemyBasePos, 100];
  572. _wp setWaypointType "SAD";
  573. _wp setWaypointBehaviour "COMBAT";
  574. _wp setWaypointSpeed "NORMAL";
  575. _wp setWaypointFormation _formation;
  576. _wp setWaypointCombatMode "RED";
  577. _wp setWaypointCompletionRadius 150;
  578.  
  579. // Additional patrol points around enemy base (reduced minimum from 300 to 100)
  580. for "_i" from 1 to 2 do {
  581. _patrolPos = [_enemyBasePos, 100, PATROL_RADIUS] call fnc_getPatrolPosition;
  582. _wp = _group addWaypoint [_patrolPos, 75];
  583. _wp setWaypointType "SAD";
  584. _wp setWaypointBehaviour "AWARE";
  585. _wp setWaypointSpeed "NORMAL";
  586. _wp setWaypointFormation _formation;
  587. _wp setWaypointCombatMode "RED";
  588. _wp setWaypointCompletionRadius 100;
  589. };
  590.  
  591. if (count (waypoints _group) > 1) then {
  592. _wp = _group addWaypoint [waypointPosition [_group, 1], 0];
  593. _wp setWaypointType "CYCLE";
  594. };
  595.  
  596. _group setBehaviour _behavior;
  597. _group setCombatMode "RED";
  598. _group setSpeedMode "NORMAL";
  599. };
  600.  
  601. fnc_createVehiclePatrolWaypoints = {
  602. params ["_group", "_enemyBasePos", "_ownBasePos"];
  603.  
  604. while {count (waypoints _group) > 0} do {
  605. deleteWaypoint ((waypoints _group) select 0);
  606. };
  607.  
  608. private _vehiclePatrolRadius = PATROL_RADIUS * 1.5;
  609.  
  610. for "_i" from 0 to 3 do {
  611. private _patrolPos = [_enemyBasePos, 200, _vehiclePatrolRadius, 10, 0, 0.4, 0] call BIS_fnc_findSafePos;
  612.  
  613. // MODIFIED: Robust fallback to prevent water waypoints.
  614. if (count _patrolPos == 0) then {
  615. // Try up to 10 times to find a random land position.
  616. for "_j" from 0 to 9 do {
  617. private _dir = random 360;
  618. private _dist = 200 + random (_vehiclePatrolRadius - 200);
  619. private _potentialPos = _enemyBasePos getPos [_dist, _dir];
  620. if (!surfaceIsWater _potentialPos) then {
  621. _patrolPos = _potentialPos;
  622. break;
  623. };
  624. };
  625. // If still no valid position, fallback to the base itself.
  626. if (count _patrolPos == 0) then {
  627. _patrolPos = _enemyBasePos;
  628. };
  629. };
  630.  
  631. private _wp = _group addWaypoint [_patrolPos, 50];
  632. _wp setWaypointType "SAD";
  633. _wp setWaypointBehaviour "AWARE";
  634. _wp setWaypointSpeed "NORMAL";
  635. _wp setWaypointCombatMode "RED";
  636. _wp setWaypointCompletionRadius 150;
  637. };
  638.  
  639. if (count (waypoints _group) > 0) then {
  640. private _wpCycle = _group addWaypoint [waypointPosition [_group, 0], 0];
  641. _wpCycle setWaypointType "CYCLE";
  642. };
  643.  
  644. _group setBehaviour "AWARE";
  645. _group setCombatMode "YELLOW";
  646. _group setSpeedMode "NORMAL";
  647. };
  648.  
  649. // Orders helicopters to patrol and attack targets from a safe altitude.
  650. fnc_createHeliPatrolWaypoints = {
  651. params ["_group", "_enemyBasePos"];
  652.  
  653. while {count (waypoints _group) > 0} do {
  654. deleteWaypoint ((waypoints _group) select 0);
  655. };
  656.  
  657. private _patrolRadius = PATROL_RADIUS * 3; // Larger radius for air patrols
  658. private _patrolAltitude = 150; // Fly 150m above ground
  659.  
  660. for "_i" from 0 to 3 do {
  661. // Find a random position in the patrol area
  662. private _patrolPosGround = [_enemyBasePos, 500, _patrolRadius] call fnc_getPatrolPosition;
  663. private _patrolPosAir = _patrolPosGround vectorAdd [0,0,_patrolAltitude];
  664.  
  665. private _wp = _group addWaypoint [_patrolPosAir, 50];
  666. _wp setWaypointType "SAD"; // Search and Destroy
  667. _wp setWaypointBehaviour "COMBAT"; // Immediately engage enemies
  668. _wp setWaypointSpeed "NORMAL";
  669. _wp setWaypointCombatMode "RED";
  670. _wp setWaypointCompletionRadius 300; // Larger radius to engage targets
  671. };
  672.  
  673. // Cycle waypoints for continuous patrol
  674. if (count (waypoints _group) > 0) then {
  675. private _wpCycle = _group addWaypoint [waypointPosition [_group, 0], 0];
  676. _wpCycle setWaypointType "CYCLE";
  677. };
  678.  
  679. // Set group combat parameters
  680. _group setBehaviour "COMBAT";
  681. _group setCombatMode "RED";
  682. _group setSpeedMode "NORMAL";
  683. };
  684.  
  685. // Orders vehicles to a defensive staging area near their own base to await HC orders.
  686. fnc_createVehicleStagingWaypoints = {
  687. params ["_group", "_ownBasePos"];
  688.  
  689. while {count (waypoints _group) > 0} do {
  690. deleteWaypoint ((waypoints _group) select 0);
  691. };
  692.  
  693. // Staging area radius, around own base
  694. private _stagingRadius = 600;
  695.  
  696. // Find a good defensive position near the base
  697. private _stagingPos = [_ownBasePos, 300, _stagingRadius, 10, 0, 0.4, 0] call BIS_fnc_findSafePos;
  698.  
  699. // MODIFIED: Robust fallback to prevent water waypoints.
  700. if (count _stagingPos == 0) then {
  701. // Try up to 10 times to find a random land position.
  702. for "_j" from 0 to 9 do {
  703. private _dir = random 360;
  704. private _dist = 300 + random (_stagingRadius - 300);
  705. private _potentialPos = _ownBasePos getPos [_dist, _dir];
  706. if (!surfaceIsWater _potentialPos) then {
  707. _stagingPos = _potentialPos;
  708. break;
  709. };
  710. };
  711. // If still no valid position, fallback to the base itself.
  712. if (count _stagingPos == 0) then {
  713. _stagingPos = _ownBasePos;
  714. };
  715. };
  716.  
  717. // Move to staging position
  718. private _wp = _group addWaypoint [_stagingPos, 50];
  719. _wp setWaypointType "MOVE";
  720. _wp setWaypointBehaviour "AWARE";
  721. _wp setWaypointSpeed "NORMAL";
  722. _wp setWaypointCombatMode "YELLOW"; // Be cautious
  723. _wp setWaypointCompletionRadius 150;
  724.  
  725. // After arriving, patrol the staging area defensively
  726. private _wp2 = _group addWaypoint [_stagingPos, 50];
  727. _wp2 setWaypointType "SAD"; // Search and Destroy in the local area
  728. _wp2 setWaypointBehaviour "AWARE";
  729. _wp2 setWaypointSpeed "NORMAL";
  730. _wp2 setWaypointCombatMode "RED"; // Engage threats
  731. _wp2 setWaypointCompletionRadius 200; // Large radius for defensive scan
  732.  
  733. // Create a small patrol loop in the staging area
  734. private _patrolPos2 = [_ownBasePos, 300, _stagingRadius, 10, 0, 0.4, 0] call BIS_fnc_findSafePos;
  735. if (count _patrolPos2 == 0) then { _patrolPos2 = _stagingPos; };
  736.  
  737. private _wp3 = _group addWaypoint [_patrolPos2, 50];
  738. _wp3 setWaypointType "SAD";
  739. _wp3 setWaypointBehaviour "AWARE";
  740. _wp3 setWaypointSpeed "NORMAL";
  741. _wp3 setWaypointCombatMode "RED";
  742. _wp3 setWaypointCompletionRadius 200;
  743.  
  744. // Cycle between the two SAD waypoints
  745. if (count (waypoints _group) > 2) then {
  746. private _wpCycle = _group addWaypoint [waypointPosition [_group, 1], 0]; // Cycle back to the first SAD WP
  747. _wpCycle setWaypointType "CYCLE";
  748. };
  749.  
  750. _group setBehaviour "AWARE";
  751. _group setCombatMode "YELLOW";
  752. _group setSpeedMode "NORMAL";
  753. };
  754.  
  755.  
  756. fnc_createStealthPatrol = {
  757. params ["_group", "_enemyBasePos"];
  758.  
  759. while {count (waypoints _group) > 0} do {
  760. deleteWaypoint ((waypoints _group) select 0);
  761. };
  762.  
  763. private _approachPos = [_enemyBasePos, PATROL_RADIUS, PATROL_RADIUS + 300, 10, 2, 0, 10] call BIS_fnc_findSafePos;
  764.  
  765. private _wp = _group addWaypoint [_approachPos, 30];
  766. _wp setWaypointType "MOVE";
  767. _wp setWaypointBehaviour "STEALTH";
  768. _wp setWaypointSpeed "LIMITED";
  769. _wp setWaypointFormation "FILE";
  770.  
  771. for "_i" from 1 to 3 do {
  772. private _patrolPos = [_enemyBasePos, 400, PATROL_RADIUS, 10, 2, 0, 10] call BIS_fnc_findSafePos;
  773. _wp = _group addWaypoint [_patrolPos, 50];
  774. _wp setWaypointType "MOVE";
  775. _wp setWaypointBehaviour "STEALTH";
  776. _wp setWaypointSpeed "LIMITED";
  777. _wp setWaypointFormation "FILE";
  778. _wp setWaypointCombatMode "GREEN";
  779. };
  780.  
  781. private _patrolPos = [_enemyBasePos, 200, 800, 10, 2, 0, 10] call BIS_fnc_findSafePos;
  782. _wp = _group addWaypoint [_patrolPos, 30];
  783. _wp setWaypointType "SAD";
  784. _wp setWaypointBehaviour "COMBAT";
  785. _wp setWaypointSpeed "NORMAL";
  786. _wp setWaypointFormation "LINE";
  787. _wp setWaypointCombatMode "RED";
  788.  
  789. if (count (waypoints _group) > 1) then {
  790. _wp = _group addWaypoint [waypointPosition [_group, 1], 0];
  791. _wp setWaypointType "CYCLE";
  792. };
  793. };
  794.  
  795. // Wave Spawning Functions
  796. fnc_spawnBluforWave = {
  797. params ["_waveType", ["_isInitialWave", false], ["_flankIndex", 0]];
  798. if (isNil "bluforSpawnObj") exitWith {grpNull};
  799. private _group = grpNull;
  800. switch (_waveType) do {
  801. case "infantry": {
  802. if (BLUFOR_POINTS >= (2 * 6 * INFANTRY_VALUE)) then {
  803. _group = [_isInitialWave, _flankIndex] call fnc_spawnBluforSquad;
  804. };
  805. };
  806. case "upgraded": {
  807. if (BLUFOR_POINTS >= (2 * 6 * UPGRADED_VALUE)) then {
  808. _group = [] call fnc_spawnBluforUpgradedSquad;
  809. };
  810. };
  811. case "specops": {
  812. if (BLUFOR_POINTS >= (2 * 6 * SPECOPS_VALUE)) then {
  813. _group = [] call fnc_spawnBluforSpecOps;
  814. };
  815. };
  816. case "sniper": {
  817. if (BLUFOR_POINTS >= (2 * 2 * SNIPER_VALUE)) then {
  818. _group = [] call fnc_spawnBluforSnipers;
  819. };
  820. };
  821. case "elite": {
  822. if (BLUFOR_POINTS >= (2 * 5 * ELITE_VALUE)) then {
  823. _group = [] call fnc_spawnBluforElite;
  824. };
  825. };
  826. case "light_vehicle": {
  827. if (BLUFOR_POINTS >= (2 * VEHICLE_VALUE)) then {
  828. _group = [] call fnc_spawnBluforLightVehicle;
  829. };
  830. };
  831. case "tank": {
  832. if (BLUFOR_POINTS >= (2 * TANK_VALUE)) then {
  833. _group = [] call fnc_spawnBluforTank;
  834. };
  835. };
  836. case "attack_heli": {
  837. if (BLUFOR_POINTS >= (2 * ATTACK_HELI_VALUE)) then {
  838. _group = [] call fnc_spawnBluforAttackHeli;
  839. };
  840. };
  841. };
  842. if (!isNull _group) then {
  843. private _waveHistory = BLUFOR_LAST_WAVES;
  844. _waveHistory pushBack _waveType;
  845. if (count _waveHistory > WAVE_HISTORY_SIZE) then {_waveHistory deleteAt 0;};
  846. BLUFOR_LAST_WAVES = _waveHistory;
  847. };
  848. _group
  849. };
  850.  
  851. fnc_spawnOpforWave = {
  852. params ["_waveType", ["_isInitialWave", false], ["_flankIndex", 0]];
  853. if (isNil "opforSpawnObj") exitWith {grpNull};
  854. private _group = grpNull;
  855. switch (_waveType) do {
  856. case "infantry": {
  857. if (OPFOR_POINTS >= (2 * 6 * INFANTRY_VALUE)) then {
  858. _group = [_isInitialWave, _flankIndex] call fnc_spawnOpforSquad;
  859. };
  860. };
  861. case "upgraded": {
  862. if (OPFOR_POINTS >= (2 * 6 * UPGRADED_VALUE)) then {
  863. _group = [] call fnc_spawnOpforUpgradedSquad;
  864. };
  865. };
  866. case "specops": {
  867. if (OPFOR_POINTS >= (2 * 6 * SPECOPS_VALUE)) then {
  868. _group = [] call fnc_spawnOpforSpecOps;
  869. };
  870. };
  871. case "sniper": {
  872. if (OPFOR_POINTS >= (2 * 2 * SNIPER_VALUE)) then {
  873. _group = [] call fnc_spawnOpforSnipers;
  874. };
  875. };
  876. case "elite": {
  877. if (OPFOR_POINTS >= (2 * 5 * ELITE_VALUE)) then {
  878. _group = [] call fnc_spawnOpforElite;
  879. };
  880. };
  881. case "light_vehicle": {
  882. if (OPFOR_POINTS >= (2 * VEHICLE_VALUE)) then {
  883. _group = [] call fnc_spawnOpforLightVehicle;
  884. };
  885. };
  886. case "tank": {
  887. if (OPFOR_POINTS >= (2 * TANK_VALUE)) then {
  888. _group = [] call fnc_spawnOpforTank;
  889. };
  890. };
  891. case "attack_heli": {
  892. if (OPFOR_POINTS >= (2 * ATTACK_HELI_VALUE)) then {
  893. _group = [] call fnc_spawnOpforAttackHeli;
  894. };
  895. };
  896. };
  897. if (!isNull _group) then {
  898. private _waveHistory = OPFOR_LAST_WAVES;
  899. _waveHistory pushBack _waveType;
  900. if (count _waveHistory > WAVE_HISTORY_SIZE) then {_waveHistory deleteAt 0;};
  901. OPFOR_LAST_WAVES = _waveHistory;
  902. };
  903. _group
  904. };
  905.  
  906. // BLUFOR Spawn Functions
  907. fnc_spawnBluforSquad = {
  908. params ["_isInitialWave", "_flankIndex"];
  909. if (isNil "bluforSpawnObj" || BLUFOR_POINTS < (2 * 6 * INFANTRY_VALUE)) exitWith {grpNull};
  910. BLUFOR_POINTS = BLUFOR_POINTS - (2 * 6 * INFANTRY_VALUE);
  911. private ["_spawnPos", "_group", "_units"];
  912. _spawnPos = [west] call fnc_getSafeSpawnPosition;
  913. _group = createGroup west;
  914. _units = selectRandom BLUFOR_INFANTRY_POOL;
  915. {
  916. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  917. [_unit, 0.6, "REGULAR"] call fnc_enhanceIndividualAI;
  918. _unit setVariable ["unitValue", INFANTRY_VALUE, true];
  919. [_unit] call fnc_markAsImportant;
  920. [_unit, west] call fnc_updateStrengthTally;
  921. } forEach _units;
  922. if (_isInitialWave) then {
  923. [_group, west, _flankIndex] call fnc_createInitialFlankWaypoints;
  924. } else {
  925. [_group, getPos opforSpawnObj] call fnc_createPatrolWaypoints;
  926. };
  927. _group setVariable ["HC_FORCED_LOCK", true];
  928. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  929. _group
  930. };
  931.  
  932. fnc_spawnBluforUpgradedSquad = {
  933. if (isNil "bluforSpawnObj" || BLUFOR_POINTS < (2 * 6 * UPGRADED_VALUE)) exitWith {grpNull};
  934. BLUFOR_POINTS = BLUFOR_POINTS - (2 * 6 * UPGRADED_VALUE);
  935. private ["_spawnPos", "_group", "_units"];
  936. _spawnPos = [west] call fnc_getSafeSpawnPosition;
  937. _group = createGroup west;
  938. _units = selectRandom BLUFOR_UPGRADED_POOL;
  939. {
  940. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  941. [_unit, 0.68, "UPGRADED"] call fnc_enhanceIndividualAI;
  942. _unit setVariable ["unitValue", UPGRADED_VALUE, true];
  943. [_unit] call fnc_markAsImportant;
  944. [_unit, west] call fnc_updateStrengthTally;
  945. } forEach _units;
  946. [_group, getPos opforSpawnObj] call fnc_createPatrolWaypoints;
  947. _group setVariable ["HC_FORCED_LOCK", true];
  948. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  949. _group
  950. };
  951.  
  952. fnc_spawnBluforSpecOps = {
  953. if (isNil "bluforSpawnObj" || BLUFOR_POINTS < (2 * 6 * SPECOPS_VALUE)) exitWith {grpNull};
  954. BLUFOR_POINTS = BLUFOR_POINTS - (2 * 6 * SPECOPS_VALUE);
  955. private ["_spawnPos", "_group", "_units"];
  956. _spawnPos = [west] call fnc_getSafeSpawnPosition;
  957. _group = createGroup west;
  958. _units = selectRandom BLUFOR_SPECOPS_POOL;
  959. {
  960. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  961. [_unit, 0.75, "SPECOPS"] call fnc_enhanceIndividualAI;
  962. _unit setVariable ["isSpecOps", true, true];
  963. _unit setVariable ["unitValue", SPECOPS_VALUE, true];
  964. [_unit] call fnc_markAsImportant;
  965. [_unit, west] call fnc_updateStrengthTally;
  966. } forEach _units;
  967. [_group, getPos opforSpawnObj] call fnc_createPatrolWaypoints;
  968. _group setVariable ["HC_FORCED_LOCK", true];
  969. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  970. _group
  971. };
  972.  
  973. fnc_spawnBluforSnipers = {
  974. if (isNil "bluforSpawnObj" || BLUFOR_POINTS < (2 * 2 * SNIPER_VALUE)) exitWith {grpNull};
  975. BLUFOR_POINTS = BLUFOR_POINTS - (2 * 2 * SNIPER_VALUE);
  976. private ["_spawnPos", "_group", "_sniper", "_spotter"];
  977. _spawnPos = [west] call fnc_getSafeSpawnPosition;
  978. _group = createGroup west;
  979. _sniper = _group createUnit ["B_sniper_F", _spawnPos, [], 20, "NONE"];
  980. [_sniper, 0.8, "SNIPER"] call fnc_enhanceIndividualAI;
  981. _spotter = _group createUnit ["B_spotter_F", _spawnPos, [], 20, "NONE"];
  982. [_spotter, 0.75, "SPECOPS"] call fnc_enhanceIndividualAI;
  983. _sniper setVariable ["isSniper", true, true]; _sniper setVariable ["unitValue", SNIPER_VALUE, true];
  984. _spotter setVariable ["isSniper", true, true]; _spotter setVariable ["unitValue", SNIPER_VALUE, true];
  985. [_sniper] call fnc_markAsImportant; [_spotter] call fnc_markAsImportant;
  986. [_sniper, west] call fnc_updateStrengthTally;
  987. [_spotter, west] call fnc_updateStrengthTally;
  988. [_group, getPos opforSpawnObj] call fnc_createStealthPatrol;
  989. _group setVariable ["HC_FORCED_LOCK", true];
  990. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  991. _group
  992. };
  993.  
  994. fnc_spawnBluforElite = {
  995. if (isNil "bluforSpawnObj" || BLUFOR_POINTS < (2 * 5 * ELITE_VALUE)) exitWith {grpNull};
  996. BLUFOR_POINTS = BLUFOR_POINTS - (2 * 5 * ELITE_VALUE);
  997. private ["_spawnPos", "_group", "_units"];
  998. _spawnPos = [west] call fnc_getSafeSpawnPosition;
  999. _group = createGroup west;
  1000. _units = selectRandom BLUFOR_ELITE_POOL;
  1001. {
  1002. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  1003. [_unit, 0.9, "ELITE"] call fnc_enhanceIndividualAI;
  1004. _unit setVariable ["isElite", true, true];
  1005. _unit setVariable ["unitValue", ELITE_VALUE, true];
  1006. [_unit] call fnc_markAsImportant;
  1007. [_unit, west] call fnc_updateStrengthTally;
  1008. } forEach _units;
  1009. [_group, getPos opforSpawnObj, "COMBAT"] call fnc_createPatrolWaypoints;
  1010. _group setVariable ["HC_FORCED_LOCK", true];
  1011. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  1012. _group
  1013. };
  1014.  
  1015. // ===================================================================================
  1016. // ==================== OPTIMIZATION: GENERIC VEHICLE SPAWN LOGIC ====================
  1017. // ===================================================================================
  1018.  
  1019. // NEW: Generic vehicle spawning function to reduce code duplication and improve maintainability.
  1020. fnc_spawnVehicleGeneric = {
  1021. params [
  1022. "_side",
  1023. "_vehiclePool",
  1024. "_activeVehicleArrayName",
  1025. "_maxVehicleVarName",
  1026. "_vehicleValue",
  1027. "_crewSkill",
  1028. "_crewSkillRole",
  1029. "_spawnMode", // "NONE" for ground, "FLY" for air
  1030. "_taskType", // "GROUND_PATROL", "HELI_PATROL"
  1031. "_hcLockDuration"
  1032. ];
  1033.  
  1034. private _spawnObj = if (_side == west) then {bluforSpawnObj} else {opforSpawnObj};
  1035. if (isNil "_spawnObj") exitWith {grpNull};
  1036.  
  1037. // Dynamically get side-specific points and vehicle arrays
  1038. private _pointsVarName = if (_side == west) then {"BLUFOR_POINTS"} else {"OPFOR_POINTS"};
  1039. private _points = missionNamespace getVariable [_pointsVarName, 0];
  1040. private _activeVehicles = missionNamespace getVariable [_activeVehicleArrayName, []];
  1041. private _maxVehicleCount = missionNamespace getVariable [_maxVehicleVarName, 0];
  1042.  
  1043. // Check if affordable
  1044. if (_points < (2 * _vehicleValue)) exitWith {grpNull};
  1045.  
  1046. // Clean the array of destroyed/null vehicles and check the cap
  1047. _activeVehicles = _activeVehicles select {!isNull _x && alive _x};
  1048. if (count _activeVehicles >= _maxVehicleCount) exitWith {grpNull};
  1049.  
  1050. // Deduct points
  1051. missionNamespace setVariable [_pointsVarName, _points - (2 * _vehicleValue)];
  1052.  
  1053. private _basePos = getPos _spawnObj;
  1054. private _spawnPos = [];
  1055. if (_spawnMode == "FLY") then {
  1056. _spawnPos = _basePos vectorAdd [((random 200) - 100), ((random 200) - 100), 100];
  1057. } else {
  1058. _spawnPos = [_basePos, 50, 150, 15, 0, 0.4, 0] call BIS_fnc_findSafePos;
  1059. if (count _spawnPos == 0) then {
  1060. _spawnPos = _basePos; // Fallback to base position
  1061. };
  1062. };
  1063.  
  1064. // Create vehicle and crew
  1065. private _vehicleClass = selectRandom _vehiclePool;
  1066. private _vehicle = createVehicle [_vehicleClass, _spawnPos, [], 0, _spawnMode];
  1067. _vehicle setVectorUp [0,0,1];
  1068. if (_spawnMode == "FLY") then { _vehicle engineOn true; };
  1069.  
  1070. private _group = createGroup _side;
  1071. createVehicleCrew _vehicle;
  1072. (crew _vehicle) joinSilent _group;
  1073.  
  1074. // Setup crew
  1075. {
  1076. private _currentSkill = _crewSkill;
  1077. // Special case for pilots
  1078. if (_spawnMode == "FLY" && {_x == driver _vehicle}) then { _currentSkill = 0.95; };
  1079.  
  1080. [_x, _currentSkill, _crewSkillRole] call fnc_enhanceIndividualAI;
  1081. _x setVariable ["unitValue", _vehicleValue / (count (crew _vehicle)), true];
  1082. [_x] call fnc_markAsImportant;
  1083. } forEach (crew _vehicle);
  1084.  
  1085. // Setup vehicle
  1086. _vehicle setVariable ["unitValue", _vehicleValue, true];
  1087. [_vehicle] call fnc_markAsImportant;
  1088.  
  1089. // Update strength tally for the vehicle and its crew
  1090. [_vehicle, _side] call fnc_updateStrengthTally;
  1091. {
  1092. [_x, _side] call fnc_updateStrengthTally;
  1093. } forEach (crew _vehicle);
  1094.  
  1095. _activeVehicles pushBack _vehicle;
  1096. missionNamespace setVariable [_activeVehicleArrayName, _activeVehicles, true]; // Update the global array
  1097.  
  1098. // Assign task
  1099. switch (_taskType) do {
  1100. case "GROUND_PATROL": {
  1101. private _hotspot = [_side] call fnc_getBattleHotspot;
  1102. [_group, _hotspot, _basePos] call fnc_createVehiclePatrolWaypoints;
  1103. };
  1104. case "HELI_PATROL": {
  1105. private _hotspot = [_side] call fnc_getBattleHotspot;
  1106. [_group, _hotspot] call fnc_createHeliPatrolWaypoints;
  1107. };
  1108. };
  1109.  
  1110. // Set HC lock
  1111. _group setVariable ["HC_FORCED_LOCK", true];
  1112. [_group, _hcLockDuration] spawn {
  1113. params ["_grp", "_duration"];
  1114. sleep _duration;
  1115. if (!isNull _grp) then {
  1116. _grp setVariable ["HC_FORCED_LOCK", false];
  1117. };
  1118. };
  1119.  
  1120. _group
  1121. };
  1122.  
  1123. // ==================== REFACTORED VEHICLE SPAWN FUNCTIONS ====================
  1124.  
  1125. fnc_spawnBluforLightVehicle = {
  1126. [
  1127. west,
  1128. BLUFOR_LIGHT_VEHICLE_POOL,
  1129. "BLUFOR_ACTIVE_LIGHT_VEHICLES",
  1130. "maxLightVehiclesPerSide",
  1131. VEHICLE_VALUE,
  1132. 0.7,
  1133. "UPGRADED_INF",
  1134. "NONE",
  1135. "GROUND_PATROL",
  1136. 90
  1137. ] call fnc_spawnVehicleGeneric;
  1138. };
  1139.  
  1140. fnc_spawnBluforTank = {
  1141. [
  1142. west,
  1143. BLUFOR_TANK_POOL,
  1144. "BLUFOR_ACTIVE_TANKS",
  1145. "maxTanksPerSide",
  1146. TANK_VALUE,
  1147. 0.8,
  1148. "ELITE",
  1149. "NONE",
  1150. "GROUND_PATROL",
  1151. 90
  1152. ] call fnc_spawnVehicleGeneric;
  1153. };
  1154.  
  1155. fnc_spawnBluforAttackHeli = {
  1156. [
  1157. west,
  1158. BLUFOR_ATTACK_HELI_POOL,
  1159. "BLUFOR_ACTIVE_ATTACK_HELIS",
  1160. "maxAttackHelisPerSide",
  1161. ATTACK_HELI_VALUE,
  1162. 0.85,
  1163. "ELITE",
  1164. "FLY",
  1165. "HELI_PATROL",
  1166. 180
  1167. ] call fnc_spawnVehicleGeneric;
  1168. };
  1169.  
  1170. // OPFOR Spawn Functions
  1171. fnc_spawnOpforSquad = {
  1172. params ["_isInitialWave", "_flankIndex"];
  1173. if (isNil "opforSpawnObj" || OPFOR_POINTS < (2 * 6 * INFANTRY_VALUE)) exitWith {grpNull};
  1174. OPFOR_POINTS = OPFOR_POINTS - (2 * 6 * INFANTRY_VALUE);
  1175. private ["_spawnPos", "_group", "_units"];
  1176. _spawnPos = [east] call fnc_getSafeSpawnPosition;
  1177. _group = createGroup east;
  1178. _units = selectRandom OPFOR_INFANTRY_POOL;
  1179. {
  1180. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  1181. [_unit, 0.6, "REGULAR"] call fnc_enhanceIndividualAI;
  1182. _unit setVariable ["unitValue", INFANTRY_VALUE, true];
  1183. [_unit] call fnc_markAsImportant;
  1184. [_unit, east] call fnc_updateStrengthTally;
  1185. } forEach _units;
  1186. if (_isInitialWave) then {
  1187. [_group, east, _flankIndex] call fnc_createInitialFlankWaypoints;
  1188. } else {
  1189. [_group, getPos bluforSpawnObj] call fnc_createPatrolWaypoints;
  1190. };
  1191. _group setVariable ["HC_FORCED_LOCK", true];
  1192. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  1193. _group
  1194. };
  1195.  
  1196. fnc_spawnOpforUpgradedSquad = {
  1197. if (isNil "opforSpawnObj" || OPFOR_POINTS < (2 * 6 * UPGRADED_VALUE)) exitWith {grpNull};
  1198. OPFOR_POINTS = OPFOR_POINTS - (2 * 6 * UPGRADED_VALUE);
  1199. private ["_spawnPos", "_group", "_units"];
  1200. _spawnPos = [east] call fnc_getSafeSpawnPosition;
  1201. _group = createGroup east;
  1202. _units = selectRandom OPFOR_UPGRADED_POOL;
  1203. {
  1204. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  1205. [_unit, 0.68, "UPGRADED"] call fnc_enhanceIndividualAI;
  1206. _unit setVariable ["unitValue", UPGRADED_VALUE, true];
  1207. [_unit] call fnc_markAsImportant;
  1208. [_unit, east] call fnc_updateStrengthTally;
  1209. } forEach _units;
  1210. [_group, getPos bluforSpawnObj] call fnc_createPatrolWaypoints;
  1211. _group setVariable ["HC_FORCED_LOCK", true];
  1212. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  1213. _group
  1214. };
  1215.  
  1216. fnc_spawnOpforSpecOps = {
  1217. if (isNil "opforSpawnObj" || OPFOR_POINTS < (2 * 6 * SPECOPS_VALUE)) exitWith {grpNull};
  1218. OPFOR_POINTS = OPFOR_POINTS - (2 * 6 * SPECOPS_VALUE);
  1219. private ["_spawnPos", "_group", "_units"];
  1220. _spawnPos = [east] call fnc_getSafeSpawnPosition;
  1221. _group = createGroup east;
  1222. _units = selectRandom OPFOR_SPECOPS_POOL;
  1223. {
  1224. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  1225. [_unit, 0.75, "SPECOPS"] call fnc_enhanceIndividualAI;
  1226. _unit setVariable ["isSpecOps", true, true];
  1227. _unit setVariable ["unitValue", SPECOPS_VALUE, true];
  1228. [_unit] call fnc_markAsImportant;
  1229. [_unit, east] call fnc_updateStrengthTally;
  1230. } forEach _units;
  1231. [_group, getPos bluforSpawnObj] call fnc_createPatrolWaypoints;
  1232. _group setVariable ["HC_FORCED_LOCK", true];
  1233. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  1234. _group
  1235. };
  1236.  
  1237. fnc_spawnOpforSnipers = {
  1238. if (isNil "opforSpawnObj" || OPFOR_POINTS < (2 * 2 * SNIPER_VALUE)) exitWith {grpNull};
  1239. OPFOR_POINTS = OPFOR_POINTS - (2 * 2 * SNIPER_VALUE);
  1240. private ["_spawnPos", "_group", "_sniper", "_spotter"];
  1241. _spawnPos = [east] call fnc_getSafeSpawnPosition;
  1242. _group = createGroup east;
  1243. _sniper = _group createUnit ["O_sniper_F", _spawnPos, [], 20, "NONE"];
  1244. [_sniper, 0.8, "SNIPER"] call fnc_enhanceIndividualAI;
  1245. _spotter = _group createUnit ["O_spotter_F", _spawnPos, [], 20, "NONE"];
  1246. [_spotter, 0.75, "SPECOPS"] call fnc_enhanceIndividualAI;
  1247. _sniper setVariable ["isSniper", true, true]; _sniper setVariable ["unitValue", SNIPER_VALUE, true];
  1248. _spotter setVariable ["isSniper", true, true]; _spotter setVariable ["unitValue", SNIPER_VALUE, true];
  1249. [_sniper] call fnc_markAsImportant; [_spotter] call fnc_markAsImportant;
  1250. [_sniper, east] call fnc_updateStrengthTally;
  1251. [_spotter, east] call fnc_updateStrengthTally;
  1252. [_group, getPos bluforSpawnObj] call fnc_createStealthPatrol;
  1253. _group setVariable ["HC_FORCED_LOCK", true];
  1254. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  1255. _group
  1256. };
  1257.  
  1258. fnc_spawnOpforElite = {
  1259. if (isNil "opforSpawnObj" || OPFOR_POINTS < (2 * 5 * ELITE_VALUE)) exitWith {grpNull};
  1260. OPFOR_POINTS = OPFOR_POINTS - (2 * 5 * ELITE_VALUE);
  1261. private ["_spawnPos", "_group", "_units"];
  1262. _spawnPos = [east] call fnc_getSafeSpawnPosition;
  1263. _group = createGroup east;
  1264. _units = selectRandom OPFOR_ELITE_POOL;
  1265. {
  1266. private _unit = _group createUnit [_x, _spawnPos, [], 10, "NONE"];
  1267. [_unit, 0.9, "ELITE"] call fnc_enhanceIndividualAI;
  1268. _unit setVariable ["isElite", true, true];
  1269. _unit setVariable ["unitValue", ELITE_VALUE, true];
  1270. [_unit] call fnc_markAsImportant;
  1271. [_unit, east] call fnc_updateStrengthTally;
  1272. } forEach _units;
  1273. [_group, getPos bluforSpawnObj, "COMBAT"] call fnc_createPatrolWaypoints;
  1274. _group setVariable ["HC_FORCED_LOCK", true];
  1275. [_group] spawn { params ["_grp"]; sleep 60; _grp setVariable ["HC_FORCED_LOCK", false]; };
  1276. _group
  1277. };
  1278.  
  1279. fnc_spawnOpforLightVehicle = {
  1280. [
  1281. east,
  1282. OPFOR_LIGHT_VEHICLE_POOL,
  1283. "OPFOR_ACTIVE_LIGHT_VEHICLES",
  1284. "maxLightVehiclesPerSide",
  1285. VEHICLE_VALUE,
  1286. 0.7,
  1287. "UPGRADED_INF",
  1288. "NONE",
  1289. "GROUND_PATROL",
  1290. 90
  1291. ] call fnc_spawnVehicleGeneric;
  1292. };
  1293.  
  1294. fnc_spawnOpforTank = {
  1295. [
  1296. east,
  1297. OPFOR_TANK_POOL,
  1298. "OPFOR_ACTIVE_TANKS",
  1299. "maxTanksPerSide",
  1300. TANK_VALUE,
  1301. 0.8,
  1302. "ELITE",
  1303. "NONE",
  1304. "GROUND_PATROL",
  1305. 90
  1306. ] call fnc_spawnVehicleGeneric;
  1307. };
  1308.  
  1309. fnc_spawnOpforAttackHeli = {
  1310. [
  1311. east,
  1312. OPFOR_ATTACK_HELI_POOL,
  1313. "OPFOR_ACTIVE_ATTACK_HELIS",
  1314. "maxAttackHelisPerSide",
  1315. ATTACK_HELI_VALUE,
  1316. 0.85,
  1317. "ELITE",
  1318. "FLY",
  1319. "HELI_PATROL",
  1320. 180
  1321. ] call fnc_spawnVehicleGeneric;
  1322. };
  1323.  
  1324. fnc_createInitialFlankWaypoints = {
  1325. params ["_group", "_side", "_flankIndex"];
  1326.  
  1327. private _capturePos = (CAPTURE_LOCATIONS select 0) select 1;
  1328. private _basePos = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  1329.  
  1330. private _dirFromCaptureToBase = _capturePos getDir _basePos;
  1331. private _frontLineDistance = 200; // Distance from capture zone towards base
  1332. private _frontLinePos = _capturePos getPos [_frontLineDistance, _dirFromCaptureToBase];
  1333.  
  1334. private _flankOffset = 100; // Offset for left and right flanks
  1335. private _targetPos = _frontLinePos;
  1336.  
  1337. switch (_flankIndex) do {
  1338. case 0: { // Left flank
  1339. _targetPos = _frontLinePos getPos [_flankOffset, _dirFromCaptureToBase - 90];
  1340. };
  1341. case 1: { // Center
  1342. _targetPos = _frontLinePos;
  1343. };
  1344. case 2: { // Right flank
  1345. _targetPos = _frontLinePos getPos [_flankOffset, _dirFromCaptureToBase + 90];
  1346. };
  1347. };
  1348.  
  1349. private _safePos = [_targetPos, 0, 50, 10, 0, 0.3, 0] call BIS_fnc_findSafePos;
  1350. if (count _safePos == 0) then { _safePos = _targetPos; };
  1351.  
  1352. while {count (waypoints _group) > 0} do {
  1353. deleteWaypoint ((waypoints _group) select 0);
  1354. };
  1355.  
  1356. private _wp = _group addWaypoint [_safePos, 20];
  1357. _wp setWaypointType "MOVE";
  1358. _wp setWaypointStatements ["true", "{_x setUnitPos 'UP';} forEach units group this;"];
  1359. _wp setWaypointBehaviour "AWARE";
  1360. _wp setWaypointSpeed "NORMAL";
  1361. _wp setWaypointFormation "LINE";
  1362. _wp setWaypointCombatMode "YELLOW";
  1363.  
  1364. private _patrolWp1 = _group addWaypoint [_safePos, 75];
  1365. _patrolWp1 setWaypointType "SAD";
  1366. _patrolWp1 setWaypointBehaviour "COMBAT";
  1367. _patrolWp1 setWaypointCombatMode "RED";
  1368. _patrolWp1 setWaypointCompletionRadius 100;
  1369.  
  1370. private _patrolPos2 = [_targetPos, 50, 150, 10, 0, 0.3, 0] call BIS_fnc_findSafePos;
  1371. if (count _patrolPos2 == 0) then { _patrolPos2 = _targetPos; };
  1372.  
  1373. private _patrolWp2 = _group addWaypoint [_patrolPos2, 75];
  1374. _patrolWp2 setWaypointType "SAD";
  1375. _patrolWp2 setWaypointBehaviour "COMBAT";
  1376. _patrolWp2 setWaypointCombatMode "RED";
  1377. _patrolWp2 setWaypointCompletionRadius 100;
  1378.  
  1379. if (count (waypoints _group) > 2) then {
  1380. private _cycleWp = _group addWaypoint [waypointPosition [_group, 1], 0];
  1381. _cycleWp setWaypointType "CYCLE";
  1382. };
  1383.  
  1384. _group setBehaviour "AWARE";
  1385. _group setCombatMode "RED";
  1386. _group setSpeedMode "NORMAL";
  1387. };
  1388.  
  1389.  
  1390. // Function to find closest suitable infantry group for merging
  1391. fnc_findClosestInfantryGroup = {
  1392. params ["_smallGroup"];
  1393.  
  1394. private _smallGroupSide = side _smallGroup;
  1395. private _leader = leader _smallGroup;
  1396. if (isNull _leader) exitWith {grpNull};
  1397.  
  1398. private _smallGroupPos = getPos _leader;
  1399. private _closestGroup = grpNull;
  1400. private _closestDistance = 99999;
  1401.  
  1402. // ==================== CHANGE START: OPTIMIZED GROUP FINDING ====================
  1403. // Instead of looping allGroups, we search a 500m radius for friendly units. This is much faster.
  1404. private _nearbyFriendlies = _smallGroupPos nearEntities [["CAManBase"], 500];
  1405. private _candidateGroups = [];
  1406.  
  1407. {
  1408. private _unit = _x;
  1409. if (side _unit == _smallGroupSide && alive _unit) then {
  1410. private _grp = group _unit;
  1411. // Add the group to our candidates list, but only if it's not already there.
  1412. _candidateGroups pushBackUnique _grp;
  1413. };
  1414. } forEach _nearbyFriendlies;
  1415.  
  1416. // Now, we iterate the much smaller list of nearby candidate groups.
  1417. {
  1418. private _group = _x;
  1419.  
  1420. // Filter out unsuitable groups
  1421. if (
  1422. _group != _smallGroup &&
  1423. count (units _x select {alive _x}) > 2 &&
  1424. !([_group] call fnc_isGroupLocked) &&
  1425. ({isPlayer _x} count (units _group) == 0)
  1426. ) then {
  1427. private _grpLeader = leader _group;
  1428. if (!isNull _grpLeader && alive _grpLeader) then {
  1429. // Check if it's an infantry group (not in vehicles)
  1430. private _isInfantryGroup = true;
  1431. {
  1432. if (vehicle _x != _x) then { _isInfantryGroup = false; };
  1433. } forEach (units _group select {alive _x});
  1434.  
  1435. if (_isInfantryGroup) then {
  1436. private _distance = _smallGroupPos distance (getPos _grpLeader);
  1437. if (_distance < _closestDistance) then {
  1438. _closestDistance = _distance;
  1439. _closestGroup = _group;
  1440. };
  1441. };
  1442. };
  1443. };
  1444. } forEach _candidateGroups;
  1445. // ===================== CHANGE END =====================
  1446.  
  1447. _closestGroup
  1448. };
  1449.  
  1450. // Function to merge a small group into a larger one
  1451. fnc_mergeSmallGroup = {
  1452. params ["_smallGroup", "_targetGroup"];
  1453.  
  1454. if (isNull _smallGroup || isNull _targetGroup) exitWith {false};
  1455. if (_smallGroup == _targetGroup) exitWith {false};
  1456.  
  1457. private _unitsToMove = units _smallGroup select {alive _x};
  1458. if (count _unitsToMove == 0) exitWith {false};
  1459.  
  1460. // Move all alive units from small group to target group
  1461. {
  1462. [_x] joinSilent _targetGroup;
  1463. } forEach _unitsToMove;
  1464.  
  1465. // Clean up the old group from HC tracking if it exists
  1466. HC_Active_Groups deleteAt (groupId _smallGroup);
  1467.  
  1468. // Delete the now-empty group
  1469. deleteGroup _smallGroup;
  1470.  
  1471. true
  1472. };
  1473.  
  1474. // Function to check and process small groups for merging
  1475. fnc_checkAndMergeSmallGroups = {
  1476. params ["_group"];
  1477.  
  1478. if (isNull _group) exitWith {};
  1479.  
  1480. private _aliveUnits = units _group select {alive _x};
  1481. private _unitCount = count _aliveUnits;
  1482.  
  1483. // Check if group qualifies for merging (1-2 alive members)
  1484. if (_unitCount >= 1 && _unitCount <= 2) then {
  1485. // Don't merge player groups
  1486. if (({isPlayer _x} count _aliveUnits) > 0) exitWith {};
  1487.  
  1488. // Don't merge sniper squads (they're supposed to be 2-man teams)
  1489. if (({_x getVariable ["isSniper", false]} count _aliveUnits) > 0) exitWith {};
  1490.  
  1491. // Don't merge vehicle crews
  1492. private _isInfantryGroup = true;
  1493. {
  1494. if (vehicle _x != _x) then {
  1495. _isInfantryGroup = false;
  1496. };
  1497. } forEach _aliveUnits;
  1498.  
  1499. if (!_isInfantryGroup) exitWith {};
  1500.  
  1501. // Don't merge if HC locked
  1502. if ([_group] call fnc_isGroupLocked) exitWith {};
  1503.  
  1504. // Find closest suitable group
  1505. private _targetGroup = [_group] call fnc_findClosestInfantryGroup;
  1506.  
  1507. if (!isNull _targetGroup) then {
  1508. // Perform the merge
  1509. private _success = [_group, _targetGroup] call fnc_mergeSmallGroup;
  1510.  
  1511. if (_success) then {
  1512. // Add a small delay before the merged units can get new HC orders
  1513. [_targetGroup] spawn {
  1514. params ["_grp"];
  1515. sleep 5;
  1516. if (!isNull _grp) then {
  1517. _grp setVariable ["HC_FORCED_LOCK", false];
  1518. };
  1519. };
  1520. };
  1521. };
  1522. };
  1523. };
  1524.  
  1525. ==================== END OF: AIstuff.sqf ====================
  1526.  
  1527.  
  1528.  
  1529. ==================== START OF: arsenal.sqf ====================
  1530.  
  1531. // arsenal.sqf
  1532. // Purpose: Creates arsenal boxes for BLUFOR and OPFOR with purchase menu and clear effects actions.
  1533. // REVISED: Added action to clear blurry post-processing effects.
  1534. // REVISED: Fixed action code to use string to prevent race conditions.
  1535. // REVISED: Removed player-purchased AI squad functionality.
  1536. // REVISED: Fixed RadialBlur ppEffectAdjust to use 4 parameters to resolve scripting error.
  1537.  
  1538. // Function to create an arsenal box at a given position for a specific side
  1539. fnc_createBaseArsenal = {
  1540. params ["_pos", "_side"];
  1541.  
  1542. private _arsenalBox = createVehicle ["B_supplyCrate_F", _pos, [], 0, "CAN_COLLIDE"];
  1543.  
  1544. _arsenalBox allowDamage false;
  1545. _arsenalBox enableSimulationGlobal false;
  1546. _arsenalBox enableDynamicSimulation false;
  1547.  
  1548. _arsenalBox setVariable ["gcImportant", true, true];
  1549. _arsenalBox setVariable ["arsenalSide", _side, true];
  1550.  
  1551. // Clear all cargo
  1552. clearWeaponCargoGlobal _arsenalBox;
  1553. clearMagazineCargoGlobal _arsenalBox;
  1554. clearItemCargoGlobal _arsenalBox;
  1555. clearBackpackCargoGlobal _arsenalBox;
  1556.  
  1557. [_arsenalBox, [], true] call BIS_fnc_removeVirtualItemCargo;
  1558.  
  1559. // Load uniform restrictions if not defined
  1560. if (isNil "BLUFOR_UNIFORMS") then {
  1561. [] call compile preprocessFileLineNumbers "loadoutlimitations.sqf";
  1562. };
  1563.  
  1564. // Add virtual cargo
  1565. [_arsenalBox, true, true] call BIS_fnc_addVirtualWeaponCargo;
  1566. [_arsenalBox, true, true] call BIS_fnc_addVirtualMagazineCargo;
  1567. [_arsenalBox, true, true] call BIS_fnc_addVirtualBackpackCargo;
  1568.  
  1569. // Filter clothing items
  1570. private _allItems = ("getNumber (_x >> 'scope') >= 2" configClasses (configFile >> "CfgWeapons")) apply {configName _x};
  1571. private _allUniforms = _allItems select { isClass (configFile >> "CfgWeapons" >> _x >> "ItemInfo") && { getNumber (configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "type") == 801 } };
  1572. private _allVests = _allItems select { isClass (configFile >> "CfgWeapons" >> _x >> "ItemInfo") && { getNumber (configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "type") == 701 } };
  1573. private _allHeadgear = _allItems select { isClass (configFile >> "CfgWeapons" >> _x >> "ItemInfo") && { getNumber (configFile >> "CfgWeapons" >> _x >> "ItemInfo" >> "type") == 605 } };
  1574.  
  1575. private _allClothing = _allUniforms + _allVests + _allHeadgear;
  1576. private _itemsWithoutClothing = _allItems - _allClothing;
  1577.  
  1578. [_arsenalBox, _itemsWithoutClothing, true] call BIS_fnc_addVirtualItemCargo;
  1579.  
  1580. // Set side-specific clothing and arsenal name
  1581. private _allowedClothing = [];
  1582. if (_side == west) then {
  1583. _allowedClothing = BLUFOR_UNIFORMS + BLUFOR_VESTS + BLUFOR_HEADGEAR;
  1584. _arsenalBox setVariable ["arsenalName", "BLUFOR Arsenal", true];
  1585. } else {
  1586. _allowedClothing = OPFOR_UNIFORMS + OPFOR_VESTS + OPFOR_HEADGEAR;
  1587. _arsenalBox setVariable ["arsenalName", "OPFOR Arsenal", true];
  1588. };
  1589.  
  1590. [_arsenalBox, _allowedClothing, true] call BIS_fnc_addVirtualItemCargo;
  1591.  
  1592. // Ensure box is grounded
  1593. private _groundPos = _pos;
  1594. _groundPos set [2, 0];
  1595. _arsenalBox setPosATL _groundPos;
  1596. _arsenalBox setVectorUp [0,0,1];
  1597.  
  1598. _arsenalBox
  1599. };
  1600.  
  1601. // Function to add purchase menu action to arsenal box (client-side)
  1602. fnc_client_addPurchaseAction = {
  1603. params ["_arsenalBox"];
  1604. if (!hasInterface) exitWith {};
  1605.  
  1606. _arsenalBox addAction [
  1607. "<t color='#FFD700'>[Open Purchase Menu]</t>",
  1608. {
  1609. [] call fnc_client_openPlayerMenu;
  1610. },
  1611. [],
  1612. 6,
  1613. true,
  1614. true,
  1615. "",
  1616. "side _this == (_target getVariable ['arsenalSide', sideUnknown]) && alive _this",
  1617. 10
  1618. ];
  1619. };
  1620.  
  1621. // Function to add clear effects action to arsenal box (client-side)
  1622. fnc_client_addClearEffectsAction = {
  1623. params ["_arsenalBox"];
  1624. if (!hasInterface) exitWith {};
  1625.  
  1626. _arsenalBox addAction [
  1627. "<t color='#00FF00'>[Clear Blurry Effects]</t>",
  1628. {
  1629. // Destroy all post-processing effects across all priority ranges
  1630. for "_i" from 0 to 2000 do {
  1631. ppEffectDestroy _i;
  1632. };
  1633.  
  1634. // Create and immediately disable clean effects to reset them
  1635. "DynamicBlur" ppEffectEnable false;
  1636. "RadialBlur" ppEffectEnable false;
  1637. "chromAberration" ppEffectEnable false;
  1638. "ColorCorrections" ppEffectEnable false;
  1639. "FilmGrain" ppEffectEnable false;
  1640. "WetDistortion" ppEffectEnable false;
  1641. "ColorInversion" ppEffectEnable false;
  1642.  
  1643. // Reset camera shake
  1644. enableCamShake false;
  1645. resetCamShake;
  1646.  
  1647. // Reset any potential BIS effects
  1648. BIS_fnc_feedback_allowPP = true;
  1649. BIS_fnc_feedback_allowDeathScreen = true;
  1650.  
  1651. hint "All visual effects cleared.";
  1652. },
  1653. [],
  1654. 5,
  1655. true,
  1656. true,
  1657. "",
  1658. "side _this == (_target getVariable ['arsenalSide', sideUnknown]) && alive _this",
  1659. 10
  1660. ];
  1661. };
  1662.  
  1663. // ==================== CHANGE START ====================
  1664. // NEW: Function to add settings menu action to arsenal box (client-side)
  1665. fnc_client_addSettingsAction = {
  1666. params ["_arsenalBox"];
  1667. if (!hasInterface) exitWith {};
  1668.  
  1669. _arsenalBox addAction [
  1670. "<t color='#ADD8E6'>[Open Settings Menu]</t>",
  1671. {
  1672. createDialog "AdminSettingsMenu";
  1673. },
  1674. [],
  1675. 4, // Priority below purchase and clear effects
  1676. true,
  1677. true,
  1678. "",
  1679. // Condition: Show if in singleplayer, or if the player is the server (host) in multiplayer.
  1680. "(!isMultiplayer) || (isServer)"
  1681. ];
  1682. };
  1683. // ===================== CHANGE END =====================
  1684.  
  1685. // Server-side execution to create arsenals and broadcast actions
  1686. if (isServer) then {
  1687. [] spawn {
  1688. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  1689. sleep 1;
  1690.  
  1691. private _bluforBasePos = getPosATL bluforSpawnObj;
  1692. _bluforBasePos set [2, 0];
  1693. private _bluforArsenal = [_bluforBasePos, west] call fnc_createBaseArsenal;
  1694.  
  1695. private _opforBasePos = getPosATL opforSpawnObj;
  1696. _opforBasePos set [2, 0];
  1697. private _opforArsenal = [_opforBasePos, east] call fnc_createBaseArsenal;
  1698.  
  1699. missionNamespace setVariable ["BLUFOR_Arsenal", _bluforArsenal, true];
  1700. missionNamespace setVariable ["OPFOR_Arsenal", _opforArsenal, true];
  1701.  
  1702. // Broadcast purchase menu action
  1703. [_bluforArsenal] remoteExecCall ["fnc_client_addPurchaseAction", 0, true];
  1704. [_opforArsenal] remoteExecCall ["fnc_client_addPurchaseAction", 0, true];
  1705.  
  1706. // Broadcast clear effects action
  1707. [_bluforArsenal] remoteExecCall ["fnc_client_addClearEffectsAction", 0, true];
  1708. [_opforArsenal] remoteExecCall ["fnc_client_addClearEffectsAction", 0, true];
  1709.  
  1710. // ==================== CHANGE START ====================
  1711. // NEW: Broadcast settings menu action
  1712. [_bluforArsenal] remoteExecCall ["fnc_client_addSettingsAction", 0, true];
  1713. [_opforArsenal] remoteExecCall ["fnc_client_addSettingsAction", 0, true];
  1714. // ===================== CHANGE END =====================
  1715. };
  1716. };
  1717.  
  1718. ==================== END OF: arsenal.sqf ====================
  1719.  
  1720.  
  1721.  
  1722. ==================== START OF: botai.sqf ====================
  1723.  
  1724. // botai.sqf
  1725. // Centralized AI manager for improved performance and tactical behaviors
  1726.  
  1727. // ==================== PERFORMANCE MONITORING SYSTEM ====================
  1728. // Global performance state - accessed by all systems
  1729. if (isNil "PERF_STATE") then {
  1730. PERF_STATE = "EXCELLENT"; // EXCELLENT, GOOD, MODERATE, POOR, CRITICAL, EMERGENCY
  1731. };
  1732. if (isNil "PERF_MULTIPLIER") then {
  1733. PERF_MULTIPLIER = 1.0; // Multiplier for sleep intervals (1.0 = normal, up to 8.0 = extreme slowdown)
  1734. };
  1735.  
  1736. // Monitor server performance and adjust intervals dynamically
  1737. fnc_monitorPerformance = {
  1738. private _deltaHistory = [];
  1739. private _execTimeHistory = [];
  1740. private _historySize = 10;
  1741.  
  1742. while {true} do {
  1743. // Measure scheduler frame delta time (works in SP and MP)
  1744. private _schedulerDelta = diag_deltaTime;
  1745.  
  1746. // Get current script execution time from main AI loop
  1747. private _lastExecTime = missionNamespace getVariable ["BOTAI_lastExecTime", 0];
  1748.  
  1749. // Add to histories
  1750. _deltaHistory pushBack _schedulerDelta;
  1751. _execTimeHistory pushBack _lastExecTime;
  1752.  
  1753. if (count _deltaHistory > _historySize) then {
  1754. _deltaHistory deleteAt 0;
  1755. };
  1756. if (count _execTimeHistory > _historySize) then {
  1757. _execTimeHistory deleteAt 0;
  1758. };
  1759.  
  1760. // Calculate averages
  1761. private _avgDelta = 0;
  1762. {_avgDelta = _avgDelta + _x;} forEach _deltaHistory;
  1763. _avgDelta = _avgDelta / count _deltaHistory;
  1764.  
  1765. private _avgExecTime = 0;
  1766. {_avgExecTime = _avgExecTime + _x;} forEach _execTimeHistory;
  1767. _avgExecTime = _avgExecTime / count _execTimeHistory;
  1768.  
  1769. // Determine performance state based on scheduler delta
  1770. // Excellent: <0.025s (40+ scheduler fps)
  1771. // Good: 0.025-0.040s (25-40 scheduler fps) - LOWERED from 0.035
  1772. // Moderate: 0.040-0.060s (16-25 scheduler fps) - LOWERED from 0.050
  1773. // Poor: 0.060-0.090s (11-16 scheduler fps) - LOWERED from 0.075
  1774. // Critical: 0.090-0.120s (8-11 scheduler fps) - LOWERED from 0.100
  1775. // Emergency: >0.120s (<8 scheduler fps) - LOWERED from 0.100
  1776.  
  1777. private _deltaState = "EXCELLENT";
  1778. private _deltaMultiplier = 1.0;
  1779.  
  1780. if (_avgDelta < 0.025) then {
  1781. _deltaState = "EXCELLENT";
  1782. _deltaMultiplier = 1.0;
  1783. } else {
  1784. if (_avgDelta < 0.040) then {
  1785. _deltaState = "GOOD";
  1786. _deltaMultiplier = 1.5;
  1787. } else {
  1788. if (_avgDelta < 0.060) then {
  1789. _deltaState = "MODERATE";
  1790. _deltaMultiplier = 2.5;
  1791. } else {
  1792. if (_avgDelta < 0.090) then {
  1793. _deltaState = "POOR";
  1794. _deltaMultiplier = 4.0;
  1795. } else {
  1796. if (_avgDelta < 0.120) then {
  1797. _deltaState = "CRITICAL";
  1798. _deltaMultiplier = 6.0;
  1799. } else {
  1800. _deltaState = "EMERGENCY";
  1801. _deltaMultiplier = 10.0;
  1802. };
  1803. };
  1804. };
  1805. };
  1806. };
  1807.  
  1808. // Check script execution time (secondary metric)
  1809. // If AI loop takes >50ms per cycle, we're in trouble
  1810. private _execState = "EXCELLENT";
  1811. private _execMultiplier = 1.0;
  1812.  
  1813. if (_avgExecTime < 0.020) then {
  1814. _execState = "EXCELLENT";
  1815. _execMultiplier = 1.0;
  1816. } else {
  1817. if (_avgExecTime < 0.040) then {
  1818. _execState = "GOOD";
  1819. _execMultiplier = 1.5;
  1820. } else {
  1821. if (_avgExecTime < 0.060) then {
  1822. _execState = "MODERATE";
  1823. _execMultiplier = 2.5;
  1824. } else {
  1825. if (_avgExecTime < 0.090) then {
  1826. _execState = "POOR";
  1827. _execMultiplier = 4.0;
  1828. } else {
  1829. if (_avgExecTime < 0.120) then {
  1830. _execState = "CRITICAL";
  1831. _execMultiplier = 6.0;
  1832. } else {
  1833. _execState = "EMERGENCY";
  1834. _execMultiplier = 10.0;
  1835. };
  1836. };
  1837. };
  1838. };
  1839. };
  1840.  
  1841. // Use the WORST of the two metrics
  1842. private _oldState = PERF_STATE;
  1843. private _oldMultiplier = PERF_MULTIPLIER;
  1844.  
  1845. private _worstMultiplier = _deltaMultiplier max _execMultiplier;
  1846.  
  1847. if (_worstMultiplier == _deltaMultiplier) then {
  1848. PERF_STATE = _deltaState;
  1849. PERF_MULTIPLIER = _deltaMultiplier;
  1850. } else {
  1851. PERF_STATE = _execState;
  1852. PERF_MULTIPLIER = _execMultiplier;
  1853. };
  1854.  
  1855. // Set skip flags for non-critical operations
  1856. PERF_SKIP_NONCRITICAL = (PERF_STATE in ["CRITICAL", "EMERGENCY"]);
  1857. PERF_SKIP_MODERATE = (PERF_STATE == "EMERGENCY");
  1858.  
  1859. // Log state changes
  1860. if (_oldState != PERF_STATE) then {
  1861. diag_log format ["[PERF] State: %1 -> %2 (x%3) | Delta: %4ms (%5) | Exec: %6ms (%7)",
  1862. _oldState, PERF_STATE, PERF_MULTIPLIER,
  1863. round (_avgDelta * 1000), _deltaState,
  1864. round (_avgExecTime * 1000), _execState];
  1865. };
  1866.  
  1867. // Adaptive monitoring interval
  1868. if (PERF_STATE == "EXCELLENT") then {
  1869. sleep 3;
  1870. } else {
  1871. if (PERF_STATE in ["CRITICAL", "EMERGENCY"]) then {
  1872. sleep 0.5; // Monitor very frequently when critical
  1873. } else {
  1874. sleep 1.5;
  1875. };
  1876. };
  1877. };
  1878. };
  1879.  
  1880. // Helper function to get adaptive sleep time
  1881. fnc_getAdaptiveSleep = {
  1882. params ["_baseSleep"];
  1883. (_baseSleep * PERF_MULTIPLIER)
  1884. };
  1885. // ==================== END PERFORMANCE MONITORING ====================
  1886.  
  1887. // Set AI skills based on level and role
  1888. fnc_botSetSkills = {
  1889. params ["_unit", "_skillLevel", "_unitRole"];
  1890.  
  1891. _unit setSkill ["aimingAccuracy", (_skillLevel + 0.5) min 1];
  1892. _unit setSkill ["aimingSpeed", (_skillLevel + 0.2) min 1];
  1893. _unit setSkill ["aimingShake", 0];
  1894. _unit setSkill ["courage", 1.0];
  1895. _unit setSkill ["commanding", 1.0];
  1896. _unit setSkill ["reloadSpeed", 1.0];
  1897. _unit setSkill ["spotDistance", 1.0];
  1898. _unit setSkill ["spotTime", 1.0];
  1899. _unit setSkill ["endurance", 1.0];
  1900. };
  1901.  
  1902. // Force AI to throw smoke grenade using correct muzzle
  1903. fnc_botUseSmoke = {
  1904. params ["_unit", "_purpose"];
  1905.  
  1906. if ("SmokeShell" in (magazines _unit)) then {
  1907. _unit forceWeaponFire ["SmokeShellMuzzle", "SmokeShellMuzzle"];
  1908. };
  1909. };
  1910.  
  1911. // Enhance individual AI unit during spawning
  1912. fnc_enhanceIndividualAI = {
  1913. params ["_unit", "_skillLevel", "_unitRole"];
  1914.  
  1915. if (isNil "_unit" || {isNull _unit} || {!alive _unit}) exitWith {
  1916. diag_log "Error: _unit is invalid in fnc_enhanceIndividualAI";
  1917. };
  1918.  
  1919. if (_unit getVariable ["BOTAI_isEnhanced", false]) exitWith {};
  1920. _unit setVariable ["BOTAI_isEnhanced", true, true];
  1921.  
  1922. // Check if unit is a helicopter pilot/gunner
  1923. private _vehicle = vehicle _unit;
  1924. if (_vehicle != _unit && {_vehicle isKindOf "Air"}) then {
  1925. // Enhanced skills for helicopter crews
  1926. _unit setSkill ["aimingAccuracy", 0.85];
  1927. _unit setSkill ["aimingSpeed", 0.9];
  1928. _unit setSkill ["aimingShake", 0.1];
  1929. _unit setSkill ["courage", 1.0];
  1930. _unit setSkill ["commanding", 1.0];
  1931. _unit setSkill ["reloadSpeed", 1.0];
  1932. _unit setSkill ["spotDistance", 1.0];
  1933. _unit setSkill ["spotTime", 0.9];
  1934. _unit setSkill ["endurance", 1.0];
  1935.  
  1936. // Add flares to helicopters if not present
  1937. if (driver _vehicle == _unit) then {
  1938. private _hasFlares = (magazinesAllTurrets _vehicle) findIf {(_x select 0) find "CMFlare" >= 0} != -1;
  1939. if (!_hasFlares) then {
  1940. _vehicle addMagazineTurret ["120Rnd_CMFlare_Chaff_Magazine", [-1]];
  1941. };
  1942. };
  1943. } else {
  1944. // Original ground unit skills
  1945. [_unit, _skillLevel, _unitRole] call fnc_botSetSkills;
  1946. };
  1947.  
  1948. // Ensure unit has a smoke grenade for tactical use
  1949. private _hasSmoke = false;
  1950. { if (_x find "SmokeShell" >= 0) exitWith { _hasSmoke = true; }; } forEach (magazines _unit);
  1951. if (!_hasSmoke && vehicle _unit == _unit) then {
  1952. _unit addMagazine "SmokeShell";
  1953. };
  1954. };
  1955.  
  1956. // ==================== CHANGE START: DYNAMIC & REWORKED FOLIAGE DETECTION ====================
  1957. // This function has been completely reworked for more accurate foliage detection.
  1958. // It now uses a multi-point "shotgun" check with a dynamically scaled range based on server performance.
  1959. fnc_checkFoliageObstruction = {
  1960. params ["_unit", "_target"];
  1961.  
  1962. // Use cached result if less than 2 seconds old to reduce performance impact.
  1963. private _cacheKey = format ["%1_%2", netId _unit, netId _target];
  1964. private _cachedResult = _unit getVariable ["foliageCache_" + _cacheKey, []];
  1965. if (count _cachedResult > 0 && {time - (_cachedResult select 1) < 2}) exitWith {
  1966. _cachedResult select 0
  1967. };
  1968.  
  1969. private _unitEyePos = eyePos _unit;
  1970.  
  1971. // --- Phase 1: Basic Engine Checks (Fast) ---
  1972. private _visibility = [_unit, "VIEW", _target] checkVisibility [_unitEyePos, eyePos _target];
  1973. private _terrainBlocked = terrainIntersectASL [_unitEyePos, eyePos _target];
  1974.  
  1975. // If the engine is certain the view is blocked by terrain, exit early.
  1976. if (_terrainBlocked && _visibility < 0.1) exitWith {
  1977. _unit setVariable ["foliageCache_" + _cacheKey, [1, time]];
  1978. 1
  1979. };
  1980.  
  1981. // --- Phase 2: Multi-Point "Shotgun" Check (Accurate, but dynamically ranged) ---
  1982. private _intersectionsCount = 0;
  1983.  
  1984. // Define the maximum range for the expensive check based on server performance.
  1985. private _maxCheckDistance = 150; // Default safe distance
  1986. switch (PERF_STATE) do {
  1987. case "EXCELLENT": { _maxCheckDistance = 1000; };
  1988. case "GOOD": { _maxCheckDistance = 800; };
  1989. case "MODERATE": { _maxCheckDistance = 400; };
  1990. case "POOR": { _maxCheckDistance = 200; };
  1991. // On CRITICAL/EMERGENCY, we stick to the 150m default.
  1992. };
  1993.  
  1994. // This expensive check only runs for targets within the dynamically determined range.
  1995. if ((_unit distance2D _target) < _maxCheckDistance) then {
  1996. // ==================== CHANGE START: Robust Type Validation ====================
  1997. private _torsoPos = _target aimedAtTarget [_unit, "Torso"];
  1998.  
  1999. // Validate the result. Check if it's NOT an array OR if it's the known failure position [0,0,0].
  2000. if (typeName _torsoPos != "ARRAY" || {_torsoPos isEqualTo [0,0,0]}) then {
  2001. _torsoPos = getPosATL _target; // Fallback to the unit's center position.
  2002. };
  2003.  
  2004. // We check three points on the target: Head, Torso, and Legs.
  2005. private _targetPositions = [
  2006. eyePos _target, // Head
  2007. _torsoPos, // Torso
  2008. getPosATL _target // Legs/Feet
  2009. ];
  2010. // ===================== CHANGE END =====================
  2011.  
  2012. // Count how many of the three lines are blocked by any object surface.
  2013. {
  2014. if (count (lineIntersectsSurfaces [_unitEyePos, _x, _unit, _target, true, 1]) > 0) then {
  2015. _intersectionsCount = _intersectionsCount + 1;
  2016. };
  2017. } forEach _targetPositions;
  2018. };
  2019.  
  2020. // --- Phase 3: Calculate Final Obstruction Score ---
  2021. private _foliagePenalty = 0;
  2022. // Add a significant penalty for each line that was blocked.
  2023. // 1 blocked line = partial cover, 3 blocked lines = heavy cover.
  2024. switch (_intersectionsCount) do {
  2025. case 1: { _foliagePenalty = 0.3; }; // Partially obscured
  2026. case 2: { _foliagePenalty = 0.6; }; // Mostly obscured
  2027. case 3: { _foliagePenalty = 0.9; }; // Fully obscured by foliage
  2028. };
  2029.  
  2030. // Also check for smoke, which is a separate type of obstruction.
  2031. private _smokeObstruction = 0;
  2032. {
  2033. if (_unit distance2D _x < (_unit distance2D _target)) then {
  2034. _smokeObstruction = _smokeObstruction + 0.5;
  2035. };
  2036. } forEach (_unit nearObjects ["SmokeShell", 40]);
  2037.  
  2038. // Combine all factors:
  2039. // 1. Start with the inverse of the engine's visibility score.
  2040. // 2. Add the heavy penalty from our multi-point foliage check.
  2041. // 3. Add penalties for smoke and terrain.
  2042. private _totalObstruction = (1 - _visibility) + _foliagePenalty + _smokeObstruction;
  2043. if (_terrainBlocked) then { _totalObstruction = _totalObstruction + 0.5; };
  2044.  
  2045. // Clamp the final value between 0 (clear) and 1 (fully obstructed).
  2046. _totalObstruction = _totalObstruction min 1;
  2047.  
  2048. // Cache the result before returning.
  2049. _unit setVariable ["foliageCache_" + _cacheKey, [_totalObstruction, time]];
  2050.  
  2051. _totalObstruction
  2052. };
  2053.  
  2054. // Check if a group has anti-tank capability
  2055. fnc_groupHasAT = {
  2056. params ["_group"];
  2057.  
  2058. private _hasAT = false;
  2059. private _units = units _group select {alive _x};
  2060.  
  2061. {
  2062. private _secondaryWeapon = secondaryWeapon _x;
  2063. if (_secondaryWeapon != "") then {
  2064. _hasAT = true;
  2065. break;
  2066. };
  2067. } forEach _units;
  2068.  
  2069. _hasAT
  2070. };
  2071.  
  2072. // Make AI infantry evade from enemy vehicles if they lack AT weapons
  2073. fnc_evadeEnemyVehicles = {
  2074. params ["_unit"];
  2075.  
  2076. if (vehicle _unit != _unit) exitWith {}; // Skip if in vehicle
  2077.  
  2078. // Only process for group leaders to avoid breaking formation
  2079. private _group = group _unit;
  2080. if (_unit != leader _group) exitWith {};
  2081.  
  2082. private _lastVehicleEvadeCheck = _group getVariable ["lastVehicleEvadeCheck", 0];
  2083.  
  2084. // Check every 5-8 seconds scaled by performance
  2085. if (time - _lastVehicleEvadeCheck < (5 * PERF_COOLDOWN_MULTIPLIER)) exitWith {};
  2086. _group setVariable ["lastVehicleEvadeCheck", time];
  2087.  
  2088. // Check if group has AT capability
  2089. private _hasAT = [_group] call fnc_groupHasAT;
  2090. if (_hasAT) exitWith {}; // Group can handle vehicles, no need to evade
  2091.  
  2092. private _enemySide = if (side _unit == west) then {east} else {west};
  2093. private _unitPos = getPosATL _unit;
  2094.  
  2095. // Search for enemy vehicles within 300m - scaled by performance
  2096. private _searchRadius = 300 * PERF_SEARCH_RADIUS_MULTIPLIER;
  2097. private _nearbyVehicles = _unitPos nearEntities [["Tank", "Wheeled_APC_F", "Tracked_APC_F"], _searchRadius];
  2098. private _threatVehicles = _nearbyVehicles select {
  2099. side _x == _enemySide &&
  2100. alive _x &&
  2101. (_x isKindOf "Tank" || _x isKindOf "Wheeled_APC_F" || _x isKindOf "Tracked_APC_F")
  2102. };
  2103.  
  2104. if (count _threatVehicles == 0) exitWith {};
  2105.  
  2106. // Find closest threat
  2107. private _closestVehicle = _threatVehicles select 0;
  2108. private _closestDistance = _unit distance2D _closestVehicle;
  2109.  
  2110. {
  2111. private _dist = _unit distance2D _x;
  2112. if (_dist < _closestDistance) then {
  2113. _closestVehicle = _x;
  2114. _closestDistance = _dist;
  2115. };
  2116. } forEach _threatVehicles;
  2117.  
  2118. // Threat assessment based on distance
  2119. private _shouldEvade = false;
  2120. private _isUrgent = false;
  2121.  
  2122. if (_closestDistance < 100) then {
  2123. _shouldEvade = true;
  2124. _isUrgent = true; // Very close, urgent evasion
  2125. } else {
  2126. if (_closestDistance < 200) then {
  2127. // Check if vehicle is moving towards us
  2128. private _vehicleVelocity = velocity _closestVehicle;
  2129. private _vehicleSpeed = sqrt((_vehicleVelocity select 0)^2 + (_vehicleVelocity select 1)^2);
  2130.  
  2131. if (_vehicleSpeed > 2) then {
  2132. private _vehicleDir = direction _closestVehicle;
  2133. private _dirToUnit = _closestVehicle getDir _unit;
  2134. private _angleDiff = abs(_vehicleDir - _dirToUnit);
  2135.  
  2136. // If vehicle heading towards us (within 45 degrees)
  2137. if (_angleDiff < 45 || _angleDiff > 315) then {
  2138. _shouldEvade = true;
  2139. };
  2140. };
  2141. };
  2142. };
  2143.  
  2144. if (!_shouldEvade) exitWith {};
  2145.  
  2146. // Check if already evading
  2147. if (_group getVariable ["evadingVehicle", false]) exitWith {};
  2148.  
  2149. // Mark group as evading
  2150. _group setVariable ["evadingVehicle", true];
  2151. _group setVariable ["evadeStartTime", time];
  2152.  
  2153. // Find cover away from the vehicle
  2154. private _dirAwayFromVehicle = _closestVehicle getDir _unit;
  2155. private _coverSearchPos = _unitPos getPos [100, _dirAwayFromVehicle];
  2156.  
  2157. // Try to find hard cover (buildings/walls)
  2158. private _coverPos = [];
  2159. private _nearBuildings = _coverSearchPos nearObjects ["House", 150];
  2160.  
  2161. if (count _nearBuildings > 0) then {
  2162. private _closestBuilding = _nearBuildings select 0;
  2163. private _minDist = 9999;
  2164.  
  2165. {
  2166. private _dist = _coverSearchPos distance _x;
  2167. if (_dist < _minDist) then {
  2168. _minDist = _dist;
  2169. _closestBuilding = _x;
  2170. };
  2171. } forEach _nearBuildings;
  2172.  
  2173. _coverPos = getPosATL _closestBuilding;
  2174. };
  2175.  
  2176. // Fallback to terrain-based cover if no buildings
  2177. if (count _coverPos == 0) then {
  2178. _coverPos = [_unit, 150] call fnc_findNearestCover;
  2179. };
  2180.  
  2181. // Final fallback: just move away from vehicle
  2182. if (count _coverPos == 0) then {
  2183. _coverPos = _unitPos getPos [150, _dirAwayFromVehicle];
  2184. };
  2185.  
  2186. if (count _coverPos > 0 && !surfaceIsWater _coverPos) then {
  2187. // Clear existing waypoints to prevent conflicts
  2188. while {count (waypoints _group) > 0} do {
  2189. deleteWaypoint ((waypoints _group) select 0);
  2190. };
  2191.  
  2192. // Order entire group to cover using waypoint system
  2193. private _wp = _group addWaypoint [_coverPos, 20];
  2194. _wp setWaypointType "MOVE";
  2195. _wp setWaypointCompletionRadius 30;
  2196.  
  2197. if (_isUrgent) then {
  2198. _wp setWaypointSpeed "FULL";
  2199. _wp setWaypointBehaviour "AWARE";
  2200. {_x setUnitPos "DOWN";} forEach (units _group);
  2201. } else {
  2202. _wp setWaypointSpeed "FULL";
  2203. _wp setWaypointBehaviour "AWARE";
  2204. {_x setUnitPos "MIDDLE";} forEach (units _group);
  2205. };
  2206.  
  2207. // Spawn monitoring script
  2208. [_group, _coverPos, _closestVehicle] spawn {
  2209. params ["_evadingGroup", "_targetCover", "_threatVehicle"];
  2210.  
  2211. private _timeout = time + 30; // 30 second timeout
  2212. private _leader = leader _evadingGroup;
  2213.  
  2214. while {!isNull _evadingGroup && count (units _evadingGroup) > 0 && time < _timeout} do {
  2215. if (isNull _leader || !alive _leader) then {
  2216. _leader = leader _evadingGroup;
  2217. };
  2218.  
  2219. // Check if reached cover or threat is gone
  2220. if (!isNull _leader && (_leader distance2D _targetCover) < 40) exitWith {};
  2221.  
  2222. if (!alive _threatVehicle || (!isNull _leader && (_leader distance2D _threatVehicle) > 350)) exitWith {};
  2223.  
  2224. sleep 1;
  2225. };
  2226.  
  2227. // Clear evading flag and restore AUTO stance
  2228. if (!isNull _evadingGroup) then {
  2229. _evadingGroup setVariable ["evadingVehicle", false];
  2230. {_x setUnitPos "AUTO";} forEach (units _evadingGroup);
  2231. };
  2232. };
  2233. };
  2234. };
  2235.  
  2236. // Find nearest cover position for AI unit - OPTIMIZED
  2237. fnc_findNearestCover = {
  2238. params ["_unit", ["_maxDistance", 50]];
  2239.  
  2240. private _unitPos = getPosATL _unit;
  2241.  
  2242. // OPTIMIZATION: Cache cover position for much longer
  2243. private _cachedCover = _unit getVariable ["cachedCoverPos", []];
  2244. private _cachedCoverTime = _unit getVariable ["cachedCoverTime", 0];
  2245.  
  2246. // Use cached position if less than 45 seconds old
  2247. if (time - _cachedCoverTime < 45 && count _cachedCover > 0) exitWith {
  2248. _cachedCover
  2249. };
  2250.  
  2251. private _bestCoverPos = [];
  2252. private _bestCoverScore = -1;
  2253.  
  2254. // OPTIMIZATION: Smaller search radius for terrain objects
  2255. private _searchRadius = 25; // Reduced from 30
  2256.  
  2257. private _terrainObjects = nearestTerrainObjects [_unitPos, ["TREE", "ROCK", "WALL"], _searchRadius, false, true];
  2258.  
  2259. // Only check first 5 objects
  2260. private _checkCount = (count _terrainObjects) min 5;
  2261.  
  2262. for "_i" from 0 to (_checkCount - 1) do {
  2263. private _object = _terrainObjects select _i;
  2264. private _objectPos = getPosATL _object;
  2265. private _distanceToObject = _unitPos distance2D _objectPos;
  2266.  
  2267. if (_distanceToObject > 3 && _distanceToObject < _searchRadius) then {
  2268. // Simplified scoring
  2269. private _coverScore = 10 / _distanceToObject;
  2270.  
  2271. if (_coverScore > _bestCoverScore) then {
  2272. private _dirToObject = [_unitPos, _objectPos] call BIS_fnc_dirTo;
  2273. private _coverPos = _objectPos getPos [2, _dirToObject + 180];
  2274.  
  2275. if (!surfaceIsWater _coverPos) then {
  2276. _bestCoverScore = _coverScore;
  2277. _bestCoverPos = _coverPos;
  2278. };
  2279. };
  2280. };
  2281. };
  2282.  
  2283. // OPTIMIZATION: Simplified building check - only check very close buildings
  2284. if (_bestCoverScore < 3) then {
  2285. private _nearBuildings = _unitPos nearObjects ["House", 20]; // Reduced from 25
  2286.  
  2287. // Only check first 3 buildings
  2288. private _buildingCount = (count _nearBuildings) min 3;
  2289.  
  2290. for "_i" from 0 to (_buildingCount - 1) do {
  2291. private _building = _nearBuildings select _i;
  2292. private _buildingPos = getPosATL _building;
  2293. private _distanceToBuilding = _unitPos distance2D _buildingPos;
  2294.  
  2295. if (_distanceToBuilding > 5) then {
  2296. private _coverScore = 15 / _distanceToBuilding;
  2297.  
  2298. if (_coverScore > _bestCoverScore) then {
  2299. _bestCoverScore = _coverScore;
  2300. _bestCoverPos = _buildingPos;
  2301. };
  2302. };
  2303. };
  2304. };
  2305.  
  2306. // OPTIMIZATION: Simplified terrain check - only 4 directions
  2307. if (count _bestCoverPos == 0) then {
  2308. for "_angle" from 0 to 270 step 90 do {
  2309. private _testPos = _unitPos getPos [15, _angle];
  2310. private _heightDiff = (getTerrainHeightASL _unitPos) - (getTerrainHeightASL _testPos);
  2311.  
  2312. if (_heightDiff > 1 && !surfaceIsWater _testPos) then {
  2313. if (_heightDiff > _bestCoverScore) then {
  2314. _bestCoverScore = _heightDiff;
  2315. _bestCoverPos = _testPos;
  2316. };
  2317. };
  2318. };
  2319. };
  2320.  
  2321. // Cache result for longer
  2322. _unit setVariable ["cachedCoverPos", _bestCoverPos];
  2323. _unit setVariable ["cachedCoverTime", time];
  2324.  
  2325. _bestCoverPos
  2326. };
  2327.  
  2328. // Move AI unit to tactical position when under fire - OPTIMIZED
  2329. fnc_moveToTacticalPosition = {
  2330. params ["_unit"];
  2331.  
  2332. if (!alive _unit || vehicle _unit != _unit) exitWith {};
  2333.  
  2334. private _lastRepositionTime = _unit getVariable ["lastRepositionTime", 0];
  2335.  
  2336. // ==================== CHANGE START ====================
  2337. // OPTIMIZATION: Increased minimum interval and tied to performance scaling
  2338. if (time - _lastRepositionTime < (15 * PERF_COOLDOWN_MULTIPLIER)) exitWith {};
  2339. // ===================== CHANGE END =====================
  2340.  
  2341. private _suppression = getSuppression _unit;
  2342. private _damage = damage _unit;
  2343. private _isUnderFire = (time - (_unit getVariable ["lastShotAtUnit_self", 0])) < 5;
  2344.  
  2345. // Check if unit needs to reposition
  2346. private _needsReposition = _suppression > 0.3 || _damage > 0.2 || _isUnderFire;
  2347.  
  2348. if (_needsReposition) then {
  2349. private _cachedCover = _unit getVariable ["cachedCoverPos", []];
  2350. private _cachedCoverTime = _unit getVariable ["cachedCoverTime", 0];
  2351.  
  2352. private _coverPos = [];
  2353.  
  2354. // Use cached position if less than 30 seconds old
  2355. if (time - _cachedCoverTime < 30 && count _cachedCover > 0) then {
  2356. _coverPos = _cachedCover;
  2357. } else {
  2358. _coverPos = [_unit, 40] call fnc_findNearestCover;
  2359. _unit setVariable ["cachedCoverPos", _coverPos];
  2360. _unit setVariable ["cachedCoverTime", time];
  2361. };
  2362.  
  2363. if (count _coverPos > 0) then {
  2364. _unit doMove _coverPos;
  2365. _unit setSpeedMode "FULL";
  2366.  
  2367. if (_suppression > 0.6 || _damage > 0.5) then {
  2368. _unit setUnitPos "DOWN";
  2369. } else {
  2370. _unit setUnitPos "MIDDLE";
  2371. };
  2372.  
  2373. _unit setVariable ["lastRepositionTime", time];
  2374. _unit setVariable ["movingToCover", true];
  2375. _unit setVariable ["coverPosition", _coverPos];
  2376.  
  2377. [_unit, _coverPos] spawn {
  2378. params ["_unit", "_coverPos"];
  2379.  
  2380. waitUntil {sleep 1; !alive _unit || _unit distance2D _coverPos < 5 || time > ((_unit getVariable ["lastRepositionTime", 0]) + 15)};
  2381.  
  2382. if (alive _unit) then {
  2383. _unit setVariable ["movingToCover", false];
  2384. sleep (5 + random 5);
  2385.  
  2386. if (getSuppression _unit < 0.2 && damage _unit < 0.3) then {
  2387. _unit setUnitPos "AUTO";
  2388. };
  2389. };
  2390. };
  2391. };
  2392. };
  2393. };
  2394.  
  2395. // Manage AI combat stance based on situation
  2396. fnc_manageCombatStance = {
  2397. params ["_unit"];
  2398.  
  2399. if (!alive _unit || vehicle _unit != _unit) exitWith {};
  2400.  
  2401. private _suppression = getSuppression _unit;
  2402. private _damage = damage _unit;
  2403. // ==================== CHANGE START ====================
  2404. private _nearEnemies = _unit nearEntities [["CAManBase", "LandVehicle"], (100 * PERF_SEARCH_RADIUS_MULTIPLIER)];
  2405. // ===================== CHANGE END =====================
  2406. private _enemyCount = {side _x != side _unit && alive _x} count _nearEnemies;
  2407. private _isMovingToCover = _unit getVariable ["movingToCover", false];
  2408.  
  2409. if (_isMovingToCover) exitWith {};
  2410.  
  2411. if (_suppression > 0.7 || _damage > 0.6) then {
  2412. _unit setUnitPos "DOWN";
  2413. } else {
  2414. if (_suppression > 0.3 || _enemyCount > 3) then {
  2415. _unit setUnitPos "MIDDLE";
  2416. } else {
  2417. if (_enemyCount > 0 && _enemyCount <= 3) then {
  2418. if (random 1 > 0.3) then {
  2419. _unit setUnitPos "MIDDLE";
  2420. } else {
  2421. _unit setUnitPos "AUTO";
  2422. };
  2423. } else {
  2424. _unit setUnitPos "AUTO";
  2425. };
  2426. };
  2427. };
  2428.  
  2429. if (_enemyCount > 0) then {
  2430. if (_suppression > 0.5) then {
  2431. _unit setBehaviour "SAFE";
  2432. _unit setSpeedMode "FULL";
  2433. } else {
  2434. _unit setBehaviour "COMBAT";
  2435. _unit setSpeedMode "NORMAL";
  2436. };
  2437. } else {
  2438. if (behaviour _unit != "STEALTH") then {
  2439. _unit setBehaviour "AWARE";
  2440. };
  2441. };
  2442. };
  2443.  
  2444. // Assign flank watch sectors to group members
  2445. fnc_assignFlankWatchSectors = {
  2446. params ["_group"];
  2447.  
  2448. if (isNull _group || count (units _group) == 0) exitWith {};
  2449.  
  2450. private _leader = leader _group;
  2451. if (isNull _leader || !alive _leader) exitWith {};
  2452.  
  2453. private _groupUnits = units _group select {alive _x && vehicle _x == _x};
  2454. private _unitCount = count _groupUnits;
  2455.  
  2456. if (_unitCount == 0) exitWith {};
  2457.  
  2458. private _leaderDir = direction _leader;
  2459. private _currentWP = currentWaypoint _group;
  2460.  
  2461. if (_currentWP > 0) then {
  2462. private _wpPos = waypointPosition [_group, _currentWP];
  2463. if (count _wpPos > 0 && !(_wpPos isEqualTo [0,0,0])) then {
  2464. _leaderDir = _leader getDir _wpPos;
  2465. };
  2466. };
  2467.  
  2468. if (_unitCount == 1) then {
  2469. _groupUnits select 0 setVariable ["watchSector", _leaderDir];
  2470. _groupUnits select 0 setVariable ["sectorWidth", 180];
  2471. } else {
  2472. if (_unitCount == 2) then {
  2473. _groupUnits select 0 setVariable ["watchSector", _leaderDir];
  2474. _groupUnits select 0 setVariable ["sectorWidth", 180];
  2475. _groupUnits select 1 setVariable ["watchSector", _leaderDir + 180];
  2476. _groupUnits select 1 setVariable ["sectorWidth", 180];
  2477. } else {
  2478. if (_unitCount == 3) then {
  2479. _groupUnits select 0 setVariable ["watchSector", _leaderDir];
  2480. _groupUnits select 0 setVariable ["sectorWidth", 120];
  2481. _groupUnits select 1 setVariable ["watchSector", _leaderDir - 90];
  2482. _groupUnits select 1 setVariable ["sectorWidth", 120];
  2483. _groupUnits select 2 setVariable ["watchSector", _leaderDir + 90];
  2484. _groupUnits select 2 setVariable ["sectorWidth", 120];
  2485. } else {
  2486. private _sectorSize = 360 / _unitCount;
  2487.  
  2488. for "_i" from 0 to (_unitCount - 1) do {
  2489. private _unit = _groupUnits select _i;
  2490. private _sectorDir = _leaderDir + (_i * _sectorSize);
  2491.  
  2492. _unit setVariable ["watchSector", _sectorDir];
  2493. _unit setVariable ["sectorWidth", _sectorSize + 20];
  2494. };
  2495. };
  2496. };
  2497. };
  2498.  
  2499. _group setVariable ["sectorsAssigned", true];
  2500. _group setVariable ["sectorAssignmentTime", time];
  2501. };
  2502.  
  2503. // Make AI unit scan their assigned sector
  2504. fnc_scanAssignedSector = {
  2505. params ["_unit"];
  2506.  
  2507. if (!alive _unit || vehicle _unit != _unit) exitWith {};
  2508. if (behaviour _unit == "COMBAT") exitWith {};
  2509.  
  2510. private _assignedSector = _unit getVariable ["watchSector", -1];
  2511. private _sectorWidth = _unit getVariable ["sectorWidth", 90];
  2512.  
  2513. if (_assignedSector < 0) exitWith {};
  2514.  
  2515. private _lastScanTime = _unit getVariable ["lastSectorScan", 0];
  2516.  
  2517. if (time - _lastScanTime > (3 + random 3)) then {
  2518. private _scanDir = _assignedSector + (random _sectorWidth - (_sectorWidth / 2));
  2519. private _unitPos = getPosATL _unit;
  2520. private _lookAtPos = _unitPos getPos [50, _scanDir];
  2521.  
  2522. _unit doWatch _lookAtPos;
  2523. _unit setVariable ["lastSectorScan", time];
  2524.  
  2525. [_unit] spawn {
  2526. params ["_unit"];
  2527. sleep (2 + random 2);
  2528. if (alive _unit && behaviour _unit != "COMBAT") then {
  2529. _unit doWatch objNull;
  2530. };
  2531. };
  2532. };
  2533. };
  2534.  
  2535. // Maintain tactical spacing between group members - OPTIMIZED
  2536. fnc_maintainTacticalSpacing = {
  2537. params ["_group"];
  2538.  
  2539. if (isNull _group || count (units _group) == 0) exitWith {};
  2540.  
  2541. private _leader = leader _group;
  2542. if (isNull _leader || !alive _leader) exitWith {};
  2543.  
  2544. private _groupUnits = units _group select {alive _x && vehicle _x == _x && _x != _leader};
  2545.  
  2546. if (count _groupUnits == 0) exitWith {};
  2547.  
  2548. private _leaderPos = getPosATL _leader;
  2549. private _optimalSpacing = 5;
  2550. private _maxSpacing = 15;
  2551.  
  2552. {
  2553. private _unit = _x;
  2554. private _unitPos = getPosATL _unit;
  2555. private _distToLeader = _unitPos distance2D _leaderPos;
  2556.  
  2557. // Early exit if already well-positioned
  2558. if (_distToLeader > _optimalSpacing && _distToLeader < _maxSpacing) then {
  2559. continue;
  2560. };
  2561.  
  2562. // ==================== CHANGE START ====================
  2563. // OPTIMIZATION: Replaced nested loop with a single, fast nearEntities check.
  2564. private _tooClose = count (_unit nearEntities ["CAManBase", _optimalSpacing]) > 1; // >1 to not count self
  2565.  
  2566. if (_tooClose && !(_unit getVariable ["movingToCover", false])) then {
  2567. // ===================== CHANGE END =====================
  2568. private _lastSpacingAdjust = _unit getVariable ["lastSpacingAdjust", 0];
  2569.  
  2570. if (time - _lastSpacingAdjust > 8) then {
  2571. private _repositionDir = _leaderPos getDir _unitPos;
  2572. private _repositionPos = _leaderPos getPos [_optimalSpacing + random 3, _repositionDir + (random 40 - 20)];
  2573.  
  2574. _repositionPos = [_repositionPos, 0, 5, 2, 0, 0.3, 0] call BIS_fnc_findSafePos;
  2575.  
  2576. if (count _repositionPos > 0 && !surfaceIsWater _repositionPos) then {
  2577. _unit doMove _repositionPos;
  2578. _unit setVariable ["lastSpacingAdjust", time];
  2579. };
  2580. };
  2581. };
  2582.  
  2583. if (_distToLeader > _maxSpacing && behaviour _unit != "COMBAT") then {
  2584. private _lastSpacingAdjust = _unit getVariable ["lastSpacingAdjust", 0];
  2585.  
  2586. if (time - _lastSpacingAdjust > 10) then {
  2587. _unit doFollow _leader;
  2588. _unit setVariable ["lastSpacingAdjust", time];
  2589. };
  2590. };
  2591. } forEach _groupUnits;
  2592. };
  2593.  
  2594.  
  2595. // NEW: Finds the nearest "hard" cover, prioritizing buildings and walls.
  2596. fnc_botFindHardCover = {
  2597. params ["_unit"];
  2598.  
  2599. private _unitPos = getPosATL _unit;
  2600. private _bestCoverPos = [];
  2601. private _maxScanRadius = 75; // Scan up to 75m for hard cover.
  2602.  
  2603. // Search for nearest buildings first, as they offer the best cover.
  2604. private _nearBuildings = nearestObjects [_unitPos, ["House"], _maxScanRadius];
  2605. if (count _nearBuildings > 0) then {
  2606. // Find the closest building that is not the one the unit is already in.
  2607. private _closestBuilding = objNull;
  2608. private _minDist = 9999;
  2609. {
  2610. private _dist = _unit distance _x;
  2611. if (_dist < _minDist && !(_unit nearObjects [_x, 1])) then {
  2612. _minDist = _dist;
  2613. _closestBuilding = _x;
  2614. };
  2615. } forEach _nearBuildings;
  2616.  
  2617. if (!isNull _closestBuilding) then {
  2618. // Find the nearest position inside the building.
  2619. _bestCoverPos = _closestBuilding buildingPos -1;
  2620. };
  2621. };
  2622.  
  2623. // If no building was found, look for nearby walls.
  2624. if (count _bestCoverPos == 0) then {
  2625. private _nearWalls = nearestTerrainObjects [_unitPos, ["WALL"], _maxScanRadius / 2];
  2626. if (count _nearWalls > 0) then {
  2627. private _closestWall = _nearWalls select 0;
  2628. private _wallPos = getPosATL _closestWall;
  2629. private _dirToWall = [_unitPos, _wallPos] call BIS_fnc_dirTo;
  2630. // Position the AI on the side of the wall facing away from the known threat or just behind it.
  2631. _bestCoverPos = _wallPos getPos [2, _dirToWall + 180];
  2632. };
  2633. };
  2634.  
  2635. _bestCoverPos
  2636. };
  2637.  
  2638. // NEW: Make infantry units seek cover and heal when damaged
  2639. fnc_botSeekCoverAndHeal = {
  2640. params ["_unit"];
  2641.  
  2642. // Only for infantry units
  2643. if (vehicle _unit != _unit) exitWith {};
  2644.  
  2645. // Check damage threshold (20%)
  2646. private _damage = damage _unit;
  2647. if (_damage <= 0.2) exitWith {};
  2648.  
  2649. // Check if unit has a FirstAidKit
  2650. if (!("FirstAidKit" in items _unit)) exitWith {};
  2651.  
  2652. // Check cooldown to prevent constant healing attempts
  2653. private _lastHealAttempt = _unit getVariable ["lastHealAttempt", 0];
  2654. if (time - _lastHealAttempt < 30) exitWith {}; // 30 second cooldown
  2655.  
  2656. // Check if already healing
  2657. if (_unit getVariable ["isHealing", false]) exitWith {};
  2658.  
  2659. _unit setVariable ["lastHealAttempt", time];
  2660. _unit setVariable ["isHealing", true];
  2661.  
  2662. private _unitPos = getPosATL _unit;
  2663. private _coverFound = false;
  2664. private _coverPos = [];
  2665.  
  2666. // Search for hard cover within 20 meters
  2667. private _nearBuildings = nearestObjects [_unitPos, ["House", "Building"], 20];
  2668. private _nearWalls = nearestTerrainObjects [_unitPos, ["WALL"], 20];
  2669.  
  2670. // Try to find building cover first
  2671. if (count _nearBuildings > 0) then {
  2672. // Find closest building that's not the one we're already in
  2673. {
  2674. private _building = _x;
  2675. private _distance = _unit distance _building;
  2676. if (_distance > 2 && _distance <= 20) then {
  2677. // Try to get a building position
  2678. private _buildingPos = _building buildingPos 0;
  2679. if (count _buildingPos > 0) then {
  2680. _coverPos = _buildingPos;
  2681. _coverFound = true;
  2682. } else {
  2683. // If no interior position, use position behind building
  2684. _coverPos = getPosATL _building;
  2685. _coverFound = true;
  2686. };
  2687. };
  2688. if (_coverFound) exitWith {};
  2689. } forEach _nearBuildings;
  2690. };
  2691.  
  2692. // If no building, try walls
  2693. if (!_coverFound && count _nearWalls > 0) then {
  2694. private _closestWall = _nearWalls select 0;
  2695. if (_unit distance _closestWall <= 20) then {
  2696. private _wallPos = getPosATL _closestWall;
  2697. // Position behind wall relative to unit's current position
  2698. private _dirToWall = _unitPos getDir _wallPos;
  2699. _coverPos = _wallPos getPos [2, _dirToWall + 180];
  2700. _coverFound = true;
  2701. };
  2702. };
  2703.  
  2704. // Execute healing behavior
  2705. if (_coverFound) then {
  2706. // Move to cover and heal
  2707. [_unit, _coverPos] spawn {
  2708. params ["_unit", "_coverPos"];
  2709.  
  2710. _unit doMove _coverPos;
  2711. _unit setSpeedMode "FULL";
  2712.  
  2713. // Wait until unit reaches cover or times out
  2714. private _timeout = time + 15;
  2715. waitUntil {
  2716. sleep 0.5;
  2717. !alive _unit ||
  2718. (_unit distance2D _coverPos) < 3 ||
  2719. time > _timeout
  2720. };
  2721.  
  2722. if (alive _unit && "FirstAidKit" in items _unit) then {
  2723. // Go prone and heal
  2724. _unit setUnitPos "DOWN";
  2725. sleep 1;
  2726. _unit action ["HealSoldierSelf", _unit];
  2727. _unit removeItem "FirstAidKit";
  2728.  
  2729. // Stay in cover for a few seconds
  2730. sleep 5;
  2731. _unit setUnitPos "AUTO";
  2732. };
  2733.  
  2734. _unit setVariable ["isHealing", false];
  2735. };
  2736. } else {
  2737. // No cover found - go prone and heal at current position
  2738. [_unit] spawn {
  2739. params ["_unit"];
  2740.  
  2741. if (alive _unit && "FirstAidKit" in items _unit) then {
  2742. _unit setUnitPos "DOWN";
  2743. sleep 1;
  2744. _unit action ["HealSoldierSelf", _unit];
  2745. _unit removeItem "FirstAidKit";
  2746.  
  2747. // Stay prone for a few seconds
  2748. sleep 5;
  2749. _unit setUnitPos "AUTO";
  2750. };
  2751.  
  2752. _unit setVariable ["isHealing", false];
  2753. };
  2754. };
  2755. };
  2756.  
  2757. // NEW: Makes AI squads attempt to scavenge an AT launcher from a fallen teammate if a vehicle threat is present.
  2758. fnc_bot_scavengeATWeapon = {
  2759. params ["_group"];
  2760.  
  2761. // Cooldown: Only check every 15-20 seconds per group
  2762. if (time < (_group getVariable ["lastATScavengeCheck", 0]) + (15 + random 5)) exitWith {};
  2763. _group setVariable ["lastATScavengeCheck", time];
  2764.  
  2765. // Exit if another scavenger is already tasked for this group
  2766. if (_group getVariable ["isScavengingAT", false]) exitWith {};
  2767.  
  2768. private _leader = leader _group;
  2769. if (isNull _leader || !alive _leader) exitWith {};
  2770.  
  2771. // 1. Find the primary vehicle threat
  2772. private _enemySide = if (side _group == west) then {east} else {west};
  2773. private _threats = _leader nearEntities [["Tank", "Armored"], 300];
  2774. private _targetVehicle = objNull;
  2775.  
  2776. {
  2777. if (side _x == _enemySide && alive _x) exitWith {
  2778. _targetVehicle = _x;
  2779. };
  2780. } forEach _threats;
  2781.  
  2782. // If no vehicle threat, exit.
  2783. if (isNull _targetVehicle) exitWith {};
  2784.  
  2785. // 2. Check for living AT soldiers in the group. If any exist, they should handle it.
  2786. private _livingUnits = units _group select {alive _x};
  2787. private _hasLivingAT = false;
  2788. {
  2789. private _unitWeaponry = secondaryWeaponItems _x;
  2790. if (count _unitWeaponry > 0) exitWith { _hasLivingAT = true; };
  2791. } forEach _livingUnits;
  2792. if (_hasLivingAT) exitWith {};
  2793.  
  2794. // 3. Find a dead AT soldier in the group who has a launcher
  2795. private _deadBody = objNull;
  2796. private _launcherClass = "";
  2797. private _launcherMags = [];
  2798. {
  2799. if (!alive _x) then {
  2800. private _bodyWeaponry = secondaryWeaponItems _x;
  2801. if (count _bodyWeaponry > 0) then {
  2802. _deadBody = _x;
  2803. _launcherClass = _bodyWeaponry # 0; // Get the launcher classname
  2804. _launcherMags = magazines _deadBody select {
  2805. _x in (getArray (configFile >> "CfgWeapons" >> _launcherClass >> "magazines"))
  2806. };
  2807. if (count _launcherMags > 0) then { break; }; // Found a body with a launcher and ammo
  2808. };
  2809. };
  2810. } forEach (units _group);
  2811.  
  2812. // If no suitable body with a launcher and ammo was found, exit.
  2813. if (isNull _deadBody || _launcherClass == "" || count _launcherMags == 0) exitWith {};
  2814.  
  2815. // 4. Find the closest living non-AT soldier to the body to be the "scavenger"
  2816. private _scavenger = objNull;
  2817. private _minDist = 9999;
  2818. {
  2819. if (count (secondaryWeaponItems _x) == 0) then { // Ensure they don't already have an AT weapon
  2820. private _dist = _x distance2D _deadBody;
  2821. if (_dist < _minDist) then {
  2822. _minDist = _dist;
  2823. _scavenger = _x;
  2824. };
  2825. };
  2826. } forEach _livingUnits;
  2827.  
  2828. // If we found a valid scavenger, task them.
  2829. if (!isNull _scavenger) then {
  2830. _group setVariable ["isScavengingAT", true, true]; // Flag the group
  2831. _scavenger setVariable ["isScavenger", true, true]; // Flag the unit
  2832.  
  2833. // Spawn the process to handle the movement and weapon swap
  2834. [_scavenger, _deadBody, _launcherClass, _launcherMags, _targetVehicle] spawn {
  2835. params ["_scavenger", "_deadBody", "_launcherClass", "_launcherMags", "_targetVehicle"];
  2836.  
  2837. // Order the unit to move to the body
  2838. _scavenger doMove (getPos _deadBody);
  2839.  
  2840. // Wait until the unit is close, dies, or the body is gone
  2841. waitUntil {sleep 1; !alive _scavenger || isNull _deadBody || _scavenger distance2D _deadBody < 4};
  2842.  
  2843. if (alive _scavenger && !isNull _deadBody) then {
  2844. // Transfer weapon and magazines
  2845. _scavenger addWeapon _launcherClass;
  2846. { _scavenger addMagazine _x; } forEach _launcherMags;
  2847.  
  2848. // Immediately command the unit to engage the threat
  2849. _scavenger selectWeapon (secondaryWeapon _scavenger);
  2850. _scavenger doTarget _targetVehicle;
  2851. _scavenger doFire _targetVehicle;
  2852. };
  2853.  
  2854. // Clean up flags
  2855. (group _scavenger) setVariable ["isScavengingAT", false, true];
  2856. _scavenger setVariable ["isScavenger", false, true];
  2857. };
  2858. };
  2859. };
  2860. // NEW: Core function to make units react to suppression in the open and to friendly casualties.
  2861. fnc_botReactToThreats = {
  2862. params ["_unit"];
  2863. if (vehicle _unit != _unit) exitWith {}; // Exit if in vehicle.
  2864.  
  2865. // --- Suppression Response ---
  2866. private _isUnderFire = (getSuppression _unit > 0.4) || (time - (_unit getVariable ["lastShotAtUnit_self", 0])) < 3;
  2867. private _isInCombat = behaviour _unit == "COMBAT";
  2868. private _isTakingCover = time - (_unit getVariable ["BOTAI_takingCoverTime", 0]) < 20; // 20 second cooldown
  2869.  
  2870. if (_isUnderFire && !_isInCombat && !_isTakingCover) then {
  2871. // Check if unit is in the open (more than 15m from any cover object).
  2872. private _coverObjects = nearestTerrainObjects [getPosATL _unit, ["TREE", "ROCK", "WALL", "BUSH"], 15];
  2873. private _nearBuildings = nearestObjects [getPosATL _unit, ["House"], 15];
  2874.  
  2875. if (count _coverObjects == 0 && count _nearBuildings == 0) then {
  2876. // Unit is in the open, find hard cover.
  2877. private _coverPos = [_unit] call fnc_botFindHardCover;
  2878. if (count _coverPos > 0) then {
  2879. _unit setVariable ["BOTAI_takingCoverTime", time]; // Set cooldown.
  2880. _unit setSpeedMode "FULL"; // Sprint!
  2881. _unit doMove _coverPos;
  2882.  
  2883. // Once in cover, hold position and wait for leader.
  2884. [_unit, _coverPos] spawn {
  2885. params ["_unit", "_pos"];
  2886. waitUntil {sleep 1; !alive _unit || _unit distance _pos < 5};
  2887. if (alive _unit) then {
  2888. // Clear waypoints to make the unit hold. The main HC loop will re-task them eventually.
  2889. while {count (waypoints (group _unit)) > 0} do {
  2890. deleteWaypoint ((waypoints (group _unit)) select 0);
  2891. };
  2892. _unit setSpeedMode "NORMAL";
  2893. };
  2894. };
  2895. };
  2896. };
  2897. };
  2898.  
  2899. // --- Casualty Awareness ---
  2900. private _lastBodyCheck = _unit getVariable ["BOTAI_lastBodyCheck", 0];
  2901. if (time - _lastBodyCheck > 10) then { // Check every 10 seconds.
  2902. _unit setVariable ["BOTAI_lastBodyCheck", time];
  2903.  
  2904. private _friendlyBodies = (getPosATL _unit) nearEntities ["CAManBase", 40] select {
  2905. side _x == side _unit && !alive _x
  2906. };
  2907. private _bodyCount = count _friendlyBodies;
  2908.  
  2909. if (_bodyCount > 0) then {
  2910. // If any friendly body is nearby, go to max alert.
  2911. if (behaviour _unit != "COMBAT") then { _unit setBehaviour "COMBAT"; };
  2912. if (combatMode _unit != "RED") then { _unit setCombatMode "RED"; };
  2913.  
  2914. // If more than one body is found, try to move away from the cluster.
  2915. if (_bodyCount > 1) then {
  2916. private _isAvoiding = time - (_unit getVariable ["BOTAI_avoidingBodiesTime", 0]) < 60;
  2917. if (!_isAvoiding) then {
  2918. _unit setVariable ["BOTAI_avoidingBodiesTime", time]; // Set cooldown.
  2919.  
  2920. // Find center of the bodies.
  2921. private _avgX = 0; private _avgY = 0;
  2922. { _avgX = _avgX + ((getPos _x) select 0); _avgY = _avgY + ((getPos _x) select 1); } forEach _friendlyBodies;
  2923. private _bodyCenter = [_avgX / _bodyCount, _avgY / _bodyCount, 0];
  2924.  
  2925. // Find a new position 50-75m away from the danger zone.
  2926. private _dirAway = [_bodyCenter, getPosATL _unit] call BIS_fnc_dirTo;
  2927. private _newPos = (getPosATL _unit) getPos [50 + (random 25), _dirAway];
  2928.  
  2929. // Order a move to the safer position.
  2930. _unit doMove _newPos;
  2931. };
  2932. };
  2933. };
  2934. };
  2935. };
  2936.  
  2937. // Main server-side AI loop - REWORKED WITH NEW PERFORMANCE SCALING
  2938. if (isServer) then {
  2939. [] spawn {
  2940. waitUntil { time > 20 };
  2941.  
  2942. private _unitIndex = 0;
  2943. private _allAiUnits = [];
  2944. private _lastUnitRefresh = 0;
  2945.  
  2946. while {true} do {
  2947. private _cycleStartTime = diag_tickTime;
  2948.  
  2949. // Refresh unit list less frequently
  2950. if (time > _lastUnitRefresh + 45 || count _allAiUnits == 0) then {
  2951. _allAiUnits = allUnits select {
  2952. !isNull _x && alive _x && !isPlayer _x && side _x in [west, east]
  2953. };
  2954. _lastUnitRefresh = time;
  2955. };
  2956.  
  2957. // This now uses the definitive batch size from performance.sqf
  2958. private _batchSize = (PERF_AI_BATCH_SIZE min (count _allAiUnits));
  2959. if (_batchSize == 0) then { _batchSize = 1; };
  2960.  
  2961. private _endIndex = (_unitIndex + _batchSize) min (count _allAiUnits);
  2962.  
  2963. // This frame-time budget prevents a single AI cycle from causing a major lag spike.
  2964. private _frameTimeBudget = 0.030; // 30ms max per cycle
  2965.  
  2966. for "_i" from _unitIndex to (_endIndex - 1) do {
  2967. if ((diag_tickTime - _cycleStartTime) > _frameTimeBudget) exitWith {
  2968. diag_log format ["[BOTAI] Frame budget exceeded at unit %1/%2", _i - _unitIndex, _batchSize];
  2969. };
  2970.  
  2971. private _unit = _allAiUnits select _i;
  2972. if (!isNull _unit && alive _unit) then {
  2973. private _vehicle = vehicle _unit;
  2974. private _isInfantry = (_vehicle == _unit);
  2975.  
  2976. private _enemySide = if (side _unit == west) then {east} else {west};
  2977. private _unitPos = getPos _unit;
  2978.  
  2979. if (_isInfantry) then {
  2980. private _group = group _unit;
  2981. // REWORKED BINOCULAR FIX: Prevents AI from being stuck with binoculars, but allows AT soldiers to aim their launchers.
  2982. if (currentWeapon _unit isKindOf "Binocular" && behaviour _unit == "COMBAT") then {
  2983. private _isPreparingToFireLauncher = false;
  2984. // Check if the AI has a launcher and a valid vehicle target nearby.
  2985. if (secondaryWeapon _unit != "") then {
  2986. // Use findIf for efficiency; it stops once a target is found.
  2987. private _foundTarget = (_unit nearEntities [["Tank", "Armored"], (500 * PERF_SEARCH_RADIUS_MULTIPLIER)]) findIf {
  2988. side _x == _enemySide && alive _x && _unit knowsAbout _x > 1.5
  2989. };
  2990.  
  2991. // If a valid target was found (findIf returns index >= 0), then the AI is likely aiming its launcher.
  2992. if (_foundTarget != -1) then {
  2993. _isPreparingToFireLauncher = true;
  2994. };
  2995. };
  2996.  
  2997. // If the AI is NOT preparing to fire a launcher, THEN it's safe to force a switch to the primary rifle.
  2998. if (!_isPreparingToFireLauncher) then {
  2999. _unit selectWeapon (primaryWeapon _unit);
  3000. };
  3001. };
  3002.  
  3003. // NEW: High-priority reactions to immediate threats.
  3004. [_unit] call fnc_botReactToThreats;
  3005. // Do not process other behaviors if the unit is busy taking cover.
  3006. if (time - (_unit getVariable ["BOTAI_takingCoverTime", 0]) < 20) then { continue; };
  3007.  
  3008. // NEW: Vehicle evasion for infantry without AT (scales with performance)
  3009. [_unit] call fnc_evadeEnemyVehicles;
  3010.  
  3011. private _unitBehaviour = behaviour _unit;
  3012.  
  3013. // CRITICAL: Combat behaviors are always processed.
  3014. if (_unitBehaviour == "COMBAT") then {
  3015. [_unit] call fnc_moveToTacticalPosition;
  3016. [_unit] call fnc_manageCombatStance;
  3017. };
  3018.  
  3019. // REWORKED: No more PERF_SKIP checks. Functions will just run less often or with smaller ranges.
  3020. // NEW: Check if the group needs to scavenge an AT weapon.
  3021. if (_unit == leader _group) then {
  3022. [_group] call fnc_bot_scavengeATWeapon;
  3023. };
  3024.  
  3025. // HIGH PRIORITY: Check if unit needs to heal
  3026. if (damage _unit > 0.2 && !(_unit getVariable ["isHealing", false])) then {
  3027. [_unit] call fnc_botSeekCoverAndHeal;
  3028. };
  3029.  
  3030. if (!isNull _group && _unitBehaviour in ["AWARE", "SAFE"]) then
  3031. {
  3032. // Sector assignment is infrequent (every 60s)
  3033. if (time > ((_group getVariable ["sectorAssignmentTime", 0]) + (60 * PERF_COOLDOWN_MULTIPLIER))) then {
  3034. [_group] call fnc_assignFlankWatchSectors;
  3035. };
  3036.  
  3037. // Scanning is staggered between units
  3038. if (_i % 2 == 0) then {
  3039. [_unit] call fnc_scanAssignedSector;
  3040. };
  3041.  
  3042. // Spacing is infrequent and only for leaders
  3043. if (_unit == leader _group && (time - (_group getVariable ["lastSpacingCheck", 0])) > 20) then {
  3044. _group setVariable ["lastSpacingCheck", time];
  3045. [_group] call fnc_maintainTacticalSpacing;
  3046. };
  3047. };
  3048.  
  3049. private _lastVehicleAvoidCheck = _unit getVariable ["lastVehicleAvoidCheck", 0];
  3050. if (time > _lastVehicleAvoidCheck + (8 * PERF_COOLDOWN_MULTIPLIER)) then {
  3051. _unit setVariable ["lastVehicleAvoidCheck", time];
  3052.  
  3053. private _nearbyVehicles = _unit nearEntities ["LandVehicle", 10];
  3054. if (count _nearbyVehicles > 0) then {
  3055. private _friendlyVehicle = _nearbyVehicles findIf {side _x == side _unit};
  3056. if (_friendlyVehicle != -1) then {
  3057. private _closestVehicle = _nearbyVehicles select _friendlyVehicle;
  3058. private _dirFromVehicle = _closestVehicle getDir _unit;
  3059. _unit doMove (_unit getPos [5, _dirFromVehicle]);
  3060. };
  3061. };
  3062. };
  3063.  
  3064. // ==================== CHANGE START ====================
  3065. if (secondaryWeapon _unit != "") then {
  3066. private _lastATCheck = _unit getVariable ["lastATCheck", 0];
  3067. if (time > _lastATCheck + (8 * PERF_COOLDOWN_MULTIPLIER)) then {
  3068. _unit setVariable ["lastATCheck", time];
  3069. private _nearVehicles = _unit nearEntities [["LandVehicle", "Air"], (400 * PERF_SEARCH_RADIUS_MULTIPLIER)];
  3070. private _enemyVehicle = _nearVehicles findIf {
  3071. side _x == _enemySide && alive _x && _unit knowsAbout _x > 0
  3072. };
  3073. if (_enemyVehicle != -1) then {
  3074. private _targetVehicle = _nearVehicles select _enemyVehicle;
  3075.  
  3076. // Relaxed foliage check for AT units
  3077. private _obstructionLevel = [_unit, _targetVehicle] call fnc_checkFoliageObstruction;
  3078.  
  3079. // If the vehicle is less than 85% obscured, the AI is cleared to fire.
  3080. if (_obstructionLevel < 0.85) then {
  3081. (group _unit) reveal [_targetVehicle, 4]; // Force reveal to allow firing
  3082. _unit selectWeapon (secondaryWeapon _unit);
  3083. _unit doTarget _targetVehicle;
  3084. _unit doFire _targetVehicle;
  3085. } else {
  3086. // Still partially reveal so the AI tracks the target, but won't waste a shot
  3087. (group _unit) reveal [_targetVehicle, 1.5];
  3088. };
  3089. };
  3090. };
  3091. };
  3092. // ===================== CHANGE END =====================
  3093.  
  3094. private _lastCombinedCheck = _unit getVariable ["lastCombinedCheck", 0];
  3095. if (time > _lastCombinedCheck + (6 * PERF_COOLDOWN_MULTIPLIER)) then {
  3096. _unit setVariable ["lastCombinedCheck", time];
  3097.  
  3098. private _nearTargets = _unit nearTargets (120 * PERF_SEARCH_RADIUS_MULTIPLIER);
  3099. private _targetCount = (count _nearTargets) min 5;
  3100.  
  3101. for "_t" from 0 to (_targetCount - 1) do {
  3102. private _targetData = _nearTargets select _t;
  3103. private _target = _targetData select 4;
  3104.  
  3105. if (alive _target && side _target == _enemySide && _target isKindOf "CAManBase") then {
  3106. private _distance = _unit distance _target;
  3107. if (_distance < (120 * PERF_SEARCH_RADIUS_MULTIPLIER) && _unit knowsAbout _target > 0) then {
  3108. private _obstructionLevel = [_unit, _target] call fnc_checkFoliageObstruction;
  3109. private _isBeingShotAt = (time - (_target getVariable ["lastShotAtUnit_" + str _unit, 0])) < 10;
  3110.  
  3111. if (_obstructionLevel > 0.6) then {
  3112. (group _unit) reveal [_target, 1.5];
  3113. _unit doWatch objNull;
  3114. } else {
  3115. if (_obstructionLevel > 0.15) then {
  3116. if (_isBeingShotAt) then {
  3117. (group _unit) reveal [_target, 4];
  3118. } else {
  3119. (group _unit) reveal [_target, 1.5];
  3120. _unit doWatch (getPos _target);
  3121. };
  3122. } else {
  3123. (group _unit) reveal [_target, 4];
  3124. };
  3125. };
  3126. };
  3127. };
  3128. };
  3129.  
  3130. private _lastDroneCheck = _unit getVariable ["lastDroneCheck", 0];
  3131. if (time > _lastDroneCheck + (10 * PERF_COOLDOWN_MULTIPLIER)) then {
  3132. _unit setVariable ["lastDroneCheck", time];
  3133. private _nearbyAir = _unit nearEntities ["Air", (350 * PERF_SEARCH_RADIUS_MULTIPLIER)];
  3134. if (count _nearbyAir > 0) then {
  3135. private _enemyDrone = _nearbyAir findIf {
  3136. alive _x && side _x == _enemySide &&
  3137. (_x isKindOf "UAV_01_base_F" || _x isKindOf "UAV_02_base_F" || _x isKindOf "UAV")
  3138. };
  3139. if (_enemyDrone != -1) then {
  3140. private _drone = _nearbyAir select _enemyDrone;
  3141. if (_unit distance _drone < (350 * PERF_SEARCH_RADIUS_MULTIPLIER)) then {
  3142. private _visibility = [_unit, "VIEW", _drone] checkVisibility [eyePos _unit, getPosASL _drone];
  3143. if (_visibility > 0.3) then {
  3144. (group _unit) reveal [_drone, 4];
  3145. _unit doWatch _drone;
  3146. _unit doTarget _drone;
  3147. _unit doFire _drone;
  3148. };
  3149. };
  3150. };
  3151. };
  3152. };
  3153. };
  3154.  
  3155. private _damage = damage _unit;
  3156. if (_damage > 0.3) then {
  3157. private _lastSmokeTime = _unit getVariable ["lastSmokeTime", 0];
  3158. if (time > _lastSmokeTime + (90 * PERF_COOLDOWN_MULTIPLIER) && getSuppression _unit > 0.5) then {
  3159. [_unit, "cover"] call fnc_botUseSmoke;
  3160. _unit setVariable ["lastSmokeTime", time];
  3161. };
  3162.  
  3163. if (_damage > 0.5) then {
  3164. private _isRetreating = _unit getVariable ["isRetreating", false];
  3165. if (!_isRetreating) then {
  3166. private _retreatPos = _unitPos getPos [30, (direction _unit) + 180];
  3167. _unit doMove _retreatPos;
  3168. _unit setBehaviour "AWARE";
  3169. _unit setVariable ["isRetreating", true];
  3170. _unit setVariable ["retreatPos", _retreatPos];
  3171. } else {
  3172. private _retreatPos = _unit getVariable ["retreatPos", [0,0,0]];
  3173. if (_unit distance _retreatPos < 10) then {
  3174. if ("FirstAidKit" in items _unit) then {
  3175. _unit action ["HealSoldierSelf", _unit];
  3176. _unit removeItem "FirstAidKit";
  3177. };
  3178. _unit setVariable ["isRetreating", false];
  3179. };
  3180. };
  3181. };
  3182. };
  3183.  
  3184. if (currentWeapon _unit != "") then {
  3185. private _currentAmmo = _unit ammo (currentWeapon _unit);
  3186. private _lastKnownAmmo = _unit getVariable ["lastKnownAmmo", _currentAmmo];
  3187. if (_currentAmmo < _lastKnownAmmo) then {
  3188. _unit setVariable ["lastFiredTime", time];
  3189. };
  3190. _unit setVariable ["lastKnownAmmo", _currentAmmo];
  3191. };
  3192. } else {
  3193. // VEHICLE CREW LOGIC
  3194. private _lastVehicleCheck = _vehicle getVariable ["lastVehicleCheck", 0];
  3195. if (time > _lastVehicleCheck + 6) then {
  3196. _vehicle setVariable ["lastVehicleCheck", time];
  3197.  
  3198. private _nearTargets = _unit nearTargets (250 * PERF_SEARCH_RADIUS_MULTIPLIER);
  3199. private _targetCount = (count _nearTargets) min 5;
  3200.  
  3201. for "_t" from 0 to (_targetCount - 1) do {
  3202. private _targetData = _nearTargets select _t;
  3203. private _target = _targetData select 4;
  3204.  
  3205. if (alive _target && side _target == _enemySide && (_target isKindOf "CAManBase" || _target isKindOf "LandVehicle")) then {
  3206. private _distance = _unit distance _target;
  3207. if (_distance < (250 * PERF_SEARCH_RADIUS_MULTIPLIER) && _unit knowsAbout _target > 0) then {
  3208. private _obstructionLevel = [_unit, _target] call fnc_checkFoliageObstruction;
  3209.  
  3210. if (_obstructionLevel > 0.6) then {
  3211. (group _unit) reveal [_target, 1.5];
  3212. } else {
  3213. (group _unit) reveal [_target, 3];
  3214. };
  3215. };
  3216. };
  3217. };
  3218. };
  3219.  
  3220. // HELICOPTER LOGIC
  3221. if (_vehicle isKindOf "Air" && driver _vehicle == _unit) then {
  3222. private _lastHeliCheck = _vehicle getVariable ["lastHeliCheck", 0];
  3223. if (time > _lastHeliCheck + 4) then {
  3224. _vehicle setVariable ["lastHeliCheck", time];
  3225.  
  3226. private _nearMissiles = _vehicle nearObjects ["MissileBase", 800];
  3227. private _lastFlareTime = _vehicle getVariable ["lastFlareTime", 0];
  3228. if (count _nearMissiles > 0 && time > _lastFlareTime + 4) then {
  3229. _vehicle action ["useWeapon", _vehicle, driver _vehicle, 0];
  3230. _vehicle setVariable ["lastFlareTime", time];
  3231. private _evadeDir = random 360;
  3232. _vehicle doMove (_vehicle getPos [300, _evadeDir]);
  3233. _vehicle flyInHeight (50 + random 150);
  3234. };
  3235.  
  3236. if (isNull (assignedTarget _unit)) then {
  3237. private _potentialTargets = _vehicle nearEntities [["CAManBase", "LandVehicle"], 1000];
  3238. private _enemies = _potentialTargets select {
  3239. side _x == _enemySide && alive _x && _vehicle knowsAbout _x > 0.5
  3240. };
  3241.  
  3242. if (count _enemies > 0) then {
  3243. (gunner _vehicle) doTarget (_enemies select 0);
  3244. (gunner _vehicle) doFire (_enemies select 0);
  3245. };
  3246. };
  3247. };
  3248. };
  3249. };
  3250.  
  3251. private _lastIntelTime = _unit getVariable ["BOTAI_lastIntelCheck", 0];
  3252. if (time > _lastIntelTime + (15 * PERF_COOLDOWN_MULTIPLIER)) then {
  3253. _unit setVariable ["BOTAI_lastIntelCheck", time];
  3254. [_unit, side _unit] call HC_fnc_gatherIntelligence;
  3255. };
  3256. };
  3257. };
  3258.  
  3259. // Move to next batch
  3260. _unitIndex = _unitIndex + _batchSize;
  3261. if (_unitIndex >= count _allAiUnits) then {
  3262. _unitIndex = 0;
  3263. };
  3264.  
  3265. // Measure cycle execution time for the performance monitor
  3266. private _cycleEndTime = diag_tickTime;
  3267. private _cycleExecTime = _cycleEndTime - _cycleStartTime;
  3268. missionNamespace setVariable ["BOTAI_lastExecTime", _cycleExecTime];
  3269.  
  3270. // Log if a single cycle takes too long
  3271. if (_cycleExecTime > 0.050) then {
  3272. diag_log format ["[BOTAI] SLOW CYCLE: %1ms (batch: %2, state: %3)",
  3273. round (_cycleExecTime * 1000), _batchSize, PERF_STATE];
  3274. };
  3275.  
  3276. // Adaptive sleep time based on server performance
  3277. sleep (1.0 * PERF_MAIN_LOOP_MULTIPLIER);
  3278. };
  3279. };
  3280. };
  3281.  
  3282. ==================== END OF: botai.sqf ====================
  3283.  
  3284.  
  3285.  
  3286. ==================== START OF: cleanup.sqf ====================
  3287.  
  3288. // cleanup.sqf
  3289.  
  3290. if (isNil "CLEANUP_BODY_LIMIT") then { CLEANUP_BODY_LIMIT = 20; };
  3291. if (isNil "CLEANUP_DELAY") then { CLEANUP_DELAY = 30; };
  3292. if (isNil "CLEANUP_BODY_TIMER") then { CLEANUP_BODY_TIMER = 120; };
  3293. if (isNil "CLEANUP_WEAPON_TIMER") then { CLEANUP_WEAPON_TIMER = 300; };
  3294. if (isNil "CLEANUP_VEHICLE_TIMER") then { CLEANUP_VEHICLE_TIMER = 120; };
  3295. if (isNil "CLEANUP_MIN_DISTANCE") then { CLEANUP_MIN_DISTANCE = 300; };
  3296.  
  3297. if (isNil "CLEANUP_BODY_ARRAY") then { CLEANUP_BODY_ARRAY = []; };
  3298.  
  3299. fnc_performCleanup = {
  3300. private _startTime = diag_tickTime;
  3301. private _deleted = 0;
  3302. private _currentTime = time;
  3303.  
  3304. // Apply dynamic multiplier from performance settings
  3305. // PERF_CLEANUP_MULTIPLIER is defined in performance.sqf (default 1.0)
  3306. // Lower multiplier means faster cleanup (shorter timers)
  3307. private _timerMult = missionNamespace getVariable ["PERF_CLEANUP_MULTIPLIER", 1.0];
  3308.  
  3309. private _bodyTimer = CLEANUP_BODY_TIMER * _timerMult;
  3310. private _weaponTimer = CLEANUP_WEAPON_TIMER * _timerMult;
  3311. private _vehicleTimer = CLEANUP_VEHICLE_TIMER * _timerMult;
  3312.  
  3313. // --- Add new dead bodies to the tracking array ---
  3314. private _deadUnits = allDead select {_x isKindOf "CAManBase"};
  3315. {
  3316. private _body = _x;
  3317. if ((CLEANUP_BODY_ARRAY findIf {(_x select 0) == _body}) == -1) then {
  3318. CLEANUP_BODY_ARRAY pushBack [_body, _currentTime];
  3319. };
  3320. } forEach _deadUnits;
  3321.  
  3322. // --- Process and delete old bodies ---
  3323. for "_i" from (count CLEANUP_BODY_ARRAY - 1) to 0 step -1 do {
  3324. private _entry = CLEANUP_BODY_ARRAY select _i;
  3325. private _body = _entry select 0;
  3326. private _deathTime = _entry select 1;
  3327.  
  3328. if (isNull _body || (_currentTime - _deathTime > _bodyTimer)) then {
  3329. if (!isNull _body) then {
  3330. deleteVehicle _body;
  3331. _deleted = _deleted + 1;
  3332. };
  3333. CLEANUP_BODY_ARRAY deleteAt _i;
  3334. };
  3335. };
  3336.  
  3337. // --- Process Ground Weapon Holders ---
  3338. {
  3339. private _holder = _x;
  3340. if ((count (weaponCargo _holder) == 0) && (count (magazineCargo _holder) == 0) && (count (itemCargo _holder) == 0)) then {
  3341. deleteVehicle _holder;
  3342. _deleted = _deleted + 1;
  3343. } else {
  3344. private _age = _currentTime - (_holder getVariable ["cleanup_time", _currentTime]);
  3345. if (isNil {_holder getVariable "cleanup_time"}) then {
  3346. _holder setVariable ["cleanup_time", _currentTime];
  3347. };
  3348. if (_age > _weaponTimer) then {
  3349. deleteVehicle _holder;
  3350. _deleted = _deleted + 1;
  3351. };
  3352. };
  3353. } forEach (allMissionObjects "GroundWeaponHolder");
  3354.  
  3355. // ==================== CHANGE START: HIGHLY OPTIMIZED VEHICLE CLEANUP ====================
  3356. private _cleanupTimer = _vehicleTimer; // Use dynamic timer
  3357.  
  3358. // Helper function to process a vehicle for cleanup
  3359. private _fnc_processVehicle = {
  3360. params ["_vehicle", "_timerValue"]; // Accept timer as argument
  3361. if (isNull _vehicle || {_vehicle isKindOf "CAManBase"}) exitWith {};
  3362.  
  3363. // Aggressive cleanup for unoccupied quad bikes
  3364. if (_vehicle isKindOf "Quadbike_01_base_F") then {
  3365. if (count crew _vehicle == 0) then {
  3366. private _startTime = _vehicle getVariable ["cleanup_unoccupied_startTime", -1];
  3367. if (_startTime == -1) then {
  3368. _vehicle setVariable ["cleanup_unoccupied_startTime", time, true];
  3369. } else {
  3370. if (time - _startTime > 300) then { // 5 minute timer for abandoned quads
  3371. deleteVehicle _vehicle;
  3372. _deleted = _deleted + 1;
  3373. continue;
  3374. };
  3375. };
  3376. } else {
  3377. if !(isNil {_vehicle getVariable "cleanup_unoccupied_startTime"}) then {
  3378. _vehicle setVariable ["cleanup_unoccupied_startTime", nil, true];
  3379. };
  3380. };
  3381. };
  3382.  
  3383. private _isDerelict = false;
  3384. if (!alive _vehicle) then {
  3385. _isDerelict = true; // Is destroyed
  3386. } else {
  3387. if (damage _vehicle > 0.9) then {
  3388. _isDerelict = true; // Is heavily damaged
  3389. } else {
  3390. if (count crew _vehicle == 0) then {
  3391. // Is abandoned (only check if it was an AI vehicle)
  3392. if ((_vehicle getVariable ["isAIVehicle", false]) || (_vehicle getVariable ["unitValue", 0] > 0)) then {
  3393. _isDerelict = true;
  3394. };
  3395. };
  3396. };
  3397. };
  3398.  
  3399. // Don't clean protected vehicles unless they are derelict
  3400. if ((_vehicle getVariable ["gcImportant", false]) && !_isDerelict) then {
  3401. if !(isNil {_vehicle getVariable "cleanup_startTime"}) then {
  3402. _vehicle setVariable ["cleanup_startTime", nil, true];
  3403. };
  3404. continue;
  3405. };
  3406.  
  3407. if (_isDerelict) then {
  3408. private _startTime = _vehicle getVariable ["cleanup_startTime", -1];
  3409. if (_startTime == -1) then {
  3410. _vehicle setVariable ["cleanup_startTime", time, true];
  3411. } else {
  3412. if (time - _startTime > _timerValue) then {
  3413. deleteVehicle _vehicle;
  3414. _deleted = _deleted + 1;
  3415. };
  3416. };
  3417. } else {
  3418. if !(isNil {_vehicle getVariable "cleanup_startTime"}) then {
  3419. _vehicle setVariable ["cleanup_startTime", nil, true];
  3420. };
  3421. };
  3422. };
  3423.  
  3424. // 1. Process all recently destroyed vehicles (fast)
  3425. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach (allDead select {!(_x isKindOf "CAManBase")});
  3426.  
  3427. // 2. Process tracked AI vehicles that might be abandoned (fast)
  3428. BLUFOR_ACTIVE_LIGHT_VEHICLES = BLUFOR_ACTIVE_LIGHT_VEHICLES select {!isNull _x};
  3429. OPFOR_ACTIVE_LIGHT_VEHICLES = OPFOR_ACTIVE_LIGHT_VEHICLES select {!isNull _x};
  3430. BLUFOR_ACTIVE_TANKS = BLUFOR_ACTIVE_TANKS select {!isNull _x};
  3431. OPFOR_ACTIVE_TANKS = OPFOR_ACTIVE_TANKS select {!isNull _x};
  3432. BLUFOR_ACTIVE_ATTACK_HELIS = BLUFOR_ACTIVE_ATTACK_HELIS select {!isNull _x};
  3433. OPFOR_ACTIVE_ATTACK_HELIS = OPFOR_ACTIVE_ATTACK_HELIS select {!isNull _x};
  3434.  
  3435. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach BLUFOR_ACTIVE_LIGHT_VEHICLES;
  3436. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach OPFOR_ACTIVE_LIGHT_VEHICLES;
  3437. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach BLUFOR_ACTIVE_TANKS;
  3438. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach OPFOR_ACTIVE_TANKS;
  3439. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach BLUFOR_ACTIVE_ATTACK_HELIS;
  3440. { [_x, _cleanupTimer] call _fnc_processVehicle; } forEach OPFOR_ACTIVE_ATTACK_HELIS;
  3441. // ===================== CHANGE END =====================
  3442.  
  3443. // --- Empty Group Cleanup ---
  3444. {
  3445. if (count (units _x) == 0) then {
  3446. deleteGroup _x;
  3447. };
  3448. } forEach allGroups;
  3449.  
  3450. // --- Crater and Smoke Cleanup ---
  3451. { deleteVehicle _x; _deleted = _deleted + 1; } forEach (allMissionObjects "CraterLong");
  3452. { deleteVehicle _x; _deleted = _deleted + 1; } forEach (allMissionObjects "CraterLong_small");
  3453.  
  3454. {
  3455. private _obj = _x;
  3456. private _age = time - (_obj getVariable ["cleanup_time", time]);
  3457.  
  3458. if (_age > 300) then {
  3459. deleteVehicle _obj;
  3460. _deleted = _deleted + 1;
  3461. } else {
  3462. if (isNil {_obj getVariable "cleanup_time"}) then {
  3463. _obj setVariable ["cleanup_time", time];
  3464. };
  3465. };
  3466. } forEach (allMissionObjects "SmokeShell");
  3467.  
  3468. private _endTime = diag_tickTime;
  3469. private _executionTime = (_endTime - _startTime) * 1000;
  3470. };
  3471.  
  3472. if (isServer) then {
  3473. [] spawn {
  3474. waitUntil {time > 10};
  3475.  
  3476. while {true} do {
  3477. // Adaptive cleanup delay
  3478. private _baseDelay = CLEANUP_DELAY;
  3479. private _adaptiveDelay = [_baseDelay] call fnc_getAdaptiveSleep;
  3480.  
  3481. sleep _adaptiveDelay;
  3482.  
  3483. if (count allPlayers > 0) then {
  3484. [] call fnc_performCleanup;
  3485. };
  3486. };
  3487. };
  3488.  
  3489. addMissionEventHandler ["EntityKilled", {
  3490. params ["_victim"];
  3491.  
  3492. if (_victim isKindOf "AllVehicles" && !(_victim isKindOf "CAManBase")) then {
  3493. _victim setVariable ["cleanup_destroyed_time", time];
  3494. };
  3495. }];
  3496. };
  3497.  
  3498. ==================== END OF: cleanup.sqf ====================
  3499.  
  3500.  
  3501.  
  3502. ==================== START OF: description.ext ====================
  3503.  
  3504. class Header
  3505. {
  3506. gameType = CTI;
  3507. minPlayers = 1;
  3508. maxPlayers = 50;
  3509. };
  3510. respawn = "BASE"; // Enables respawn at the defined respawn positions (from BIS_fnc_addRespawnPosition in init.sqf)
  3511. respawnDelay = 5; // Default respawn delay in seconds. Will be dynamically adjusted by playerinit.sqf
  3512. respawnDialog = 0; // Hides the respawn screen, making respawn automatic after the delay
  3513.  
  3514. // ======================= BASE UI CLASSES =======================
  3515. class RscText_Base {
  3516. type = 0; style = 0;
  3517. font = "RobotoCondensed"; sizeEx = 0.035;
  3518. colorText[] = {1,1,1,1};
  3519. colorBackground[] = {0,0,0,0}; // Transparent background
  3520. text = "";
  3521. };
  3522.  
  3523. class RscButton_Base {
  3524. type = 1; style = 2;
  3525. font = "RobotoCondensed"; sizeEx = 0.035;
  3526. colorText[] = {1,1,1,1};
  3527. colorBackground[] = {0,0,0,0.8};
  3528. colorBackgroundActive[] = {1,1,1,0.3};
  3529. colorFocused[] = {0,0,0,1};
  3530. colorDisabled[] = {1,1,1,0.3};
  3531. colorBackgroundDisabled[] = {0,0,0,0.3};
  3532. colorBorder[] = {0,0,0,1};
  3533. colorShadow[] = {0,0,0,0.5};
  3534. soundEnter[] = {"\A3\ui_f\data\sound\RscButton\soundEnter", 0.09, 1};
  3535. soundPush[] = {"\A3\ui_f\data\sound\RscButton\soundPush", 0.09, 1};
  3536. soundClick[] = {"\A3\ui_f\data\sound\RscButton\soundClick", 0.09, 1};
  3537. soundEscape[] = {"\A3\ui_f\data\sound\RscButton\soundEscape", 0.09, 1};
  3538. borderSize = 0; offsetX = 0; offsetY = 0; offsetPressedX = 0; offsetPressedY = 0;
  3539. };
  3540.  
  3541. // ======================= MISSION BRIEFING DIALOG =======================
  3542. class MissionBriefingDialog {
  3543. idd = 9300;
  3544. movingEnable = false;
  3545. onLoad = "uiNamespace setVariable ['MissionBriefingDisplay', _this select 0];";
  3546. onUnload = "uiNamespace setVariable ['MissionBriefingDisplay', displayNull];";
  3547.  
  3548. class Controls {
  3549. // --- BACKGROUND ---
  3550. class Background {
  3551. type = 0; style = 0; idc = 93000;
  3552. x = 0.1; y = 0.05; w = 0.8; h = 0.9; // Increased height, moved up
  3553. font = "RobotoCondensed"; sizeEx = 0.04;
  3554. colorBackground[] = {0,0,0,0.8};
  3555. colorText[] = {1,1,1,1};
  3556. text = "";
  3557. };
  3558. // --- TITLE ---
  3559. class Title {
  3560. type = 0; style = 2; idc = 93001;
  3561. text = "Mission Briefing";
  3562. x = 0.1; y = 0.07; w = 0.8; h = 0.04; // Moved up
  3563. font = "RobotoCondensed"; sizeEx = 0.04;
  3564. colorBackground[] = {0,0,0,0};
  3565. colorText[] = {1,1,1,1};
  3566. };
  3567. // --- TEXT AREA (SCROLLABLE) ---
  3568. class BriefingText {
  3569. type = 0; style = 16; // ST_MULTI for scrollable text
  3570. idc = 93002;
  3571. text = "QUICK START GUIDE\n\n--- OBJECTIVE ---\nYour primary goal is to capture the enemy's main Command Tower. You can also win if the enemy team's forces collapse.\n\n--- AI COMMANDER & TEAM POINTS ---\nEach team is led by an AI Commander who automatically purchases AI squads and vehicles. The Commander uses Team Points, which your side earns for every enemy unit destroyed.\n\n--- YOUR ROLE & PLAYER POINTS ---\nAs a player, you earn Personal Points for kills. These points are yours to spend. To purchase vehicles, go to the Arsenal Box at your base and use your scroll wheel menu to open the Purchase Menu.\n\n--- WINNING THE GAME ---\n1. Base Capture: Get within 5 meters of the enemy Command Tower and use your scroll wheel to select 'Capture Base'. You must survive for 60 seconds to win.\n\n2. Enemy Collapse: The round will also end if an enemy team's points fall below 50 AND their army is less than half the size of yours.";
  3572. x = 0.12; y = 0.13; w = 0.76; h = 0.74; // Increased height, moved up
  3573. font = "RobotoCondensed"; sizeEx = 0.032; // Reduced font size
  3574. colorBackground[] = {0,0,0,0};
  3575. colorText[] = {1,1,1,1};
  3576. lineSpacing = 1;
  3577. };
  3578. // --- CLOSE BUTTON ---
  3579. class CloseButton: RscButton_Base {
  3580. idc = 93003;
  3581. text = "Close";
  3582. x = 0.475; y = 0.88; w = 0.15; h = 0.04; // Adjusted position for larger dialog
  3583. action = "closeDialog 0;";
  3584. };
  3585. };
  3586. };
  3587.  
  3588. // ======================= MAIN PURCHASE MENU =======================
  3589. class PlayerPurchaseMenu {
  3590. idd = 9000;
  3591. movingEnable = false;
  3592. onLoad = "((_this select 0) displayCtrl 9002) ctrlSetText format['Points: %1', player getVariable ['playerPoints', 0]];";
  3593.  
  3594. class Controls {
  3595. // --- BACKGROUND AND TITLES ---
  3596. class Background {
  3597. type = 0; style = 0; idc = 90000;
  3598. x = 0.3; y = 0.28; w = 0.4; h = 0.48;
  3599. font = "RobotoCondensed"; sizeEx = 0.04;
  3600. colorBackground[] = {0,0,0,0.7};
  3601. colorText[] = {1,1,1,1};
  3602. text = "";
  3603. };
  3604. class Title {
  3605. type = 0; style = 2; idc = 9001;
  3606. text = "Purchase Menu";
  3607. x = 0.3; y = 0.3; w = 0.4; h = 0.04;
  3608. font = "RobotoCondensed"; sizeEx = 0.04;
  3609. colorBackground[] = {0,0,0,0};
  3610. colorText[] = {1,1,1,1};
  3611. };
  3612. class PointsDisplay {
  3613. type = 0; style = 2; idc = 9002;
  3614. text = "Points: 100";
  3615. x = 0.3; y = 0.34; w = 0.4; h = 0.04;
  3616. font = "RobotoCondensed"; sizeEx = 0.04;
  3617. colorBackground[] = {0,0,0,0};
  3618. colorText[] = {1,1,0,1};
  3619. };
  3620.  
  3621. // --- BUTTONS ---
  3622. class QuadButton: RscButton_Base {
  3623. idc = 9003;
  3624. text = "Quad Bike (1 pt)";
  3625. x = 0.32; y = 0.40; w = 0.36; h = 0.05;
  3626. action = "private _veh = if (side player == west) then {'B_Quadbike_01_F'} else {'O_Quadbike_01_F'}; [player, _veh] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3627. };
  3628.  
  3629. class LightVicButton: RscButton_Base {
  3630. idc = 9004;
  3631. text = "Light Vehicles (10 pts)";
  3632. x = 0.32; y = 0.46; w = 0.36; h = 0.05;
  3633. action = "closeDialog 0; createDialog 'LightVehicleMenu';";
  3634. };
  3635.  
  3636. class HeavyVicButton: RscButton_Base {
  3637. idc = 9006;
  3638. text = "Heavy Vehicles (20-30 pts)";
  3639. x = 0.32; y = 0.52; w = 0.36; h = 0.05;
  3640. action = "closeDialog 0; createDialog 'HeavyVehicleMenu';";
  3641. };
  3642.  
  3643. class ParadropButton: RscButton_Base {
  3644. idc = 9007;
  3645. text = "Paradrop Near Stronghold (2 pts)";
  3646. x = 0.32; y = 0.58; w = 0.36; h = 0.05;
  3647. action = "[player] remoteExecCall ['fnc_server_paradropPlayer', 2]; closeDialog 0;";
  3648. };
  3649.  
  3650. // ==================== CHANGE START ====================
  3651. class AISquadButton: RscButton_Base {
  3652. idc = 9008;
  3653. text = "Buy 3 Man AI Squad (15 pts)";
  3654. x = 0.32; y = 0.64; w = 0.36; h = 0.05;
  3655. action = "[player] remoteExecCall ['fnc_server_purchaseAISquad', 2]; closeDialog 0;";
  3656. };
  3657. // ===================== CHANGE END =====================
  3658.  
  3659. class CloseButton: RscButton_Base {
  3660. idc = 9005;
  3661. text = "Close";
  3662. x = 0.425; y = 0.70; w = 0.15; h = 0.04;
  3663. action = "closeDialog 0;";
  3664. };
  3665. };
  3666. };
  3667.  
  3668. // ======================= LIGHT VEHICLE SUB-MENU (Self-Contained) =======================
  3669. class LightVehicleMenu {
  3670. idd = 9100;
  3671. movingEnable = false;
  3672. onLoad = " private _display = _this select 0; private _isBlufor = side player == west; ((_display displayCtrl 9100) ctrlSetText format ['Points: %1', player getVariable ['playerPoints', 0]]); ((_display displayCtrl 9101) ctrlShow _isBlufor); ((_display displayCtrl 9102) ctrlShow _isBlufor); ((_display displayCtrl 9103) ctrlShow !_isBlufor); ((_display displayCtrl 9104) ctrlShow !_isBlufor); ";
  3673.  
  3674. class Controls {
  3675. class Background {
  3676. type = 0; style = 0; idc = 91000;
  3677. x = 0.3; y = 0.28; w = 0.4; h = 0.35;
  3678. font = "RobotoCondensed"; sizeEx = 0.04;
  3679. colorBackground[] = {0,0,0,0.7};
  3680. colorText[] = {1,1,1,1}; text = "";
  3681. };
  3682. class Title {
  3683. type = 0; style = 2; idc = 91001;
  3684. text = "Light Vehicles (10 pts)";
  3685. x = 0.3; y = 0.3; w = 0.4; h = 0.04;
  3686. font = "RobotoCondensed"; sizeEx = 0.04;
  3687. colorBackground[] = {0,0,0,0}; colorText[] = {1,1,1,1};
  3688. };
  3689. class PointsDisplay {
  3690. type = 0; style = 2; idc = 9100;
  3691. text = "Points: 100";
  3692. x = 0.3; y = 0.34; w = 0.4; h = 0.04;
  3693. font = "RobotoCondensed"; sizeEx = 0.04;
  3694. colorBackground[] = {0,0,0,0}; colorText[] = {1,1,0,1};
  3695. };
  3696. // --- BLUFOR BUTTONS ---
  3697. class HunterButton: RscButton_Base {
  3698. idc = 9101;
  3699. text = "Hunter HMG";
  3700. x = 0.32; y = 0.40; w = 0.17; h = 0.05;
  3701. action = "[player, 'B_MRAP_01_hmg_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3702. };
  3703. class PawneeButton: RscButton_Base {
  3704. idc = 9102;
  3705. text = "Pawnee";
  3706. x = 0.51; y = 0.40; w = 0.17; h = 0.05;
  3707. action = "[player, 'B_Heli_Light_01_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3708. };
  3709. // --- OPFOR BUTTONS ---
  3710. class IfritButton: RscButton_Base {
  3711. idc = 9103;
  3712. text = "Ifrit HMG";
  3713. x = 0.32; y = 0.40; w = 0.17; h = 0.05;
  3714. action = "[player, 'O_MRAP_02_hmg_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3715. };
  3716. class OrcaButton: RscButton_Base {
  3717. idc = 9104;
  3718. text = "Orca";
  3719. x = 0.51; y = 0.40; w = 0.17; h = 0.05;
  3720. action = "[player, 'O_Heli_Light_02_unarmed_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3721. };
  3722. // --- UTILITY BUTTONS ---
  3723. class BackButton: RscButton_Base {
  3724. idc = 9198;
  3725. text = "Back";
  3726. x = 0.32; y = 0.58; w = 0.15; h = 0.04;
  3727. action = "closeDialog 0; createDialog 'PlayerPurchaseMenu';";
  3728. };
  3729. class CloseButton: RscButton_Base {
  3730. idc = 9199;
  3731. text = "Close";
  3732. x = 0.53; y = 0.58; w = 0.15; h = 0.04;
  3733. action = "closeDialog 0;";
  3734. };
  3735. };
  3736. };
  3737.  
  3738. // ======================= HEAVY VEHICLE SUB-MENU (Self-Contained) =======================
  3739. class HeavyVehicleMenu {
  3740. idd = 9200;
  3741. movingEnable = false;
  3742. onLoad = " private _display = _this select 0; private _isBlufor = side player == west; ((_display displayCtrl 9200) ctrlSetText format ['Points: %1', player getVariable ['playerPoints', 0]]); ((_display displayCtrl 9201) ctrlShow _isBlufor); ((_display displayCtrl 9202) ctrlShow _isBlufor); ((_display displayCtrl 9203) ctrlShow !_isBlufor); ((_display displayCtrl 9204) ctrlShow !_isBlufor); ";
  3743.  
  3744. class Controls {
  3745. class Background {
  3746. type = 0; style = 0; idc = 92000;
  3747. x = 0.3; y = 0.28; w = 0.4; h = 0.35;
  3748. font = "RobotoCondensed"; sizeEx = 0.04;
  3749. colorBackground[] = {0,0,0,0.7};
  3750. colorText[] = {1,1,1,1}; text = "";
  3751. };
  3752. class Title {
  3753. type = 0; style = 2; idc = 92001;
  3754. text = "Heavy Vehicles";
  3755. x = 0.3; y = 0.3; w = 0.4; h = 0.04;
  3756. font = "RobotoCondensed"; sizeEx = 0.04;
  3757. colorBackground[] = {0,0,0,0}; colorText[] = {1,1,1,1};
  3758. };
  3759. class PointsDisplay {
  3760. type = 0; style = 2; idc = 9200;
  3761. text = "Points: 100";
  3762. x = 0.3; y = 0.34; w = 0.4; h = 0.04;
  3763. font = "RobotoCondensed"; sizeEx = 0.04;
  3764. colorBackground[] = {0,0,0,0}; colorText[] = {1,1,0,1};
  3765. };
  3766. // --- BLUFOR BUTTONS ---
  3767. class MarshallButton: RscButton_Base {
  3768. idc = 9201;
  3769. text = "Marshall APC (20 pts)";
  3770. x = 0.32; y = 0.40; w = 0.17; h = 0.05;
  3771. action = "[player, 'B_APC_Wheeled_01_cannon_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3772. };
  3773. class SlammerButton: RscButton_Base {
  3774. idc = 9202;
  3775. text = "Slammer MBT (30 pts)";
  3776. x = 0.51; y = 0.40; w = 0.17; h = 0.05;
  3777. action = "[player, 'B_MBT_01_cannon_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3778. };
  3779. // --- OPFOR BUTTONS ---
  3780. class MaridButton: RscButton_Base {
  3781. idc = 9203;
  3782. text = "Marid APC (20 pts)";
  3783. x = 0.32; y = 0.40; w = 0.17; h = 0.05;
  3784. action = "[player, 'O_APC_Wheeled_02_rcws_v2_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3785. };
  3786. class VarsukButton: RscButton_Base {
  3787. idc = 9204;
  3788. text = "Varsuk MBT (30 pts)";
  3789. x = 0.51; y = 0.40; w = 0.17; h = 0.05;
  3790. action = "[player, 'O_MBT_02_cannon_F'] remoteExecCall ['fnc_server_purchaseVehicle', 2]; closeDialog 0;";
  3791. };
  3792. // --- UTILITY BUTTONS ---
  3793. class BackButton: RscButton_Base {
  3794. idc = 9298;
  3795. text = "Back";
  3796. x = 0.32; y = 0.58; w = 0.15; h = 0.04;
  3797. action = "closeDialog 0; createDialog 'PlayerPurchaseMenu';";
  3798. };
  3799. class CloseButton: RscButton_Base {
  3800. idc = 9299;
  3801. text = "Close";
  3802. x = 0.53; y = 0.58; w = 0.15; h = 0.04;
  3803. action = "closeDialog 0;";
  3804. };
  3805. };
  3806. };
  3807.  
  3808. // ======================= ADMIN/SP SETTINGS MENU =======================
  3809. class AdminSettingsMenu {
  3810. idd = 9400;
  3811. movingEnable = false;
  3812. onLoad = " \
  3813. private _display = _this select 0; \
  3814. private _isSP = !isMultiplayer; \
  3815. _display displayCtrl 9411 ctrlShow _isSP; \
  3816. _display displayCtrl 9412 ctrlShow _isSP; \
  3817. _display displayCtrl 9413 ctrlShow _isSP; \
  3818. _display displayCtrl 9414 ctrlShow _isSP; \
  3819. _display displayCtrl 9415 ctrlShow _isSP; \
  3820. if (!_isSP) then { \
  3821. (_display displayCtrl 9410) ctrlSetText 'Team Points (SP Only - Disabled)'; \
  3822. (_display displayCtrl 9416) ctrlSetText 'Personal Points (SP Only - Disabled)'; \
  3823. }; \
  3824. _display displayCtrl 9420 ctrlSetText (if (_isSP) then {'Singleplayer Settings'} else {'Host Settings'}); \
  3825. private _currentMaxAI = missionNamespace getVariable ['maxAI', 120]; \
  3826. (_display displayCtrl 9421) ctrlSetText format ['Current: %1', _currentMaxAI]; \
  3827. ";
  3828.  
  3829. class Controls {
  3830. // --- BACKGROUND AND TITLES ---
  3831. class Background: RscText_Base {
  3832. idc = 94000;
  3833. x = 0.25; y = 0.1; w = 0.5; h = 0.8;
  3834. colorBackground[] = {0,0,0,0.7};
  3835. };
  3836. class Title: RscText_Base {
  3837. idc = 9420; style = 2; // Centered
  3838. text = "Settings";
  3839. x = 0.25; y = 0.12; w = 0.5; h = 0.04;
  3840. sizeEx = 0.04;
  3841. };
  3842.  
  3843. // --- MAX AI SETTINGS ---
  3844. class MaxAITitle: RscText_Base {
  3845. idc = -1;
  3846. text = "Max AI Count:";
  3847. x = 0.26; y = 0.18; w = 0.2; h = 0.04;
  3848. sizeEx = 0.03;
  3849. };
  3850. class MaxAIDisplay: RscText_Base {
  3851. idc = 9421;
  3852. text = "Current: 120";
  3853. x = 0.55; y = 0.18; w = 0.2; h = 0.04;
  3854. style = 1; // Right aligned
  3855. sizeEx = 0.03;
  3856. colorText[] = {1,1,0,1};
  3857. };
  3858. // --- ROW 1 ---
  3859. class MaxAIButton80: RscButton_Base {
  3860. idc = -1; text = "80";
  3861. x = 0.26; y = 0.22; w = 0.08; h = 0.04;
  3862. action = "[80] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 80';";
  3863. };
  3864. class MaxAIButton90: RscButton_Base {
  3865. idc = -1; text = "90";
  3866. x = 0.36; y = 0.22; w = 0.08; h = 0.04;
  3867. action = "[90] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 90';";
  3868. };
  3869. class MaxAIButton100: RscButton_Base {
  3870. idc = -1; text = "100";
  3871. x = 0.46; y = 0.22; w = 0.08; h = 0.04;
  3872. action = "[100] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 100';";
  3873. };
  3874. class MaxAIButton110: RscButton_Base {
  3875. idc = -1; text = "110";
  3876. x = 0.56; y = 0.22; w = 0.08; h = 0.04;
  3877. action = "[110] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 110';";
  3878. };
  3879. class MaxAIButton120: RscButton_Base {
  3880. idc = -1; text = "120";
  3881. x = 0.66; y = 0.22; w = 0.08; h = 0.04;
  3882. action = "[120] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 120';";
  3883. };
  3884. // --- ROW 2 ---
  3885. class MaxAIButton130: RscButton_Base {
  3886. idc = -1; text = "130";
  3887. x = 0.26; y = 0.27; w = 0.08; h = 0.04;
  3888. action = "[130] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 130';";
  3889. };
  3890. class MaxAIButton140: RscButton_Base {
  3891. idc = -1; text = "140";
  3892. x = 0.36; y = 0.27; w = 0.08; h = 0.04;
  3893. action = "[140] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 140';";
  3894. };
  3895. class MaxAIButton150: RscButton_Base {
  3896. idc = -1; text = "150";
  3897. x = 0.46; y = 0.27; w = 0.08; h = 0.04;
  3898. action = "[150] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 150';";
  3899. };
  3900. class MaxAIButton160: RscButton_Base {
  3901. idc = -1; text = "160";
  3902. x = 0.56; y = 0.27; w = 0.08; h = 0.04;
  3903. action = "[160] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 160';";
  3904. };
  3905. class MaxAIButton170: RscButton_Base {
  3906. idc = -1; text = "170";
  3907. x = 0.66; y = 0.27; w = 0.08; h = 0.04;
  3908. action = "[170] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 170';";
  3909. };
  3910. // --- ROW 3 ---
  3911. class MaxAIButton180: RscButton_Base {
  3912. idc = -1; text = "180";
  3913. x = 0.26; y = 0.32; w = 0.08; h = 0.04;
  3914. action = "[180] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 180';";
  3915. };
  3916. class MaxAIButton190: RscButton_Base {
  3917. idc = -1; text = "190";
  3918. x = 0.36; y = 0.32; w = 0.08; h = 0.04;
  3919. action = "[190] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 190';";
  3920. };
  3921. class MaxAIButton200: RscButton_Base {
  3922. idc = -1; text = "200";
  3923. x = 0.46; y = 0.32; w = 0.08; h = 0.04;
  3924. action = "[200] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 200';";
  3925. };
  3926. class MaxAIButton210: RscButton_Base {
  3927. idc = -1; text = "210";
  3928. x = 0.56; y = 0.32; w = 0.08; h = 0.04;
  3929. action = "[210] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 210';";
  3930. };
  3931. class MaxAIButton220: RscButton_Base {
  3932. idc = -1; text = "220";
  3933. x = 0.66; y = 0.32; w = 0.08; h = 0.04;
  3934. action = "[220] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 220';";
  3935. };
  3936. // --- ROW 4 ---
  3937. class MaxAIButton230: RscButton_Base {
  3938. idc = -1; text = "230";
  3939. x = 0.26; y = 0.37; w = 0.08; h = 0.04;
  3940. action = "[230] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 230';";
  3941. };
  3942. class MaxAIButton240: RscButton_Base {
  3943. idc = -1; text = "240";
  3944. x = 0.36; y = 0.37; w = 0.08; h = 0.04;
  3945. action = "[240] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 240';";
  3946. };
  3947. class MaxAIButton250: RscButton_Base {
  3948. idc = -1; text = "250";
  3949. x = 0.46; y = 0.37; w = 0.08; h = 0.04;
  3950. action = "[250] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 250';";
  3951. };
  3952. class MaxAIButton260: RscButton_Base {
  3953. idc = -1; text = "260";
  3954. x = 0.56; y = 0.37; w = 0.08; h = 0.04;
  3955. action = "[260] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 260';";
  3956. };
  3957. class MaxAIButton270: RscButton_Base {
  3958. idc = -1; text = "270";
  3959. x = 0.66; y = 0.37; w = 0.08; h = 0.04;
  3960. action = "[270] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 270';";
  3961. };
  3962. // --- ROW 5 ---
  3963. class MaxAIButton280: RscButton_Base {
  3964. idc = -1; text = "280";
  3965. x = 0.26; y = 0.42; w = 0.08; h = 0.04;
  3966. action = "[280] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 280';";
  3967. };
  3968. class MaxAIButton290: RscButton_Base {
  3969. idc = -1; text = "290";
  3970. x = 0.36; y = 0.42; w = 0.08; h = 0.04;
  3971. action = "[290] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 290';";
  3972. };
  3973. class MaxAIButton300: RscButton_Base {
  3974. idc = -1; text = "300";
  3975. x = 0.46; y = 0.42; w = 0.08; h = 0.04;
  3976. action = "[300] remoteExecCall ['fnc_server_setMaxAI', 2]; ((findDisplay 9400) displayCtrl 9421) ctrlSetText 'Current: 300';";
  3977. };
  3978.  
  3979. // --- TIMESCALE SETTINGS ---
  3980. class TimescaleTitle: RscText_Base {
  3981. idc = -1;
  3982. text = "Timescale:";
  3983. x = 0.26; y = 0.48; w = 0.2; h = 0.04;
  3984. sizeEx = 0.03;
  3985. };
  3986. class TimescaleButton1: RscButton_Base {
  3987. idc = -1; text = "Realtime";
  3988. x = 0.26; y = 0.52; w = 0.15; h = 0.04;
  3989. action = "[1] remoteExecCall ['fnc_server_setTimescale', 2];";
  3990. };
  3991. class TimescaleButton2: RscButton_Base {
  3992. idc = -1; text = "2h = 1 Day";
  3993. x = 0.42; y = 0.52; w = 0.15; h = 0.04;
  3994. action = "[12] remoteExecCall ['fnc_server_setTimescale', 2];";
  3995. };
  3996. class TimescaleButton3: RscButton_Base {
  3997. idc = -1; text = "1h = 1 Day";
  3998. x = 0.58; y = 0.52; w = 0.15; h = 0.04;
  3999. action = "[24] remoteExecCall ['fnc_server_setTimescale', 2];";
  4000. };
  4001.  
  4002. // --- TEAM POINTS (SP ONLY) ---
  4003. class TeamPointsTitle: RscText_Base {
  4004. idc = 9410;
  4005. text = "Team Points (SP Only):";
  4006. x = 0.26; y = 0.58; w = 0.3; h = 0.04;
  4007. sizeEx = 0.03;
  4008. };
  4009. class AddBluforPoints: RscButton_Base {
  4010. idc = 9411; text = "+100 BLUFOR";
  4011. x = 0.26; y = 0.62; w = 0.23; h = 0.04;
  4012. action = "['BLUFOR', 100] remoteExecCall ['fnc_server_adjustTeamPoints', 2];";
  4013. };
  4014. class RemBluforPoints: RscButton_Base {
  4015. idc = 9412; text = "-100 BLUFOR";
  4016. x = 0.26; y = 0.67; w = 0.23; h = 0.04;
  4017. action = "['BLUFOR', -100] remoteExecCall ['fnc_server_adjustTeamPoints', 2];";
  4018. };
  4019. class AddOpforPoints: RscButton_Base {
  4020. idc = 9413; text = "+100 OPFOR";
  4021. x = 0.51; y = 0.62; w = 0.23; h = 0.04;
  4022. action = "['OPFOR', 100] remoteExecCall ['fnc_server_adjustTeamPoints', 2];";
  4023. };
  4024. class RemOpforPoints: RscButton_Base {
  4025. idc = 9414; text = "-100 OPFOR";
  4026. x = 0.51; y = 0.67; w = 0.23; h = 0.04;
  4027. action = "['OPFOR', -100] remoteExecCall ['fnc_server_adjustTeamPoints', 2];";
  4028. };
  4029.  
  4030. // --- PERSONAL POINTS (SP ONLY) ---
  4031. class PersonalPointsTitle: RscText_Base {
  4032. idc = 9416;
  4033. text = "Personal Points (SP Only):";
  4034. x = 0.26; y = 0.73; w = 0.3; h = 0.04;
  4035. sizeEx = 0.03;
  4036. };
  4037. class AddPersonalPoints: RscButton_Base {
  4038. idc = 9415;
  4039. text = "+100 Personal Points";
  4040. x = 0.26; y = 0.77; w = 0.48; h = 0.04;
  4041. action = "[player, 100] remoteExecCall ['fnc_server_addPersonalPoints', 2];";
  4042. };
  4043.  
  4044. // --- CLOSE BUTTON ---
  4045. class CloseButton: RscButton_Base {
  4046. idc = -1;
  4047. text = "Close";
  4048. x = 0.4; y = 0.83; w = 0.2; h = 0.04;
  4049. action = "closeDialog 0;";
  4050. };
  4051. };
  4052. };
  4053.  
  4054. ==================== END OF: description.ext ====================
  4055.  
  4056.  
  4057.  
  4058. ==================== START OF: highcommand.sqf ====================
  4059.  
  4060. // highcommand.sqf
  4061. // High Command AI logic for Arma 3 mission
  4062.  
  4063. // Initialize global variables
  4064. if (isNil "BLUFOR_STRATEGY") then { BLUFOR_STRATEGY = "SEARCH"; };
  4065. if (isNil "OPFOR_STRATEGY") then { OPFOR_STRATEGY = "SEARCH"; };
  4066. if (isNil "BLUFOR_LAST_DECISION") then { BLUFOR_LAST_DECISION = time; };
  4067. if (isNil "OPFOR_LAST_DECISION") then { OPFOR_LAST_DECISION = time; };
  4068.  
  4069. if (isNil "BLUFOR_LAST_FLARE") then { BLUFOR_LAST_FLARE = time - 3600; };
  4070. if (isNil "OPFOR_LAST_FLARE") then { OPFOR_LAST_FLARE = time - 3600; };
  4071. if (isNil "BLUFOR_LAST_SMOKE") then { BLUFOR_LAST_SMOKE = time - 3600; };
  4072. if (isNil "OPFOR_LAST_SMOKE") then { OPFOR_LAST_SMOKE = time - 3600; };
  4073. if (isNil "BLUFOR_LAST_DRONE") then { BLUFOR_LAST_DRONE = time - 3600; };
  4074. if (isNil "OPFOR_LAST_DRONE") then { OPFOR_LAST_DRONE = time - 3600; };
  4075. if (isNil "BLUFOR_LAST_ARTILLERY") then { BLUFOR_LAST_ARTILLERY = time - 3600; };
  4076. if (isNil "OPFOR_LAST_ARTILLERY") then { OPFOR_LAST_ARTILLERY = time - 3600; };
  4077.  
  4078. HC_SUPPORT_COST = 0;
  4079.  
  4080. if (isNil "BLUFOR_COMMANDER_PERSONALITY") then {
  4081. BLUFOR_COMMANDER_PERSONALITY = selectRandom HC_COMMANDER_PERSONALITIES;
  4082. };
  4083. if (isNil "OPFOR_COMMANDER_PERSONALITY") then {
  4084. OPFOR_COMMANDER_PERSONALITY = selectRandom HC_COMMANDER_PERSONALITIES;
  4085. };
  4086.  
  4087. if (isNil "BLUFOR_INTEL") then { BLUFOR_INTEL = createHashMap; };
  4088. if (isNil "OPFOR_INTEL") then { OPFOR_INTEL = createHashMap; };
  4089. if (isNil "BLUFOR_SEARCH_AREAS") then { BLUFOR_SEARCH_AREAS = []; };
  4090. if (isNil "OPFOR_SEARCH_AREAS") then { OPFOR_SEARCH_AREAS = []; };
  4091.  
  4092. if (isNil "BLUFOR_TACTICAL_STATE") then { BLUFOR_TACTICAL_STATE = createHashMap; };
  4093. if (isNil "OPFOR_TACTICAL_STATE") then { OPFOR_TACTICAL_STATE = createHashMap; };
  4094. if (isNil "BLUFOR_LAST_SUPPORT_REQUEST") then { BLUFOR_LAST_SUPPORT_REQUEST = 0; };
  4095. if (isNil "OPFOR_LAST_SUPPORT_REQUEST") then { OPFOR_LAST_SUPPORT_REQUEST = 0; };
  4096.  
  4097. if (isNil "HC_Active_Groups") then { HC_Active_Groups = createHashMap; };
  4098.  
  4099. // ==================== INTEL OPTIMIZATION START ====================
  4100. // NEW: Lightweight queues for recent tactical reports to avoid scanning the entire intel database.
  4101. if (isNil "BLUFOR_TACTICAL_REPORTS") then { BLUFOR_TACTICAL_REPORTS = []; };
  4102. if (isNil "OPFOR_TACTICAL_REPORTS") then { OPFOR_TACTICAL_REPORTS = []; };
  4103. HC_REPORTS_MAX_SIZE = 100; // Max size for the tactical report queue.
  4104. // ===================== INTEL OPTIMIZATION END =====================
  4105.  
  4106. // REVISED FUNCTION: Creates a temporary map marker that fades out over 60 seconds.
  4107. fnc_HC_createSupportMarker = {
  4108. params ["_side", "_pos", "_type", "_text"];
  4109. if (!isServer) exitWith {};
  4110.  
  4111. private _markerName = format ["support_marker_%1_%2", round(random 99999), time];
  4112. private _markerColor = if (_side == west) then { "ColorBlue" } else { "ColorRed" };
  4113.  
  4114. // Create marker only for the appropriate side's players
  4115. [_markerName, _pos, _type, _markerColor, _text] remoteExec ["fnc_createLocalSupportMarker", _side, true];
  4116.  
  4117. // Server-side cleanup spawn
  4118. [_markerName, _side] spawn {
  4119. params ["_marker", "_side"];
  4120. sleep 60;
  4121. [_marker] remoteExec ["deleteMarkerLocal", _side, true];
  4122. };
  4123. };
  4124.  
  4125. fnc_HC_assignGroupWithSpacing = {
  4126. params ["_groups", "_role", "_centerPos", "_ownBase", "_enemyBase"];
  4127.  
  4128. {
  4129. private _group = _x;
  4130. private _targetPos = _centerPos;
  4131.  
  4132. // Only apply spacing for the first 10 minutes (600s) of the mission.
  4133. // This encourages spreading out for initial recon. After that, groups will be more focused.
  4134. if (time < 600) then {
  4135. // Assign a random offset within a 250m radius of the main target.
  4136. _targetPos = _centerPos getPos [50 + random 200, random 360];
  4137. };
  4138.  
  4139. // Execute the role with the (potentially offset) target position.
  4140. [_group, _role, _targetPos, _ownBase, _enemyBase] call fnc_executeGroupRole;
  4141.  
  4142. } forEach _groups;
  4143. };
  4144.  
  4145. fnc_createLocalSupportMarker = {
  4146. params ["_markerName", "_pos", "_type", "_markerColor", "_text"];
  4147. if (!hasInterface) exitWith {};
  4148.  
  4149. createMarkerLocal [_markerName, _pos];
  4150. _markerName setMarkerShapeLocal "ICON";
  4151. _markerName setMarkerTypeLocal _type;
  4152. _markerName setMarkerColorLocal _markerColor;
  4153. _markerName setMarkerTextLocal _text;
  4154. _markerName setMarkerSizeLocal [0.8, 0.8];
  4155.  
  4156. // Local fade effect
  4157. [_markerName] spawn {
  4158. params ["_marker"];
  4159. private _lifetime = 60;
  4160. for "_i" from 0 to (_lifetime - 1) do {
  4161. private _alpha = 1 - (_i / _lifetime);
  4162. _marker setMarkerAlphaLocal _alpha;
  4163. sleep 1;
  4164. };
  4165. deleteMarkerLocal _marker;
  4166. };
  4167. };
  4168.  
  4169. // Check if support is available
  4170. fnc_isSupportAvailable = {
  4171. params ["_side", "_supportType"];
  4172. private _lastSupport = 0;
  4173. private _cooldown = 9999;
  4174.  
  4175. switch (_supportType) do {
  4176. case "FLARE": {
  4177. _lastSupport = if (_side == west) then {BLUFOR_LAST_FLARE} else {OPFOR_LAST_FLARE};
  4178. _cooldown = HC_FLARE_COOLDOWN;
  4179. };
  4180. case "SMOKE": {
  4181. _lastSupport = if (_side == west) then {BLUFOR_LAST_SMOKE} else {OPFOR_LAST_SMOKE};
  4182. _cooldown = HC_SMOKE_COOLDOWN;
  4183. };
  4184. case "DRONE": {
  4185. _lastSupport = if (_side == west) then {BLUFOR_LAST_DRONE} else {OPFOR_LAST_DRONE};
  4186. _cooldown = HC_DRONE_COOLDOWN;
  4187. };
  4188. case "ARTILLERY": {
  4189. _lastSupport = if (_side == west) then {BLUFOR_LAST_ARTILLERY} else {OPFOR_LAST_ARTILLERY};
  4190. _cooldown = HC_ARTILLERY_COOLDOWN;
  4191. };
  4192. };
  4193.  
  4194. (time - _lastSupport >= _cooldown)
  4195. };
  4196.  
  4197. // Check if support can be afforded
  4198. fnc_canAffordSupport = {
  4199. params ["_side"];
  4200. private _points = if (_side == west) then {missionNamespace getVariable ["BLUFOR_POINTS", 0]} else {missionNamespace getVariable ["OPFOR_POINTS", 0]};
  4201. (_points >= HC_SUPPORT_COST)
  4202. };
  4203.  
  4204. // Deduct support cost
  4205. fnc_deductSupportCost = {
  4206. params ["_side"];
  4207. if (_side == west) then {
  4208. BLUFOR_POINTS = (missionNamespace getVariable ["BLUFOR_POINTS", 0]) - HC_SUPPORT_COST;
  4209. publicVariable "BLUFOR_POINTS";
  4210. } else {
  4211. OPFOR_POINTS = (missionNamespace getVariable ["OPFOR_POINTS", 0]) - HC_SUPPORT_COST;
  4212. publicVariable "OPFOR_POINTS";
  4213. };
  4214. };
  4215.  
  4216. // Call flare support
  4217. fnc_callFlareSupport = {
  4218. params ["_side", "_targetPos"];
  4219. if (!([_side, "FLARE"] call fnc_isSupportAvailable)) exitWith { false };
  4220. if (!([_side] call fnc_canAffordSupport)) exitWith { false };
  4221.  
  4222. [_side] call fnc_deductSupportCost;
  4223. private _sideName = if (_side == west) then {"BLUFOR"} else {"OPFOR"};
  4224. systemChat format ["GLOBAL ALERT: %1 has called for Illumination support.", _sideName];
  4225. [
  4226. _side,
  4227. "Support Command",
  4228. format["Illumination mission requested over grid %1.", mapGridPosition _targetPos]
  4229. ] remoteExecCall ["sideChat", _side];
  4230.  
  4231. if (_side == west) then {
  4232. BLUFOR_LAST_FLARE = time;
  4233. } else {
  4234. OPFOR_LAST_FLARE = time;
  4235. };
  4236.  
  4237. [_side, _targetPos, "mil_dot", "Flare Illumination"] call fnc_HC_createSupportMarker;
  4238.  
  4239. [_targetPos] spawn {
  4240. params ["_pos"];
  4241. sleep 3; // Small delay for realism
  4242. private _flarePos = [(_pos select 0), (_pos select 1), 250];
  4243. private _flareShell = "F_40mm_White" createVehicle _flarePos;
  4244. _flareShell setPosASL _flarePos;
  4245. _flareShell setVelocity [0, 0, -5];
  4246. };
  4247.  
  4248. true
  4249. };
  4250.  
  4251. // Call smoke support
  4252. fnc_callSmokeSupport = {
  4253. params ["_side", "_targetPos"];
  4254. if (!([_side, "SMOKE"] call fnc_isSupportAvailable)) exitWith { false };
  4255. if (!([_side] call fnc_canAffordSupport)) exitWith { false };
  4256.  
  4257. [_side] call fnc_deductSupportCost;
  4258. private _sideName = if (_side == west) then {"BLUFOR"} else {"OPFOR"};
  4259. systemChat format ["GLOBAL ALERT: %1 has called for Smoke Screen support.", _sideName];
  4260. [
  4261. _side,
  4262. "Support Command",
  4263. format["Smoke cover requested for friendlies at grid %1.", mapGridPosition _targetPos]
  4264. ] remoteExecCall ["sideChat", _side];
  4265.  
  4266. if (_side == west) then {
  4267. BLUFOR_LAST_SMOKE = time;
  4268. } else {
  4269. OPFOR_LAST_SMOKE = time;
  4270. };
  4271.  
  4272. [_side, _targetPos, "mil_dot", "Smoke Cover"] call fnc_HC_createSupportMarker;
  4273.  
  4274. [_targetPos, _side] spawn {
  4275. params ["_pos", "_side"];
  4276. private _smokeRadius = 40;
  4277.  
  4278. [
  4279. _side,
  4280. "Support Command",
  4281. "Smoke cover inbound. ETA 10 seconds."
  4282. ] remoteExecCall ["sideChat", _side];
  4283.  
  4284. sleep 10;
  4285.  
  4286. [
  4287. _side,
  4288. "Support Command",
  4289. "SPLASH! Smoke rounds impacting."
  4290. ] remoteExecCall ["sideChat", _side];
  4291.  
  4292. for "_i" from 1 to HC_SMOKE_ROUNDS do {
  4293. private _roundPos = _pos vectorAdd [
  4294. (random _smokeRadius) - (_smokeRadius / 2),
  4295. (random _smokeRadius) - (_smokeRadius / 2),
  4296. 0
  4297. ];
  4298. private _smoke = createVehicle ["SmokeShellArty", _roundPos, [], 0, "CAN_COLLIDE"];
  4299. _smoke setPos _roundPos;
  4300. sleep (random 2);
  4301. };
  4302.  
  4303. [
  4304. _side,
  4305. "Support Command",
  4306. "Smoke screen deployed for friendly cover."
  4307. ] remoteExecCall ["sideChat", _side];
  4308. };
  4309.  
  4310. true
  4311. };
  4312.  
  4313. // Call recon drone support
  4314. fnc_callReconDroneSupport = {
  4315. params ["_side", "_targetPos"];
  4316. if (time < 600) exitWith { false };
  4317. if (!([_side, "DRONE"] call fnc_isSupportAvailable)) exitWith { false };
  4318. if (!([_side] call fnc_canAffordSupport)) exitWith { false };
  4319.  
  4320. // Check if target position is inside the play area
  4321. private _playAreaCenter = missionNamespace getVariable ["PLAY_AREA_CENTER", [0,0,0]];
  4322. private _playAreaRadius = missionNamespace getVariable ["PLAY_AREA_RADIUS", 0];
  4323. if (_playAreaRadius > 0 && (_targetPos distance2D _playAreaCenter > _playAreaRadius)) exitWith { false }; // Don't call drones outside the battle zone
  4324.  
  4325. [_side] call fnc_deductSupportCost;
  4326. private _sideName = if (_side == west) then {"BLUFOR"} else {"OPFOR"};
  4327. systemChat format ["GLOBAL ALERT: %1 has deployed Recon Drone support.", _sideName];
  4328. [
  4329. _side,
  4330. "Support Command",
  4331. format["Recon Drone en route to grid %1.", mapGridPosition _targetPos]
  4332. ] remoteExecCall ["sideChat", _side];
  4333.  
  4334. if (_side == west) then {
  4335. BLUFOR_LAST_DRONE = time;
  4336. } else {
  4337. OPFOR_LAST_DRONE = time;
  4338. };
  4339.  
  4340. private _basePos = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  4341. private _droneClass = if (_side == west) then {"B_UAV_01_F"} else {"O_UAV_01_F"};
  4342. private _enemySide = if (_side == west) then {east} else {west};
  4343.  
  4344. private _droneSpawnPos = _basePos vectorAdd [0, 0, 500];
  4345. private _drone = createVehicle [_droneClass, _droneSpawnPos, [], 0, "FLY"];
  4346. createVehicleCrew _drone;
  4347. _drone flyInHeight 300;
  4348.  
  4349. private _droneGroup = group _drone;
  4350. private _wpToTarget = _droneGroup addWaypoint [_targetPos, 0];
  4351. _wpToTarget setWaypointType "MOVE";
  4352. _wpToTarget setWaypointBehaviour "SAFE";
  4353. _wpToTarget setWaypointSpeed "NORMAL";
  4354.  
  4355. private _loiterWp = _droneGroup addWaypoint [_targetPos, 0];
  4356. _loiterWp setWaypointType "LOITER";
  4357. _loiterWp setWaypointLoiterType "CIRCLE_L";
  4358. _loiterWp setWaypointLoiterRadius 500;
  4359. _loiterWp setWaypointBehaviour "SAFE";
  4360. _loiterWp setWaypointSpeed "NORMAL";
  4361.  
  4362. // MODIFIED: The drone will now loiter and spot indefinitely until destroyed.
  4363. [_drone, _targetPos, _enemySide] spawn {
  4364. params ["_drone", "_targetPos", "_enemySide"];
  4365. while {!isNull _drone && alive _drone} do {
  4366. private _nearEnemies = _targetPos nearEntities [["CAManBase", "LandVehicle", "Air"], 1000] select {side _x == _enemySide && alive _x};
  4367. { _drone reveal [_x, 4]; } forEach _nearEnemies;
  4368. sleep 5; // Check for enemies every 5 seconds
  4369. };
  4370. };
  4371.  
  4372. true
  4373. };
  4374.  
  4375. // Call artillery support
  4376. fnc_callArtillerySupport = {
  4377. params ["_side", "_targetPos"];
  4378. if (!([_side, "ARTILLERY"] call fnc_isSupportAvailable)) exitWith { false };
  4379. if (!([_side] call fnc_canAffordSupport)) exitWith { false };
  4380.  
  4381. // Check if target is inside enemy base area
  4382. private _enemyBaseMarker = if (_side == west) then {"opfor_base_area"} else {"blufor_base_area"};
  4383. private _enemyBasePos = markerPos _enemyBaseMarker;
  4384. private _enemyBaseSize = (markerSize _enemyBaseMarker) select 0;
  4385.  
  4386. if (_targetPos distance2D _enemyBasePos < _enemyBaseSize) exitWith {
  4387. false // Don't allow mortars inside enemy base area
  4388. };
  4389.  
  4390. // Check for friendlies in target area
  4391. private _nearbyFriendlies = _targetPos nearEntities [["CAManBase", "LandVehicle"], 50];
  4392. private _friendliesInArea = _nearbyFriendlies select {side _x == _side && alive _x};
  4393.  
  4394. if (count _friendliesInArea > 0) exitWith {
  4395. false // Don't allow mortars on friendlies
  4396. };
  4397.  
  4398. [_side] call fnc_deductSupportCost;
  4399. private _sideName = if (_side == west) then {"BLUFOR"} else {"OPFOR"};
  4400. systemChat format ["GLOBAL ALERT: %1 has called for Mortar support.", _sideName];
  4401. [
  4402. _side,
  4403. "Support Command",
  4404. format["Mortar fire mission requested on grid %1.", mapGridPosition _targetPos]
  4405. ] remoteExecCall ["sideChat", _side];
  4406.  
  4407. if (_side == west) then {
  4408. BLUFOR_LAST_ARTILLERY = time;
  4409. } else {
  4410. OPFOR_LAST_ARTILLERY = time;
  4411. };
  4412.  
  4413. [_side, _targetPos, "mil_destroy", "Mortar Strike"] call fnc_HC_createSupportMarker;
  4414.  
  4415. [_targetPos, _side] spawn {
  4416. params ["_pos", "_side"];
  4417.  
  4418. // Spawn red smoke first
  4419. [
  4420. _side,
  4421. "Support Command",
  4422. "Marking target with red smoke. Mortars inbound."
  4423. ] remoteExecCall ["sideChat", _side];
  4424.  
  4425. private _smoke = createVehicle ["SmokeShellRed", _pos, [], 0, "CAN_COLLIDE"];
  4426. _smoke setPos _pos;
  4427.  
  4428. // Wait 10 seconds after smoke
  4429. sleep 10;
  4430.  
  4431. [
  4432. _side,
  4433. "Support Command",
  4434. "SPLASH! Mortar rounds impacting."
  4435. ] remoteExecCall ["sideChat", _side];
  4436.  
  4437. // Fire 10 mortar rounds over 30 seconds in a 20-40 meter area
  4438. for "_i" from 1 to 10 do {
  4439. private _impactRadius = 20 + (random 20); // 20-40 meter radius
  4440. private _roundPos = _pos vectorAdd [
  4441. (random (_impactRadius * 2)) - _impactRadius,
  4442. (random (_impactRadius * 2)) - _impactRadius,
  4443. 0
  4444. ];
  4445.  
  4446. // Create mortar shell
  4447. private _shell = "Sh_82mm_AMOS" createVehicle [_roundPos select 0, _roundPos select 1, 200];
  4448. _shell setVectorDir [0, 0, -1];
  4449. _shell setVelocity [0, 0, -100];
  4450.  
  4451. sleep 3; // 30 seconds / 10 rounds = 3 seconds between rounds
  4452. };
  4453.  
  4454. [
  4455. _side,
  4456. "Support Command",
  4457. "Mortar mission complete. BDA to follow."
  4458. ] remoteExecCall ["sideChat", _side];
  4459. };
  4460.  
  4461. true
  4462. };
  4463.  
  4464. // ==================== INTEL OPTIMIZATION START ====================
  4465. // MODIFIED: This function now reads from the new, lightweight tactical report queue.
  4466. fnc_evaluateSupportNeed = {
  4467. params ["_side"];
  4468. private _supportToCall = "";
  4469. if (!([_side] call fnc_canAffordSupport)) exitWith {_supportToCall};
  4470.  
  4471. // OPTIMIZATION: Read from the fast report queue instead of the state machine.
  4472. private _recentReports = if (_side == west) then {BLUFOR_TACTICAL_REPORTS} else {OPFOR_TACTICAL_REPORTS};
  4473.  
  4474. // Clean out stale reports from the queue (older than 5 minutes)
  4475. for "_i" from (count _recentReports - 1) to 0 step -1 do {
  4476. private _report = _recentReports select _i;
  4477. if (time - (_report select 0) > 300) then {
  4478. _recentReports deleteAt _i;
  4479. };
  4480. };
  4481.  
  4482. private _heavyContactReports = count _recentReports;
  4483. private _recentCombatAreas = [];
  4484. { _recentCombatAreas pushBackUnique (_x select 2); } forEach _recentReports;
  4485.  
  4486. // Check for friendly casualties to call smoke
  4487. private _friendlyGroups = allGroups select {side _x == _side && count (units _x) > 0};
  4488. private _casualtyAreas = [];
  4489. {
  4490. private _group = _x;
  4491. private _casualties = {damage _x > 0.3} count (units _group);
  4492. if (_casualties >= 2 && behaviour leader _group == "COMBAT") then {
  4493. _casualtyAreas pushBack (getPos leader _group);
  4494. };
  4495. } forEach _friendlyGroups;
  4496.  
  4497. // Priority 1: Drone for reconnaissance
  4498. if ([_side, "DRONE"] call fnc_isSupportAvailable) then {
  4499. _supportToCall = "DRONE";
  4500. };
  4501.  
  4502. // Priority 2: Artillery on any enemy concentration
  4503. if (_supportToCall == "" && _heavyContactReports > 0) then {
  4504. if ([_side, "ARTILLERY"] call fnc_isSupportAvailable) then {
  4505. _supportToCall = "ARTILLERY";
  4506. };
  4507. };
  4508.  
  4509. // Priority 3: Smoke for friendly casualties
  4510. if (_supportToCall == "") then {
  4511. if (count _casualtyAreas > 0 && ([_side, "SMOKE"] call fnc_isSupportAvailable)) then {
  4512. _supportToCall = "SMOKE";
  4513. };
  4514. };
  4515.  
  4516. // Priority 4: Flares for night operations
  4517. if (_supportToCall == "") then {
  4518. if (sunOrMoon < 0.1 && ([_side, "FLARE"] call fnc_isSupportAvailable)) then {
  4519. if (count _recentCombatAreas > 0) then {
  4520. _supportToCall = "FLARE";
  4521. };
  4522. };
  4523. };
  4524.  
  4525. _supportToCall
  4526. };
  4527. // ===================== INTEL OPTIMIZATION END =====================
  4528.  
  4529. // Cluster positions for analysis
  4530. fnc_clusterPositions = {
  4531. params ["_positions", "_clusterRadius"];
  4532. private _clusters = [];
  4533. private _unclustered = +_positions;
  4534.  
  4535. while {count _unclustered > 0} do {
  4536. private _seed = _unclustered select 0;
  4537. private _cluster = [_seed];
  4538. _unclustered = _unclustered - [_seed];
  4539.  
  4540. private _toCheck = +_cluster;
  4541. while {count _toCheck > 0} do {
  4542. private _current = _toCheck select 0;
  4543. _toCheck = _toCheck - [_current];
  4544. private _nearby = _unclustered select {_x distance _current < _clusterRadius};
  4545. _cluster append _nearby;
  4546. _unclustered = _unclustered - _nearby;
  4547. _toCheck append _nearby;
  4548. };
  4549. _clusters pushBack _cluster;
  4550. };
  4551.  
  4552. _clusters
  4553. };
  4554.  
  4555. // Find tactical positions on the battlefield (high ground, flanking routes, defensive positions)
  4556. fnc_findTacticalPosition = {
  4557. params ["_centerPos", "_role", "_friendlyBase", "_enemyBase"];
  4558.  
  4559. private _position = [];
  4560. private _rawPosition = [];
  4561. private _playAreaCenter = missionNamespace getVariable ["PLAY_AREA_CENTER", [worldSize/2, worldSize/2, 0]];
  4562. private _playAreaRadius = missionNamespace getVariable ["PLAY_AREA_RADIUS", 5000];
  4563.  
  4564. switch (_role) do {
  4565. case "HIGH_GROUND": {
  4566. // Find elevated positions near the battle
  4567. private _bestHeight = -999;
  4568. for "_i" from 0 to 8 do {
  4569. private _testPos = _centerPos getPos [random 800, random 360];
  4570. private _height = getTerrainHeightASL _testPos;
  4571. // Check if position is within play area
  4572. if (_height > _bestHeight && !surfaceIsWater _testPos && (_testPos distance2D _playAreaCenter) < _playAreaRadius) then {
  4573. _bestHeight = _height;
  4574. _position = _testPos;
  4575. };
  4576. };
  4577. };
  4578. case "FLANK_LEFT": {
  4579. // Position to the left of the line between friendly and enemy base
  4580. private _dirToEnemy = _friendlyBase getDir _enemyBase;
  4581. private _flankDir = _dirToEnemy - 90;
  4582. private _distance = ((_friendlyBase distance _enemyBase) * 0.6) min (_playAreaRadius - 100);
  4583. _rawPosition = _centerPos getPos [_distance, _flankDir];
  4584. };
  4585. case "FLANK_RIGHT": {
  4586. // Position to the right of the line between friendly and enemy base
  4587. private _dirToEnemy = _friendlyBase getDir _enemyBase;
  4588. private _flankDir = _dirToEnemy + 90;
  4589. private _distance = ((_friendlyBase distance _enemyBase) * 0.6) min (_playAreaRadius - 100);
  4590. _rawPosition = _centerPos getPos [_distance, _flankDir];
  4591. };
  4592. case "DEFENSIVE": {
  4593. // Position between friendly base and battle center
  4594. private _dirToBase = _centerPos getDir _friendlyBase;
  4595. private _distance = ((_centerPos distance _friendlyBase) * 0.4) min (_playAreaRadius - 100);
  4596. _rawPosition = _centerPos getPos [_distance, _dirToBase];
  4597. };
  4598. case "FORWARD": {
  4599. // Position toward enemy base
  4600. private _dirToEnemy = _centerPos getDir _enemyBase;
  4601. private _distance = (400 + random 200) min (_playAreaRadius - 100);
  4602. _rawPosition = _centerPos getPos [_distance, _dirToEnemy];
  4603. };
  4604. case "SUPPORT": {
  4605. // Position behind the main battle line
  4606. private _dirToBase = _centerPos getDir _friendlyBase;
  4607. private _distance = (300 + random 200) min (_playAreaRadius - 100);
  4608. _rawPosition = _centerPos getPos [_distance, _dirToBase];
  4609. };
  4610. };
  4611.  
  4612. // MODIFIED: Centralized validation for all roles except HIGH_GROUND
  4613. if (count _rawPosition > 0) then {
  4614. _position = [_rawPosition, 50, 200, 10, 0, 0.4, 0] call BIS_fnc_findSafePos;
  4615. };
  4616.  
  4617. // Ensure position is valid and within play area
  4618. if (count _position == 0 || (_position distance2D _playAreaCenter) > _playAreaRadius) then {
  4619. _position = _centerPos;
  4620. };
  4621.  
  4622. _position
  4623. };
  4624.  
  4625. // Create a comprehensive battle plan based on available forces and intel
  4626. fnc_createBattlePlan = {
  4627. params ["_side", "_availableGroups", "_enemyBase", "_ownBase"];
  4628.  
  4629. private _battlePlan = createHashMap;
  4630.  
  4631. // Get intel to determine battle center
  4632. private _intel = if (_side == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  4633. private _enemyPositions = [];
  4634. {
  4635. private _report = _y;
  4636. private _age = time - (_report get "time");
  4637. if (_age < 120) then {
  4638. _enemyPositions pushBack (_report get "position");
  4639. };
  4640. } forEach _intel;
  4641.  
  4642. // Determine battle center
  4643. private _battleCenter = _enemyBase;
  4644. if (count _enemyPositions > 0) then {
  4645. private _avgX = 0; private _avgY = 0;
  4646. {_avgX = _avgX + (_x select 0); _avgY = _avgY + (_x select 1);} forEach _enemyPositions;
  4647. _battleCenter = [_avgX / count _enemyPositions, _avgY / count _enemyPositions, 0];
  4648. };
  4649.  
  4650. // OPTIMIZATION: Pre-allocate arrays for group classification
  4651. private _infantryGroups = [];
  4652. private _armorGroups = [];
  4653. private _mechanizedGroups = [];
  4654. private _airGroups = [];
  4655. private _specialGroups = [];
  4656.  
  4657. // OPTIMIZATION: Classify groups with caching
  4658. {
  4659. private _group = _x;
  4660.  
  4661. // Check if classification is cached and still valid
  4662. private _cachedType = _group getVariable ["classifiedType", ""];
  4663. private _cacheTime = _group getVariable ["classificationTime", 0];
  4664.  
  4665. // Cache is valid for 60 seconds
  4666. if (_cachedType == "" || (time - _cacheTime) > 60) then {
  4667. _cachedType = [_group] call fnc_classifyGroup;
  4668. _group setVariable ["classifiedType", _cachedType];
  4669. _group setVariable ["classificationTime", time];
  4670. };
  4671.  
  4672. // Use cached type for categorization
  4673. switch (_cachedType) do {
  4674. case "INFANTRY": {_infantryGroups pushBack _group;};
  4675. case "ARMOR": {_armorGroups pushBack _group;};
  4676. case "MECHANIZED": {_mechanizedGroups pushBack _group;};
  4677. case "AIR": {_airGroups pushBack _group;};
  4678. case "SNIPER";
  4679. case "SPECOPS";
  4680. case "ELITE": {_specialGroups pushBack _group;};
  4681. };
  4682. } forEach _availableGroups;
  4683.  
  4684. // Calculate force strength
  4685. private _totalInfantry = count _infantryGroups;
  4686. private _totalArmor = count _armorGroups;
  4687. private _totalMech = count _mechanizedGroups;
  4688.  
  4689. // Initialize battle plan structure
  4690. _battlePlan set ["attackers", []];
  4691. _battlePlan set ["defenders", []];
  4692. _battlePlan set ["leftFlank", []];
  4693. _battlePlan set ["rightFlank", []];
  4694. _battlePlan set ["reserve", []];
  4695. _battlePlan set ["support", []];
  4696. _battlePlan set ["battleCenter", _battleCenter];
  4697.  
  4698. // Defense: Keep some forces back if under pressure
  4699. private _enemiesNearBase = [_side, 800] call fnc_enemiesNearBase;
  4700. if (_enemiesNearBase) then {
  4701. private _defenderCount = (count _availableGroups * 0.3) max 2;
  4702. for "_i" from 0 to (_defenderCount min (count _infantryGroups) - 1) do {
  4703. (_battlePlan get "defenders") pushBack (_infantryGroups select _i);
  4704. };
  4705. _infantryGroups = _infantryGroups - (_battlePlan get "defenders");
  4706. };
  4707.  
  4708. // Special forces: Flanking and harassment
  4709. {
  4710. if ((count (_battlePlan get "leftFlank")) < 2) then {
  4711. (_battlePlan get "leftFlank") pushBack _x;
  4712. } else {
  4713. (_battlePlan get "rightFlank") pushBack _x;
  4714. };
  4715. } forEach _specialGroups;
  4716.  
  4717. // Armor: Main assault force
  4718. {
  4719. (_battlePlan get "attackers") pushBack _x;
  4720. } forEach _armorGroups;
  4721.  
  4722. // Mechanized: Support assault or flanking
  4723. private _mechCount = count _mechanizedGroups;
  4724. for "_i" from 0 to (_mechCount - 1) do {
  4725. private _group = _mechanizedGroups select _i;
  4726. if (_i % 2 == 0) then {
  4727. (_battlePlan get "attackers") pushBack _group;
  4728. } else {
  4729. if ((count (_battlePlan get "leftFlank")) <= (count (_battlePlan get "rightFlank"))) then {
  4730. (_battlePlan get "leftFlank") pushBack _group;
  4731. } else {
  4732. (_battlePlan get "rightFlank") pushBack _group;
  4733. };
  4734. };
  4735. };
  4736.  
  4737. // OPTIMIZATION: Simplified infantry distribution using direct calculations
  4738. private _infCount = count _infantryGroups;
  4739. if (_infCount > 0) then {
  4740. private _attackerCount = ceil (_infCount * 0.4);
  4741. private _flankCount = ceil (_infCount * 0.3);
  4742.  
  4743. // Attackers
  4744. for "_i" from 0 to (_attackerCount - 1) do {
  4745. if (_i < _infCount) then {
  4746. (_battlePlan get "attackers") pushBack (_infantryGroups select _i);
  4747. };
  4748. };
  4749.  
  4750. // Flankers
  4751. for "_i" from _attackerCount to (_attackerCount + _flankCount - 1) do {
  4752. if (_i < _infCount) then {
  4753. if ((_i - _attackerCount) % 2 == 0) then {
  4754. (_battlePlan get "leftFlank") pushBack (_infantryGroups select _i);
  4755. } else {
  4756. (_battlePlan get "rightFlank") pushBack (_infantryGroups select _i);
  4757. };
  4758. };
  4759. };
  4760.  
  4761. // Reserve
  4762. for "_i" from (_attackerCount + _flankCount) to (_infCount - 1) do {
  4763. (_battlePlan get "reserve") pushBack (_infantryGroups select _i);
  4764. };
  4765. };
  4766.  
  4767. // Air units: Support role
  4768. {
  4769. (_battlePlan get "support") pushBack _x;
  4770. } forEach _airGroups;
  4771.  
  4772. _battlePlan
  4773. };
  4774.  
  4775. // Execute a specific role for a group with appropriate waypoints
  4776. fnc_executeGroupRole = {
  4777. params ["_group", "_role", "_battleCenter", "_ownBase", "_enemyBase"];
  4778.  
  4779. private _groupType = [_group] call fnc_classifyGroup;
  4780.  
  4781. switch (_role) do {
  4782. case "ATTACK": {
  4783. // Direct assault on battle center
  4784. private _attackPos = [_battleCenter, "FORWARD", _ownBase, _enemyBase] call fnc_findTacticalPosition;
  4785. [_group, _attackPos, _groupType] call fnc_createTacticalPath;
  4786. _group setVariable ["HC_ROLE", "ATTACKER", true];
  4787. };
  4788. case "DEFEND": {
  4789. // Defend own base
  4790. private _defendPos = [_battleCenter, "DEFENSIVE", _ownBase, _enemyBase] call fnc_findTacticalPosition;
  4791. [_group, _defendPos] call fnc_createDefenseWaypoints;
  4792. _group setVariable ["HC_ROLE", "DEFENDER", true];
  4793. };
  4794. case "FLANK_LEFT": {
  4795. // Flank from the left
  4796. private _flankPos = [_battleCenter, "FLANK_LEFT", _ownBase, _enemyBase] call fnc_findTacticalPosition;
  4797. [_group, "FLANK_LEFT", _flankPos, _groupType] call fnc_createAdvancedWaypoints;
  4798. _group setVariable ["HC_ROLE", "FLANKER", true];
  4799. };
  4800. case "FLANK_RIGHT": {
  4801. // Flank from the right
  4802. private _flankPos = [_battleCenter, "FLANK_RIGHT", _ownBase, _enemyBase] call fnc_findTacticalPosition;
  4803. [_group, "FLANK_RIGHT", _flankPos, _groupType] call fnc_createAdvancedWaypoints;
  4804. _group setVariable ["HC_ROLE", "FLANKER", true];
  4805. };
  4806. case "RESERVE": {
  4807. // Hold position between base and battle
  4808. private _reservePos = [_battleCenter, "SUPPORT", _ownBase, _enemyBase] call fnc_findTacticalPosition;
  4809. [_group, _reservePos] call fnc_createDefenseWaypoints;
  4810. _group setVariable ["HC_ROLE", "RESERVE", true];
  4811. };
  4812. case "SUPPORT": {
  4813. // Air or long-range support
  4814. if (_groupType == "AIR") then {
  4815. [_group, _battleCenter] call fnc_createHelicopterAttackWaypoints;
  4816. } else {
  4817. private _supportPos = [_battleCenter, "HIGH_GROUND", _ownBase, _enemyBase] call fnc_findTacticalPosition;
  4818. [_group, "OVERWATCH", _supportPos, _groupType] call fnc_createAdvancedWaypoints;
  4819. };
  4820. _group setVariable ["HC_ROLE", "SUPPORT", true];
  4821. };
  4822. };
  4823.  
  4824. [_group, _role, _battleCenter] call fnc_assignGroupObjective;
  4825. };
  4826.  
  4827. // Get strategic groups
  4828. fnc_getStrategicGroups = {
  4829. params ["_side"];
  4830. allGroups select {
  4831. (side _x == _side) &&
  4832. (count (units _x) > 0) &&
  4833. ({isPlayer _x} count (units _x) == 0)
  4834. };
  4835. };
  4836.  
  4837. // Check for enemies near base
  4838. fnc_enemiesNearBase = {
  4839. params ["_side", ["_radius", 500]];
  4840. private _ownBasePos = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  4841. private _enemySide = if (_side == west) then {east} else {west};
  4842. private _nearbyEnemyUnits = _ownBasePos nearEntities [["CAManBase", "LandVehicle", "Air"], _radius];
  4843. private _firstEnemy = _nearbyEnemyUnits findIf {alive _x && side _x == _enemySide};
  4844. (_firstEnemy != -1)
  4845. };
  4846.  
  4847. // Check if group is locked
  4848. fnc_isGroupLocked = {
  4849. params ["_group"];
  4850. if (isNull _group || {count (units _group) == 0}) exitWith {false};
  4851. if (_group getVariable ["HC_FORCED_LOCK", false]) exitWith {true};
  4852.  
  4853. private _currentTime = time;
  4854. private _assignmentTime = _group getVariable ["HC_ASSIGNMENT_TIME", 0];
  4855. if (_assignmentTime == 0) exitWith {false};
  4856. if (_currentTime - _assignmentTime < HC_LOCK_DURATION) exitWith {true};
  4857. if (_currentTime - (_group getVariable ["HC_LAST_COMBAT", 0]) < HC_COMBAT_LOCK_TIME) exitWith {true};
  4858. false
  4859. };
  4860.  
  4861. // Get available groups
  4862. fnc_getAvailableGroups = {
  4863. params ["_side"];
  4864. private _allGroups = [_side] call fnc_getStrategicGroups;
  4865. private _availableGroups = [];
  4866. {
  4867. if !([_x] call fnc_isGroupLocked) then {
  4868. _availableGroups pushBack _x;
  4869. _x setVariable ["HC_ASSIGNMENT_TIME", nil];
  4870. _x setVariable ["HC_TARGET", nil];
  4871. _x setVariable ["HC_ORDER", nil];
  4872. HC_Active_Groups deleteAt (groupId _x);
  4873. };
  4874. } forEach _allGroups;
  4875. _availableGroups
  4876. };
  4877.  
  4878. // Assign group objective
  4879. fnc_assignGroupObjective = {
  4880. params ["_group", "_targetPos", "_orderType", ["_groupType", "INFANTRY"]];
  4881.  
  4882. // Validate targetPos is a proper position array
  4883. if (typeName _targetPos != "ARRAY" || count _targetPos < 2) exitWith {
  4884. diag_log format ["ERROR: Invalid target position for group %1: %2", groupId _group, _targetPos];
  4885. };
  4886.  
  4887. _group setVariable ["HC_ASSIGNMENT_TIME", time];
  4888. _group setVariable ["HC_TARGET", _targetPos];
  4889. _group setVariable ["HC_ORDER", _orderType];
  4890. _group setVariable ["HC_FORCED_LOCK", false];
  4891. HC_Active_Groups set [groupId _group, _group];
  4892.  
  4893. switch (_orderType) do {
  4894. case "ATTACK": {
  4895. [_group, _targetPos, _groupType] call fnc_createTacticalPath;
  4896. };
  4897. case "DEFEND": {
  4898. [_group, _targetPos] call fnc_createDefenseWaypoints;
  4899. };
  4900. case "PATROL": {
  4901. [_group, _targetPos] call fnc_createPatrolWaypoints;
  4902. };
  4903. case "RETREAT": {
  4904. while {count (waypoints _group) > 0} do {
  4905. deleteWaypoint ((waypoints _group) select 0);
  4906. };
  4907. private _wp = _group addWaypoint [_targetPos, 50];
  4908. _wp setWaypointType "MOVE";
  4909. _wp setWaypointBehaviour "AWARE";
  4910. _wp setWaypointSpeed "FULL";
  4911. };
  4912. default {
  4913. [_group, _targetPos] call fnc_createPatrolWaypoints;
  4914. };
  4915. };
  4916. };
  4917.  
  4918. // Classify group type
  4919. fnc_classifyGroup = {
  4920. params ["_group"];
  4921. private _units = units _group select {alive _x};
  4922. if (count _units == 0) exitWith { "EMPTY" };
  4923.  
  4924. // OPTIMIZATION: Check vehicles first (fastest check)
  4925. private _leader = leader _group;
  4926. if (!isNull _leader) then {
  4927. private _vehicle = vehicle _leader;
  4928. if (_vehicle != _leader) exitWith {
  4929. // Unit is in a vehicle - classify by vehicle type
  4930. if (_vehicle isKindOf "Air") exitWith { "AIR" };
  4931. if (_vehicle isKindOf "Tank") exitWith { "ARMOR" };
  4932. if ("APC" in typeOf _vehicle) exitWith { "MECHANIZED" };
  4933. "MOTORIZED"
  4934. };
  4935. };
  4936.  
  4937. // OPTIMIZATION: Check special unit flags first (most units won't have these)
  4938. private _firstUnit = _units select 0;
  4939. if (_firstUnit getVariable ["isSniper", false]) exitWith { "SNIPER" };
  4940. if (_firstUnit getVariable ["isSpecOps", false]) exitWith { "SPECOPS" };
  4941. if (_firstUnit getVariable ["isElite", false]) exitWith { "ELITE" };
  4942.  
  4943. // OPTIMIZATION: Only do detailed composition check if no special flags found
  4944. // Count special unit types
  4945. private _sniperCount = 0;
  4946. private _specOpsCount = 0;
  4947. private _eliteCount = 0;
  4948.  
  4949. {
  4950. if (_x getVariable ["isSniper", false]) then { _sniperCount = _sniperCount + 1; };
  4951. if (_x getVariable ["isSpecOps", false]) then { _specOpsCount = _specOpsCount + 1; };
  4952. if (_x getVariable ["isElite", false]) then { _eliteCount = _eliteCount + 1; };
  4953.  
  4954. // Early exit if we found enough special units
  4955. if (_sniperCount >= 2) exitWith {};
  4956. if (_specOpsCount >= 3) exitWith {};
  4957. if (_eliteCount >= 3) exitWith {};
  4958. } forEach _units;
  4959.  
  4960. if (_sniperCount >= 2) exitWith { "SNIPER" };
  4961. if (_specOpsCount >= 3) exitWith { "SPECOPS" };
  4962. if (_eliteCount >= 3) exitWith { "ELITE" };
  4963.  
  4964. // OPTIMIZATION: Only check unit types if needed for specialized roles
  4965. private _atCount = 0;
  4966. {
  4967. private _typeStr = toLower (typeOf _x);
  4968. if ("_at_" in _typeStr || "_lat_" in _typeStr) then {
  4969. _atCount = _atCount + 1;
  4970. if (_atCount >= 2) exitWith {}; // Early exit
  4971. };
  4972. } forEach _units;
  4973.  
  4974. if (_atCount >= 2) exitWith { "ANTI_ARMOR" };
  4975.  
  4976. // Default to infantry
  4977. "INFANTRY"
  4978. };
  4979.  
  4980. // Calculate how detectable a target is based on movement and stance
  4981. fnc_calculateTargetDetectability = {
  4982. params ["_target"];
  4983.  
  4984. private _velocity = velocity _target;
  4985. private _speed = sqrt((_velocity select 0)^2 + (_velocity select 1)^2); // 2D speed in m/s
  4986. private _stance = stance _target; // "STAND", "CROUCH", "PRONE", "UNDEFINED"
  4987.  
  4988. // Base detectability by stance
  4989. private _stanceMultiplier = 1.0;
  4990. switch (_stance) do {
  4991. case "STAND": { _stanceMultiplier = 1.0; };
  4992. case "CROUCH": { _stanceMultiplier = 0.6; };
  4993. case "PRONE": { _stanceMultiplier = 0.3; };
  4994. default { _stanceMultiplier = 1.0; }; // Undefined or in vehicle
  4995. };
  4996.  
  4997. // Movement speed modifiers
  4998. private _movementMultiplier = 1.0;
  4999.  
  5000. if (_speed < 0.5) then {
  5001. // Stationary or very slow
  5002. _movementMultiplier = 0.5;
  5003. } else {
  5004. if (_speed < 2.5) then {
  5005. // Slow walk/crouch walk
  5006. _movementMultiplier = 0.7;
  5007. } else {
  5008. if (_speed < 4.5) then {
  5009. // Normal walk
  5010. _movementMultiplier = 0.85;
  5011. } else {
  5012. if (_speed < 6.5) then {
  5013. // Fast walk / jog
  5014. _movementMultiplier = 1.0;
  5015. } else {
  5016. // Sprint
  5017. _movementMultiplier = 1.3;
  5018. };
  5019. };
  5020. };
  5021. };
  5022.  
  5023. // Combine stance and movement
  5024. private _finalDetectability = _stanceMultiplier * _movementMultiplier;
  5025.  
  5026. // Clamp between 0.15 and 1.3
  5027. _finalDetectability = (_finalDetectability max 0.15) min 1.3;
  5028.  
  5029. _finalDetectability
  5030. };
  5031.  
  5032. // Calculate spotter's detection skill
  5033. fnc_calculateSpotterSkill = {
  5034. params ["_spotter"];
  5035.  
  5036. private _skillMultiplier = 1.0;
  5037.  
  5038. // Elite troops are the best spotters
  5039. if (_spotter getVariable ["isElite", false]) exitWith { 1.4 };
  5040.  
  5041. // SpecOps and Snipers are excellent spotters
  5042. if (_spotter getVariable ["isSpecOps", false]) exitWith { 1.3 };
  5043. if (_spotter getVariable ["isSniper", false]) exitWith { 1.3 };
  5044.  
  5045. // Upgraded troops are above average
  5046. if (_spotter getVariable ["isUpgraded", false]) exitWith { 1.15 };
  5047.  
  5048. // Standard infantry (default)
  5049. 1.0
  5050. };
  5051.  
  5052. // ==================== INTEL OPTIMIZATION START ====================
  5053. // MODIFIED: This function now also populates the new lightweight tactical report queue.
  5054. HC_fnc_gatherIntelligence = {
  5055. params ["_unit", "_side"];
  5056. if (!alive _unit) exitWith {};
  5057.  
  5058. // NEW: Prevent intel gathering while parachuting or in freefall
  5059. private _vehicle = vehicle _unit;
  5060. if (_vehicle != _unit && {_vehicle isKindOf "ParachuteBase"}) exitWith {};
  5061. if ((getPosATL _unit select 2) > 5 && (velocity _unit select 2) < -5 && _vehicle == _unit) exitWith {};
  5062.  
  5063. // Performance throttling - skip some spotting checks when server is struggling
  5064. private _skipSpotting = missionNamespace getVariable ["PERF_SKIP_SPOTTING", false];
  5065. if (_skipSpotting) then {
  5066. private _spottingChance = missionNamespace getVariable ["PERF_SPOTTING_CHANCE", 1.0];
  5067. if (random 1 > _spottingChance) exitWith {}; // Skip this unit's spotting check
  5068. };
  5069.  
  5070. private _enemySide = if (_side == west) then {east} else {west};
  5071. private _intel = if (_side == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  5072. private _viewDistance = 500;
  5073.  
  5074. if (_unit getVariable ["isElite", false]) then { _viewDistance = 1200; };
  5075. if (_unit getVariable ["isSpecOps", false]) then { _viewDistance = 1200; };
  5076. if (_unit getVariable ["isSniper", false]) then { _viewDistance = 1500; };
  5077. if (vehicle _unit != _unit) then { _viewDistance = 1000; };
  5078.  
  5079. // Get spotter's skill modifier
  5080. private _spotterSkill = [_unit] call fnc_calculateSpotterSkill;
  5081.  
  5082. // OPTIMIZATION: Use nearEntities with type filter to reduce initial search
  5083. private _potentialEnemies = _unit nearEntities [["CAManBase", "LandVehicle", "Air"], _viewDistance];
  5084.  
  5085. // OPTIMIZATION: Early exit if no potential enemies found
  5086. if (count _potentialEnemies == 0) exitWith {};
  5087.  
  5088. // OPTIMIZATION: Filter enemies with combined checks to reduce iterations
  5089. private _nearEnemies = _potentialEnemies select {
  5090. (side _x == _enemySide || (_x getVariable ["isAAF", false])) &&
  5091. alive _x
  5092. };
  5093.  
  5094. // OPTIMIZATION: Early exit if no visible enemies
  5095. if (count _nearEnemies == 0) exitWith {};
  5096.  
  5097. if (behaviour _unit in ["COMBAT", "STEALTH"]) then {
  5098. (group _unit) setVariable ["HC_LAST_COMBAT", time];
  5099. };
  5100.  
  5101. // OPTIMIZATION: Get tactical reports array once
  5102. private _tacticalReports = if (_side == west) then {BLUFOR_TACTICAL_REPORTS} else {OPFOR_TACTICAL_REPORTS};
  5103.  
  5104. {
  5105. private _enemy = _x;
  5106.  
  5107. // Check basic visibility first
  5108. private _visibility = [_unit, "VIEW", _enemy] checkVisibility [eyePos _unit, eyePos _enemy];
  5109.  
  5110. if (_visibility > 0.1) then {
  5111. // Calculate target detectability based on movement and stance
  5112. private _targetDetectability = [_enemy] call fnc_calculateTargetDetectability;
  5113.  
  5114. // Combine visibility, spotter skill, and target detectability
  5115. private _detectionChance = _visibility * _spotterSkill * _targetDetectability;
  5116.  
  5117. // Random roll to see if detection succeeds
  5118. private _randomRoll = random 1;
  5119.  
  5120. if (_randomRoll < _detectionChance) then {
  5121. // Successfully spotted the enemy - gather intel
  5122. private _enemyID = netId _enemy;
  5123.  
  5124. // OPTIMIZATION: Determine entity type with early exit checks
  5125. private _entityType = "INFANTRY";
  5126. private _isTank = false;
  5127.  
  5128. if !(_enemy isKindOf "CAManBase") then {
  5129. if (_enemy isKindOf "Air") then {
  5130. _entityType = "AIR_VEHICLE";
  5131. } else {
  5132. _entityType = "GROUND_VEHICLE";
  5133. if (_enemy isKindOf "Tank") then {
  5134. _isTank = true;
  5135. };
  5136. };
  5137. };
  5138.  
  5139. // OPTIMIZATION: Create report with minimal overhead
  5140. private _intelReport = createHashMap;
  5141. _intelReport set ["position", getPos _enemy];
  5142. _intelReport set ["time", time];
  5143. _intelReport set ["type", typeOf _enemy];
  5144. _intelReport set ["isMounted", vehicle _enemy != _enemy];
  5145. _intelReport set ["entityType", _entityType];
  5146. _intelReport set ["isTank", _isTank];
  5147. _intelReport set ["isAAF", _enemy getVariable ["isAAF", false]];
  5148. _intel set [_enemyID, _intelReport];
  5149.  
  5150. // Add to tactical report queue
  5151. _tacticalReports pushBack [time, _entityType, getPos _enemy, _isTank];
  5152.  
  5153. // OPTIMIZATION: Cap array size inline (no separate check needed)
  5154. if (count _tacticalReports > HC_REPORTS_MAX_SIZE) then {
  5155. _tacticalReports deleteAt 0;
  5156. };
  5157. };
  5158. };
  5159. } forEach _nearEnemies;
  5160. };
  5161.  
  5162.  
  5163. // Plan coordinated attack
  5164. fnc_planCoordinatedAttack = {
  5165. params ["_attackGroups", "_targetPos", "_side"];
  5166. if (count _attackGroups == 0) exitWith {};
  5167.  
  5168. if (count _attackGroups < 2) exitWith {
  5169. private _group = _attackGroups select 0;
  5170. [_group, "ASSAULT", _targetPos, [_group] call fnc_classifyGroup] call fnc_createAdvancedWaypoints;
  5171. };
  5172.  
  5173. private _assaultGroups = [];
  5174. private _supportGroups = [];
  5175. private _flankingGroups = [];
  5176.  
  5177. {
  5178. private _group = _x;
  5179. private _type = [_group] call fnc_classifyGroup;
  5180. switch (_type) do {
  5181. case "ARMOR";
  5182. case "MECHANIZED";
  5183. case "SNIPER";
  5184. case "SUPPORT": { _supportGroups pushBack _group; };
  5185. case "SPECOPS";
  5186. case "ELITE": { _flankingGroups pushBack _group; };
  5187. default {
  5188. if (count _assaultGroups <= count _flankingGroups) then {
  5189. _assaultGroups pushBack _group;
  5190. } else {
  5191. _flankingGroups pushBack _group;
  5192. };
  5193. };
  5194. };
  5195. } forEach _attackGroups;
  5196.  
  5197. { [_x, "ASSAULT", _targetPos, [_x] call fnc_classifyGroup] call fnc_createAdvancedWaypoints; } forEach _assaultGroups;
  5198. {
  5199. private _grpType = [_x] call fnc_classifyGroup;
  5200. private _order = if (_grpType == "SNIPER") then {"DEEP_FLANK"} else {"OVERWATCH"};
  5201. [_x, _order, _targetPos, _grpType] call fnc_createAdvancedWaypoints;
  5202. } forEach _supportGroups;
  5203.  
  5204. private _flankCount = count _flankingGroups;
  5205. {
  5206. private _group = _x;
  5207. private _grpType = [_group] call fnc_classifyGroup;
  5208. private _flankSide = if (_forEachIndex < (_flankCount / 2)) then {"FLANK_LEFT"} else {"FLANK_RIGHT"};
  5209. [_group, _flankSide, _targetPos, _grpType] call fnc_createAdvancedWaypoints;
  5210. } forEach _flankingGroups;
  5211. };
  5212.  
  5213. fnc_processIntelligence = {
  5214. params ["_side"];
  5215. private _intel = if (_side == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  5216.  
  5217. // OPTIMIZATION: Early exit if no intel to process
  5218. if (count _intel == 0) exitWith {};
  5219.  
  5220. private _currentTime = time;
  5221. private _keysToRemove = [];
  5222.  
  5223. // OPTIMIZATION: Removed complex threat categorization - it's rarely used effectively
  5224. // Simply clean old intel and let other systems query what they need directly
  5225. {
  5226. private _report = _y;
  5227. private _age = _currentTime - (_report get "time");
  5228. if (_age > HC_INTEL_DECAY_TIME) then {
  5229. _keysToRemove pushBack _x;
  5230. };
  5231. } forEach _intel;
  5232.  
  5233. // Clean up old intel
  5234. { _intel deleteAt _x; } forEach _keysToRemove;
  5235.  
  5236. // OPTIMIZATION: No need to store empty threat categorization
  5237. // Other functions can query intel directly when needed
  5238. };
  5239.  
  5240. // Create tactical path
  5241. fnc_createTacticalPath = {
  5242. params ["_group", "_targetPos", "_groupType"];
  5243. while {count (waypoints _group) > 0} do {
  5244. deleteWaypoint ((waypoints _group) select 0);
  5245. };
  5246.  
  5247. private _leader = leader _group;
  5248. if (isNull _leader) exitWith {};
  5249.  
  5250. private _startPos = position _leader;
  5251. private _distance = _startPos distance _targetPos;
  5252. private _directionVector = _targetPos vectorDiff _startPos;
  5253. private _isVehicleGroup = vehicle _leader != _leader;
  5254.  
  5255. if (_distance < 500) then {
  5256. private _wp = _group addWaypoint [_targetPos, 20];
  5257. _wp setWaypointType "SAD";
  5258. _wp setWaypointBehaviour "AWARE";
  5259. _wp setWaypointSpeed "FULL";
  5260. _wp setWaypointCombatMode "RED";
  5261. _wp setWaypointCompletionRadius 100;
  5262. } else {
  5263. private _segmentLength = 350;
  5264. private _numWaypoints = floor (_distance / _segmentLength);
  5265. _numWaypoints = ((_numWaypoints max 2) min 8);
  5266.  
  5267. for "_i" from 1 to _numWaypoints do {
  5268. private _multiplier = _i / (_numWaypoints + 1);
  5269. private _scaledVector = _directionVector vectorMultiply _multiplier;
  5270. private _directLinePos = _startPos vectorAdd _scaledVector;
  5271. private _searchRadius = 300;
  5272. private _tacticalPos = [];
  5273.  
  5274. if (_isVehicleGroup) then {
  5275. _tacticalPos = [_directLinePos, 50, _searchRadius, 10, 2, 0, 0] call BIS_fnc_findSafePos;
  5276. } else {
  5277. _tacticalPos = [_directLinePos, 50, _searchRadius, 10, 0, 0.5, 0] call BIS_fnc_findSafePos;
  5278. };
  5279.  
  5280. // MODIFIED: Only add the waypoint if a safe, non-water position was found.
  5281. if (count _tacticalPos > 0) then {
  5282. private _wp = _group addWaypoint [_tacticalPos, 50];
  5283. _wp setWaypointType "MOVE";
  5284. _wp setWaypointBehaviour "AWARE";
  5285. _wp setWaypointSpeed "NORMAL";
  5286. _wp setWaypointFormation "LINE";
  5287. _wp setWaypointCombatMode "YELLOW";
  5288. _wp setWaypointCompletionRadius 75;
  5289. };
  5290. };
  5291.  
  5292. private _finalWp = _group addWaypoint [_targetPos, 20];
  5293. _finalWp setWaypointType "SAD";
  5294. _finalWp setWaypointBehaviour "AWARE";
  5295. _finalWp setWaypointSpeed "FULL";
  5296. _finalWp setWaypointCombatMode "RED";
  5297. _finalWp setWaypointCompletionRadius 100;
  5298. };
  5299. };
  5300.  
  5301. // Create advanced waypoints
  5302. fnc_createAdvancedWaypoints = {
  5303. params ["_group", "_orderType", "_targetPos", "_groupType"];
  5304. while {count (waypoints _group) > 0} do {
  5305. deleteWaypoint ((waypoints _group) select 0);
  5306. };
  5307.  
  5308. switch (_orderType) do {
  5309. case "ASSAULT": {
  5310. [_group, _targetPos, _groupType] call fnc_createTacticalPath;
  5311. };
  5312. case "FLANK_LEFT": {
  5313. private _angle = [position leader _group, _targetPos] call BIS_fnc_dirTo;
  5314. private _flankAngle = _angle - 90 + (random 40 - 20);
  5315. private _flankDist1 = 400 + (random 400);
  5316. private _flankDist2 = 200 + (random 300);
  5317. private _flankPos1 = _targetPos getPos [_flankDist1, _flankAngle];
  5318. private _flankPos2 = _targetPos getPos [_flankDist2, _flankAngle - 45];
  5319.  
  5320. private _wp1 = _group addWaypoint [_flankPos1, 50];
  5321. _wp1 setWaypointType "MOVE";
  5322. _wp1 setWaypointBehaviour "AWARE";
  5323. _wp1 setWaypointSpeed "FULL";
  5324.  
  5325. private _wp2 = _group addWaypoint [_flankPos2, 30];
  5326. _wp2 setWaypointType "MOVE";
  5327. _wp2 setWaypointBehaviour "COMBAT";
  5328.  
  5329. private _wp3 = _group addWaypoint [_targetPos, 50];
  5330. _wp3 setWaypointType "SAD";
  5331. _wp3 setWaypointBehaviour "COMBAT";
  5332. };
  5333. case "FLANK_RIGHT": {
  5334. private _angle = [position leader _group, _targetPos] call BIS_fnc_dirTo;
  5335. private _flankAngle = _angle + 90 + (random 40 - 20);
  5336. private _flankDist1 = 400 + (random 400);
  5337. private _flankDist2 = 200 + (random 300);
  5338. private _flankPos1 = _targetPos getPos [_flankDist1, _flankAngle];
  5339. private _flankPos2 = _targetPos getPos [_flankDist2, _flankAngle + 45];
  5340.  
  5341. private _wp1 = _group addWaypoint [_flankPos1, 50];
  5342. _wp1 setWaypointType "MOVE";
  5343. _wp1 setWaypointBehaviour "AWARE";
  5344. _wp1 setWaypointSpeed "FULL";
  5345.  
  5346. private _wp2 = _group addWaypoint [_flankPos2, 30];
  5347. _wp2 setWaypointType "MOVE";
  5348. _wp2 setWaypointBehaviour "COMBAT";
  5349.  
  5350. private _wp3 = _group addWaypoint [_targetPos, 50];
  5351. _wp3 setWaypointType "SAD";
  5352. _wp3 setWaypointBehaviour "COMBAT";
  5353. };
  5354. case "OVERWATCH": {
  5355. private _overwatchPos = [_targetPos, 300, 600, 10, 0, 2, 0] call BIS_fnc_findSafePos;
  5356. if (count _overwatchPos == 0) then {
  5357. _overwatchPos = _targetPos getPos [400, random 360];
  5358. };
  5359.  
  5360. private _wp = _group addWaypoint [_overwatchPos, 10];
  5361. _wp setWaypointType "MOVE";
  5362. _wp setWaypointBehaviour "AWARE";
  5363. _wp setWaypointSpeed "NORMAL";
  5364.  
  5365. private _patrolWp = _group addWaypoint [_overwatchPos, 30];
  5366. _patrolWp setWaypointType "SAD";
  5367. _patrolWp setWaypointBehaviour "AWARE";
  5368. _patrolWp setWaypointSpeed "LIMITED";
  5369. _patrolWp setWaypointCompletionRadius 150;
  5370. };
  5371. case "DEEP_FLANK": {
  5372. private _angleBack = [_targetPos, position leader _group] call BIS_fnc_dirTo;
  5373. private _flankPos = _targetPos getPos [600, _angleBack + (random 60 - 30)];
  5374. private _wp1 = _group addWaypoint [_flankPos, 60];
  5375. _wp1 setWaypointType "MOVE";
  5376. _wp1 setWaypointBehaviour "STEALTH";
  5377. _wp1 setWaypointSpeed "LIMITED";
  5378.  
  5379. private _wpFinal = _group addWaypoint [_flankPos, 5];
  5380. _wpFinal setWaypointType "SAD";
  5381. _wpFinal setWaypointBehaviour "STEALTH";
  5382. _wpFinal setWaypointCompletionRadius 200;
  5383. };
  5384. case "RECON": {
  5385. for "_i" from 0 to 2 do {
  5386. private _reconPos = _targetPos getPos [300 + (_i * 100), random 360];
  5387. private _wp = _group addWaypoint [_reconPos, 30];
  5388. _wp setWaypointType "MOVE";
  5389. _wp setWaypointBehaviour "STEALTH";
  5390. _wp setWaypointSpeed "LIMITED";
  5391. };
  5392. if (count (waypoints _group) > 0) then {
  5393. (_group addWaypoint [waypointPosition [_group, 0], 0]) setWaypointType "CYCLE";
  5394. };
  5395. };
  5396. };
  5397.  
  5398. _group setVariable ["HC_ORDER", _orderType, true];
  5399. _group setVariable ["HC_ORDER_TIME", time, true];
  5400. _group setVariable ["HC_TARGET", _targetPos, true];
  5401. };
  5402.  
  5403. // Create patrol waypoints
  5404. fnc_createPatrolWaypoints = {
  5405. params ["_group", "_targetPos", ["_behavior", "AWARE"], ["_formation", "LINE"]];
  5406. while {count (waypoints _group) > 0} do {
  5407. deleteWaypoint ((waypoints _group) select 0);
  5408. };
  5409.  
  5410. private _patrolPos = [_targetPos, 500, 1000, 10, 2, 0, 10] call BIS_fnc_findSafePos;
  5411. if (count _patrolPos == 0) then { _patrolPos = _targetPos; };
  5412.  
  5413. (_group addWaypoint [_patrolPos, 50]) setWaypointType "MOVE";
  5414.  
  5415. for "_i" from 1 to 3 do {
  5416. _patrolPos = [_targetPos, 300, 1000, 10, 2, 0, 10] call BIS_fnc_findSafePos;
  5417. if (count _patrolPos == 0) then { _patrolPos = _targetPos; };
  5418. private _wp = _group addWaypoint [_patrolPos, 75];
  5419. _wp setWaypointType "SAD";
  5420. _wp setWaypointCompletionRadius 100;
  5421. };
  5422.  
  5423. if (count (waypoints _group) > 1) then {
  5424. (_group addWaypoint [waypointPosition [_group, 1], 0]) setWaypointType "CYCLE";
  5425. };
  5426.  
  5427. _group setBehaviour _behavior;
  5428. _group setCombatMode "RED";
  5429. };
  5430.  
  5431. // Create defense waypoints
  5432. fnc_createDefenseWaypoints = {
  5433. params ["_group", "_targetPos"];
  5434. while {count (waypoints _group) > 0} do {
  5435. deleteWaypoint ((waypoints _group) select 0);
  5436. };
  5437.  
  5438. private _patrolPoints = [];
  5439. for "_i" from 0 to 4 do {
  5440. private _point = [_targetPos, 0, 250, 10, 0, 0.3, 0] call BIS_fnc_findSafePos;
  5441. if (count _point > 0) then { _patrolPoints pushBack _point; };
  5442. };
  5443. if (count _patrolPoints == 0) then { _patrolPoints pushBack _targetPos; };
  5444.  
  5445. {
  5446. private _wp = _group addWaypoint [_x, 10];
  5447. _wp setWaypointType "SAD";
  5448. _wp setWaypointCompletionRadius 100;
  5449. _wp setWaypointBehaviour "AWARE";
  5450. _wp setWaypointCombatMode "RED";
  5451. } forEach _patrolPoints;
  5452.  
  5453. if (count (waypoints _group) > 1) then {
  5454. (_group addWaypoint [waypointPosition [_group, 0], 0]) setWaypointType "CYCLE";
  5455. };
  5456. _group setBehaviour "COMBAT";
  5457. };
  5458.  
  5459. // Get side state
  5460. HC_fnc_getSideState = {
  5461. params ["_side"];
  5462. private _availableGroups = [];
  5463. private _busyGroups = [];
  5464.  
  5465. {
  5466. if (side _x == _side && count (units _x) > 0 && {isPlayer _x} count (units _x) == 0) then {
  5467. private _group = _x;
  5468. if ([_group] call fnc_isGroupLocked) then {
  5469. _busyGroups pushBack _group;
  5470. } else {
  5471. _group setVariable ["HC_ASSIGNMENT_TIME", nil];
  5472. _group setVariable ["HC_TARGET", nil];
  5473. _group setVariable ["HC_ORDER", nil];
  5474. HC_Active_Groups deleteAt (groupId _group);
  5475. _availableGroups pushBack _group;
  5476. };
  5477. };
  5478. } forEach allGroups;
  5479.  
  5480. private _state = createHashMap;
  5481. _state set ["groups", _availableGroups];
  5482. _state set ["busyGroups", _busyGroups];
  5483. _state
  5484. };
  5485.  
  5486. fnc_getBattleHotspot = {
  5487. params ["_side"];
  5488. private _enemySide = if (_side == west) then {east} else {west};
  5489. private _enemyBase = if (_side == west) then {getPos opforSpawnObj} else {getPos bluforSpawnObj};
  5490. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  5491.  
  5492. // Find the midpoint between bases as default
  5493. private _defaultPos = [
  5494. ((_enemyBase select 0) + (_ownBase select 0)) / 2,
  5495. ((_enemyBase select 1) + (_ownBase select 1)) / 2,
  5496. 0
  5497. ];
  5498.  
  5499. // Try to find actual combat areas
  5500. private _combatPositions = [];
  5501. {
  5502. if (side _x == _side && behaviour leader _x == "COMBAT") then {
  5503. _combatPositions pushBack (getPos leader _x);
  5504. };
  5505. } forEach allGroups;
  5506.  
  5507. if (count _combatPositions > 0) exitWith {
  5508. selectRandom _combatPositions
  5509. };
  5510.  
  5511. _defaultPos
  5512. };
  5513.  
  5514. // Main commander decision-making
  5515. fnc_commanderMakeDecision = {
  5516. params ["_side"];
  5517. if (isNil "bluforSpawnObj" || isNil "opforSpawnObj") exitWith {};
  5518.  
  5519. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  5520. private _enemyBase = if (_side == west) then {getPos opforSpawnObj} else {getPos bluforSpawnObj};
  5521. private _enemySide = if (_side == west) then {east} else {west};
  5522.  
  5523. private _friendlyState = [_side] call HC_fnc_getSideState;
  5524. private _availableGroups = _friendlyState get "groups";
  5525. if (count _availableGroups == 0) exitWith {};
  5526.  
  5527. // Emergency base defense takes priority over strategy system
  5528. if ([_side, 600] call fnc_enemiesNearBase) then {
  5529. _availableGroups = [_availableGroups, [], {leader _x distance _ownBase}, "ASCEND"] call BIS_fnc_sortBy;
  5530. private _groupsToDefendBase = _availableGroups select [0, floor(count _availableGroups / 3) max 1];
  5531. { [_x, _ownBase, "DEFEND"] call fnc_assignGroupObjective; } forEach _groupsToDefendBase;
  5532. _availableGroups = _availableGroups - _groupsToDefendBase;
  5533. };
  5534.  
  5535. if (count _availableGroups == 0) exitWith {};
  5536.  
  5537. // Use strategy system if enabled
  5538. if (STRATEGY_ENABLED) then {
  5539. private _strategicRecommendation = [_side, _availableGroups] call fnc_getStrategicRecommendation;
  5540.  
  5541. if (count _strategicRecommendation > 0) then {
  5542. private _objective = _strategicRecommendation get "objective";
  5543. private _recommendedGroupCount = _strategicRecommendation get "groupCount";
  5544. private _prediction = _strategicRecommendation get "prediction";
  5545. private _score = _strategicRecommendation get "score";
  5546.  
  5547. private _expectedCasualties = _prediction get "expectedCasualties";
  5548.  
  5549. // Only execute if score is good and casualties are acceptable
  5550. if (_score >= STRATEGY_MIN_SCORE && _expectedCasualties <= STRATEGY_CASUALTY_TOLERANCE) then {
  5551. private _objectivePos = _objective get "position";
  5552. private _objectiveType = _objective get "type";
  5553.  
  5554. // Sort groups by distance to objective
  5555. private _sortedGroups = [_availableGroups, [], {(leader _x) distance _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  5556.  
  5557. // Select recommended number of groups
  5558. private _groupsToAssign = _sortedGroups select [0, _recommendedGroupCount min count _sortedGroups];
  5559.  
  5560. // Create battle plan for these groups
  5561. private _battlePlan = [_side, _groupsToAssign, _objectivePos, _ownBase] call fnc_createBattlePlan;
  5562. private _battleCenter = _battlePlan get "battleCenter";
  5563.  
  5564. // Execute battle plan roles
  5565. private _attackers = _battlePlan get "attackers";
  5566. private _defenders = _battlePlan get "defenders";
  5567. private _leftFlank = _battlePlan get "leftFlank";
  5568. private _rightFlank = _battlePlan get "rightFlank";
  5569. private _reserve = _battlePlan get "reserve";
  5570. private _support = _battlePlan get "support";
  5571.  
  5572. // ==================== CHANGE START ====================
  5573. // Use the new spacing function to assign roles
  5574. [_attackers, "ATTACK", _battleCenter, _ownBase, _objectivePos] call fnc_HC_assignGroupWithSpacing;
  5575. [_defenders, "DEFEND", _battleCenter, _ownBase, _objectivePos] call fnc_HC_assignGroupWithSpacing;
  5576. [_leftFlank, "FLANK_LEFT", _battleCenter, _ownBase, _objectivePos] call fnc_HC_assignGroupWithSpacing;
  5577. [_rightFlank, "FLANK_RIGHT", _battleCenter, _ownBase, _objectivePos] call fnc_HC_assignGroupWithSpacing;
  5578. [_reserve, "RESERVE", _battleCenter, _ownBase, _objectivePos] call fnc_HC_assignGroupWithSpacing;
  5579. [_support, "SUPPORT", _battleCenter, _ownBase, _objectivePos] call fnc_HC_assignGroupWithSpacing;
  5580. // ===================== CHANGE END =====================
  5581.  
  5582. // Update strategy tracking
  5583. if (_side == west) then {
  5584. BLUFOR_STRATEGY = _objectiveType;
  5585. } else {
  5586. OPFOR_STRATEGY = _objectiveType;
  5587. };
  5588.  
  5589. // Exit - strategy system handled decision
  5590. } else {
  5591. // Strategy says it's not worth it - use fallback defensive behavior
  5592. private _battleCenter = [_side] call fnc_getBattleHotspot;
  5593. {
  5594. [_x, _battleCenter, "PATROL"] call fnc_assignGroupObjective;
  5595. } forEach _availableGroups;
  5596. };
  5597. } else {
  5598. // No strategic recommendation - use old system
  5599. private _battlePlan = [_side, _availableGroups, _enemyBase, _ownBase] call fnc_createBattlePlan;
  5600. private _battleCenter = _battlePlan get "battleCenter";
  5601.  
  5602. private _attackers = _battlePlan get "attackers";
  5603. private _defenders = _battlePlan get "defenders";
  5604. private _leftFlank = _battlePlan get "leftFlank";
  5605. private _rightFlank = _battlePlan get "rightFlank";
  5606. private _reserve = _battlePlan get "reserve";
  5607. private _support = _battlePlan get "support";
  5608.  
  5609. // ==================== CHANGE START ====================
  5610. // Use the new spacing function to assign roles
  5611. [_attackers, "ATTACK", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5612. [_defenders, "DEFEND", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5613. [_leftFlank, "FLANK_LEFT", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5614. [_rightFlank, "FLANK_RIGHT", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5615. [_reserve, "RESERVE", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5616. [_support, "SUPPORT", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5617. // ===================== CHANGE END =====================
  5618.  
  5619. if (_side == west) then {
  5620. BLUFOR_STRATEGY = "RTS_COMBINED_ARMS";
  5621. } else {
  5622. OPFOR_STRATEGY = "RTS_COMBINED_ARMS";
  5623. };
  5624. };
  5625. } else {
  5626. // Strategy system disabled - use original behavior
  5627. private _battlePlan = [_side, _availableGroups, _enemyBase, _ownBase] call fnc_createBattlePlan;
  5628. private _battleCenter = _battlePlan get "battleCenter";
  5629.  
  5630. private _attackers = _battlePlan get "attackers";
  5631. private _defenders = _battlePlan get "defenders";
  5632. private _leftFlank = _battlePlan get "leftFlank";
  5633. private _rightFlank = _battlePlan get "rightFlank";
  5634. private _reserve = _battlePlan get "reserve";
  5635. private _support = _battlePlan get "support";
  5636.  
  5637. // ==================== CHANGE START ====================
  5638. // Use the new spacing function to assign roles
  5639. [_attackers, "ATTACK", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5640. [_defenders, "DEFEND", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5641. [_leftFlank, "FLANK_LEFT", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5642. [_rightFlank, "FLANK_RIGHT", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5643. [_reserve, "RESERVE", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5644. [_support, "SUPPORT", _battleCenter, _ownBase, _enemyBase] call fnc_HC_assignGroupWithSpacing;
  5645. // ===================== CHANGE END =====================
  5646.  
  5647. if (_side == west) then {
  5648. BLUFOR_STRATEGY = "RTS_COMBINED_ARMS";
  5649. } else {
  5650. OPFOR_STRATEGY = "RTS_COMBINED_ARMS";
  5651. };
  5652. };
  5653. };
  5654.  
  5655. // Create attack helicopter waypoints with improved combat patterns
  5656. fnc_createHelicopterAttackWaypoints = {
  5657. params ["_group", "_targetArea", "_centerPos"];
  5658.  
  5659. // Clear existing waypoints
  5660. while {count (waypoints _group) > 0} do {
  5661. deleteWaypoint ((waypoints _group) select 0);
  5662. };
  5663.  
  5664. private _heli = vehicle (leader _group);
  5665. if (isNull _heli || {!(_heli isKindOf "Air")}) exitWith {};
  5666.  
  5667. // Set optimal flight height for attack helicopters
  5668. _heli flyInHeight 150;
  5669.  
  5670. // Create attack run pattern
  5671. private _attackDistance = 2000; // Stand-off distance for initial approach
  5672. private _orbitRadius = 1500; // Orbit radius around target area
  5673.  
  5674. // Waypoint 1: Move to attack position (approach from distance)
  5675. private _approachAngle = random 360;
  5676. private _approachPos = _targetArea getPos [_attackDistance, _approachAngle];
  5677. private _wp1 = _group addWaypoint [_approachPos, 50];
  5678. _wp1 setWaypointType "MOVE";
  5679. _wp1 setWaypointBehaviour "AWARE";
  5680. _wp1 setWaypointSpeed "NORMAL";
  5681. _wp1 setWaypointStatements ["true", "(vehicle this) flyInHeight 100;"];
  5682.  
  5683. // Waypoint 2: SAD at target area
  5684. private _wp2 = _group addWaypoint [_targetArea, 200];
  5685. _wp2 setWaypointType "SAD";
  5686. _wp2 setWaypointBehaviour "COMBAT";
  5687. _wp2 setWaypointSpeed "LIMITED";
  5688. _wp2 setWaypointTimeout [30, 45, 60];
  5689. _wp2 setWaypointStatements ["true", "(vehicle this) flyInHeight 80;"];
  5690.  
  5691. // Waypoint 3-5: Orbit pattern around target
  5692. for "_i" from 0 to 2 do {
  5693. private _orbitAngle = _approachAngle + (120 * (_i + 1));
  5694. private _orbitPos = _targetArea getPos [_orbitRadius, _orbitAngle];
  5695. private _wpOrbit = _group addWaypoint [_orbitPos, 100];
  5696. _wpOrbit setWaypointType "SAD";
  5697. _wpOrbit setWaypointBehaviour "COMBAT";
  5698. _wpOrbit setWaypointSpeed "NORMAL";
  5699. _wpOrbit setWaypointTimeout [10, 15, 20];
  5700. };
  5701.  
  5702. // Waypoint 6: Return to patrol the center area
  5703. private _wpCenter = _group addWaypoint [_centerPos, 500];
  5704. _wpCenter setWaypointType "SAD";
  5705. _wpCenter setWaypointBehaviour "AWARE";
  5706. _wpCenter setWaypointSpeed "NORMAL";
  5707. _wpCenter setWaypointStatements ["true", "(vehicle this) flyInHeight 150;"];
  5708.  
  5709. // Cycle back to waypoint 1
  5710. private _wpCycle = _group addWaypoint [_approachPos, 0];
  5711. _wpCycle setWaypointType "CYCLE";
  5712.  
  5713. // Force the group to start moving
  5714. _group setCurrentWaypoint [_group, 1];
  5715. };
  5716.  
  5717. // Server-side loops
  5718. if (isServer) then {
  5719. // Main decision-making loop
  5720. [] spawn {
  5721. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  5722. sleep 10;
  5723.  
  5724. while {true} do {
  5725. // Adaptive decision interval based on performance
  5726. private _baseInterval = HC_DECISION_INTERVAL;
  5727. private _adaptiveInterval = [_baseInterval] call fnc_getAdaptiveSleep;
  5728.  
  5729. if (time - BLUFOR_LAST_DECISION > _adaptiveInterval) then {
  5730. [west] call fnc_commanderMakeDecision;
  5731. BLUFOR_LAST_DECISION = time;
  5732. };
  5733.  
  5734. sleep 3;
  5735.  
  5736. if (time - OPFOR_LAST_DECISION > _adaptiveInterval) then {
  5737. [east] call fnc_commanderMakeDecision;
  5738. OPFOR_LAST_DECISION = time;
  5739. };
  5740.  
  5741. sleep (_adaptiveInterval - 3);
  5742. };
  5743. };
  5744.  
  5745. // Check active groups for objective completion
  5746. [] spawn {
  5747. while {true} do {
  5748. sleep 10;
  5749. private _activeGroupIds = keys HC_Active_Groups;
  5750. {
  5751. private _group = HC_Active_Groups getOrDefault [_x, grpNull];
  5752. if (isNull _group || count (units _group) == 0) then {
  5753. HC_Active_Groups deleteAt _x;
  5754. } else {
  5755. private _targetPos = _group getVariable ["HC_TARGET", [0,0,0]];
  5756. if !(_targetPos isEqualTo [0,0,0]) then {
  5757. private _leader = leader _group;
  5758. if (!isNull _leader && alive _leader) then {
  5759. if ((_leader distance _targetPos) < HC_SUCCESS_UNLOCK_RADIUS) then {
  5760. _group setVariable ["HC_ASSIGNMENT_TIME", 0];
  5761. };
  5762. };
  5763. };
  5764. };
  5765. } forEach _activeGroupIds;
  5766. };
  5767. };
  5768.  
  5769. // Intel processing and sharing
  5770. [] spawn {
  5771. waitUntil { time > 5 };
  5772. while {true} do {
  5773. [west] call fnc_processIntelligence;
  5774. [east] call fnc_processIntelligence;
  5775. publicVariable "BLUFOR_INTEL";
  5776. publicVariable "OPFOR_INTEL";
  5777. sleep 5;
  5778. };
  5779. };
  5780.  
  5781. // Support evaluation and calling loop
  5782. [] spawn {
  5783. waitUntil { time > 5 };
  5784.  
  5785. // Function to handle support calls for a side
  5786. private _fnc_handleSideSupport = {
  5787. params ["_side"];
  5788.  
  5789. private _support = [_side] call fnc_evaluateSupportNeed;
  5790. if (_support == "") exitWith {};
  5791.  
  5792. private _recentReports = if (_side == west) then {BLUFOR_TACTICAL_REPORTS} else {OPFOR_TACTICAL_REPORTS};
  5793. private _enemySide = if (_side == west) then {east} else {west};
  5794. private _target = [0,0,0];
  5795.  
  5796. switch (_support) do {
  5797. case "ARTILLERY": {
  5798. private _enemyPositions = [];
  5799. { _enemyPositions pushBack (_x select 2); } forEach _recentReports;
  5800.  
  5801. private _bestTarget = [0,0,0];
  5802. private _bestScore = 0;
  5803. private _enemyBaseMarker = if (_side == west) then {"opfor_base_area"} else {"blufor_base_area"};
  5804. private _enemyBasePos = markerPos _enemyBaseMarker;
  5805. private _enemyBaseSize = (markerSize _enemyBaseMarker) select 0;
  5806.  
  5807. {
  5808. private _pos = _x;
  5809. if (_pos distance2D _enemyBasePos >= _enemyBaseSize) then {
  5810. private _nearbyFriendlies = _pos nearEntities [["CAManBase", "LandVehicle"], 30];
  5811. private _friendliesInArea = {side _x == _side && alive _x} count _nearbyFriendlies;
  5812.  
  5813. if (_friendliesInArea == 0) then {
  5814. private _clusterScore = 1 + ({_x distance _pos < 100 && !(_x isEqualTo _pos)} count _enemyPositions);
  5815. if (_clusterScore > _bestScore) then {
  5816. _bestScore = _clusterScore;
  5817. _bestTarget = _pos;
  5818. };
  5819. };
  5820. };
  5821. } forEach _enemyPositions;
  5822.  
  5823. _target = _bestTarget;
  5824. };
  5825.  
  5826. case "SMOKE": {
  5827. private _casualtyAreas = [];
  5828. private _friendlyGroups = allGroups select {side _x == _side && count (units _x) > 0};
  5829. {
  5830. private _group = _x;
  5831. private _casualties = {damage _x > 0.3} count (units _group);
  5832. if (_casualties >= 2 && behaviour leader _group == "COMBAT") then {
  5833. _casualtyAreas pushBack (getPos leader _group);
  5834. };
  5835. } forEach _friendlyGroups;
  5836. if (count _casualtyAreas > 0) then {
  5837. _target = selectRandom _casualtyAreas;
  5838. };
  5839. };
  5840.  
  5841. default {
  5842. private _recentCombatAreas = [];
  5843. { _recentCombatAreas pushBackUnique (_x select 2); } forEach _recentReports;
  5844. _target = if (count _recentCombatAreas > 0) then {selectRandom _recentCombatAreas} else {[_side] call fnc_getBattleHotspot};
  5845. };
  5846. };
  5847.  
  5848. if !(_target isEqualTo [0,0,0]) then {
  5849. switch (_support) do {
  5850. case "FLARE": { [_side, _target] call fnc_callFlareSupport; };
  5851. case "SMOKE": { [_side, _target] call fnc_callSmokeSupport; };
  5852. case "DRONE": { [_side, _target] call fnc_callReconDroneSupport; };
  5853. case "ARTILLERY": { [_side, _target] call fnc_callArtillerySupport; };
  5854. };
  5855. };
  5856. };
  5857.  
  5858. while {true} do {
  5859. [west] call _fnc_handleSideSupport;
  5860. sleep 3;
  5861. [east] call _fnc_handleSideSupport;
  5862. sleep 30;
  5863. };
  5864. };
  5865.  
  5866. // Battlefield situation monitor - reassigns groups based on changing conditions
  5867. [] spawn {
  5868. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  5869. sleep 30;
  5870.  
  5871. while {true} do {
  5872. // Check both sides for groups that need reassignment
  5873. {
  5874. private _side = _x;
  5875. private _allGroups = [_side] call fnc_getStrategicGroups;
  5876.  
  5877. {
  5878. private _group = _x;
  5879. private _assignTime = _group getVariable ["HC_ASSIGNMENT_TIME", 0];
  5880. private _currentRole = _group getVariable ["HC_ROLE", ""];
  5881. private _target = _group getVariable ["HC_TARGET", [0,0,0]];
  5882.  
  5883. // Validate target is an array before using distance
  5884. if (typeName _target == "ARRAY" && count _target >= 2) then {
  5885. // Check if group has been on same task for too long without progress
  5886. if (time - _assignTime > 180 && !([_group] call fnc_isGroupLocked)) then {
  5887. private _leader = leader _group;
  5888. if (!isNull _leader && alive _leader) then {
  5889. // If group hasn't moved much toward target, reassign
  5890. if (!(_target isEqualTo [0,0,0]) && ((_leader distance _target) > 300)) then {
  5891. // Clear old assignment
  5892. _group setVariable ["HC_ASSIGNMENT_TIME", nil];
  5893. _group setVariable ["HC_TARGET", nil];
  5894. _group setVariable ["HC_ORDER", nil];
  5895. _group setVariable ["HC_ROLE", nil];
  5896. HC_Active_Groups deleteAt (groupId _group);
  5897. };
  5898. };
  5899. };
  5900. };
  5901.  
  5902. // Reassign defenders if base is no longer under threat
  5903. if (_currentRole == "DEFENDER" && !([_side, 800] call fnc_enemiesNearBase)) then {
  5904. if (time - _assignTime > 120) then {
  5905. _group setVariable ["HC_ASSIGNMENT_TIME", nil];
  5906. _group setVariable ["HC_TARGET", nil];
  5907. _group setVariable ["HC_ORDER", nil];
  5908. _group setVariable ["HC_ROLE", nil];
  5909. HC_Active_Groups deleteAt (groupId _group);
  5910. };
  5911. };
  5912.  
  5913. } forEach _allGroups;
  5914. } forEach [west, east];
  5915.  
  5916. sleep 45;
  5917. };
  5918. };
  5919. };
  5920.  
  5921. ==================== END OF: highcommand.sqf ====================
  5922.  
  5923.  
  5924.  
  5925. ==================== START OF: init.sqf ====================
  5926.  
  5927. // init.sqf
  5928. // Mission initialization script for Arma 3
  5929.  
  5930. // ==================== CHANGE START ====================
  5931. // Explicitly set date and time multiplier on the server to ensure consistency
  5932. // This overrides mission.sqm settings and prevents unpredictable time acceleration.
  5933. if (isServer) then {
  5934. setDate [2035, 6, 24, 8, 0]; // Set start time to 08:00
  5935. setTimeMultiplier 1; // Ensure default timescale is realtime (1x)
  5936.  
  5937. // Randomize overcast and set rain based on it.
  5938. private _overcast = random 1; // Generate a random overcast value from 0 to 1.
  5939. 0 setOvercast _overcast; // Apply the new overcast value.
  5940.  
  5941. private _rain = 0; // Initialize _rain to 0 (no rain).
  5942.  
  5943. // If overcast is greater than 70%, enable rain.
  5944. if (_overcast > 0.7) then {
  5945. // Rain intensity is a random value up to the current overcast level.
  5946. _rain = random _overcast;
  5947. };
  5948.  
  5949. 0 setRain _rain;
  5950. forceWeatherChange;
  5951. };
  5952. // ===================== CHANGE END =====================
  5953.  
  5954. // Global Mission Configuration Variables
  5955. BLUFOR_POINTS = 1000;
  5956. OPFOR_POINTS = 1000;
  5957. BLUFOR_STRENGTH = 0;
  5958. OPFOR_STRENGTH = 0;
  5959.  
  5960. // Base Spawning Configuration
  5961. RANDOMIZE_BASE_LOCATIONS = 1; // 1 to randomize base locations, 0 for fixed North/South
  5962. missionNamespace setVariable ["RANDOMIZE_BASE_LOCATIONS", RANDOMIZE_BASE_LOCATIONS];
  5963. COAST_EXCLUSION_RADIUS = 100; // Minimum distance from coast for base spawns
  5964. PATROL_RADIUS = 300; // Used for general AI patrols (also relevant for HC)
  5965.  
  5966. // AI Spawning & Waves
  5967. maxAI = 170;
  5968. maxLightVehiclesPerSide = 2; // Max light vehicles (APCs, MRAPs) per side
  5969. maxTanksPerSide = 1; // Max tanks per side
  5970. maxAttackHelisPerSide = 1; // Max attack helicopters per side
  5971. waveDelay = 3; // Base delay in seconds between AI waves
  5972. BLUFOR_LAST_WAVES = []; // History of last spawned BLUFOR wave types
  5973. OPFOR_LAST_WAVES = []; // History of last spawned OPFOR wave types
  5974. WAVE_HISTORY_SIZE = 100; // How many past wave types to remember
  5975.  
  5976. // AI Unit Values (points gained per kill, or points cost per spawn)
  5977. INFANTRY_VALUE = 1;
  5978. SPECOPS_VALUE = 2;
  5979. UPGRADED_VALUE = 2;
  5980. SNIPER_VALUE = 2;
  5981. ELITE_VALUE = 3;
  5982. VEHICLE_VALUE = 20; // Cost for spawning AI light vehicles
  5983. TANK_VALUE = 40; // Cost for spawning AI tanks
  5984. ATTACK_HELI_VALUE = 30; // Cost for spawning AI attack helicopters
  5985.  
  5986. // AI Wave Composition Weights (used by fnc_getRandomWaveType)
  5987. WAVE_WEIGHTS = [
  5988. ["infantry", 50],
  5989. ["upgraded", 30],
  5990. ["specops", 20],
  5991. ["sniper", 15],
  5992. ["elite", 10],
  5993. ["light_vehicle", 25],
  5994. ["tank", 10],
  5995. ["attack_heli", 10]
  5996. ];
  5997.  
  5998. // AAF (Syndicate/Looter) Configuration
  5999. AAF_START_SIZE = round(maxAI / 5); // Initial AAF garrison size at mission start
  6000.  
  6001. // Strategy System Configuration
  6002. STRATEGY_ENABLED = true; // Enable strategic thinking system
  6003. STRATEGY_MIN_SCORE = 10; // Minimum score to execute an objective
  6004. STRATEGY_CASUALTY_TOLERANCE = 0.4; // Maximum acceptable casualty rate (40%)
  6005.  
  6006. // AI Behavior (botai.sqf)
  6007. BOTAI_SUPPRESSION_THRESHOLD = 50; // Distance threshold for AI to react to nearby fire
  6008. BOTAI_REACTION_COOLDOWN = 5; // Seconds between AI tactical decisions
  6009. BOTAI_CRITICAL_DAMAGE = 0.6; // Damage level for defensive actions
  6010. BOTAI_HEAVY_SUPPRESSION = 5; // Suppression threshold
  6011. BOTAI_SUPPRESSION_DECAY = 0.95; // Suppression decay per second
  6012. BOTAI_GRENADE_RANGE_MAX = 50; // Max grenade throw distance
  6013. BOTAI_GRENADE_RANGE_MIN = 15; // Min grenade throw distance
  6014.  
  6015. // Cleanup System Configuration
  6016. CLEANUP_BODY_LIMIT = 20;
  6017. CLEANUP_DELAY = 30; // Check every 30 seconds
  6018. CLEANUP_BODY_TIMER = 300; // Delete bodies older than 5 minutes
  6019. CLEANUP_WEAPON_TIMER = 300; // Delete dropped weapons after 5 minutes
  6020. CLEANUP_VEHICLE_TIMER = 120; // Delete destroyed vehicles after 2 minutes
  6021. CLEANUP_MIN_DISTANCE = 300; // Min distance from players for cleanup
  6022.  
  6023. // High Command Configuration
  6024. HC_DECISION_INTERVAL = 90; // Strategic decision interval (seconds)
  6025. HC_THREAT_EVAL_RADIUS = 4000; // Radius for threat evaluation
  6026. HC_MIN_ATTACK_FORCE = 10; // Min force strength for major attack
  6027. HC_AGGRESSIVE_THRESHOLD = 0.8; // Force ratio for aggressive stance
  6028. HC_RETREAT_THRESHOLD = 0.2; // Force ratio for retreat
  6029. HC_REGROUP_THRESHOLD = 0.7; // Force ratio for regrouping
  6030. HC_INTEL_SHARE_RADIUS = 10000; // Intel sharing radius
  6031. HC_INTEL_DECAY_TIME = 180; // Intel validity duration (seconds)
  6032. HC_SEARCH_GRID_SIZE = 200; // Grid cell size for search operations
  6033.  
  6034. // Enhanced High Command Configuration
  6035. HC_COORDINATED_ATTACK_RADIUS = 600; // Coordination distance for assaults
  6036. HC_SUPPORT_REQUEST_COOLDOWN = 240; // Cooldown for support requests
  6037. HC_SUPPORT_MIN_ENEMY_BASE_DISTANCE = 600; // NEW: Minimum distance from enemy base to call support
  6038. HC_MORALE_BASE = 1.0; // Base morale multiplier
  6039. HC_SUPPLY_CHECK_INTERVAL = 60; // Supply check interval
  6040. HC_REINFORCEMENT_THRESHOLD = 0.8; // Reinforcement request threshold
  6041. HC_FLARE_COOLDOWN = 600; // 10 minute cooldown for flare support
  6042. HC_SMOKE_COOLDOWN = 900; // 15 minute cooldown for smoke support
  6043. HC_SMOKE_ROUNDS = 3; // Number of smoke shells
  6044. HC_DRONE_COOLDOWN = 900; // 15 minute cooldown for recon drone
  6045. HC_ARTILLERY_COOLDOWN = 1800; // 30 minute cooldown for artillery support
  6046. HC_ARTILLERY_ROUNDS = 5; // Number of artillery shells
  6047.  
  6048. // Group Lock Configuration (HC)
  6049. HC_LOCK_DURATION = 300; // Task persistence duration
  6050. HC_ENGAGEMENT_LOCK_RADIUS = 150; // Combat lock distance
  6051. HC_SUCCESS_UNLOCK_RADIUS = 100; // Success unlock distance
  6052. HC_COMBAT_LOCK_TIME = 180; // Combat lock duration
  6053.  
  6054. // Commander Personality Types (HC)
  6055. HC_COMMANDER_PERSONALITIES = ["AGGRESSIVE", "DEFENSIVE", "BALANCED", "CUNNING"];
  6056.  
  6057. // Player Purchase System Configuration (player_server.sqf)
  6058. PURCHASE_VEHICLE_DATA = createHashMapFromArray [
  6059. // Utility (Cost: 1)
  6060. ["B_Quadbike_01_F", ["Quad Bike", 1, west]],
  6061. ["O_Quadbike_01_F", ["Quad Bike", 1, east]],
  6062. // Light Vehicles (Cost: 10)
  6063. ["B_MRAP_01_hmg_F", ["Hunter HMG", 10, west]],
  6064. ["B_Heli_Light_01_F", ["Pawnee", 10, west]],
  6065. ["O_MRAP_02_hmg_F", ["Ifrit HMG", 10, east]],
  6066. ["O_Heli_Light_02_unarmed_F", ["Orca", 10, east]],
  6067. // Heavy Vehicles (Cost: 20)
  6068. ["B_APC_Wheeled_01_cannon_F", ["Marshall", 20, west]],
  6069. ["O_APC_Wheeled_02_rcws_v2_F", ["Marid", 20, east]],
  6070. // Tanks (Cost: 30)
  6071. ["B_MBT_01_cannon_F", ["Slammer", 30, west]],
  6072. ["O_MBT_02_cannon_F", ["Varsuk", 30, east]]
  6073. ];
  6074.  
  6075. // ==================== CHANGE START ====================
  6076. PLAYER_SQUAD_COST = 15;
  6077. PLAYER_SQUAD_COOLDOWN = 120; // 2 minutes
  6078. // ===================== CHANGE END =====================
  6079.  
  6080. // Compile and preprocess mission scripts
  6081. [] call compile preprocessFileLineNumbers "highcommand.sqf";
  6082. [] call compile preprocessFileLineNumbers "strategy.sqf";
  6083. [] call compile preprocessFileLineNumbers "botai.sqf";
  6084. [] call compile preprocessFileLineNumbers "performance.sqf";
  6085. [] call compile preprocessFileLineNumbers "AIstuff.sqf";
  6086. [] call compile preprocessFileLineNumbers "AAF.sqf";
  6087. [] call compile preprocessFileLineNumbers "cleanup.sqf";
  6088. [] call compile preprocessFileLineNumbers "arsenal.sqf";
  6089. if (isServer) then {
  6090. [] call compile preprocessFileLineNumbers "player_server.sqf";
  6091. };
  6092. if (hasInterface) then {
  6093. [] call compile preprocessFileLineNumbers "playerinit.sqf";
  6094. };
  6095.  
  6096. // Main mission setup
  6097. [] call compile preprocessFileLineNumbers "mission_setup.sqf";
  6098.  
  6099. fnc_disablePlayerStaminaAndSway = {
  6100. if (hasInterface) then {
  6101. waitUntil {!isNull player};
  6102. player enableStamina false;
  6103. player enableFatigue false;
  6104. player setCustomAimCoef 0;
  6105. while {true} do {
  6106. player setFatigue 0;
  6107. player setCustomAimCoef 0;
  6108. sleep 1;
  6109. };
  6110. };
  6111. };
  6112. [] call fnc_disablePlayerStaminaAndSway;
  6113.  
  6114. // Server-side loop to re-task lost groups and merge small groups
  6115. if (isServer) then {
  6116. [] spawn {
  6117. waitUntil {!isNil "mission_centralPoint" && !isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  6118.  
  6119. private _centerPos = missionNamespace getVariable ["mission_centralPoint", [worldSize/2, worldSize/2, 0]];
  6120. private _maxDistance = 5000;
  6121. private _maxHeliDistance = 8000;
  6122.  
  6123. while {true} do {
  6124. sleep 60;
  6125.  
  6126. // OPTIMIZATION: Pre-calculate base positions once per cycle
  6127. private _bluforBase = getPos bluforSpawnObj;
  6128. private _opforBase = getPos opforSpawnObj;
  6129.  
  6130. // OPTIMIZATION: Pre-filter groups more aggressively
  6131. private _aiGroups = allGroups select {
  6132. !isNull _x &&
  6133. count (units _x) > 0 &&
  6134. ({isPlayer _x} count (units _x) == 0) &&
  6135. (side _x in [west, east]) &&
  6136. {!isNull leader _x && alive leader _x} // Only process groups with valid leaders
  6137. };
  6138.  
  6139. // Check for small groups that need merging
  6140. {
  6141. [_x] call fnc_checkAndMergeSmallGroups;
  6142. } forEach _aiGroups;
  6143.  
  6144. // Re-filter after potential merging (some groups may have been deleted)
  6145. _aiGroups = allGroups select {
  6146. !isNull _x &&
  6147. count (units _x) > 0 &&
  6148. ({isPlayer _x} count (units _x) == 0) &&
  6149. (side _x in [west, east]) &&
  6150. {!isNull leader _x && alive leader _x}
  6151. };
  6152.  
  6153. {
  6154. private _group = _x;
  6155. private _leader = leader _group;
  6156. private _vehicle = vehicle _leader;
  6157. private _isVehicleGroup = (_vehicle != _leader);
  6158. private _side = side _group;
  6159. private _distanceFromCenter = _leader distance2D _centerPos;
  6160.  
  6161. // OPTIMIZATION: Pre-calculate positions based on side
  6162. private _enemyBasePos = if (_side == west) then {_opforBase} else {_bluforBase};
  6163. private _ownBasePos = if (_side == west) then {_bluforBase} else {_opforBase};
  6164.  
  6165. // Handle attack helicopters
  6166. if (_isVehicleGroup && {_vehicle isKindOf "Air"}) then {
  6167. // OPTIMIZATION: Only check waypoints if distance threshold exceeded
  6168. if (_distanceFromCenter > _maxHeliDistance) then {
  6169. private _wpPos = waypointPosition [_group, currentWaypoint _group];
  6170. private _hasValidWaypoints = (count (waypoints _group) > 0) && !(_wpPos isEqualTo [0,0,0]);
  6171.  
  6172. if (!_hasValidWaypoints) then {
  6173. [_group, _enemyBasePos, _centerPos] call fnc_createHelicopterAttackWaypoints;
  6174. };
  6175. };
  6176. } else {
  6177. // Ground unit logic
  6178. if (_distanceFromCenter > _maxDistance) then {
  6179. if (_isVehicleGroup) then {
  6180. [_group, _enemyBasePos, _ownBasePos] call fnc_createVehiclePatrolWaypoints;
  6181. } else {
  6182. [_group, _enemyBasePos] call fnc_createPatrolWaypoints;
  6183. };
  6184. };
  6185. };
  6186. } forEach _aiGroups;
  6187. };
  6188. };
  6189. };
  6190.  
  6191. ==================== END OF: init.sqf ====================
  6192.  
  6193.  
  6194.  
  6195. ==================== START OF: loadoutlimitations.sqf ====================
  6196.  
  6197. // Allowed gear lists for Virtual Arsenal
  6198.  
  6199. // BLUFOR allowed uniforms
  6200. BLUFOR_UNIFORMS = [
  6201. "U_B_CombatUniform_mcam",
  6202. "U_B_CombatUniform_mcam_tshirt",
  6203. "U_B_CombatUniform_mcam_vest",
  6204. "U_B_CombatUniform_mcam_worn",
  6205. "U_B_CTRG_1",
  6206. "U_B_CTRG_2",
  6207. "U_B_CTRG_3",
  6208. "U_B_CTRG_Soldier_F",
  6209. "U_B_CTRG_Soldier_2_F",
  6210. "U_B_CTRG_Soldier_3_F",
  6211. "U_B_CTRG_Soldier_urb_1_F",
  6212. "U_B_CTRG_Soldier_urb_2_F",
  6213. "U_B_CTRG_Soldier_urb_3_F",
  6214. "U_B_GhillieSuit",
  6215. "U_B_T_Soldier_F",
  6216. "U_B_T_Soldier_AR_F",
  6217. "U_B_T_Soldier_SL_F",
  6218. "U_B_T_Sniper_F",
  6219. "U_B_T_FullGhillie_tna_F",
  6220. "U_B_survival_uniform"
  6221. ];
  6222.  
  6223. // BLUFOR allowed vests
  6224. BLUFOR_VESTS = [
  6225. "V_PlateCarrier1_rgr",
  6226. "V_PlateCarrier2_rgr",
  6227. "V_PlateCarrierGL_rgr",
  6228. "V_PlateCarrierSpec_rgr",
  6229. "V_Chestrig_rgr",
  6230. "V_Chestrig_khk",
  6231. "V_Chestrig_oli",
  6232. "V_BandollierB_rgr",
  6233. "V_BandollierB_khk",
  6234. "V_BandollierB_oli"
  6235. ];
  6236.  
  6237. // BLUFOR allowed headgear
  6238. BLUFOR_HEADGEAR = [
  6239. "H_HelmetB",
  6240. "H_HelmetB_black",
  6241. "H_HelmetB_camo",
  6242. "H_HelmetB_desert",
  6243. "H_HelmetB_grass",
  6244. "H_HelmetB_sand",
  6245. "H_HelmetB_snakeskin",
  6246. "H_HelmetSpecB",
  6247. "H_HelmetSpecB_black",
  6248. "H_HelmetSpecB_paint2",
  6249. "H_HelmetSpecB_paint1",
  6250. "H_HelmetB_light",
  6251. "H_HelmetB_light_black",
  6252. "H_HelmetB_light_desert",
  6253. "H_HelmetB_light_grass",
  6254. "H_HelmetB_light_sand",
  6255. "H_HelmetB_light_snakeskin",
  6256. "H_Booniehat_mcam",
  6257. "H_Booniehat_khk",
  6258. "H_MilCap_mcam",
  6259. "H_Watchcap_camo",
  6260. "H_Bandanna_cbr",
  6261. "H_Bandanna_khk",
  6262. "H_Bandanna_sgg",
  6263. "H_Shemag_olive",
  6264. "H_ShemagOpen_tan",
  6265. "H_MilCap_mcamo" // ADDED: BLUFOR Officer Cap
  6266. ];
  6267.  
  6268. // OPFOR allowed uniforms
  6269. OPFOR_UNIFORMS = [
  6270. "U_O_CombatUniform_ocamo",
  6271. "U_O_CombatUniform_oucamo",
  6272. "U_O_OfficerUniform_ocamo",
  6273. "U_O_SpecopsUniform_ocamo",
  6274. "U_O_SpecopsUniform_blk",
  6275. "U_O_V_Soldier_Viper_F",
  6276. "U_O_V_Soldier_Viper_hex_F",
  6277. "U_O_GhillieSuit",
  6278. "U_O_T_Soldier_F",
  6279. "U_O_T_Officer_F",
  6280. "U_O_T_Sniper_F",
  6281. "U_O_T_FullGhillie_tna_F"
  6282. ];
  6283.  
  6284. // OPFOR allowed vests
  6285. OPFOR_VESTS = [
  6286. "V_TacVest_brn",
  6287. "V_TacVest_camo",
  6288. "V_TacVest_khk",
  6289. "V_TacVest_oli",
  6290. "V_HarnessO_brn",
  6291. "V_HarnessO_gry",
  6292. "V_HarnessOGL_brn",
  6293. "V_HarnessOGL_gry",
  6294. "V_BandollierB_cbr",
  6295. "V_BandollierB_khk",
  6296. "V_BandollierB_oli",
  6297. "V_BandollierB_rgr"
  6298. ];
  6299.  
  6300. // OPFOR allowed headgear
  6301. OPFOR_HEADGEAR = [
  6302. "H_HelmetO_ocamo",
  6303. "H_HelmetO_oucamo",
  6304. "H_HelmetLeaderO_ocamo",
  6305. "H_HelmetLeaderO_oucamo",
  6306. "H_HelmetSpecO_ocamo",
  6307. "H_HelmetSpecO_blk",
  6308. "H_Booniehat_ocamo",
  6309. "H_MilCap_ocamo",
  6310. "H_MilCap_oucamo",
  6311. "H_Bandanna_cbr",
  6312. "H_Bandanna_khk",
  6313. "H_Bandanna_sgg",
  6314. "H_Shemag_olive",
  6315. "H_ShemagOpen_tan",
  6316. "H_Beret_ocamo" // ADDED: OPFOR Officer Beret
  6317. ];
  6318.  
  6319. ==================== END OF: loadoutlimitations.sqf ====================
  6320.  
  6321.  
  6322.  
  6323. ==================== START OF: mission.sqm ====================
  6324.  
  6325. version=54;
  6326. class EditorData
  6327. {
  6328. moveGridStep=1;
  6329. angleGridStep=0.2617994;
  6330. scaleGridStep=1;
  6331. autoGroupingDist=10;
  6332. toggles=1;
  6333. class ItemIDProvider
  6334. {
  6335. nextID=728;
  6336. };
  6337. class Camera
  6338. {
  6339. pos[]={8469.1787,96.641701,25282.076};
  6340. dir[]={0.46490091,-0.29877436,-0.83342725};
  6341. up[]={0.14554858,0.95432377,-0.26092476};
  6342. aside[]={-0.87331706,0,-0.48715216};
  6343. };
  6344. };
  6345. binarizationWanted=0;
  6346. sourceName="altis-test";
  6347. addons[]=
  6348. {
  6349. "A3_Ui_F",
  6350. "A3_Structures_F_EPC_Civ_Camping",
  6351. "A3_Characters_F"
  6352. };
  6353. class AddonsMetaData
  6354. {
  6355. class List
  6356. {
  6357. items=3;
  6358. class Item0
  6359. {
  6360. className="A3_Ui_F";
  6361. name="Arma 3 - User Interface";
  6362. author="Bohemia Interactive";
  6363. url="https://www.arma3.com";
  6364. };
  6365. class Item1
  6366. {
  6367. className="A3_Structures_F_EPC";
  6368. name="Arma 3 Win Episode - Buildings and Structures";
  6369. author="Bohemia Interactive";
  6370. url="https://www.arma3.com";
  6371. };
  6372. class Item2
  6373. {
  6374. className="A3_Characters_F";
  6375. name="Arma 3 Alpha - Characters and Clothing";
  6376. author="Bohemia Interactive";
  6377. url="https://www.arma3.com";
  6378. };
  6379. };
  6380. };
  6381. randomSeed=14934283;
  6382. class ScenarioData
  6383. {
  6384. author="chinese communist death vans";
  6385. };
  6386. class Mission
  6387. {
  6388. class Intel
  6389. {
  6390. resistanceWest=0;
  6391. timeOfChanges=1800.0002;
  6392. startWeather=0.19925441;
  6393. startWind=0.1;
  6394. startWaves=0.1;
  6395. forecastWeather=0.25008479;
  6396. forecastWind=0.1;
  6397. forecastWaves=0.1;
  6398. forecastLightnings=0.1;
  6399. year=2035;
  6400. month=10;
  6401. day=16;
  6402. hour=11;
  6403. minute=13;
  6404. startFogDecay=0.014;
  6405. forecastFogDecay=0.014;
  6406. };
  6407. class Entities
  6408. {
  6409. items=59;
  6410. class Item0
  6411. {
  6412. dataType="Marker";
  6413. position[]={12283.546,42.890999,18734.664};
  6414. name="basemarker1";
  6415. type="Empty";
  6416. angle=354.22101;
  6417. id=201;
  6418. atlOffset=-0.00024414063;
  6419. };
  6420. class Item1
  6421. {
  6422. dataType="Object";
  6423. class PositionInfo
  6424. {
  6425. position[]={14475.708,32.810844,20277.342};
  6426. };
  6427. side="Empty";
  6428. class Attributes
  6429. {
  6430. name="custom_capture_21";
  6431. };
  6432. id=250;
  6433. type="Land_Sunshade_01_F";
  6434. atlOffset=0.33879089;
  6435. };
  6436. class Item2
  6437. {
  6438. dataType="Marker";
  6439. position[]={14259.634,76.668999,22040.857};
  6440. name="basemarker2";
  6441. type="Empty";
  6442. id=251;
  6443. atlOffset=-0.0001373291;
  6444. };
  6445. class Item3
  6446. {
  6447. dataType="Object";
  6448. class PositionInfo
  6449. {
  6450. position[]={13974.829,50.862137,20782.016};
  6451. angles[]={6.2551923,0,0.0026744273};
  6452. };
  6453. side="Empty";
  6454. flags=4;
  6455. class Attributes
  6456. {
  6457. name="custom_capture_22";
  6458. };
  6459. id=300;
  6460. type="Land_Sunshade_01_F";
  6461. };
  6462. class Item4
  6463. {
  6464. dataType="Object";
  6465. class PositionInfo
  6466. {
  6467. position[]={14653.132,48.692924,20776.793};
  6468. angles[]={6.1716504,0,6.2818484};
  6469. };
  6470. side="Empty";
  6471. flags=4;
  6472. class Attributes
  6473. {
  6474. name="custom_capture_23";
  6475. };
  6476. id=301;
  6477. type="Land_Sunshade_01_F";
  6478. };
  6479. class Item5
  6480. {
  6481. dataType="Object";
  6482. class PositionInfo
  6483. {
  6484. position[]={14190.832,78.276566,21228.215};
  6485. angles[]={6.211309,0,6.0313168};
  6486. };
  6487. side="Empty";
  6488. flags=4;
  6489. class Attributes
  6490. {
  6491. name="custom_capture_24";
  6492. };
  6493. id=302;
  6494. type="Land_Sunshade_01_F";
  6495. };
  6496. class Item6
  6497. {
  6498. dataType="Object";
  6499. class PositionInfo
  6500. {
  6501. position[]={13332.5,37.353886,20158.957};
  6502. angles[]={6.2618537,0,6.2645183};
  6503. };
  6504. side="Empty";
  6505. flags=4;
  6506. class Attributes
  6507. {
  6508. name="custom_capture_25";
  6509. };
  6510. id=303;
  6511. type="Land_Sunshade_01_F";
  6512. atlOffset=3.8146973e-06;
  6513. };
  6514. class Item7
  6515. {
  6516. dataType="Object";
  6517. class PositionInfo
  6518. {
  6519. position[]={12826.544,45.03352,19649.203};
  6520. angles[]={6.2325621,0,0.0093350215};
  6521. };
  6522. side="Empty";
  6523. flags=4;
  6524. class Attributes
  6525. {
  6526. name="custom_capture_27";
  6527. };
  6528. id=305;
  6529. type="Land_Sunshade_01_F";
  6530. atlOffset=3.8146973e-06;
  6531. };
  6532. class Item8
  6533. {
  6534. dataType="Object";
  6535. class PositionInfo
  6536. {
  6537. position[]={14033.105,28.411243,18715.609};
  6538. angles[]={0.027993103,0,0.014664836};
  6539. };
  6540. side="Empty";
  6541. flags=4;
  6542. class Attributes
  6543. {
  6544. name="custom_capture_28";
  6545. };
  6546. id=306;
  6547. type="Land_Sunshade_01_F";
  6548. atlOffset=3.8146973e-06;
  6549. };
  6550. class Item9
  6551. {
  6552. dataType="Group";
  6553. side="Civilian";
  6554. class Entities
  6555. {
  6556. items=1;
  6557. class Item0
  6558. {
  6559. dataType="Object";
  6560. class PositionInfo
  6561. {
  6562. position[]={8487.0635,88.450882,25255.645};
  6563. };
  6564. side="Civilian";
  6565. flags=7;
  6566. class Attributes
  6567. {
  6568. skill=1;
  6569. isPlayer=1;
  6570. };
  6571. id=412;
  6572. type="C_man_p_beggar_F";
  6573. atlOffset=-7.6293945e-06;
  6574. class CustomAttributes
  6575. {
  6576. class Attribute0
  6577. {
  6578. property="speaker";
  6579. expression="_this setspeaker _value;";
  6580. class Value
  6581. {
  6582. class data
  6583. {
  6584. singleType="STRING";
  6585. value="Male04GRE";
  6586. };
  6587. };
  6588. };
  6589. class Attribute1
  6590. {
  6591. property="pitch";
  6592. expression="_this setpitch _value;";
  6593. class Value
  6594. {
  6595. class data
  6596. {
  6597. singleType="SCALAR";
  6598. value=1.05;
  6599. };
  6600. };
  6601. };
  6602. nAttributes=2;
  6603. };
  6604. };
  6605. };
  6606. class Attributes
  6607. {
  6608. };
  6609. id=411;
  6610. atlOffset=-7.6293945e-06;
  6611. };
  6612. class Item10
  6613. {
  6614. dataType="Group";
  6615. side="Civilian";
  6616. class Entities
  6617. {
  6618. items=1;
  6619. class Item0
  6620. {
  6621. dataType="Object";
  6622. class PositionInfo
  6623. {
  6624. position[]={8493.6631,87.965904,25258.918};
  6625. };
  6626. side="Civilian";
  6627. flags=7;
  6628. class Attributes
  6629. {
  6630. skill=1;
  6631. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  6632. isPlayable=1;
  6633. };
  6634. id=416;
  6635. type="C_man_p_beggar_F";
  6636. atlOffset=-7.6293945e-06;
  6637. class CustomAttributes
  6638. {
  6639. class Attribute0
  6640. {
  6641. property="speaker";
  6642. expression="_this setspeaker _value;";
  6643. class Value
  6644. {
  6645. class data
  6646. {
  6647. singleType="STRING";
  6648. value="Male03GRE";
  6649. };
  6650. };
  6651. };
  6652. class Attribute1
  6653. {
  6654. property="pitch";
  6655. expression="_this setpitch _value;";
  6656. class Value
  6657. {
  6658. class data
  6659. {
  6660. singleType="SCALAR";
  6661. value=0.97000003;
  6662. };
  6663. };
  6664. };
  6665. nAttributes=2;
  6666. };
  6667. };
  6668. };
  6669. class Attributes
  6670. {
  6671. };
  6672. id=415;
  6673. atlOffset=-7.6293945e-06;
  6674. };
  6675. class Item11
  6676. {
  6677. dataType="Group";
  6678. side="Civilian";
  6679. class Entities
  6680. {
  6681. items=1;
  6682. class Item0
  6683. {
  6684. dataType="Object";
  6685. class PositionInfo
  6686. {
  6687. position[]={8493.4658,87.665306,25261.18};
  6688. };
  6689. side="Civilian";
  6690. flags=7;
  6691. class Attributes
  6692. {
  6693. skill=1;
  6694. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  6695. isPlayable=1;
  6696. };
  6697. id=531;
  6698. type="C_man_p_beggar_F";
  6699. atlOffset=-7.6293945e-06;
  6700. class CustomAttributes
  6701. {
  6702. class Attribute0
  6703. {
  6704. property="speaker";
  6705. expression="_this setspeaker _value;";
  6706. class Value
  6707. {
  6708. class data
  6709. {
  6710. singleType="STRING";
  6711. value="Male03GRE";
  6712. };
  6713. };
  6714. };
  6715. class Attribute1
  6716. {
  6717. property="pitch";
  6718. expression="_this setpitch _value;";
  6719. class Value
  6720. {
  6721. class data
  6722. {
  6723. singleType="SCALAR";
  6724. value=0.97000003;
  6725. };
  6726. };
  6727. };
  6728. nAttributes=2;
  6729. };
  6730. };
  6731. };
  6732. class Attributes
  6733. {
  6734. };
  6735. id=530;
  6736. atlOffset=-7.6293945e-06;
  6737. };
  6738. class Item12
  6739. {
  6740. dataType="Group";
  6741. side="Civilian";
  6742. class Entities
  6743. {
  6744. items=1;
  6745. class Item0
  6746. {
  6747. dataType="Object";
  6748. class PositionInfo
  6749. {
  6750. position[]={8491.8467,88.023788,25259.121};
  6751. };
  6752. side="Civilian";
  6753. flags=7;
  6754. class Attributes
  6755. {
  6756. skill=1;
  6757. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  6758. isPlayable=1;
  6759. };
  6760. id=533;
  6761. type="C_man_p_beggar_F";
  6762. atlOffset=-7.6293945e-06;
  6763. class CustomAttributes
  6764. {
  6765. class Attribute0
  6766. {
  6767. property="speaker";
  6768. expression="_this setspeaker _value;";
  6769. class Value
  6770. {
  6771. class data
  6772. {
  6773. singleType="STRING";
  6774. value="Male03GRE";
  6775. };
  6776. };
  6777. };
  6778. class Attribute1
  6779. {
  6780. property="pitch";
  6781. expression="_this setpitch _value;";
  6782. class Value
  6783. {
  6784. class data
  6785. {
  6786. singleType="SCALAR";
  6787. value=0.97000003;
  6788. };
  6789. };
  6790. };
  6791. nAttributes=2;
  6792. };
  6793. };
  6794. };
  6795. class Attributes
  6796. {
  6797. };
  6798. id=532;
  6799. atlOffset=-7.6293945e-06;
  6800. };
  6801. class Item13
  6802. {
  6803. dataType="Group";
  6804. side="Civilian";
  6805. class Entities
  6806. {
  6807. items=1;
  6808. class Item0
  6809. {
  6810. dataType="Object";
  6811. class PositionInfo
  6812. {
  6813. position[]={8491.5908,87.778473,25261.01};
  6814. };
  6815. side="Civilian";
  6816. flags=7;
  6817. class Attributes
  6818. {
  6819. skill=1;
  6820. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  6821. isPlayable=1;
  6822. };
  6823. id=535;
  6824. type="C_man_p_beggar_F";
  6825. atlOffset=-7.6293945e-06;
  6826. class CustomAttributes
  6827. {
  6828. class Attribute0
  6829. {
  6830. property="speaker";
  6831. expression="_this setspeaker _value;";
  6832. class Value
  6833. {
  6834. class data
  6835. {
  6836. singleType="STRING";
  6837. value="Male03GRE";
  6838. };
  6839. };
  6840. };
  6841. class Attribute1
  6842. {
  6843. property="pitch";
  6844. expression="_this setpitch _value;";
  6845. class Value
  6846. {
  6847. class data
  6848. {
  6849. singleType="SCALAR";
  6850. value=0.97000003;
  6851. };
  6852. };
  6853. };
  6854. nAttributes=2;
  6855. };
  6856. };
  6857. };
  6858. class Attributes
  6859. {
  6860. };
  6861. id=534;
  6862. atlOffset=-7.6293945e-06;
  6863. };
  6864. class Item14
  6865. {
  6866. dataType="Group";
  6867. side="Civilian";
  6868. class Entities
  6869. {
  6870. items=1;
  6871. class Item0
  6872. {
  6873. dataType="Object";
  6874. class PositionInfo
  6875. {
  6876. position[]={8492.9512,87.519699,25262.789};
  6877. };
  6878. side="Civilian";
  6879. flags=7;
  6880. class Attributes
  6881. {
  6882. skill=1;
  6883. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  6884. isPlayable=1;
  6885. };
  6886. id=537;
  6887. type="C_man_p_beggar_F";
  6888. atlOffset=-7.6293945e-06;
  6889. class CustomAttributes
  6890. {
  6891. class Attribute0
  6892. {
  6893. property="speaker";
  6894. expression="_this setspeaker _value;";
  6895. class Value
  6896. {
  6897. class data
  6898. {
  6899. singleType="STRING";
  6900. value="Male03GRE";
  6901. };
  6902. };
  6903. };
  6904. class Attribute1
  6905. {
  6906. property="pitch";
  6907. expression="_this setpitch _value;";
  6908. class Value
  6909. {
  6910. class data
  6911. {
  6912. singleType="SCALAR";
  6913. value=0.97000003;
  6914. };
  6915. };
  6916. };
  6917. nAttributes=2;
  6918. };
  6919. };
  6920. };
  6921. class Attributes
  6922. {
  6923. };
  6924. id=536;
  6925. atlOffset=-7.6293945e-06;
  6926. };
  6927. class Item15
  6928. {
  6929. dataType="Group";
  6930. side="Civilian";
  6931. class Entities
  6932. {
  6933. items=1;
  6934. class Item0
  6935. {
  6936. dataType="Object";
  6937. class PositionInfo
  6938. {
  6939. position[]={8491.293,87.591988,25262.898};
  6940. };
  6941. side="Civilian";
  6942. flags=7;
  6943. class Attributes
  6944. {
  6945. skill=1;
  6946. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  6947. isPlayable=1;
  6948. };
  6949. id=539;
  6950. type="C_man_p_beggar_F";
  6951. atlOffset=-7.6293945e-06;
  6952. class CustomAttributes
  6953. {
  6954. class Attribute0
  6955. {
  6956. property="speaker";
  6957. expression="_this setspeaker _value;";
  6958. class Value
  6959. {
  6960. class data
  6961. {
  6962. singleType="STRING";
  6963. value="Male03GRE";
  6964. };
  6965. };
  6966. };
  6967. class Attribute1
  6968. {
  6969. property="pitch";
  6970. expression="_this setpitch _value;";
  6971. class Value
  6972. {
  6973. class data
  6974. {
  6975. singleType="SCALAR";
  6976. value=0.97000003;
  6977. };
  6978. };
  6979. };
  6980. nAttributes=2;
  6981. };
  6982. };
  6983. };
  6984. class Attributes
  6985. {
  6986. };
  6987. id=538;
  6988. atlOffset=-7.6293945e-06;
  6989. };
  6990. class Item16
  6991. {
  6992. dataType="Group";
  6993. side="Civilian";
  6994. class Entities
  6995. {
  6996. items=1;
  6997. class Item0
  6998. {
  6999. dataType="Object";
  7000. class PositionInfo
  7001. {
  7002. position[]={8492.7686,87.380341,25264.182};
  7003. };
  7004. side="Civilian";
  7005. flags=7;
  7006. class Attributes
  7007. {
  7008. skill=1;
  7009. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7010. isPlayable=1;
  7011. };
  7012. id=541;
  7013. type="C_man_p_beggar_F";
  7014. atlOffset=-7.6293945e-06;
  7015. class CustomAttributes
  7016. {
  7017. class Attribute0
  7018. {
  7019. property="speaker";
  7020. expression="_this setspeaker _value;";
  7021. class Value
  7022. {
  7023. class data
  7024. {
  7025. singleType="STRING";
  7026. value="Male03GRE";
  7027. };
  7028. };
  7029. };
  7030. class Attribute1
  7031. {
  7032. property="pitch";
  7033. expression="_this setpitch _value;";
  7034. class Value
  7035. {
  7036. class data
  7037. {
  7038. singleType="SCALAR";
  7039. value=0.97000003;
  7040. };
  7041. };
  7042. };
  7043. nAttributes=2;
  7044. };
  7045. };
  7046. };
  7047. class Attributes
  7048. {
  7049. };
  7050. id=540;
  7051. atlOffset=-7.6293945e-06;
  7052. };
  7053. class Item17
  7054. {
  7055. dataType="Group";
  7056. side="Civilian";
  7057. class Entities
  7058. {
  7059. items=1;
  7060. class Item0
  7061. {
  7062. dataType="Object";
  7063. class PositionInfo
  7064. {
  7065. position[]={8490.9512,87.428673,25264.592};
  7066. };
  7067. side="Civilian";
  7068. flags=7;
  7069. class Attributes
  7070. {
  7071. skill=1;
  7072. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7073. isPlayable=1;
  7074. };
  7075. id=543;
  7076. type="C_man_p_beggar_F";
  7077. atlOffset=-7.6293945e-06;
  7078. class CustomAttributes
  7079. {
  7080. class Attribute0
  7081. {
  7082. property="speaker";
  7083. expression="_this setspeaker _value;";
  7084. class Value
  7085. {
  7086. class data
  7087. {
  7088. singleType="STRING";
  7089. value="Male03GRE";
  7090. };
  7091. };
  7092. };
  7093. class Attribute1
  7094. {
  7095. property="pitch";
  7096. expression="_this setpitch _value;";
  7097. class Value
  7098. {
  7099. class data
  7100. {
  7101. singleType="SCALAR";
  7102. value=0.97000003;
  7103. };
  7104. };
  7105. };
  7106. nAttributes=2;
  7107. };
  7108. };
  7109. };
  7110. class Attributes
  7111. {
  7112. };
  7113. id=542;
  7114. atlOffset=-7.6293945e-06;
  7115. };
  7116. class Item18
  7117. {
  7118. dataType="Group";
  7119. side="Civilian";
  7120. class Entities
  7121. {
  7122. items=1;
  7123. class Item0
  7124. {
  7125. dataType="Object";
  7126. class PositionInfo
  7127. {
  7128. position[]={8492.5508,87.158485,25265.615};
  7129. };
  7130. side="Civilian";
  7131. flags=7;
  7132. class Attributes
  7133. {
  7134. skill=1;
  7135. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7136. isPlayable=1;
  7137. };
  7138. id=545;
  7139. type="C_man_p_beggar_F";
  7140. atlOffset=-7.6293945e-06;
  7141. class CustomAttributes
  7142. {
  7143. class Attribute0
  7144. {
  7145. property="speaker";
  7146. expression="_this setspeaker _value;";
  7147. class Value
  7148. {
  7149. class data
  7150. {
  7151. singleType="STRING";
  7152. value="Male03GRE";
  7153. };
  7154. };
  7155. };
  7156. class Attribute1
  7157. {
  7158. property="pitch";
  7159. expression="_this setpitch _value;";
  7160. class Value
  7161. {
  7162. class data
  7163. {
  7164. singleType="SCALAR";
  7165. value=0.97000003;
  7166. };
  7167. };
  7168. };
  7169. nAttributes=2;
  7170. };
  7171. };
  7172. };
  7173. class Attributes
  7174. {
  7175. };
  7176. id=544;
  7177. atlOffset=-7.6293945e-06;
  7178. };
  7179. class Item19
  7180. {
  7181. dataType="Group";
  7182. side="Civilian";
  7183. class Entities
  7184. {
  7185. items=1;
  7186. class Item0
  7187. {
  7188. dataType="Object";
  7189. class PositionInfo
  7190. {
  7191. position[]={8491.0137,87.284843,25265.91};
  7192. };
  7193. side="Civilian";
  7194. flags=7;
  7195. class Attributes
  7196. {
  7197. skill=1;
  7198. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7199. isPlayable=1;
  7200. };
  7201. id=547;
  7202. type="C_man_p_beggar_F";
  7203. atlOffset=-7.6293945e-06;
  7204. class CustomAttributes
  7205. {
  7206. class Attribute0
  7207. {
  7208. property="speaker";
  7209. expression="_this setspeaker _value;";
  7210. class Value
  7211. {
  7212. class data
  7213. {
  7214. singleType="STRING";
  7215. value="Male03GRE";
  7216. };
  7217. };
  7218. };
  7219. class Attribute1
  7220. {
  7221. property="pitch";
  7222. expression="_this setpitch _value;";
  7223. class Value
  7224. {
  7225. class data
  7226. {
  7227. singleType="SCALAR";
  7228. value=0.97000003;
  7229. };
  7230. };
  7231. };
  7232. nAttributes=2;
  7233. };
  7234. };
  7235. };
  7236. class Attributes
  7237. {
  7238. };
  7239. id=546;
  7240. atlOffset=-7.6293945e-06;
  7241. };
  7242. class Item20
  7243. {
  7244. dataType="Group";
  7245. side="Civilian";
  7246. class Entities
  7247. {
  7248. items=1;
  7249. class Item0
  7250. {
  7251. dataType="Object";
  7252. class PositionInfo
  7253. {
  7254. position[]={8487.9951,87.905685,25261.072};
  7255. };
  7256. side="Civilian";
  7257. flags=7;
  7258. class Attributes
  7259. {
  7260. skill=1;
  7261. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7262. isPlayable=1;
  7263. };
  7264. id=549;
  7265. type="C_man_p_beggar_F";
  7266. atlOffset=-7.6293945e-06;
  7267. class CustomAttributes
  7268. {
  7269. class Attribute0
  7270. {
  7271. property="speaker";
  7272. expression="_this setspeaker _value;";
  7273. class Value
  7274. {
  7275. class data
  7276. {
  7277. singleType="STRING";
  7278. value="Male03GRE";
  7279. };
  7280. };
  7281. };
  7282. class Attribute1
  7283. {
  7284. property="pitch";
  7285. expression="_this setpitch _value;";
  7286. class Value
  7287. {
  7288. class data
  7289. {
  7290. singleType="SCALAR";
  7291. value=0.97000003;
  7292. };
  7293. };
  7294. };
  7295. nAttributes=2;
  7296. };
  7297. };
  7298. };
  7299. class Attributes
  7300. {
  7301. };
  7302. id=548;
  7303. atlOffset=-7.6293945e-06;
  7304. };
  7305. class Item21
  7306. {
  7307. dataType="Group";
  7308. side="Civilian";
  7309. class Entities
  7310. {
  7311. items=1;
  7312. class Item0
  7313. {
  7314. dataType="Object";
  7315. class PositionInfo
  7316. {
  7317. position[]={8487.7979,87.602234,25263.334};
  7318. };
  7319. side="Civilian";
  7320. flags=7;
  7321. class Attributes
  7322. {
  7323. skill=1;
  7324. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7325. isPlayable=1;
  7326. };
  7327. id=551;
  7328. type="C_man_p_beggar_F";
  7329. atlOffset=-7.6293945e-06;
  7330. class CustomAttributes
  7331. {
  7332. class Attribute0
  7333. {
  7334. property="speaker";
  7335. expression="_this setspeaker _value;";
  7336. class Value
  7337. {
  7338. class data
  7339. {
  7340. singleType="STRING";
  7341. value="Male03GRE";
  7342. };
  7343. };
  7344. };
  7345. class Attribute1
  7346. {
  7347. property="pitch";
  7348. expression="_this setpitch _value;";
  7349. class Value
  7350. {
  7351. class data
  7352. {
  7353. singleType="SCALAR";
  7354. value=0.97000003;
  7355. };
  7356. };
  7357. };
  7358. nAttributes=2;
  7359. };
  7360. };
  7361. };
  7362. class Attributes
  7363. {
  7364. };
  7365. id=550;
  7366. atlOffset=-7.6293945e-06;
  7367. };
  7368. class Item22
  7369. {
  7370. dataType="Group";
  7371. side="Civilian";
  7372. class Entities
  7373. {
  7374. items=1;
  7375. class Item0
  7376. {
  7377. dataType="Object";
  7378. class PositionInfo
  7379. {
  7380. position[]={8486.1787,87.977829,25261.275};
  7381. };
  7382. side="Civilian";
  7383. flags=7;
  7384. class Attributes
  7385. {
  7386. skill=1;
  7387. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7388. isPlayable=1;
  7389. };
  7390. id=553;
  7391. type="C_man_p_beggar_F";
  7392. atlOffset=-7.6293945e-06;
  7393. class CustomAttributes
  7394. {
  7395. class Attribute0
  7396. {
  7397. property="speaker";
  7398. expression="_this setspeaker _value;";
  7399. class Value
  7400. {
  7401. class data
  7402. {
  7403. singleType="STRING";
  7404. value="Male03GRE";
  7405. };
  7406. };
  7407. };
  7408. class Attribute1
  7409. {
  7410. property="pitch";
  7411. expression="_this setpitch _value;";
  7412. class Value
  7413. {
  7414. class data
  7415. {
  7416. singleType="SCALAR";
  7417. value=0.97000003;
  7418. };
  7419. };
  7420. };
  7421. nAttributes=2;
  7422. };
  7423. };
  7424. };
  7425. class Attributes
  7426. {
  7427. };
  7428. id=552;
  7429. atlOffset=-7.6293945e-06;
  7430. };
  7431. class Item23
  7432. {
  7433. dataType="Group";
  7434. side="Civilian";
  7435. class Entities
  7436. {
  7437. items=1;
  7438. class Item0
  7439. {
  7440. dataType="Object";
  7441. class PositionInfo
  7442. {
  7443. position[]={8485.9229,87.673195,25263.164};
  7444. };
  7445. side="Civilian";
  7446. flags=7;
  7447. class Attributes
  7448. {
  7449. skill=1;
  7450. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7451. isPlayable=1;
  7452. };
  7453. id=555;
  7454. type="C_man_p_beggar_F";
  7455. atlOffset=-7.6293945e-06;
  7456. class CustomAttributes
  7457. {
  7458. class Attribute0
  7459. {
  7460. property="speaker";
  7461. expression="_this setspeaker _value;";
  7462. class Value
  7463. {
  7464. class data
  7465. {
  7466. singleType="STRING";
  7467. value="Male03GRE";
  7468. };
  7469. };
  7470. };
  7471. class Attribute1
  7472. {
  7473. property="pitch";
  7474. expression="_this setpitch _value;";
  7475. class Value
  7476. {
  7477. class data
  7478. {
  7479. singleType="SCALAR";
  7480. value=0.97000003;
  7481. };
  7482. };
  7483. };
  7484. nAttributes=2;
  7485. };
  7486. };
  7487. };
  7488. class Attributes
  7489. {
  7490. };
  7491. id=554;
  7492. atlOffset=-7.6293945e-06;
  7493. };
  7494. class Item24
  7495. {
  7496. dataType="Group";
  7497. side="Civilian";
  7498. class Entities
  7499. {
  7500. items=1;
  7501. class Item0
  7502. {
  7503. dataType="Object";
  7504. class PositionInfo
  7505. {
  7506. position[]={8487.2832,87.428505,25264.943};
  7507. };
  7508. side="Civilian";
  7509. flags=7;
  7510. class Attributes
  7511. {
  7512. skill=1;
  7513. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7514. isPlayable=1;
  7515. };
  7516. id=557;
  7517. type="C_man_p_beggar_F";
  7518. atlOffset=-7.6293945e-06;
  7519. class CustomAttributes
  7520. {
  7521. class Attribute0
  7522. {
  7523. property="speaker";
  7524. expression="_this setspeaker _value;";
  7525. class Value
  7526. {
  7527. class data
  7528. {
  7529. singleType="STRING";
  7530. value="Male03GRE";
  7531. };
  7532. };
  7533. };
  7534. class Attribute1
  7535. {
  7536. property="pitch";
  7537. expression="_this setpitch _value;";
  7538. class Value
  7539. {
  7540. class data
  7541. {
  7542. singleType="SCALAR";
  7543. value=0.97000003;
  7544. };
  7545. };
  7546. };
  7547. nAttributes=2;
  7548. };
  7549. };
  7550. };
  7551. class Attributes
  7552. {
  7553. };
  7554. id=556;
  7555. atlOffset=-7.6293945e-06;
  7556. };
  7557. class Item25
  7558. {
  7559. dataType="Group";
  7560. side="Civilian";
  7561. class Entities
  7562. {
  7563. items=1;
  7564. class Item0
  7565. {
  7566. dataType="Object";
  7567. class PositionInfo
  7568. {
  7569. position[]={8485.625,87.410339,25265.053};
  7570. };
  7571. side="Civilian";
  7572. flags=7;
  7573. class Attributes
  7574. {
  7575. skill=1;
  7576. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7577. isPlayable=1;
  7578. };
  7579. id=559;
  7580. type="C_man_p_beggar_F";
  7581. atlOffset=-7.6293945e-06;
  7582. class CustomAttributes
  7583. {
  7584. class Attribute0
  7585. {
  7586. property="speaker";
  7587. expression="_this setspeaker _value;";
  7588. class Value
  7589. {
  7590. class data
  7591. {
  7592. singleType="STRING";
  7593. value="Male03GRE";
  7594. };
  7595. };
  7596. };
  7597. class Attribute1
  7598. {
  7599. property="pitch";
  7600. expression="_this setpitch _value;";
  7601. class Value
  7602. {
  7603. class data
  7604. {
  7605. singleType="SCALAR";
  7606. value=0.97000003;
  7607. };
  7608. };
  7609. };
  7610. nAttributes=2;
  7611. };
  7612. };
  7613. };
  7614. class Attributes
  7615. {
  7616. };
  7617. id=558;
  7618. atlOffset=-7.6293945e-06;
  7619. };
  7620. class Item26
  7621. {
  7622. dataType="Group";
  7623. side="Civilian";
  7624. class Entities
  7625. {
  7626. items=1;
  7627. class Item0
  7628. {
  7629. dataType="Object";
  7630. class PositionInfo
  7631. {
  7632. position[]={8487.1006,87.279335,25266.336};
  7633. };
  7634. side="Civilian";
  7635. flags=7;
  7636. class Attributes
  7637. {
  7638. skill=1;
  7639. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7640. isPlayable=1;
  7641. };
  7642. id=561;
  7643. type="C_man_p_beggar_F";
  7644. atlOffset=-7.6293945e-06;
  7645. class CustomAttributes
  7646. {
  7647. class Attribute0
  7648. {
  7649. property="speaker";
  7650. expression="_this setspeaker _value;";
  7651. class Value
  7652. {
  7653. class data
  7654. {
  7655. singleType="STRING";
  7656. value="Male03GRE";
  7657. };
  7658. };
  7659. };
  7660. class Attribute1
  7661. {
  7662. property="pitch";
  7663. expression="_this setpitch _value;";
  7664. class Value
  7665. {
  7666. class data
  7667. {
  7668. singleType="SCALAR";
  7669. value=0.97000003;
  7670. };
  7671. };
  7672. };
  7673. nAttributes=2;
  7674. };
  7675. };
  7676. };
  7677. class Attributes
  7678. {
  7679. };
  7680. id=560;
  7681. atlOffset=-7.6293945e-06;
  7682. };
  7683. class Item27
  7684. {
  7685. dataType="Group";
  7686. side="Civilian";
  7687. class Entities
  7688. {
  7689. items=1;
  7690. class Item0
  7691. {
  7692. dataType="Object";
  7693. class PositionInfo
  7694. {
  7695. position[]={8485.2832,87.22831,25266.746};
  7696. };
  7697. side="Civilian";
  7698. flags=7;
  7699. class Attributes
  7700. {
  7701. skill=1;
  7702. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7703. isPlayable=1;
  7704. };
  7705. id=563;
  7706. type="C_man_p_beggar_F";
  7707. atlOffset=-7.6293945e-06;
  7708. class CustomAttributes
  7709. {
  7710. class Attribute0
  7711. {
  7712. property="speaker";
  7713. expression="_this setspeaker _value;";
  7714. class Value
  7715. {
  7716. class data
  7717. {
  7718. singleType="STRING";
  7719. value="Male03GRE";
  7720. };
  7721. };
  7722. };
  7723. class Attribute1
  7724. {
  7725. property="pitch";
  7726. expression="_this setpitch _value;";
  7727. class Value
  7728. {
  7729. class data
  7730. {
  7731. singleType="SCALAR";
  7732. value=0.97000003;
  7733. };
  7734. };
  7735. };
  7736. nAttributes=2;
  7737. };
  7738. };
  7739. };
  7740. class Attributes
  7741. {
  7742. };
  7743. id=562;
  7744. atlOffset=-7.6293945e-06;
  7745. };
  7746. class Item28
  7747. {
  7748. dataType="Group";
  7749. side="Civilian";
  7750. class Entities
  7751. {
  7752. items=1;
  7753. class Item0
  7754. {
  7755. dataType="Object";
  7756. class PositionInfo
  7757. {
  7758. position[]={8486.8828,87.122017,25267.77};
  7759. };
  7760. side="Civilian";
  7761. flags=7;
  7762. class Attributes
  7763. {
  7764. skill=1;
  7765. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7766. isPlayable=1;
  7767. };
  7768. id=565;
  7769. type="C_man_p_beggar_F";
  7770. atlOffset=-7.6293945e-06;
  7771. class CustomAttributes
  7772. {
  7773. class Attribute0
  7774. {
  7775. property="speaker";
  7776. expression="_this setspeaker _value;";
  7777. class Value
  7778. {
  7779. class data
  7780. {
  7781. singleType="STRING";
  7782. value="Male03GRE";
  7783. };
  7784. };
  7785. };
  7786. class Attribute1
  7787. {
  7788. property="pitch";
  7789. expression="_this setpitch _value;";
  7790. class Value
  7791. {
  7792. class data
  7793. {
  7794. singleType="SCALAR";
  7795. value=0.97000003;
  7796. };
  7797. };
  7798. };
  7799. nAttributes=2;
  7800. };
  7801. };
  7802. };
  7803. class Attributes
  7804. {
  7805. };
  7806. id=564;
  7807. atlOffset=-7.6293945e-06;
  7808. };
  7809. class Item29
  7810. {
  7811. dataType="Group";
  7812. side="Civilian";
  7813. class Entities
  7814. {
  7815. items=1;
  7816. class Item0
  7817. {
  7818. dataType="Object";
  7819. class PositionInfo
  7820. {
  7821. position[]={8485.3457,87.079704,25268.064};
  7822. };
  7823. side="Civilian";
  7824. flags=7;
  7825. class Attributes
  7826. {
  7827. skill=1;
  7828. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7829. isPlayable=1;
  7830. };
  7831. id=567;
  7832. type="C_man_p_beggar_F";
  7833. atlOffset=-7.6293945e-06;
  7834. class CustomAttributes
  7835. {
  7836. class Attribute0
  7837. {
  7838. property="speaker";
  7839. expression="_this setspeaker _value;";
  7840. class Value
  7841. {
  7842. class data
  7843. {
  7844. singleType="STRING";
  7845. value="Male03GRE";
  7846. };
  7847. };
  7848. };
  7849. class Attribute1
  7850. {
  7851. property="pitch";
  7852. expression="_this setpitch _value;";
  7853. class Value
  7854. {
  7855. class data
  7856. {
  7857. singleType="SCALAR";
  7858. value=0.97000003;
  7859. };
  7860. };
  7861. };
  7862. nAttributes=2;
  7863. };
  7864. };
  7865. };
  7866. class Attributes
  7867. {
  7868. };
  7869. id=566;
  7870. atlOffset=-7.6293945e-06;
  7871. };
  7872. class Item30
  7873. {
  7874. dataType="Group";
  7875. side="Civilian";
  7876. class Entities
  7877. {
  7878. items=1;
  7879. class Item0
  7880. {
  7881. dataType="Object";
  7882. class PositionInfo
  7883. {
  7884. position[]={8483.2041,88.020248,25262.057};
  7885. };
  7886. side="Civilian";
  7887. flags=7;
  7888. class Attributes
  7889. {
  7890. skill=1;
  7891. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7892. isPlayable=1;
  7893. };
  7894. id=569;
  7895. type="C_man_p_beggar_F";
  7896. atlOffset=-7.6293945e-06;
  7897. class CustomAttributes
  7898. {
  7899. class Attribute0
  7900. {
  7901. property="speaker";
  7902. expression="_this setspeaker _value;";
  7903. class Value
  7904. {
  7905. class data
  7906. {
  7907. singleType="STRING";
  7908. value="Male03GRE";
  7909. };
  7910. };
  7911. };
  7912. class Attribute1
  7913. {
  7914. property="pitch";
  7915. expression="_this setpitch _value;";
  7916. class Value
  7917. {
  7918. class data
  7919. {
  7920. singleType="SCALAR";
  7921. value=0.97000003;
  7922. };
  7923. };
  7924. };
  7925. nAttributes=2;
  7926. };
  7927. };
  7928. };
  7929. class Attributes
  7930. {
  7931. };
  7932. id=568;
  7933. atlOffset=-7.6293945e-06;
  7934. };
  7935. class Item31
  7936. {
  7937. dataType="Group";
  7938. side="Civilian";
  7939. class Entities
  7940. {
  7941. items=1;
  7942. class Item0
  7943. {
  7944. dataType="Object";
  7945. class PositionInfo
  7946. {
  7947. position[]={8483.0068,87.648933,25264.318};
  7948. };
  7949. side="Civilian";
  7950. flags=7;
  7951. class Attributes
  7952. {
  7953. skill=1;
  7954. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  7955. isPlayable=1;
  7956. };
  7957. id=571;
  7958. type="C_man_p_beggar_F";
  7959. atlOffset=-7.6293945e-06;
  7960. class CustomAttributes
  7961. {
  7962. class Attribute0
  7963. {
  7964. property="speaker";
  7965. expression="_this setspeaker _value;";
  7966. class Value
  7967. {
  7968. class data
  7969. {
  7970. singleType="STRING";
  7971. value="Male03GRE";
  7972. };
  7973. };
  7974. };
  7975. class Attribute1
  7976. {
  7977. property="pitch";
  7978. expression="_this setpitch _value;";
  7979. class Value
  7980. {
  7981. class data
  7982. {
  7983. singleType="SCALAR";
  7984. value=0.97000003;
  7985. };
  7986. };
  7987. };
  7988. nAttributes=2;
  7989. };
  7990. };
  7991. };
  7992. class Attributes
  7993. {
  7994. };
  7995. id=570;
  7996. atlOffset=-7.6293945e-06;
  7997. };
  7998. class Item32
  7999. {
  8000. dataType="Group";
  8001. side="Civilian";
  8002. class Entities
  8003. {
  8004. items=1;
  8005. class Item0
  8006. {
  8007. dataType="Object";
  8008. class PositionInfo
  8009. {
  8010. position[]={8481.3877,88.06424,25262.26};
  8011. };
  8012. side="Civilian";
  8013. flags=7;
  8014. class Attributes
  8015. {
  8016. skill=1;
  8017. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8018. isPlayable=1;
  8019. };
  8020. id=573;
  8021. type="C_man_p_beggar_F";
  8022. atlOffset=-7.6293945e-06;
  8023. class CustomAttributes
  8024. {
  8025. class Attribute0
  8026. {
  8027. property="speaker";
  8028. expression="_this setspeaker _value;";
  8029. class Value
  8030. {
  8031. class data
  8032. {
  8033. singleType="STRING";
  8034. value="Male03GRE";
  8035. };
  8036. };
  8037. };
  8038. class Attribute1
  8039. {
  8040. property="pitch";
  8041. expression="_this setpitch _value;";
  8042. class Value
  8043. {
  8044. class data
  8045. {
  8046. singleType="SCALAR";
  8047. value=0.97000003;
  8048. };
  8049. };
  8050. };
  8051. nAttributes=2;
  8052. };
  8053. };
  8054. };
  8055. class Attributes
  8056. {
  8057. };
  8058. id=572;
  8059. atlOffset=-7.6293945e-06;
  8060. };
  8061. class Item33
  8062. {
  8063. dataType="Group";
  8064. side="Civilian";
  8065. class Entities
  8066. {
  8067. items=1;
  8068. class Item0
  8069. {
  8070. dataType="Object";
  8071. class PositionInfo
  8072. {
  8073. position[]={8481.1318,87.752808,25264.148};
  8074. };
  8075. side="Civilian";
  8076. flags=7;
  8077. class Attributes
  8078. {
  8079. skill=1;
  8080. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8081. isPlayable=1;
  8082. };
  8083. id=575;
  8084. type="C_man_p_beggar_F";
  8085. atlOffset=-7.6293945e-06;
  8086. class CustomAttributes
  8087. {
  8088. class Attribute0
  8089. {
  8090. property="speaker";
  8091. expression="_this setspeaker _value;";
  8092. class Value
  8093. {
  8094. class data
  8095. {
  8096. singleType="STRING";
  8097. value="Male03GRE";
  8098. };
  8099. };
  8100. };
  8101. class Attribute1
  8102. {
  8103. property="pitch";
  8104. expression="_this setpitch _value;";
  8105. class Value
  8106. {
  8107. class data
  8108. {
  8109. singleType="SCALAR";
  8110. value=0.97000003;
  8111. };
  8112. };
  8113. };
  8114. nAttributes=2;
  8115. };
  8116. };
  8117. };
  8118. class Attributes
  8119. {
  8120. };
  8121. id=574;
  8122. atlOffset=-7.6293945e-06;
  8123. };
  8124. class Item34
  8125. {
  8126. dataType="Group";
  8127. side="Civilian";
  8128. class Entities
  8129. {
  8130. items=1;
  8131. class Item0
  8132. {
  8133. dataType="Object";
  8134. class PositionInfo
  8135. {
  8136. position[]={8482.4922,87.406387,25265.928};
  8137. };
  8138. side="Civilian";
  8139. flags=7;
  8140. class Attributes
  8141. {
  8142. skill=1;
  8143. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8144. isPlayable=1;
  8145. };
  8146. id=577;
  8147. type="C_man_p_beggar_F";
  8148. atlOffset=-7.6293945e-06;
  8149. class CustomAttributes
  8150. {
  8151. class Attribute0
  8152. {
  8153. property="speaker";
  8154. expression="_this setspeaker _value;";
  8155. class Value
  8156. {
  8157. class data
  8158. {
  8159. singleType="STRING";
  8160. value="Male03GRE";
  8161. };
  8162. };
  8163. };
  8164. class Attribute1
  8165. {
  8166. property="pitch";
  8167. expression="_this setpitch _value;";
  8168. class Value
  8169. {
  8170. class data
  8171. {
  8172. singleType="SCALAR";
  8173. value=0.97000003;
  8174. };
  8175. };
  8176. };
  8177. nAttributes=2;
  8178. };
  8179. };
  8180. };
  8181. class Attributes
  8182. {
  8183. };
  8184. id=576;
  8185. atlOffset=-7.6293945e-06;
  8186. };
  8187. class Item35
  8188. {
  8189. dataType="Group";
  8190. side="Civilian";
  8191. class Entities
  8192. {
  8193. items=1;
  8194. class Item0
  8195. {
  8196. dataType="Object";
  8197. class PositionInfo
  8198. {
  8199. position[]={8480.834,87.443054,25266.037};
  8200. };
  8201. side="Civilian";
  8202. flags=7;
  8203. class Attributes
  8204. {
  8205. skill=1;
  8206. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8207. isPlayable=1;
  8208. };
  8209. id=579;
  8210. type="C_man_p_beggar_F";
  8211. atlOffset=-7.6293945e-06;
  8212. class CustomAttributes
  8213. {
  8214. class Attribute0
  8215. {
  8216. property="speaker";
  8217. expression="_this setspeaker _value;";
  8218. class Value
  8219. {
  8220. class data
  8221. {
  8222. singleType="STRING";
  8223. value="Male03GRE";
  8224. };
  8225. };
  8226. };
  8227. class Attribute1
  8228. {
  8229. property="pitch";
  8230. expression="_this setpitch _value;";
  8231. class Value
  8232. {
  8233. class data
  8234. {
  8235. singleType="SCALAR";
  8236. value=0.97000003;
  8237. };
  8238. };
  8239. };
  8240. nAttributes=2;
  8241. };
  8242. };
  8243. };
  8244. class Attributes
  8245. {
  8246. };
  8247. id=578;
  8248. atlOffset=-7.6293945e-06;
  8249. };
  8250. class Item36
  8251. {
  8252. dataType="Group";
  8253. side="Civilian";
  8254. class Entities
  8255. {
  8256. items=1;
  8257. class Item0
  8258. {
  8259. dataType="Object";
  8260. class PositionInfo
  8261. {
  8262. position[]={8482.3096,87.176537,25267.32};
  8263. };
  8264. side="Civilian";
  8265. flags=7;
  8266. class Attributes
  8267. {
  8268. skill=1;
  8269. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8270. isPlayable=1;
  8271. };
  8272. id=581;
  8273. type="C_man_p_beggar_F";
  8274. atlOffset=-7.6293945e-06;
  8275. class CustomAttributes
  8276. {
  8277. class Attribute0
  8278. {
  8279. property="speaker";
  8280. expression="_this setspeaker _value;";
  8281. class Value
  8282. {
  8283. class data
  8284. {
  8285. singleType="STRING";
  8286. value="Male03GRE";
  8287. };
  8288. };
  8289. };
  8290. class Attribute1
  8291. {
  8292. property="pitch";
  8293. expression="_this setpitch _value;";
  8294. class Value
  8295. {
  8296. class data
  8297. {
  8298. singleType="SCALAR";
  8299. value=0.97000003;
  8300. };
  8301. };
  8302. };
  8303. nAttributes=2;
  8304. };
  8305. };
  8306. };
  8307. class Attributes
  8308. {
  8309. };
  8310. id=580;
  8311. atlOffset=-7.6293945e-06;
  8312. };
  8313. class Item37
  8314. {
  8315. dataType="Group";
  8316. side="Civilian";
  8317. class Entities
  8318. {
  8319. items=1;
  8320. class Item0
  8321. {
  8322. dataType="Object";
  8323. class PositionInfo
  8324. {
  8325. position[]={8480.4922,87.173218,25267.73};
  8326. };
  8327. side="Civilian";
  8328. flags=7;
  8329. class Attributes
  8330. {
  8331. skill=1;
  8332. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8333. isPlayable=1;
  8334. };
  8335. id=583;
  8336. type="C_man_p_beggar_F";
  8337. atlOffset=-7.6293945e-06;
  8338. class CustomAttributes
  8339. {
  8340. class Attribute0
  8341. {
  8342. property="speaker";
  8343. expression="_this setspeaker _value;";
  8344. class Value
  8345. {
  8346. class data
  8347. {
  8348. singleType="STRING";
  8349. value="Male03GRE";
  8350. };
  8351. };
  8352. };
  8353. class Attribute1
  8354. {
  8355. property="pitch";
  8356. expression="_this setpitch _value;";
  8357. class Value
  8358. {
  8359. class data
  8360. {
  8361. singleType="SCALAR";
  8362. value=0.97000003;
  8363. };
  8364. };
  8365. };
  8366. nAttributes=2;
  8367. };
  8368. };
  8369. };
  8370. class Attributes
  8371. {
  8372. };
  8373. id=582;
  8374. atlOffset=-7.6293945e-06;
  8375. };
  8376. class Item38
  8377. {
  8378. dataType="Group";
  8379. side="Civilian";
  8380. class Entities
  8381. {
  8382. items=1;
  8383. class Item0
  8384. {
  8385. dataType="Object";
  8386. class PositionInfo
  8387. {
  8388. position[]={8482.0918,86.990707,25268.754};
  8389. };
  8390. side="Civilian";
  8391. flags=7;
  8392. class Attributes
  8393. {
  8394. skill=1;
  8395. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8396. isPlayable=1;
  8397. };
  8398. id=585;
  8399. type="C_man_p_beggar_F";
  8400. atlOffset=-7.6293945e-06;
  8401. class CustomAttributes
  8402. {
  8403. class Attribute0
  8404. {
  8405. property="speaker";
  8406. expression="_this setspeaker _value;";
  8407. class Value
  8408. {
  8409. class data
  8410. {
  8411. singleType="STRING";
  8412. value="Male03GRE";
  8413. };
  8414. };
  8415. };
  8416. class Attribute1
  8417. {
  8418. property="pitch";
  8419. expression="_this setpitch _value;";
  8420. class Value
  8421. {
  8422. class data
  8423. {
  8424. singleType="SCALAR";
  8425. value=0.97000003;
  8426. };
  8427. };
  8428. };
  8429. nAttributes=2;
  8430. };
  8431. };
  8432. };
  8433. class Attributes
  8434. {
  8435. };
  8436. id=584;
  8437. atlOffset=-7.6293945e-06;
  8438. };
  8439. class Item39
  8440. {
  8441. dataType="Group";
  8442. side="Civilian";
  8443. class Entities
  8444. {
  8445. items=1;
  8446. class Item0
  8447. {
  8448. dataType="Object";
  8449. class PositionInfo
  8450. {
  8451. position[]={8480.5547,86.988304,25269.049};
  8452. };
  8453. side="Civilian";
  8454. flags=7;
  8455. class Attributes
  8456. {
  8457. skill=1;
  8458. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8459. isPlayable=1;
  8460. };
  8461. id=587;
  8462. type="C_man_p_beggar_F";
  8463. atlOffset=-7.6293945e-06;
  8464. class CustomAttributes
  8465. {
  8466. class Attribute0
  8467. {
  8468. property="speaker";
  8469. expression="_this setspeaker _value;";
  8470. class Value
  8471. {
  8472. class data
  8473. {
  8474. singleType="STRING";
  8475. value="Male03GRE";
  8476. };
  8477. };
  8478. };
  8479. class Attribute1
  8480. {
  8481. property="pitch";
  8482. expression="_this setpitch _value;";
  8483. class Value
  8484. {
  8485. class data
  8486. {
  8487. singleType="SCALAR";
  8488. value=0.97000003;
  8489. };
  8490. };
  8491. };
  8492. nAttributes=2;
  8493. };
  8494. };
  8495. };
  8496. class Attributes
  8497. {
  8498. };
  8499. id=586;
  8500. atlOffset=-7.6293945e-06;
  8501. };
  8502. class Item40
  8503. {
  8504. dataType="Group";
  8505. side="Civilian";
  8506. class Entities
  8507. {
  8508. items=1;
  8509. class Item0
  8510. {
  8511. dataType="Object";
  8512. class PositionInfo
  8513. {
  8514. position[]={8478.3203,88.260071,25261.908};
  8515. };
  8516. side="Civilian";
  8517. flags=7;
  8518. class Attributes
  8519. {
  8520. skill=1;
  8521. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8522. isPlayable=1;
  8523. };
  8524. id=589;
  8525. type="C_man_p_beggar_F";
  8526. atlOffset=-7.6293945e-06;
  8527. class CustomAttributes
  8528. {
  8529. class Attribute0
  8530. {
  8531. property="speaker";
  8532. expression="_this setspeaker _value;";
  8533. class Value
  8534. {
  8535. class data
  8536. {
  8537. singleType="STRING";
  8538. value="Male03GRE";
  8539. };
  8540. };
  8541. };
  8542. class Attribute1
  8543. {
  8544. property="pitch";
  8545. expression="_this setpitch _value;";
  8546. class Value
  8547. {
  8548. class data
  8549. {
  8550. singleType="SCALAR";
  8551. value=0.97000003;
  8552. };
  8553. };
  8554. };
  8555. nAttributes=2;
  8556. };
  8557. };
  8558. };
  8559. class Attributes
  8560. {
  8561. };
  8562. id=588;
  8563. atlOffset=-7.6293945e-06;
  8564. };
  8565. class Item41
  8566. {
  8567. dataType="Group";
  8568. side="Civilian";
  8569. class Entities
  8570. {
  8571. items=1;
  8572. class Item0
  8573. {
  8574. dataType="Object";
  8575. class PositionInfo
  8576. {
  8577. position[]={8478.123,87.853439,25264.17};
  8578. };
  8579. side="Civilian";
  8580. flags=7;
  8581. class Attributes
  8582. {
  8583. skill=1;
  8584. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8585. isPlayable=1;
  8586. };
  8587. id=591;
  8588. type="C_man_p_beggar_F";
  8589. atlOffset=-7.6293945e-06;
  8590. class CustomAttributes
  8591. {
  8592. class Attribute0
  8593. {
  8594. property="speaker";
  8595. expression="_this setspeaker _value;";
  8596. class Value
  8597. {
  8598. class data
  8599. {
  8600. singleType="STRING";
  8601. value="Male03GRE";
  8602. };
  8603. };
  8604. };
  8605. class Attribute1
  8606. {
  8607. property="pitch";
  8608. expression="_this setpitch _value;";
  8609. class Value
  8610. {
  8611. class data
  8612. {
  8613. singleType="SCALAR";
  8614. value=0.97000003;
  8615. };
  8616. };
  8617. };
  8618. nAttributes=2;
  8619. };
  8620. };
  8621. };
  8622. class Attributes
  8623. {
  8624. };
  8625. id=590;
  8626. atlOffset=-7.6293945e-06;
  8627. };
  8628. class Item42
  8629. {
  8630. dataType="Group";
  8631. side="Civilian";
  8632. class Entities
  8633. {
  8634. items=1;
  8635. class Item0
  8636. {
  8637. dataType="Object";
  8638. class PositionInfo
  8639. {
  8640. position[]={8476.5039,88.309868,25262.111};
  8641. };
  8642. side="Civilian";
  8643. flags=7;
  8644. class Attributes
  8645. {
  8646. skill=1;
  8647. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8648. isPlayable=1;
  8649. };
  8650. id=593;
  8651. type="C_man_p_beggar_F";
  8652. atlOffset=-7.6293945e-06;
  8653. class CustomAttributes
  8654. {
  8655. class Attribute0
  8656. {
  8657. property="speaker";
  8658. expression="_this setspeaker _value;";
  8659. class Value
  8660. {
  8661. class data
  8662. {
  8663. singleType="STRING";
  8664. value="Male03GRE";
  8665. };
  8666. };
  8667. };
  8668. class Attribute1
  8669. {
  8670. property="pitch";
  8671. expression="_this setpitch _value;";
  8672. class Value
  8673. {
  8674. class data
  8675. {
  8676. singleType="SCALAR";
  8677. value=0.97000003;
  8678. };
  8679. };
  8680. };
  8681. nAttributes=2;
  8682. };
  8683. };
  8684. };
  8685. class Attributes
  8686. {
  8687. };
  8688. id=592;
  8689. atlOffset=-7.6293945e-06;
  8690. };
  8691. class Item43
  8692. {
  8693. dataType="Group";
  8694. side="Civilian";
  8695. class Entities
  8696. {
  8697. items=1;
  8698. class Item0
  8699. {
  8700. dataType="Object";
  8701. class PositionInfo
  8702. {
  8703. position[]={8476.248,87.974426,25264};
  8704. };
  8705. side="Civilian";
  8706. flags=7;
  8707. class Attributes
  8708. {
  8709. skill=1;
  8710. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8711. isPlayable=1;
  8712. };
  8713. id=595;
  8714. type="C_man_p_beggar_F";
  8715. atlOffset=-7.6293945e-06;
  8716. class CustomAttributes
  8717. {
  8718. class Attribute0
  8719. {
  8720. property="speaker";
  8721. expression="_this setspeaker _value;";
  8722. class Value
  8723. {
  8724. class data
  8725. {
  8726. singleType="STRING";
  8727. value="Male03GRE";
  8728. };
  8729. };
  8730. };
  8731. class Attribute1
  8732. {
  8733. property="pitch";
  8734. expression="_this setpitch _value;";
  8735. class Value
  8736. {
  8737. class data
  8738. {
  8739. singleType="SCALAR";
  8740. value=0.97000003;
  8741. };
  8742. };
  8743. };
  8744. nAttributes=2;
  8745. };
  8746. };
  8747. };
  8748. class Attributes
  8749. {
  8750. };
  8751. id=594;
  8752. atlOffset=-7.6293945e-06;
  8753. };
  8754. class Item44
  8755. {
  8756. dataType="Group";
  8757. side="Civilian";
  8758. class Entities
  8759. {
  8760. items=1;
  8761. class Item0
  8762. {
  8763. dataType="Object";
  8764. class PositionInfo
  8765. {
  8766. position[]={8477.6084,87.594284,25265.779};
  8767. };
  8768. side="Civilian";
  8769. flags=7;
  8770. class Attributes
  8771. {
  8772. skill=1;
  8773. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8774. isPlayable=1;
  8775. };
  8776. id=597;
  8777. type="C_man_p_beggar_F";
  8778. atlOffset=-7.6293945e-06;
  8779. class CustomAttributes
  8780. {
  8781. class Attribute0
  8782. {
  8783. property="speaker";
  8784. expression="_this setspeaker _value;";
  8785. class Value
  8786. {
  8787. class data
  8788. {
  8789. singleType="STRING";
  8790. value="Male03GRE";
  8791. };
  8792. };
  8793. };
  8794. class Attribute1
  8795. {
  8796. property="pitch";
  8797. expression="_this setpitch _value;";
  8798. class Value
  8799. {
  8800. class data
  8801. {
  8802. singleType="SCALAR";
  8803. value=0.97000003;
  8804. };
  8805. };
  8806. };
  8807. nAttributes=2;
  8808. };
  8809. };
  8810. };
  8811. class Attributes
  8812. {
  8813. };
  8814. id=596;
  8815. atlOffset=-7.6293945e-06;
  8816. };
  8817. class Item45
  8818. {
  8819. dataType="Group";
  8820. side="Civilian";
  8821. class Entities
  8822. {
  8823. items=1;
  8824. class Item0
  8825. {
  8826. dataType="Object";
  8827. class PositionInfo
  8828. {
  8829. position[]={8475.9502,87.641396,25265.889};
  8830. };
  8831. side="Civilian";
  8832. flags=7;
  8833. class Attributes
  8834. {
  8835. skill=1;
  8836. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8837. isPlayable=1;
  8838. };
  8839. id=599;
  8840. type="C_man_p_beggar_F";
  8841. atlOffset=-7.6293945e-06;
  8842. class CustomAttributes
  8843. {
  8844. class Attribute0
  8845. {
  8846. property="speaker";
  8847. expression="_this setspeaker _value;";
  8848. class Value
  8849. {
  8850. class data
  8851. {
  8852. singleType="STRING";
  8853. value="Male03GRE";
  8854. };
  8855. };
  8856. };
  8857. class Attribute1
  8858. {
  8859. property="pitch";
  8860. expression="_this setpitch _value;";
  8861. class Value
  8862. {
  8863. class data
  8864. {
  8865. singleType="SCALAR";
  8866. value=0.97000003;
  8867. };
  8868. };
  8869. };
  8870. nAttributes=2;
  8871. };
  8872. };
  8873. };
  8874. class Attributes
  8875. {
  8876. };
  8877. id=598;
  8878. atlOffset=-7.6293945e-06;
  8879. };
  8880. class Item46
  8881. {
  8882. dataType="Group";
  8883. side="Civilian";
  8884. class Entities
  8885. {
  8886. items=1;
  8887. class Item0
  8888. {
  8889. dataType="Object";
  8890. class PositionInfo
  8891. {
  8892. position[]={8477.4258,87.364433,25267.172};
  8893. };
  8894. side="Civilian";
  8895. flags=7;
  8896. class Attributes
  8897. {
  8898. skill=1;
  8899. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8900. isPlayable=1;
  8901. };
  8902. id=601;
  8903. type="C_man_p_beggar_F";
  8904. atlOffset=-7.6293945e-06;
  8905. class CustomAttributes
  8906. {
  8907. class Attribute0
  8908. {
  8909. property="speaker";
  8910. expression="_this setspeaker _value;";
  8911. class Value
  8912. {
  8913. class data
  8914. {
  8915. singleType="STRING";
  8916. value="Male03GRE";
  8917. };
  8918. };
  8919. };
  8920. class Attribute1
  8921. {
  8922. property="pitch";
  8923. expression="_this setpitch _value;";
  8924. class Value
  8925. {
  8926. class data
  8927. {
  8928. singleType="SCALAR";
  8929. value=0.97000003;
  8930. };
  8931. };
  8932. };
  8933. nAttributes=2;
  8934. };
  8935. };
  8936. };
  8937. class Attributes
  8938. {
  8939. };
  8940. id=600;
  8941. atlOffset=-7.6293945e-06;
  8942. };
  8943. class Item47
  8944. {
  8945. dataType="Group";
  8946. side="Civilian";
  8947. class Entities
  8948. {
  8949. items=1;
  8950. class Item0
  8951. {
  8952. dataType="Object";
  8953. class PositionInfo
  8954. {
  8955. position[]={8475.6084,87.356567,25267.582};
  8956. };
  8957. side="Civilian";
  8958. flags=7;
  8959. class Attributes
  8960. {
  8961. skill=1;
  8962. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  8963. isPlayable=1;
  8964. };
  8965. id=603;
  8966. type="C_man_p_beggar_F";
  8967. atlOffset=-7.6293945e-06;
  8968. class CustomAttributes
  8969. {
  8970. class Attribute0
  8971. {
  8972. property="speaker";
  8973. expression="_this setspeaker _value;";
  8974. class Value
  8975. {
  8976. class data
  8977. {
  8978. singleType="STRING";
  8979. value="Male03GRE";
  8980. };
  8981. };
  8982. };
  8983. class Attribute1
  8984. {
  8985. property="pitch";
  8986. expression="_this setpitch _value;";
  8987. class Value
  8988. {
  8989. class data
  8990. {
  8991. singleType="SCALAR";
  8992. value=0.97000003;
  8993. };
  8994. };
  8995. };
  8996. nAttributes=2;
  8997. };
  8998. };
  8999. };
  9000. class Attributes
  9001. {
  9002. };
  9003. id=602;
  9004. atlOffset=-7.6293945e-06;
  9005. };
  9006. class Item48
  9007. {
  9008. dataType="Group";
  9009. side="Civilian";
  9010. class Entities
  9011. {
  9012. items=1;
  9013. class Item0
  9014. {
  9015. dataType="Object";
  9016. class PositionInfo
  9017. {
  9018. position[]={8477.208,87.161316,25268.605};
  9019. };
  9020. side="Civilian";
  9021. flags=7;
  9022. class Attributes
  9023. {
  9024. skill=1;
  9025. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9026. isPlayable=1;
  9027. };
  9028. id=605;
  9029. type="C_man_p_beggar_F";
  9030. atlOffset=-7.6293945e-06;
  9031. class CustomAttributes
  9032. {
  9033. class Attribute0
  9034. {
  9035. property="speaker";
  9036. expression="_this setspeaker _value;";
  9037. class Value
  9038. {
  9039. class data
  9040. {
  9041. singleType="STRING";
  9042. value="Male03GRE";
  9043. };
  9044. };
  9045. };
  9046. class Attribute1
  9047. {
  9048. property="pitch";
  9049. expression="_this setpitch _value;";
  9050. class Value
  9051. {
  9052. class data
  9053. {
  9054. singleType="SCALAR";
  9055. value=0.97000003;
  9056. };
  9057. };
  9058. };
  9059. nAttributes=2;
  9060. };
  9061. };
  9062. };
  9063. class Attributes
  9064. {
  9065. };
  9066. id=604;
  9067. atlOffset=-7.6293945e-06;
  9068. };
  9069. class Item49
  9070. {
  9071. dataType="Group";
  9072. side="Civilian";
  9073. class Entities
  9074. {
  9075. items=1;
  9076. class Item0
  9077. {
  9078. dataType="Object";
  9079. class PositionInfo
  9080. {
  9081. position[]={8475.6709,87.171677,25268.9};
  9082. };
  9083. side="Civilian";
  9084. flags=7;
  9085. class Attributes
  9086. {
  9087. skill=1;
  9088. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9089. isPlayable=1;
  9090. };
  9091. id=607;
  9092. type="C_man_p_beggar_F";
  9093. atlOffset=-7.6293945e-06;
  9094. class CustomAttributes
  9095. {
  9096. class Attribute0
  9097. {
  9098. property="speaker";
  9099. expression="_this setspeaker _value;";
  9100. class Value
  9101. {
  9102. class data
  9103. {
  9104. singleType="STRING";
  9105. value="Male03GRE";
  9106. };
  9107. };
  9108. };
  9109. class Attribute1
  9110. {
  9111. property="pitch";
  9112. expression="_this setpitch _value;";
  9113. class Value
  9114. {
  9115. class data
  9116. {
  9117. singleType="SCALAR";
  9118. value=0.97000003;
  9119. };
  9120. };
  9121. };
  9122. nAttributes=2;
  9123. };
  9124. };
  9125. };
  9126. class Attributes
  9127. {
  9128. };
  9129. id=606;
  9130. atlOffset=-7.6293945e-06;
  9131. };
  9132. class Item50
  9133. {
  9134. dataType="Group";
  9135. side="Civilian";
  9136. class Entities
  9137. {
  9138. items=1;
  9139. class Item0
  9140. {
  9141. dataType="Object";
  9142. class PositionInfo
  9143. {
  9144. position[]={8473.959,88.366005,25261.775};
  9145. };
  9146. side="Civilian";
  9147. flags=7;
  9148. class Attributes
  9149. {
  9150. skill=1;
  9151. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9152. isPlayable=1;
  9153. };
  9154. id=609;
  9155. type="C_man_p_beggar_F";
  9156. atlOffset=-7.6293945e-06;
  9157. class CustomAttributes
  9158. {
  9159. class Attribute0
  9160. {
  9161. property="speaker";
  9162. expression="_this setspeaker _value;";
  9163. class Value
  9164. {
  9165. class data
  9166. {
  9167. singleType="STRING";
  9168. value="Male03GRE";
  9169. };
  9170. };
  9171. };
  9172. class Attribute1
  9173. {
  9174. property="pitch";
  9175. expression="_this setpitch _value;";
  9176. class Value
  9177. {
  9178. class data
  9179. {
  9180. singleType="SCALAR";
  9181. value=0.97000003;
  9182. };
  9183. };
  9184. };
  9185. nAttributes=2;
  9186. };
  9187. };
  9188. };
  9189. class Attributes
  9190. {
  9191. };
  9192. id=608;
  9193. atlOffset=-7.6293945e-06;
  9194. };
  9195. class Item51
  9196. {
  9197. dataType="Group";
  9198. side="Civilian";
  9199. class Entities
  9200. {
  9201. items=1;
  9202. class Item0
  9203. {
  9204. dataType="Object";
  9205. class PositionInfo
  9206. {
  9207. position[]={8473.7617,87.935211,25264.037};
  9208. };
  9209. side="Civilian";
  9210. flags=7;
  9211. class Attributes
  9212. {
  9213. skill=1;
  9214. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9215. isPlayable=1;
  9216. };
  9217. id=611;
  9218. type="C_man_p_beggar_F";
  9219. atlOffset=-7.6293945e-06;
  9220. class CustomAttributes
  9221. {
  9222. class Attribute0
  9223. {
  9224. property="speaker";
  9225. expression="_this setspeaker _value;";
  9226. class Value
  9227. {
  9228. class data
  9229. {
  9230. singleType="STRING";
  9231. value="Male03GRE";
  9232. };
  9233. };
  9234. };
  9235. class Attribute1
  9236. {
  9237. property="pitch";
  9238. expression="_this setpitch _value;";
  9239. class Value
  9240. {
  9241. class data
  9242. {
  9243. singleType="SCALAR";
  9244. value=0.97000003;
  9245. };
  9246. };
  9247. };
  9248. nAttributes=2;
  9249. };
  9250. };
  9251. };
  9252. class Attributes
  9253. {
  9254. };
  9255. id=610;
  9256. atlOffset=-7.6293945e-06;
  9257. };
  9258. class Item52
  9259. {
  9260. dataType="Group";
  9261. side="Civilian";
  9262. class Entities
  9263. {
  9264. items=1;
  9265. class Item0
  9266. {
  9267. dataType="Object";
  9268. class PositionInfo
  9269. {
  9270. position[]={8472.1426,88.180611,25261.979};
  9271. };
  9272. side="Civilian";
  9273. flags=7;
  9274. class Attributes
  9275. {
  9276. skill=1;
  9277. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9278. isPlayable=1;
  9279. };
  9280. id=613;
  9281. type="C_man_p_beggar_F";
  9282. atlOffset=-7.6293945e-06;
  9283. class CustomAttributes
  9284. {
  9285. class Attribute0
  9286. {
  9287. property="speaker";
  9288. expression="_this setspeaker _value;";
  9289. class Value
  9290. {
  9291. class data
  9292. {
  9293. singleType="STRING";
  9294. value="Male03GRE";
  9295. };
  9296. };
  9297. };
  9298. class Attribute1
  9299. {
  9300. property="pitch";
  9301. expression="_this setpitch _value;";
  9302. class Value
  9303. {
  9304. class data
  9305. {
  9306. singleType="SCALAR";
  9307. value=0.97000003;
  9308. };
  9309. };
  9310. };
  9311. nAttributes=2;
  9312. };
  9313. };
  9314. };
  9315. class Attributes
  9316. {
  9317. };
  9318. id=612;
  9319. atlOffset=-7.6293945e-06;
  9320. };
  9321. class Item53
  9322. {
  9323. dataType="Group";
  9324. side="Civilian";
  9325. class Entities
  9326. {
  9327. items=1;
  9328. class Item0
  9329. {
  9330. dataType="Object";
  9331. class PositionInfo
  9332. {
  9333. position[]={8471.8867,87.826546,25263.867};
  9334. };
  9335. side="Civilian";
  9336. flags=7;
  9337. class Attributes
  9338. {
  9339. skill=1;
  9340. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9341. isPlayable=1;
  9342. };
  9343. id=615;
  9344. type="C_man_p_beggar_F";
  9345. atlOffset=-7.6293945e-06;
  9346. class CustomAttributes
  9347. {
  9348. class Attribute0
  9349. {
  9350. property="speaker";
  9351. expression="_this setspeaker _value;";
  9352. class Value
  9353. {
  9354. class data
  9355. {
  9356. singleType="STRING";
  9357. value="Male03GRE";
  9358. };
  9359. };
  9360. };
  9361. class Attribute1
  9362. {
  9363. property="pitch";
  9364. expression="_this setpitch _value;";
  9365. class Value
  9366. {
  9367. class data
  9368. {
  9369. singleType="SCALAR";
  9370. value=0.97000003;
  9371. };
  9372. };
  9373. };
  9374. nAttributes=2;
  9375. };
  9376. };
  9377. };
  9378. class Attributes
  9379. {
  9380. };
  9381. id=614;
  9382. atlOffset=-7.6293945e-06;
  9383. };
  9384. class Item54
  9385. {
  9386. dataType="Group";
  9387. side="Civilian";
  9388. class Entities
  9389. {
  9390. items=1;
  9391. class Item0
  9392. {
  9393. dataType="Object";
  9394. class PositionInfo
  9395. {
  9396. position[]={8473.2471,87.600647,25265.646};
  9397. };
  9398. side="Civilian";
  9399. flags=7;
  9400. class Attributes
  9401. {
  9402. skill=1;
  9403. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9404. isPlayable=1;
  9405. };
  9406. id=617;
  9407. type="C_man_p_beggar_F";
  9408. atlOffset=-7.6293945e-06;
  9409. class CustomAttributes
  9410. {
  9411. class Attribute0
  9412. {
  9413. property="speaker";
  9414. expression="_this setspeaker _value;";
  9415. class Value
  9416. {
  9417. class data
  9418. {
  9419. singleType="STRING";
  9420. value="Male03GRE";
  9421. };
  9422. };
  9423. };
  9424. class Attribute1
  9425. {
  9426. property="pitch";
  9427. expression="_this setpitch _value;";
  9428. class Value
  9429. {
  9430. class data
  9431. {
  9432. singleType="SCALAR";
  9433. value=0.97000003;
  9434. };
  9435. };
  9436. };
  9437. nAttributes=2;
  9438. };
  9439. };
  9440. };
  9441. class Attributes
  9442. {
  9443. };
  9444. id=616;
  9445. atlOffset=-7.6293945e-06;
  9446. };
  9447. class Item55
  9448. {
  9449. dataType="Group";
  9450. side="Civilian";
  9451. class Entities
  9452. {
  9453. items=1;
  9454. class Item0
  9455. {
  9456. dataType="Object";
  9457. class PositionInfo
  9458. {
  9459. position[]={8471.5889,87.45697,25265.756};
  9460. };
  9461. side="Civilian";
  9462. flags=7;
  9463. class Attributes
  9464. {
  9465. skill=1;
  9466. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9467. isPlayable=1;
  9468. };
  9469. id=619;
  9470. type="C_man_p_beggar_F";
  9471. atlOffset=-7.6293945e-06;
  9472. class CustomAttributes
  9473. {
  9474. class Attribute0
  9475. {
  9476. property="speaker";
  9477. expression="_this setspeaker _value;";
  9478. class Value
  9479. {
  9480. class data
  9481. {
  9482. singleType="STRING";
  9483. value="Male03GRE";
  9484. };
  9485. };
  9486. };
  9487. class Attribute1
  9488. {
  9489. property="pitch";
  9490. expression="_this setpitch _value;";
  9491. class Value
  9492. {
  9493. class data
  9494. {
  9495. singleType="SCALAR";
  9496. value=0.97000003;
  9497. };
  9498. };
  9499. };
  9500. nAttributes=2;
  9501. };
  9502. };
  9503. };
  9504. class Attributes
  9505. {
  9506. };
  9507. id=618;
  9508. atlOffset=-7.6293945e-06;
  9509. };
  9510. class Item56
  9511. {
  9512. dataType="Group";
  9513. side="Civilian";
  9514. class Entities
  9515. {
  9516. items=1;
  9517. class Item0
  9518. {
  9519. dataType="Object";
  9520. class PositionInfo
  9521. {
  9522. position[]={8473.0645,87.330925,25267.039};
  9523. };
  9524. side="Civilian";
  9525. flags=7;
  9526. class Attributes
  9527. {
  9528. skill=1;
  9529. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9530. isPlayable=1;
  9531. };
  9532. id=621;
  9533. type="C_man_p_beggar_F";
  9534. atlOffset=-7.6293945e-06;
  9535. class CustomAttributes
  9536. {
  9537. class Attribute0
  9538. {
  9539. property="speaker";
  9540. expression="_this setspeaker _value;";
  9541. class Value
  9542. {
  9543. class data
  9544. {
  9545. singleType="STRING";
  9546. value="Male03GRE";
  9547. };
  9548. };
  9549. };
  9550. class Attribute1
  9551. {
  9552. property="pitch";
  9553. expression="_this setpitch _value;";
  9554. class Value
  9555. {
  9556. class data
  9557. {
  9558. singleType="SCALAR";
  9559. value=0.97000003;
  9560. };
  9561. };
  9562. };
  9563. nAttributes=2;
  9564. };
  9565. };
  9566. };
  9567. class Attributes
  9568. {
  9569. };
  9570. id=620;
  9571. atlOffset=-7.6293945e-06;
  9572. };
  9573. class Item57
  9574. {
  9575. dataType="Group";
  9576. side="Civilian";
  9577. class Entities
  9578. {
  9579. items=1;
  9580. class Item0
  9581. {
  9582. dataType="Object";
  9583. class PositionInfo
  9584. {
  9585. position[]={8471.2471,87.119759,25267.449};
  9586. };
  9587. side="Civilian";
  9588. flags=7;
  9589. class Attributes
  9590. {
  9591. skill=1;
  9592. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9593. isPlayable=1;
  9594. };
  9595. id=623;
  9596. type="C_man_p_beggar_F";
  9597. atlOffset=-7.6293945e-06;
  9598. class CustomAttributes
  9599. {
  9600. class Attribute0
  9601. {
  9602. property="speaker";
  9603. expression="_this setspeaker _value;";
  9604. class Value
  9605. {
  9606. class data
  9607. {
  9608. singleType="STRING";
  9609. value="Male03GRE";
  9610. };
  9611. };
  9612. };
  9613. class Attribute1
  9614. {
  9615. property="pitch";
  9616. expression="_this setpitch _value;";
  9617. class Value
  9618. {
  9619. class data
  9620. {
  9621. singleType="SCALAR";
  9622. value=0.97000003;
  9623. };
  9624. };
  9625. };
  9626. nAttributes=2;
  9627. };
  9628. };
  9629. };
  9630. class Attributes
  9631. {
  9632. };
  9633. id=622;
  9634. atlOffset=-7.6293945e-06;
  9635. };
  9636. class Item58
  9637. {
  9638. dataType="Group";
  9639. side="Civilian";
  9640. class Entities
  9641. {
  9642. items=1;
  9643. class Item0
  9644. {
  9645. dataType="Object";
  9646. class PositionInfo
  9647. {
  9648. position[]={8472.8467,87.124672,25268.473};
  9649. };
  9650. side="Civilian";
  9651. flags=7;
  9652. class Attributes
  9653. {
  9654. skill=1;
  9655. init="if (!isPlayer this) then { this hideObjectGlobal true; this enableSimulationGlobal false; };";
  9656. isPlayable=1;
  9657. };
  9658. id=625;
  9659. type="C_man_p_beggar_F";
  9660. atlOffset=-7.6293945e-06;
  9661. class CustomAttributes
  9662. {
  9663. class Attribute0
  9664. {
  9665. property="speaker";
  9666. expression="_this setspeaker _value;";
  9667. class Value
  9668. {
  9669. class data
  9670. {
  9671. singleType="STRING";
  9672. value="Male03GRE";
  9673. };
  9674. };
  9675. };
  9676. class Attribute1
  9677. {
  9678. property="pitch";
  9679. expression="_this setpitch _value;";
  9680. class Value
  9681. {
  9682. class data
  9683. {
  9684. singleType="SCALAR";
  9685. value=0.97000003;
  9686. };
  9687. };
  9688. };
  9689. nAttributes=2;
  9690. };
  9691. };
  9692. };
  9693. class Attributes
  9694. {
  9695. };
  9696. id=624;
  9697. atlOffset=-7.6293945e-06;
  9698. };
  9699. };
  9700. };
  9701.  
  9702.  
  9703. ==================== END OF: mission.sqm ====================
  9704.  
  9705.  
  9706.  
  9707. ==================== START OF: mission_setup.sqf ====================
  9708.  
  9709. // mission_setup.sqf
  9710. // Contains core mission initialization logic and main game loops.
  9711.  
  9712. // REVISED: The SITREP function is now simplified to report team points every 5 minutes.
  9713. fnc_broadcastSitRep = {
  9714. while {true} do {
  9715. sleep 300; // Report every 5 minutes
  9716.  
  9717. private _bluforPoints = round (missionNamespace getVariable ["BLUFOR_POINTS", 0]);
  9718. private _opforPoints = round (missionNamespace getVariable ["OPFOR_POINTS", 0]);
  9719.  
  9720. systemChat format ["[SITREP] Team Points | BLUFOR: %1 | OPFOR: %2", _bluforPoints, _opforPoints];
  9721. };
  9722. };
  9723.  
  9724. fnc_isPositionValidForBase = {
  9725. params ["_pos"];
  9726.  
  9727. // Check 1: Must not be in water.
  9728. if (surfaceIsWater _pos) exitWith {false};
  9729.  
  9730. // Check 2: Ground must not be too steep - made stricter for tower base access.
  9731. private _normal = surfaceNormal [(_pos select 0), (_pos select 1)];
  9732. if ((_normal select 2) < 0.98) exitWith {false};
  9733.  
  9734. // Check 3: IMPROVED - Must not be too close to the coast.
  9735. // Test multiple distances and more directions to ensure no water nearby
  9736. private _exclusionRadius = COAST_EXCLUSION_RADIUS;
  9737.  
  9738. // Check 16 directions instead of 8 for better coverage
  9739. for "_angle" from 0 to 337.5 step 22.5 do {
  9740. // Check at multiple distances from center outward
  9741. for "_dist" from 20 to _exclusionRadius step 20 do {
  9742. private _checkPos = _pos getPos [_dist, _angle];
  9743. if (surfaceIsWater _checkPos) exitWith {false};
  9744. };
  9745. };
  9746.  
  9747. // Additional concentric ring check at the exact exclusion radius
  9748. for "_angle" from 0 to 360 step 15 do {
  9749. private _checkPos = _pos getPos [_exclusionRadius, _angle];
  9750. if (surfaceIsWater _checkPos) exitWith {false};
  9751. };
  9752.  
  9753. // NEW Check 4: Ensure the entire 600m base marker radius is clear of water
  9754. // This is the radius used for the base area marker in fnc_createRandomizedBases
  9755. private _baseMarkerRadius = 600;
  9756.  
  9757. // Check perimeter of the base circle at 600m radius (24 points around the circle)
  9758. for "_angle" from 0 to 345 step 15 do {
  9759. private _perimeterPos = _pos getPos [_baseMarkerRadius, _angle];
  9760. if (surfaceIsWater _perimeterPos) exitWith {false};
  9761. };
  9762.  
  9763. // Check intermediate rings within the base circle to ensure no water inside
  9764. // Check at 200m, 400m, and 600m radius
  9765. for "_radius" from 200 to 600 step 200 do {
  9766. for "_angle" from 0 to 330 step 30 do {
  9767. private _ringPos = _pos getPos [_radius, _angle];
  9768. if (surfaceIsWater _ringPos) exitWith {false};
  9769. };
  9770. };
  9771.  
  9772. // Check 5: Must not have terrain objects (trees, rocks, etc.) within 20 meters.
  9773. private _terrainObjects = nearestTerrainObjects [_pos, ["TREE", "SMALL TREE", "BUSH", "ROCK", "ROCKS"], 20];
  9774. if (count _terrainObjects > 0) exitWith {false};
  9775.  
  9776. // Check 6: Must not have buildings or structures within 20 meters.
  9777. private _nearbyObjects = nearestObjects [_pos, ["Building", "House", "Wall", "Fence"], 20];
  9778. if (count _nearbyObjects > 0) exitWith {false};
  9779.  
  9780. // Check 7: Get ALL objects within 20 meters and filter out harmless ones.
  9781. private _allObjects = nearestObjects [_pos, [], 20];
  9782. private _blockingObjects = _allObjects select {
  9783. !(typeOf _x in ["Logic", "EmptyDetector"]) &&
  9784. !(_x isKindOf "Man") &&
  9785. !(_x isKindOf "Air") &&
  9786. !(_x isKindOf "WeaponHolder")
  9787. };
  9788. if (count _blockingObjects > 0) exitWith {false};
  9789.  
  9790. // If all checks pass, the position is valid.
  9791. true
  9792. };
  9793.  
  9794. // MODIFIED: Robust base placement to ensure balanced, opposing starting locations.
  9795. fnc_createRandomizedBases = {
  9796. if (!isServer) exitWith {};
  9797.  
  9798. // Ensure a default value is set if the variable isn't defined in init.sqf
  9799. if (isNil "RANDOMIZE_BASE_LOCATIONS") then { RANDOMIZE_BASE_LOCATIONS = 1; };
  9800.  
  9801. private _bluforPos = [];
  9802. private _opforPos = [];
  9803.  
  9804. if (RANDOMIZE_BASE_LOCATIONS == 0) then {
  9805. // --- MARKER-BASED BASE LOCATIONS ---
  9806. _bluforPos = markerPos "basemarker1";
  9807. _opforPos = markerPos "basemarker2";
  9808.  
  9809. private _centralPoint = [((_bluforPos select 0) + (_opforPos select 0)) / 2, ((_bluforPos select 1) + (_opforPos select 1)) / 2, 0];
  9810. missionNamespace setVariable ["mission_centralPoint", _centralPoint, true];
  9811.  
  9812. if (_bluforPos isEqualTo [0,0,0] || _opforPos isEqualTo [0,0,0]) then {
  9813. systemChat "ERROR: 'basemarker1' or 'basemarker2' not found. Mission cannot start correctly. Please place these markers in the editor.";
  9814. };
  9815. } else {
  9816. // --- NEW MULTI-STAGE RANDOMIZED BASE LOGIC ---
  9817. private _layoutFound = false;
  9818.  
  9819. // Step 1: Find all MEDIUM AND LARGE CITIES ONLY (no villages) to serve as potential battle locations
  9820. private _allTowns = nearestLocations [
  9821. getArray (configFile >> "CfgWorlds" >> worldName >> "centerPosition"),
  9822. ["NameCity", "NameCityCapital"],
  9823. worldSize
  9824. ];
  9825.  
  9826. // Shuffle the list of all towns to ensure variety in each session.
  9827. _allTowns = _allTowns call BIS_fnc_arrayShuffle;
  9828.  
  9829. // Helper function to find a valid spot within a search arc
  9830. private _fnc_findValidSpotInArc = {
  9831. params ["_center", "_distance", "_baseAngle"];
  9832. private _foundPos = [];
  9833. // Search in a 90-degree arc for a valid spot - INCREASED attempts from 15 to 30
  9834. for "_i" from 0 to 30 do {
  9835. private _testAngle = _baseAngle + (random 90) - 45;
  9836. private _potentialPos = _center getPos [_distance, _testAngle];
  9837. // NOW ACTUALLY USE THE VALIDATION FUNCTION
  9838. if ([_potentialPos] call fnc_isPositionValidForBase) exitWith {
  9839. _foundPos = _potentialPos;
  9840. };
  9841. };
  9842. _foundPos
  9843. };
  9844.  
  9845. // Step 2: Iterate through the shuffled list of all towns and try to build a valid layout around them.
  9846. {
  9847. private _centralCity = _x;
  9848. private _centralPos = position _centralCity;
  9849.  
  9850. // Skip if the central city itself is too close to water
  9851. if (surfaceIsWater _centralPos) then { continue; };
  9852.  
  9853. // Try 20 different conflict axes for the current town (INCREASED from 10)
  9854. for "_i" from 1 to 20 do {
  9855. // The distance is now randomized for each placement attempt.
  9856. // 1000m base + (100 to 700m random) = 1100m to 1700m total distance.
  9857. private _baseDistance = 1400 + (random 300);
  9858.  
  9859. private _conflictAxis = random 360;
  9860.  
  9861. // Find a spot for BLUFOR
  9862. private _potentialBluforPos = [_centralPos, _baseDistance, _conflictAxis] call _fnc_findValidSpotInArc;
  9863.  
  9864. if (count _potentialBluforPos > 0) then {
  9865. // Find a spot for OPFOR on the opposite side
  9866. private _potentialOpforPos = [_centralPos, _baseDistance, _conflictAxis + 180] call _fnc_findValidSpotInArc;
  9867.  
  9868. if (count _potentialOpforPos > 0) then {
  9869. // DOUBLE CHECK both positions are valid
  9870. if ([_potentialBluforPos] call fnc_isPositionValidForBase && [_potentialOpforPos] call fnc_isPositionValidForBase) then {
  9871. // SUCCESS! We have a valid town and two opposing bases.
  9872. _bluforPos = _potentialBluforPos;
  9873. _opforPos = _potentialOpforPos;
  9874. missionNamespace setVariable ["mission_centralPoint", _centralPos, true];
  9875. _layoutFound = true;
  9876. break; // Exit the conflict axis loop
  9877. };
  9878. };
  9879. };
  9880. };
  9881.  
  9882. if (_layoutFound) then {
  9883. break; // Exit the town iteration loop
  9884. };
  9885. } forEach _allTowns; // This now iterates through the full, shuffled list.
  9886. };
  9887.  
  9888. // --- COMMON BASE CREATION LOGIC ---
  9889.  
  9890. // Emergency fallback if the entire random placement process fails
  9891. if (count _bluforPos == 0 || count _opforPos == 0) then {
  9892. _bluforPos = markerPos "basemarker1";
  9893. _opforPos = markerPos "basemarker2";
  9894. private _centralPoint = [((_bluforPos select 0) + (_opforPos select 0)) / 2, ((_bluforPos select 1) + (_opforPos select 1)) / 2, 0];
  9895. missionNamespace setVariable ["mission_centralPoint", _centralPoint, true];
  9896. };
  9897.  
  9898. // Create BLUFOR spawn object
  9899. bluforSpawnObj = createVehicle ["Land_Cargo_Tower_V1_F", _bluforPos, [], 0, "NONE"];
  9900. bluforSpawnObj allowDamage false;
  9901. bluforSpawnObj enableSimulationGlobal false;
  9902. bluforSpawnObj setVectorUp [0,0,1];
  9903.  
  9904. private _bluforGroundPos = getPosATL bluforSpawnObj;
  9905. _bluforGroundPos set [2, 0];
  9906. bluforSpawnObj setPosATL _bluforGroundPos;
  9907. publicVariable "bluforSpawnObj";
  9908.  
  9909. // Create OPFOR spawn object
  9910. opforSpawnObj = createVehicle ["Land_Cargo_Tower_V3_F", _opforPos, [], 0, "NONE"];
  9911. opforSpawnObj allowDamage false;
  9912. opforSpawnObj enableSimulationGlobal false;
  9913. opforSpawnObj setVectorUp [0,0,1];
  9914.  
  9915. private _opforGroundPos = getPosATL opforSpawnObj;
  9916. _opforGroundPos set [2, 0];
  9917. opforSpawnObj setPosATL _opforGroundPos;
  9918. publicVariable "opforSpawnObj";
  9919.  
  9920. // Create BLUFOR Base Area Marker with a randomized offset
  9921. private _bluforMarkerCenter = _bluforPos getPos [random 300, random 360];
  9922. private _bluforMarker = createMarker ["blufor_base_area", _bluforMarkerCenter];
  9923. _bluforMarker setMarkerShape "ELLIPSE";
  9924. _bluforMarker setMarkerSize [600, 600];
  9925. _bluforMarker setMarkerType "mil_unknown";
  9926. _bluforMarker setMarkerColor "ColorBlue";
  9927. _bluforMarker setMarkerBrush "Border";
  9928. _bluforMarker setMarkerText "";
  9929.  
  9930. // Create OPFOR Base Area Marker with a randomized offset
  9931. private _opforMarkerCenter = _opforPos getPos [random 300, random 360];
  9932. private _opforMarker = createMarker ["opfor_base_area", _opforMarkerCenter];
  9933. _opforMarker setMarkerShape "ELLIPSE";
  9934. _opforMarker setMarkerSize [600, 600];
  9935. _opforMarker setMarkerType "mil_unknown";
  9936. _opforMarker setMarkerColor "ColorRed";
  9937. _opforMarker setMarkerBrush "Border";
  9938. _opforMarker setMarkerText "";
  9939. };
  9940.  
  9941. fnc_decorateFOB = {
  9942. params ["_baseCenterObj", "_side"];
  9943.  
  9944. private _basePos = getPosATL _baseCenterObj;
  9945.  
  9946. private _sideFlag = if (_side == west) then { "Flag_NATO_F" } else { "Flag_CSAT_F" };
  9947. private _sideCamoNet = if (_side == west) then { "Land_CamoNet_BLUFOR_F" } else { "Land_CamoNet_OPFOR_F" };
  9948. private _sideTower = if (_side == west) then { "Land_Cargo_Patrol_V4_F" } else { "Land_Cargo_Patrol_V3_F" };
  9949.  
  9950. // Calculate direction to enemy base for barrier placement
  9951. private _enemyBasePos = if (_side == west) then {
  9952. if (!isNil "opforSpawnObj") then {getPos opforSpawnObj} else {[0,0,0]}
  9953. } else {
  9954. if (!isNil "bluforSpawnObj") then {getPos bluforSpawnObj} else {[0,0,0]}
  9955. };
  9956.  
  9957. private _dirToEnemy = _basePos getDir _enemyBasePos;
  9958.  
  9959. // Create square wall perimeter around tower with gaps
  9960. private _wallDistance = 15; // Distance from center to walls
  9961. private _wallSegments = [
  9962. // North wall (2 segments with gap in middle)
  9963. [[-_wallDistance, _wallDistance, 0], 0, "Land_HBarrierWall6_F"],
  9964. [[_wallDistance * 0.5, _wallDistance, 0], 0, "Land_HBarrierWall6_F"],
  9965.  
  9966. // South wall (2 segments with gap in middle)
  9967. [[-_wallDistance, -_wallDistance, 0], 180, "Land_HBarrierWall6_F"],
  9968. [[_wallDistance * 0.5, -_wallDistance, 0], 180, "Land_HBarrierWall6_F"],
  9969.  
  9970. // East wall (2 segments with gap in middle)
  9971. [[_wallDistance, -_wallDistance * 0.5, 0], 90, "Land_HBarrierWall6_F"],
  9972. [[_wallDistance, _wallDistance * 0.5, 0], 90, "Land_HBarrierWall4_F"],
  9973.  
  9974. // West wall (2 segments with gap in middle)
  9975. [[-_wallDistance, -_wallDistance * 0.5, 0], 270, "Land_HBarrierWall6_F"],
  9976. [[-_wallDistance, _wallDistance * 0.5, 0], 270, "Land_HBarrierWall4_F"]
  9977. ];
  9978.  
  9979. // Create the wall segments
  9980. {
  9981. private _relPos = _x select 0;
  9982. private _dir = _x select 1;
  9983. private _className = _x select 2;
  9984.  
  9985. private _worldPos = _basePos vectorAdd _relPos;
  9986. private _wall = createVehicle [_className, _worldPos, [], 0, "NONE"];
  9987.  
  9988. private _wallGroundPos = getPosATL _wall;
  9989. _wallGroundPos set [2, 0];
  9990. _wall setPosATL _wallGroundPos;
  9991. _wall setDir _dir;
  9992. _wall allowDamage true;
  9993. _wall enableSimulationGlobal false;
  9994. _wall setVectorUp [0,0,1];
  9995. } forEach _wallSegments;
  9996.  
  9997. // Create defensive barriers facing enemy base (30 meters from tower)
  9998. private _barrierDistance = 30;
  9999. private _barrierCount = 5; // Number of barriers in the arc
  10000. private _arcWidth = 120; // Total arc width in degrees
  10001.  
  10002. for "_i" from 0 to (_barrierCount - 1) do {
  10003. // Calculate angle for each barrier in the arc
  10004. private _angleOffset = -(_arcWidth / 2) + (_i * (_arcWidth / (_barrierCount - 1)));
  10005. private _barrierDir = _dirToEnemy + _angleOffset;
  10006.  
  10007. // Calculate position for barrier
  10008. private _barrierPos = _basePos getPos [_barrierDistance, _barrierDir];
  10009.  
  10010. // Create the barrier
  10011. private _barrier = createVehicle ["Land_HBarrierBig_F", _barrierPos, [], 0, "NONE"];
  10012.  
  10013. private _barrierGroundPos = getPosATL _barrier;
  10014. _barrierGroundPos set [2, 0];
  10015. _barrier setPosATL _barrierGroundPos;
  10016. _barrier setDir (_barrierDir); // Face towards enemy
  10017. _barrier allowDamage true;
  10018. _barrier enableSimulationGlobal false;
  10019. _barrier setVectorUp [0,0,1];
  10020. };
  10021.  
  10022. // Additional corner reinforcement barriers
  10023. private _cornerBarriers = [
  10024. [_barrierDistance, _dirToEnemy - 70],
  10025. [_barrierDistance, _dirToEnemy + 70]
  10026. ];
  10027.  
  10028. {
  10029. private _distance = _x select 0;
  10030. private _dir = _x select 1;
  10031.  
  10032. private _pos = _basePos getPos [_distance, _dir];
  10033. private _barrier = createVehicle ["Land_HBarrierWall_corner_F", _pos, [], 0, "NONE"];
  10034.  
  10035. private _groundPos = getPosATL _barrier;
  10036. _groundPos set [2, 0];
  10037. _barrier setPosATL _groundPos;
  10038. _barrier setDir _dir;
  10039. _barrier allowDamage true;
  10040. _barrier enableSimulationGlobal false;
  10041. _barrier setVectorUp [0,0,1];
  10042. } forEach _cornerBarriers;
  10043.  
  10044. // Fix watchtower positioning: Create at position, then place on ground
  10045. private _towerPosRel = [-70, -70, 0];
  10046. private _towerWorldPos = _basePos vectorAdd _towerPosRel;
  10047.  
  10048. private _towerObj = createVehicle [_sideTower, _towerWorldPos, [], 0, "NONE"];
  10049. _towerObj setDir 225;
  10050. _towerObj allowDamage false;
  10051. _towerObj enableSimulationGlobal false;
  10052. _towerObj setVectorUp [0,0,1];
  10053.  
  10054. private _towerGroundPos = getPosATL _towerObj;
  10055. _towerGroundPos set [2, 0];
  10056. _towerObj setPosATL _towerGroundPos;
  10057.  
  10058. private _sideName = if (_side == west) then {"BLUFOR"} else {"OPFOR"};
  10059. };
  10060.  
  10061. // Create and manage the play area restriction zone
  10062. fnc_createPlayArea = {
  10063. if (!isServer) exitWith {};
  10064.  
  10065. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  10066.  
  10067. private _bluforPos = getPos bluforSpawnObj;
  10068. private _opforPos = getPos opforSpawnObj;
  10069.  
  10070. // Calculate center point between bases
  10071. private _centerX = ((_bluforPos select 0) + (_opforPos select 0)) / 2;
  10072. private _centerY = ((_bluforPos select 1) + (_opforPos select 1)) / 2;
  10073. private _playAreaCenter = [_centerX, _centerY, 0];
  10074.  
  10075. // Calculate required radius (distance between bases / 2 + 200m buffer)
  10076. private _baseDistance = _bluforPos distance2D _opforPos;
  10077. private _playAreaRadius = (_baseDistance / 2) + 200;
  10078.  
  10079. // Store globally for reference
  10080. missionNamespace setVariable ["PLAY_AREA_CENTER", _playAreaCenter, true];
  10081. missionNamespace setVariable ["PLAY_AREA_RADIUS", _playAreaRadius, true];
  10082.  
  10083. // Create visual marker for the play area
  10084. private _marker = createMarker ["play_area_boundary", _playAreaCenter];
  10085. _marker setMarkerShape "ELLIPSE";
  10086. _marker setMarkerSize [_playAreaRadius, _playAreaRadius];
  10087. _marker setMarkerColor "ColorRed";
  10088. _marker setMarkerBrush "Border";
  10089. _marker setMarkerAlpha 0.5;
  10090.  
  10091. diag_log format ["Play Area Created - Center: %1, Radius: %2m", _playAreaCenter, round _playAreaRadius];
  10092.  
  10093. // Start player monitoring
  10094. [] spawn fnc_monitorPlayAreaPlayers;
  10095.  
  10096. // Start AI waypoint validation
  10097. [] spawn fnc_monitorPlayAreaAI;
  10098. };
  10099.  
  10100. // Monitor players and warn/kill those outside play area
  10101. fnc_monitorPlayAreaPlayers = {
  10102. if (!isServer) exitWith {};
  10103.  
  10104. waitUntil {!isNil "PLAY_AREA_CENTER" && !isNil "PLAY_AREA_RADIUS"};
  10105.  
  10106. private _center = missionNamespace getVariable "PLAY_AREA_CENTER";
  10107. private _radius = missionNamespace getVariable "PLAY_AREA_RADIUS";
  10108.  
  10109. while {true} do {
  10110. {
  10111. if (isPlayer _x && alive _x && side _x != civilian) then {
  10112. private _player = _x;
  10113. private _distance = _player distance2D _center;
  10114.  
  10115. if (_distance > _radius) then {
  10116. // Player is outside play area
  10117. private _warningTime = _player getVariable ["playAreaWarningTime", 0];
  10118.  
  10119. if (_warningTime == 0) then {
  10120. // First warning
  10121. _player setVariable ["playAreaWarningTime", time, true];
  10122. [_center, _radius] remoteExecCall ["fnc_client_showPlayAreaWarning", owner _player];
  10123. } else {
  10124. // Check if 10 seconds have passed
  10125. if (time - _warningTime >= 10) then {
  10126. // Kill the player
  10127. _player setDamage 1;
  10128. _player setVariable ["playAreaWarningTime", 0, true];
  10129. ["You were eliminated for leaving the battle area!"] remoteExecCall ["hint", owner _player];
  10130. };
  10131. };
  10132. } else {
  10133. // Player is inside, clear any warnings
  10134. if ((_player getVariable ["playAreaWarningTime", 0]) > 0) then {
  10135. _player setVariable ["playAreaWarningTime", 0, true];
  10136. [] remoteExecCall ["fnc_client_hidePlayAreaWarning", owner _player];
  10137. };
  10138. };
  10139. };
  10140. } forEach allPlayers;
  10141.  
  10142. sleep 1;
  10143. };
  10144. };
  10145.  
  10146. // Monitor AI groups and prevent waypoints outside play area
  10147. fnc_monitorPlayAreaAI = {
  10148. if (!isServer) exitWith {};
  10149.  
  10150. waitUntil {!isNil "PLAY_AREA_CENTER" && !isNil "PLAY_AREA_RADIUS" && !isNil "mission_centralPoint"};
  10151.  
  10152. private _center = missionNamespace getVariable "PLAY_AREA_CENTER";
  10153. private _radius = missionNamespace getVariable "PLAY_AREA_RADIUS";
  10154. private _strongholdPos = missionNamespace getVariable "mission_centralPoint";
  10155.  
  10156. while {true} do {
  10157. {
  10158. private _group = _x;
  10159. if (!isNull _group && count (units _group) > 0 && {!isPlayer leader _group}) then {
  10160. private _leader = leader _group;
  10161.  
  10162. // Skip if group is in helicopters (exception for air units)
  10163. private _isAirUnit = false;
  10164. if (!isNull _leader && vehicle _leader != _leader) then {
  10165. if ((vehicle _leader) isKindOf "Air") then {
  10166. _isAirUnit = true;
  10167. };
  10168. };
  10169.  
  10170. if (!_isAirUnit) then {
  10171. // Check all waypoints
  10172. private _waypoints = waypoints _group;
  10173. {
  10174. private _wpPos = waypointPosition _x;
  10175. if (count _wpPos >= 2 && !(_wpPos isEqualTo [0,0,0])) then {
  10176. private _wpDistance = _wpPos distance2D _center;
  10177. if (_wpDistance > _radius) then {
  10178. // Waypoint is outside, delete it
  10179. deleteWaypoint _x;
  10180. };
  10181. };
  10182. } forEach _waypoints;
  10183.  
  10184. // If group leader is outside, teleport them back
  10185. if (!isNull _leader && alive _leader) then {
  10186. private _leaderDistance = _leader distance2D _center;
  10187. if (_leaderDistance > _radius) then {
  10188. // Find a position back inside the play area
  10189. private _directionToCenter = _leader getDir _center;
  10190. private _safeDistance = _radius - 50;
  10191. private _safePos = _center getPos [_safeDistance, _directionToCenter + 180];
  10192.  
  10193. // Teleport entire group
  10194. {
  10195. if (alive _x) then {
  10196. _x setPos _safePos;
  10197. };
  10198. } forEach (units _group);
  10199.  
  10200. // NEW: Clear old tasks and give a new one to prevent repeated wandering.
  10201. while {count (waypoints _group) > 0} do {
  10202. deleteWaypoint ((waypoints _group) select 0);
  10203. };
  10204.  
  10205. // Give them a new task to move towards the center battlefield.
  10206. private _wp = _group addWaypoint [_strongholdPos, 0];
  10207. _wp setWaypointType "SAD"; // Search and Destroy
  10208. _wp setWaypointBehaviour "COMBAT";
  10209. _wp setWaypointCompletionRadius 200;
  10210.  
  10211. // Apply a 60-second HC lock to this new task.
  10212. _group setVariable ["HC_FORCED_LOCK", true];
  10213. [_group] spawn {
  10214. params ["_grp"];
  10215. sleep 60;
  10216. if (!isNull _grp) then {
  10217. _grp setVariable ["HC_FORCED_LOCK", false];
  10218. };
  10219. };
  10220. };
  10221. };
  10222. };
  10223. };
  10224. } forEach allGroups;
  10225.  
  10226. sleep 3;
  10227. };
  10228. };
  10229.  
  10230. // Player capture related functions
  10231. fnc_captureInProgress = { params ["_unit"]; _unit getVariable ["capturing", false] };
  10232.  
  10233. fnc_startCapture = {
  10234. params ["_unit", "_targetBase", "_targetSide"];
  10235. if ([_unit] call fnc_captureInProgress) exitWith { systemChat "Already capturing!"; };
  10236. _unit setVariable ["capturing", true];
  10237. _unit setVariable ["captureProgress", 0];
  10238. private _captureStartTime = time;
  10239.  
  10240. [_unit, _targetBase, _targetSide, _captureStartTime] spawn {
  10241. params ["_unit", "_targetBase", "_targetSide", "_startTime"];
  10242. while {alive _unit && [_unit] call fnc_captureInProgress && (_unit distance _targetBase) < 5} do {
  10243. private _progress = ((time - _startTime) / 60) * 100;
  10244. _unit setVariable ["captureProgress", _progress];
  10245. hintSilent format ["Capturing %1 Base: %2%3", _targetSide, round _progress, "%"];
  10246. if (_progress >= 100) exitWith {
  10247. _unit setVariable ["capturing", false];
  10248. hint "";
  10249. private _winnerSide = if (side _unit == west) then {"BLUFOR"} else {"OPFOR"};
  10250. private _loserSide = if (_targetSide == "OPFOR") then {"OPFOR"} else {"BLUFOR"};
  10251. // ==================== CHANGE START ====================
  10252. // Use the new centralized function to end the round.
  10253. private _reason = format ["%1 has captured the %2 base!", _winnerSide, _loserSide];
  10254. [_winnerSide, _loserSide, _reason] call fnc_endRound;
  10255. // ===================== CHANGE END =====================
  10256. };
  10257. sleep 0.1;
  10258. };
  10259. _unit setVariable ["capturing", false];
  10260. _unit setVariable ["captureProgress", 0];
  10261. hint "";
  10262. if (!alive _unit) then {
  10263. systemChat "Capture failed - capturer died!";
  10264. } else {
  10265. if ((_unit distance _targetBase) >= 5) then {
  10266. systemChat "Capture failed - moved too far from base!";
  10267. };
  10268. };
  10269. };
  10270. };
  10271.  
  10272. fnc_cancelCapture = { params ["_unit"]; _unit setVariable ["capturing", false]; _unit setVariable ["captureProgress", 0]; hint ""; systemChat "Capture cancelled!"; };
  10273.  
  10274. fnc_setupCaptureActions = {
  10275. if (!hasInterface) exitWith {};
  10276. [] spawn {
  10277. waitUntil {!isNull player};
  10278. sleep 1;
  10279. private _captureActionBlufor = -1;
  10280. private _captureActionOpfor = -1;
  10281. private _cancelActionID = -1;
  10282. while {true} do {
  10283. private _playerSide = side player;
  10284. private _nearOpforBase = (player distance opforSpawnObj) < 5;
  10285. private _nearBluforBase = (player distance bluforSpawnObj) < 5;
  10286.  
  10287. if (_playerSide == west && _nearOpforBase && _captureActionOpfor == -1 && !([player] call fnc_captureInProgress)) then {
  10288. _captureActionOpfor = player addAction [
  10289. "<t color='#FF0000'>Capture OPFOR Base</t>",
  10290. {[_this select 0, opforSpawnObj, "OPFOR"] call fnc_startCapture;},
  10291. nil, 10, true, true, "", "true", 5
  10292. ];
  10293. };
  10294. if (_playerSide == west && (!_nearOpforBase || [player] call fnc_captureInProgress) && _captureActionOpfor != -1) then {
  10295. player removeAction _captureActionOpfor; _captureActionOpfor = -1;
  10296. };
  10297.  
  10298. if (_playerSide == east && _nearBluforBase && _captureActionBlufor == -1 && !([player] call fnc_captureInProgress)) then {
  10299. _captureActionBlufor = player addAction [
  10300. "<t color='#0000FF'>Capture BLUFOR Base</t>",
  10301. {[_this select 0, bluforSpawnObj, "BLUFOR"] call fnc_startCapture;},
  10302. nil, 10, true, true, "", "true", 5
  10303. ];
  10304. };
  10305. if (_playerSide == east && (!_nearBluforBase || [player] call fnc_captureInProgress) && _captureActionBlufor != -1) then {
  10306. player removeAction _captureActionBlufor; _captureActionBlufor = -1;
  10307. };
  10308.  
  10309. if ([player] call fnc_captureInProgress && _cancelActionID == -1) then {
  10310. _cancelActionID = player addAction [
  10311. "<t color='#FFFF00'>Cancel Capture</t>",
  10312. {[_this select 0] call fnc_cancelCapture;},
  10313. nil, 11, true, true, "", "true"
  10314. ];
  10315. };
  10316. if (!([player] call fnc_captureInProgress) && _cancelActionID != -1) then {
  10317. player removeAction _cancelActionID; _cancelActionID = -1;
  10318. };
  10319. sleep 0.5;
  10320. };
  10321. };
  10322. };
  10323.  
  10324. // Function to get unit value for kill rewards
  10325. fnc_getUnitValue = {
  10326. params ["_unit"];
  10327. private "_value";
  10328. if (_unit getVariable ["isAAF", false]) then {
  10329. _value = 1;
  10330. } else {
  10331. if (_unit getVariable ["isElite", false]) then {
  10332. _value = ELITE_VALUE;
  10333. } else {
  10334. if (_unit getVariable ["isSpecOps", false]) then {
  10335. _value = SPECOPS_VALUE;
  10336. } else {
  10337. if (_unit getVariable ["isUpgraded", false]) then {
  10338. _value = UPGRADED_VALUE;
  10339. } else {
  10340. if (_unit getVariable ["isSniper", false]) then {
  10341. _value = SNIPER_VALUE;
  10342. } else {
  10343. _value = INFANTRY_VALUE;
  10344. };
  10345. };
  10346. };
  10347. };
  10348. };
  10349. _value
  10350. };
  10351.  
  10352. // ==================== CHANGE START ====================
  10353. // NEW: Centralized function to end the mission and declare a winner.
  10354. fnc_endRound = {
  10355. params ["_winnerSide", "_loserSide", "_reason"];
  10356. if (!isServer) exitWith {};
  10357.  
  10358. // Prevent this function from running more than once if multiple triggers happen at the same time.
  10359. if (missionNamespace getVariable ["missionEnding", false]) exitWith {};
  10360. missionNamespace setVariable ["missionEnding", true, true];
  10361.  
  10362. private _winnerSideName = toUpper _winnerSide;
  10363.  
  10364. private _endMessage = format ["%1 WINS! %2", _winnerSideName, _reason];
  10365. [
  10366. "EndMission",
  10367. ["", _endMessage]
  10368. ] remoteExec ["BIS_fnc_showNotification", 0]; // Show a large notification to all players.
  10369.  
  10370. private _endType = if (_winnerSide == "BLUFOR") then {"end1"} else {"end2"};
  10371.  
  10372. // End the mission after a short delay to let players see the message.
  10373. sleep 5;
  10374. [_endType, true, true] remoteExec ["BIS_fnc_endMission", 0];
  10375. };
  10376. // ===================== CHANGE END =====================
  10377.  
  10378. if (isServer) then {
  10379. // ==================== CHANGE START ====================
  10380. // Initialize the hash map for storing player team selections to prevent team switching.
  10381. missionNamespace setVariable ["PLAYER_TEAM_LOCKS", createHashMap, true];
  10382. // ===================== CHANGE END =====================
  10383.  
  10384. // Initialize randomized bases first (blocking call)
  10385. [] call fnc_createRandomizedBases;
  10386.  
  10387. // Main server-side loops and event handlers
  10388. [] spawn {
  10389. // Wait until all base objects are public and known to missionNamespace
  10390. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  10391.  
  10392. // Spawn the new SITREP broadcast loop
  10393. [] spawn fnc_broadcastSitRep;
  10394.  
  10395. // Initialize all playable units as players
  10396. { if (isPlayer _x) then { [_x] call fnc_server_initializePlayer; } } forEach playableUnits;
  10397.  
  10398. // ==================== CHANGE START ====================
  10399. // Handle players connecting after mission start, now with team lock enforcement.
  10400. addMissionEventHandler ["PlayerConnected", {
  10401. params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
  10402. if (_jip) then {
  10403. // Short delay to ensure the player's lobby unit has been created.
  10404. [_uid, _owner] spawn {
  10405. params ["_uid", "_owner"];
  10406. sleep 3; // Longer delay for JIP synchronization
  10407.  
  10408. // Check if this player has a locked team (meaning they disconnected earlier)
  10409. private _locks = missionNamespace getVariable ["PLAYER_TEAM_LOCKS", createHashMap];
  10410. private _lockedSide = _locks getOrDefault [_uid, sideUnknown];
  10411.  
  10412. if (_lockedSide != sideUnknown) then {
  10413. // Player is rejoining - create a new unit for them at base
  10414. [objNull, _lockedSide, _uid, _owner] call fnc_server_playerChooseSide;
  10415. } else {
  10416. // New JIP player - find their unit and set up team selection
  10417. private _playerUnit = objNull;
  10418. {
  10419. if (getPlayerUID _x == _uid) exitWith {
  10420. _playerUnit = _x;
  10421. };
  10422. } forEach allPlayers;
  10423.  
  10424. if (!isNull _playerUnit && side _playerUnit == civilian) then {
  10425. [_playerUnit] remoteExecCall ["fnc_client_setupTeamSelection", owner _playerUnit];
  10426. };
  10427. };
  10428. };
  10429. };
  10430. }];
  10431.  
  10432. // Handle players disconnecting - remove their unit
  10433. addMissionEventHandler ["PlayerDisconnected", {
  10434. params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
  10435.  
  10436. // Find the disconnecting player's unit by UID
  10437. private _disconnectedUnit = objNull;
  10438. {
  10439. if (getPlayerUID _x == _uid) exitWith {
  10440. _disconnectedUnit = _x;
  10441. };
  10442. } forEach allPlayers;
  10443.  
  10444. // If we found their unit, store data and delete it
  10445. if (!isNull _disconnectedUnit) then {
  10446. // Store their side if not already locked (team lock system already handles this)
  10447. private _locks = missionNamespace getVariable ["PLAYER_TEAM_LOCKS", createHashMap];
  10448. private _currentSide = side _disconnectedUnit;
  10449.  
  10450. // Only store side if they're actually on a team (not civilian)
  10451. // Fixed: Check if the key doesn't exist in the hashmap
  10452. if (_currentSide in [west, east] && {!(_uid in _locks)}) then {
  10453. _locks set [_uid, _currentSide];
  10454. missionNamespace setVariable ["PLAYER_TEAM_LOCKS", _locks, true];
  10455. };
  10456.  
  10457. // Delete the disconnected player's unit
  10458. deleteVehicle _disconnectedUnit;
  10459.  
  10460. // Log the disconnection
  10461. diag_log format ["Player %1 (UID: %2) disconnected and unit removed", _name, _uid];
  10462. };
  10463. }];
  10464. // ===================== CHANGE END =====================
  10465.  
  10466. // EntityKilled Event Handler for points and respawn penalties
  10467. addMissionEventHandler ["EntityKilled", {
  10468. params ["_victim", "_killer", "_instigator"];
  10469.  
  10470. // UPDATE STRENGTH TALLY ON DEATH
  10471. if (!isPlayer _victim) then { // We don't tally players
  10472. private _victimSide = side _victim;
  10473. private _strengthVal = _victim getVariable ["strengthValue", 0]; // Get stored value
  10474. if (_strengthVal > 0) then {
  10475. if (_victimSide == west) then {
  10476. BLUFOR_STRENGTH = (BLUFOR_STRENGTH - _strengthVal) max 0; // max 0 prevents negative scores
  10477. } else {
  10478. OPFOR_STRENGTH = (OPFOR_STRENGTH - _strengthVal) max 0;
  10479. };
  10480. };
  10481. };
  10482.  
  10483. private _actualKiller = if (!isNull _killer) then { _killer } else { _instigator };
  10484.  
  10485. if (isNull _actualKiller || {_actualKiller == _victim}) exitWith {};
  10486.  
  10487. private _killerGroup = group _actualKiller;
  10488. private _victimGroup = group _victim;
  10489.  
  10490. if (isNull _killerGroup || isNull _victimGroup) exitWith {};
  10491.  
  10492. private _killerSide = side _killerGroup;
  10493. private _victimSide = side _victimGroup;
  10494.  
  10495. if (_killerSide == _victimSide) then {
  10496. if (isPlayer _actualKiller && _victim isKindOf "CAManBase") then {
  10497. private _currentPoints = _actualKiller getVariable ["playerPoints", 0];
  10498. private _newPoints = (_currentPoints - 5) max 0;
  10499. _actualKiller setVariable ["playerPoints", _newPoints, true];
  10500. private _tkCount = _actualKiller getVariable ["teamkillCount", 0];
  10501. _tkCount = _tkCount + 1;
  10502. _actualKiller setVariable ["teamkillCount", _tkCount, true];
  10503.  
  10504. if (_tkCount >= 3) then {
  10505. _actualKiller setDamage 1;
  10506. _actualKiller setVariable ["teamkillCount", 0, true];
  10507. _actualKiller setVariable ["penaltyRespawn", true, true];
  10508. systemChat format ["%1 has been executed for excessive teamkilling.", name _actualKiller];
  10509. } else {
  10510. [format["TEAMKILL! (%1/3). -5 Points. You now have %2 points.", _tkCount, _newPoints]] remoteExecCall ["hint", owner _actualKiller];
  10511. };
  10512. };
  10513. } else {
  10514. private _victimValue = if (_victim isKindOf "CAManBase") then {
  10515. [_victim] call fnc_getUnitValue;
  10516. } else {
  10517. _victim getVariable ["unitValue", 0];
  10518. };
  10519.  
  10520. if (_killerSide == west) then {
  10521. BLUFOR_POINTS = BLUFOR_POINTS + _victimValue; publicVariable "BLUFOR_POINTS";
  10522. } else {
  10523. if (_killerSide == east) then {
  10524. OPFOR_POINTS = OPFOR_POINTS + _victimValue; publicVariable "OPFOR_POINTS";
  10525. };
  10526. };
  10527.  
  10528. private _pointRecipient = objNull;
  10529. if (isPlayer _actualKiller) then {
  10530. _pointRecipient = _actualKiller;
  10531. } else {
  10532. // Check if the AI killer is owned by a player
  10533. private _owner = _actualKiller getVariable ["ownerPlayer", objNull];
  10534. if (!isNull _owner && isPlayer _owner && alive _owner) then {
  10535. _pointRecipient = _owner;
  10536. };
  10537. };
  10538.  
  10539. // If we have a valid player to give points to (either the killer or the owner)
  10540. if (!isNull _pointRecipient) then {
  10541. private _currentPoints = _pointRecipient getVariable ["playerPoints", 0];
  10542.  
  10543. // ==================== CHANGE START ====================
  10544. private _pointsAwarded = 0;
  10545. if (_victim isKindOf "CAManBase") then {
  10546. _pointsAwarded = 1;
  10547. } else {
  10548. // Award 10 points for tanks (includes APCs) and all air vehicles
  10549. if (_victim isKindOf "Tank" || _victim isKindOf "Air") then {
  10550. _pointsAwarded = 10;
  10551. } else {
  10552. // For other vehicles (MRAPs, Quads), award half their point value, with a minimum of 1
  10553. _pointsAwarded = (_victimValue / 2) max 1;
  10554. };
  10555. };
  10556. // ===================== CHANGE END =====================
  10557.  
  10558. private _isOwner = (_pointRecipient != _actualKiller); // Check if the recipient is the bot's owner
  10559.  
  10560. _pointRecipient setVariable ["playerPoints", _currentPoints + _pointsAwarded, true];
  10561.  
  10562. private _hintMessage = "";
  10563. if (_isOwner) then {
  10564. _hintMessage = format["Your AI squad got a kill! +%1 Personal Points! You now have %2.", _pointsAwarded, _currentPoints + _pointsAwarded];
  10565. } else {
  10566. _hintMessage = format["+%1 Personal Points! You now have %2.", _pointsAwarded, _currentPoints + _pointsAwarded];
  10567. };
  10568. [_hintMessage] remoteExecCall ["hint", owner _pointRecipient];
  10569. };
  10570. };
  10571.  
  10572. // ==================== CHANGE START ====================
  10573. // Deduct points on player death and check for bankruptcy
  10574. if (isPlayer _victim) then {
  10575. // Deduct personal point
  10576. private _playerPoints = _victim getVariable ["playerPoints", 0];
  10577. private _newPlayerPoints = (_playerPoints - 1) max 0;
  10578. _victim setVariable ["playerPoints", _newPlayerPoints, true];
  10579. [format["You have died. -1 Point. You now have %1 points.", _newPlayerPoints]] remoteExecCall ["hint", owner _victim];
  10580.  
  10581. // Deduct team point and check for game end condition
  10582. if (_victimSide == west) then {
  10583. BLUFOR_POINTS = BLUFOR_POINTS - 1;
  10584. publicVariable "BLUFOR_POINTS";
  10585. if (BLUFOR_POINTS < 1) then {
  10586. ["OPFOR", "BLUFOR", "BLUFOR has run out of points to reinforce their troops."] call fnc_endRound;
  10587. };
  10588. } else {
  10589. OPFOR_POINTS = OPFOR_POINTS - 1;
  10590. publicVariable "OPFOR_POINTS";
  10591. if (OPFOR_POINTS < 1) then {
  10592. ["BLUFOR", "OPFOR", "OPFOR has run out of points to reinforce their troops."] call fnc_endRound;
  10593. };
  10594. };
  10595. };
  10596. // ===================== CHANGE END =====================
  10597. }];
  10598.  
  10599. // Spawn the first waves of standard infantry to move towards the center of the map.
  10600. [] spawn {
  10601. sleep 15;
  10602. waitUntil {!isNil "mission_centralPoint"};
  10603. private _centerPos = missionNamespace getVariable "mission_centralPoint";
  10604.  
  10605. for "_i" from 0 to 2 do {
  10606. if (count allUnits >= maxAI) exitWith {};
  10607.  
  10608. // BLUFOR INFANTRY WAVE
  10609. private _bluforGroup = ["infantry"] call fnc_spawnBluforWave;
  10610. if (!isNull _bluforGroup) then {
  10611. [_bluforGroup, _centerPos] call fnc_createPatrolWaypoints;
  10612. };
  10613.  
  10614. sleep 1;
  10615.  
  10616. if (count allUnits >= maxAI) exitWith {};
  10617.  
  10618. // OPFOR INFANTRY WAVE
  10619. private _opforGroup = ["infantry"] call fnc_spawnOpforWave;
  10620. if (!isNull _opforGroup) then {
  10621. [_opforGroup, _centerPos] call fnc_createPatrolWaypoints;
  10622. };
  10623.  
  10624. sleep 2;
  10625. };
  10626. };
  10627.  
  10628. // Main AI Wave Spawning Loop (for continuous reinforcement)
  10629. [] spawn {
  10630. sleep 15; // This now acts as a delay AFTER the initial waves
  10631. while {true} do {
  10632. if (count allUnits < maxAI) then {
  10633. // Count alive non-player AI for each side
  10634. private _bluforAliveCount = count (allUnits select {side _x == west && !isPlayer _x && alive _x});
  10635. private _opforAliveCount = count (allUnits select {side _x == east && !isPlayer _x && alive _x});
  10636.  
  10637. // Define the population cap threshold (110% of the enemy)
  10638. private _bluforCap = _opforAliveCount * 1.1;
  10639. private _opforCap = _bluforAliveCount * 1.1;
  10640.  
  10641. // Define the low-point threshold to ignore the cap
  10642. private _lowPointThreshold = 20;
  10643.  
  10644. // Determine if each side is allowed to spawn based on population
  10645. private _bluforCanSpawn = (_bluforAliveCount <= _bluforCap) || (OPFOR_POINTS < _lowPointThreshold);
  10646. private _opforCanSpawn = (_opforAliveCount <= _opforCap) || (BLUFOR_POINTS < _lowPointThreshold);
  10647.  
  10648. // BLUFOR WAVE
  10649. if (_bluforCanSpawn) then {
  10650. private _bluforWaveType = [BLUFOR_POINTS, west] call fnc_getRandomWaveType;
  10651. if (!isNil "_bluforWaveType") then {
  10652. [_bluforWaveType] call fnc_spawnBluforWave;
  10653. };
  10654. };
  10655.  
  10656. sleep (waveDelay / 2);
  10657.  
  10658. // OPFOR WAVE
  10659. if (_opforCanSpawn) then {
  10660. private _opforWaveType = [OPFOR_POINTS, east] call fnc_getRandomWaveType;
  10661. if (!isNil "_opforWaveType") then {
  10662. [_opforWaveType] call fnc_spawnOpforWave;
  10663. };
  10664. };
  10665.  
  10666. sleep waveDelay;
  10667. } else {
  10668. sleep 60;
  10669. };
  10670. };
  10671. };
  10672. // ==================== CHANGE START ====================
  10673. // NEW: Game Win/Loss Condition Check by Unit Count
  10674. [] spawn {
  10675. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  10676.  
  10677. // Do not start checks until 10 minutes (600 seconds) have passed.
  10678. waitUntil {time > 600};
  10679.  
  10680. while {true} do {
  10681. if (missionNamespace getVariable ["missionEnding", false]) exitWith {};
  10682.  
  10683. // Count ALL alive units (players + AI) for each side
  10684. private _bluforUnitCount = count (allUnits select {side _x == west && alive _x});
  10685. private _opforUnitCount = count (allUnits select {side _x == east && alive _x});
  10686.  
  10687. // Check for BLUFOR loss due to catastrophic casualties (if they have players)
  10688. if (_bluforUnitCount > 0 && _bluforUnitCount < 10) then {
  10689. if (count (allPlayers select {side _x == west}) > 0) then {
  10690. ["OPFOR", "BLUFOR", "BLUFOR's forces have been eliminated."] call fnc_endRound;
  10691. break;
  10692. };
  10693. };
  10694.  
  10695. // Check for OPFOR loss due to catastrophic casualties (if they have players)
  10696. if (_opforUnitCount > 0 && _opforUnitCount < 10) then {
  10697. if (count (allPlayers select {side _x == east}) > 0) then {
  10698. ["BLUFOR", "OPFOR", "OPFOR's forces have been eliminated."] call fnc_endRound;
  10699. break;
  10700. };
  10701. };
  10702.  
  10703. sleep 15; // Check every 15 seconds
  10704. };
  10705. };
  10706. // ===================== CHANGE END =====================
  10707.  
  10708. // FOB Decoration and Respawn Position Setup
  10709. [] spawn {
  10710. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  10711. sleep 3;
  10712.  
  10713. [bluforSpawnObj, west] call fnc_decorateFOB;
  10714. [opforSpawnObj, east] call fnc_decorateFOB;
  10715.  
  10716. [west, getPos bluforSpawnObj, "BLUFOR Base"] call BIS_fnc_addRespawnPosition;
  10717. [east, getPos opforSpawnObj, "OPFOR Base"] call BIS_fnc_addRespawnPosition;
  10718.  
  10719. // Create play area restriction zone
  10720. [] call fnc_createPlayArea;
  10721. };
  10722. };
  10723. };
  10724.  
  10725. if (hasInterface) then {
  10726. [] spawn { [] call fnc_setupCaptureActions; };
  10727. };
  10728.  
  10729. // Global friendly collision damage reduction (always active)
  10730. [] spawn {
  10731. while {true} do {
  10732. {
  10733. if (_x isKindOf "LandVehicle" && !(_x getVariable ["friendlyCollisionSetup", false])) then {
  10734. _x setVariable ["friendlyCollisionSetup", true];
  10735. _x addEventHandler ["HandleDamage", {
  10736. params ["_vehicle", "_selection", "_damage", "_source", "_projectile", "_hitIndex", "_instigator", "_hitPoint"];
  10737. if (_source isKindOf "LandVehicle" && _projectile == "" && _selection == "") then {
  10738. private _vehicleSide = side (effectiveCommander _source);
  10739. private _unitSide = side _vehicle;
  10740. if (_vehicleSide == _unitSide) then {
  10741. _damage = _damage * 0.05;
  10742. };
  10743. };
  10744. _damage
  10745. }];
  10746. };
  10747. } forEach vehicles;
  10748. sleep 10;
  10749. };
  10750. };
  10751.  
  10752. ==================== END OF: mission_setup.sqf ====================
  10753.  
  10754.  
  10755.  
  10756. ==================== START OF: performance.sqf ====================
  10757.  
  10758. // performance.sqf
  10759. // REWORKED: Centralized performance monitoring and dynamic scaling system.
  10760. // This system no longer disables features, but instead scales their intensity and frequency.
  10761.  
  10762. // --- Global Performance Variables ---
  10763. if (isNil "PERF_STATE") then { PERF_STATE = "EXCELLENT"; };
  10764. if (isNil "PERF_MAIN_LOOP_MULTIPLIER") then { PERF_MAIN_LOOP_MULTIPLIER = 1.0; };
  10765. if (isNil "PERF_AI_BATCH_SIZE") then { PERF_AI_BATCH_SIZE = 8; };
  10766. if (isNil "PERF_SEARCH_RADIUS_MULTIPLIER") then { PERF_SEARCH_RADIUS_MULTIPLIER = 1.0; };
  10767. if (isNil "PERF_COOLDOWN_MULTIPLIER") then { PERF_COOLDOWN_MULTIPLIER = 1.0; };
  10768.  
  10769. // Monitor server performance and adjust intervals dynamically
  10770. fnc_monitorPerformance = {
  10771. private _deltaHistory = [];
  10772. private _execTimeHistory = [];
  10773. private _historySize = 4;
  10774.  
  10775. while {true} do {
  10776. private _schedulerDelta = diag_deltaTime;
  10777. private _lastExecTime = missionNamespace getVariable ["BOTAI_lastExecTime", 0];
  10778.  
  10779. _deltaHistory pushBack _schedulerDelta;
  10780. _execTimeHistory pushBack _lastExecTime;
  10781.  
  10782. if (count _deltaHistory > _historySize) then { _deltaHistory deleteAt 0; };
  10783. if (count _execTimeHistory > _historySize) then { _execTimeHistory deleteAt 0; };
  10784.  
  10785. private _avgDelta = 0;
  10786. {_avgDelta = _avgDelta + _x;} forEach _deltaHistory;
  10787. _avgDelta = _avgDelta / count _deltaHistory;
  10788.  
  10789. private _avgExecTime = 0;
  10790. {_avgExecTime = _avgExecTime + _x;} forEach _execTimeHistory;
  10791. _avgExecTime = _avgExecTime / count _execTimeHistory;
  10792.  
  10793. private _deltaState = "EXCELLENT";
  10794. private _deltaMultiplier = 1.0;
  10795.  
  10796. if (_avgDelta < 0.025) then {
  10797. _deltaState = "EXCELLENT"; _deltaMultiplier = 1.0;
  10798. } else {
  10799. if (_avgDelta < 0.040) then {
  10800. _deltaState = "GOOD"; _deltaMultiplier = 1.5;
  10801. } else {
  10802. if (_avgDelta < 0.060) then {
  10803. _deltaState = "MODERATE"; _deltaMultiplier = 2.5;
  10804. } else {
  10805. if (_avgDelta < 0.090) then {
  10806. _deltaState = "POOR"; _deltaMultiplier = 4.0;
  10807. } else {
  10808. if (_avgDelta < 0.120) then {
  10809. _deltaState = "CRITICAL"; _deltaMultiplier = 6.0;
  10810. } else {
  10811. _deltaState = "EMERGENCY"; _deltaMultiplier = 10.0;
  10812. };
  10813. };
  10814. };
  10815. };
  10816. };
  10817.  
  10818. private _execState = "EXCELLENT";
  10819. private _execMultiplier = 1.0;
  10820.  
  10821. if (_avgExecTime < 0.020) then {
  10822. _execState = "EXCELLENT"; _execMultiplier = 1.0;
  10823. } else {
  10824. if (_avgExecTime < 0.040) then {
  10825. _execState = "GOOD"; _execMultiplier = 1.5;
  10826. } else {
  10827. if (_avgExecTime < 0.060) then {
  10828. _execState = "MODERATE"; _execMultiplier = 2.5;
  10829. } else {
  10830. if (_avgExecTime < 0.090) then {
  10831. _execState = "POOR"; _execMultiplier = 4.0;
  10832. } else {
  10833. if (_avgExecTime < 0.120) then {
  10834. _execState = "CRITICAL"; _execMultiplier = 6.0;
  10835. } else {
  10836. _execState = "EMERGENCY"; _execMultiplier = 10.0;
  10837. };
  10838. };
  10839. };
  10840. };
  10841. };
  10842.  
  10843. private _oldState = PERF_STATE;
  10844. private _worstMultiplier = _deltaMultiplier max _execMultiplier;
  10845.  
  10846. if (_worstMultiplier == _deltaMultiplier) then {
  10847. PERF_STATE = _deltaState;
  10848. } else {
  10849. PERF_STATE = _execState;
  10850. };
  10851.  
  10852. if (_oldState != PERF_STATE) then {
  10853. diag_log format ["[PERF] State Change: %1 -> %2", _oldState, PERF_STATE];
  10854. [] call fnc_adjustPerformanceScales;
  10855. };
  10856.  
  10857. sleep 2.5;
  10858. };
  10859. };
  10860.  
  10861. // Helper function to get adaptive sleep time
  10862. fnc_getAdaptiveSleep = {
  10863. params ["_baseSleep"];
  10864. (_baseSleep * PERF_MAIN_LOOP_MULTIPLIER)
  10865. };
  10866.  
  10867. // REWORKED: Centralized function to adjust all game scales based on performance state.
  10868. fnc_adjustPerformanceScales = {
  10869. switch (PERF_STATE) do {
  10870. case "EXCELLENT": {
  10871. PERF_MAIN_LOOP_MULTIPLIER = 1.0;
  10872. PERF_AI_BATCH_SIZE = 8;
  10873. PERF_SEARCH_RADIUS_MULTIPLIER = 1.0; // 100% search distance
  10874. PERF_COOLDOWN_MULTIPLIER = 1.0; // 100% cooldown time
  10875. };
  10876. case "GOOD": {
  10877. PERF_MAIN_LOOP_MULTIPLIER = 1.5;
  10878. PERF_AI_BATCH_SIZE = 6;
  10879. PERF_SEARCH_RADIUS_MULTIPLIER = 0.9; // 90% search distance
  10880. PERF_COOLDOWN_MULTIPLIER = 1.25; // 125% cooldown time
  10881. };
  10882. case "MODERATE": {
  10883. PERF_MAIN_LOOP_MULTIPLIER = 2.5;
  10884. PERF_AI_BATCH_SIZE = 4;
  10885. PERF_SEARCH_RADIUS_MULTIPLIER = 0.8; // 80% search distance
  10886. PERF_COOLDOWN_MULTIPLIER = 1.75; // 175% cooldown time
  10887. };
  10888. case "POOR": {
  10889. PERF_MAIN_LOOP_MULTIPLIER = 4.0;
  10890. PERF_AI_BATCH_SIZE = 3;
  10891. PERF_SEARCH_RADIUS_MULTIPLIER = 0.7; // 70% search distance
  10892. PERF_COOLDOWN_MULTIPLIER = 2.5; // 250% cooldown time
  10893. };
  10894. case "CRITICAL": {
  10895. PERF_MAIN_LOOP_MULTIPLIER = 6.0;
  10896. PERF_AI_BATCH_SIZE = 2;
  10897. PERF_SEARCH_RADIUS_MULTIPLIER = 0.6; // 60% search distance
  10898. PERF_COOLDOWN_MULTIPLIER = 3.0; // 300% cooldown time
  10899. };
  10900. case "EMERGENCY": {
  10901. PERF_MAIN_LOOP_MULTIPLIER = 10.0;
  10902. PERF_AI_BATCH_SIZE = 1;
  10903. PERF_SEARCH_RADIUS_MULTIPLIER = 0.6; // 60% search distance
  10904. PERF_COOLDOWN_MULTIPLIER = 4.0; // 400% cooldown time
  10905. };
  10906. };
  10907. };
  10908.  
  10909. if (isServer) then {
  10910. [] spawn {
  10911. sleep 5;
  10912. [] spawn fnc_monitorPerformance;
  10913. diag_log "[PERFORMANCE] System initialized.";
  10914. };
  10915. };
  10916.  
  10917. ==================== END OF: performance.sqf ====================
  10918.  
  10919.  
  10920.  
  10921. ==================== START OF: playerinit.sqf ====================
  10922.  
  10923. // playerinit.sqf
  10924. // Client-side initialization and UI functions
  10925.  
  10926. fnc_client_showBriefing = {
  10927. if (isNull (findDisplay 9300)) then {
  10928. if (createDialog "MissionBriefingDialog") then {
  10929. waitUntil {!isNull (findDisplay 9300)};
  10930. };
  10931. };
  10932. };
  10933. missionNamespace setVariable ["fnc_client_showBriefing", fnc_client_showBriefing, true];
  10934.  
  10935. fnc_client_openPlayerMenu = {
  10936. if (isNull (findDisplay 9000)) then {
  10937. if (createDialog "PlayerPurchaseMenu") then {
  10938. private _pointsCtrl = (findDisplay 9000) displayCtrl 9002;
  10939. private _points = player getVariable ["playerPoints", 0];
  10940. _pointsCtrl ctrlSetText format ["Points: %1", _points];
  10941. };
  10942. };
  10943. };
  10944. missionNamespace setVariable ["fnc_client_openPlayerMenu", fnc_client_openPlayerMenu, true];
  10945.  
  10946. fnc_client_addVehicleWaypoint = {
  10947. params ["_vehiclePos", "_displayName"];
  10948. private _grp = group player;
  10949.  
  10950. _grp addWaypoint [_vehiclePos, 0];
  10951.  
  10952. private _newWPIndex = (count (waypoints _grp)) - 1;
  10953.  
  10954. private _wp = (waypoints _grp) select _newWPIndex;
  10955.  
  10956. _wp setWaypointType "MOVE";
  10957. _wp setWaypointCompletionRadius 30;
  10958.  
  10959. [_grp, _newWPIndex] setWaypointDescription format["Your %1", _displayName];
  10960.  
  10961. hint format ["Waypoint added to your map for the %1.", _displayName];
  10962. };
  10963. missionNamespace setVariable ["fnc_client_addVehicleWaypoint", fnc_client_addVehicleWaypoint, true];
  10964. player setVariable ["spawnTime", time, true];
  10965.  
  10966. fnc_client_completeSideSwitch = {
  10967. params ["_newUnit"];
  10968.  
  10969. // CHANGE: Remove the local check that was preventing execution
  10970. // Wait for the unit to become local if it isn't already
  10971. if (!local _newUnit) then {
  10972. waitUntil {local _newUnit || !alive _newUnit};
  10973. };
  10974.  
  10975. // Exit if unit died during the wait
  10976. if (!alive _newUnit) exitWith {
  10977. hint "Failed to join team - unit was destroyed.";
  10978. };
  10979.  
  10980. selectPlayer _newUnit;
  10981.  
  10982. waitUntil {player == _newUnit};
  10983. player setVariable ["sideSwitchComplete", true, true];
  10984.  
  10985. [player] call fnc_initializePlayerUnit;
  10986. [player] call fnc_teleportToBase;
  10987. [player] call fnc_client_initializePlayer;
  10988.  
  10989. waitUntil {!isNil "BLUFOR_POINTS"};
  10990. [] call fnc_client_createTeamScoreUI;
  10991.  
  10992. hint "You have joined a team. Good luck, Officer.";
  10993.  
  10994. // SHOW BRIEFING ON TEAM SWITCH
  10995. [] call fnc_client_showBriefing;
  10996. };
  10997. missionNamespace setVariable ["fnc_client_completeSideSwitch", fnc_client_completeSideSwitch, true];
  10998.  
  10999. fnc_client_createTeamScoreUI = {
  11000. // UI has been disabled by user request as the information is provided in chat.
  11001. };
  11002.  
  11003. fnc_teleportToBase = {
  11004. params ["_unit"];
  11005. if (!local _unit) exitWith {};
  11006. private _side = side _unit;
  11007. private _baseObj = objNull;
  11008.  
  11009. if (_side == west) then {
  11010. _baseObj = bluforSpawnObj;
  11011. } else {
  11012. if (_side == east) then {
  11013. _baseObj = opforSpawnObj;
  11014. };
  11015. };
  11016.  
  11017. if (!isNull _baseObj) then {
  11018. private _spawnPos = getPosATL _baseObj;
  11019. _spawnPos set [2, 0.5];
  11020.  
  11021. if (count _spawnPos < 3 || {!finite (_spawnPos select 0)} || {!finite (_spawnPos select 1)}) exitWith {
  11022. systemChat "Error: Invalid base position detected";
  11023. };
  11024.  
  11025. _unit setPosATL _spawnPos;
  11026. _unit setDir (random 360);
  11027. _unit setVelocity [0,0,0];
  11028. _unit setDamage 0;
  11029.  
  11030. } else {
  11031. systemChat format ["Error: No base object found for side %1", _side];
  11032. };
  11033. };
  11034.  
  11035. fnc_singleplayerRespawn = {
  11036. params ["_deadUnit"];
  11037.  
  11038. private _unitType = typeOf _deadUnit;
  11039. private _unitGroup = group _deadUnit;
  11040. private _unitSide = side _unitGroup;
  11041. private _unitLoadout = _deadUnit getVariable ["savedLoadout", getUnitLoadout _deadUnit];
  11042. private _playerPoints = _deadUnit getVariable ["playerPoints", 0];
  11043. private _teamkillCount = _deadUnit getVariable ["teamkillCount", 0];
  11044. private _isPenaltyRespawn = _deadUnit getVariable ["penaltyRespawn", false];
  11045.  
  11046. private _respawnDelay = if (_isPenaltyRespawn) then {600} else {5};
  11047.  
  11048. private _baseObj = if (_unitSide == west) then {bluforSpawnObj} else {opforSpawnObj};
  11049. private _spawnPos = getPosATL _baseObj;
  11050. _spawnPos set [2, 0.5];
  11051.  
  11052. private _newUnit = _unitGroup createUnit [_unitType, _spawnPos, [], 0, "NONE"];
  11053. _newUnit setPosATL _spawnPos;
  11054. _newUnit setDir (random 360);
  11055.  
  11056. _newUnit enableSimulation false;
  11057. _newUnit hideObjectGlobal true;
  11058. _newUnit allowDamage false;
  11059.  
  11060. selectPlayer _newUnit;
  11061.  
  11062. deleteVehicle _deadUnit;
  11063.  
  11064. cutText ["", "BLACK OUT", 0];
  11065.  
  11066. private _respawnEndTime = time + _respawnDelay;
  11067. while {time < _respawnEndTime} do {
  11068. private _timeLeft = ceil (_respawnEndTime - time);
  11069. private _message = if (_isPenaltyRespawn) then {
  11070. format ["PENALTY RESPAWN IN: %1", [_timeLeft, "MM:SS"] call BIS_fnc_secondsToString]
  11071. } else {
  11072. format ["RESPAWNING IN: %1", _timeLeft]
  11073. };
  11074. cutText [_message, "BLACK FADED", 0];
  11075. sleep 0.1;
  11076. };
  11077.  
  11078. _newUnit enableSimulation true;
  11079. _newUnit hideObjectGlobal false;
  11080.  
  11081. _newUnit setUnitLoadout _unitLoadout;
  11082. _newUnit setVariable ["playerPoints", _playerPoints - 1 max 0, true];
  11083. _newUnit setVariable ["teamkillCount", _teamkillCount, true];
  11084.  
  11085. if (_isPenaltyRespawn) then {
  11086. _newUnit setVariable ["penaltyRespawn", false, true];
  11087. _newUnit setVariable ["clearMyPenaltyFlag", true, true];
  11088. };
  11089.  
  11090. [_newUnit] call fnc_initializePlayerUnit;
  11091.  
  11092. cutText ["", "BLACK IN", 2];
  11093.  
  11094. _newUnit setDamage 0;
  11095. _newUnit setFatigue 0;
  11096.  
  11097. // Apply respawn protection
  11098. [_newUnit] call fnc_applyRespawnProtection;
  11099.  
  11100. "DynamicBlur" ppEffectEnable true;
  11101. "DynamicBlur" ppEffectAdjust [0];
  11102. "DynamicBlur" ppEffectCommit 0;
  11103. "DynamicBlur" ppEffectEnable false;
  11104.  
  11105. "RadialBlur" ppEffectEnable true;
  11106. "RadialBlur" ppEffectAdjust [0, 0, 0, 0];
  11107. "RadialBlur" ppEffectCommit 0;
  11108. "RadialBlur" ppEffectEnable false;
  11109.  
  11110. "chromAberration" ppEffectEnable true;
  11111. "chromAberration" ppEffectAdjust [0,0,true];
  11112. "chromAberration" ppEffectCommit 0;
  11113. "chromAberration" ppEffectEnable false;
  11114.  
  11115. "ColorCorrections" ppEffectEnable true;
  11116. "ColorCorrections" ppEffectAdjust [1, 1, 0, [0,0,0,0], [0,0,0,0], [0,0,0,0]];
  11117. "ColorCorrections" ppEffectCommit 0;
  11118. "ColorCorrections" ppEffectEnable false;
  11119.  
  11120. "FilmGrain" ppEffectEnable true;
  11121. "FilmGrain" ppEffectAdjust [0, 1, 1, 1, 0, false];
  11122. "FilmGrain" ppEffectCommit 0;
  11123. "FilmGrain" ppEffectEnable false;
  11124.  
  11125. sleep 0.1;
  11126.  
  11127. [] call fnc_client_showBriefing;
  11128.  
  11129. // Force reset all screen effects 1 second after respawn
  11130. [] spawn {
  11131. sleep 1;
  11132. "DynamicBlur" ppEffectEnable true;
  11133. "DynamicBlur" ppEffectAdjust [0];
  11134. "DynamicBlur" ppEffectCommit 0;
  11135. "DynamicBlur" ppEffectEnable false;
  11136.  
  11137. "RadialBlur" ppEffectEnable true;
  11138. "RadialBlur" ppEffectAdjust [0, 0, 0, 0];
  11139. "RadialBlur" ppEffectCommit 0;
  11140. "RadialBlur" ppEffectEnable false;
  11141.  
  11142. "chromAberration" ppEffectEnable true;
  11143. "chromAberration" ppEffectAdjust [0,0,true];
  11144. "chromAberration" ppEffectCommit 0;
  11145. "chromAberration" ppEffectEnable false;
  11146.  
  11147. "ColorCorrections" ppEffectEnable true;
  11148. "ColorCorrections" ppEffectAdjust [1, 1, 0, [0,0,0,0], [1,1,1,1], [1,1,1,0]];
  11149. "ColorCorrections" ppEffectCommit 0;
  11150. "ColorCorrections" ppEffectEnable false;
  11151.  
  11152. "FilmGrain" ppEffectEnable true;
  11153. "FilmGrain" ppEffectAdjust [0, 1, 1, 1, 0, false];
  11154. "FilmGrain" ppEffectCommit 0;
  11155. "FilmGrain" ppEffectEnable false;
  11156.  
  11157. "WetDistortion" ppEffectEnable true;
  11158. "WetDistortion" ppEffectAdjust [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  11159. "WetDistortion" ppEffectCommit 0;
  11160. "WetDistortion" ppEffectEnable false;
  11161.  
  11162. "ColorInversion" ppEffectEnable true;
  11163. "ColorInversion" ppEffectAdjust [0,0,0];
  11164. "ColorInversion" ppEffectCommit 0;
  11165. "ColorInversion" ppEffectEnable false;
  11166.  
  11167. enableCamShake false;
  11168. resetCamShake;
  11169. };
  11170. };
  11171.  
  11172. fnc_multiplayerRespawn = {
  11173. params ["_deadUnit"];
  11174.  
  11175. private _unitLoadout = _deadUnit getVariable ["savedLoadout", getUnitLoadout _deadUnit];
  11176. private _playerPoints = _deadUnit getVariable ["playerPoints", 0];
  11177. private _teamkillCount = _deadUnit getVariable ["teamkillCount", 0];
  11178. private _isPenaltyRespawn = _deadUnit getVariable ["penaltyRespawn", false];
  11179.  
  11180. private _respawnDelay = if (_isPenaltyRespawn) then {600} else {5};
  11181.  
  11182. // Store data on the server for post-respawn processing - ONLY for this specific player
  11183. [_deadUnit, _unitLoadout, _playerPoints, _teamkillCount, _isPenaltyRespawn] remoteExecCall ["fnc_server_storeRespawnData", 2, false];
  11184.  
  11185. // Set respawn time ONLY for this player
  11186. setPlayerRespawnTime _respawnDelay;
  11187. };
  11188.  
  11189. // Helper function to check and replace disallowed gear
  11190. fnc_checkAndReplaceGear = {
  11191. params ["_unit"];
  11192.  
  11193. if (isNil "BLUFOR_UNIFORMS" || isNil "OPFOR_UNIFORMS") exitWith {};
  11194.  
  11195. private _side = playerSide;
  11196. private _allowedUniforms = if (_side == west) then {BLUFOR_UNIFORMS} else {OPFOR_UNIFORMS};
  11197. private _allowedVests = if (_side == west) then {BLUFOR_VESTS} else {OPFOR_VESTS};
  11198. private _allowedHeadgear = if (_side == west) then {BLUFOR_HEADGEAR} else {OPFOR_HEADGEAR};
  11199. private _changed = false;
  11200.  
  11201. private _fnc_findReplacement = {
  11202. params ["_invalidItem", "_allowedList"];
  11203. private _replacement = _allowedList # 0;
  11204. private _invalidLower = toLower _invalidItem;
  11205. {
  11206. if (_invalidLower find (toLower (getText (configFile >> "CfgWeapons" >> _x >> "displayName"))) > -1) exitWith {
  11207. _replacement = _x;
  11208. };
  11209. } forEach _allowedList;
  11210. _replacement
  11211. };
  11212.  
  11213. private _currentUniform = uniform _unit;
  11214. // MODIFIED: Check for no uniform OR disallowed uniform
  11215. if (_currentUniform == "" || !(_currentUniform in _allowedUniforms)) then {
  11216. private _uniformItems = [];
  11217.  
  11218. // Only save items if there was a uniform to begin with
  11219. if (_currentUniform != "") then {
  11220. _uniformItems = uniformItems _unit;
  11221. removeUniform _unit;
  11222. };
  11223.  
  11224. // Determine replacement uniform
  11225. private _replacementUniform = if (_currentUniform != "") then {
  11226. [_currentUniform, _allowedUniforms] call _fnc_findReplacement
  11227. } else {
  11228. // No uniform - just use the first default uniform for the faction
  11229. _allowedUniforms # 0
  11230. };
  11231.  
  11232. _unit forceAddUniform _replacementUniform;
  11233. {_unit addItemToUniform _x} forEach _uniformItems;
  11234. _changed = true;
  11235. };
  11236.  
  11237. private _currentVest = vest _unit;
  11238. if (_currentVest != "" && !(_currentVest in _allowedVests)) then {
  11239. private _vestItems = vestItems _unit;
  11240. removeVest _unit;
  11241. private _replacementVest = [_currentVest, _allowedVests] call _fnc_findReplacement;
  11242. _unit addVest _replacementVest;
  11243. {_unit addItemToVest _x} forEach _vestItems;
  11244. _changed = true;
  11245. };
  11246.  
  11247. private _currentHeadgear = headgear _unit;
  11248. if (_currentHeadgear != "" && !(_currentHeadgear in _allowedHeadgear)) then {
  11249. removeHeadgear _unit;
  11250. private _replacementHeadgear = [_currentHeadgear, _allowedHeadgear] call _fnc_findReplacement;
  11251. _unit addHeadgear _replacementHeadgear;
  11252. _changed = true;
  11253. };
  11254.  
  11255. if (_changed) then {
  11256. hint "Invalid gear detected and replaced with allowed items.";
  11257. };
  11258. };
  11259.  
  11260. fnc_initializePlayerUnit = {
  11261. params ["_unit"];
  11262.  
  11263. _unit disableAI "ALL";
  11264. _unit enableAI "ANIM";
  11265. _unit enableAI "MOVE";
  11266.  
  11267. _unit addEventHandler ["InventoryOpened", {
  11268. params ["_unit"];
  11269. [_unit] call fnc_checkAndReplaceGear;
  11270. }];
  11271.  
  11272. _unit addEventHandler ["InventoryClosed", {
  11273. params ["_unit"];
  11274. _unit setVariable ["savedLoadout", getUnitLoadout _unit];
  11275. [_unit] call fnc_checkAndReplaceGear;
  11276. }];
  11277.  
  11278. if (!isMultiplayer) then {
  11279. _unit addEventHandler ["Killed", {
  11280. params ["_unit"];
  11281.  
  11282. // ==================== CHANGE START ====================
  11283. // Get AI units in squad before respawn logic runs
  11284. private _aiToDelete = units (group _unit) select {!isPlayer _x};
  11285. if (count _aiToDelete > 0) then {
  11286. // Tell the server to delete these specific AI units
  11287. [_aiToDelete] remoteExecCall ["fnc_server_deletePlayerAISquad", 2];
  11288. };
  11289. // ===================== CHANGE END =====================
  11290.  
  11291. _unit spawn {
  11292. sleep 0.01;
  11293. [_this] call fnc_singleplayerRespawn;
  11294. };
  11295. }];
  11296. } else {
  11297. _unit addEventHandler ["Killed", {
  11298. params ["_unit"];
  11299.  
  11300. // ==================== CHANGE START ====================
  11301. // Get AI units in squad before respawn logic runs
  11302. private _aiToDelete = units (group _unit) select {!isPlayer _x};
  11303. if (count _aiToDelete > 0) then {
  11304. // Tell the server to delete these specific AI units
  11305. [_aiToDelete] remoteExecCall ["fnc_server_deletePlayerAISquad", 2];
  11306. };
  11307. // ===================== CHANGE END =====================
  11308.  
  11309. _unit spawn {
  11310. sleep 0.01;
  11311. [_this] call fnc_multiplayerRespawn;
  11312. };
  11313. }];
  11314.  
  11315. // NEW: Add Respawn event handler to immediately teleport to base in MP
  11316. _unit addEventHandler ["Respawn", {
  11317. params ["_unit", "_corpse"];
  11318. // Mark the spawn time for tracking
  11319. _unit setVariable ["spawnTime", time, true];
  11320. // Immediately teleport to base
  11321. [_unit] call fnc_teleportToBase;
  11322. }];
  11323. };
  11324.  
  11325. // ==================== CHANGE START ====================
  11326. // MOVED & REWORKED: Player intel reporting loop.
  11327. // This now runs for each new player unit, ensuring intel continues after respawn.
  11328. if (isNil {_unit getVariable "intelLoopStarted"}) then {
  11329. _unit setVariable ["intelLoopStarted", true];
  11330.  
  11331. [_unit] spawn {
  11332. params ["_playerUnit"];
  11333. // Wait for the unit to be on a team.
  11334. waitUntil { !isNull _playerUnit && side _playerUnit in [west, east] };
  11335.  
  11336. // Loop only as long as this specific unit is alive.
  11337. while {alive _playerUnit} do {
  11338. // Call the intel gathering function that AI also uses.
  11339. [_playerUnit, side _playerUnit] call HC_fnc_gatherIntelligence;
  11340.  
  11341. // Check every 8-13 seconds.
  11342. sleep (8 + random 5);
  11343. };
  11344. };
  11345. };
  11346. // ===================== CHANGE END =====================
  11347. };
  11348.  
  11349. fnc_applyRespawnProtection = {
  11350. params ["_unit"];
  11351. if (!local _unit) exitWith {};
  11352.  
  11353. // Set protection flag and make unit invulnerable
  11354. _unit setVariable ["respawnProtection", true, true];
  11355. _unit allowDamage false;
  11356.  
  11357. // Visual feedback - slight transparency effect
  11358. _unit setUnitTrait ["audibleCoef", 0.5];
  11359. _unit setUnitTrait ["camouflageCoef", 0.5];
  11360.  
  11361. // Show protection status
  11362. hint "Spawn Protection Active: 10 seconds";
  11363.  
  11364. // Create the protection removal script
  11365. [_unit] spawn {
  11366. params ["_protectedUnit"];
  11367. private _protectionEndTime = time + 10;
  11368.  
  11369. // Countdown loop
  11370. while {time < _protectionEndTime && alive _protectedUnit} do {
  11371. private _timeLeft = ceil (_protectionEndTime - time);
  11372. hintSilent format ["Spawn Protection: %1 seconds remaining", _timeLeft];
  11373. sleep 0.5;
  11374. };
  11375.  
  11376. // Remove protection
  11377. if (!isNull _protectedUnit) then {
  11378. _protectedUnit allowDamage true;
  11379. _protectedUnit setVariable ["respawnProtection", false, true];
  11380. _protectedUnit setUnitTrait ["audibleCoef", 1];
  11381. _protectedUnit setUnitTrait ["camouflageCoef", 1];
  11382. hintSilent "Spawn Protection Ended";
  11383.  
  11384. // Clear hint after 2 seconds
  11385. [] spawn {
  11386. sleep 2;
  11387. hintSilent "";
  11388. };
  11389. };
  11390. };
  11391.  
  11392. // Failsafe: Additional safety check to prevent permanent godmode
  11393. [_unit] spawn {
  11394. params ["_protectedUnit"];
  11395. sleep 15; // 5 seconds after protection should have ended
  11396.  
  11397. // Force remove protection if it somehow still exists
  11398. if (!isNull _protectedUnit && (_protectedUnit getVariable ["respawnProtection", false])) then {
  11399. _protectedUnit allowDamage true;
  11400. _protectedUnit setVariable ["respawnProtection", false, true];
  11401. _protectedUnit setUnitTrait ["audibleCoef", 1];
  11402. _protectedUnit setUnitTrait ["camouflageCoef", 1];
  11403. diag_log format ["WARNING: Respawn protection failsafe triggered for %1", name _protectedUnit];
  11404. };
  11405. };
  11406. };
  11407.  
  11408. fnc_client_moveIntoVehicle = {
  11409. params ["_vehicle"];
  11410. player assignAsDriver _vehicle;
  11411. player moveInDriver _vehicle;
  11412. };
  11413.  
  11414. if (hasInterface) then {
  11415. // Display play area warning to player
  11416. fnc_client_showPlayAreaWarning = {
  11417. params ["_center", "_radius"];
  11418.  
  11419. // Create warning display
  11420. if (isNil "PLAY_AREA_WARNING_ACTIVE" || {!PLAY_AREA_WARNING_ACTIVE}) then {
  11421. PLAY_AREA_WARNING_ACTIVE = true;
  11422.  
  11423. [] spawn {
  11424. while {!isNil "PLAY_AREA_WARNING_ACTIVE" && {PLAY_AREA_WARNING_ACTIVE}} do {
  11425. private _warningTime = player getVariable ["playAreaWarningTime", 0];
  11426. if (_warningTime == 0) exitWith {
  11427. PLAY_AREA_WARNING_ACTIVE = false;
  11428. };
  11429.  
  11430. private _timeLeft = 10 - (time - _warningTime);
  11431. if (_timeLeft < 0) then {_timeLeft = 0;};
  11432.  
  11433. hintSilent parseText format [
  11434. "<t color='#ff0000' size='2' align='center'>WARNING!</t><br/>" +
  11435. "<t color='#ffff00' size='1.5' align='center'>RETURN TO BATTLE AREA</t><br/>" +
  11436. "<t color='#ffffff' size='1.2' align='center'>%1 SECONDS REMAINING</t>",
  11437. ceil _timeLeft
  11438. ];
  11439.  
  11440. sleep 0.1;
  11441. };
  11442.  
  11443. // Clean up when loop exits
  11444. hintSilent "";
  11445. PLAY_AREA_WARNING_ACTIVE = false;
  11446. };
  11447. };
  11448. };
  11449.  
  11450. // Hide play area warning from player
  11451. fnc_client_hidePlayAreaWarning = {
  11452. if (!isNil "PLAY_AREA_WARNING_ACTIVE") then {
  11453. PLAY_AREA_WARNING_ACTIVE = false;
  11454. };
  11455. hintSilent "";
  11456. };
  11457.  
  11458. // Existing client-side code continues below...
  11459. [] spawn {
  11460. waitUntil { !isNull player && player == player };
  11461.  
  11462. // Compile loadout limitations on client
  11463. [] call compile preprocessFileLineNumbers "loadoutlimitations.sqf";
  11464.  
  11465. // Compile loadout limitations on client
  11466. [] call compile preprocessFileLineNumbers "loadoutlimitations.sqf";
  11467.  
  11468. // Periodic gear check to catch loadout changes bypassing event handlers
  11469. [] spawn {
  11470. waitUntil {!isNil "BLUFOR_UNIFORMS" && !isNil "OPFOR_UNIFORMS"};
  11471. while {true} do {
  11472. if (playerSide in [west, east]) then {
  11473. [player] call fnc_checkAndReplaceGear;
  11474. };
  11475. sleep 5;
  11476. };
  11477. };
  11478.  
  11479. if (side player == civilian) then {
  11480. player setVariable ["bluforAction", player addAction [
  11481. "<t color='#0040FF'>Join BLUFOR</t>",
  11482. {
  11483. params ["_target", "_caller", "_actionId", "_arguments"];
  11484. _caller removeAction _actionId;
  11485. _caller removeAction (_caller getVariable ["opforAction", -1]);
  11486. [_caller, west] remoteExecCall ["fnc_server_playerChooseSide", 2];
  11487. },
  11488. [], 6, true, true, "", "true"
  11489. ]];
  11490.  
  11491. player setVariable ["opforAction", player addAction [
  11492. "<t color='#FF0000'>Join OPFOR</t>",
  11493. {
  11494. params ["_target", "_caller", "_actionId", "_arguments"];
  11495. _caller removeAction _actionId;
  11496. _caller removeAction (_caller getVariable ["bluforAction", -1]);
  11497. [_caller, east] remoteExecCall ["fnc_server_playerChooseSide", 2];
  11498. },
  11499. [], 5, true, true, "", "true"
  11500. ]];
  11501.  
  11502. hint "Choose your side using the scroll wheel menu.";
  11503. } else {
  11504. [player] call fnc_initializePlayerUnit;
  11505.  
  11506. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  11507. sleep 1;
  11508.  
  11509. [player] call fnc_teleportToBase;
  11510. [player] call fnc_client_initializePlayer;
  11511.  
  11512. waitUntil {!isNil "BLUFOR_POINTS"};
  11513. [] call fnc_client_createTeamScoreUI;
  11514.  
  11515. // SHOW BRIEFING ON INITIAL SPAWN
  11516. [] call fnc_client_showBriefing;
  11517. };
  11518. };
  11519.  
  11520. [] spawn {
  11521. waitUntil { !isNull player };
  11522.  
  11523. private _trackedMarkers = createHashMap;
  11524.  
  11525. while {true} do {
  11526. private _playerSide = side player;
  11527.  
  11528. if (_playerSide in [west, east]) then {
  11529. private _allFriendlyUnits = allUnits select { side _x == _playerSide && alive _x };
  11530. private _infantryToMark = [];
  11531. private _vehiclesToMark = [];
  11532. private _playersToMark = [];
  11533.  
  11534. {
  11535. private _unit = _x;
  11536.  
  11537. // OPTIMIZATION: Cache unit type on the unit itself to avoid repeated checks
  11538. private _cachedType = _unit getVariable ["markerType", ""];
  11539.  
  11540. if (_cachedType == "") then {
  11541. // First time seeing this unit - determine and cache its type
  11542. if (isPlayer _unit) then {
  11543. _unit setVariable ["markerType", "PLAYER"];
  11544. _cachedType = "PLAYER";
  11545. } else {
  11546. private _vehicle = vehicle _unit;
  11547. if (_vehicle == _unit) then {
  11548. _unit setVariable ["markerType", "INFANTRY"];
  11549. _cachedType = "INFANTRY";
  11550. } else {
  11551. _unit setVariable ["markerType", "VEHICLE"];
  11552. _unit setVariable ["markerVehicle", _vehicle];
  11553. _cachedType = "VEHICLE";
  11554. };
  11555. };
  11556. };
  11557.  
  11558. // Use cached type for categorization
  11559. switch (_cachedType) do {
  11560. case "PLAYER": { _playersToMark pushBackUnique _unit; };
  11561. case "INFANTRY": { _infantryToMark pushBack _unit; };
  11562. case "VEHICLE": {
  11563. private _veh = _unit getVariable ["markerVehicle", vehicle _unit];
  11564. if (!isNull _veh && alive _veh) then {
  11565. _vehiclesToMark pushBackUnique _veh;
  11566. };
  11567. };
  11568. };
  11569. } forEach _allFriendlyUnits;
  11570.  
  11571. private _allCurrentNetIDs = (_infantryToMark apply {netId _x}) + (_vehiclesToMark apply {netId _x}) + (_playersToMark apply {netId _x});
  11572.  
  11573. private _markersToDelete = [];
  11574. {
  11575. private _entityNetId = _x;
  11576. if !(_entityNetId in _allCurrentNetIDs) then {
  11577. private _markerName = _y;
  11578. deleteMarkerLocal _markerName;
  11579. _markersToDelete pushBack _entityNetId;
  11580. };
  11581. } forEach _trackedMarkers;
  11582.  
  11583. { _trackedMarkers deleteAt _x } forEach _markersToDelete;
  11584.  
  11585. private _sideColor = if (_playerSide == west) then {"ColorBlue"} else {"ColorRed"};
  11586. private _markerPrefix = if (_playerSide == west) then {"b_"} else {"o_"};
  11587.  
  11588. {
  11589. private _vehicle = _x;
  11590. private _vehicleNetId = netId _vehicle;
  11591. private _markerName = format ["friendly_marker_%1", _vehicleNetId];
  11592.  
  11593. // OPTIMIZATION: Only update position if vehicle moved >10m
  11594. private _lastPos = _vehicle getVariable ["lastMarkerPos", [0,0,0]];
  11595. private _currentPos = getPos _vehicle;
  11596. private _shouldUpdate = (_lastPos distance2D _currentPos) > 10;
  11597.  
  11598. if (isNil {_trackedMarkers get _vehicleNetId}) then {
  11599. // New marker
  11600. private _markerType = "mil_unknown";
  11601. if (_vehicle isKindOf "Air") then {
  11602. _markerType = _markerPrefix + "air";
  11603. } else {
  11604. if (_vehicle isKindOf "Tank") then {
  11605. _markerType = _markerPrefix + "armor";
  11606. } else {
  11607. if (_vehicle isKindOf "APC") then {
  11608. _markerType = _markerPrefix + "mech_inf";
  11609. } else {
  11610. _markerType = _markerPrefix + "motor_inf";
  11611. };
  11612. };
  11613. };
  11614.  
  11615. createMarkerLocal [_markerName, _currentPos];
  11616. _markerName setMarkerTypeLocal _markerType;
  11617. _markerName setMarkerColorLocal _sideColor;
  11618. _markerName setMarkerSizeLocal [0.8, 0.8];
  11619. _markerName setMarkerAlphaLocal 1;
  11620. _markerName setMarkerTextLocal "";
  11621. _trackedMarkers set [_vehicleNetId, _markerName];
  11622. _vehicle setVariable ["lastMarkerPos", _currentPos];
  11623. } else {
  11624. // Existing marker - only update if moved significantly
  11625. if (_shouldUpdate) then {
  11626. _markerName setMarkerPosLocal _currentPos;
  11627. _vehicle setVariable ["lastMarkerPos", _currentPos];
  11628. };
  11629. };
  11630. } forEach _vehiclesToMark;
  11631.  
  11632. {
  11633. private _unit = _x;
  11634. private _unitNetId = netId _unit;
  11635. private _markerName = format ["friendly_marker_%1", _unitNetId];
  11636.  
  11637. // OPTIMIZATION: Only update position if unit moved >10m
  11638. private _lastPos = _unit getVariable ["lastMarkerPos", [0,0,0]];
  11639. private _currentPos = getPos _unit;
  11640. private _shouldUpdate = (_lastPos distance2D _currentPos) > 10;
  11641.  
  11642. if (isNil {_trackedMarkers get _unitNetId}) then {
  11643. createMarkerLocal [_markerName, _currentPos];
  11644. _markerName setMarkerTypeLocal "mil_dot";
  11645. _markerName setMarkerColorLocal _sideColor;
  11646. _markerName setMarkerSizeLocal [0.7, 0.7];
  11647. _markerName setMarkerAlphaLocal 1;
  11648. _markerName setMarkerTextLocal "";
  11649. _trackedMarkers set [_unitNetId, _markerName];
  11650. _unit setVariable ["lastMarkerPos", _currentPos];
  11651. } else {
  11652. if (_shouldUpdate) then {
  11653. _markerName setMarkerPosLocal _currentPos;
  11654. _unit setVariable ["lastMarkerPos", _currentPos];
  11655. };
  11656. };
  11657. } forEach _infantryToMark;
  11658.  
  11659. {
  11660. private _unit = _x;
  11661. private _unitNetId = netId _unit;
  11662. private _markerName = format ["friendly_marker_%1", _unitNetId];
  11663.  
  11664. // OPTIMIZATION: Only update position if unit moved >10m
  11665. private _lastPos = _unit getVariable ["lastMarkerPos", [0,0,0]];
  11666. private _currentPos = getPos _unit;
  11667. private _shouldUpdate = (_lastPos distance2D _currentPos) > 10;
  11668.  
  11669. if (isNil {_trackedMarkers get _unitNetId}) then {
  11670. createMarkerLocal [_markerName, _currentPos];
  11671. _markerName setMarkerTypeLocal "mil_dot";
  11672. _markerName setMarkerColorLocal _sideColor;
  11673. _markerName setMarkerSizeLocal [0.7, 0.7];
  11674. _markerName setMarkerAlphaLocal 1;
  11675. _markerName setMarkerTextLocal (name _unit);
  11676. _trackedMarkers set [_unitNetId, _markerName];
  11677. _unit setVariable ["lastMarkerPos", _currentPos];
  11678. } else {
  11679. if (_shouldUpdate) then {
  11680. _markerName setMarkerPosLocal _currentPos;
  11681. _unit setVariable ["lastMarkerPos", _currentPos];
  11682. };
  11683. };
  11684. } forEach _playersToMark;
  11685. };
  11686.  
  11687. // OPTIMIZATION: Increased from 2s to 3s
  11688. sleep 3;
  11689. };
  11690. };
  11691.  
  11692. [] spawn {
  11693. waitUntil { !isNull player && !isNil "BLUFOR_INTEL" && !isNil "OPFOR_INTEL" };
  11694.  
  11695. private _enemyIntelMarkers = createHashMap;
  11696.  
  11697. while {true} do {
  11698. private _playerSide = side player;
  11699.  
  11700. private _currentIntelData = if (_playerSide == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  11701.  
  11702. private _allKnownIntelNetIds = keys _currentIntelData;
  11703.  
  11704. private _enemyColorName = "ColorRed";
  11705. if (_playerSide == east) then { _enemyColorName = "ColorBlue"; };
  11706. private _aafColorName = "ColorGreen";
  11707.  
  11708. private _markerPrefix = if (_playerSide == west) then {"o_"} else {"b_"};
  11709. private _aafMarkerPrefix = "n_";
  11710.  
  11711. // OPTIMIZATION: Pre-calculate decay threshold (90% of max age)
  11712. private _maxIntelAge = HC_INTEL_DECAY_TIME;
  11713. private _staleThreshold = _maxIntelAge * 0.9;
  11714.  
  11715. {
  11716. private _enemyNetId = _x;
  11717. private _intelReport = _currentIntelData get _enemyNetId;
  11718.  
  11719. if (isNil "_intelReport" || {typeName _intelReport != "HASHMAP"}) then {
  11720. continue;
  11721. };
  11722.  
  11723. private _timeSpotted = _intelReport get "time";
  11724. private _intelAge = time - _timeSpotted;
  11725.  
  11726. // OPTIMIZATION: Skip intel that's about to expire (>90% of max age)
  11727. if (_intelAge > _staleThreshold) then {
  11728. continue;
  11729. };
  11730.  
  11731. private _enemyPos = _intelReport get "position";
  11732. private _isAAF = _intelReport get "isAAF";
  11733. private _entityType = _intelReport getOrDefault ["entityType", "UNKNOWN"];
  11734. private _isTank = _intelReport get "isTank";
  11735.  
  11736. // OPTIMIZATION: Pre-calculate marker name once
  11737. private _markerName = format ["enemy_intel_%1", _enemyNetId];
  11738.  
  11739. private _fadeAlpha = 1 - (_intelAge / _maxIntelAge);
  11740. _fadeAlpha = (_fadeAlpha max 0.2) min 1.0;
  11741.  
  11742. private _markerColorName = if (_isAAF) then { _aafColorName } else { _enemyColorName };
  11743.  
  11744. private _markerType = "mil_dot";
  11745. private _currentPrefix = if (_isAAF) then {_aafMarkerPrefix} else {_markerPrefix};
  11746. if (_entityType == "GROUND_VEHICLE") then {
  11747. if (_isTank) then {
  11748. _markerType = _currentPrefix + "armor";
  11749. } else {
  11750. _markerType = _currentPrefix + "mech_inf";
  11751. };
  11752. } else {
  11753. if (_entityType == "AIR_VEHICLE") then {
  11754. _markerType = _currentPrefix + "air";
  11755. };
  11756. };
  11757.  
  11758. if (isNil {_enemyIntelMarkers get _markerName}) then {
  11759. createMarkerLocal [_markerName, _enemyPos];
  11760. _enemyIntelMarkers set [_markerName, true];
  11761. };
  11762.  
  11763. _markerName setMarkerPosLocal _enemyPos;
  11764. _markerName setMarkerTypeLocal _markerType;
  11765. _markerName setMarkerColorLocal _markerColorName;
  11766. _markerName setMarkerAlphaLocal _fadeAlpha;
  11767. _markerName setMarkerSizeLocal [0.8, 0.8];
  11768. _markerName setMarkerTextLocal "";
  11769.  
  11770. } forEach _allKnownIntelNetIds;
  11771.  
  11772. private _markersToDelete = [];
  11773. {
  11774. private _markerName = _x;
  11775. private _enemyNetId = (_markerName splitString "_") select 2;
  11776.  
  11777. if !(_enemyNetId in _allKnownIntelNetIds) then {
  11778. deleteMarkerLocal _markerName;
  11779. _markersToDelete pushBack _markerName;
  11780. };
  11781. } forEach (keys _enemyIntelMarkers);
  11782.  
  11783. {
  11784. _enemyIntelMarkers deleteAt _x;
  11785. } forEach _markersToDelete;
  11786.  
  11787. // OPTIMIZATION: Increased from 2s to 3s
  11788. sleep 3;
  11789. };
  11790. };
  11791. };
  11792.  
  11793.  
  11794. if (isServer) then {
  11795. [] spawn {
  11796. waitUntil {time > 0};
  11797.  
  11798. {
  11799. if (!isPlayer _x && isPlayer leader _x == false) then {
  11800. _x disableAI "ALL";
  11801. _x enableAI "ANIM";
  11802. _x enableAI "MOVE";
  11803. };
  11804. } forEach playableUnits;
  11805. };
  11806. };
  11807.  
  11808. fnc_client_setupTeamSelection = {
  11809. params ["_unit"];
  11810. if (!hasInterface || _unit != player) exitWith {};
  11811.  
  11812. // Wait for base objects to be available
  11813. waitUntil {!isNil "bluforSpawnObj" && !isNil "opforSpawnObj"};
  11814.  
  11815. _unit setVariable ["bluforAction", _unit addAction [
  11816. "<t color='#0040FF'>Join BLUFOR</t>",
  11817. {
  11818. params ["_target", "_caller", "_actionId", "_arguments"];
  11819. _caller removeAction _actionId;
  11820. _caller removeAction (_caller getVariable ["opforAction", -1]);
  11821. [_caller, west] remoteExecCall ["fnc_server_playerChooseSide", 2];
  11822. },
  11823. [], 6, true, true, "", "true"
  11824. ]];
  11825.  
  11826. _unit setVariable ["opforAction", _unit addAction [
  11827. "<t color='#FF0000'>Join OPFOR</t>",
  11828. {
  11829. params ["_target", "_caller", "_actionId", "_arguments"];
  11830. _caller removeAction _actionId;
  11831. _caller removeAction (_caller getVariable ["bluforAction", -1]);
  11832. [_caller, east] remoteExecCall ["fnc_server_playerChooseSide", 2];
  11833. },
  11834. [], 5, true, true, "", "true"
  11835. ]];
  11836.  
  11837. hint "Choose your side using the scroll wheel menu.";
  11838. };
  11839.  
  11840. ==================== END OF: playerinit.sqf ====================
  11841.  
  11842.  
  11843.  
  11844. ==================== START OF: player_server.sqf ====================
  11845.  
  11846. // player_server.sqf
  11847. // Contains server-side logic for player points and purchases.
  11848.  
  11849. fnc_server_purchaseVehicle = {
  11850. params ["_player", "_vehicleClass"];
  11851. if (!isServer) exitWith {};
  11852.  
  11853. // Get vehicle data from server-side config (now from init.sqf)
  11854. private _data = PURCHASE_VEHICLE_DATA getOrDefault [_vehicleClass, nil];
  11855.  
  11856. if (isNil "_data") exitWith {
  11857. ["SERVER: Invalid vehicle type selected."] remoteExecCall ["hint", owner _player];
  11858. };
  11859.  
  11860. private _displayName = _data select 0;
  11861. private _cost = _data select 1;
  11862. private _requiredSide = _data select 2;
  11863. private _playerSide = side _player;
  11864. private _playerPoints = _player getVariable ["playerPoints", 0];
  11865.  
  11866. // Check if player is on the correct side for this vehicle
  11867. if (_playerSide != _requiredSide) exitWith {
  11868. ["SERVER: This vehicle is not available for your faction."] remoteExecCall ["hint", owner _player];
  11869. };
  11870.  
  11871. // Verify player has enough points
  11872. if (_playerPoints < _cost) exitWith {
  11873. [format["SERVER: Not enough points. You need %1, you have %2.", _cost, _playerPoints]] remoteExecCall ["hint", owner _player];
  11874. };
  11875.  
  11876. // Deduct points and broadcast the new value
  11877. _player setVariable ["playerPoints", _playerPoints - _cost, true];
  11878.  
  11879. // Find a flatter spawn position in a larger radius.
  11880. private _baseObj = if (_playerSide == west) then { bluforSpawnObj } else { opforSpawnObj };
  11881. private _spawnPos = [getPos _baseObj, 20, 100, 15, 0, 0.1, 0] call BIS_fnc_findSafePos;
  11882. if (count _spawnPos == 0) then { _spawnPos = getPos _baseObj; }; // Fallback
  11883.  
  11884. private _vehicle = createVehicle [_vehicleClass, _spawnPos vectorAdd [0,0,1.5], [], 0, "NONE"];
  11885. _vehicle setPos _spawnPos;
  11886. _vehicle setVectorUp [0,0,1];
  11887. _vehicle setVelocity [0,0,0];
  11888.  
  11889. // ==================== CHANGE START ====================
  11890. _vehicle setVariable ["unitValue", _cost, true]; // Set the point value for kill rewards
  11891. // ===================== CHANGE END =====================
  11892.  
  11893. _vehicle lock 1;
  11894. _vehicle setVariable ["ownerPlayer", _player, true];
  11895.  
  11896. [_vehicle] remoteExecCall ["fnc_client_moveIntoVehicle", owner _player];
  11897.  
  11898. [getPosATL _vehicle, _displayName] remoteExecCall ["fnc_client_addVehicleWaypoint", owner _player];
  11899. [format["%1 delivered. %2 points remaining.", _displayName, _player getVariable "playerPoints"]] remoteExecCall ["hint", owner _player];
  11900. };
  11901.  
  11902. fnc_server_initializePlayer = {
  11903. params ["_player"];
  11904. if (!isServer) exitWith {};
  11905.  
  11906. _player setVariable ["playerPoints", 5, true];
  11907. _player setVariable ["teamkillCount", 0, true];
  11908. _player setVariable ["penaltyRespawn", false, true];
  11909. _player setVariable ["clearMyPenaltyFlag", false, true];
  11910.  
  11911. [
  11912. "Welcome! You start with 1 personal point. Get kills to earn more. Approach the arsenal box to open the purchase menu."
  11913. ] remoteExecCall ["hint", owner _player];
  11914. };
  11915.  
  11916. // Server-side function to paradrop a player near the AAF stronghold
  11917. fnc_server_paradropPlayer = {
  11918. params ["_player"];
  11919. if (!isServer) exitWith {};
  11920.  
  11921. // Check if 2 minutes have passed
  11922. if (time < 120) exitWith {
  11923. ["Paradrop unavailable: Must wait 2 minutes into the mission."] remoteExecCall ["hint", owner _player];
  11924. };
  11925.  
  11926. // Check if mission_centralPoint exists (AAF stronghold position)
  11927. if (isNil "mission_centralPoint") exitWith {
  11928. ["Paradrop unavailable: Stronghold location unknown."] remoteExecCall ["hint", owner _player];
  11929. };
  11930.  
  11931. private _playerPoints = _player getVariable ["playerPoints", 0];
  11932. private _cost = 2;
  11933.  
  11934. // Verify player has enough points
  11935. if (_playerPoints < _cost) exitWith {
  11936. [format["Not enough points. You need %1, you have %2.", _cost, _playerPoints]] remoteExecCall ["hint", owner _player];
  11937. };
  11938.  
  11939. // Get player's side and base position
  11940. private _playerSide = side _player;
  11941. private _ownBase = if (_playerSide == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  11942. private _strongholdPos = missionNamespace getVariable ["mission_centralPoint", [0,0,0]];
  11943.  
  11944. // Validate stronghold position
  11945. if (_strongholdPos isEqualTo [0,0,0]) exitWith {
  11946. ["Paradrop unavailable: Invalid stronghold location."] remoteExecCall ["hint", owner _player];
  11947. };
  11948.  
  11949. // Calculate direction from stronghold to player's base
  11950. private _dirToBase = _strongholdPos getDir _ownBase;
  11951.  
  11952. // Calculate drop position: 300m offset toward own base, then randomize within 100m radius
  11953. private _baseDropPos = _strongholdPos getPos [300, _dirToBase];
  11954.  
  11955. // Add random offset within 100m radius
  11956. private _randomAngle = random 360;
  11957. private _randomDistance = random 100;
  11958. private _dropPos = _baseDropPos getPos [_randomDistance, _randomAngle];
  11959. // Set altitude to a random value between 400 and 600m
  11960. _dropPos set [2, 400 + random 200];
  11961.  
  11962. // Deduct points
  11963. _player setVariable ["playerPoints", _playerPoints - _cost, true];
  11964.  
  11965. private _groupToDrop = units (group _player);
  11966. private _unitData = []; // Store original backpacks and other data
  11967.  
  11968. {
  11969. private _unit = _x;
  11970.  
  11971. // Store unit's current backpack data
  11972. _unitData pushBack [_unit, backpack _unit, backpackItems _unit, backpackMagazines _unit];
  11973.  
  11974. // Calculate a slightly offset drop position for each unit
  11975. private _offsetDropPos = _dropPos getPos [random 25, random 360];
  11976. _offsetDropPos set [2, (_dropPos select 2) + (random 50)];
  11977.  
  11978. // Disable damage before teleporting to prevent accidental death
  11979. if (!isPlayer _unit) then {
  11980. _unit allowDamage false;
  11981. // NEW: Set flag to track parachute status
  11982. _unit setVariable ["parachuteGiven", false, true];
  11983. };
  11984.  
  11985. // Teleport unit
  11986. _unit setPosASL (AGLToASL _offsetDropPos);
  11987. _unit setVelocity [0, 0, 0];
  11988.  
  11989. // Only give the parachute to the human player. AI will get theirs later.
  11990. if (isPlayer _unit) then {
  11991. _unit addBackpack "B_Parachute";
  11992. };
  11993.  
  11994. } forEach _groupToDrop;
  11995.  
  11996. // Notify player
  11997. [format["Paradrop initiated for your squad! %1 points remaining.", _playerPoints - _cost]] remoteExecCall ["hint", owner _player];
  11998.  
  11999. // Monitor for landing and restore backpacks for the whole group
  12000. [_unitData] spawn {
  12001. params ["_allUnitData"];
  12002.  
  12003. while {count _allUnitData > 0} do {
  12004. {
  12005. private _dataEntry = _x;
  12006. private _unit = _dataEntry select 0;
  12007.  
  12008. if (!alive _unit) then {
  12009. _allUnitData = _allUnitData - [_dataEntry];
  12010. };
  12011.  
  12012. if (isPlayer _unit) then {
  12013. // This is the player unit - force their chute open at 120m as a failsafe
  12014. if (alive _unit && !isTouchingGround _unit && backpack _unit == "B_Parachute" && ((getPosATL _unit) select 2) <= 120) then {
  12015. _unit action ["OpenParachute", vehicle _unit];
  12016. };
  12017. } else {
  12018. // This is an AI unit - give them a chute and open it at 120m
  12019. // NEW: Check if parachute was already given using flag and vehicle check
  12020. private _parachuteGiven = _unit getVariable ["parachuteGiven", false];
  12021. private _vehicle = vehicle _unit;
  12022. private _isInParachute = (_vehicle != _unit && {_vehicle isKindOf "ParachuteBase"});
  12023.  
  12024. if (alive _unit && !isTouchingGround _unit && !_parachuteGiven && !_isInParachute && ((getPosATL _unit) select 2) <= 120) then {
  12025. _unit addBackpack "B_Parachute";
  12026. sleep 0.1;
  12027. _unit action ["OpenParachute", vehicle _unit];
  12028. // Set flag to prevent multiple parachutes
  12029. _unit setVariable ["parachuteGiven", true, true];
  12030. // Re-enable damage after the chute is open
  12031. _unit allowDamage true;
  12032. };
  12033. };
  12034.  
  12035. if (alive _unit && isTouchingGround _unit) then {
  12036. private _originalBackpack = _dataEntry select 1;
  12037. private _items = _dataEntry select 2;
  12038. private _magazines = _dataEntry select 3;
  12039.  
  12040. sleep 0.5;
  12041.  
  12042. // Failsafe in case unit lands without ever opening chute
  12043. if (!isPlayer _unit) then {
  12044. _unit allowDamage true;
  12045. // Clear the parachute flag
  12046. _unit setVariable ["parachuteGiven", nil, true];
  12047. };
  12048.  
  12049. if (_originalBackpack != "") then {
  12050. if (backpack _unit == "B_Parachute") then {
  12051. removeBackpack _unit;
  12052. };
  12053.  
  12054. _unit addBackpack _originalBackpack;
  12055. clearBackpackCargoGlobal (backpackContainer _unit);
  12056.  
  12057. { (backpackContainer _unit) addItemCargoGlobal [_x, 1]; } forEach _items;
  12058. { (backpackContainer _unit) addMagazineCargoGlobal [_x, 1]; } forEach _magazines;
  12059. };
  12060.  
  12061. _allUnitData = _allUnitData - [_dataEntry];
  12062. };
  12063. } forEach _allUnitData;
  12064.  
  12065. sleep 0.5;
  12066. };
  12067. };
  12068. };
  12069.  
  12070. // Server-side function to store respawn data and handle post-respawn processing
  12071. fnc_server_storeRespawnData = {
  12072. params ["_deadUnit", "_unitLoadout", "_playerPoints", "_teamkillCount", "_isPenaltyRespawn"];
  12073. if (!isServer) exitWith {};
  12074.  
  12075. private _playerUID = getPlayerUID _deadUnit;
  12076.  
  12077. // Store the data with a unique key for this specific death event
  12078. private _deathTime = time;
  12079. private _respawnDataKey = format["respawnData_%1_%2", _playerUID, _deathTime];
  12080.  
  12081. private _respawnData = createHashMap;
  12082. _respawnData set ["loadout", _unitLoadout];
  12083. _respawnData set ["points", _playerPoints];
  12084. _respawnData set ["teamkills", _teamkillCount];
  12085. _respawnData set ["penalty", _isPenaltyRespawn];
  12086. _respawnData set ["deathTime", _deathTime];
  12087.  
  12088. missionNamespace setVariable [_respawnDataKey, _respawnData, false];
  12089.  
  12090. // Monitor for this specific player's respawn using their UID and death time
  12091. [_playerUID, _deathTime] spawn {
  12092. params ["_uid", "_deathTime"];
  12093.  
  12094. private _timeout = time + 60;
  12095. private _respawnDataKey = format["respawnData_%1_%2", _uid, _deathTime];
  12096.  
  12097. while {time < _timeout} do {
  12098. // Look for a player with matching UID who respawned after the death time
  12099. private _respawnedPlayer = objNull;
  12100. {
  12101. if (isPlayer _x &&
  12102. getPlayerUID _x == _uid &&
  12103. alive _x &&
  12104. (_x getVariable ["spawnTime", 0]) > _deathTime) exitWith {
  12105. _respawnedPlayer = _x;
  12106. };
  12107. } forEach allPlayers;
  12108.  
  12109. if (!isNull _respawnedPlayer) exitWith {
  12110. private _storedData = missionNamespace getVariable [_respawnDataKey, createHashMap];
  12111.  
  12112. if (count _storedData > 0) then {
  12113. private _loadout = _storedData get "loadout";
  12114. private _points = _storedData get "points";
  12115. private _teamkills = _storedData get "teamkills";
  12116. private _penalty = _storedData get "penalty";
  12117.  
  12118. // Apply data to this specific player
  12119. _respawnedPlayer setUnitLoadout _loadout;
  12120. _respawnedPlayer setVariable ["playerPoints", _points - 1 max 0, true];
  12121. _respawnedPlayer setVariable ["teamkillCount", _teamkills, true];
  12122.  
  12123. if (_penalty) then {
  12124. _respawnedPlayer setVariable ["clearMyPenaltyFlag", true, true];
  12125. };
  12126.  
  12127. // Apply respawn actions to ONLY this player
  12128. private _playerOwner = owner _respawnedPlayer;
  12129. [_respawnedPlayer] remoteExecCall ["fnc_teleportToBase", _playerOwner, false];
  12130. [_respawnedPlayer] remoteExecCall ["fnc_initializePlayerUnit", _playerOwner, false];
  12131. [_respawnedPlayer] remoteExecCall ["fnc_applyRespawnProtection", _playerOwner, false];
  12132. [] remoteExecCall ["fnc_client_showBriefing", _playerOwner, false];
  12133.  
  12134. // Clean up
  12135. missionNamespace setVariable [_respawnDataKey, nil, false];
  12136. };
  12137. };
  12138.  
  12139. sleep 1;
  12140. };
  12141.  
  12142. // Cleanup on timeout
  12143. missionNamespace setVariable [_respawnDataKey, nil, false];
  12144. };
  12145. };
  12146.  
  12147. // Server-side function to handle a player choosing their side.
  12148. fnc_server_playerChooseSide = {
  12149. params ["_civilianUnit", "_chosenSide"];
  12150. if (!isServer) exitWith {};
  12151.  
  12152. // Handle case where civilian unit might be null (for rejoining players)
  12153. private _isRejoining = isNull _civilianUnit;
  12154.  
  12155. // Get player info differently based on whether they're rejoining
  12156. private _playerUID = "";
  12157. private _playerOwner = 0;
  12158. private _currentPlayerUnit = objNull;
  12159.  
  12160. if (_isRejoining) then {
  12161. // For rejoining players, we need to find them by the side they're joining
  12162. _playerUID = param [2, ""];
  12163. _playerOwner = param [3, 0];
  12164.  
  12165. // Find if this player already has a unit they're controlling
  12166. {
  12167. if (getPlayerUID _x == _playerUID) exitWith {
  12168. _currentPlayerUnit = _x;
  12169. };
  12170. } forEach allPlayers;
  12171. } else {
  12172. // Normal case - player choosing side from civilian
  12173. if (!alive _civilianUnit) exitWith {};
  12174. _playerUID = getPlayerUID _civilianUnit;
  12175. _playerOwner = owner _civilianUnit;
  12176. _currentPlayerUnit = _civilianUnit;
  12177. };
  12178.  
  12179. if (_playerUID != "") then {
  12180. (missionNamespace getVariable "PLAYER_TEAM_LOCKS") set [_playerUID, _chosenSide];
  12181. };
  12182.  
  12183. // Spawn the entire process to allow delays
  12184. [_civilianUnit, _chosenSide, _playerUID, _playerOwner, _isRejoining, _currentPlayerUnit] spawn {
  12185. params ["_civilianUnit", "_chosenSide", "_playerUID", "_playerOwner", "_isRejoining", "_currentPlayerUnit"];
  12186.  
  12187. private _officerType = if (_chosenSide == west) then {"B_officer_F"} else {"O_officer_F"};
  12188. private _spawnObj = if (_chosenSide == west) then {bluforSpawnObj} else {opforSpawnObj};
  12189. private _spawnPos = getPosATL _spawnObj;
  12190. _spawnPos set [2, 0.5];
  12191.  
  12192. private _playerGroup = createGroup _chosenSide;
  12193. _playerGroup setGroupOwner _playerOwner;
  12194.  
  12195. sleep 0.1;
  12196.  
  12197. private _newUnit = _playerGroup createUnit [_officerType, _spawnPos, [], 0, "NONE"];
  12198.  
  12199. // Mark the spawn time for respawn tracking
  12200. _newUnit setVariable ["spawnTime", time, true];
  12201.  
  12202. [_newUnit] call fnc_server_initializePlayer;
  12203.  
  12204. sleep 0.2;
  12205.  
  12206. // Switch player control
  12207. [_newUnit] remoteExecCall ["fnc_client_completeSideSwitch", _playerOwner];
  12208.  
  12209. // Wait for the switch to complete before deleting old unit
  12210. private _timeout = time + 15;
  12211. waitUntil {(_newUnit getVariable ["sideSwitchComplete", false]) || time > _timeout};
  12212.  
  12213. // Delete the old unit (whether it's civilian or any other unit the player was controlling)
  12214. if (!isNull _currentPlayerUnit && _currentPlayerUnit != _newUnit) then {
  12215. deleteVehicle _currentPlayerUnit;
  12216. };
  12217. };
  12218. };
  12219.  
  12220. [] spawn {
  12221. if (!isServer) exitWith {};
  12222.  
  12223. while {true} do {
  12224. sleep 5;
  12225. {
  12226. if (_x getVariable ["clearMyPenaltyFlag", false]) then {
  12227. _x setVariable ["penaltyRespawn", false, true];
  12228. _x setVariable ["clearMyPenaltyFlag", false, true];
  12229. };
  12230. } forEach playableUnits;
  12231. };
  12232. };
  12233.  
  12234. fnc_server_purchaseAISquad = {
  12235. params ["_player"];
  12236. if (!isServer) exitWith {};
  12237.  
  12238. private _cost = missionNamespace getVariable ["PLAYER_SQUAD_COST", 15];
  12239. private _cooldown = missionNamespace getVariable ["PLAYER_SQUAD_COOLDOWN", 120];
  12240. private _lastPurchaseTime = _player getVariable ["lastAISquadPurchaseTime", 0];
  12241.  
  12242. // Cooldown check
  12243. if (time < _lastPurchaseTime + _cooldown) exitWith {
  12244. private _timeLeft = round((_lastPurchaseTime + _cooldown) - time);
  12245. [format["AI Squad purchase on cooldown. Time remaining: %1s", _timeLeft]] remoteExecCall ["hint", owner _player];
  12246. };
  12247.  
  12248. // Cost check
  12249. private _playerPoints = _player getVariable ["playerPoints", 0];
  12250. if (_playerPoints < _cost) exitWith {
  12251. [format["Not enough points to purchase an AI squad. Cost: %1, You have: %2.", _cost, _playerPoints]] remoteExecCall ["hint", owner _player];
  12252. };
  12253.  
  12254. // Deduct points
  12255. _player setVariable ["playerPoints", _playerPoints - _cost, true];
  12256.  
  12257. private _playerGroup = group _player;
  12258. private _playerSide = side _player;
  12259.  
  12260. // Wipe existing group members (except player)
  12261. {
  12262. if (_x != _player) then {
  12263. private _vehicle = vehicle _x;
  12264. // If the AI is in a parachute, delete the parachute as well.
  12265. if (_vehicle != _x && {_vehicle isKindOf "ParachuteBase"}) then {
  12266. deleteVehicle _vehicle;
  12267. };
  12268. deleteVehicle _x;
  12269. };
  12270. } forEach (units _playerGroup);
  12271.  
  12272. // Select unit pool
  12273. private _reconPool = if (_playerSide == west) then { BLUFOR_SPECOPS_POOL } else { OPFOR_SPECOPS_POOL };
  12274. if (isNil "_reconPool" || {count _reconPool == 0}) exitWith { diag_log "ERROR: Recon pool not found for player's side."; };
  12275.  
  12276. private _squadComposition = selectRandom _reconPool;
  12277.  
  12278. // Spawn 3 random recon units
  12279. for "_i" from 1 to 3 do {
  12280. private _unitType = selectRandom _squadComposition;
  12281. private _spawnPos = getPos _player;
  12282.  
  12283. private _unit = _playerGroup createUnit [_unitType, _spawnPos, [], 5, "NONE"];
  12284.  
  12285. // Apply recon AI settings from fnc_spawnBluforSpecOps
  12286. [_unit, 0.75, "SPECOPS"] call fnc_enhanceIndividualAI;
  12287. _unit setVariable ["isSpecOps", true, true];
  12288. _unit setVariable ["unitValue", SPECOPS_VALUE, true];
  12289. [_unit] call fnc_markAsImportant;
  12290. [_unit, _playerSide] call fnc_updateStrengthTally; // Count towards maxAI/strength
  12291. _unit setVariable ["ownerPlayer", _player, true]; // TAG THE AI WITH ITS OWNER
  12292. };
  12293.  
  12294. // Check and enforce hard AI cap after spawning
  12295. private _sideAICount = count (allUnits select {side _x == _playerSide && !isPlayer _x && alive _x});
  12296. private _hardCap = maxAI + 10;
  12297.  
  12298. if (_sideAICount > _hardCap) then {
  12299. private _unitsToDeleteCount = _sideAICount - _hardCap;
  12300. systemChat format ["%1 side is over AI hard cap. Culling %2 units.", _playerSide, _unitsToDeleteCount];
  12301.  
  12302. // Find all AI on the team, excluding the player's own group
  12303. private _potentialUnitsToDelete = allUnits select {
  12304. side _x == _playerSide &&
  12305. !isPlayer _x &&
  12306. alive _x &&
  12307. group _x != _playerGroup
  12308. };
  12309.  
  12310. // Prioritize deleting standard infantry first
  12311. private _standardInfantry = _potentialUnitsToDelete select {
  12312. (_x getVariable ["unitValue", 999]) == INFANTRY_VALUE
  12313. };
  12314.  
  12315. // Delete the required number of units
  12316. for "_i" from 1 to _unitsToDeleteCount do {
  12317. if (count _standardInfantry > 0) then {
  12318. private _unitToDelete = _standardInfantry select 0; // Get the first one
  12319. deleteVehicle _unitToDelete;
  12320. _standardInfantry deleteAt 0; // Remove it from the list
  12321. } else {
  12322. diag_log "AI hard cap reached, but no more standard infantry to delete.";
  12323. break;
  12324. };
  12325. };
  12326. };
  12327.  
  12328. // Apply cooldown AFTER successful purchase
  12329. _player setVariable ["lastAISquadPurchaseTime", time, true];
  12330.  
  12331. [format["AI Recon Squad purchased for %1 points. %2 points remaining.", _cost, _player getVariable "playerPoints"]] remoteExecCall ["hint", owner _player];
  12332. };
  12333. // =====================================================================
  12334. // Admin/Host/Singleplayer Settings Functions
  12335. // =====================================================================
  12336.  
  12337. // Function to set the max AI count
  12338. fnc_server_setMaxAI = {
  12339. params ["_count"];
  12340. if (!isServer) exitWith {}; // Only server can change this
  12341.  
  12342. maxAI = _count;
  12343. publicVariable "maxAI";
  12344.  
  12345. hint format ["Server: Max AI count has been set to %1.", maxAI];
  12346. };
  12347.  
  12348. // Function to set the time multiplier
  12349. fnc_server_setTimescale = {
  12350. params ["_multiplier"];
  12351. if (!isServer) exitWith {}; // Only server can change this
  12352.  
  12353. setTimeMultiplier _multiplier;
  12354.  
  12355. private _text = "Realtime";
  12356. if (_multiplier == 12) then { _text = "2h = 1 Day"; };
  12357. if (_multiplier == 24) then { _text = "1h = 1 Day"; };
  12358.  
  12359. hint format ["Server: Timescale set to %1.", _text];
  12360. };
  12361.  
  12362. // Function to adjust team points (SP only)
  12363. fnc_server_adjustTeamPoints = {
  12364. params ["_sideStr", "_amount"];
  12365. if (!isServer || isMultiplayer) exitWith {}; // Only server in singleplayer
  12366.  
  12367. if (_sideStr == "BLUFOR") then {
  12368. BLUFOR_POINTS = BLUFOR_POINTS + _amount;
  12369. publicVariable "BLUFOR_POINTS";
  12370. hint format ["BLUFOR points adjusted by %1. New total: %2", _amount, round BLUFOR_POINTS];
  12371. } else {
  12372. OPFOR_POINTS = OPFOR_POINTS + _amount;
  12373. publicVariable "OPFOR_POINTS";
  12374. hint format ["OPFOR points adjusted by %1. New total: %2", _amount, round OPFOR_POINTS];
  12375. };
  12376. };
  12377.  
  12378. // Function to add personal points to the player (SP only)
  12379. fnc_server_addPersonalPoints = {
  12380. params ["_player", "_amount"];
  12381. if (!isServer || isMultiplayer) exitWith {}; // Only server in singleplayer
  12382.  
  12383. private _currentPoints = _player getVariable ["playerPoints", 0];
  12384. _player setVariable ["playerPoints", _currentPoints + _amount, true];
  12385.  
  12386. [format ["Added %1 personal points. You now have %2.", _amount, _player getVariable 'playerPoints']] remoteExecCall ["hint", owner _player];
  12387. };
  12388.  
  12389. // Function to delete AI squad members upon player's death
  12390. fnc_server_deletePlayerAISquad = {
  12391. params ["_unitsToDelete"];
  12392. if (!isServer) exitWith {};
  12393.  
  12394. {
  12395. // Ensure the unit object is not null and is a valid AI before deleting
  12396. if (!isNull _x && !isPlayer _x) then {
  12397. private _vehicle = vehicle _x;
  12398. // If the AI is in a parachute, delete the parachute as well.
  12399. if (_vehicle != _x && {_vehicle isKindOf "ParachuteBase"}) then {
  12400. deleteVehicle _vehicle;
  12401. };
  12402. deleteVehicle _x;
  12403. };
  12404. } forEach _unitsToDelete;
  12405. };
  12406.  
  12407. ==================== END OF: player_server.sqf ====================
  12408.  
  12409.  
  12410.  
  12411. ==================== START OF: strategy.sqf ====================
  12412.  
  12413. // strategy.sqf
  12414. // Strategic thinking and outcome prediction for High Command AI
  12415.  
  12416. // Initialize strategy variables
  12417. if (isNil "BLUFOR_STRATEGIC_PLAN") then { BLUFOR_STRATEGIC_PLAN = createHashMap; };
  12418. if (isNil "OPFOR_STRATEGIC_PLAN") then { OPFOR_STRATEGIC_PLAN = createHashMap; };
  12419. if (isNil "BLUFOR_HELD_POSITIONS") then { BLUFOR_HELD_POSITIONS = []; };
  12420. if (isNil "OPFOR_HELD_POSITIONS") then { OPFOR_HELD_POSITIONS = []; };
  12421.  
  12422. // Find high ground positions on the battlefield
  12423. fnc_findHighGroundPositions = {
  12424. params ["_side", "_centerPos", "_radius"];
  12425.  
  12426. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  12427. private _enemyBase = if (_side == west) then {getPos opforSpawnObj} else {getPos bluforSpawnObj};
  12428.  
  12429. private _highGroundPositions = [];
  12430. private _searchPositions = [];
  12431.  
  12432. // Create search grid around center position
  12433. for "_angle" from 0 to 315 step 45 do {
  12434. for "_dist" from 300 to _radius step 200 do {
  12435. private _testPos = _centerPos getPos [_dist, _angle];
  12436. _searchPositions pushBack _testPos;
  12437. };
  12438. };
  12439.  
  12440. // Evaluate each position for elevation
  12441. {
  12442. private _pos = _x;
  12443. private _height = getTerrainHeightASL _pos;
  12444.  
  12445. // Check surrounding area is lower (confirms it's high ground)
  12446. private _surroundingHeights = [];
  12447. for "_i" from 0 to 7 do {
  12448. private _checkPos = _pos getPos [50, _i * 45];
  12449. _surroundingHeights pushBack (getTerrainHeightASL _checkPos);
  12450. };
  12451.  
  12452. private _avgSurroundingHeight = 0;
  12453. {_avgSurroundingHeight = _avgSurroundingHeight + _x;} forEach _surroundingHeights;
  12454. _avgSurroundingHeight = _avgSurroundingHeight / count _surroundingHeights;
  12455.  
  12456. // If this position is higher than surrounding area
  12457. if (_height > (_avgSurroundingHeight + 10) && !surfaceIsWater _pos) then {
  12458. private _highGroundData = createHashMap;
  12459. _highGroundData set ["position", _pos];
  12460. _highGroundData set ["elevation", _height];
  12461. _highGroundData set ["elevationAdvantage", _height - _avgSurroundingHeight];
  12462. _highGroundData set ["distanceToFront", _pos distance2D _centerPos];
  12463. _highGroundData set ["distanceToEnemyBase", _pos distance2D _enemyBase];
  12464. _highGroundData set ["distanceToOwnBase", _pos distance2D _ownBase];
  12465.  
  12466. _highGroundPositions pushBack _highGroundData;
  12467. };
  12468. } forEach _searchPositions;
  12469.  
  12470. // Sort by tactical value (elevation advantage + proximity to battle)
  12471. _highGroundPositions = [_highGroundPositions, [], {
  12472. private _elevScore = (_x get "elevationAdvantage") * 2;
  12473. private _distScore = 1000 - ((_x get "distanceToFront") min 1000);
  12474. _elevScore + _distScore
  12475. }, "DESCEND"] call BIS_fnc_sortBy;
  12476.  
  12477. // Return top 3 positions
  12478. _highGroundPositions select [0, 3 min count _highGroundPositions]
  12479. };
  12480.  
  12481. // Find containment positions around enemy base to block reinforcements
  12482. fnc_findContainmentPositions = {
  12483. params ["_side", "_enemyBase"];
  12484.  
  12485. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  12486.  
  12487. // Calculate the direction from enemy base to our base (their reinforcement route)
  12488. private _reinforcementAxis = _enemyBase getDir _ownBase;
  12489.  
  12490. private _containmentPositions = [];
  12491. private _containmentRadius = 800; // 800m from enemy base
  12492.  
  12493. // Create 5 containment positions in an arc facing our base (where enemies come from)
  12494. // This blocks the enemy's path between their base and the battlefield
  12495. for "_i" from 0 to 4 do {
  12496. // Spread positions in a 120-degree arc centered on the reinforcement axis
  12497. private _angleOffset = -60 + (_i * 30); // -60, -30, 0, 30, 60 degrees
  12498. private _positionAngle = _reinforcementAxis + _angleOffset;
  12499.  
  12500. private _testPos = _enemyBase getPos [_containmentRadius, _positionAngle];
  12501.  
  12502. // Validate position
  12503. if (!surfaceIsWater _testPos) then {
  12504. private _height = getTerrainHeightASL _testPos;
  12505.  
  12506. private _containmentData = createHashMap;
  12507. _containmentData set ["position", _testPos];
  12508. _containmentData set ["angle", _positionAngle];
  12509. _containmentData set ["arcPosition", _i]; // 0=far left, 2=center, 4=far right
  12510. _containmentData set ["elevation", _height];
  12511. _containmentData set ["distanceToEnemyBase", _testPos distance2D _enemyBase];
  12512. _containmentData set ["distanceToOwnBase", _testPos distance2D _ownBase];
  12513.  
  12514. _containmentPositions pushBack _containmentData;
  12515. };
  12516. };
  12517.  
  12518. _containmentPositions
  12519. };
  12520.  
  12521. // Find nearby friendly groups that could assist an objective
  12522. fnc_findNearbyReinforcements = {
  12523. params ["_side", "_objectivePos", "_excludeGroups", "_maxDistance"];
  12524.  
  12525. private _allFriendlyGroups = allGroups select {
  12526. side _x == _side &&
  12527. count (units _x) > 0 &&
  12528. ({isPlayer _x} count (units _x) == 0) &&
  12529. !(_x in _excludeGroups)
  12530. };
  12531.  
  12532. private _nearbyGroups = [];
  12533.  
  12534. {
  12535. private _group = _x;
  12536. private _leader = leader _group;
  12537.  
  12538. if (!isNull _leader && alive _leader) then {
  12539. private _distance = _leader distance2D _objectivePos;
  12540.  
  12541. if (_distance <= _maxDistance) then {
  12542. // Check if group is locked or busy
  12543. private _isLocked = [_group] call fnc_isGroupLocked;
  12544.  
  12545. if (!_isLocked) then {
  12546. private _reinforcementData = createHashMap;
  12547. _reinforcementData set ["group", _group];
  12548. _reinforcementData set ["distance", _distance];
  12549. _reinforcementData set ["type", [_group] call fnc_classifyGroup];
  12550. _reinforcementData set ["strength", count (units _group select {alive _x})];
  12551.  
  12552. _nearbyGroups pushBack _reinforcementData;
  12553. };
  12554. };
  12555. };
  12556. } forEach _allFriendlyGroups;
  12557.  
  12558. // Sort by distance (closest first)
  12559. _nearbyGroups = [_nearbyGroups, [], {_x get "distance"}, "ASCEND"] call BIS_fnc_sortBy;
  12560.  
  12561. _nearbyGroups
  12562. };
  12563.  
  12564. // Calculate minimum defenders needed for base
  12565. fnc_calculateMinimumDefenders = {
  12566. params ["_side"];
  12567.  
  12568. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  12569. private _enemySide = if (_side == west) then {east} else {west};
  12570.  
  12571. // Count enemies near base
  12572. private _nearbyEnemies = _ownBase nearEntities [["CAManBase", "LandVehicle", "Air"], 1000];
  12573. private _enemyCount = {side _x == _enemySide && alive _x} count _nearbyEnemies;
  12574.  
  12575. // Base minimum defenders
  12576. private _minDefenders = 2;
  12577.  
  12578. // Add defenders based on threat level
  12579. if (_enemyCount > 20) then {
  12580. _minDefenders = 6;
  12581. } else {
  12582. if (_enemyCount > 10) then {
  12583. _minDefenders = 4;
  12584. } else {
  12585. if (_enemyCount > 5) then {
  12586. _minDefenders = 3;
  12587. };
  12588. };
  12589. };
  12590.  
  12591. // Check time - more defenders at night
  12592. if (sunOrMoon < 0.3) then {
  12593. _minDefenders = _minDefenders + 1;
  12594. };
  12595.  
  12596. _minDefenders
  12597. };
  12598.  
  12599. // Check if base has adequate defenders
  12600. fnc_checkBaseDefense = {
  12601. params ["_side"];
  12602.  
  12603. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  12604. private _minDefenders = [_side] call fnc_calculateMinimumDefenders;
  12605.  
  12606. // Count current defenders
  12607. private _currentDefenders = 0;
  12608. {
  12609. if (side _x == _side && count (units _x) > 0) then {
  12610. private _role = _x getVariable ["HC_ROLE", ""];
  12611. if (_role == "DEFENDER" || _role == "BASE_DEFENSE") then {
  12612. _currentDefenders = _currentDefenders + 1;
  12613. } else {
  12614. // Also count groups very close to base as implicit defenders
  12615. private _leader = leader _x;
  12616. if (!isNull _leader && alive _leader && (_leader distance2D _ownBase) < 300) then {
  12617. _currentDefenders = _currentDefenders + 1;
  12618. };
  12619. };
  12620. };
  12621. } forEach allGroups;
  12622.  
  12623. private _defenseStatus = createHashMap;
  12624. _defenseStatus set ["currentDefenders", _currentDefenders];
  12625. _defenseStatus set ["minimumDefenders", _minDefenders];
  12626. _defenseStatus set ["needsReinforcement", _currentDefenders < _minDefenders];
  12627. _defenseStatus set ["defenderShortfall", (_minDefenders - _currentDefenders) max 0];
  12628.  
  12629. _defenseStatus
  12630. };
  12631.  
  12632. // Create defensive perimeter for recon groups
  12633. fnc_createReconPerimeter = {
  12634. params ["_reconGroup", "_centerPos", "_radius"];
  12635.  
  12636. // Create 4 waypoints in a diamond pattern around center
  12637. while {count (waypoints _reconGroup) > 0} do {
  12638. deleteWaypoint ((waypoints _reconGroup) select 0);
  12639. };
  12640.  
  12641. private _angles = [0, 90, 180, 270];
  12642.  
  12643. {
  12644. private _angle = _x;
  12645. private _perimeterPos = _centerPos getPos [_radius, _angle];
  12646.  
  12647. // Try to find cover at this position
  12648. private _coverPos = [_perimeterPos, 0, 30, 3, 0, 0.7, 0] call BIS_fnc_findSafePos;
  12649. if (count _coverPos == 0) then {
  12650. _coverPos = _perimeterPos;
  12651. };
  12652.  
  12653. private _wp = _reconGroup addWaypoint [_coverPos, 10];
  12654. _wp setWaypointType "MOVE";
  12655. _wp setWaypointBehaviour "STEALTH";
  12656. _wp setWaypointSpeed "LIMITED";
  12657. _wp setWaypointCombatMode "YELLOW";
  12658. _wp setWaypointCompletionRadius 15;
  12659.  
  12660. if (_forEachIndex == (count _angles - 1)) then {
  12661. _wp setWaypointStatements ["true", ""];
  12662. };
  12663. } forEach _angles;
  12664.  
  12665. // Add cycle waypoint to patrol the perimeter
  12666. if (count (waypoints _reconGroup) > 0) then {
  12667. private _cycleWp = _reconGroup addWaypoint [waypointPosition [_reconGroup, 0], 0];
  12668. _cycleWp setWaypointType "CYCLE";
  12669. };
  12670.  
  12671. _reconGroup setVariable ["HC_PERIMETER_CENTER", _centerPos, true];
  12672. _reconGroup setVariable ["HC_PERIMETER_RADIUS", _radius, true];
  12673. };
  12674.  
  12675. // Evaluate potential objectives from intel
  12676. fnc_identifyPotentialObjectives = {
  12677. params ["_side"];
  12678.  
  12679. private _intel = if (_side == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  12680. private _enemyBase = if (_side == west) then {getPos opforSpawnObj} else {getPos bluforSpawnObj};
  12681. private _ownBase = if (_side == west) then {getPos bluforSpawnObj} else {getPos opforSpawnObj};
  12682.  
  12683. private _objectives = [];
  12684. private _enemyPositions = [];
  12685.  
  12686. // Collect recent enemy positions from intel
  12687. {
  12688. private _report = _y;
  12689. private _age = time - (_report get "time");
  12690. if (_age < 120) then {
  12691. _enemyPositions pushBack (_report get "position");
  12692. };
  12693. } forEach _intel;
  12694.  
  12695. // Calculate battle center from enemy positions
  12696. private _battleCenter = _enemyBase;
  12697. if (count _enemyPositions > 0) then {
  12698. private _avgX = 0;
  12699. private _avgY = 0;
  12700. {
  12701. _avgX = _avgX + (_x select 0);
  12702. _avgY = _avgY + (_x select 1);
  12703. } forEach _enemyPositions;
  12704. _battleCenter = [_avgX / count _enemyPositions, _avgY / count _enemyPositions, 0];
  12705. };
  12706.  
  12707. // PRIORITY 1: Base Defense (always consider this)
  12708. private _defenseStatus = [_side] call fnc_checkBaseDefense;
  12709. if (_defenseStatus get "needsReinforcement") then {
  12710. private _defendObjective = createHashMap;
  12711. _defendObjective set ["position", _ownBase];
  12712. _defendObjective set ["type", "BASE_DEFENSE"];
  12713. _defendObjective set ["enemyCount", 0];
  12714. _defendObjective set ["priority", 100]; // Highest priority
  12715. _defendObjective set ["requiredGroups", _defenseStatus get "defenderShortfall"];
  12716. _objectives pushBack _defendObjective;
  12717. };
  12718.  
  12719. // PRIORITY 2: CONTAINMENT - Surround enemy base to block their reinforcements
  12720. // This is the key strategic objective to win
  12721. private _heldPositions = if (_side == west) then {BLUFOR_HELD_POSITIONS} else {OPFOR_HELD_POSITIONS};
  12722. private _containmentPositions = [_side, _enemyBase] call fnc_findContainmentPositions;
  12723.  
  12724. {
  12725. private _containmentData = _x;
  12726. private _containmentPos = _containmentData get "position";
  12727.  
  12728. // Check if we already hold this containment position
  12729. private _alreadyHeld = false;
  12730. {
  12731. if ((_x distance2D _containmentPos) < 150) then {
  12732. _alreadyHeld = true;
  12733. };
  12734. } forEach _heldPositions;
  12735.  
  12736. if (!_alreadyHeld) then {
  12737. private _containmentObjective = createHashMap;
  12738. _containmentObjective set ["position", _containmentPos];
  12739. _containmentObjective set ["type", "CONTAINMENT"];
  12740. _containmentObjective set ["enemyCount", 3]; // Assume moderate resistance
  12741. _containmentObjective set ["priority", 85]; // Very high priority - key to victory
  12742. _containmentObjective set ["arcPosition", _containmentData get "arcPosition"];
  12743. _containmentObjective set ["requiresHolding", true]; // Must hold position
  12744. _objectives pushBack _containmentObjective;
  12745. };
  12746. } forEach _containmentPositions;
  12747.  
  12748. // PRIORITY 3: High Ground positions for snipers/elite
  12749. private _highGroundPositions = [_side, _battleCenter, 2000] call fnc_findHighGroundPositions;
  12750.  
  12751. {
  12752. private _hgData = _x;
  12753. private _hgPos = _hgData get "position";
  12754.  
  12755. // Check if we already hold this position
  12756. private _alreadyHeld = false;
  12757. {
  12758. if ((_x distance2D _hgPos) < 100) then {
  12759. _alreadyHeld = true;
  12760. };
  12761. } forEach _heldPositions;
  12762.  
  12763. if (!_alreadyHeld) then {
  12764. private _hgObjective = createHashMap;
  12765. _hgObjective set ["position", _hgPos];
  12766. _hgObjective set ["type", "HIGH_GROUND"];
  12767. _hgObjective set ["enemyCount", 2]; // Assume light resistance
  12768. _hgObjective set ["priority", 70];
  12769. _hgObjective set ["elevationAdvantage", _hgData get "elevationAdvantage"];
  12770. _hgObjective set ["requiresSpecialized", true]; // Needs sniper/elite
  12771. _objectives pushBack _hgObjective;
  12772. };
  12773. } forEach _highGroundPositions;
  12774.  
  12775. // PRIORITY 4: Enemy concentrations from intel
  12776. if (count _enemyPositions > 0) then {
  12777. private _clusters = [_enemyPositions, 200] call fnc_clusterPositions;
  12778.  
  12779. {
  12780. private _cluster = _x;
  12781. if (count _cluster >= 3) then {
  12782. // Calculate cluster center
  12783. private _avgX = 0;
  12784. private _avgY = 0;
  12785. {
  12786. _avgX = _avgX + (_x select 0);
  12787. _avgY = _avgY + (_x select 1);
  12788. } forEach _cluster;
  12789.  
  12790. private _centerPos = [
  12791. _avgX / count _cluster,
  12792. _avgY / count _cluster,
  12793. 0
  12794. ];
  12795.  
  12796. // Determine if this is behind enemy lines
  12797. private _distToEnemyBase = _centerPos distance2D _enemyBase;
  12798. private _distToOwnBase = _centerPos distance2D _ownBase;
  12799. private _isBehindEnemyLines = _distToEnemyBase < (_distToOwnBase * 0.5);
  12800.  
  12801. private _objectiveType = if (_isBehindEnemyLines) then {
  12802. "ENEMY_REAR"
  12803. } else {
  12804. "ENEMY_CONCENTRATION"
  12805. };
  12806.  
  12807. // Create objective for this enemy concentration
  12808. private _objective = createHashMap;
  12809. _objective set ["position", _centerPos];
  12810. _objective set ["type", _objectiveType];
  12811. _objective set ["enemyCount", count _cluster];
  12812. _objective set ["priority", if (_isBehindEnemyLines) then {60} else {50}];
  12813.  
  12814. if (_objectiveType == "ENEMY_REAR") then {
  12815. _objective set ["requiresReconPerimeter", true];
  12816. };
  12817.  
  12818. _objectives pushBack _objective;
  12819. };
  12820. } forEach _clusters;
  12821. };
  12822.  
  12823. // PRIORITY 5: Enemy base assault (final objective after containment)
  12824. private _baseObjective = createHashMap;
  12825. _baseObjective set ["position", _enemyBase];
  12826. _baseObjective set ["type", "ENEMY_BASE"];
  12827. _baseObjective set ["enemyCount", 15]; // Assume base is well defended
  12828. _baseObjective set ["priority", 40];
  12829. _objectives pushBack _baseObjective;
  12830.  
  12831. // PRIORITY 6: Defensive positions between own base and battle front
  12832. if (count _enemyPositions > 0) then {
  12833. private _dirToEnemy = _ownBase getDir _battleCenter;
  12834. private _distToFront = _ownBase distance2D _battleCenter;
  12835.  
  12836. // Create defensive line halfway to battle
  12837. private _defensivePos = _ownBase getPos [_distToFront * 0.4, _dirToEnemy];
  12838.  
  12839. private _defensiveObjective = createHashMap;
  12840. _defensiveObjective set ["position", _defensivePos];
  12841. _defensiveObjective set ["type", "DEFENSIVE_LINE"];
  12842. _defensiveObjective set ["enemyCount", 0];
  12843. _defensiveObjective set ["priority", 55];
  12844. _objectives pushBack _defensiveObjective;
  12845. };
  12846.  
  12847. _objectives
  12848. };
  12849.  
  12850. // Predict outcome of sending forces to an objective
  12851. fnc_predictBattleOutcome = {
  12852. params ["_side", "_objective", "_assignedGroups", ["_reinforcements", []]];
  12853.  
  12854. private _objectivePos = _objective get "position";
  12855. private _enemyCount = _objective get "enemyCount";
  12856. private _objectiveType = _objective get "type";
  12857.  
  12858. // Calculate friendly force strength including reinforcements
  12859. private _friendlyStrength = 0;
  12860. private _friendlyUnits = 0;
  12861. private _hasArmor = false;
  12862. private _hasAir = false;
  12863. private _hasSnipers = false;
  12864. private _hasElite = false;
  12865.  
  12866. private _allGroups = _assignedGroups + _reinforcements;
  12867.  
  12868. {
  12869. private _group = _x;
  12870. private _groupType = [_group] call fnc_classifyGroup;
  12871. private _unitCount = count (units _group select {alive _x});
  12872.  
  12873. _friendlyUnits = _friendlyUnits + _unitCount;
  12874.  
  12875. switch (_groupType) do {
  12876. case "ARMOR": {
  12877. _friendlyStrength = _friendlyStrength + (_unitCount * 4);
  12878. _hasArmor = true;
  12879. };
  12880. case "MECHANIZED": {
  12881. _friendlyStrength = _friendlyStrength + (_unitCount * 2);
  12882. };
  12883. case "AIR": {
  12884. _friendlyStrength = _friendlyStrength + (_unitCount * 3);
  12885. _hasAir = true;
  12886. };
  12887. case "ELITE": {
  12888. _friendlyStrength = _friendlyStrength + (_unitCount * 1.8);
  12889. _hasElite = true;
  12890. };
  12891. case "SPECOPS": {
  12892. _friendlyStrength = _friendlyStrength + (_unitCount * 1.5);
  12893. };
  12894. case "SNIPER": {
  12895. _friendlyStrength = _friendlyStrength + (_unitCount * 1.5);
  12896. _hasSnipers = true;
  12897. };
  12898. default {
  12899. _friendlyStrength = _friendlyStrength + _unitCount;
  12900. };
  12901. };
  12902. } forEach _allGroups;
  12903.  
  12904. // Estimate enemy strength at objective
  12905. private _enemyStrength = _enemyCount * 1.2;
  12906.  
  12907. // Adjust for objective type
  12908. switch (_objectiveType) do {
  12909. case "ENEMY_BASE": {
  12910. _enemyStrength = _enemyStrength * 1.5; // Base defenders have advantage
  12911. };
  12912. case "HIGH_GROUND": {
  12913. _enemyStrength = _enemyStrength * 0.8; // Usually lightly defended
  12914. // Bonus if we have snipers for high ground
  12915. if (_hasSnipers || _hasElite) then {
  12916. _friendlyStrength = _friendlyStrength * 1.2;
  12917. };
  12918. };
  12919. case "DEFENSIVE_LINE": {
  12920. _enemyStrength = 0; // Setting up defense, no immediate enemy
  12921. };
  12922. case "BASE_DEFENSE": {
  12923. _enemyStrength = _enemyCount * 1.3; // Attackers have initiative
  12924. };
  12925. case "ENEMY_REAR": {
  12926. _enemyStrength = _enemyStrength * 1.1; // Slightly harder (surrounded)
  12927. // SpecOps bonus for behind enemy lines
  12928. if (_hasElite) then {
  12929. _friendlyStrength = _friendlyStrength * 1.15;
  12930. };
  12931. };
  12932. };
  12933.  
  12934. // Calculate force ratio
  12935. private _forceRatio = if (_enemyStrength > 0) then {
  12936. _friendlyStrength / _enemyStrength
  12937. } else {
  12938. 999
  12939. };
  12940.  
  12941. // Predict success probability (0 to 1)
  12942. private _successProbability = 0;
  12943. if (_forceRatio >= 2.5) then {
  12944. _successProbability = 0.95;
  12945. } else {
  12946. if (_forceRatio >= 2.0) then {
  12947. _successProbability = 0.85;
  12948. } else {
  12949. if (_forceRatio >= 1.5) then {
  12950. _successProbability = 0.7;
  12951. } else {
  12952. if (_forceRatio >= 1.0) then {
  12953. _successProbability = 0.5;
  12954. } else {
  12955. if (_forceRatio >= 0.7) then {
  12956. _successProbability = 0.3;
  12957. } else {
  12958. _successProbability = 0.1;
  12959. };
  12960. };
  12961. };
  12962. };
  12963. };
  12964.  
  12965. // Predict expected casualties (as percentage of friendly force)
  12966. private _expectedCasualties = 0;
  12967. if (_forceRatio >= 2.5) then {
  12968. _expectedCasualties = 0.05;
  12969. } else {
  12970. if (_forceRatio >= 2.0) then {
  12971. _expectedCasualties = 0.1;
  12972. } else {
  12973. if (_forceRatio >= 1.5) then {
  12974. _expectedCasualties = 0.2;
  12975. } else {
  12976. if (_forceRatio >= 1.0) then {
  12977. _expectedCasualties = 0.35;
  12978. } else {
  12979. if (_forceRatio >= 0.7) then {
  12980. _expectedCasualties = 0.5;
  12981. } else {
  12982. _expectedCasualties = 0.7;
  12983. };
  12984. };
  12985. };
  12986. };
  12987. };
  12988.  
  12989. // Adjust for tactical advantages
  12990. if (_hasArmor && !(_objectiveType in ["ENEMY_BASE", "HIGH_GROUND"])) then {
  12991. _successProbability = _successProbability + 0.1;
  12992. _expectedCasualties = _expectedCasualties * 0.8;
  12993. };
  12994.  
  12995. if (_hasAir) then {
  12996. _successProbability = _successProbability + 0.05;
  12997. _expectedCasualties = _expectedCasualties * 0.9;
  12998. };
  12999.  
  13000. // Reinforcement bonus
  13001. if (count _reinforcements > 0) then {
  13002. _successProbability = _successProbability + 0.1;
  13003. _expectedCasualties = _expectedCasualties * 0.85;
  13004. };
  13005.  
  13006. // Clamp values
  13007. _successProbability = (_successProbability min 0.95) max 0.05;
  13008. _expectedCasualties = (_expectedCasualties min 0.9) max 0.02;
  13009.  
  13010. // Return prediction
  13011. private _prediction = createHashMap;
  13012. _prediction set ["successProbability", _successProbability];
  13013. _prediction set ["expectedCasualties", _expectedCasualties];
  13014. _prediction set ["expectedCasualtyCount", round (_friendlyUnits * _expectedCasualties)];
  13015. _prediction set ["forceRatio", _forceRatio];
  13016. _prediction set ["friendlyStrength", _friendlyStrength];
  13017. _prediction set ["enemyStrength", _enemyStrength];
  13018. _prediction set ["hasReinforcements", count _reinforcements > 0];
  13019. _prediction set ["reinforcementCount", count _reinforcements];
  13020.  
  13021. _prediction
  13022. };
  13023.  
  13024. // Evaluate risk vs reward for an objective
  13025. fnc_evaluateObjectiveValue = {
  13026. params ["_side", "_objective", "_prediction"];
  13027.  
  13028. private _objectiveType = _objective get "type";
  13029. private _successProb = _prediction get "successProbability";
  13030. private _expectedCasualties = _prediction get "expectedCasualties";
  13031. private _hasReinforcements = _prediction get "hasReinforcements";
  13032.  
  13033. // Base value by objective type
  13034. private _baseValue = 0;
  13035. switch (_objectiveType) do {
  13036. case "ENEMY_BASE": { _baseValue = 100; };
  13037. case "BASE_DEFENSE": { _baseValue = 95; }; // Critical
  13038. case "HIGH_GROUND": { _baseValue = 70; }; // Very valuable
  13039. case "DEFENSIVE_LINE": { _baseValue = 60; };
  13040. case "ENEMY_REAR": { _baseValue = 55; }; // Disruptive
  13041. case "ENEMY_CONCENTRATION": { _baseValue = 50; };
  13042. default { _baseValue = 30; };
  13043. };
  13044.  
  13045. // Calculate expected value (probability of success * base value)
  13046. private _expectedValue = _baseValue * _successProb;
  13047.  
  13048. // Calculate risk cost (casualties are bad)
  13049. private _riskCost = 50 * _expectedCasualties;
  13050.  
  13051. // Net value = expected benefit - risk cost
  13052. private _netValue = _expectedValue - _riskCost;
  13053.  
  13054. // Urgency modifier
  13055. private _urgency = 1.0;
  13056. switch (_objectiveType) do {
  13057. case "BASE_DEFENSE": {
  13058. // Critical urgency for base defense
  13059. private _enemiesNearBase = [_side, 800] call fnc_enemiesNearBase;
  13060. if (_enemiesNearBase) then {
  13061. _urgency = 5.0; // Extreme priority
  13062. } else {
  13063. _urgency = 3.0; // Still important
  13064. };
  13065. };
  13066. case "HIGH_GROUND": {
  13067. // Good to have, not critical
  13068. _urgency = 1.2;
  13069. };
  13070. case "DEFENSIVE_LINE": {
  13071. // More urgent if enemies are pushing
  13072. private _intel = if (_side == west) then {BLUFOR_INTEL} else {OPFOR_INTEL};
  13073. if (count _intel > 10) then {
  13074. _urgency = 1.5;
  13075. } else {
  13076. _urgency = 1.0;
  13077. };
  13078. };
  13079. case "ENEMY_REAR": {
  13080. // Opportunistic
  13081. _urgency = 1.3;
  13082. };
  13083. };
  13084.  
  13085. // Reinforcement availability bonus
  13086. if (_hasReinforcements) then {
  13087. _netValue = _netValue * 1.15;
  13088. };
  13089.  
  13090. private _finalScore = _netValue * _urgency;
  13091.  
  13092. // Return evaluation
  13093. private _evaluation = createHashMap;
  13094. _evaluation set ["score", _finalScore];
  13095. _evaluation set ["baseValue", _baseValue];
  13096. _evaluation set ["expectedValue", _expectedValue];
  13097. _evaluation set ["riskCost", _riskCost];
  13098. _evaluation set ["urgency", _urgency];
  13099.  
  13100. _evaluation
  13101. };
  13102.  
  13103. // Select best groups for an objective type
  13104. fnc_selectGroupsForObjective = {
  13105. params ["_objective", "_availableGroups", "_count"];
  13106.  
  13107. private _objectiveType = _objective get "type";
  13108. private _objectivePos = _objective get "position";
  13109. private _selectedGroups = [];
  13110.  
  13111. // For specialized objectives, filter for appropriate group types
  13112. switch (_objectiveType) do {
  13113. case "CONTAINMENT": {
  13114. // Prefer infantry and mechanized for holding positions
  13115. // Avoid using all elite/specops for containment - save them for other tasks
  13116. private _holdingGroups = _availableGroups select {
  13117. private _type = [_x] call fnc_classifyGroup;
  13118. _type in ["INFANTRY", "MECHANIZED", "UPGRADED", "SPECOPS"]
  13119. };
  13120.  
  13121. if (count _holdingGroups > 0) then {
  13122. // Sort by distance to position
  13123. _holdingGroups = [_holdingGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13124. _selectedGroups = _holdingGroups select [0, _count min count _holdingGroups];
  13125. };
  13126.  
  13127. // Fill remaining slots if needed
  13128. if (count _selectedGroups < _count) then {
  13129. private _remainingGroups = _availableGroups - _selectedGroups;
  13130. private _needed = _count - count _selectedGroups;
  13131. _remainingGroups = [_remainingGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13132. _selectedGroups append (_remainingGroups select [0, _needed min count _remainingGroups]);
  13133. };
  13134. };
  13135.  
  13136. case "HIGH_GROUND": {
  13137. // Prefer snipers and elite for high ground
  13138. private _specializedGroups = _availableGroups select {
  13139. private _type = [_x] call fnc_classifyGroup;
  13140. _type in ["SNIPER", "ELITE", "SPECOPS"]
  13141. };
  13142.  
  13143. if (count _specializedGroups > 0) then {
  13144. // Sort by distance to position
  13145. _specializedGroups = [_specializedGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13146. _selectedGroups = _specializedGroups select [0, _count min count _specializedGroups];
  13147. };
  13148.  
  13149. // Fill remaining slots with regular infantry if needed
  13150. if (count _selectedGroups < _count) then {
  13151. private _remainingGroups = _availableGroups - _selectedGroups;
  13152. private _needed = _count - count _selectedGroups;
  13153. _remainingGroups = [_remainingGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13154. _selectedGroups append (_remainingGroups select [0, _needed min count _remainingGroups]);
  13155. };
  13156. };
  13157.  
  13158. case "ENEMY_REAR": {
  13159. // Prefer elite/specops for behind enemy lines
  13160. private _stealthGroups = _availableGroups select {
  13161. private _type = [_x] call fnc_classifyGroup;
  13162. _type in ["ELITE", "SPECOPS", "SNIPER"]
  13163. };
  13164.  
  13165. if (count _stealthGroups > 0) then {
  13166. _stealthGroups = [_stealthGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13167. _selectedGroups = _stealthGroups select [0, _count min count _stealthGroups];
  13168. };
  13169.  
  13170. if (count _selectedGroups < _count) then {
  13171. private _remainingGroups = _availableGroups - _selectedGroups;
  13172. private _needed = _count - count _selectedGroups;
  13173. _remainingGroups = [_remainingGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13174. _selectedGroups append (_remainingGroups select [0, _needed min count _remainingGroups]);
  13175. };
  13176. };
  13177.  
  13178. case "BASE_DEFENSE": {
  13179. // Prefer groups already close to base
  13180. private _sortedGroups = [_availableGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13181. _selectedGroups = _sortedGroups select [0, _count min count _sortedGroups];
  13182. };
  13183.  
  13184. case "ENEMY_BASE": {
  13185. // Prefer armor and mechanized for base assault
  13186. private _heavyGroups = _availableGroups select {
  13187. private _type = [_x] call fnc_classifyGroup;
  13188. _type in ["ARMOR", "MECHANIZED", "AIR"]
  13189. };
  13190.  
  13191. if (count _heavyGroups > 0) then {
  13192. _selectedGroups = _heavyGroups select [0, (_count / 2) min count _heavyGroups];
  13193. };
  13194.  
  13195. if (count _selectedGroups < _count) then {
  13196. private _remainingGroups = _availableGroups - _selectedGroups;
  13197. private _needed = _count - count _selectedGroups;
  13198. _remainingGroups = [_remainingGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13199. _selectedGroups append (_remainingGroups select [0, _needed min count _remainingGroups]);
  13200. };
  13201. };
  13202.  
  13203. default {
  13204. // Default: pick closest groups
  13205. private _sortedGroups = [_availableGroups, [], {(leader _x) distance2D _objectivePos}, "ASCEND"] call BIS_fnc_sortBy;
  13206. _selectedGroups = _sortedGroups select [0, _count min count _sortedGroups];
  13207. };
  13208. };
  13209.  
  13210. _selectedGroups
  13211. };
  13212.  
  13213. // Create strategic plan by evaluating all possible objectives
  13214. fnc_createStrategicPlan = {
  13215. params ["_side", "_availableGroups"];
  13216.  
  13217. if (count _availableGroups == 0) exitWith { createHashMap };
  13218.  
  13219. private _objectives = [_side] call fnc_identifyPotentialObjectives;
  13220. private _scoredObjectives = [];
  13221.  
  13222. // Evaluate each objective
  13223. {
  13224. private _objective = _x;
  13225. private _objectivePos = _objective get "position";
  13226. private _objectiveType = _objective get "type";
  13227.  
  13228. // Handle fixed-requirement objectives (like base defense)
  13229. if (_objectiveType == "BASE_DEFENSE") then {
  13230. private _requiredGroups = _objective get "requiredGroups";
  13231. private _selectedGroups = [_objective, _availableGroups, _requiredGroups] call fnc_selectGroupsForObjective;
  13232.  
  13233. if (count _selectedGroups > 0) then {
  13234. private _prediction = [_side, _objective, _selectedGroups] call fnc_predictBattleOutcome;
  13235. private _evaluation = [_side, _objective, _prediction] call fnc_evaluateObjectiveValue;
  13236.  
  13237. private _scoredObjective = createHashMap;
  13238. _scoredObjective set ["objective", _objective];
  13239. _scoredObjective set ["score", _evaluation get "score"];
  13240. _scoredObjective set ["recommendedGroupCount", count _selectedGroups];
  13241. _scoredObjective set ["selectedGroups", _selectedGroups];
  13242. _scoredObjective set ["prediction", _prediction];
  13243. _scoredObjective set ["evaluation", _evaluation];
  13244.  
  13245. _scoredObjectives pushBack _scoredObjective;
  13246. };
  13247. } else {
  13248. // For other objectives, test different force allocations
  13249. private _minForce = 1;
  13250. private _maxForce = (count _availableGroups) min 8; // Don't commit everything
  13251.  
  13252. // For high ground, only need 1-2 groups
  13253. if (_objectiveType == "HIGH_GROUND") then {
  13254. _maxForce = 2;
  13255. };
  13256.  
  13257. private _bestScore = -999999;
  13258. private _bestAllocation = _minForce;
  13259. private _bestPrediction = createHashMap;
  13260. private _bestEvaluation = createHashMap;
  13261. private _bestGroups = [];
  13262.  
  13263. for "_groupCount" from _minForce to _maxForce step 1 do {
  13264. // Select appropriate groups for this objective
  13265. private _testGroups = [_objective, _availableGroups, _groupCount] call fnc_selectGroupsForObjective;
  13266.  
  13267. if (count _testGroups > 0) then {
  13268. // Check for potential reinforcements
  13269. private _reinforcements = [_side, _objectivePos, _testGroups, 1500] call fnc_findNearbyReinforcements;
  13270. private _reinforcementGroups = [];
  13271.  
  13272. // Take up to 2 nearest reinforcement groups
  13273. if (count _reinforcements > 0) then {
  13274. for "_i" from 0 to ((2 min count _reinforcements) - 1) do {
  13275. _reinforcementGroups pushBack ((_reinforcements select _i) get "group");
  13276. };
  13277. };
  13278.  
  13279. // Predict outcome with reinforcements
  13280. private _prediction = [_side, _objective, _testGroups, _reinforcementGroups] call fnc_predictBattleOutcome;
  13281.  
  13282. // Evaluate value
  13283. private _evaluation = [_side, _objective, _prediction] call fnc_evaluateObjectiveValue;
  13284.  
  13285. private _score = _evaluation get "score";
  13286.  
  13287. // Efficiency penalty for using too many groups
  13288. private _efficiencyPenalty = (_groupCount / count _availableGroups) * 8;
  13289. _score = _score - _efficiencyPenalty;
  13290.  
  13291. if (_score > _bestScore) then {
  13292. _bestScore = _score;
  13293. _bestAllocation = _groupCount;
  13294. _bestPrediction = _prediction;
  13295. _bestEvaluation = _evaluation;
  13296. _bestGroups = _testGroups;
  13297. };
  13298. };
  13299. };
  13300.  
  13301. // Store objective with its best score and allocation
  13302. if (count _bestGroups > 0) then {
  13303. private _scoredObjective = createHashMap;
  13304. _scoredObjective set ["objective", _objective];
  13305. _scoredObjective set ["score", _bestScore];
  13306. _scoredObjective set ["recommendedGroupCount", _bestAllocation];
  13307. _scoredObjective set ["selectedGroups", _bestGroups];
  13308. _scoredObjective set ["prediction", _bestPrediction];
  13309. _scoredObjective set ["evaluation", _bestEvaluation];
  13310.  
  13311. _scoredObjectives pushBack _scoredObjective;
  13312. };
  13313. };
  13314. } forEach _objectives;
  13315.  
  13316. // Sort objectives by score (highest first)
  13317. _scoredObjectives = [_scoredObjectives, [], {_x get "score"}, "DESCEND"] call BIS_fnc_sortBy;
  13318.  
  13319. // Create final strategic plan
  13320. private _strategicPlan = createHashMap;
  13321. _strategicPlan set ["objectives", _scoredObjectives];
  13322. _strategicPlan set ["timestamp", time];
  13323.  
  13324. _strategicPlan
  13325. };
  13326.  
  13327. // Get strategic recommendation for group assignment
  13328. fnc_getStrategicRecommendation = {
  13329. params ["_side", "_availableGroups"];
  13330.  
  13331. if (count _availableGroups == 0) exitWith { createHashMap };
  13332.  
  13333. // Create or update strategic plan
  13334. private _strategicPlan = [_side, _availableGroups] call fnc_createStrategicPlan;
  13335.  
  13336. // Store plan for this side
  13337. if (_side == west) then {
  13338. BLUFOR_STRATEGIC_PLAN = _strategicPlan;
  13339. } else {
  13340. OPFOR_STRATEGIC_PLAN = _strategicPlan;
  13341. };
  13342.  
  13343. private _objectives = _strategicPlan get "objectives";
  13344.  
  13345. if (count _objectives == 0) exitWith { createHashMap };
  13346.  
  13347. // Get top priority objective
  13348. private _topObjective = _objectives select 0;
  13349. private _score = _topObjective get "score";
  13350.  
  13351. // Only recommend if score is positive
  13352. if (_score <= 0) exitWith { createHashMap };
  13353.  
  13354. private _objective = _topObjective get "objective";
  13355. private _objectiveType = _objective get "type";
  13356.  
  13357. // Special handling for high ground - track held positions
  13358. if (_objectiveType == "HIGH_GROUND") then {
  13359. private _heldPositions = if (_side == west) then {BLUFOR_HELD_POSITIONS} else {OPFOR_HELD_POSITIONS};
  13360. _heldPositions pushBackUnique (_objective get "position");
  13361.  
  13362. if (_side == west) then {
  13363. BLUFOR_HELD_POSITIONS = _heldPositions;
  13364. } else {
  13365. OPFOR_HELD_POSITIONS = _heldPositions;
  13366. };
  13367. };
  13368.  
  13369. private _recommendation = createHashMap;
  13370. _recommendation set ["objective", _objective];
  13371. _recommendation set ["groupCount", _topObjective get "recommendedGroupCount"];
  13372. _recommendation set ["selectedGroups", _topObjective get "selectedGroups"];
  13373. _recommendation set ["prediction", _topObjective get "prediction"];
  13374. _recommendation set ["evaluation", _topObjective get "evaluation"];
  13375. _recommendation set ["score", _score];
  13376.  
  13377. _recommendation
  13378. };
  13379.  
  13380. // Apply special tactics for objective types
  13381. fnc_applySpecialTactics = {
  13382. params ["_group", "_objective"];
  13383.  
  13384. private _objectiveType = _objective get "type";
  13385. private _objectivePos = _objective get "position";
  13386.  
  13387. switch (_objectiveType) do {
  13388. case "CONTAINMENT": {
  13389. // Lock group to containment position - critical for victory
  13390. _group setVariable ["HC_FORCED_LOCK", true, true];
  13391. _group setVariable ["HC_ROLE", "CONTAINMENT", true];
  13392. _group setVariable ["HC_HOLD_POSITION", true, true];
  13393.  
  13394. // Create defensive waypoints to hold the containment position
  13395. [_group, _objectivePos] call fnc_createDefenseWaypoints;
  13396.  
  13397. // Track this position as held by this side
  13398. private _side = side _group;
  13399. private _heldPositions = if (_side == west) then {BLUFOR_HELD_POSITIONS} else {OPFOR_HELD_POSITIONS};
  13400. _heldPositions pushBackUnique _objectivePos;
  13401.  
  13402. if (_side == west) then {
  13403. BLUFOR_HELD_POSITIONS = _heldPositions;
  13404. } else {
  13405. OPFOR_HELD_POSITIONS = _heldPositions;
  13406. };
  13407. };
  13408.  
  13409. case "HIGH_GROUND": {
  13410. // Set group to hold position on high ground
  13411. _group setVariable ["HC_HOLD_POSITION", true, true];
  13412. _group setVariable ["HC_ROLE", "HIGH_GROUND", true];
  13413.  
  13414. // Create defensive waypoints around the high ground
  13415. [_group, _objectivePos] call fnc_createDefenseWaypoints;
  13416. };
  13417.  
  13418. case "ENEMY_REAR": {
  13419. // Set up defensive perimeter for recon
  13420. _group setVariable ["HC_ROLE", "DEEP_RECON", true];
  13421. [_group, _objectivePos, 150] call fnc_createReconPerimeter;
  13422. };
  13423.  
  13424. case "BASE_DEFENSE": {
  13425. // Lock group to base defense
  13426. _group setVariable ["HC_FORCED_LOCK", true, true];
  13427. _group setVariable ["HC_ROLE", "BASE_DEFENSE", true];
  13428. [_group, _objectivePos] call fnc_createDefenseWaypoints;
  13429. };
  13430.  
  13431. case "DEFENSIVE_LINE": {
  13432. // Create defensive positions
  13433. _group setVariable ["HC_ROLE", "DEFENSIVE_LINE", true];
  13434. [_group, _objectivePos] call fnc_createDefenseWaypoints;
  13435. };
  13436. };
  13437. };
  13438.  
  13439. ==================== END OF: strategy.sqf ====================
  13440.  
  13441.  
Add Comment
Please, Sign In to add comment