Add defend wave templates and standardize task endings
- Let defend tasks use synced enemy groups as wave templates - Record task status changes on fail/success - Route end conditions through the server-side mission end helper
This commit is contained in:
parent
9be7f91e2f
commit
e6eceac4ec
@ -375,13 +375,13 @@ class CfgVehicles {
|
||||
};
|
||||
|
||||
class ModuleDescription: ModuleDescription {
|
||||
description = "Creates a defend task with configurable defense zone and enemy wave parameters";
|
||||
sync[] = { "Anything" };
|
||||
description = "Creates a defend task with configurable defense zone and designer-controlled enemy wave templates";
|
||||
sync[] = { "AnyBrain" };
|
||||
|
||||
class Anything {
|
||||
class AnyBrain {
|
||||
description[] = {
|
||||
"Defend task module",
|
||||
"Enemy waves are spawned automatically; no synced entities are required"
|
||||
"Sync with enemy units or group members to use their groups as wave templates"
|
||||
};
|
||||
position = 1;
|
||||
direction = 1;
|
||||
|
||||
@ -143,7 +143,8 @@ Available task modules:
|
||||
- `FORGE_Module_Hostage`: sync to `FORGE_Module_Hostages` and
|
||||
`FORGE_Module_Shooters`
|
||||
- `FORGE_Module_HVT`: sync directly to HVT units
|
||||
- `FORGE_Module_Defend`: configure the defense marker and wave settings
|
||||
- `FORGE_Module_Defend`: configure the defense marker and wave settings; sync
|
||||
enemy units to use their groups as wave templates
|
||||
|
||||
These modules delegate to `fnc_startTask.sqf`.
|
||||
|
||||
|
||||
@ -119,7 +119,7 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
["INFO", format [
|
||||
"Attack task %1 succeeded. TargetsRequired=%2, TargetsKilled=%3",
|
||||
@ -149,5 +149,5 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
@ -16,11 +16,12 @@
|
||||
* 8: Enemy wave count <NUMBER> (default: 3)
|
||||
* 9: Time between waves in seconds <NUMBER> (default: 300)
|
||||
* 10: Minimum BLUFOR units required in zone <NUMBER> (default: 1)
|
||||
* 11: Equipment rewards <ARRAY> (default: [])
|
||||
* 12: Supply rewards <ARRAY> (default: [])
|
||||
* 13: Weapon rewards <ARRAY> (default: [])
|
||||
* 14: Vehicle rewards <ARRAY> (default: [])
|
||||
* 15: Special rewards <ARRAY> (default: [])
|
||||
* 11: Enemy template groups <ARRAY> (default: [])
|
||||
* 12: Equipment rewards <ARRAY> (default: [])
|
||||
* 13: Supply rewards <ARRAY> (default: [])
|
||||
* 14: Weapon rewards <ARRAY> (default: [])
|
||||
* 15: Vehicle rewards <ARRAY> (default: [])
|
||||
* 16: Special rewards <ARRAY> (default: [])
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
@ -43,6 +44,7 @@ params [
|
||||
["_waveCount", 3, [0]],
|
||||
["_waveCooldown", 300, [0]],
|
||||
["_minBlufor", 1, [0]],
|
||||
["_enemyTemplates", [], [[]]],
|
||||
["_equipmentRewards", [], [[]]],
|
||||
["_supplyRewards", [], [[]]],
|
||||
["_weaponRewards", [], [[]]],
|
||||
@ -104,7 +106,7 @@ waitUntil {
|
||||
};
|
||||
|
||||
if (_currentWave < _waveCount && _defenseStarted && { time >= _nextWaveTime }) then {
|
||||
[_defenseZone, _taskID, _currentWave] call FUNC(spawnEnemyWave);
|
||||
[_defenseZone, _taskID, _currentWave, _enemyTemplates] call FUNC(spawnEnemyWave);
|
||||
|
||||
_currentWave = _currentWave + 1;
|
||||
_nextWaveTime = time + _waveCooldown;
|
||||
@ -119,6 +121,7 @@ waitUntil {
|
||||
|
||||
if (_result == 1) then {
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -126,7 +129,7 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
private _rewards = createHashMap;
|
||||
_rewards set ["funds", _companyFunds];
|
||||
@ -139,6 +142,7 @@ if (_result == 1) then {
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -146,5 +150,5 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
@ -84,13 +84,14 @@ if (_result == 1) then {
|
||||
{ deleteVehicle _x } forEach _entities;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _ieds;
|
||||
{ deleteVehicle _x } forEach _entities;
|
||||
@ -106,13 +107,14 @@ if (_result == 1) then {
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_companyFunds] call EFUNC(common,formatNumber)]]];
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
@ -95,6 +95,7 @@ if (_result == 1) then {
|
||||
{ deleteVehicle _x } forEach _cargo;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -102,7 +103,7 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _cargo;
|
||||
|
||||
@ -117,6 +118,7 @@ if (_result == 1) then {
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -124,5 +126,5 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
@ -89,6 +89,7 @@ if (_result == 1) then {
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -96,7 +97,7 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
@ -111,6 +112,7 @@ if (_result == 1) then {
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -118,5 +120,5 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
@ -91,29 +91,27 @@ waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _hostages + _shooters, _extZone, 250]];
|
||||
|
||||
private _playerGroups = allPlayers apply { group _x };
|
||||
private _hostagesFreed = ({
|
||||
alive _x && { ((group _x) in _playerGroups) || { !captive _x } }
|
||||
} count _hostages);
|
||||
private _hostagesInZone = ({ _x inArea _extZone } count _hostages);
|
||||
private _hostagesKilled = ({ !alive _x } count _hostages);
|
||||
private _shootersAlive = ({ alive _x } count _shooters);
|
||||
private _hostageSucceeded = (_hostagesInZone >= _requiredRescues) && { _hostagesKilled < _maxHostageLosses };
|
||||
private _shootersClearedSucceeded = (!isNil "_shooters") && { _shootersAlive <= 0 } && { _hostageSucceeded };
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
private _timeExpired = (floor time - _startTime >= _timeLimit);
|
||||
|
||||
if (_hostagesFreed < _requiredRescues && _timeExpired) then { _result = 1; };
|
||||
if (!_hostageSucceeded && _timeExpired) then { _result = 1; };
|
||||
if (_hostagesKilled >= _maxHostageLosses) then { _result = 1; };
|
||||
|
||||
(_result == 1) or
|
||||
((_hostagesInZone >= _requiredRescues) && (_hostagesKilled < _maxHostageLosses)) or
|
||||
((!isNil "_shooters") && (_shootersAlive <= 0) && (_hostagesInZone >= _requiredRescues) && (_hostagesKilled < _maxHostageLosses))
|
||||
_hostageSucceeded or
|
||||
_shootersClearedSucceeded
|
||||
} else {
|
||||
if (_hostagesKilled >= _maxHostageLosses) then { _result = 1; };
|
||||
|
||||
(_result == 1) or
|
||||
((_hostagesInZone >= _requiredRescues) && (_hostagesKilled < _maxHostageLosses)) or
|
||||
((!isNil "_shooters") && (_shootersAlive <= 0) && (_hostagesInZone >= _requiredRescues) && (_hostagesKilled < _maxHostageLosses))
|
||||
_hostageSucceeded or
|
||||
_shootersClearedSucceeded
|
||||
};
|
||||
};
|
||||
|
||||
@ -152,6 +150,7 @@ if (_result == 1) then {
|
||||
{ deleteVehicle _x } forEach _shooters;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -159,7 +158,7 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _hostages;
|
||||
{ deleteVehicle _x } forEach _shooters;
|
||||
@ -175,6 +174,7 @@ if (_result == 1) then {
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -182,5 +182,5 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
@ -66,6 +66,8 @@ waitUntil {
|
||||
};
|
||||
|
||||
_hvts = GVAR(TaskStore) call ["getTaskEntities", ["hvts", _taskID]];
|
||||
private _requiredHvts = if (_limitSuccess < 0) then { count _hvts } else { _limitSuccess };
|
||||
private _maxHvtLosses = if (_limitFail < 0) then { count _hvts } else { _limitFail };
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
waitUntil {
|
||||
@ -80,22 +82,23 @@ waitUntil {
|
||||
sleep 1;
|
||||
GVAR(TaskStore) call ["trackParticipants", [_taskID, _hvts, _extZone, 250]];
|
||||
|
||||
private _hvtsCaptive = ({ captive _x } count _hvts);
|
||||
private _hvtsKilled = ({ !alive _x } count _hvts);
|
||||
private _hvtsInZone = ({ _x inArea _extZone } count _hvts);
|
||||
private _captureSucceeded = _capture && { _hvtsInZone >= _requiredHvts } && { _hvtsKilled < _maxHvtLosses };
|
||||
private _eliminateSucceeded = _eliminate && { _hvtsKilled >= _requiredHvts };
|
||||
|
||||
if (_timeLimit isNotEqualTo 0) then {
|
||||
private _timeExpired = (floor time - _startTime >= _timeLimit);
|
||||
|
||||
if (_capture && _hvtsKilled >= _limitFail) then { _result = 1; };
|
||||
if (_capture && _hvtsCaptive < _limitSuccess && _timeExpired) then { _result = 1; };
|
||||
if (_eliminate && _hvtsKilled < _limitSuccess && _timeExpired) then { _result = 1; };
|
||||
if (_capture && { _hvtsKilled >= _maxHvtLosses }) then { _result = 1; };
|
||||
if (_capture && { !_captureSucceeded } && { _timeExpired }) then { _result = 1; };
|
||||
if (_eliminate && { !_eliminateSucceeded } && { _timeExpired }) then { _result = 1; };
|
||||
|
||||
(_result == 1) or (_capture && (_hvtsInZone >= _limitSuccess) && (_hvtsKilled < _limitFail)) or (_eliminate && (_hvtsKilled >= _limitSuccess))
|
||||
(_result == 1) or _captureSucceeded or _eliminateSucceeded
|
||||
} else {
|
||||
if (_capture && (_hvtsKilled >= _limitFail)) then { _result = 1; };
|
||||
if (_capture && { _hvtsKilled >= _maxHvtLosses }) then { _result = 1; };
|
||||
|
||||
(_result == 1) or (_capture && (_hvtsInZone >= _limitSuccess) && (_hvtsKilled < _limitFail)) or (_eliminate && (_hvtsKilled >= _limitSuccess))
|
||||
(_result == 1) or _captureSucceeded or _eliminateSucceeded
|
||||
};
|
||||
};
|
||||
|
||||
@ -103,6 +106,7 @@ if (_result == 1) then {
|
||||
{ deleteVehicle _x } forEach _hvts;
|
||||
|
||||
[_taskID, "FAILED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "failed"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -110,7 +114,7 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _hvts;
|
||||
|
||||
@ -125,6 +129,7 @@ if (_result == 1) then {
|
||||
|
||||
[_taskID, _rewards] call FUNC(handleTaskRewards);
|
||||
[_taskID, "SUCCEEDED"] call BFUNC(taskSetState);
|
||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "succeeded"]];
|
||||
|
||||
sleep 1;
|
||||
|
||||
@ -132,5 +137,5 @@ if (_result == 1) then {
|
||||
GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]];
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
@ -28,8 +28,6 @@ SETVAR(_entity,assignedTask,_taskID);
|
||||
GVAR(TaskStore) call ["registerTaskEntity", ["hostages", _taskID, _entity]];
|
||||
|
||||
if (alive _entity) then {
|
||||
// Apply hostage protection immediately so nearby hostile AI cannot kill the
|
||||
// unit before the scheduled heartbeat initializes the animation state.
|
||||
_entity setCaptive true;
|
||||
_entity enableAIFeature ["MOVE", false];
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
* 0: Defense zone marker name <STRING>
|
||||
* 1: Task ID <STRING>
|
||||
* 2: Wave number (0-based) <NUMBER>
|
||||
* 3: Enemy template groups <ARRAY> (default: [])
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
@ -18,7 +19,7 @@
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params [["_defenseZone", "", [""]], ["_taskID", "", [""]], ["_waveNumber", 0, [0]]];
|
||||
params [["_defenseZone", "", [""]], ["_taskID", "", [""]], ["_waveNumber", 0, [0]], ["_enemyTemplates", [], [[]]]];
|
||||
|
||||
if (_defenseZone == "") exitWith { ["ERROR", "No defense zone provided for enemy wave spawn"] call EFUNC(common,log); };
|
||||
|
||||
@ -47,37 +48,83 @@ for "_i" from 0 to 3 do {
|
||||
private _spawnPos = [_spawnX, _spawnY, 0];
|
||||
|
||||
private _safePos = _spawnPos findEmptyPosition [0, 50, "O_Soldier_F"];
|
||||
if (count _safePos > 0) then {
|
||||
_spawnPositions pushBack _safePos;
|
||||
};
|
||||
if (count _safePos > 0) then { _spawnPositions pushBack _safePos; };
|
||||
};
|
||||
|
||||
private _groups = [];
|
||||
{
|
||||
private _groupSize = ceil(_unitCount / (count _spawnPositions));
|
||||
private _group = createGroup east;
|
||||
_groups pushBack _group;
|
||||
if (_spawnPositions isEqualTo []) exitWith {
|
||||
["ERROR", format ["Defense wave %1 for task %2 could not find spawn positions", _waveNumber + 1, _taskID]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
for "_i" from 1 to _groupSize do {
|
||||
private _unitType = _basicTypes select (floor random count _basicTypes);
|
||||
private _roll = random 1;
|
||||
if (_enemyTemplates isNotEqualTo []) then {
|
||||
private _groupCount = ((_waveNumber + 1) min 4) min (count _spawnPositions);
|
||||
private _selectedSpawnPositions = +_spawnPositions;
|
||||
_selectedSpawnPositions resize _groupCount;
|
||||
|
||||
if (_roll < _eliteChance) then {
|
||||
_unitType = _eliteTypes select (floor random count _eliteTypes);
|
||||
} else {
|
||||
if (_roll < _specialChance) then {
|
||||
_unitType = _specialTypes select (floor random count _specialTypes);
|
||||
{
|
||||
private _spawnPos = _x;
|
||||
private _templateGroup = selectRandom _enemyTemplates;
|
||||
if !(_templateGroup isEqualType []) then { continue; };
|
||||
if (_templateGroup isEqualTo []) then { continue; };
|
||||
|
||||
private _firstTemplate = _templateGroup select 0;
|
||||
if !(_firstTemplate isEqualType createHashMap) then { continue; };
|
||||
|
||||
private _side = _firstTemplate getOrDefault ["side", east];
|
||||
private _group = createGroup _side;
|
||||
_groups pushBack _group;
|
||||
|
||||
{
|
||||
private _unitTemplate = _x;
|
||||
if !(_unitTemplate isEqualType createHashMap) then { continue; };
|
||||
|
||||
private _unitType = _unitTemplate getOrDefault ["type", "O_Soldier_F"];
|
||||
private _unit = _group createUnit [_unitType, _spawnPos, [], 0, "NONE"];
|
||||
_unit setVariable ["assignedTask", _taskID, true];
|
||||
_unit setUnitLoadout (_unitTemplate getOrDefault ["loadout", getUnitLoadout _unit]);
|
||||
_unit setSkill (_unitTemplate getOrDefault ["skill", skill _unit]);
|
||||
_unit setRank (_unitTemplate getOrDefault ["rank", rank _unit]);
|
||||
_unit setBehaviour "AWARE";
|
||||
_unit setSpeedMode "NORMAL";
|
||||
_unit enableDynamicSimulation true;
|
||||
} forEach _templateGroup;
|
||||
|
||||
[_group, _center, _radius * 0.75] call CFUNC(taskDefend);
|
||||
} forEach _selectedSpawnPositions;
|
||||
|
||||
["INFO", format [
|
||||
"Spawned defense wave %1 for task %2 from %3 template group(s)",
|
||||
_waveNumber + 1,
|
||||
_taskID,
|
||||
count _groups
|
||||
]] call EFUNC(common,log);
|
||||
} else {
|
||||
{
|
||||
private _groupSize = ceil(_unitCount / (count _spawnPositions));
|
||||
private _group = createGroup east;
|
||||
_groups pushBack _group;
|
||||
|
||||
for "_i" from 1 to _groupSize do {
|
||||
private _unitType = _basicTypes select (floor random count _basicTypes);
|
||||
private _roll = random 1;
|
||||
|
||||
if (_roll < _eliteChance) then {
|
||||
_unitType = _eliteTypes select (floor random count _eliteTypes);
|
||||
} else {
|
||||
if (_roll < _specialChance) then {
|
||||
_unitType = _specialTypes select (floor random count _specialTypes);
|
||||
};
|
||||
};
|
||||
|
||||
private _unit = _group createUnit [_unitType, _x, [], 0, "NONE"];
|
||||
_unit setVariable ["assignedTask", _taskID, true];
|
||||
_unit setBehaviour "AWARE";
|
||||
_unit setSpeedMode "NORMAL";
|
||||
_unit enableDynamicSimulation true;
|
||||
};
|
||||
|
||||
private _unit = _group createUnit [_unitType, _x, [], 0, "NONE"];
|
||||
_unit setVariable ["assignedTask", _taskID, true];
|
||||
_unit setBehaviour "AWARE";
|
||||
_unit setSpeedMode "NORMAL";
|
||||
_unit enableDynamicSimulation true;
|
||||
};
|
||||
[_group, _center, _radius * 0.75] call CFUNC(taskDefend);
|
||||
} forEach _spawnPositions;
|
||||
|
||||
[_group, _center, _radius * 0.75] call CFUNC(taskDefend);
|
||||
} forEach _spawnPositions;
|
||||
|
||||
["INFO", format ["Spawned defense wave %1 for task %2 with %3 units", _waveNumber + 1, _taskID, _unitCount]] call EFUNC(common,log);
|
||||
["INFO", format ["Spawned defense wave %1 for task %2 with %3 fallback units", _waveNumber + 1, _taskID, _unitCount]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
* "waveCount" <NUMBER> (default: 3)
|
||||
* "waveCooldown" <NUMBER> (default: 300)
|
||||
* "minBlufor" <NUMBER> (default: 1)
|
||||
* "enemyTemplates" <ARRAY> (default: [])
|
||||
* 7: Minimum org reputation required <NUMBER> (default: 0)
|
||||
* 8: Requester UID <STRING> (default: "")
|
||||
* 9: Source tag <STRING> (default: "eden") -- "eden"|"mission_manager"|"script"
|
||||
@ -186,7 +187,8 @@ private _handlerArgs = switch (_taskType) do {
|
||||
private _waveCount = _taskParams getOrDefault ["waveCount", 3];
|
||||
private _waveCooldown = _taskParams getOrDefault ["waveCooldown", 300];
|
||||
private _minBlufor = _taskParams getOrDefault ["minBlufor", 1];
|
||||
[_taskID, _defenseZone, _defendTime, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _waveCount, _waveCooldown, _minBlufor] + _rewardTail
|
||||
private _enemyTemplates = _taskParams getOrDefault ["enemyTemplates", []];
|
||||
[_taskID, _defenseZone, _defendTime, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _waveCount, _waveCooldown, _minBlufor, _enemyTemplates] + _rewardTail
|
||||
};
|
||||
default {
|
||||
["ERROR", format ["startTask: unknown task type '%1'.", _taskType]] call EFUNC(common,log);
|
||||
|
||||
@ -5,8 +5,7 @@
|
||||
* Initializes the defend task module.
|
||||
* Reads parameters from the logic object and delegates to fnc_startTask.
|
||||
* The designer must place a named marker in Eden for the defense zone.
|
||||
* Enemy waves are spawned automatically by fnc_defend — no entities need
|
||||
* to be synced to this module.
|
||||
* Synced enemy units are used as wave composition templates.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Logic <OBJECT>
|
||||
@ -33,13 +32,51 @@ if (_defenseZone isEqualTo "" || { markerShape _defenseZone isEqualTo "" }) exit
|
||||
["ERROR", format ["Defend module '%1': DefenseZone marker '%2' is missing or invalid.", _taskID, _defenseZone]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
private _syncedEnemies = synchronizedObjects _logic select { _x isKindOf "CAManBase" };
|
||||
private _templateGroups = [];
|
||||
private _templateUnits = [];
|
||||
private _seenGroups = [];
|
||||
|
||||
{
|
||||
private _group = group _x;
|
||||
if (_group in _seenGroups) then { continue; };
|
||||
_seenGroups pushBack _group;
|
||||
|
||||
private _templates = [];
|
||||
{
|
||||
if (isNull _x) then { continue; };
|
||||
_templateUnits pushBackUnique _x;
|
||||
_templates pushBack createHashMapFromArray [
|
||||
["type", typeOf _x],
|
||||
["loadout", getUnitLoadout _x],
|
||||
["skill", skill _x],
|
||||
["rank", rank _x],
|
||||
["side", side _x]
|
||||
];
|
||||
} forEach (units _group);
|
||||
|
||||
if (_templates isNotEqualTo []) then {
|
||||
_templateGroups pushBack _templates;
|
||||
};
|
||||
} forEach _syncedEnemies;
|
||||
|
||||
{ deleteVehicle _x } forEach _templateUnits;
|
||||
|
||||
if (_templateGroups isEqualTo []) then {
|
||||
["WARNING", format [
|
||||
"Defend module '%1' has no synced enemy units. Falling back to default CSAT wave templates.",
|
||||
_taskID
|
||||
]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
["INFO", format [
|
||||
"Defend Module Parameters: TaskID: %1, DefenseZone: %2, DefendTime: %3, WaveCount: %4, WaveCooldown: %5, MinBlufor: %6",
|
||||
"Defend Module Parameters: TaskID: %1, DefenseZone: %2, DefendTime: %3, WaveCount: %4, WaveCooldown: %5, MinBlufor: %6, EnemyTemplateGroups: %7",
|
||||
_taskID, _defenseZone,
|
||||
_logic getVariable ["DefendTime", 600],
|
||||
_logic getVariable ["WaveCount", 3],
|
||||
_logic getVariable ["WaveCooldown", 300],
|
||||
_logic getVariable ["MinBlufor", 1]
|
||||
_logic getVariable ["MinBlufor", 1],
|
||||
count _templateGroups
|
||||
]] call EFUNC(common,log);
|
||||
|
||||
private _equipmentRewards = [_logic getVariable ["EquipmentRewards", "[]"], _taskID, "equipment"] call FUNC(parseRewards);
|
||||
@ -66,6 +103,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
||||
["waveCount", _logic getVariable ["WaveCount", 3]],
|
||||
["waveCooldown", _logic getVariable ["WaveCooldown", 300]],
|
||||
["minBlufor", _logic getVariable ["MinBlufor", 1]],
|
||||
["enemyTemplates", _templateGroups],
|
||||
["equipment", _equipmentRewards],
|
||||
["supplies", _supplyRewards],
|
||||
["weapons", _weaponRewards],
|
||||
|
||||
@ -158,7 +158,7 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
} else {
|
||||
{ deleteVehicle _x } forEach _targets;
|
||||
|
||||
@ -174,7 +174,7 @@ GVAR(AttackTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
};
|
||||
|
||||
_self call ["cleanup", []];
|
||||
|
||||
@ -22,6 +22,7 @@ GVAR(DefendTaskBaseClass) = createHashMapFromArray [
|
||||
_self set ["waveCount", _taskParams getOrDefault ["waveCount", 3]];
|
||||
_self set ["waveCooldown", _taskParams getOrDefault ["waveCooldown", 300]];
|
||||
_self set ["minBlufor", _taskParams getOrDefault ["minBlufor", 1]];
|
||||
_self set ["enemyTemplates", _taskParams getOrDefault ["enemyTemplates", []]];
|
||||
_self set ["nextWaveTime", -1];
|
||||
_self set ["currentWave", 0];
|
||||
_self set ["zoneEmptyCounter", 0];
|
||||
@ -91,6 +92,7 @@ GVAR(DefendTaskBaseClass) = createHashMapFromArray [
|
||||
private _waveCooldown = _self getOrDefault ["waveCooldown", 300];
|
||||
private _minBlufor = _self getOrDefault ["minBlufor", 1];
|
||||
private _currentWave = _self getOrDefault ["currentWave", 0];
|
||||
private _enemyTemplates = _self getOrDefault ["enemyTemplates", []];
|
||||
private _nextWaveTime = _self getOrDefault ["nextWaveTime", -1];
|
||||
private _zoneEmptyCounter = _self getOrDefault ["zoneEmptyCounter", 0];
|
||||
private _warningIssued = _self getOrDefault ["warningIssued", false];
|
||||
@ -110,7 +112,7 @@ GVAR(DefendTaskBaseClass) = createHashMapFromArray [
|
||||
};
|
||||
|
||||
if (_currentWave < _waveCount && { serverTime >= _nextWaveTime }) then {
|
||||
[_defenseZone, _taskID, _currentWave] call FUNC(spawnEnemyWave);
|
||||
[_defenseZone, _taskID, _currentWave, _enemyTemplates] call FUNC(spawnEnemyWave);
|
||||
|
||||
_currentWave = _currentWave + 1;
|
||||
_nextWaveTime = serverTime + _waveCooldown;
|
||||
@ -152,7 +154,7 @@ GVAR(DefendTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["handleSuccessOutcome", compileFinal {
|
||||
@ -174,7 +176,7 @@ GVAR(DefendTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
|
||||
@ -125,7 +125,7 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["handleSuccessOutcome", compileFinal {
|
||||
@ -152,7 +152,7 @@ GVAR(DefuseTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
|
||||
@ -145,7 +145,7 @@ GVAR(DeliveryTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["handleSuccessOutcome", compileFinal {
|
||||
@ -170,7 +170,7 @@ GVAR(DeliveryTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
|
||||
@ -125,7 +125,7 @@ GVAR(DestroyTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["handleSuccessOutcome", compileFinal {
|
||||
@ -150,7 +150,7 @@ GVAR(DestroyTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
|
||||
@ -121,6 +121,9 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [
|
||||
_timeExpired = (serverTime - _startedAt) >= _timeLimit;
|
||||
};
|
||||
|
||||
private _captureSucceeded = _capture && { _inZone >= _required } && { _killed < _maxKilled };
|
||||
private _eliminateSucceeded = _eliminate && { _killed >= _required };
|
||||
|
||||
createHashMapFromArray [
|
||||
["captives", _captives],
|
||||
["killed", _killed],
|
||||
@ -128,8 +131,8 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [
|
||||
["required", _required],
|
||||
["maxKilled", _maxKilled],
|
||||
["timeExpired", _timeExpired],
|
||||
["shouldFail", (_capture && { _killed >= _maxKilled }) || { _timeExpired && { (_capture && { _captives < _required }) || { _eliminate && { _killed < _required } } } }],
|
||||
["shouldSucceed", (_capture && { _inZone >= _required } && { _killed < _maxKilled }) || { _eliminate && { _killed >= _required } }]
|
||||
["shouldFail", (_capture && { _killed >= _maxKilled }) || { _timeExpired && { (_capture && { !_captureSucceeded }) || { _eliminate && { !_eliminateSucceeded } } } }],
|
||||
["shouldSucceed", _captureSucceeded || _eliminateSucceeded]
|
||||
]
|
||||
}],
|
||||
["handleFailureOutcome", compileFinal {
|
||||
@ -152,7 +155,7 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["handleSuccessOutcome", compileFinal {
|
||||
@ -177,7 +180,7 @@ GVAR(HVTTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
|
||||
@ -234,6 +234,9 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
_timeExpired = (serverTime - _startedAt) >= _timeLimit;
|
||||
};
|
||||
|
||||
private _hostageSucceeded = (_inZone >= _requiredRescues) && { _killed < _maxHostageLosses };
|
||||
private _shootersClearedSucceeded = (_shootersAlive <= 0) && { _hostageSucceeded };
|
||||
|
||||
createHashMapFromArray [
|
||||
["freed", _freed],
|
||||
["inZone", _inZone],
|
||||
@ -242,8 +245,8 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
["requiredRescues", _requiredRescues],
|
||||
["maxHostageLosses", _maxHostageLosses],
|
||||
["timeExpired", _timeExpired],
|
||||
["shouldFail", (_killed >= _maxHostageLosses) || { _timeExpired && { _freed < _requiredRescues } }],
|
||||
["shouldSucceed", ((_inZone >= _requiredRescues) && { _killed < _maxHostageLosses }) || { (_shootersAlive <= 0) && { _inZone >= _requiredRescues } && { _killed < _maxHostageLosses } }]
|
||||
["shouldFail", (_killed >= _maxHostageLosses) || { _timeExpired && { !_hostageSucceeded } }],
|
||||
["shouldSucceed", _hostageSucceeded || _shootersClearedSucceeded]
|
||||
]
|
||||
}],
|
||||
["handleFailureOutcome", compileFinal {
|
||||
@ -301,7 +304,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endFail) then { "EveryoneLost" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["handleSuccessOutcome", compileFinal {
|
||||
@ -328,7 +331,7 @@ GVAR(HostageTaskBaseClass) = createHashMapFromArray [
|
||||
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||
};
|
||||
|
||||
if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; };
|
||||
if (_endSuccess) then { "EveryoneWon" call BFUNC(endMissionServer); };
|
||||
true
|
||||
}],
|
||||
["runLoop", compileFinal {
|
||||
|
||||
@ -199,7 +199,8 @@ Available task modules:
|
||||
- `FORGE_Module_Hostage`: sync to `FORGE_Module_Hostages` and
|
||||
`FORGE_Module_Shooters`.
|
||||
- `FORGE_Module_HVT`: sync directly to HVT units.
|
||||
- `FORGE_Module_Defend`: configure the defense marker and wave settings.
|
||||
- `FORGE_Module_Defend`: configure the defense marker and wave settings; sync
|
||||
enemy units to use their groups as wave templates.
|
||||
|
||||
These modules delegate to `forge_server_task_fnc_startTask`.
|
||||
|
||||
@ -411,12 +412,16 @@ Setup:
|
||||
6. Set `WaveCount`.
|
||||
7. Set `WaveCooldown`.
|
||||
8. Set `MinBlufor` to the minimum number of friendlies required in the zone.
|
||||
9. Set rewards, rating, and end-state options.
|
||||
9. Place one or more enemy groups or units to use as wave templates.
|
||||
10. Sync any unit from each enemy group to the defend module.
|
||||
11. Set rewards, rating, and end-state options.
|
||||
|
||||
Notes:
|
||||
|
||||
- No enemy groups need to be pre-placed or synced. The defend task spawns its
|
||||
own enemy waves.
|
||||
- Synced enemy units are treated as templates. Syncing one unit from a group
|
||||
makes the whole group available as a wave composition.
|
||||
- If no enemy units are synced, the defend task falls back to default CSAT
|
||||
infantry waves.
|
||||
- The defend task waits for the required number of BLUFOR to enter the zone
|
||||
before the timer, waves, and empty-zone failure checks begin.
|
||||
- `DefenseZone` must be an area marker.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user