forge/arma/server/addons/task/functions/fnc_missionManager.sqf
Jacob Schmidt 5576cc4746 Refactor task modules for improved parameter handling and logging
- Updated fnc_defuseModule, fnc_deliveryModule, fnc_destroyModule, fnc_hostageModule, fnc_hvtModule to enhance parameter retrieval and logging.
- Introduced error handling for missing task IDs across modules.
- Consolidated task initialization logic into fnc_startTask for better maintainability.
- Added fnc_cargoModule as a sync target for cargo entities in delivery tasks.
- Improved logging to provide clearer insights into task parameters and synced entities.
2026-04-17 19:34:21 -05:00

351 lines
14 KiB
Plaintext

#include "..\script_component.hpp"
/*
* Author: IDSolutions
* Manages attack-only dynamic mission generation.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call forge_server_task_fnc_missionManager
*
* Public: No
*/
#pragma hemtt ignore_variables ["_self"]
GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [
["#type", "MissionManagerBaseClass"],
["#create", compileFinal {
private _missionConfig = missionConfigFile >> "CfgMissions";
_self set ["missionConfig", _missionConfig];
_self set ["locationsConfig", (_missionConfig >> "Locations")];
_self set ["aiGroupsConfig", (_missionConfig >> "AIGroups")];
_self set ["attackConfig", (_missionConfig >> "MissionTypes" >> "Attack")];
_self set ["maxConcurrentMissions", getNumber (_missionConfig >> "maxConcurrentMissions")];
_self set ["missionInterval", getNumber (_missionConfig >> "missionInterval")];
_self set ["recentLocationRegistry", createHashMap];
_self set ["activeMissionRegistry", createHashMap];
}],
["getMissionInterval", compileFinal {
private _interval = _self getOrDefault ["missionInterval", 300];
if (_interval <= 0) then { _interval = 300; };
_interval
}],
["getMaxConcurrentMissions", compileFinal {
private _maxConcurrent = _self getOrDefault ["maxConcurrentMissions", 1];
if (_maxConcurrent <= 0) then { _maxConcurrent = 1; };
private _attackLocationCount = _self call ["getAttackLocationCount", []];
if (_attackLocationCount > 0) then {
_maxConcurrent = _maxConcurrent min _attackLocationCount;
};
_maxConcurrent
}],
["getAttackLocationCount", compileFinal {
private _locationsConfig = _self getOrDefault ["locationsConfig", configNull];
if (isNull _locationsConfig) exitWith { 0 };
private _count = 0;
{
if ("attack" in getArray (_x >> "suitable")) then {
_count = _count + 1;
};
} forEach ("true" configClasses _locationsConfig);
_count
}],
["getLocationReuseCooldown", compileFinal {
private _missionConfig = _self getOrDefault ["missionConfig", configNull];
private _cooldown = getNumber (_missionConfig >> "locationReuseCooldown");
if (_cooldown <= 0) then { _cooldown = 900; };
_cooldown
}],
["getActiveMissionIds", compileFinal {
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
keys _activeMissionRegistry
}],
["getActiveLocationKeys", compileFinal {
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
private _locationKeys = [];
{
private _locationKey = _y getOrDefault ["locationKey", ""];
if (_locationKey isNotEqualTo "") then {
_locationKeys pushBackUnique _locationKey;
};
} forEach _activeMissionRegistry;
_locationKeys
}],
["buildAttackSpawnPosition", compileFinal {
params [["_locationConfig", configNull, [configNull]]];
if (isNull _locationConfig) exitWith { [0, 0, 0] };
private _center = getArray (_locationConfig >> "position");
private _radius = getNumber (_locationConfig >> "radius");
if (_radius <= 0) exitWith { _center };
private _spawnPosition = +_center;
private _attempts = 0;
while { _attempts < 8 } do {
private _angle = random 360;
private _distance = (_radius * 0.2) + random (_radius * 0.65);
private _candidate = [
(_center # 0) + ((sin _angle) * _distance),
(_center # 1) + ((cos _angle) * _distance),
_center param [2, 0]
];
if !(surfaceIsWater _candidate) exitWith {
_spawnPosition = _candidate;
};
_attempts = _attempts + 1;
};
_spawnPosition
}],
["selectAttackLocation", compileFinal {
private _locationsConfig = _self getOrDefault ["locationsConfig", configNull];
private _locations = [];
private _recentLocationRegistry = _self getOrDefault ["recentLocationRegistry", createHashMap];
private _activeLocationKeys = _self call ["getActiveLocationKeys", []];
private _reuseCooldown = _self call ["getLocationReuseCooldown", []];
private _now = serverTime;
{
private _locationKey = configName _x;
private _lastUsed = _recentLocationRegistry getOrDefault [_locationKey, -1];
private _isCoolingDown = (_lastUsed >= 0) && { (_now - _lastUsed) < _reuseCooldown };
if (
"attack" in getArray (_x >> "suitable")
&& { !(_locationKey in _activeLocationKeys) }
&& { !_isCoolingDown }
) then {
_locations pushBack _x;
};
} forEach ("true" configClasses _locationsConfig);
if (_locations isEqualTo []) then {
{
if ("attack" in getArray (_x >> "suitable") && { !(configName _x in _activeLocationKeys) }) then {
_locations pushBack _x;
};
} forEach ("true" configClasses _locationsConfig);
};
if (_locations isEqualTo []) exitWith { createHashMap };
private _location = selectRandom _locations;
createHashMapFromArray [
["config", _location],
["key", configName _location],
["position", _self call ["buildAttackSpawnPosition", [_location]]]
]
}],
["spawnAttackGroup", compileFinal {
params [["_position", [0, 0, 0], [[]]]];
private _aiGroupsConfig = _self getOrDefault ["aiGroupsConfig", configNull];
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
private _groups = [];
{
if ("attack" in getArray (_x >> "suitable")) then {
_groups pushBack _x;
};
} forEach ("true" configClasses _aiGroupsConfig);
if (_groups isEqualTo []) exitWith { grpNull };
private _groupConfig = selectRandom _groups;
private _side = getText (_groupConfig >> "side");
private _group = createGroup (call compile _side);
private _minUnits = getNumber (_attackConfig >> "minUnits");
private _maxUnits = getNumber (_attackConfig >> "maxUnits");
if (_minUnits <= 0) then { _minUnits = 4; };
if (_maxUnits < _minUnits) then { _maxUnits = _minUnits; };
private _targetUnitCount = floor random [ _minUnits, ceil ((_minUnits + _maxUnits) / 2), _maxUnits + 1 ];
private _unitPool = [];
{
if ((getText (_x >> "side")) isNotEqualTo _side) then { continue; };
{
_unitPool pushBack createHashMapFromArray [
["vehicle", getText (_x >> "vehicle")],
["rank", getText (_x >> "rank")],
["position", getArray (_x >> "position")]
];
} forEach ("true" configClasses (_x >> "Units"));
} forEach _groups;
if (_unitPool isEqualTo []) exitWith {
deleteGroup _group;
grpNull
};
private _leaderPool = _unitPool select {
toUpperANSI (_x getOrDefault ["rank", "PRIVATE"]) in ["SERGEANT", "LIEUTENANT", "CAPTAIN", "MAJOR", "COLONEL"]
};
if (_leaderPool isEqualTo []) then { _leaderPool = +_unitPool; };
private _spawnDefs = [selectRandom _leaderPool];
for "_i" from 1 to (_targetUnitCount - 1) do {
_spawnDefs pushBack (selectRandom _unitPool);
};
{
private _unitClass = _x getOrDefault ["vehicle", ""];
if (_unitClass isEqualTo "") then { continue; };
private _unitOffset = +(_x getOrDefault ["position", [0, 0, 0]]);
if (count _unitOffset < 3) then { _unitOffset resize 3; };
_unitOffset set [0, (_unitOffset # 0) + (random 6 - 3)];
_unitOffset set [1, (_unitOffset # 1) + (random 6 - 3)];
private _unit = _group createUnit [_unitClass, _position vectorAdd _unitOffset, [], 0, "NONE"];
_unit setRank (_x getOrDefault ["rank", "PRIVATE"]);
} forEach _spawnDefs;
_group
}],
["rollRewards", compileFinal {
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
private _equipmentRewards = [];
private _supplyRewards = [];
private _weaponRewards = [];
private _vehicleRewards = [];
private _specialRewards = [];
{
private _category = _x;
{
_x params ["_item", "_chance"];
if (random 1 < _chance) then {
switch (_category) do {
case "equipment": { _equipmentRewards pushBack _item; };
case "supplies": { _supplyRewards pushBack _item; };
case "weapons": { _weaponRewards pushBack _item; };
case "vehicles": { _vehicleRewards pushBack _item; };
case "special": { _specialRewards pushBack _item; };
};
};
} forEach (getArray (_attackConfig >> "Rewards" >> _category));
} forEach ["equipment", "supplies", "weapons", "vehicles", "special"];
createHashMapFromArray [
["equipment", _equipmentRewards],
["supplies", _supplyRewards],
["weapons", _weaponRewards],
["vehicles", _vehicleRewards],
["special", _specialRewards]
]
}],
["startAttackMission", compileFinal {
private _attackConfig = _self getOrDefault ["attackConfig", configNull];
private _locationData = _self call ["selectAttackLocation"];
if (_locationData isEqualTo createHashMap) exitWith { "" };
private _locationKey = _locationData getOrDefault ["key", ""];
private _position = _locationData getOrDefault ["position", [0, 0, 0]];
private _group = _self call ["spawnAttackGroup", [_position]];
if (isNull _group) exitWith { "" };
private _units = units _group;
if (_units isEqualTo []) exitWith {
deleteGroup _group;
""
};
private _taskID = format ["task_attack_%1", round (diag_tickTime * 1000)];
private _rewardRange = getArray (_attackConfig >> "Rewards" >> "money");
private _reputationRange = getArray (_attackConfig >> "Rewards" >> "reputation");
private _penaltyRange = getArray (_attackConfig >> "penalty");
private _timeRange = getArray (_attackConfig >> "timeLimit");
private _rewards = _self call ["rollRewards"];
private _success = [
"attack",
_taskID,
_position,
format ["Attack: %1", _locationKey],
format ["Eliminate hostile forces operating near %1.", _locationKey],
createHashMapFromArray [["targets", _units]],
createHashMapFromArray [
["limitFail", 0],
["limitSuccess", count _units],
["funds", _rewardRange call BFUNC(randomNum)],
["ratingFail", _penaltyRange call BFUNC(randomNum)],
["ratingSuccess", _reputationRange call BFUNC(randomNum)],
["endSuccess", false],
["endFail", false],
["timeLimit", _timeRange call BFUNC(randomNum)],
["equipment", _rewards get "equipment"],
["supplies", _rewards get "supplies"],
["weapons", _rewards get "weapons"],
["vehicles", _rewards get "vehicles"],
["special", _rewards get "special"]
],
0,
"",
"mission_manager"
] call FUNC(startTask);
if !(_success) exitWith { "" };
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
_activeMissionRegistry set [_taskID, createHashMapFromArray [
["locationKey", _locationKey],
["startedAt", serverTime]
]];
_self set ["activeMissionRegistry", _activeMissionRegistry];
_taskID
}],
["completeMission", compileFinal {
params [["_taskID", "", [""]]];
if (_taskID isEqualTo "") exitWith { false };
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
private _missionRecord = _activeMissionRegistry getOrDefault [_taskID, createHashMap];
private _locationKey = _missionRecord getOrDefault ["locationKey", ""];
_activeMissionRegistry deleteAt _taskID;
_self set ["activeMissionRegistry", _activeMissionRegistry];
if (_locationKey isNotEqualTo "") then {
private _recentLocationRegistry = _self getOrDefault ["recentLocationRegistry", createHashMap];
_recentLocationRegistry set [_locationKey, serverTime];
_self set ["recentLocationRegistry", _recentLocationRegistry];
};
true
}]
];
GVAR(MissionManager) = createHashMapObject [GVAR(MissionManagerBaseClass)];
[{
{
private _status = GVAR(TaskStore) call ["getTaskStatus", [_x]];
private _hasCatalogEntry = GVAR(TaskStore) call ["hasTaskCatalogEntry", [_x]];
if (_status in ["succeeded", "failed"] || { _status isEqualTo "" && { !_hasCatalogEntry } }) then {
GVAR(MissionManager) call ["completeMission", [_x]];
GVAR(TaskStore) call ["clearTaskStatus", [_x]];
};
} forEach (GVAR(MissionManager) call ["getActiveMissionIds", []]);
if (count (GVAR(MissionManager) call ["getActiveMissionIds", []]) >= (GVAR(MissionManager) call ["getMaxConcurrentMissions", []])) exitWith {};
private _taskID = GVAR(MissionManager) call ["startAttackMission", []];
if (_taskID isEqualTo "") exitWith {
["WARNING", "Mission manager failed to start an attack mission."] call EFUNC(common,log);
};
["INFO", format ["Mission manager started attack mission %1.", _taskID]] call EFUNC(common,log);
}, GVAR(MissionManager) call ["getMissionInterval", []], []] call CFUNC(addPerFrameHandler);