diff --git a/CHANGELOG.md b/CHANGELOG.md index 3661d5ff1..63373286a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Added: Czech translation. Thanks to [MJVEVERUSKA](https://github.com/MJVEVERUSKA) * Added: Ability to carry ressource crates. * Added: Scripts/configs to setup and run development environment from VSCode tasks +* Added: Enemy boats can spawn as part of battlegroups * Updated: Italian localization. Thanks to [k4s0](https://github.com/k4s0) * Tweaked: Splitted the config file in separate files, as it was getting quite big. * Tweaked: Unified the prefix of all variables to `KPLIB_`. diff --git a/Missionframework/CfgFunctions.hpp b/Missionframework/CfgFunctions.hpp index 2749daa07..ba7eb9de4 100644 --- a/Missionframework/CfgFunctions.hpp +++ b/Missionframework/CfgFunctions.hpp @@ -27,6 +27,7 @@ class KPLIB { class fillStorage {}; class forceBluforCrew {}; class getAdaptiveVehicle {}; + class getBoatWaypoints {}; class getBluforRatio {}; class getCommander {}; class getCrateHeight {}; diff --git a/Missionframework/functions/fn_getBoatWaypoints.sqf b/Missionframework/functions/fn_getBoatWaypoints.sqf new file mode 100644 index 000000000..1a37c33fc --- /dev/null +++ b/Missionframework/functions/fn_getBoatWaypoints.sqf @@ -0,0 +1,52 @@ +/* + File: fn_getBoatWaypoints.sqf + Author: FatRefrigerator + Date: 2024-11-6 + Last Update: 2024-11-6 + License: MIT License - http://www.opensource.org/licenses/MIT + + Description: + Gets waypoints for boats. Created this since I realized it could be used on multiple boat-related scripts, so figured it should be a function. + + Parameter(s): + _objective - The place to search for safe boat waypoints [POS, defaults to 0,0,0] + + Returns: + a safe boat waypoint [ARRAY] +*/ + +params [ + ["_objective", [0, 0, 0], [[]], [2, 3]] +]; + +if(_objective isEqualTo [0,0,0]) exitWith {[]}; + +private _posFound = false; +private _randomPos = []; +private _boatWaypoint = []; +private _waterDepth = 0; + +_searchCounter = 0; + while {!_posFound} do + { + _randomPos - []; + //counter because there are cases where there will be water near an objective but none will be deep enough + //most boats can drive in 1m of water, but i have it set to 3m since that will keep waypoints from being put super close to shore + _searchCounter = _searchCounter + 1; + + if(_searchCounter isEqualTo 20) then {break}; + + _randomPos = [_objective,1,300,0,2] call BIS_fnc_findSafePos; + + if(_randomPos distance _objective > 500) then {break}; + + _randomPos pushBack 0; + _waterDepth = ASLToATL _randomPos select 2; + + if (!(_waterDepth < 3)) then + { + _boatWaypoint = _randomPos; + _posFound = true; + }; + }; +_boatWaypoint; \ No newline at end of file diff --git a/Missionframework/functions/fn_initSectors.sqf b/Missionframework/functions/fn_initSectors.sqf index 68ef89237..0e25ece13 100644 --- a/Missionframework/functions/fn_initSectors.sqf +++ b/Missionframework/functions/fn_initSectors.sqf @@ -17,6 +17,7 @@ KPLIB_sectors_airSpawn = []; KPLIB_sectors_all = []; +KPLIB_sectors_boatSpawn = []; KPLIB_sectors_capital = []; KPLIB_sectors_city = []; KPLIB_sectors_factory = []; @@ -31,6 +32,7 @@ KPLIB_sectors_tower = []; case (_x find "factory" == 0): {KPLIB_sectors_factory pushBack _x; KPLIB_sectors_all pushBack _x;}; case (_x find "military" == 0): {KPLIB_sectors_military pushBack _x; KPLIB_sectors_all pushBack _x;}; case (_x find "opfor_airspawn" == 0): {KPLIB_sectors_airSpawn pushBack _x;}; + case (_x find "opfor_boatspawn" == 0): {KPLIB_sectors_boatSpawn pushBack _x;}; case (_x find "opfor_point" == 0): {KPLIB_sectors_spawn pushBack _x;}; case (_x find "tower" == 0): {KPLIB_sectors_tower pushBack _x; if (isServer) then {_x setMarkerText format ["%1 %2",markerText _x, mapGridPosition (markerPos _x)];}; KPLIB_sectors_all pushBack _x;}; }; diff --git a/Missionframework/presets/enemies/aaf.sqf b/Missionframework/presets/enemies/aaf.sqf index db1e6b788..ee29aaec4 100644 --- a/Missionframework/presets/enemies/aaf.sqf +++ b/Missionframework/presets/enemies/aaf.sqf @@ -173,3 +173,7 @@ KPLIB_o_planes = [ "I_Plane_Fighter_03_dynamicLoadout_F", // L-159 "I_Plane_Fighter_04_F" // Gripen ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/apex.sqf b/Missionframework/presets/enemies/apex.sqf index 7c08ced99..fffeea073 100644 --- a/Missionframework/presets/enemies/apex.sqf +++ b/Missionframework/presets/enemies/apex.sqf @@ -153,3 +153,7 @@ KPLIB_o_planes = [ "O_Plane_CAS_02_dynamicLoadout_F", // To-199 Neophron (CAS) "O_Plane_Fighter_02_F" // To-201 Shikra ]; + +KPLIB_o_boats = [ + "O_Boat_Armed_01_hmg_F" // Speedboat HMG +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_afrf_msv.sqf b/Missionframework/presets/enemies/cup_afrf_msv.sqf index 66bdb2ec6..80ab560d3 100644 --- a/Missionframework/presets/enemies/cup_afrf_msv.sqf +++ b/Missionframework/presets/enemies/cup_afrf_msv.sqf @@ -187,3 +187,7 @@ KPLIB_o_planes = [ "CUP_O_Su25_Dyn_RU", // Su-25T Frogfoot "CUP_O_SU34_RU" // Su-34 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_afrf_msv_modern.sqf b/Missionframework/presets/enemies/cup_afrf_msv_modern.sqf index 5be057239..68482fea1 100644 --- a/Missionframework/presets/enemies/cup_afrf_msv_modern.sqf +++ b/Missionframework/presets/enemies/cup_afrf_msv_modern.sqf @@ -187,3 +187,7 @@ KPLIB_o_planes = [ "CUP_O_Su25_Dyn_RU", // Su-25T Frogfoot "CUP_O_SU34_RU" // Su-34 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_baf_desert.sqf b/Missionframework/presets/enemies/cup_baf_desert.sqf index 4d3608985..508c0f756 100644 --- a/Missionframework/presets/enemies/cup_baf_desert.sqf +++ b/Missionframework/presets/enemies/cup_baf_desert.sqf @@ -176,3 +176,7 @@ KPLIB_o_planes = [ "CUP_B_F35B_Stealth_BAF", // F-35B Lightning II (Stealth) "CUP_B_GR9_DYN_GB" // Harrier GR.9 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_baf_woodland.sqf b/Missionframework/presets/enemies/cup_baf_woodland.sqf index 4100c1cb8..c11349a05 100644 --- a/Missionframework/presets/enemies/cup_baf_woodland.sqf +++ b/Missionframework/presets/enemies/cup_baf_woodland.sqf @@ -176,3 +176,7 @@ KPLIB_o_planes = [ "CUP_B_F35B_Stealth_BAF", // F-35B Lightning II (Stealth) "CUP_B_GR9_DYN_GB" // Harrier GR.9 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_cdf.sqf b/Missionframework/presets/enemies/cup_cdf.sqf index d475075bb..fa6b1a5bb 100644 --- a/Missionframework/presets/enemies/cup_cdf.sqf +++ b/Missionframework/presets/enemies/cup_cdf.sqf @@ -162,3 +162,7 @@ KPLIB_o_planes = [ "CUP_B_SU34_CDF", // Su-34 "CUP_B_Su25_Dyn_CDF" // Su-25 Frogfoot ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_chdkz.sqf b/Missionframework/presets/enemies/cup_chdkz.sqf index d3d6e5166..b06afd071 100644 --- a/Missionframework/presets/enemies/cup_chdkz.sqf +++ b/Missionframework/presets/enemies/cup_chdkz.sqf @@ -158,3 +158,7 @@ KPLIB_o_helicopters = [ KPLIB_o_planes = [ "CUP_O_Su25_Dyn_RU" // Su-25T Frogfoot ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_sla.sqf b/Missionframework/presets/enemies/cup_sla.sqf index 8a032cebc..60cc83c2f 100644 --- a/Missionframework/presets/enemies/cup_sla.sqf +++ b/Missionframework/presets/enemies/cup_sla.sqf @@ -176,3 +176,7 @@ KPLIB_o_planes = [ "CUP_O_Su25_Dyn_SLA", // Su-25 Frogfoot "CUP_O_SU34_SLA" // Su-34 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/cup_takistan.sqf b/Missionframework/presets/enemies/cup_takistan.sqf index caef34873..a30c6c819 100644 --- a/Missionframework/presets/enemies/cup_takistan.sqf +++ b/Missionframework/presets/enemies/cup_takistan.sqf @@ -196,3 +196,7 @@ KPLIB_o_planes = [ "CUP_O_L39_TK", // L-39ZA "CUP_O_Su25_Dyn_TKA" // Su-25 Frogfoot ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/custom.sqf b/Missionframework/presets/enemies/custom.sqf index c6cee9085..32f483d12 100644 --- a/Missionframework/presets/enemies/custom.sqf +++ b/Missionframework/presets/enemies/custom.sqf @@ -149,3 +149,7 @@ KPLIB_o_planes = [ "O_Plane_CAS_02_dynamicLoadout_F", // To-199 Neophron (CAS) "O_Plane_Fighter_02_F" // To-201 Shikra ]; + +KPLIB_o_boats = [ + "O_Boat_Armed_01_hmg_F" // Speedboat HMG +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/gm_east.sqf b/Missionframework/presets/enemies/gm_east.sqf index 61977110f..2476c0d8c 100644 --- a/Missionframework/presets/enemies/gm_east.sqf +++ b/Missionframework/presets/enemies/gm_east.sqf @@ -148,3 +148,7 @@ KPLIB_o_helicopters = [ KPLIB_o_planes = [ "len_l39_nva" // Aero L-39 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/gm_east_win.sqf b/Missionframework/presets/enemies/gm_east_win.sqf index 701073845..6ba41fded 100644 --- a/Missionframework/presets/enemies/gm_east_win.sqf +++ b/Missionframework/presets/enemies/gm_east_win.sqf @@ -144,3 +144,7 @@ KPLIB_o_helicopters = [ KPLIB_o_planes = [ "len_l39_nva" // Aero L-39 ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/gm_west.sqf b/Missionframework/presets/enemies/gm_west.sqf index 999bf80d4..1ad86ae36 100644 --- a/Missionframework/presets/enemies/gm_west.sqf +++ b/Missionframework/presets/enemies/gm_west.sqf @@ -138,3 +138,7 @@ KPLIB_o_helicopters = [ // Enemy fixed-wings that will need to spawn in the air. KPLIB_o_planes = []; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/gm_west_win.sqf b/Missionframework/presets/enemies/gm_west_win.sqf index 171ae3d9f..9bb544a69 100644 --- a/Missionframework/presets/enemies/gm_west_win.sqf +++ b/Missionframework/presets/enemies/gm_west_win.sqf @@ -138,3 +138,7 @@ KPLIB_o_helicopters = [ // Enemy fixed-wings that will need to spawn in the air. KPLIB_o_planes = []; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/islamic_state.sqf b/Missionframework/presets/enemies/islamic_state.sqf index 07c96a9b8..612707ed3 100644 --- a/Missionframework/presets/enemies/islamic_state.sqf +++ b/Missionframework/presets/enemies/islamic_state.sqf @@ -168,3 +168,7 @@ KPLIB_o_planes = [ "RHS_Su25SM_vvsc", // Su-25 "RHS_Su25SM_KH29_vvsc" // Su-25 (KH29) ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/nato.sqf b/Missionframework/presets/enemies/nato.sqf index 002d15da5..18366686d 100644 --- a/Missionframework/presets/enemies/nato.sqf +++ b/Missionframework/presets/enemies/nato.sqf @@ -156,3 +156,7 @@ KPLIB_o_planes = [ "B_Plane_CAS_01_dynamicLoadout_F", // A-10D Thunderbolt II (CAS) "B_Plane_Fighter_01_F" // F/A-181 Black Wasp II ]; + +KPLIB_o_boats = [ + "O_Boat_Armed_01_hmg_F" // Speedboat HMG +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/rhs_afrf.sqf b/Missionframework/presets/enemies/rhs_afrf.sqf index 54d7cc045..853ae1b3c 100644 --- a/Missionframework/presets/enemies/rhs_afrf.sqf +++ b/Missionframework/presets/enemies/rhs_afrf.sqf @@ -152,3 +152,7 @@ KPLIB_o_planes = [ "RHS_Su25SM_vvsc", // Su-25 "RHS_Su25SM_KH29_vvsc" // Su-25 (KH29) ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/sla.sqf b/Missionframework/presets/enemies/sla.sqf index 0f975f25a..66a38fa16 100644 --- a/Missionframework/presets/enemies/sla.sqf +++ b/Missionframework/presets/enemies/sla.sqf @@ -152,3 +152,7 @@ KPLIB_o_helicopters = [ // Enemy fixed-wings that will need to spawn in the air. KPLIB_o_planes = []; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/takistan.sqf b/Missionframework/presets/enemies/takistan.sqf index 73740df2b..ee8da9909 100644 --- a/Missionframework/presets/enemies/takistan.sqf +++ b/Missionframework/presets/enemies/takistan.sqf @@ -156,3 +156,7 @@ KPLIB_o_planes = [ "RHS_Su25SM_vvsc", // Su-25 "RHS_Su25SM_KH29_vvsc" // Su-25 (KH29) ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/enemies/unsung.sqf b/Missionframework/presets/enemies/unsung.sqf index 6ed02d267..ef0cc966f 100644 --- a/Missionframework/presets/enemies/unsung.sqf +++ b/Missionframework/presets/enemies/unsung.sqf @@ -151,3 +151,7 @@ KPLIB_o_planes = [ "uns_Mig21_CAP", // Mig-21 Fishbed F (CAP) "uns_Mig21_CAS" // Mig-21 Fishbed F (CAS) ]; + +KPLIB_o_boats = [ + +]; \ No newline at end of file diff --git a/Missionframework/presets/init_presets.sqf b/Missionframework/presets/init_presets.sqf index 2fac49860..d745095b0 100644 --- a/Missionframework/presets/init_presets.sqf +++ b/Missionframework/presets/init_presets.sqf @@ -136,6 +136,7 @@ KPLIB_o_battleGrpVehiclesLight = KPLIB_o_battleGrpVehiclesLight select {[_x] KPLIB_o_troopTransports = KPLIB_o_troopTransports select {[_x] call KPLIB_fnc_checkClass}; KPLIB_o_helicopters = KPLIB_o_helicopters select {[_x] call KPLIB_fnc_checkClass}; KPLIB_o_planes = KPLIB_o_planes select {[_x] call KPLIB_fnc_checkClass}; +KPLIB_o_boats = KPLIB_o_boats select {[_x] call KPLIB_fnc_checkClass}; // Resistance KPLIB_r_units = KPLIB_r_units select {[_x] call KPLIB_fnc_checkClass}; @@ -234,7 +235,8 @@ KPLIB_o_allVeh_classes = []; KPLIB_o_battleGrpVehiclesLight, KPLIB_o_troopTransports, KPLIB_o_helicopters, - KPLIB_o_planes + KPLIB_o_planes, + KPLIB_o_boats ]; KPLIB_o_allVeh_classes = KPLIB_o_allVeh_classes apply {toLower _x}; KPLIB_o_allVeh_classes = KPLIB_o_allVeh_classes arrayIntersect KPLIB_o_allVeh_classes; diff --git a/Missionframework/scripts/server/battlegroup/spawn_battlegroup.sqf b/Missionframework/scripts/server/battlegroup/spawn_battlegroup.sqf index fe5a573de..3a7c7c099 100644 --- a/Missionframework/scripts/server/battlegroup/spawn_battlegroup.sqf +++ b/Missionframework/scripts/server/battlegroup/spawn_battlegroup.sqf @@ -68,6 +68,10 @@ if !(_spawn_marker isEqualTo "") then { }; } forEach _selected_opfor_battlegroup; + if (KPLIB_param_aggressivity > 0.5) then { + [[markerPos _spawn_marker] call KPLIB_fnc_getNearestBluforObjective] spawn spawn_boat; + }; + if (KPLIB_param_aggressivity > 0.9) then { [[markerPos _spawn_marker] call KPLIB_fnc_getNearestBluforObjective] spawn spawn_air; }; diff --git a/Missionframework/scripts/server/battlegroup/spawn_boat.sqf b/Missionframework/scripts/server/battlegroup/spawn_boat.sqf new file mode 100644 index 000000000..6add539d4 --- /dev/null +++ b/Missionframework/scripts/server/battlegroup/spawn_boat.sqf @@ -0,0 +1,69 @@ +//jank and mangled spawn_air implementation +params ["_first_objective"]; + +if (KPLIB_o_boats isEqualTo []) exitWith {false}; +if (KPLIB_sectors_boatSpawn isEqualTo[]) exitWith {false}; + +private _boats_number = ((floor linearConversion [25, 100, KPLIB_enemyReadiness, 1, 2]) min 2) max 0; + +if (_boats_number < 1) exitWith {}; + +private _class = selectRandom KPLIB_o_boats; +private _spawnPoint = ([KPLIB_sectors_boatSpawn, [_first_objective], {(markerPos _x) distance _input0}, "ASCEND"] call BIS_fnc_sortBy) select 0; +private _spawnPos = []; +private _boat = objNull; +private _boats = []; +private _grp = createGroup [KPLIB_side_enemy, true]; + +for "_i" from 1 to _boats_number do { + _spawnPos = markerPos _spawnPoint; + _spawnPos = [(((_spawnPos select 0) + 5) - random 1), (((_spawnPos select 1) + 5) - random 1), 2]; + _boat = createVehicle [_class, _spawnPos, [], 0, "NONE"]; + createVehicleCrew _boat; + _boats pushBack _boat; + _boat addMPEventHandler ["MPKilled", {_this spawn kill_manager}]; + [_boat] call KPLIB_fnc_addObjectInit; + {_x addMPEventHandler ["MPKilled", {_this spawn kill_manager}];} forEach (crew _boat); + (crew _boat) joinSilent _grp; + sleep 1; +}; + +while {!((waypoints _grp) isEqualTo [])} do {deleteWaypoint ((waypoints _grp) select 0);}; +sleep 1; +{_x doFollow leader _grp} forEach (units _grp); +sleep 1; + +private _boatWaypoint = []; +private _waypoint = []; + +//create waypoints for boats +for "_i" from 1 to 6 do{ + + _boatWaypoint = [_first_objective] call KPLIB_fnc_getBoatWaypoints; + + if(_boatWaypoint isEqualTo []) then {break}; + _waypoint = _grp addWaypoint [_boatWaypoint, 1]; + _waypoint setWayPointType "SAD"; +}; +//if there's no suitable waypoints for the boat, get rid of it +if (_waypoint isEqualTo []) exitWith { + {deleteVehicle _x} forEach units _grp; + {deleteVehicle _x} forEach _boats; + deleteGroup _grp; +}; +_waypoint = _grp addWaypoint [_boatWaypoint,1]; +_waypoint setWaypointType "CYCLE"; + +_grp setCurrentWaypoint [_grp, 2]; + +// wait and check if the boat has moved yet, and delete it if not (boats won't move if their waypoint can't be reached) +// this is really only necessary on maps with sectors near lakes, which the script would interpret as good waypoints, but would be typically inaccessible +// sleep 15 since with scheduled things spawning of boats can be a bit delayed and with lower timers they could be deleted before they have a chance to move +// checks the first boat in the array since the lead boat will sit still if the waypoint is inaccesssible, but the follower boat will constantly drive around the leader +sleep 15; + +if(abs(speed (_boats select 0)) < 5) exitWith{ + {deleteVehicle _x} forEach units _grp; + {deleteVehicle _x} forEach _boats; + deleteGroup _grp; +}; \ No newline at end of file diff --git a/Missionframework/scripts/server/init_server.sqf b/Missionframework/scripts/server/init_server.sqf index 693e9b134..d42688b09 100644 --- a/Missionframework/scripts/server/init_server.sqf +++ b/Missionframework/scripts/server/init_server.sqf @@ -9,6 +9,7 @@ troup_transport = compile preprocessFileLineNumbers "scripts\server\ai\troup_tra // Battlegroup spawn_air = compile preprocessFileLineNumbers "scripts\server\battlegroup\spawn_air.sqf"; +spawn_boat = compileFinal preprocessFileLineNumbers "scripts\server\battlegroup\spawn_boat.sqf"; spawn_battlegroup = compile preprocessFileLineNumbers "scripts\server\battlegroup\spawn_battlegroup.sqf"; // Game