diff --git a/arma/server/addons/phone.7z b/arma/server/addons/phone.7z deleted file mode 100644 index a8f16b5..0000000 Binary files a/arma/server/addons/phone.7z and /dev/null differ diff --git a/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf b/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf index 1ba8f83..8eaec05 100644 --- a/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf +++ b/arma/server/addons/task/functions/fnc_handleTaskRewards.sqf @@ -201,29 +201,16 @@ private _grantOrgFleet = { }; private _equipment = _rewards getOrDefault ["equipment", []]; -if (count _equipment > 0) then { - ["equipment", _equipment] call _grantOrgAssets; -}; - -private _supplies = _rewards getOrDefault ["supplies", []]; -if (count _supplies > 0) then { - ["supplies", _supplies] call _grantOrgAssets; -}; - -private _weapons = _rewards getOrDefault ["weapons", []]; -if (count _weapons > 0) then { - ["weapons", _weapons] call _grantOrgAssets; -}; - private _special = _rewards getOrDefault ["special", []]; -if (count _special > 0) then { - ["special", _special] call _grantOrgAssets; -}; - +private _supplies = _rewards getOrDefault ["supplies", []]; private _vehicles = _rewards getOrDefault ["vehicles", []]; -if (count _vehicles > 0) then { - [_vehicles] call _grantOrgFleet; -}; +private _weapons = _rewards getOrDefault ["weapons", []]; + +if (_equipment isNotEqualTo []) then { ["equipment", _equipment] call _grantOrgAssets; }; +if (_supplies isNotEqualTo []) then {["supplies", _supplies] call _grantOrgAssets; }; +if (_weapons isNotEqualTo []) then { ["weapons", _weapons] call _grantOrgAssets; }; +if (_special isNotEqualTo []) then { ["special", _special] call _grantOrgAssets; }; +if (_vehicles isNotEqualTo []) then { [_vehicles] call _grantOrgFleet; }; if (_success) then { private _orgName = ""; diff --git a/arma/server/addons/task/functions/fnc_startTask.sqf b/arma/server/addons/task/functions/fnc_startTask.sqf index b108747..89c08af 100644 --- a/arma/server/addons/task/functions/fnc_startTask.sqf +++ b/arma/server/addons/task/functions/fnc_startTask.sqf @@ -96,9 +96,13 @@ if (_taskType isEqualTo "" || { _taskID isEqualTo "" }) exitWith { private _iedTimer = _taskParams getOrDefault ["iedTimer", 0]; { - private _role = _x; + private _role = _x; private _objects = _entities getOrDefault [_role, []]; { + if !(_x isEqualType objNull) then { + ["WARNING", format ["startTask: skipping non-object entity for role '%1' in task '%2': %3", _role, _taskID, _x]] call EFUNC(common,log); + continue; + }; if (isNull _x) then { continue; }; switch (_role) do { case "targets": { [_x, _taskID] call FUNC(makeTarget); }; diff --git a/arma/server/addons/task/prototypes/AttackTaskBaseClass.sqf b/arma/server/addons/task/prototypes/AttackTaskBaseClass.sqf new file mode 100644 index 0000000..23e7649 --- /dev/null +++ b/arma/server/addons/task/prototypes/AttackTaskBaseClass.sqf @@ -0,0 +1,171 @@ +#include "..\script_component.hpp" + +/* + * Review-only prototype attack task class. + * + * Example: + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\TaskInstanceBaseClass.sqf"; + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\AttackTaskBaseClass.sqf"; + * + * private _task = createHashMapObject [ + * GVAR(AttackTaskBaseClass), + * [ + * "task_attack_review", + * createHashMapFromArray [ + * ["targets", [unit1, unit2, unit3]] + * ], + * createHashMapFromArray [ + * ["limitSuccess", 3], + * ["timeLimit", 900], + * ["funds", 50000], + * ["ratingSuccess", 25] + * ] + * ] + * ]; + * + * [_task] spawn { + * params ["_task"]; + * _task call ["runLoop", []]; + * }; + * _task = nil; // Safe after the spawned closure has captured the reference. + * + * Note: + * `runLoop` uses `sleep`, so it must be entered from scheduled code. + */ + +#pragma hemtt ignore_variables ["_self"] + +GVAR(AttackTaskBaseClass) = createHashMapFromArray [ + ["#base", GVAR(TaskInstanceBaseClass)], + ["#type", "AttackTaskBaseClass"], + ["#create", compileFinal { + params [ + ["_taskID", "", [""]], + ["_entities", createHashMap, [createHashMap]], + ["_taskParams", createHashMap, [createHashMap]] + ]; + + _self call ["initializeBaseState", [_taskID, "attack", _entities, _taskParams]]; + + private _targets = +(_entities getOrDefault ["targets", []]); + private _requiredKills = _taskParams getOrDefault ["limitSuccess", -1]; + if (_requiredKills < 0) then { _requiredKills = count _targets; }; + + private _maxTargetLosses = _taskParams getOrDefault ["limitFail", -1]; + if (_maxTargetLosses < 0) then { _maxTargetLosses = count _targets; }; + + _self set ["targets", _targets]; + _self set ["requiredKills", _requiredKills]; + _self set ["maxTargetLosses", _maxTargetLosses]; + _self set ["timeLimit", _taskParams getOrDefault ["timeLimit", 0]]; + + // Review-only registry entry to demonstrate where #delete is useful. + missionNamespace setVariable [_taskID, _self]; + }], + ["#delete", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + if (_taskID isNotEqualTo "") then { + missionNamespace setVariable [_taskID, nil]; + }; + }], + ["countKilledTargets", compileFinal { + private _targets = _self getOrDefault ["targets", []]; + { !alive _x } count _targets + }], + ["tick", compileFinal { + private _startedAt = _self getOrDefault ["startedAt", -1]; + private _timeLimit = _self getOrDefault ["timeLimit", 0]; + private _targetsKilled = _self call ["countKilledTargets", []]; + private _requiredKills = _self getOrDefault ["requiredKills", 0]; + private _maxTargetLosses = _self getOrDefault ["maxTargetLosses", 0]; + private _timeExpired = false; + + if (_timeLimit > 0 && { _startedAt >= 0 }) then { + _timeExpired = (serverTime - _startedAt) >= _timeLimit; + }; + + createHashMapFromArray [ + ["targetsKilled", _targetsKilled], + ["requiredKills", _requiredKills], + ["maxTargetLosses", _maxTargetLosses], + ["timeExpired", _timeExpired], + ["shouldFail", _timeExpired && { _targetsKilled < _requiredKills }], + ["shouldSucceed", _targetsKilled >= _requiredKills] + ] + }], + ["runLoop", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + private _targets = _self getOrDefault ["targets", []]; + private _timeLimit = _self getOrDefault ["timeLimit", 0]; + private _rewardData = _self getOrDefault ["rewardData", createHashMap]; + private _ratingFail = _rewardData getOrDefault ["ratingFail", 0]; + private _ratingSuccess = _rewardData getOrDefault ["ratingSuccess", 0]; + private _funds = _rewardData getOrDefault ["funds", 0]; + private _endFail = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endFail", false]; + private _endSuccess = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endSuccess", false]; + + waitUntil { + sleep 1; + GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]]; + count _targets > 0 + }; + + if (_timeLimit isNotEqualTo 0) then { + waitUntil { + sleep 1; + GVAR(TaskStore) call ["isTaskAccepted", [_taskID]] + }; + }; + + _self call ["markActive", []]; + + while { (_self call ["getStatus", []]) isEqualTo "active" } do { + GVAR(TaskStore) call ["trackParticipants", [_taskID, _targets, "", 300]]; + + private _snapshot = _self call ["tick", []]; + + if (_snapshot getOrDefault ["shouldFail", false]) exitWith { + _self call ["markFailed", ["Attack fail conditions met.", _snapshot]]; + }; + + if (_snapshot getOrDefault ["shouldSucceed", false]) exitWith { + _self call ["markSucceeded", [_snapshot]]; + }; + + sleep 1; + }; + + if ((_self call ["getStatus", []]) isEqualTo "failed") then { + { deleteVehicle _x } forEach _targets; + + [_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]]; + GVAR(TaskStore) call ["clearTask", [_taskID]]; + + if (_endFail) then { ["MissionFail", false] remoteExec ["BIS_fnc_endMission", playerSide]; }; + } else { + { deleteVehicle _x } forEach _targets; + + [_taskID, _rewardData] 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, [_funds] call EFUNC(common,formatNumber)]]]; + GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]]; + GVAR(TaskStore) call ["clearTask", [_taskID]]; + + if (_endSuccess) then { ["MissionSuccess", true] remoteExec ["BIS_fnc_endMission", playerSide]; }; + }; + + true + }] +]; diff --git a/arma/server/addons/task/prototypes/DefuseTaskBaseClass.sqf b/arma/server/addons/task/prototypes/DefuseTaskBaseClass.sqf new file mode 100644 index 0000000..ea89bf3 --- /dev/null +++ b/arma/server/addons/task/prototypes/DefuseTaskBaseClass.sqf @@ -0,0 +1,119 @@ +#include "..\script_component.hpp" + +/* + * Review-only prototype defuse task class. + * + * Example: + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\TaskInstanceBaseClass.sqf"; + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\DefuseTaskBaseClass.sqf"; + * + * private _task = createHashMapObject [ + * GVAR(DefuseTaskBaseClass), + * [ + * "task_defuse_review", + * createHashMapFromArray [ + * ["ieds", [ied1, ied2]], + * ["protected", [truck1]] + * ], + * createHashMapFromArray [ + * ["limitSuccess", 2], + * ["limitFail", 1], + * ["iedTimer", 300], + * ["funds", 75000], + * ["ratingSuccess", 30] + * ] + * ] + * ]; + * + * [_task] spawn { + * params ["_task"]; + * _task call ["runLoop", []]; + * }; + * + * Note: + * `runLoop` uses `sleep`, so it must be entered from scheduled code. + */ + +#pragma hemtt ignore_variables ["_self"] +GVAR(DefuseTaskBaseClass) = createHashMapFromArray [ + ["#base", GVAR(TaskInstanceBaseClass)], + ["#type", "DefuseTaskBaseClass"], + ["#create", compileFinal { + params [ + ["_taskID", "", [""]], + ["_entities", createHashMap, [createHashMap]], + ["_taskParams", createHashMap, [createHashMap]] + ]; + + _self call ["initializeBaseState", [_taskID, "defuse", _entities, _taskParams]]; + + private _ieds = +(_entities getOrDefault ["ieds", []]); + private _protected = +(_entities getOrDefault ["protected", []]); + private _requiredDefusals = _taskParams getOrDefault ["limitSuccess", -1]; + if (_requiredDefusals < 0) then { _requiredDefusals = count _ieds; }; + + private _maxProtectedLosses = _taskParams getOrDefault ["limitFail", -1]; + if (_maxProtectedLosses < 0) then { _maxProtectedLosses = count _protected; }; + + _self set ["ieds", _ieds]; + _self set ["protected", _protected]; + _self set ["requiredDefusals", _requiredDefusals]; + _self set ["maxProtectedLosses", _maxProtectedLosses]; + _self set ["iedTimer", _taskParams getOrDefault ["iedTimer", 300]]; + + // Review-only registry entry to demonstrate where #delete is useful. + missionNamespace setVariable [_taskID, _self]; + }], + ["#delete", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + if (_taskID isNotEqualTo "") then { + missionNamespace setVariable [_taskID, nil]; + }; + }], + ["countProtectedDestroyed", compileFinal { + private _protected = _self getOrDefault ["protected", []]; + { !alive _x } count _protected + }], + ["getDefuseCount", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + if (_taskID isEqualTo "") exitWith { 0 }; + + GVAR(TaskStore) call ["getDefuseCount", [_taskID]] + }], + ["tick", compileFinal { + private _defusedCount = _self call ["getDefuseCount", []]; + private _protectedDestroyed = _self call ["countProtectedDestroyed", []]; + private _requiredDefusals = _self getOrDefault ["requiredDefusals", 0]; + private _maxProtectedLosses = _self getOrDefault ["maxProtectedLosses", 0]; + + createHashMapFromArray [ + ["defusedCount", _defusedCount], + ["protectedDestroyed", _protectedDestroyed], + ["requiredDefusals", _requiredDefusals], + ["maxProtectedLosses", _maxProtectedLosses], + ["shouldFail", (_protectedDestroyed >= _maxProtectedLosses) && { _maxProtectedLosses > 0 }], + ["shouldSucceed", (_defusedCount >= _requiredDefusals) && { _requiredDefusals > 0 } && { _protectedDestroyed < _maxProtectedLosses || { _maxProtectedLosses <= 0 } }] + ] + }], + ["runLoop", compileFinal { + _self call ["markActive", []]; + + while { (_self call ["getStatus", []]) isEqualTo "active" } do { + private _snapshot = _self call ["tick", []]; + + if (_snapshot getOrDefault ["shouldFail", false]) exitWith { + _self call ["markFailed", ["Defuse fail conditions met.", _snapshot]]; + }; + + if (_snapshot getOrDefault ["shouldSucceed", false]) exitWith { + _self call ["markSucceeded", [_snapshot]]; + }; + + sleep 1; + }; + + true + }] +]; diff --git a/arma/server/addons/task/prototypes/EntityControllerBaseClass.sqf b/arma/server/addons/task/prototypes/EntityControllerBaseClass.sqf new file mode 100644 index 0000000..ddb3efd --- /dev/null +++ b/arma/server/addons/task/prototypes/EntityControllerBaseClass.sqf @@ -0,0 +1,87 @@ +#include "..\script_component.hpp" + +/* + * Review-only prototype base class for object-based entity controllers. + * + * Example: + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\EntityControllerBaseClass.sqf"; + * + * private _controller = createHashMapObject [ + * GVAR(EntityControllerBaseClass), + * [ + * "task_review_001", + * hostage1, + * "custom", + * createHashMapFromArray [ + * ["radius", 2] + * ] + * ] + * ]; + */ + +#pragma hemtt ignore_variables ["_self"] +GVAR(EntityControllerBaseClass) = createHashMapFromArray [ + ["#type", "EntityControllerBaseClass"], + ["initializeControllerState", compileFinal { + params [ + ["_taskID", "", [""]], + ["_entity", objNull, [objNull]], + ["_controllerType", "custom", [""]], + ["_controllerParams", createHashMap, [createHashMap]] + ]; + + _self set ["taskID", _taskID]; + _self set ["entity", _entity]; + _self set ["controllerType", _controllerType]; + _self set ["controllerParams", _controllerParams]; + _self set ["status", "created"]; + _self set ["startedAt", -1]; + _self set ["finishedAt", -1]; + true + }], + ["#create", compileFinal { + private _taskID = ""; + private _entity = objNull; + private _controllerType = "custom"; + private _controllerParams = createHashMap; + + if (_this isEqualType [] && { count _this > 0 }) then { + _taskID = _this param [0, "", [""]]; + _entity = _this param [1, objNull, [objNull]]; + + if ((count _this > 2) && { (_this select 2) isEqualType "" }) then { + _controllerType = _this param [2, "custom", [""]]; + _controllerParams = _this param [3, createHashMap, [createHashMap]]; + } else { + _controllerParams = _this param [2, createHashMap, [createHashMap]]; + }; + }; + + _self call ["initializeControllerState", [_taskID, _entity, _controllerType, _controllerParams]]; + }], + ["getEntity", compileFinal { + _self getOrDefault ["entity", objNull] + }], + ["markActive", compileFinal { + _self set ["status", "active"]; + _self set ["startedAt", serverTime]; + true + }], + ["markFinished", compileFinal { + _self set ["status", "finished"]; + _self set ["finishedAt", serverTime]; + true + }], + ["markAborted", compileFinal { + _self set ["status", "aborted"]; + _self set ["finishedAt", serverTime]; + true + }], + ["cleanup", compileFinal { + false + }], + ["runLoop", compileFinal { + false + }] +]; diff --git a/arma/server/addons/task/prototypes/HostageEntityController.sqf b/arma/server/addons/task/prototypes/HostageEntityController.sqf new file mode 100644 index 0000000..a538e43 --- /dev/null +++ b/arma/server/addons/task/prototypes/HostageEntityController.sqf @@ -0,0 +1,127 @@ +#include "..\script_component.hpp" + +/* + * Review-only prototype hostage entity controller. + * + * Example: + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\EntityControllerBaseClass.sqf"; + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\HostageEntityController.sqf"; + * + * private _controller = createHashMapObject [ + * GVAR(HostageEntityController), + * [ + * "task_hostage_review", + * hostage1, + * createHashMapFromArray [ + * ["rescueRadius", 2], + * ["loopAnimation", "acts_executionvictim_loop"], + * ["rescueAnimation", "acts_executionvictim_unbow"] + * ] + * ] + * ]; + * + * [_controller] spawn { + * params ["_controller"]; + * _controller call ["runLoop", []]; + * }; + * + * Note: + * `runLoop` uses `sleep`, so it must be entered from scheduled code. + */ + +#pragma hemtt ignore_variables ["_self"] +GVAR(HostageEntityController) = createHashMapFromArray [ + ["#base", GVAR(EntityControllerBaseClass)], + ["#type", "HostageEntityController"], + ["#create", compileFinal { + params [ + ["_taskID", "", [""]], + ["_entity", objNull, [objNull]], + ["_controllerParams", createHashMap, [createHashMap]] + ]; + + _self call ["initializeControllerState", [_taskID, _entity, "hostage", _controllerParams]]; + _self set ["rescueRadius", _controllerParams getOrDefault ["rescueRadius", 2]]; + _self set ["loopAnimation", _controllerParams getOrDefault ["loopAnimation", "acts_executionvictim_loop"]]; + _self set ["rescueAnimation", _controllerParams getOrDefault ["rescueAnimation", "acts_executionvictim_unbow"]]; + + private _netID = if (isNull _entity) then { "" } else { netId _entity }; + if (_netID isNotEqualTo "") then { + private _controllerKey = format ["hostage_controller_%1", _netID]; + missionNamespace setVariable [_controllerKey, _self]; + }; + }], + ["#delete", compileFinal { + private _entity = _self getOrDefault ["entity", objNull]; + if (!isNull _entity) then { + private _controllerKey = format ["hostage_controller_%1", netId _entity]; + missionNamespace setVariable [_controllerKey, nil]; + }; + }], + ["applyInitialState", compileFinal { + private _entity = _self getOrDefault ["entity", objNull]; + if (isNull _entity || { !alive _entity }) exitWith { false }; + + _entity setCaptive true; + _entity enableAIFeature ["MOVE", false]; + _entity playMove (_self getOrDefault ["loopAnimation", "acts_executionvictim_loop"]); + true + }], + ["findNearbyRescuer", compileFinal { + private _entity = _self getOrDefault ["entity", objNull]; + if (isNull _entity || { !alive _entity }) exitWith { objNull }; + + private _radius = _self getOrDefault ["rescueRadius", 2]; + private _nearPlayers = allPlayers inAreaArray [ASLToAGL getPosASL _entity, _radius, _radius, 0, false, 2]; + if (_nearPlayers isEqualTo []) exitWith { objNull }; + + _nearPlayers select 0 + }], + ["transitionToRescued", compileFinal { + params [["_rescuer", objNull, [objNull]]]; + + private _entity = _self getOrDefault ["entity", objNull]; + if (isNull _entity || { isNull _rescuer }) exitWith { false }; + + [_entity] joinSilent (group _rescuer); + _entity setCaptive true; + _entity enableAIFeature ["MOVE", true]; + _entity playMove (_self getOrDefault ["rescueAnimation", "acts_executionvictim_unbow"]); + true + }], + ["runLoop", compileFinal { + private _entity = _self getOrDefault ["entity", objNull]; + if (isNull _entity) exitWith { + _self call ["markAborted", []]; + false + }; + + _self call ["markActive", []]; + + if !(_self call ["applyInitialState", []]) exitWith { + _self call ["markAborted", []]; + false + }; + + private _rescuer = objNull; + waitUntil { + sleep 1; + + if (isNull _entity || { !alive _entity }) exitWith { true }; + + _rescuer = _self call ["findNearbyRescuer", []]; + !isNull _rescuer + }; + + if (isNull _entity || { !alive _entity }) exitWith { + _self call ["markAborted", []]; + false + }; + + _self call ["transitionToRescued", [_rescuer]]; + _self call ["markFinished", []]; + true + }] +]; diff --git a/arma/server/addons/task/prototypes/HostageTaskBaseClass.sqf b/arma/server/addons/task/prototypes/HostageTaskBaseClass.sqf new file mode 100644 index 0000000..2d9aa4a --- /dev/null +++ b/arma/server/addons/task/prototypes/HostageTaskBaseClass.sqf @@ -0,0 +1,339 @@ +#include "..\script_component.hpp" + +/* + * Review-only prototype hostage task class. + * + * Example: + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\TaskInstanceBaseClass.sqf"; + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\HostageTaskBaseClass.sqf"; + * + * private _task = createHashMapObject [ + * GVAR(HostageTaskBaseClass), + * [ + * "task_hostage_review", + * createHashMapFromArray [ + * ["hostages", [hostage1, hostage2]], + * ["shooters", [shooter1, shooter2]] + * ], + * createHashMapFromArray [ + * ["extractionZone", "hostage_extract"], + * ["limitSuccess", 2], + * ["limitFail", 1], + * ["execution", true], + * ["timeLimit", 900], + * ["funds", 100000], + * ["ratingSuccess", 50] + * ] + * ] + * ]; + * + * [_task] spawn { + * params ["_task"]; + * _task call ["runLoop", []]; + * }; + * + * Note: + * `runLoop` and the wait helpers use `sleep`, so they must be entered from + * scheduled code. + */ + +#pragma hemtt ignore_variables ["_self"] +GVAR(HostageTaskBaseClass) = createHashMapFromArray [ + ["#base", GVAR(TaskInstanceBaseClass)], + ["#type", "HostageTaskBaseClass"], + ["#create", compileFinal { + params [ + ["_taskID", "", [""]], + ["_entities", createHashMap, [createHashMap]], + ["_taskParams", createHashMap, [createHashMap]] + ]; + + _self call ["initializeBaseState", [_taskID, "hostage", _entities, _taskParams]]; + + private _hostages = +(_entities getOrDefault ["hostages", []]); + private _shooters = +(_entities getOrDefault ["shooters", []]); + private _requiredRescues = _taskParams getOrDefault ["limitSuccess", -1]; + if (_requiredRescues < 0) then { _requiredRescues = count _hostages; }; + + private _maxHostageLosses = _taskParams getOrDefault ["limitFail", -1]; + if (_maxHostageLosses < 0) then { _maxHostageLosses = count _hostages; }; + + _self set ["hostages", _hostages]; + _self set ["shooters", _shooters]; + _self set ["extractionZone", _taskParams getOrDefault ["extractionZone", ""]]; + _self set ["timeLimit", _taskParams getOrDefault ["timeLimit", 0]]; + _self set ["execution", _taskParams getOrDefault ["execution", false]]; + _self set ["cbrn", _taskParams getOrDefault ["cbrn", false]]; + _self set ["cbrnZone", _taskParams getOrDefault ["cbrnZone", ""]]; + _self set ["requiredRescues", _requiredRescues]; + _self set ["maxHostageLosses", _maxHostageLosses]; + _self set ["hostageControllers", []]; + + _self call ["createHostageControllers", []]; + + // Review-only registry entry to demonstrate where #delete is useful. + missionNamespace setVariable [_taskID, _self]; + }], + ["#delete", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + if (_taskID isNotEqualTo "") then { + missionNamespace setVariable [_taskID, nil]; + }; + }], + ["createHostageControllers", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + private _hostages = _self getOrDefault ["hostages", []]; + private _controllers = []; + + { + _controllers pushBack (createHashMapObject [ + GVAR(HostageEntityController), + [ + _taskID, + _x, + createHashMapFromArray [ + ["rescueRadius", 2], + ["loopAnimation", "acts_executionvictim_loop"], + ["rescueAnimation", "acts_executionvictim_unbow"] + ] + ] + ]); + } forEach _hostages; + + _self set ["hostageControllers", _controllers]; + _controllers + }], + ["startHostageControllers", compileFinal { + private _controllers = _self getOrDefault ["hostageControllers", []]; + + { + [_x] spawn { + params ["_controller"]; + _controller call ["runLoop", []]; + }; + } forEach _controllers; + + true + }], + ["refreshEntitiesFromStore", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + if (_taskID isEqualTo "") exitWith { false }; + + private _hostages = GVAR(TaskStore) call ["getTaskEntities", ["hostages", _taskID]]; + private _shooters = GVAR(TaskStore) call ["getTaskEntities", ["shooters", _taskID]]; + + _self set ["hostages", _hostages]; + _self set ["shooters", _shooters]; + true + }], + ["trackParticipants", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + if (_taskID isEqualTo "") exitWith { false }; + + private _hostages = _self getOrDefault ["hostages", []]; + private _shooters = _self getOrDefault ["shooters", []]; + private _extZone = _self getOrDefault ["extractionZone", ""]; + + GVAR(TaskStore) call ["trackParticipants", [_taskID, _hostages + _shooters, _extZone, 250]]; + true + }], + ["waitForRequiredEntities", compileFinal { + waitUntil { + sleep 1; + _self call ["refreshEntitiesFromStore", []]; + count (_self getOrDefault ["hostages", []]) > 0 + }; + + waitUntil { + sleep 1; + _self call ["refreshEntitiesFromStore", []]; + _self call ["trackParticipants", []]; + count (_self getOrDefault ["shooters", []]) > 0 + }; + + private _hostages = _self getOrDefault ["hostages", []]; + private _taskParams = _self getOrDefault ["taskParams", createHashMap]; + private _requiredRescues = _taskParams getOrDefault ["limitSuccess", -1]; + if (_requiredRescues < 0) then { _requiredRescues = count _hostages; }; + + private _maxHostageLosses = _taskParams getOrDefault ["limitFail", -1]; + if (_maxHostageLosses < 0) then { _maxHostageLosses = count _hostages; }; + + _self set ["requiredRescues", _requiredRescues]; + _self set ["maxHostageLosses", _maxHostageLosses]; + true + }], + ["waitForAssignmentIfTimed", compileFinal { + private _timeLimit = _self getOrDefault ["timeLimit", 0]; + private _taskID = _self getOrDefault ["taskID", ""]; + + if (_timeLimit <= 0 || { _taskID isEqualTo "" }) exitWith { true }; + + waitUntil { + sleep 1; + GVAR(TaskStore) call ["isTaskAccepted", [_taskID]] + }; + + true + }], + ["countFreedHostages", compileFinal { + private _playerGroups = allPlayers apply { group _x }; + private _hostages = _self getOrDefault ["hostages", []]; + + { + alive _x && { ((group _x) in _playerGroups) || { !captive _x } } + } count _hostages + }], + ["countHostagesInZone", compileFinal { + private _extZone = _self getOrDefault ["extractionZone", ""]; + private _hostages = _self getOrDefault ["hostages", []]; + + if (_extZone isEqualTo "") exitWith { 0 }; + { _x inArea _extZone } count _hostages + }], + ["countKilledHostages", compileFinal { + private _hostages = _self getOrDefault ["hostages", []]; + { !alive _x } count _hostages + }], + ["countAliveShooters", compileFinal { + private _shooters = _self getOrDefault ["shooters", []]; + { alive _x } count _shooters + }], + ["tick", compileFinal { + private _startedAt = _self getOrDefault ["startedAt", -1]; + private _timeLimit = _self getOrDefault ["timeLimit", 0]; + private _killed = _self call ["countKilledHostages", []]; + private _freed = _self call ["countFreedHostages", []]; + private _inZone = _self call ["countHostagesInZone", []]; + private _shootersAlive = _self call ["countAliveShooters", []]; + private _requiredRescues = _self getOrDefault ["requiredRescues", 0]; + private _maxHostageLosses = _self getOrDefault ["maxHostageLosses", 0]; + private _timeExpired = false; + + if (_timeLimit > 0 && { _startedAt >= 0 }) then { + _timeExpired = (serverTime - _startedAt) >= _timeLimit; + }; + + createHashMapFromArray [ + ["freed", _freed], + ["inZone", _inZone], + ["killed", _killed], + ["shootersAlive", _shootersAlive], + ["requiredRescues", _requiredRescues], + ["maxHostageLosses", _maxHostageLosses], + ["timeExpired", _timeExpired], + ["shouldFail", (_killed >= _maxHostageLosses) || { _timeExpired && { _freed < _requiredRescues } }], + ["shouldSucceed", ((_inZone >= _requiredRescues) && { _killed < _maxHostageLosses }) || { (_shootersAlive <= 0) && { _inZone >= _requiredRescues } && { _killed < _maxHostageLosses } }] + ] + }], + ["handleFailureOutcome", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + private _hostages = _self getOrDefault ["hostages", []]; + private _shooters = _self getOrDefault ["shooters", []]; + private _rewardData = _self getOrDefault ["rewardData", createHashMap]; + private _ratingFail = _rewardData getOrDefault ["ratingFail", 0]; + private _endFail = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endFail", false]; + private _cbrn = _self getOrDefault ["cbrn", false]; + private _hostage = _self getOrDefault ["execution", false]; + private _cbrnZone = _self getOrDefault ["cbrnZone", ""]; + + if (_cbrn && { _cbrnZone isNotEqualTo "" }) then { + "SmokeShellYellow" createVehicle getMarkerPos _cbrnZone; + + sleep 5; + + { + if (captive _x) then { + _x setDamage 0.9; + _x playMove "acts_executionvictim_kill_end"; + + sleep 2.75; + + _x setDamage 1; + }; + } forEach _hostages; + }; + + if (_hostage) then { + { + _x enableAIFeature ["MOVE", true]; + _x playMove ""; + } forEach _shooters; + + sleep 1; + + { _x setCaptive false; } forEach _hostages; + + sleep 5; + }; + + { deleteVehicle _x } forEach _hostages; + { deleteVehicle _x } forEach _shooters; + + [_taskID, "FAILED"] call BFUNC(taskSetState); + + sleep 1; + + GVAR(TaskStore) call ["notifyParticipants", [_taskID, "warning", "Tasks", format ["Task failed: %1 reputation", _ratingFail]]]; + GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingFail]]; + GVAR(TaskStore) call ["clearTask", [_taskID]]; + + if (_endFail) then { ["MissionFail", false] remoteExecCall ["BIS_fnc_endMission", playerSide]; }; + true + }], + ["handleSuccessOutcome", compileFinal { + private _taskID = _self getOrDefault ["taskID", ""]; + private _hostages = _self getOrDefault ["hostages", []]; + private _shooters = _self getOrDefault ["shooters", []]; + private _rewardData = _self getOrDefault ["rewardData", createHashMap]; + private _ratingSuccess = _rewardData getOrDefault ["ratingSuccess", 0]; + private _funds = _rewardData getOrDefault ["funds", 0]; + private _endSuccess = (_self getOrDefault ["taskParams", createHashMap]) getOrDefault ["endSuccess", false]; + + { deleteVehicle _x } forEach _hostages; + { deleteVehicle _x } forEach _shooters; + + [_taskID, _rewardData] call FUNC(handleTaskRewards); + [_taskID, "SUCCEEDED"] call BFUNC(taskSetState); + + sleep 1; + + GVAR(TaskStore) call ["notifyParticipants", [_taskID, "success", "Tasks", format ["Task completed: %1 reputation, $%2 funds", _ratingSuccess, [_funds] call EFUNC(common,formatNumber)]]]; + GVAR(TaskStore) call ["applyRatingOutcome", [_taskID, _ratingSuccess]]; + GVAR(TaskStore) call ["clearTask", [_taskID]]; + + if (_endSuccess) then { ["MissionSuccess", true] remoteExecCall ["BIS_fnc_endMission", playerSide]; }; + true + }], + ["runLoop", compileFinal { + _self call ["waitForRequiredEntities", []]; + _self call ["startHostageControllers", []]; + _self call ["waitForAssignmentIfTimed", []]; + _self call ["markActive", []]; + + while { (_self call ["getStatus", []]) isEqualTo "active" } do { + _self call ["trackParticipants", []]; + private _snapshot = _self call ["tick", []]; + + if (_snapshot getOrDefault ["shouldFail", false]) exitWith { + _self call ["markFailed", ["Hostage fail conditions met.", _snapshot]]; + }; + + if (_snapshot getOrDefault ["shouldSucceed", false]) exitWith { + _self call ["markSucceeded", [_snapshot]]; + }; + + sleep 1; + }; + + if ((_self call ["getStatus", []]) isEqualTo "failed") then { + _self call ["handleFailureOutcome", []]; + } else { + _self call ["handleSuccessOutcome", []]; + }; + + true + }] +]; diff --git a/arma/server/addons/task/prototypes/README.md b/arma/server/addons/task/prototypes/README.md index 1bb039a..98a2d0a 100644 --- a/arma/server/addons/task/prototypes/README.md +++ b/arma/server/addons/task/prototypes/README.md @@ -5,21 +5,37 @@ instances. They are not part of runtime initialization. Current prototypes: - `TaskInstanceBaseClass` +- `EntityControllerBaseClass` +- `AttackTaskBaseClass` - `HostageTaskBaseClass` +- `HostageEntityController` - `DefuseTaskBaseClass` Source: - [taskObjectPrototypes.sqf](./taskObjectPrototypes.sqf) +- [TaskInstanceBaseClass.sqf](./TaskInstanceBaseClass.sqf) +- [EntityControllerBaseClass.sqf](./EntityControllerBaseClass.sqf) +- [AttackTaskBaseClass.sqf](./AttackTaskBaseClass.sqf) +- [HostageTaskBaseClass.sqf](./HostageTaskBaseClass.sqf) +- [HostageEntityController.sqf](./HostageEntityController.sqf) +- [DefuseTaskBaseClass.sqf](./DefuseTaskBaseClass.sqf) Purpose: - show what per-task instance objects could look like +- show what per-entity heartbeat/controller objects could look like - separate state ownership from the current long procedural functions - avoid committing the live addon to a large refactor before the model is reviewed +- keep shared lifecycle and reward initialization in `TaskInstanceBaseClass` + so concrete task prototypes only define task-specific state +- keep heartbeat-style AI/object behavior in separate entity controllers instead + of mixing it into task outcome loops Important design choice: - these prototypes use explicit `markSucceeded`, `markFailed`, and `cleanup` methods instead of relying on `#delete` +- task loops that use `sleep` or `waitUntil` with `sleep` must be started from + scheduled code, typically via `spawn` That is intentional. `createHashMapObject` destructor timing is reference-based, so `#delete` is not a good primitive for mission-critical task completion or diff --git a/arma/server/addons/task/prototypes/TaskInstanceBaseClass.sqf b/arma/server/addons/task/prototypes/TaskInstanceBaseClass.sqf new file mode 100644 index 0000000..a0b084d --- /dev/null +++ b/arma/server/addons/task/prototypes/TaskInstanceBaseClass.sqf @@ -0,0 +1,120 @@ +#include "..\script_component.hpp" + +/* + * Review-only prototype base class for object-based task instances. + * + * Example: + * call compile preprocessFileLineNumbers + * "\forge\forge_server\addons\task\prototypes\TaskInstanceBaseClass.sqf"; + * + * private _task = createHashMapObject [ + * GVAR(TaskInstanceBaseClass), + * [ + * "task_review_001", + * "custom", + * createHashMap, + * createHashMapFromArray [ + * ["funds", 50000], + * ["ratingSuccess", 25] + * ] + * ] + * ]; + */ + +#pragma hemtt ignore_variables ["_self"] +GVAR(TaskInstanceBaseClass) = createHashMapFromArray [ + ["#type", "TaskInstanceBaseClass"], + ["initializeBaseState", compileFinal { + params [ + ["_taskID", "", [""]], + ["_taskType", "custom", [""]], + ["_entities", createHashMap, [createHashMap]], + ["_taskParams", createHashMap, [createHashMap]] + ]; + + _self set ["taskID", _taskID]; + _self set ["taskType", _taskType]; + _self set ["entities", _entities]; + _self set ["taskParams", _taskParams]; + _self set ["status", "created"]; + _self set ["startedAt", -1]; + _self set ["finishedAt", -1]; + _self set ["failureReason", ""]; + _self set ["resultSnapshot", createHashMap]; + _self set ["rewardData", createHashMapFromArray [ + ["funds", _taskParams getOrDefault ["funds", 0]], + ["ratingFail", _taskParams getOrDefault ["ratingFail", 0]], + ["ratingSuccess", _taskParams getOrDefault ["ratingSuccess", 0]], + ["equipment", _taskParams getOrDefault ["equipment", []]], + ["supplies", _taskParams getOrDefault ["supplies", []]], + ["weapons", _taskParams getOrDefault ["weapons", []]], + ["vehicles", _taskParams getOrDefault ["vehicles", []]], + ["special", _taskParams getOrDefault ["special", []]] + ]]; + true + }], + ["#create", compileFinal { + private _taskID = ""; + private _taskType = "custom"; + private _entities = createHashMap; + private _taskParams = createHashMap; + + if (_this isEqualType [] && { count _this > 0 }) then { + _taskID = _this param [0, "", [""]]; + + if ((count _this > 1) && { (_this select 1) isEqualType "" }) then { + _taskType = _this param [1, "custom", [""]]; + _entities = _this param [2, createHashMap, [createHashMap]]; + _taskParams = _this param [3, createHashMap, [createHashMap]]; + } else { + _entities = _this param [1, createHashMap, [createHashMap]]; + _taskParams = _this param [2, createHashMap, [createHashMap]]; + }; + }; + + _self call ["initializeBaseState", [_taskID, _taskType, _entities, _taskParams]]; + }], + ["getTaskID", compileFinal { + _self getOrDefault ["taskID", ""] + }], + ["getTaskType", compileFinal { + _self getOrDefault ["taskType", ""] + }], + ["getStatus", compileFinal { + _self getOrDefault ["status", "created"] + }], + ["getRewardData", compileFinal { + _self getOrDefault ["rewardData", createHashMap] + }], + ["markActive", compileFinal { + _self set ["status", "active"]; + _self set ["startedAt", serverTime]; + true + }], + ["markSucceeded", compileFinal { + params [["_resultSnapshot", createHashMap, [createHashMap]]]; + + _self set ["status", "succeeded"]; + _self set ["finishedAt", serverTime]; + _self set ["resultSnapshot", _resultSnapshot]; + true + }], + ["markFailed", compileFinal { + params [["_reason", "", [""]], ["_resultSnapshot", createHashMap, [createHashMap]]]; + + _self set ["status", "failed"]; + _self set ["finishedAt", serverTime]; + _self set ["failureReason", _reason]; + _self set ["resultSnapshot", _resultSnapshot]; + true + }], + ["cleanup", compileFinal { + false + }], + ["tick", compileFinal { + createHashMap + }], + ["runLoop", compileFinal { + false + }] +]; diff --git a/arma/server/addons/task/prototypes/taskObjectPrototypes.sqf b/arma/server/addons/task/prototypes/taskObjectPrototypes.sqf index f87bc57..bb834ba 100644 --- a/arma/server/addons/task/prototypes/taskObjectPrototypes.sqf +++ b/arma/server/addons/task/prototypes/taskObjectPrototypes.sqf @@ -1,7 +1,7 @@ #include "..\script_component.hpp" /* - * Review-only prototype for object-based task instances. + * Review-only prototype loader for object-based task instances. * * This file is intentionally not referenced by XEH_PREP or runtime init. * It exists so the current procedural task flows can be compared against @@ -30,261 +30,18 @@ * ]; */ -#pragma hemtt ignore_variables ["_self"] - -GVAR(TaskInstanceBaseClass) = createHashMapFromArray [ - ["#type", "TaskInstanceBaseClass"], - ["#create", compileFinal { - params [ - ["_taskID", "", [""]], - ["_taskType", "", [""]], - ["_entities", createHashMap, [createHashMap]], - ["_taskParams", createHashMap, [createHashMap]] - ]; - - _self set ["taskID", _taskID]; - _self set ["taskType", _taskType]; - _self set ["entities", _entities]; - _self set ["taskParams", _taskParams]; - _self set ["status", "created"]; - _self set ["startedAt", -1]; - _self set ["finishedAt", -1]; - _self set ["failureReason", ""]; - _self set ["outcomeData", createHashMap]; - }], - ["getTaskID", compileFinal { - _self getOrDefault ["taskID", ""] - }], - ["getTaskType", compileFinal { - _self getOrDefault ["taskType", ""] - }], - ["getStatus", compileFinal { - _self getOrDefault ["status", "created"] - }], - ["markActive", compileFinal { - _self set ["status", "active"]; - _self set ["startedAt", serverTime]; - true - }], - ["markSucceeded", compileFinal { - params [["_outcomeData", createHashMap, [createHashMap]]]; - - _self set ["status", "succeeded"]; - _self set ["finishedAt", serverTime]; - _self set ["outcomeData", _outcomeData]; - true - }], - ["markFailed", compileFinal { - params [["_reason", "", [""]], ["_outcomeData", createHashMap, [createHashMap]]]; - - _self set ["status", "failed"]; - _self set ["finishedAt", serverTime]; - _self set ["failureReason", _reason]; - _self set ["outcomeData", _outcomeData]; - true - }], - ["cleanup", compileFinal { - false - }], - ["tick", compileFinal { - createHashMap - }], - ["runLoop", compileFinal { - false - }] -]; - -GVAR(HostageTaskBaseClass) = createHashMapFromArray [ - ["#base", GVAR(TaskInstanceBaseClass)], - ["#type", "HostageTaskBaseClass"], - ["#create", compileFinal { - params [ - ["_taskID", "", [""]], - ["_entities", createHashMap, [createHashMap]], - ["_taskParams", createHashMap, [createHashMap]] - ]; - - _self set ["taskID", _taskID]; - _self set ["taskType", "hostage"]; - _self set ["entities", _entities]; - _self set ["taskParams", _taskParams]; - _self set ["status", "created"]; - _self set ["startedAt", -1]; - _self set ["finishedAt", -1]; - _self set ["failureReason", ""]; - _self set ["outcomeData", createHashMap]; - - private _hostages = +(_entities getOrDefault ["hostages", []]); - private _shooters = +(_entities getOrDefault ["shooters", []]); - private _requiredRescues = _taskParams getOrDefault ["limitSuccess", -1]; - if (_requiredRescues < 0) then { _requiredRescues = count _hostages; }; - - private _maxHostageLosses = _taskParams getOrDefault ["limitFail", -1]; - if (_maxHostageLosses < 0) then { _maxHostageLosses = count _hostages; }; - - _self set ["hostages", _hostages]; - _self set ["shooters", _shooters]; - _self set ["extractionZone", _taskParams getOrDefault ["extractionZone", ""]]; - _self set ["timeLimit", _taskParams getOrDefault ["timeLimit", 0]]; - _self set ["execution", _taskParams getOrDefault ["execution", false]]; - _self set ["cbrn", _taskParams getOrDefault ["cbrn", false]]; - _self set ["cbrnZone", _taskParams getOrDefault ["cbrnZone", ""]]; - _self set ["requiredRescues", _requiredRescues]; - _self set ["maxHostageLosses", _maxHostageLosses]; - }], - ["countFreedHostages", compileFinal { - private _playerGroups = allPlayers apply { group _x }; - private _hostages = _self getOrDefault ["hostages", []]; - - { - alive _x && { ((group _x) in _playerGroups) || { !captive _x } } - } count _hostages - }], - ["countHostagesInZone", compileFinal { - private _extZone = _self getOrDefault ["extractionZone", ""]; - private _hostages = _self getOrDefault ["hostages", []]; - - if (_extZone isEqualTo "") exitWith { 0 }; - { _x inArea _extZone } count _hostages - }], - ["countKilledHostages", compileFinal { - private _hostages = _self getOrDefault ["hostages", []]; - { !alive _x } count _hostages - }], - ["countAliveShooters", compileFinal { - private _shooters = _self getOrDefault ["shooters", []]; - { alive _x } count _shooters - }], - ["tick", compileFinal { - private _startedAt = _self getOrDefault ["startedAt", -1]; - private _timeLimit = _self getOrDefault ["timeLimit", 0]; - private _killed = _self call ["countKilledHostages", []]; - private _freed = _self call ["countFreedHostages", []]; - private _inZone = _self call ["countHostagesInZone", []]; - private _shootersAlive = _self call ["countAliveShooters", []]; - private _requiredRescues = _self getOrDefault ["requiredRescues", 0]; - private _maxHostageLosses = _self getOrDefault ["maxHostageLosses", 0]; - private _timeExpired = false; - - if (_timeLimit > 0 && { _startedAt >= 0 }) then { - _timeExpired = (serverTime - _startedAt) >= _timeLimit; - }; - - createHashMapFromArray [ - ["freed", _freed], - ["inZone", _inZone], - ["killed", _killed], - ["shootersAlive", _shootersAlive], - ["requiredRescues", _requiredRescues], - ["maxHostageLosses", _maxHostageLosses], - ["timeExpired", _timeExpired], - ["shouldFail", (_killed >= _maxHostageLosses) || { _timeExpired && { _freed < _requiredRescues } }], - ["shouldSucceed", (_inZone >= _requiredRescues) && { _killed < _maxHostageLosses }] - ] - }], - ["runLoop", compileFinal { - _self call ["markActive", []]; - - while { (_self call ["getStatus", []]) isEqualTo "active" } do { - private _snapshot = _self call ["tick", []]; - - if (_snapshot getOrDefault ["shouldFail", false]) exitWith { - _self call ["markFailed", ["Hostage fail conditions met.", _snapshot]]; - }; - - if (_snapshot getOrDefault ["shouldSucceed", false]) exitWith { - _self call ["markSucceeded", [_snapshot]]; - }; - - sleep 1; - }; - - true - }] -]; - -GVAR(DefuseTaskBaseClass) = createHashMapFromArray [ - ["#base", GVAR(TaskInstanceBaseClass)], - ["#type", "DefuseTaskBaseClass"], - ["#create", compileFinal { - params [ - ["_taskID", "", [""]], - ["_entities", createHashMap, [createHashMap]], - ["_taskParams", createHashMap, [createHashMap]] - ]; - - _self set ["taskID", _taskID]; - _self set ["taskType", "defuse"]; - _self set ["entities", _entities]; - _self set ["taskParams", _taskParams]; - _self set ["status", "created"]; - _self set ["startedAt", -1]; - _self set ["finishedAt", -1]; - _self set ["failureReason", ""]; - _self set ["outcomeData", createHashMap]; - - private _ieds = +(_entities getOrDefault ["ieds", []]); - private _protected = +(_entities getOrDefault ["protected", []]); - private _requiredDefusals = _taskParams getOrDefault ["limitSuccess", -1]; - if (_requiredDefusals < 0) then { _requiredDefusals = count _ieds; }; - - private _maxProtectedLosses = _taskParams getOrDefault ["limitFail", -1]; - if (_maxProtectedLosses < 0) then { _maxProtectedLosses = count _protected; }; - - _self set ["ieds", _ieds]; - _self set ["protected", _protected]; - _self set ["requiredDefusals", _requiredDefusals]; - _self set ["maxProtectedLosses", _maxProtectedLosses]; - _self set ["iedTimer", _taskParams getOrDefault ["iedTimer", 300]]; - }], - ["countProtectedDestroyed", compileFinal { - private _protected = _self getOrDefault ["protected", []]; - { !alive _x } count _protected - }], - ["getDefuseCount", compileFinal { - private _taskID = _self getOrDefault ["taskID", ""]; - if (_taskID isEqualTo "") exitWith { 0 }; - - GVAR(TaskStore) call ["getDefuseCount", [_taskID]] - }], - ["tick", compileFinal { - private _defusedCount = _self call ["getDefuseCount", []]; - private _protectedDestroyed = _self call ["countProtectedDestroyed", []]; - private _requiredDefusals = _self getOrDefault ["requiredDefusals", 0]; - private _maxProtectedLosses = _self getOrDefault ["maxProtectedLosses", 0]; - - createHashMapFromArray [ - ["defusedCount", _defusedCount], - ["protectedDestroyed", _protectedDestroyed], - ["requiredDefusals", _requiredDefusals], - ["maxProtectedLosses", _maxProtectedLosses], - ["shouldFail", (_protectedDestroyed >= _maxProtectedLosses) && { _maxProtectedLosses > 0 }], - ["shouldSucceed", (_defusedCount >= _requiredDefusals) && { _requiredDefusals > 0 } && { _protectedDestroyed < _maxProtectedLosses || { _maxProtectedLosses <= 0 } }] - ] - }], - ["runLoop", compileFinal { - _self call ["markActive", []]; - - while { (_self call ["getStatus", []]) isEqualTo "active" } do { - private _snapshot = _self call ["tick", []]; - - if (_snapshot getOrDefault ["shouldFail", false]) exitWith { - _self call ["markFailed", ["Defuse fail conditions met.", _snapshot]]; - }; - - if (_snapshot getOrDefault ["shouldSucceed", false]) exitWith { - _self call ["markSucceeded", [_snapshot]]; - }; - - sleep 1; - }; - - true - }] -]; +call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\TaskInstanceBaseClass.sqf"; +call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\EntityControllerBaseClass.sqf"; +call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\AttackTaskBaseClass.sqf"; +call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\HostageTaskBaseClass.sqf"; +call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\HostageEntityController.sqf"; +call compile preprocessFileLineNumbers "\forge\forge_server\addons\task\prototypes\DefuseTaskBaseClass.sqf"; createHashMapFromArray [ ["TaskInstanceBaseClass", GVAR(TaskInstanceBaseClass)], + ["EntityControllerBaseClass", GVAR(EntityControllerBaseClass)], + ["AttackTaskBaseClass", GVAR(AttackTaskBaseClass)], ["HostageTaskBaseClass", GVAR(HostageTaskBaseClass)], + ["HostageEntityController", GVAR(HostageEntityController)], ["DefuseTaskBaseClass", GVAR(DefuseTaskBaseClass)] ]