Skip to content

Commit

Permalink
fix spawn positions in relation to players
Browse files Browse the repository at this point in the history
	* lots of refactor - refactored house finding / spawn position / patrol waypoint logic
		* there were bugfixes to be had in relation to
		  player distancing & exclusion zones
	* make cars appear it least 100m away from other "Man" (dont look into civilians array)
	* add some tests (have to be executed manually on a terrain with flat SW
	  corner like Zargabad… there is some TODO to be had here)

	NOTE FOR USERS:
		I recommend defining exclusion zones for little
		islands, steep mountains and other non-trivial terrain
		to avoid bad server fps. For the same reason, using a headless client is recommended.
  • Loading branch information
Fusselwurm authored and Moritz Schmidt committed Mar 12, 2020
1 parent d3da1b6 commit 6779b00
Show file tree
Hide file tree
Showing 25 changed files with 547 additions and 146 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ maxCivsInVehicles | 10 | Maximum number of civs in vehicles.
maxCivsResidents | 20 | Maximum number of civs that are residents, mostly doing their thing at home.
spawnDistancesOnFoot | [1000,4500] | Minimum and maximum distance to players that civilians on foot can spawn in.
spawnDistancesResidents | [500, 1000] | Minimum and maximum distance to players that civilians living in houses spawn in.
spawnDistancesOnFoot | [1000,4500] | Minimum and maximum distance to players that civilians on foot can spawn in.
spawnDistancesInVehicles | [1500,6000] | Minimum and maximum distance to players that civilians in vehicles can spawn in.
debugCivState | 0 | Toggles civ behavior debugging mode (0/1).
debugFps | 0 | Toggles fps monitoring mode (0/1).
initialGroupSize | 3 | Initial group size for civilians, between 1..N . Value may be any valid parameter to the `random` command.
Expand Down
9 changes: 7 additions & 2 deletions cfgFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class grad_civs {
file = MODULES_DIRECTORY\grad-civs\functions\api;

class addExclusionZone {};
class clearExclusionZones {};
class getExclusionZones {};
class populateArea {};
class setBackpacks {};
class setClothes {};
Expand All @@ -36,11 +38,14 @@ class grad_civs {
class addCycleWaypoint {};
class taskPatrol {};
class taskPatrolAddWaypoint {};
class taskPatrolFindWaypoints {};
class taskPatrolFindWaypoint {};
};

class common {
file = MODULES_DIRECTORY\grad-civs\functions\common;

class arrayContains {};
class compare {};
class findBuildings {};
class findPositionOfInterest {};
Expand Down Expand Up @@ -181,8 +186,8 @@ class grad_civs {
class createInfoChannel {};
class createSideRoadVehicles {};
class deleteIfDamaged {};
class findResidentSpawnHouse {};
class findSpawnPositionCandidates {};
class findUnclaimedHouse {};
class findSpawnPosition {};
class findSpawnRoadSegment {};
class isInDistanceFromOtherPlayers {};
class serverLoop {};
Expand Down
2 changes: 1 addition & 1 deletion functions/api/fn_addExclusionZone.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ if (isNull _trigger) exitWith {

GRAD_CIVS_EXCLUSION_ZONES pushBack _trigger;

INFO_1("added exclusion zone %1", triggerArea _trigger);
INFO_2("added exclusion zone %1 at %2", triggerArea _trigger, getPos _trigger);
5 changes: 5 additions & 0 deletions functions/api/fn_clearExclusionZones.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "..\..\component.hpp"

GRAD_CIVS_EXCLUSION_ZONES = [];

INFO("all exclusion zones removed");
1 change: 1 addition & 0 deletions functions/api/fn_getExclusionZones.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GRAD_CIVS_EXCLUSION_ZONES
134 changes: 123 additions & 11 deletions functions/behaviour/fn_taskPatrol.spec.sqf
Original file line number Diff line number Diff line change
@@ -1,30 +1,142 @@
["a civilian",
{
call grad_civs_fnc_clearExclusionZones;
private _group = createGroup [civilian, true];
_group setCombatMode "GREEN";
private _pos = getPos player;
private _pos = [0, 0, 0];
private _civ = _group createUnit ["C_Man_1", _pos, [], 0, "NONE"];
_civ setPos (getPos player);
_civ setPos _pos;
[_civ]
},
[
["who is tasked to patrol",
["(homeless or not)",
[
{
params [["_civ", objNull]];
private _grp = (group _civ);
private _house = "Land_Lighthouse_small_F" createVehicle [0, 0, 0];
_house setPos (getPos _civ);
_grp setVariable ["grad_civs_home", _house];
[_civ]
},
{
_this
}
],
[
["who is tasked to patrol on foot",
{
params [["_civ", objNull]];
[_civ, getPos _civ, 250, 3] call grad_civs_fnc_taskPatrol;
[_civ]
},
[
["has at least number of desired MOVE waypoints",
{
params [["_civ", objNull]];
private _grp = (group _civ);
private _wps = waypoints _grp;
private _moveWps = { (waypointType _x) == "MOVE"} count _wps;
[_moveWps, 2] call grad_testing_fnc_assertGreaterThan;
}
],
["has a CYCLE waypoint as last waypoint",
{
params [["_civ", objNull]];
private _grp = (group _civ);
private _wps = waypoints _grp;
private _lastWp = _wps select ((count _wps) - 1);

[waypointType _lastWp, "CYCLE"] call grad_testing_fnc_assertEquals;
}
],
["has waypoints not too close",
{
params [["_civ", objNull]];
private _grp = (group _civ);
private _wps = waypoints _grp;

private _totalDistance = 0;
{
private _prevIndex = if (_forEachIndex == 0) then {
(count _wps - 1)
} else {
_forEachIndex - 1
};
private _distance = (waypointPosition _x) distance (waypointPosition (_wps select _prevIndex));
_totalDistance = _totalDistance + _distance;
} forEach _wps;

[_totalDistance, 100] call grad_testing_fnc_assertGreaterThan;
}
]
]
]
],
{
params [["_civ", objNull]];
[_civ, getPos _civ, 250, 3] call GRAD_CIVS_fnc_taskPatrol;
private _grp = (group _civ);

private _house = _grp getVariable ["grad_civs_home", objNull];
if (isNull _house) exitWith {};
_grp setVariable ["grad_civs_home", objNull];
deleteVehicle _house;
}
],
["surrounded by exclusion zones on three sides",
{
params [["_civ", objNull]];

private _trgW = createTrigger ["EmptyDetector", (getPos _civ) vectorAdd [-250, 150, 0]];
_trgW setTriggerArea [150, 250, 0, true];
[_trgW] call grad_civs_fnc_addExclusionZone;

private _trgS = createTrigger ["EmptyDetector", (getPos _civ) vectorAdd [0, -250, 0]];
_trgS setTriggerArea [200, 150, 0, true];
[_trgS] call grad_civs_fnc_addExclusionZone;

private _trgE = createTrigger ["EmptyDetector", (getPos _civ) vectorAdd [250, 150, 0]];
_trgE setTriggerArea [150, 250, 0, true];
[_trgE] call grad_civs_fnc_addExclusionZone;

[_civ]
},
[
["gets all waypoints",
["and tasked to patrol",
{
params [["_civ", objNull]];
private _grp = (group _civ);
private _wps = waypoints _grp;
[count _wps, 3] call grad_testing_fnc_assertGreaterThan;
}
]
]
[_civ, getPos _civ, 250, 3] call GRAD_CIVS_fnc_taskPatrol;
[_civ]
},
[
["will avoid exclusion zones as waypoints",
{
params [["_civ", objNull]];

private _waypointPositions = (waypoints group _civ) apply {
waypointPosition _x
};
{
private _exclusionZone = _x;
private _exclusionZoneIdx = _forEachIndex;
{
[
_x inArea _exclusionZone,
format ["waypoint %1 at %2 is not in exclusion zone at %3 (%4)", _forEachIndex, _x, getPos _exclusionZone, triggerArea _exclusionZone]
] call grad_testing_fnc_assertFalse;
} forEach _waypointPositions;
} forEach (call grad_civs_fnc_getExclusionZones);
}
],
["will avoid crossing exclusion zones",
{
["TODO"] call grad_testing_fnc_skipTest;
}
]
]
]
],
grad_civs_fnc_clearExclusionZones
]
],
{
Expand Down
39 changes: 18 additions & 21 deletions functions/behaviour/fn_taskPatrol.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
#include "..\..\component.hpp"

params [
"_groupOrUnit",
"_centerPositionOrObject",
"_radius",
"_count",
["_groupOrUnit", grpNull],
["_centerPositionOrObject", [0, 0, 0]],
["_radius", 0],
["_count", 3],
["_timeout",[0,0,0]],
["_findPosOfInterest",false],
["_findRoadPos",false],
Expand All @@ -31,29 +31,26 @@ private _position = _centerPosition;

assert(_count > 1);

LOG_3("taskPatrol start. waypoints to be added: %1, group: %2. previous waypoints: %3", _count, _group, count waypoints _group);

if !(local _group) exitWith {};
[_group] call CBA_fnc_clearWaypoints;

//create waypoints
for [{_i=0}, {_i<_count}, {_i=_i+1}] do {
private _searchPosition = [_centerPosition,[_radius / 2 ,_radius],[0,360],nil,_findWaterPos,_findRoadPos] call grad_civs_fnc_findRandomPos;

private _inExclusionZone = {
if (_searchPosition inArea _x) exitWith {true};
false
} forEach (GRAD_CIVS_EXCLUSION_ZONES + [[[0, 0, 0], 1, 1, 0, true]]);
if (!_inExclusionZone) then {
if (count _searchPosition > 0) then {
_position = if (_findPosOfInterest && {80 > random 100}) then {[_searchPosition, false] call grad_civs_fnc_findPositionOfInterest} else {_searchPosition};
[_group, _position, _timeout] call grad_civs_fnc_taskPatrolAddWaypoint;
};
};
};
private _moveWps = [_position, _radius, _count, _findPosOfInterest, _findRoadPos, _findWaterPos] call FUNC(taskPatrolFindWaypoints);
{
LOG_1("adding wp at %1", _x);
[_group, _x, _timeout] call FUNC(taskPatrolAddWaypoint);
} forEach _moveWps;


// add home waypoint!
private _home = _group getVariable ["grad_civs_home", objNull];
if (!isNull _home) then {
[_group, getPos _home, [0, 15, 30], 20] call grad_civs_fnc_taskPatrolAddWaypoint;
LOG_1("adding home wp at %1", getPos _home);
[_group, getPos _home, [0, 15, 30], 20] call FUNC(taskPatrolAddWaypoint);
};

LOG("adding cycle wp close by group position");
// NOTE : a cycle waypoint points to the *closest waypoint other than the previous one*! which means in our case: close to the initial waypoint
[_group, _position vectorAdd [10, 0, 0]] call grad_civs_fnc_addCycleWaypoint;

LOG_2("taskPatrol end. waypoints for group %1 : %2", _group, count waypoints _group);
46 changes: 46 additions & 0 deletions functions/behaviour/fn_taskPatrolFindWaypoint.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 1: position position or object - if object, position of object is used
// 2: radius number or array - if array, random radius between first and second element is used
// 3: count number > 1, or array - amount of waypoints to generate, if array, random amount between first and second element is used

#include "..\..\component.hpp"

params [
["_position", [0, 0, 0]],
["_radius", 0],
["_maxTries", 10],
["_findPosOfInterest",false],
["_findRoadPos",false],
["_findWaterPos",false]
];

private _waypointPosition = [];

//create waypoints

for "_i" from 1 to _maxTries do {
LOG_1("trying to create wp, pass #%1 ", _i);

private _searchPosition = [
_position,
[_radius / 2 ,_radius],
[0,360],
nil,
_findWaterPos,
_findRoadPos
] call FUNC(findRandomPos);

_searchPosition = if (_findPosOfInterest && {80 > random 100}) then {
[_searchPosition, false] call FUNC(findPositionOfInterest);
} else {
_searchPosition
};

private _inAnyExclusionZone = [GRAD_CIVS_EXCLUSION_ZONES, {_searchPosition inArea (_this#0)}] call FUNC(arrayContains);
if (!_inAnyExclusionZone) exitWith {
LOG_1("position %1 is not in exclusionzone, return it", _searchPosition);
_waypointPosition = _searchPosition;
};
LOG_1("not creating WP at %1", _searchPosition);
};

_waypointPosition
52 changes: 52 additions & 0 deletions functions/behaviour/fn_taskPatrolFindWaypoints.spec.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
["given a point surrounded by exclusion zones on three sides",
{
private _trgW = createTrigger ["EmptyDetector", [-250, 150, 0]];
_trgW setTriggerArea [150, 250, 0, true];
[_trgW] call grad_civs_fnc_addExclusionZone;

private _trgS = createTrigger ["EmptyDetector", [0, -250, 0]];
_trgS setTriggerArea [200, 150, 0, true];
[_trgS] call grad_civs_fnc_addExclusionZone;

private _trgE = createTrigger ["EmptyDetector", [250, 150, 0]];
_trgE setTriggerArea [150, 250, 0, true];
[_trgE] call grad_civs_fnc_addExclusionZone;
},
[
["when a patrol path is created",
{
[([[0, 0, 0], 250, 3] call grad_civs_fnc_taskPatrolFindWaypoints)]
},
[
["three positions are returned as requested",
{
params [["_positions", []]];
[3, count _positions] call grad_testing_fnc_assertEquals;
}
],
["will avoid exclusion zones as waypoints",
{
params [["_positions", []]];

{
private _exclusionZone = _x;
private _exclusionZoneIdx = _forEachIndex;
{
[
_x inArea _exclusionZone,
format ["waypoint %1 at %2 is not in exclusion zone at %3 (%4)", _forEachIndex, _x, getPos _exclusionZone, triggerArea _exclusionZone]
] call grad_testing_fnc_assertFalse;
} forEach _positions;
} forEach (call grad_civs_fnc_getExclusionZones);
}
],
["will avoid crossing exclusion zones",
{
["TODO"] call grad_testing_fnc_skipTest;
}
]
]
]
],
grad_civs_fnc_clearExclusionZones
] call grad_testing_fnc_executeTest;
Loading

0 comments on commit 6779b00

Please sign in to comment.