diff --git a/README.md b/README.md index afe89312..c53ae389 100644 --- a/README.md +++ b/README.md @@ -179,3 +179,13 @@ Used to manually initialize module. Has to be executed on clients and server. Ef Parameter | Explanation ----------|----------------------------------------------------------- headgear | Array - All classnames of clothes that civilians may wear. + +## grad_civs_fnc_populateArea +Manually populates an area with civilians. These civilians count towards the maximum amount, but will get cleaned up when no players are near enough. + +### Syntax +`[area] call grad_civs_fnc_populateArea` + +Parameter | Explanation +----------|------------------------------------------------------------------------------------------------------------------------------------------------- +area | Array / Object - Area array in format `[a, b, angle, isRectangle]` or array of area arrays or gamelogic synchronzed to one or multiple triggers. diff --git a/cfgFunctions.hpp b/cfgFunctions.hpp index ebc6b21d..88dce7ff 100644 --- a/cfgFunctions.hpp +++ b/cfgFunctions.hpp @@ -15,8 +15,11 @@ class grad_civs { class common { file = MODULES_DIRECTORY\grad-civs\functions\common; + class findBuildings {}; class findPositionOfInterest {}; class findRandomPos {}; + class findRandomPosArea {}; + class populateArea {}; class setBackpacks {}; class setClothes {}; class setDebugMode {}; @@ -54,6 +57,8 @@ class grad_civs { class spawnCivilian {}; class cleanup {}; + class createSideRoadVehicles {}; + class deleteIfDamaged {}; class dressAndBehave {}; class findSpawnPosition {}; class addToVehicle {}; diff --git a/functions/common/fn_findBuildings.sqf b/functions/common/fn_findBuildings.sqf new file mode 100644 index 00000000..a62bc6e6 --- /dev/null +++ b/functions/common/fn_findBuildings.sqf @@ -0,0 +1,34 @@ +#include "..\..\component.hpp" + +params ["_searchPos","_radius"]; + +//exclusion list for houses +_exclusionList = [ + "Land_Pier_F", + "Land_Pier_small_F", + "Land_NavigLight", + "Land_LampHarbour_F", + "Land_runway_edgelight" +]; + +if (count _searchPos == 0) exitWith {[]}; + +//HOUSE LIST =================================================================== +_houseList = nearestObjects [_searchPos,["House"],_radius]; + +//Clean up house list (remove buildings that have no positions) +_cleanUpCounter = 0; +{ + _buildingPos = _x buildingPos 0; + if ((str _buildingPos) == "[0,0,0]") then { + _houseList = _houseList - [_x]; + _cleanUpCounter = _cleanUpCounter + 1; + } else { + if (typeOf _x in _exclusionList) then { + _houseList = _houseList - [_x]; + _cleanUpCounter = _cleanUpCounter + 1; + }; + }; +}forEach _houseList; + +_houseList diff --git a/functions/common/fn_findRandomPosArea.sqf b/functions/common/fn_findRandomPosArea.sqf new file mode 100644 index 00000000..bcfc3c22 --- /dev/null +++ b/functions/common/fn_findRandomPosArea.sqf @@ -0,0 +1,18 @@ +#include "..\..\component.hpp" + +params ["_area",["_vehicleType","B_Soldier_F"],["_findWaterPos",false],["_findRoadPos",false]]; + +_pos = []; +for [{private _i=0}, {_i<25}, {_i=_i+1}] do { + _searchPos = _area call BIS_fnc_randomPosTrigger; + + if (_findRoadPos) then { + _nearRoads = _searchPos nearRoads 50; + _searchPos = if (count _nearRoads > 0) then {getPos (_nearRoads select 0)} else {[]}; + }; + + _pos = if (_vehicleType != "" && {count _searchPos > 0}) then {_searchPos findEmptyPosition [0,10,_vehicleType]} else {_searchPos}; + if (count _pos > 0 && {(surfaceIsWater _pos) isEqualTo _findWaterPos}) exitWith {}; +}; + +_pos diff --git a/functions/common/fn_populateArea.sqf b/functions/common/fn_populateArea.sqf new file mode 100644 index 00000000..515b9131 --- /dev/null +++ b/functions/common/fn_populateArea.sqf @@ -0,0 +1,39 @@ +#include "..\..\component.hpp" + +if (!isServer) exitWith {}; +if (!canSuspend) exitWith {_this spawn grad_civs_fnc_populateArea}; + +params ["_area",["_amount",20]]; + +private _areas = if (_area isEqualType objNull) then {synchronizedObjects _area} else {[_area]}; +private _maxLoops = _amount * 5; +{ + _amountSpawned = 0; + for [{_i=0},{_i<1000},{_i=_i+1}] do { + + _spawnPos = [_x] call grad_civs_fnc_findRandomPosArea; + if (count _spawnPos > 0) then { + _civ = [_spawnPos] call grad_civs_fnc_spawnCivilian; + _civ setVariable ["grad_civs_excludeFromCleanup",true]; + GRAD_CIVS_ONFOOTUNITS pushBack _civ; + GRAD_CIVS_ONFOOTCOUNT = GRAD_CIVS_ONFOOTCOUNT + 1; + [_civ,_spawnPos,300 - (random 250),[3,6],[0,2,10],true] spawn grad_civs_fnc_taskPatrol; + _amountSpawned = _amountSpawned + 1; + }; + + if (_amountSpawned >= _amount) exitWith {}; + if (_i > _maxLoops) exitWith {}; + }; + + _vehAmount = if (_x isEqualType objNull) then { + (triggerArea _x) params [["_a",0],["_b",0]]; + [getPos _x,_a max _b,1,30,15] call grad_civs_fnc_createSideRoadVehicles + } else { + _x params ["_center",["_a",0],["_b",0]]; + [_center,_a max _b,1,1,15] call grad_civs_fnc_createSideRoadVehicles + }; + + INFO_2("Populated area with %1 civilians and %2 static cars.",_amountSpawned,_vehAmount); + + false +} count _areas; diff --git a/functions/spawn/fn_cleanup.sqf b/functions/spawn/fn_cleanup.sqf index 8d047827..c562b7c3 100644 --- a/functions/spawn/fn_cleanup.sqf +++ b/functions/spawn/fn_cleanup.sqf @@ -24,6 +24,10 @@ if (count _civsArray == 0) exitWith {}; private _id = missionNamespace getVariable [_idVar,0]; _id = if (_id >= (count _civsArray)-1) then {0} else {_id+1}; +for [{_i=0},{_i= (count _civsArray)-1) then {0} else {_id+1}; +}; missionNamespace setVariable [_idVar,_id]; private _civ = _civsArray select _id; diff --git a/functions/spawn/fn_createSideRoadVehicles.sqf b/functions/spawn/fn_createSideRoadVehicles.sqf new file mode 100644 index 00000000..e0560b77 --- /dev/null +++ b/functions/spawn/fn_createSideRoadVehicles.sqf @@ -0,0 +1,83 @@ +#include "..\..\component.hpp" + +params ["_locationPosition","_locationRadius","_amountFactor","_houseFactor","_minDistance"]; + +if (count GRAD_CIVS_VEHICLES == 0) exitWith {}; +private _vehiclePositions = []; + +//LOCAL FUNCTIONS ============================================================== +private _fnc_nearbyVehiclePositions = { + params ["_pos"]; + + _nearbyVehiclePositions = []; + {if (_pos distance2D _x < _minDistance) then {_nearbyVehiclePositions pushBack _x}} forEach _vehiclePositions; + _nearbyVehiclePositions +}; + +private _fnc_isSafe = { + params ["_pos"]; + !(([_pos,0,1,0,0,0.6,0,[],[[0,0,0],[0,0,0]]] call BIS_fnc_findSafePos) isEqualTo [0,0,0]) +}; + + +//MAIN ========================================================================= +private _thesePositions = []; +private _roads = _locationPosition nearRoads _locationRadius; +private _vehiclesToCreate = (round ((count _roads) * 0.07 * _amountFactor)); +_vehiclesToCreate = round (2 max (_vehiclesToCreate + ((random (_vehiclesToCreate * 0.4)) - _vehiclesToCreate * 0.2))); + +while {count _roads > 0 && count _thesePositions < _vehiclesToCreate} do { + private ["_vehPos","_canCreate","_chosenDirection","_offRoadFound"]; + + _randomRoadID = round (random ((count _roads)-1)); + _road = _roads deleteAt _randomRoadID; + + if (!isNull _road) then { + if (count (roadsConnectedTo _road) == 0) exitWith {}; + _roadDir = _road getDir ((roadsConnectedTo _road) select 0); + _boundingBox = boundingBox _road; + _width = ((_boundingBox select 1) select 0) - ((_boundingBox select 0) select 0); + + _startDirection = selectRandom [1,-1]; + { + _chosenDirection = _x; + _offRoadFound = false; + for [{_i=1}, {_i<50}, {_i=_i+1}] do { + _testPos = _road getRelPos [1.5 + _i*0.2,_roadDir+90*_chosenDirection]; + _vehPos = _testPos; + if (!isOnRoad _testPos) exitWith {_offRoadFound = true}; + }; + + _enoughHouses = if (_houseFactor < 0) then {true} else {(count ([_vehPos,20] call grad_civs_fnc_findBuildings)) * _houseFactor > random 100}; + + _canCreate = switch (true) do { + case (!_offRoadFound): {"ONROAD"}; + case (count (getPos _road nearRoads 10) > 2): {"ONINTERSECTION"}; + case (!_enoughHouses): {"NOHOUSES"}; + case (count ([_vehPos] call _fnc_nearbyVehiclePositions) > 0): {"TOOCLOSE"}; + case (!([_vehPos] call _fnc_isSafe)): {"UNSAFE"}; + default {"CANCREATE"}; + }; + + if (_forEachIndex == 1) then {_roadDir = _roadDir + 180}; + if (_canCreate == "CANCREATE") exitWith {}; + } forEach [_startDirection,-_startDirection]; + + if (_canCreate == "CANCREATE") then { + _type = selectRandom GRAD_CIVS_VEHICLES; + _thesePositions pushBack _vehPos; + _vehiclePositions pushBack _vehPos; + _veh = createVehicle [_type,[0,0,0],[],0,"CAN_COLLIDE"]; + [{!isNull (_this select 0)}, { + params ["_veh","_roadDir","_chosenDirection","_vehPos"]; + _veh setDir _roadDir + (90 + 90*_chosenDirection); + _veh setPos _vehPos; + _veh setVelocity [0,0,1]; + _veh lock 2; + [_veh] call grad_civs_fnc_deleteIfDamaged; + }, [_veh,_roadDir,_chosenDirection,_vehPos]] call CBA_fnc_waitUntilAndExecute; + }; + }; +}; + +count _vehiclePositions diff --git a/functions/spawn/fn_deleteIfDamaged.sqf b/functions/spawn/fn_deleteIfDamaged.sqf new file mode 100644 index 00000000..4cdc74c2 --- /dev/null +++ b/functions/spawn/fn_deleteIfDamaged.sqf @@ -0,0 +1,10 @@ +#include "..\..\component.hpp" + +[{!isNull (_this select 0)}, { + [{ + params ["_object"]; + if (damage _object > 0) then { + deleteVehicle _object; + }; + }, _this, 5] call CBA_fnc_waitAndExecute; +}, _this] call CBA_fnc_waitUntilAndExecute; diff --git a/package.json b/package.json index cabeac3d..37d8afb3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "grad-civs", "description": "ambient civilians", - "version": "0.2.5", + "version": "0.3.0", "contributors": [ { "name": "nomisum"