From 5576cc4746b74173fd779b9ad9f9eb07a6f35dde Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Fri, 17 Apr 2026 19:34:21 -0500 Subject: [PATCH 1/2] 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. --- arma/server/addons/task/CfgVehicles.hpp | 150 +++++++++++++ arma/server/addons/task/XEH_PREP.hpp | 2 + .../task/functions/fnc_attackModule.sqf | 53 +++-- .../addons/task/functions/fnc_cargoModule.sqf | 23 ++ .../task/functions/fnc_defendModule.sqf | 93 ++++---- .../task/functions/fnc_defuseModule.sqf | 70 +++--- .../task/functions/fnc_deliveryModule.sqf | 98 +++++---- .../task/functions/fnc_destroyModule.sqf | 53 +++-- .../task/functions/fnc_hostageModule.sqf | 96 ++++---- .../addons/task/functions/fnc_hvtModule.sqf | 69 +++--- .../task/functions/fnc_missionManager.sqf | 89 +++----- .../addons/task/functions/fnc_startTask.sqf | 206 ++++++++++++++++++ 12 files changed, 695 insertions(+), 307 deletions(-) create mode 100644 arma/server/addons/task/functions/fnc_cargoModule.sqf create mode 100644 arma/server/addons/task/functions/fnc_startTask.sqf diff --git a/arma/server/addons/task/CfgVehicles.hpp b/arma/server/addons/task/CfgVehicles.hpp index 06a1a39..f045710 100644 --- a/arma/server/addons/task/CfgVehicles.hpp +++ b/arma/server/addons/task/CfgVehicles.hpp @@ -638,6 +638,156 @@ class CfgVehicles { }; }; + class FORGE_Module_Delivery: Module_F { + scope = 2; + displayName = "Delivery Task"; + category = "FORGE_Modules"; + + function = QFUNC(deliveryModule); + functionPriority = 1; + isGlobal = 1; + isTriggerActivated = 1; + isDisposable = 1; + is3DEN = 0; + + canSetArea = 0; + canSetAreaShape = 0; + canSetAreaHeight = 0; + + class AttributeValues {}; + class Attributes: AttributesBase { + class TaskID: Edit { + property = "FORGE_Module_Delivery_TaskID"; + displayName = "Task ID"; + tooltip = "Unique identifier for this task"; + typeName = "STRING"; + }; + class DeliveryZone: Edit { + property = "FORGE_Module_Delivery_DeliveryZone"; + displayName = "Delivery Zone Marker"; + tooltip = "Name of the marker defining the delivery destination"; + typeName = "STRING"; + }; + class LimitFail: Edit { + property = "FORGE_Module_Delivery_LimitFail"; + displayName = "Fail Limit"; + tooltip = "Number of cargo items damaged or lost before failing"; + typeName = "NUMBER"; + defaultValue = 1; + }; + class LimitSuccess: Edit { + property = "FORGE_Module_Delivery_LimitSuccess"; + displayName = "Success Limit"; + tooltip = "Number of cargo items that must reach the delivery zone"; + typeName = "NUMBER"; + defaultValue = 1; + }; + class CompanyFunds: Edit { + property = "FORGE_Module_Delivery_CompanyFunds"; + displayName = "Reward Funds"; + tooltip = "Amount of funds awarded on success"; + typeName = "NUMBER"; + defaultValue = 0; + }; + class RatingFail: Edit { + property = "FORGE_Module_Delivery_RatingFail"; + displayName = "Rating Loss"; + tooltip = "Amount of rating lost on failure"; + typeName = "NUMBER"; + defaultValue = 0; + }; + class RatingSuccess: Edit { + property = "FORGE_Module_Delivery_RatingSuccess"; + displayName = "Rating Gain"; + tooltip = "Amount of rating gained on success"; + typeName = "NUMBER"; + defaultValue = 0; + }; + class EndSuccess: Combo { + property = "FORGE_Module_Delivery_EndSuccess"; + displayName = "End on Success"; + tooltip = "End mission when task is completed successfully"; + typeName = "BOOL"; + defaultValue = 0; + + class Values { + class EnableEndSuccess { name = "Enable"; value = 1; }; + class DisableEndSuccess { name = "Disable"; value = 0; }; + }; + }; + class EndFail: Combo { + property = "FORGE_Module_Delivery_EndFail"; + displayName = "End on Failure"; + tooltip = "End mission when task fails"; + typeName = "BOOL"; + defaultValue = 0; + + class Values { + class EnableEndFail { name = "Enable"; value = 1; }; + class DisableEndFail { name = "Disable"; value = 0; }; + }; + }; + class TimeLimit: Edit { + property = "FORGE_Module_Delivery_TimeLimit"; + displayName = "Time Limit"; + tooltip = "Seconds to complete delivery (-1 for no limit)"; + typeName = "NUMBER"; + defaultValue = -1; + }; + }; + + class ModuleDescription: ModuleDescription { + description = "Creates a delivery task. Sync with a FORGE_Module_Cargo to register cargo objects."; + sync[] = { "Anything" }; + + class Anything { + description[] = { + "Delivery task module", + "Sync with FORGE_Module_Cargo which groups the cargo objects" + }; + position = 1; + direction = 1; + optional = 1; + duplicate = 1; + }; + }; + }; + + class FORGE_Module_Cargo: Module_F { + scope = 2; + displayName = "Cargo Entities"; + category = "FORGE_Modules"; + + function = QFUNC(cargoModule); + functionPriority = 1; + isGlobal = 1; + isTriggerActivated = 0; + isDisposable = 1; + is3DEN = 0; + + canSetArea = 0; + canSetAreaShape = 0; + canSetAreaHeight = 0; + + class AttributeValues {}; + class Attributes: AttributesBase {}; + class ModuleDescription: ModuleDescription { + description = "Grouping module for cargo objects in a delivery task. Sync with objects to mark as cargo, then sync this module to FORGE_Module_Delivery."; + sync[] = { "Anything" }; + + class Anything { + description[] = { + "Cargo entities module", + "Sync with objects to mark as delivery cargo" + }; + position = 1; + direction = 1; + optional = 1; + duplicate = 1; + }; + }; + }; + class FORGE_Module_HVT: Module_F { scope = 2; displayName = "HVT Task"; diff --git a/arma/server/addons/task/XEH_PREP.hpp b/arma/server/addons/task/XEH_PREP.hpp index 1fb187a..db12aaa 100644 --- a/arma/server/addons/task/XEH_PREP.hpp +++ b/arma/server/addons/task/XEH_PREP.hpp @@ -1,5 +1,6 @@ PREP(attack); PREP(attackModule); +PREP(cargoModule); PREP(defend); PREP(defendModule); PREP(defuse); @@ -29,3 +30,4 @@ PREP(initTaskStore); PREP(protectedModule); PREP(shootersModule); PREP(spawnEnemyWave); +PREP(startTask); diff --git a/arma/server/addons/task/functions/fnc_attackModule.sqf b/arma/server/addons/task/functions/fnc_attackModule.sqf index 7a364d6..e0cd214 100644 --- a/arma/server/addons/task/functions/fnc_attackModule.sqf +++ b/arma/server/addons/task/functions/fnc_attackModule.sqf @@ -23,29 +23,38 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru if !(_activated) exitWith {}; private _taskID = _logic getVariable ["TaskID", ""]; -private _limitFail = _logic getVariable ["LimitFail", -1]; -private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; -private _companyFunds = _logic getVariable ["CompanyFunds", 0]; -private _ratingFail = _logic getVariable ["RatingFail", 0]; -private _ratingSuccess = _logic getVariable ["RatingSuccess", 0]; -private _endSuccess = _logic getVariable ["EndSuccess", false]; -private _endFail = _logic getVariable ["EndFail", false]; -private _timeLimit = _logic getVariable ["TimeLimit", 0]; - -["INFO", format ["Attack Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, Funds: %4, RatingFail: %5, RatingSuccess: %6, EndSuccess: %7, EndFail: %8, Time: %9", _taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]] call EFUNC(common,log); - -private _syncedEntities = synchronizedObjects _logic; -["INFO", format ["Attack Module Synced Entities: %1", _syncedEntities]] call EFUNC(common,log); - -{ - [_x, _taskID] spawn FUNC(makeTarget); -} forEach _syncedEntities; - -private _params = [_taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; -if (_timeLimit != 0) then { - _params pushBack _timeLimit; +if (_taskID isEqualTo "") exitWith { + ["ERROR", "Attack module: no task ID configured."] call EFUNC(common,log); }; -["attack", _params, 0, ""] spawn FUNC(handler); +private _syncedEntities = synchronizedObjects _logic; +["INFO", format ["Attack Module: TaskID: %1, Synced entities: %2", _taskID, count _syncedEntities]] call EFUNC(common,log); + +private _taskPos = if (_syncedEntities isNotEqualTo []) then { + getPosATL (_syncedEntities select 0) +} else { + getPosATL _logic +}; + +[ + "attack", + _taskID, + _taskPos, + format ["Attack: %1", _taskID], + "Eliminate all hostile forces in the area.", + createHashMapFromArray [ + ["targets", _syncedEntities] + ], + createHashMapFromArray [ + ["limitFail", _logic getVariable ["LimitFail", -1]], + ["limitSuccess", _logic getVariable ["LimitSuccess", -1]], + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["timeLimit", _logic getVariable ["TimeLimit", -1]] + ] +] call FUNC(startTask); deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_cargoModule.sqf b/arma/server/addons/task/functions/fnc_cargoModule.sqf new file mode 100644 index 0000000..6971693 --- /dev/null +++ b/arma/server/addons/task/functions/fnc_cargoModule.sqf @@ -0,0 +1,23 @@ +#include "..\script_component.hpp" + +/* + * Author: IDSolutions + * Grouping module for cargo entities in a delivery task. + * This module has no logic of its own — it acts as a sync target so that + * cargo objects can be grouped and discovered by the parent delivery module + * via synchronizedObjects + typeOf "FORGE_Module_Cargo". + * + * Arguments: + * 0: Logic + * 1: Units + * 2: Activated + * + * Return Value: + * None + * + * Public: No + */ + +params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", true, [true]]]; + +if !(_activated) exitWith {}; diff --git a/arma/server/addons/task/functions/fnc_defendModule.sqf b/arma/server/addons/task/functions/fnc_defendModule.sqf index 8d8dbe3..37f9cd0 100644 --- a/arma/server/addons/task/functions/fnc_defendModule.sqf +++ b/arma/server/addons/task/functions/fnc_defendModule.sqf @@ -2,60 +2,65 @@ /* * Author: IDSolutions - * Creates a defend task module + * 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. * * Arguments: - * None + * 0: Logic + * 1: Units + * 2: Activated * * Return Value: * None * - * Example: - * call forge_server_task_fnc_defendModule; - * * Public: No */ -// Module category -private _category = "Forge Tasks"; -private _subCategory = "Defense Tasks"; +params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", true, [true]]]; -// Create the module -private _module = createDialog "RscDisplayAttributes"; -_module setVariable ["category", _category]; -_module setVariable ["subcategory", _subCategory]; -_module setVariable ["description", "Configure a defend task"]; +if !(_activated) exitWith {}; -// Add fields for task configuration -[_module, "Task ID", "taskID", "", true] call BIS_fnc_addAttribute; -[_module, "Defense Zone Marker", "defenseZone", "", true] call BIS_fnc_addAttribute; -[_module, "Defense Time (seconds)", "defendTime", "600", true] call BIS_fnc_addAttribute; -[_module, "Min BLUFOR in Zone", "minBlufor", "1", true] call BIS_fnc_addAttribute; -[_module, "Company Funds Reward", "companyFunds", "500000", true] call BIS_fnc_addAttribute; -[_module, "Rating Loss on Fail", "ratingFail", "-100", true] call BIS_fnc_addAttribute; -[_module, "Rating Gain on Success", "ratingSuccess", "400", true] call BIS_fnc_addAttribute; -[_module, "End Mission on Success", "endSuccess", "false", false] call BIS_fnc_addAttribute; -[_module, "End Mission on Fail", "endFail", "false", false] call BIS_fnc_addAttribute; -[_module, "Enemy Wave Count", "waveCount", "3", false] call BIS_fnc_addAttribute; -[_module, "Time Between Waves (seconds)", "waveCooldown", "300", false] call BIS_fnc_addAttribute; +private _taskID = _logic getVariable ["TaskID", ""]; +private _defenseZone = _logic getVariable ["DefenseZone", ""]; -// Add confirm button handler -_module setVariable ["onConfirm", { - params ["_module"]; - private _taskID = _module getVariable ["taskID", ""]; - private _defenseZone = _module getVariable ["defenseZone", ""]; - private _defendTime = parseNumber (_module getVariable ["defendTime", "600"]); - private _companyFunds = parseNumber (_module getVariable ["companyFunds", "500000"]); - private _ratingFail = parseNumber (_module getVariable ["ratingFail", "-100"]); - private _ratingSuccess = parseNumber (_module getVariable ["ratingSuccess", "400"]); - private _endSuccess = _module getVariable ["endSuccess", "false"] == "true"; - private _endFail = _module getVariable ["endFail", "false"] == "true"; - private _waveCount = parseNumber (_module getVariable ["waveCount", "3"]); - private _waveCooldown = parseNumber (_module getVariable ["waveCooldown", "300"]); - private _minBlufor = parseNumber (_module getVariable ["minBlufor", "1"]); +if (_taskID isEqualTo "") exitWith { + ["ERROR", "Defend module: no task ID configured."] call EFUNC(common,log); +}; +if (_defenseZone isEqualTo "" || { markerShape _defenseZone isEqualTo "" }) exitWith { + ["ERROR", format ["Defend module '%1': DefenseZone marker '%2' is missing or invalid.", _taskID, _defenseZone]] call EFUNC(common,log); +}; - // Create the task - private _params = [_taskID, _defenseZone, _defendTime, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _waveCount, _waveCooldown, _minBlufor]; - private _requesterUid = ["", getPlayerUID player] select hasInterface; - ["defend", _params, 0, _requesterUid] spawn FUNC(handler); -}]; +["INFO", format [ + "Defend Module Parameters: TaskID: %1, DefenseZone: %2, DefendTime: %3, WaveCount: %4, WaveCooldown: %5, MinBlufor: %6", + _taskID, _defenseZone, + _logic getVariable ["DefendTime", 600], + _logic getVariable ["WaveCount", 3], + _logic getVariable ["WaveCooldown", 300], + _logic getVariable ["MinBlufor", 1] +]] call EFUNC(common,log); + +[ + "defend", + _taskID, + getMarkerPos _defenseZone, + format ["Defend: %1", _taskID], + "Hold the defense zone against incoming enemy forces.", + createHashMap, + createHashMapFromArray [ + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["defenseZone", _defenseZone], + ["defendTime", _logic getVariable ["DefendTime", 600]], + ["waveCount", _logic getVariable ["WaveCount", 3]], + ["waveCooldown", _logic getVariable ["WaveCooldown", 300]], + ["minBlufor", _logic getVariable ["MinBlufor", 1]] + ] +] call FUNC(startTask); + +deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_defuseModule.sqf b/arma/server/addons/task/functions/fnc_defuseModule.sqf index 759c62b..e3b8b1e 100644 --- a/arma/server/addons/task/functions/fnc_defuseModule.sqf +++ b/arma/server/addons/task/functions/fnc_defuseModule.sqf @@ -23,42 +23,48 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru if !(_activated) exitWith {}; private _taskID = _logic getVariable ["TaskID", ""]; -private _limitFail = _logic getVariable ["LimitFail", -1]; -private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; -private _companyFunds = _logic getVariable ["CompanyFunds", 0]; -private _ratingFail = _logic getVariable ["RatingFail", 0]; -private _ratingSuccess = _logic getVariable ["RatingSuccess", 0]; -private _endSuccess = _logic getVariable ["EndSuccess", false]; -private _endFail = _logic getVariable ["EndFail", false]; -private _timeLimit = _logic getVariable ["TimeLimit", 0]; - -["INFO", format ["Defuse Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, Funds: %4, RatingFail: %5, RatingSuccess: %6, EndSuccess: %7, EndFail: %8, Time: %9", _taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]] call EFUNC(common,log); +if (_taskID isEqualTo "") exitWith { + ["ERROR", "Defuse module: no task ID configured."] call EFUNC(common,log); +}; private _syncedModules = synchronizedObjects _logic; -["INFO", format ["Defuse Module Synced Modules: %1", _syncedModules]] call EFUNC(common,log); +private _iedModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Explosives" }) param [0, objNull]; +private _protectedModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Protected" }) param [0, objNull]; +private _iedEntities = if (!isNull _iedModule) then { synchronizedObjects _iedModule } else { [] }; +private _protectedEntities = if (!isNull _protectedModule) then { synchronizedObjects _protectedModule } else { [] }; -private _iedModule = (_syncedModules select { typeOf _x == "FORGE_Module_Explosives" }) select 0; -private _protectedModule = (_syncedModules select { typeOf _x == "FORGE_Module_Protected" }) select 0; +["INFO", format [ + "Defuse Module: TaskID: %1, IEDs: %2, Protected: %3, IED timer: %4s", + _taskID, count _iedEntities, count _protectedEntities, + _logic getVariable ["TimeLimit", 0] +]] call EFUNC(common,log); -private _explosiveEntities = synchronizedObjects _iedModule; -["INFO", format ["Defuse Module Explosive Entites: %1", _explosiveEntities]] call EFUNC(common,log); +private _taskPos = if (_iedEntities isNotEqualTo []) then { + getPosATL (_iedEntities select 0) +} else { + getPosATL _logic +}; -private _protectedEntities = synchronizedObjects _protectedModule; -["INFO", format ["Defuse Module Protected Entities: %1", _protectedEntities]] call EFUNC(common,log); - -{ - if (!isNull _x) then { - [_x, _taskID, _timeLimit] spawn FUNC(makeIED); - }; -} forEach _explosiveEntities; - -{ - if (!isNull _x) then { - [_x, _taskID] spawn FUNC(makeObject); - }; -} forEach _protectedEntities; - -private _params = [_taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; -["defuse", _params, 0, ""] spawn FUNC(handler); +[ + "defuse", + _taskID, + _taskPos, + format ["Defuse: %1", _taskID], + "Locate and defuse all explosive devices before they detonate.", + createHashMapFromArray [ + ["ieds", _iedEntities], + ["protected", _protectedEntities] + ], + createHashMapFromArray [ + ["limitFail", _logic getVariable ["LimitFail", -1]], + ["limitSuccess", _logic getVariable ["LimitSuccess", -1]], + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["iedTimer", _logic getVariable ["TimeLimit", 0]] + ] +] call FUNC(startTask); deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_deliveryModule.sqf b/arma/server/addons/task/functions/fnc_deliveryModule.sqf index 19ead22..2a9c151 100644 --- a/arma/server/addons/task/functions/fnc_deliveryModule.sqf +++ b/arma/server/addons/task/functions/fnc_deliveryModule.sqf @@ -2,66 +2,70 @@ /* * Author: IDSolutions - * Creates a delivery task module + * Initializes the delivery task module. + * Reads parameters from the logic object, collects cargo from the synced + * FORGE_Module_Cargo grouping module, and delegates to fnc_startTask. + * + * Eden layout: + * [FORGE_Module_Delivery] --sync--> [FORGE_Module_Cargo] --sync--> cargo_obj1, cargo_obj2 * * Arguments: - * None + * 0: Logic + * 1: Units + * 2: Activated * * Return Value: * None * - * Example: - * call forge_server_task_fnc_deliveryModule; - * * Public: No */ -// Module category -private _category = "Forge Tasks"; -private _subCategory = "Delivery Tasks"; +params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", true, [true]]]; -// Create the module -private _module = createDialog "RscDisplayAttributes"; -_module setVariable ["category", _category]; -_module setVariable ["subcategory", _subCategory]; -_module setVariable ["description", "Configure a delivery task"]; +if !(_activated) exitWith {}; -// Add fields for task configuration -[_module, "Task ID", "taskID", "", true] call BIS_fnc_addAttribute; -[_module, "Fail Limit", "limitFail", "1", true] call BIS_fnc_addAttribute; -[_module, "Success Count", "limitSuccess", "3", true] call BIS_fnc_addAttribute; -[_module, "Delivery Zone", "deliveryZone", "", true] call BIS_fnc_addAttribute; -[_module, "Company Funds Reward", "companyFunds", "250000", true] call BIS_fnc_addAttribute; -[_module, "Rating Loss on Fail", "ratingFail", "-75", true] call BIS_fnc_addAttribute; -[_module, "Rating Gain on Success", "ratingSuccess", "300", true] call BIS_fnc_addAttribute; -[_module, "End Mission on Success", "endSuccess", "false", false] call BIS_fnc_addAttribute; -[_module, "End Mission on Fail", "endFail", "false", false] call BIS_fnc_addAttribute; -[_module, "Time Limit (seconds)", "timeLimit", "", false] call BIS_fnc_addAttribute; +private _taskID = _logic getVariable ["TaskID", ""]; +if (_taskID isEqualTo "") exitWith { + ["ERROR", "Delivery module: no task ID configured."] call EFUNC(common,log); +}; -// Add confirm button handler -_module setVariable ["onConfirm", { - params ["_module"]; +private _syncedModules = synchronizedObjects _logic; +private _cargoModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Cargo" }) param [0, objNull]; +private _cargoEntities = if (!isNull _cargoModule) then { synchronizedObjects _cargoModule } else { [] }; - private _taskID = _module getVariable ["taskID", ""]; - private _limitFail = parseNumber (_module getVariable ["limitFail", "1"]); - private _limitSuccess = parseNumber (_module getVariable ["limitSuccess", "3"]); - private _deliveryZone = _module getVariable ["deliveryZone", ""]; - private _companyFunds = parseNumber (_module getVariable ["companyFunds", "250000"]); - private _ratingFail = parseNumber (_module getVariable ["ratingFail", "-75"]); - private _ratingSuccess = parseNumber (_module getVariable ["ratingSuccess", "300"]); - private _endSuccess = _module getVariable ["endSuccess", "false"] == "true"; - private _endFail = _module getVariable ["endFail", "false"] == "true"; - private _timeLimit = _module getVariable ["timeLimit", ""]; +["INFO", format [ + "Delivery Module Parameters: TaskID: %1, DeliveryZone: %2, Cargo count: %3", + _taskID, + _logic getVariable ["DeliveryZone", ""], + count _cargoEntities +]] call EFUNC(common,log); - // Convert time limit to number or nil - private _timeLimitNum = if (_timeLimit == "") then { nil } else { parseNumber _timeLimit }; +private _taskPos = if (_cargoEntities isNotEqualTo []) then { + getPosATL (_cargoEntities select 0) +} else { + getPosATL _logic +}; - // Create the task - private _params = [_taskID, _limitFail, _limitSuccess, _deliveryZone, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; - if (!isNil "_timeLimitNum") then { - _params pushBack _timeLimitNum; - }; +[ + "delivery", + _taskID, + _taskPos, + format ["Delivery: %1", _taskID], + "Transport all cargo to the designated delivery zone.", + createHashMapFromArray [ + ["cargo", _cargoEntities] + ], + createHashMapFromArray [ + ["limitFail", _logic getVariable ["LimitFail", -1]], + ["limitSuccess", _logic getVariable ["LimitSuccess", -1]], + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["timeLimit", _logic getVariable ["TimeLimit", -1]], + ["deliveryZone", _logic getVariable ["DeliveryZone", ""]] + ] +] call FUNC(startTask); - private _requesterUid = ["", getPlayerUID player] select hasInterface; - ["delivery", _params, 0, _requesterUid] spawn FUNC(handler); -}]; +deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_destroyModule.sqf b/arma/server/addons/task/functions/fnc_destroyModule.sqf index eaac010..b3b2015 100644 --- a/arma/server/addons/task/functions/fnc_destroyModule.sqf +++ b/arma/server/addons/task/functions/fnc_destroyModule.sqf @@ -23,29 +23,38 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru if !(_activated) exitWith {}; private _taskID = _logic getVariable ["TaskID", ""]; -private _limitFail = _logic getVariable ["LimitFail", -1]; -private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; -private _companyFunds = _logic getVariable ["CompanyFunds", 0]; -private _ratingFail = _logic getVariable ["RatingFail", 0]; -private _ratingSuccess = _logic getVariable ["RatingSuccess", 0]; -private _endSuccess = _logic getVariable ["EndSuccess", false]; -private _endFail = _logic getVariable ["EndFail", false]; -private _timeLimit = _logic getVariable ["TimeLimit", 0]; - -["INFO", format ["Destroy Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, Funds: %4, RatingFail: %5, RatingSuccess: %6, EndSuccess: %7, EndFail: %8, Time: %9", _taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]] call EFUNC(common,log); - -private _syncedEntities = synchronizedObjects _logic; -["INFO", format ["Destroy Module Synced Entities: %1", _syncedEntities]] call EFUNC(common,log); - -{ - [_x, _taskID] spawn FUNC(makeTarget); -} forEach _syncedEntities; - -private _params = [_taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; -if (_timeLimit != 0) then { - _params pushBack _timeLimit; +if (_taskID isEqualTo "") exitWith { + ["ERROR", "Destroy module: no task ID configured."] call EFUNC(common,log); }; -["destroy", _params, 0, ""] spawn FUNC(handler); +private _syncedEntities = synchronizedObjects _logic; +["INFO", format ["Destroy Module: TaskID: %1, Synced entities: %2", _taskID, count _syncedEntities]] call EFUNC(common,log); + +private _taskPos = if (_syncedEntities isNotEqualTo []) then { + getPosATL (_syncedEntities select 0) +} else { + getPosATL _logic +}; + +[ + "destroy", + _taskID, + _taskPos, + format ["Destroy: %1", _taskID], + "Locate and destroy all designated targets.", + createHashMapFromArray [ + ["targets", _syncedEntities] + ], + createHashMapFromArray [ + ["limitFail", _logic getVariable ["LimitFail", -1]], + ["limitSuccess", _logic getVariable ["LimitSuccess", -1]], + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["timeLimit", _logic getVariable ["TimeLimit", -1]] + ] +] call FUNC(startTask); deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_hostageModule.sqf b/arma/server/addons/task/functions/fnc_hostageModule.sqf index b5d2a9a..b40de6c 100644 --- a/arma/server/addons/task/functions/fnc_hostageModule.sqf +++ b/arma/server/addons/task/functions/fnc_hostageModule.sqf @@ -23,54 +23,56 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru if !(_activated) exitWith {}; private _taskID = _logic getVariable ["TaskID", ""]; -private _limitFail = _logic getVariable ["LimitFail", -1]; -private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; -private _extraction = _logic getVariable ["ExtZone", ""]; -private _companyFunds = _logic getVariable ["CompanyFunds", 0]; -private _ratingFail = _logic getVariable ["RatingFail", 0]; -private _ratingSuccess = _logic getVariable ["RatingSuccess", 0]; -private _cbrn = _logic getVariable ["CBRN", false]; -private _execution = _logic getVariable ["Execution", false]; -private _endSuccess = _logic getVariable ["EndSuccess", false]; -private _endFail = _logic getVariable ["EndFail", false]; -private _timeLimit = _logic getVariable ["TimeLimit", 0]; -private _cbrnZone = _logic getVariable ["CBRNZone", ""]; - -["INFO", format [ - "Hostage Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, ExtractionZone: %4, Funds: %5, RatingFail: %6, RatingSuccess: %7, CBRN: %8, Execution: %9, EndSuccess: %10, EndFail: %11, Time: %12, CBRNZone: %13", - _taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, _cbrn, _execution, _endSuccess, _endFail, _timeLimit, _cbrnZone -]] call EFUNC(common,log); - -private _syncedModules = synchronizedObjects _logic; -["INFO", format ["Hostage Module Synced Entities: %1", _syncedModules]] call EFUNC(common,log); - -private _hostageModule = (_syncedModules select { typeOf _x == "FORGE_Module_Hostages" }) select 0; -private _shooterModule = (_syncedModules select { typeOf _x == "FORGE_Module_Shooters" }) select 0; - -private _hostageEntities = synchronizedObjects _hostageModule; -["INFO", format ["Hostage Module Hostage Entities: %1", _hostageEntities]] call EFUNC(common,log); - -private _shooterEntities = synchronizedObjects _shooterModule; -["INFO", format ["Hostage Module Shooter Entities: %1", _shooterEntities]] call EFUNC(common,log); - -{ - if (!isNull _x && (_x isNotEqualTo str objNull)) then { - [_x, _taskID] spawn FUNC(makeHostage); - }; -} forEach _hostageEntities; - -{ - if (!isNull _x && (_x isNotEqualTo str objNull)) then { - [_x, _taskID] spawn FUNC(makeShooter); - }; -} forEach _shooterEntities; - -private _params = [_taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, [_cbrn, _execution], _endSuccess, _endFail]; -if (_timeLimit != 0) then { - _params pushBack _timeLimit; - _params pushBack _cbrnZone; +if (_taskID isEqualTo "") exitWith { + ["ERROR", "Hostage module: no task ID configured."] call EFUNC(common,log); }; -["hostage", _params, 0, ""] spawn FUNC(handler); +private _syncedModules = synchronizedObjects _logic; +private _hostageModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Hostages" }) param [0, objNull]; +private _shooterModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Shooters" }) param [0, objNull]; +private _hostageEntities = if (!isNull _hostageModule) then { synchronizedObjects _hostageModule } else { [] }; +private _shooterEntities = if (!isNull _shooterModule) then { synchronizedObjects _shooterModule } else { [] }; + +["INFO", format [ + "Hostage Module: TaskID: %1, ExtZone: %2, Hostages: %3, Shooters: %4, CBRN: %5, Execution: %6", + _taskID, + _logic getVariable ["ExtZone", ""], + count _hostageEntities, + count _shooterEntities, + _logic getVariable ["CBRN", false], + _logic getVariable ["Execution", false] +]] call EFUNC(common,log); + +private _taskPos = if (_hostageEntities isNotEqualTo []) then { + getPosATL (_hostageEntities select 0) +} else { + getPosATL _logic +}; + +[ + "hostage", + _taskID, + _taskPos, + format ["Hostage Rescue: %1", _taskID], + "Locate and rescue the hostages and bring them to the extraction zone.", + createHashMapFromArray [ + ["hostages", _hostageEntities], + ["shooters", _shooterEntities] + ], + createHashMapFromArray [ + ["limitFail", _logic getVariable ["LimitFail", -1]], + ["limitSuccess", _logic getVariable ["LimitSuccess", -1]], + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["timeLimit", _logic getVariable ["TimeLimit", -1]], + ["extractionZone", _logic getVariable ["ExtZone", ""]], + ["cbrn", _logic getVariable ["CBRN", false]], + ["execution", _logic getVariable ["Execution", false]], + ["cbrnZone", _logic getVariable ["CBRNZone", ""]] + ] +] call FUNC(startTask); deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_hvtModule.sqf b/arma/server/addons/task/functions/fnc_hvtModule.sqf index 2ed59b0..fdc2672 100644 --- a/arma/server/addons/task/functions/fnc_hvtModule.sqf +++ b/arma/server/addons/task/functions/fnc_hvtModule.sqf @@ -23,37 +23,46 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru if !(_activated) exitWith {}; private _taskID = _logic getVariable ["TaskID", ""]; -private _limitFail = _logic getVariable ["LimitFail", -1]; -private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; -private _extraction = _logic getVariable ["ExtZone", ""]; -private _companyFunds = _logic getVariable ["CompanyFunds", 0]; -private _ratingFail = _logic getVariable ["RatingFail", 0]; -private _ratingSuccess = _logic getVariable ["RatingSuccess", 0]; -private _capture = _logic getVariable ["CaptureHVT", true]; -private _eliminate = _logic getVariable ["EliminateHVT", false]; -private _endSuccess = _logic getVariable ["EndSuccess", false]; -private _endFail = _logic getVariable ["EndFail", false]; -private _timeLimit = _logic getVariable ["TimeLimit", 0]; - -["INFO", format [ - "HVT Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, ExtractionZone: %4, Funds: %5, RatingFail: %6, RatingSuccess: %7, CaptureHvt: %8, EliminateHvt: %9, EndSuccess: %10, EndFail: %11, Time: %12", - _taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, _capture, _eliminate, _endSuccess, _endFail, _timeLimit -]] call EFUNC(common,log); - -private _syncedEntities = synchronizedObjects _logic; -["INFO", format ["HVT Module Synced Entities: %1", _syncedEntities]] call EFUNC(common,log); - -{ - if (!isNull _x && (_x isNotEqualTo str objNull)) then { - [_x, _taskID] spawn FUNC(makeHVT); - }; -} forEach _syncedEntities; - -private _params = [_taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, [_capture, _eliminate], _endSuccess, _endFail]; -if (_timeLimit != 0) then { - _params pushBack _timeLimit; +if (_taskID isEqualTo "") exitWith { + ["ERROR", "HVT module: no task ID configured."] call EFUNC(common,log); }; -["hvt", _params, 0, ""] spawn FUNC(handler); +private _syncedEntities = synchronizedObjects _logic; +["INFO", format [ + "HVT Module: TaskID: %1, ExtZone: %2, CaptureHVT: %3, HVTs: %4", + _taskID, + _logic getVariable ["ExtZone", ""], + _logic getVariable ["CaptureHVT", true], + count _syncedEntities +]] call EFUNC(common,log); + +private _taskPos = if (_syncedEntities isNotEqualTo []) then { + getPosATL (_syncedEntities select 0) +} else { + getPosATL _logic +}; + +[ + "hvt", + _taskID, + _taskPos, + format ["HVT: %1", _taskID], + "Locate and capture or eliminate the high-value target.", + createHashMapFromArray [ + ["hvts", _syncedEntities] + ], + createHashMapFromArray [ + ["limitFail", _logic getVariable ["LimitFail", -1]], + ["limitSuccess", _logic getVariable ["LimitSuccess", -1]], + ["funds", _logic getVariable ["CompanyFunds", 0]], + ["ratingFail", _logic getVariable ["RatingFail", 0]], + ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], + ["endSuccess", _logic getVariable ["EndSuccess", false]], + ["endFail", _logic getVariable ["EndFail", false]], + ["timeLimit", _logic getVariable ["TimeLimit", -1]], + ["extractionZone", _logic getVariable ["ExtZone", ""]], + ["captureHvt", _logic getVariable ["CaptureHVT", true]] + ] +] call FUNC(startTask); deleteVehicle _logic; diff --git a/arma/server/addons/task/functions/fnc_missionManager.sqf b/arma/server/addons/task/functions/fnc_missionManager.sqf index e471b27..bde5f6f 100644 --- a/arma/server/addons/task/functions/fnc_missionManager.sqf +++ b/arma/server/addons/task/functions/fnc_missionManager.sqf @@ -244,42 +244,11 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [ ["special", _specialRewards] ] }], - ["createAttackTask", compileFinal { - params [ - ["_taskID", "", [""]], - ["_position", [0, 0, 0], [[]]], - ["_locationConfig", configNull, [configNull]] - ]; - - if (_taskID isEqualTo "" || { isNull _locationConfig }) exitWith { false }; - - private _locationKey = configName _locationConfig; - private _locationType = getText (_locationConfig >> "type"); - if (_locationType isEqualTo "") then { _locationType = "area"; }; - - [ - west, - _taskID, - [ - format ["Eliminate hostile forces operating near %1.", _locationKey], - format ["Attack: %1", _locationKey], - _locationType - ], - _position, - "CREATED", - 1, - true, - "attack" - ] call BFUNC(taskCreate); - - true - }], ["startAttackMission", compileFinal { private _attackConfig = _self getOrDefault ["attackConfig", configNull]; private _locationData = _self call ["selectAttackLocation"]; if (_locationData isEqualTo createHashMap) exitWith { "" }; - private _location = _locationData getOrDefault ["config", configNull]; private _locationKey = _locationData getOrDefault ["key", ""]; private _position = _locationData getOrDefault ["position", [0, 0, 0]]; private _group = _self call ["spawnAttackGroup", [_position]]; @@ -292,45 +261,40 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [ }; private _taskID = format ["task_attack_%1", round (diag_tickTime * 1000)]; - { - [_x, _taskID] call FUNC(makeTarget); - } forEach _units; - - _self call ["createAttackTask", [_taskID, _position, _location]]; - GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArray [ - ["type", "attack"], - ["title", format ["Attack: %1", _locationKey]], - ["description", format ["Eliminate hostile forces operating near %1.", _locationKey]], - ["position", _position], - ["locationKey", _locationKey], - ["accepted", false], - ["requesterUid", ""], - ["orgID", "default"], - ["source", "mission_manager"] - ]]]; - 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 _params = [ + 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, - count _units, - _rewardRange call BFUNC(randomNum), - _penaltyRange call BFUNC(randomNum), - _reputationRange call BFUNC(randomNum), - false, - false, - _timeRange call BFUNC(randomNum), - _rewards get "equipment", - _rewards get "supplies", - _rewards get "weapons", - _rewards get "vehicles", - _rewards get "special" - ]; + "", + "mission_manager" + ] call FUNC(startTask); + + if !(_success) exitWith { "" }; private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap]; _activeMissionRegistry set [_taskID, createHashMapFromArray [ @@ -339,7 +303,6 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [ ]]; _self set ["activeMissionRegistry", _activeMissionRegistry]; - ["attack", _params, 0, ""] spawn FUNC(handler); _taskID }], ["completeMission", compileFinal { diff --git a/arma/server/addons/task/functions/fnc_startTask.sqf b/arma/server/addons/task/functions/fnc_startTask.sqf new file mode 100644 index 0000000..761bc9b --- /dev/null +++ b/arma/server/addons/task/functions/fnc_startTask.sqf @@ -0,0 +1,206 @@ +#include "..\script_component.hpp" + +/* + * Author: IDSolutions + * Unified task initializer used by both Eden modules and missionManager-generated + * tasks. Registers entities, creates the BIS task, registers the catalog entry, + * and dispatches to the task handler. + * + * Arguments: + * 0: Task type ("attack"|"defuse"|"destroy"|"delivery"|"hostage"|"hvt"|"defend") + * 1: Task ID + * 2: Task position + * 3: Task title + * 4: Task description + * 5: Task entities + * Keys: "targets" -- attack, destroy + * "hostages" -- hostage + * "shooters" -- hostage + * "hvts" -- hvt + * "ieds" -- defuse + * "protected" -- defuse + * "cargo" -- delivery + * 6: Task parameters + * Common keys: + * "limitFail" (default: -1) + * "limitSuccess" (default: -1) + * "funds" (default: 0) + * "ratingFail" (default: 0) + * "ratingSuccess" (default: 0) + * "endSuccess" (default: false) + * "endFail" (default: false) + * "timeLimit" (default: -1, -1 = no limit) + * Reward keys: + * "equipment" , "supplies" , "weapons" , + * "vehicles" , "special" + * Type-specific keys: + * defuse: "iedTimer" -- IED countdown in seconds (default: 0) + * delivery: "deliveryZone" -- marker name + * hostage: "extractionZone" -- marker name + * "cbrn" (default: false) + * "execution" (default: false) + * "cbrnZone" (default: "") + * hvt: "extractionZone" -- marker name (capture mode only) + * "captureHvt" (default: true) + * defend: "defenseZone" -- marker name + * "defendTime" (default: 600) + * "waveCount" (default: 3) + * "waveCooldown" (default: 300) + * "minBlufor" (default: 1) + * 7: Minimum org reputation required (default: 0) + * 8: Requester UID (default: "") + * 9: Source tag (default: "eden") -- "eden"|"mission_manager"|"script" + * + * Return Value: + * Success + * + * Examples: + * // From a unit init field -- register entity first, then start task from trigger/init.sqf: + * [this, "compound_attack_01"] call forge_server_task_fnc_makeTarget; + * + * // From a trigger or init.sqf (all-in-one): + * [ + * "attack", "compound_attack_01", getPosATL leader1, + * "Attack: East Compound", "Eliminate all hostile forces.", + * createHashMapFromArray [["targets", [unit1, unit2, unit3]]], + * createHashMapFromArray [ + * ["limitFail", 0], ["limitSuccess", 3], + * ["funds", 50000], ["ratingFail", -10], ["ratingSuccess", 20], + * ["timeLimit", 900] + * ] + * ] call forge_server_task_fnc_startTask; + * + * Public: Yes + */ + +params [ + ["_taskType", "", [""]], + ["_taskID", "", [""]], + ["_position", [0, 0, 0], [[]]], + ["_title", "", [""]], + ["_description", "", [""]], + ["_entities", createHashMap, [createHashMap]], + ["_taskParams", createHashMap, [createHashMap]], + ["_minRating", 0, [0]], + ["_requesterUid", "", [""]], + ["_source", "eden", [""]] +]; + +if (_taskType isEqualTo "" || { _taskID isEqualTo "" }) exitWith { + ["ERROR", "startTask: missing task type or task ID."] call EFUNC(common,log); + false +}; + +// --- 1. Register task entities --- + +private _iedTimer = _taskParams getOrDefault ["iedTimer", 0]; + +{ + private _role = _x; + private _objects = _entities getOrDefault [_role, []]; + { + if (isNull _x) then { continue; }; + switch (_role) do { + case "targets": { [_x, _taskID] call FUNC(makeTarget); }; + case "hostages": { [_x, _taskID] call FUNC(makeHostage); }; + case "shooters": { [_x, _taskID] call FUNC(makeShooter); }; + case "hvts": { [_x, _taskID] call FUNC(makeHVT); }; + case "ieds": { [_x, _taskID, _iedTimer] call FUNC(makeIED); }; + case "protected": { [_x, _taskID] call FUNC(makeObject); }; + case "cargo": { [_x, _taskID] call FUNC(makeCargo); }; + }; + } forEach _objects; +} forEach ["targets", "hostages", "shooters", "hvts", "ieds", "protected", "cargo"]; + +// --- 2. Create BIS task --- + +[west, _taskID, [_description, _title, _taskType], _position, "CREATED", 1, true, _taskType] call BFUNC(taskCreate); + +// --- 3. Register catalog entry --- + +GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArray [ + ["type", _taskType], + ["title", _title], + ["description", _description], + ["position", _position], + ["accepted", false], + ["requesterUid", _requesterUid], + ["orgID", "default"], + ["source", _source] +]]]; + +// --- 4. Assemble type-specific handler args --- + +private _limitFail = _taskParams getOrDefault ["limitFail", -1]; +private _limitSuccess = _taskParams getOrDefault ["limitSuccess", -1]; +private _funds = _taskParams getOrDefault ["funds", 0]; +private _ratingFail = _taskParams getOrDefault ["ratingFail", 0]; +private _ratingSuccess = _taskParams getOrDefault ["ratingSuccess", 0]; +private _endSuccess = _taskParams getOrDefault ["endSuccess", false]; +private _endFail = _taskParams getOrDefault ["endFail", false]; +private _timeLimit = _taskParams getOrDefault ["timeLimit", -1]; +private _equipRewards = _taskParams getOrDefault ["equipment", []]; +private _supplyRewards = _taskParams getOrDefault ["supplies", []]; +private _weaponRewards = _taskParams getOrDefault ["weapons", []]; +private _vehicleRewards = _taskParams getOrDefault ["vehicles", []]; +private _specialRewards = _taskParams getOrDefault ["special", []]; + +private _rewardTail = [_equipRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards]; + +private _handlerArgs = switch (_taskType) do { + case "attack"; + case "destroy": { + private _args = [_taskID, _limitFail, _limitSuccess, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; + if (_timeLimit >= 0) then { _args pushBack _timeLimit; }; + _args + _rewardTail + }; + case "defuse": { + [_taskID, _limitFail, _limitSuccess, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail] + _rewardTail + }; + case "delivery": { + private _deliveryZone = _taskParams getOrDefault ["deliveryZone", ""]; + private _args = [_taskID, _limitFail, _limitSuccess, _deliveryZone, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; + if (_timeLimit >= 0) then { _args pushBack _timeLimit; }; + _args + _rewardTail + }; + case "hostage": { + private _extZone = _taskParams getOrDefault ["extractionZone", ""]; + private _cbrn = _taskParams getOrDefault ["cbrn", false]; + private _execution = _taskParams getOrDefault ["execution", false]; + private _cbrnZone = _taskParams getOrDefault ["cbrnZone", ""]; + private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_cbrn, _execution], _endSuccess, _endFail]; + if (_timeLimit >= 0) then { + _args pushBack _timeLimit; + _args pushBack _cbrnZone; + }; + _args + _rewardTail + }; + case "hvt": { + private _extZone = _taskParams getOrDefault ["extractionZone", ""]; + private _captureHvt = _taskParams getOrDefault ["captureHvt", true]; + private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_captureHvt, !_captureHvt], _endSuccess, _endFail]; + if (_timeLimit >= 0) then { _args pushBack _timeLimit; }; + _args + _rewardTail + }; + case "defend": { + private _defenseZone = _taskParams getOrDefault ["defenseZone", ""]; + private _defendTime = _taskParams getOrDefault ["defendTime", 600]; + 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 + }; + default { + ["ERROR", format ["startTask: unknown task type '%1'.", _taskType]] call EFUNC(common,log); + [] + }; +}; + +if (_handlerArgs isEqualTo []) exitWith { false }; + +// --- 5. Dispatch handler --- + +[_taskType, _handlerArgs, _minRating, _requesterUid] spawn FUNC(handler); + +true -- 2.47.2 From 9d789440e04fd313005f781af658a1164aa965a7 Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Sat, 18 Apr 2026 11:55:19 -0500 Subject: [PATCH 2/2] Enhance documentation and functionality across various server addons - Updated README.md files for extension, garage, locker, main, organization, phone, store, and task addons to provide clearer overviews, dependencies, main components, and usage notes. - Improved task module documentation to clarify timer semantics and server task flows, ensuring accurate usage of time limits. - Adjusted default values for task time limits and IED timers to enforce positive countdown requirements. - Added new CAD addon for dispatch coordination, including its overview, dependencies, main components, and event handling. --- arma/server/addons/actor/README.md | 33 ++++++- arma/server/addons/bank/README.md | 40 ++++++++- arma/server/addons/cad/README.md | 38 ++++++++ arma/server/addons/common/README.md | 24 +++++- arma/server/addons/economy/README.md | 32 ++++++- arma/server/addons/extension/README.md | 30 ++++++- arma/server/addons/garage/README.md | 31 ++++++- arma/server/addons/locker/README.md | 31 ++++++- arma/server/addons/main/README.md | 30 ++++++- arma/server/addons/org/README.md | 37 +++++++- arma/server/addons/phone/README.md | 42 ++++++--- arma/server/addons/store/README.md | 41 ++++++--- arma/server/addons/task/CfgVehicles.hpp | 12 +-- arma/server/addons/task/README.md | 5 ++ .../addons/task/functions/fnc_attack.sqf | 10 +-- .../task/functions/fnc_attackModule.sqf | 2 +- .../task/functions/fnc_defuseModule.sqf | 4 +- .../addons/task/functions/fnc_delivery.sqf | 10 +-- .../task/functions/fnc_deliveryModule.sqf | 2 +- .../addons/task/functions/fnc_destroy.sqf | 10 +-- .../task/functions/fnc_destroyModule.sqf | 2 +- .../addons/task/functions/fnc_hostage.sqf | 10 +-- .../task/functions/fnc_hostageModule.sqf | 2 +- arma/server/addons/task/functions/fnc_hvt.sqf | 10 +-- .../addons/task/functions/fnc_hvtModule.sqf | 2 +- .../addons/task/functions/fnc_makeIED.sqf | 6 +- .../addons/task/functions/fnc_startTask.sqf | 25 ++---- docs/MODULE_REFERENCE.md | 2 +- docs/TASK_USAGE_GUIDE.md | 86 +++++++++++++++++++ 29 files changed, 507 insertions(+), 102 deletions(-) create mode 100644 arma/server/addons/cad/README.md diff --git a/arma/server/addons/actor/README.md b/arma/server/addons/actor/README.md index 004359b..3a69954 100644 --- a/arma/server/addons/actor/README.md +++ b/arma/server/addons/actor/README.md @@ -1,3 +1,32 @@ -# forge_server_actor +# Forge Server Actor -Description for this addon +## Overview +The actor addon is the server-side bridge for player identity and character +state. It keeps Arma-facing actor snapshots in SQF while durable and hot actor +state are owned by the Rust extension. + +Actor records include UID, name, loadout, position, direction, stance, rank, +life state, phone number, email, organization, and holster state. + +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_extension` at runtime for actor extension calls +- `forge_client_actor` for response RPCs + +## Main Components +- `fnc_initActorStore.sqf` initializes `ActorModel` and `ActorStore`. +- `ActorModel` provides defaults, player snapshot conversion, migration, and + validation. +- `ActorStore` wraps extension hot-state calls and exposes event-facing actor + operations. + +## Runtime Behavior +- Missing persistent actors can be created from live player snapshots. +- Hot actor reads are migrated and hydrated before use. +- `saveHotState` in the main addon snapshots and saves actor state on player + disconnect and mission end. + +## Event Surface +The addon handles server events for actor init, get, set, multi-set, save, and +remove requests, then replies to the requesting player through client actor RPCs. diff --git a/arma/server/addons/bank/README.md b/arma/server/addons/bank/README.md index ab01bfc..b1875b8 100644 --- a/arma/server/addons/bank/README.md +++ b/arma/server/addons/bank/README.md @@ -1,3 +1,39 @@ -# forge_server_bank +# Forge Server Bank -Description for this addon +## Overview +The bank addon owns the SQF bridge for player accounts, cash and bank balances, +PIN/session handling, transfers, checkout charging, earnings deposits, and +credit-line repayment. + +Account truth lives in the extension hot cache. SQF handles Arma-facing +validation, client messaging, session state, and payment integration with other +server addons. + +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_extension` at runtime for bank extension calls +- `forge_server_org` at runtime for credit-line repayment +- `forge_client_bank` and `forge_client_notifications` for response RPCs + +## Main Components +- `fnc_initBank.sqf` initializes all bank stores and helpers. +- `fnc_initModel.sqf` defines account defaults and migration behavior. +- `fnc_initPayloadBuilder.sqf` builds UI, checkout, and organization payment + context. +- `fnc_initSessionManager.sqf` manages PIN and authorization session state. +- `fnc_initMessenger.sqf` sends account syncs, alerts, and notifications. +- `fnc_initStore.sqf` wraps hot bank calls and account mutations. + +## Supported Operations +- initialize and hydrate player bank state +- deposit, withdraw, transfer, and deposit earnings +- validate PIN-backed sessions +- charge checkout previews and committed purchases +- repay organization credit lines with rollback on failure +- save hot bank state to durable storage + +## Runtime Notes +`forge_server_main_fnc_saveHotState` saves bank hot state on disconnect and +mission shutdown. Store checkout and task rewards use this addon for +authoritative player balance changes. diff --git a/arma/server/addons/cad/README.md b/arma/server/addons/cad/README.md new file mode 100644 index 0000000..3c17a90 --- /dev/null +++ b/arma/server/addons/cad/README.md @@ -0,0 +1,38 @@ +# Forge Server CAD + +## Overview +The CAD addon coordinates dispatch-facing operational state: groups, +assignments, dispatch orders, support requests, task assignment, permissions, +hydrate payloads, and recent activity. + +CAD state is extension-backed but intentionally transient. It is scoped to the +active server or mission lifecycle and starts fresh after restart. + +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_actor` +- `forge_server_org` +- `forge_server_task` +- `forge_server_extension` at runtime for CAD extension calls +- `forge_client_cad` and `forge_client_notifications` for response RPCs + +## Main Components +- `fnc_initCadStore.sqf` coordinates repositories and request handling. +- `fnc_initActivityRepository.sqf` records recent CAD activity. +- `fnc_initAssignmentRepository.sqf` manages task assignments and dispatch + orders. +- `fnc_initGroupRepository.sqf` manages group membership, role, and status. +- `fnc_initPermissionService.sqf` resolves dispatch permissions. +- `fnc_initPersistenceService.sqf` bridges SQF state to extension hot CAD + storage. +- `fnc_initRequestRepository.sqf` manages support requests. + +## Event Surface +The addon handles hydrate, task assignment, dispatch order, support request, +task acknowledge/decline, and group update events. Successful mutations can +invalidate CAD state globally so clients refresh their views. + +## Notes +CAD hydrate payloads include active task catalog entries from `TaskStore` and +organization context from `ActorStore`. diff --git a/arma/server/addons/common/README.md b/arma/server/addons/common/README.md index 54ae201..a097c6b 100644 --- a/arma/server/addons/common/README.md +++ b/arma/server/addons/common/README.md @@ -1,3 +1,23 @@ -# forge_server_common +# Forge Server Common -Common functionality shared between addons. +## Overview +The common addon provides shared SQF utilities used by server-side Forge +addons. It contains lightweight helpers only; gameplay domain state belongs in +the specific domain addons or the Rust extension. + +## Dependencies +- `forge_server_main` + +## Main Components +- `fnc_baseStore.sqf` provides shared hash-map object behavior such as JSON + conversion. +- `fnc_log.sqf` standardizes server log messages. +- `fnc_getPlayer.sqf` resolves online players by UID. +- `fnc_formatNumber.sqf` formats numeric values for notifications and UI text. +- `fnc_generateHash.sqf` and `fnc_generateSecureData.sqf` provide hashing and + random data helpers. +- `fnc_timeToSeconds.sqf` converts time values into seconds. + +## Notes +Keep this addon free of domain-specific behavior. If a helper needs actor, +bank, org, task, store, or CAD state, it belongs in that addon instead. diff --git a/arma/server/addons/economy/README.md b/arma/server/addons/economy/README.md index 2522c25..51adab8 100644 --- a/arma/server/addons/economy/README.md +++ b/arma/server/addons/economy/README.md @@ -1,3 +1,31 @@ -# forge_server_economy +# Forge Server Economy -Description for this addon +## Overview +The economy addon contains server-side systems for world economic interactions +that are still implemented in SQF. + +Current stores cover fuel tracking, medical service behavior, and a placeholder +service economy store. + +## Dependencies +- `forge_server_main` +- `forge_server_common` at runtime for logging, formatting, and player lookup +- `forge_server_bank` at runtime for medical service charges +- `forge_client_actor` and `forge_client_notifications` for response RPCs + +## Main Components +- `fnc_initFEconomyStore.sqf` tracks active refueling sessions and reports fuel + totals. +- `fnc_initMEconomyStore.sqf` manages medical spawn occupancy, healing charges, + respawn placement, death inventory handling, and body-bag transfer. +- `fnc_initSEconomyStore.sqf` initializes the service economy placeholder. + +## Event Surface +The addon registers CBA server events for fuel start/tick/stop, player killed, +player respawn, and healing. Medical store initialization runs after post-init +to discover configured medical spawn objects. + +## Notes +The service economy store is currently a stub. Fuel and medical behavior should +stay server-authoritative because they mutate money, inventory, and respawn +state. diff --git a/arma/server/addons/extension/README.md b/arma/server/addons/extension/README.md index bd27353..2d939a3 100644 --- a/arma/server/addons/extension/README.md +++ b/arma/server/addons/extension/README.md @@ -1,3 +1,29 @@ -# forge_server_extension +# Forge Server Extension -Extension functionality shared between addons. +## Overview +The extension addon is the SQF bridge to the `forge_server` arma-rs extension. +It normalizes `callExtension` responses, routes large payloads through the +transport layer, and exposes helper functions used by extension-backed server +addons. + +## Dependencies +- `forge_server_main` + +## Main Components +- `fnc_extCall.sqf` is the primary extension call wrapper. +- `fnc_transport.sqf` stages large requests and assembles chunked responses. +- `fnc_setHandler.sqf` registers local SQF handlers for extension callback + integration. + +## Transport Behavior +Most commands use direct `callExtension`. Commands that can return large +payloads, or requests whose encoded arguments exceed the chunk threshold, are +routed through `transport:invoke` or staged transport requests. + +The wrapper falls back to direct calls if the transport route is unsupported and +the request was not chunked. + +## Notes +Domain addons should call `EFUNC(extension,extCall)` instead of calling the +extension directly. This keeps response handling, chunking, and error logging +consistent. diff --git a/arma/server/addons/garage/README.md b/arma/server/addons/garage/README.md index 6002948..9de1717 100644 --- a/arma/server/addons/garage/README.md +++ b/arma/server/addons/garage/README.md @@ -1,3 +1,30 @@ -# forge_server_garage +# Forge Server Garage -Description for this addon +## Overview +The garage addon is the server-side bridge for player vehicle storage and +owner-scoped vehicle unlock storage. + +Garage hot state is owned by the extension. SQF validates Arma-facing requests, +serializes vehicle payloads, sends client syncs, and marks editor-placed garage +objects. + +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_extension` at runtime for garage extension calls +- `forge_client_garage` for response RPCs + +## Main Components +- `fnc_initGarage.sqf` initializes garage world objects. +- `fnc_initGarageStore.sqf` manages player garage hot state. +- `fnc_initVGStore.sqf` manages owner-scoped vehicle unlock state. + +## Supported Operations +- initialize player garage data +- save player and owner-scoped garage state +- store and retrieve player vehicles +- initialize and save owner-scoped vehicle storage + +## Runtime Notes +`forge_server_main_fnc_saveHotState` saves both `GarageStore` and +`VGarageStore` on disconnect and mission shutdown. diff --git a/arma/server/addons/locker/README.md b/arma/server/addons/locker/README.md index b9c2ce7..dc7e8ce 100644 --- a/arma/server/addons/locker/README.md +++ b/arma/server/addons/locker/README.md @@ -1,3 +1,30 @@ -# forge_server_locker +# Forge Server Locker -Description for this addon +## Overview +The locker addon is the server-side bridge for player item storage and +owner-scoped arsenal unlock storage. + +Locker hot state is owned by the extension. SQF handles client events, payload +validation, synchronization, and save calls. + +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_extension` at runtime for locker extension calls +- `forge_client_locker` for response RPCs + +## Main Components +- `fnc_initLocker.sqf` initializes locker world objects. +- `fnc_initLockerStore.sqf` manages player locker hot state. +- `fnc_initVAStore.sqf` manages owner-scoped arsenal unlock state. + +## Supported Operations +- initialize player locker data +- save player and owner-scoped locker state +- override locker data from trusted server-side callers +- initialize and save owner-scoped arsenal storage + +## Runtime Notes +`forge_server_main_fnc_saveHotState` saves both `LockerStore` and `VAStore` on +disconnect and mission shutdown. Store checkout and task rewards can grant +assets into organization-owned storage through the org addon. diff --git a/arma/server/addons/main/README.md b/arma/server/addons/main/README.md index 014bc81..bc7d2aa 100644 --- a/arma/server/addons/main/README.md +++ b/arma/server/addons/main/README.md @@ -1,3 +1,29 @@ -# forge_server_main +# Forge Server Main -Main Addon for forge-server +## Overview +The main addon owns server-side bootstrap behavior for Forge. It prepares +functions, wires extension callbacks and ICom events, initializes shared stores, +and flushes hot state when players disconnect or the mission ends. + +## Dependencies +- `cba_main` +- `ace_main` + +## Main Components +- `fnc_initStores.sqf` initializes core server stores in dependency order. +- `fnc_saveHotState.sqf` snapshots and saves extension-backed hot state. +- `fnc_initValidationHarness.sqf` provides targeted runtime smoke checks for + multi-module flows. +- `XEH_preInit.sqf` registers ICom and extension callback handlers. +- `XEH_postInit.sqf` starts store initialization. + +## Store Initialization +The main addon initializes shared base stores, actor, bank, garage, locker, +organization, store, and validation harness state. Some addons initialize their +own state in pre-init or post-init when they are intentionally independent of +the main bootstrap flow. + +## Hot State Flush +On player disconnect, mission ended, and MP ended events, `saveHotState` +persists actor, bank, locker, virtual arsenal, garage, virtual garage, and +organization hot state for the relevant UID or all known UIDs. diff --git a/arma/server/addons/org/README.md b/arma/server/addons/org/README.md index c9c6ab7..730e40c 100644 --- a/arma/server/addons/org/README.md +++ b/arma/server/addons/org/README.md @@ -1,3 +1,36 @@ -# forge_server_org +# Forge Server Organization -Description for this addon +## Overview +The organization addon is the server-side bridge for player organizations, +membership, treasury funds, reputation, credit lines, shared assets, fleet +entries, and invitations. + +Organization hot state is owned by the extension. SQF coordinates Arma-facing +events, UI payloads, membership syncs, and integration with actor, bank, store, +and task flows. + +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_extension` at runtime for organization extension calls +- `forge_server_actor` at runtime for organization membership lookups +- `forge_client_org` and `forge_client_notifications` for response RPCs + +## Main Components +- `fnc_initOrgStore.sqf` initializes `OrgModel` and `OrgStore`. +- `fnc_initPayloadBuilder.sqf` builds portal, organization, member, asset, and + fleet payloads. + +## Supported Operations +- initialize and hydrate organization portal data +- register, leave, and disband organizations +- invite, accept, and decline members +- assign and repay credit lines +- update funds and reputation +- grant assets and fleet vehicles +- save organization hot state + +## Runtime Notes +The addon ensures the `default` organization exists during store creation. +Task rewards and store checkout both rely on `OrgStore` for authoritative +organization-owned state. diff --git a/arma/server/addons/phone/README.md b/arma/server/addons/phone/README.md index b88555e..2b2398e 100644 --- a/arma/server/addons/phone/README.md +++ b/arma/server/addons/phone/README.md @@ -1,19 +1,33 @@ -forge_server_phone -=================== +# Forge Server Phone -This addon provides the phone user interface and functionality for the in-game phone system. It handles all phone-related features including the UI display, interactions, and core phone operations. +## Overview +The phone addon is the server-side bridge for contacts, SMS messages, and email. +Phone runtime state is owned by the extension. SQF stores preserve the +event-facing API and synchronize client UI state. -Server State ------------- +## Dependencies +- `forge_server_main` +- `forge_server_common` at runtime for online player lookup +- `forge_server_extension` at runtime for phone extension calls +- `forge_client_phone` for response RPCs -Phone contacts, messages, and emails are owned by the server extension. SQF phone stores act as bridge objects for CBA events and UI sync only. +## Main Components +- `fnc_initPhoneStore.sqf` coordinates the phone facade. +- `fnc_initContactStore.sqf` manages contacts. +- `fnc_initMessageStore.sqf` manages SMS messages and threads. +- `fnc_initEmailStore.sqf` manages email messages. +- `fnc_initPlayer.sqf` initializes phone data for a player. -Persistent SurrealDB tables: +## Persistent Extension Tables +- `phone_user`: owner row for an initialized phone profile +- `phone_contact`: per-owner contact rows keyed by owner UID and contact UID +- `phone_message`: shared message records +- `phone_message_index`: per-owner message visibility and read state +- `phone_email`: shared email records +- `phone_email_index`: per-owner email visibility and read state +- `phone_sequence`: global sequence state for generated message and email IDs -- `phone_user`: owner row for an initialized phone profile. -- `phone_contact`: per-owner contact rows keyed by owner UID and contact UID. -- `phone_message`: shared message records. -- `phone_message_index`: per-owner message visibility and read state. -- `phone_email`: shared email records. -- `phone_email_index`: per-owner email visibility and read state. -- `phone_sequence`: global sequence state for generated message and email IDs. +## Event Surface +The addon handles client requests to initialize phone state, add/remove/refresh +contacts, send/read/delete messages, send/read/delete emails, and remove phone +state. diff --git a/arma/server/addons/store/README.md b/arma/server/addons/store/README.md index 08d733a..46feb3c 100644 --- a/arma/server/addons/store/README.md +++ b/arma/server/addons/store/README.md @@ -1,18 +1,35 @@ -# forge_server_store +# Forge Server Store -Server-side SQF module for storefront entities, catalog hydration, and checkout -coordination. +## Overview +The store addon manages server-side storefront entities, catalog hydration, and +checkout coordination. -## Stores and Services +SQF owns Arma-facing storefront discovery and request validation. The Rust +extension owns authoritative checkout calculation through `store:checkout`. -- `StorefrontStore` builds storefront hydrate payloads, validates checkout - requests, calls the Rust `store:checkout` backend, syncs UI patches, and saves - hot state for related modules. -- `StoreCatalogService` scans live Arma config categories, builds catalog - responses, resolves checkout entries, and calculates authoritative prices. +## Dependencies +- `forge_server_main` +- `forge_server_common` +- `forge_server_extension` at runtime for checkout calls +- `forge_server_actor`, `forge_server_bank`, and `forge_server_org` at runtime + for checkout context and payment state +- `forge_client_store` for response RPCs + +## Main Components +- `fnc_initStore.sqf` marks editor-placed store objects with `isStore = true`. +- `fnc_initCatalogService.sqf` scans live Arma config categories, builds + catalog responses, resolves checkout entries, and calculates authoritative + catalog prices. +- `fnc_initStorefrontStore.sqf` builds hydrate payloads, validates checkout + requests, calls `store:checkout`, syncs client patches, and coordinates + related bank/org persistence. ## Editor Entities +`fnc_initStore` matches non-null mission namespace objects whose variable names +contain `store`, mirroring the garage entity initialization pattern. -`fnc_initStore` marks editor-placed store objects with `isStore = true`. It -matches non-null mission namespace objects whose variable names contain -`store`, mirroring the garage entity initialization pattern. +## Checkout Flow +Store checkout can charge cash, bank balance, organization funds, or approved +credit lines depending on the hydrated session context. Checkout results can +grant locker assets, organization assets, and fleet vehicles through the +related domain stores. diff --git a/arma/server/addons/task/CfgVehicles.hpp b/arma/server/addons/task/CfgVehicles.hpp index f045710..dcd25c4 100644 --- a/arma/server/addons/task/CfgVehicles.hpp +++ b/arma/server/addons/task/CfgVehicles.hpp @@ -355,9 +355,9 @@ class CfgVehicles { class TimeLimit: Edit { property = "FORGE_Module_Defuse_TimeLimit"; displayName = "Time Limit"; - tooltip = "Time in seconds before detenation (0 for no limit)"; + tooltip = "Time in seconds before detonation; must be greater than 0"; typeName = "NUMBER"; - defaultValue = 0; + defaultValue = 300; }; }; @@ -608,7 +608,7 @@ class CfgVehicles { class TimeLimit: Edit { property = "FORGE_Module_Hostage_TimeLimit"; displayName = "Time Limit"; - tooltip = "Time in seconds before HVTs escape (0 for no limit)"; + tooltip = "Time in seconds before hostages are executed (0 for no limit)"; typeName = "NUMBER"; defaultValue = 0; }; @@ -730,9 +730,9 @@ class CfgVehicles { class TimeLimit: Edit { property = "FORGE_Module_Delivery_TimeLimit"; displayName = "Time Limit"; - tooltip = "Seconds to complete delivery (-1 for no limit)"; + tooltip = "Seconds to complete delivery (0 for no limit)"; typeName = "NUMBER"; - defaultValue = -1; + defaultValue = 0; }; }; @@ -907,7 +907,7 @@ class CfgVehicles { class TimeLimit: Edit { property = "FORGE_Module_HVT_TimeLimit"; displayName = "Time Limit"; - tooltip = "Time in seconds before HVTs escape (0 for no limit)"; + tooltip = "Time in seconds before the HVT task fails (0 for no limit)"; typeName = "NUMBER"; defaultValue = 0; }; diff --git a/arma/server/addons/task/README.md b/arma/server/addons/task/README.md index ef67658..038449d 100644 --- a/arma/server/addons/task/README.md +++ b/arma/server/addons/task/README.md @@ -16,6 +16,7 @@ system intentionally starts clean after each server or mission restart. - notify task participants and sync org updates to online members ## Dependencies +- `forge_server_extension` - `forge_server_common` - `forge_server_actor` - `forge_server_bank` @@ -58,6 +59,10 @@ Org rewards always go to the bound owner org. Player earnings still use per-play ## Usage +Task time limits use `0` for no limit on attack, destroy, delivery, hostage, +and HVT tasks. Defuse IED timers are different: each IED must have a positive +countdown value. + ### Start Through The Handler Use the handler when you want reputation gating and task ownership binding. diff --git a/arma/server/addons/task/functions/fnc_attack.sqf b/arma/server/addons/task/functions/fnc_attack.sqf index ef91a6e..a4893fc 100644 --- a/arma/server/addons/task/functions/fnc_attack.sqf +++ b/arma/server/addons/task/functions/fnc_attack.sqf @@ -13,7 +13,7 @@ * 5: Amount of rating the company and player recieve if the task is successful (default: 0) * 6: Should the mission end (MissionSuccess) if the task is successful (default: false) * 7: Should the mission end (MissionFailed) if the task is failed (default: false) - * 8: Amount of time before target(s) escape (default: -1) + * 8: Amount of time before target(s) escape (default: 0, 0 = no limit) * 9: Equipment rewards (default: []) * 10: Supply rewards (default: []) * 11: Weapon rewards (default: []) @@ -39,7 +39,7 @@ params [ ["_ratingSuccess", 0, [0]], ["_endSuccess", false, [false]], ["_endFail", false, [false]], - ["_time", -1, [0]], + ["_timeLimit", 0, [0]], ["_equipmentRewards", [], [[]]], ["_supplyRewards", [], [[]]], ["_weaponRewards", [], [[]]], @@ -58,7 +58,7 @@ waitUntil { }; _targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]]; -private _startTime = if (!isNil "_time") then { floor(time) } else { nil }; +private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil }; waitUntil { sleep 1; @@ -66,8 +66,8 @@ waitUntil { private _targetsKilled = ({ !alive _x } count _targets); - if (_time isNotEqualTo -1) then { - private _timeExpired = (floor time - _startTime >= _time); + if (_timeLimit isNotEqualTo 0) then { + private _timeExpired = (floor time - _startTime >= _timeLimit); if (_targetsKilled < _limitSuccess && _timeExpired) then { _result = 1; }; diff --git a/arma/server/addons/task/functions/fnc_attackModule.sqf b/arma/server/addons/task/functions/fnc_attackModule.sqf index e0cd214..17d413e 100644 --- a/arma/server/addons/task/functions/fnc_attackModule.sqf +++ b/arma/server/addons/task/functions/fnc_attackModule.sqf @@ -53,7 +53,7 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then { ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], ["endSuccess", _logic getVariable ["EndSuccess", false]], ["endFail", _logic getVariable ["EndFail", false]], - ["timeLimit", _logic getVariable ["TimeLimit", -1]] + ["timeLimit", _logic getVariable ["TimeLimit", 0]] ] ] call FUNC(startTask); diff --git a/arma/server/addons/task/functions/fnc_defuseModule.sqf b/arma/server/addons/task/functions/fnc_defuseModule.sqf index e3b8b1e..3324c02 100644 --- a/arma/server/addons/task/functions/fnc_defuseModule.sqf +++ b/arma/server/addons/task/functions/fnc_defuseModule.sqf @@ -36,7 +36,7 @@ private _protectedEntities = if (!isNull _protectedModule) then { synchronizedOb ["INFO", format [ "Defuse Module: TaskID: %1, IEDs: %2, Protected: %3, IED timer: %4s", _taskID, count _iedEntities, count _protectedEntities, - _logic getVariable ["TimeLimit", 0] + _logic getVariable ["TimeLimit", 300] ]] call EFUNC(common,log); private _taskPos = if (_iedEntities isNotEqualTo []) then { @@ -63,7 +63,7 @@ private _taskPos = if (_iedEntities isNotEqualTo []) then { ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], ["endSuccess", _logic getVariable ["EndSuccess", false]], ["endFail", _logic getVariable ["EndFail", false]], - ["iedTimer", _logic getVariable ["TimeLimit", 0]] + ["iedTimer", _logic getVariable ["TimeLimit", 300]] ] ] call FUNC(startTask); diff --git a/arma/server/addons/task/functions/fnc_delivery.sqf b/arma/server/addons/task/functions/fnc_delivery.sqf index 1088e0f..426e10a 100644 --- a/arma/server/addons/task/functions/fnc_delivery.sqf +++ b/arma/server/addons/task/functions/fnc_delivery.sqf @@ -14,7 +14,7 @@ * 6: Amount of rating the company and player receive if the task is successful (default: 0) * 7: Should the mission end (MissionSuccess) if the task is successful (default: false) * 8: Should the mission end (MissionFailed) if the task is failed (default: false) - * 9: Amount of time to complete delivery (default: -1) + * 9: Amount of time to complete delivery (default: 0) * 10: Equipment rewards (default: []) * 11: Supply rewards (default: []) * 12: Weapon rewards (default: []) @@ -41,7 +41,7 @@ params [ ["_ratingSuccess", 0, [0]], ["_endSuccess", false, [false]], ["_endFail", false, [false]], - ["_time", -1, [0]], + ["_timeLimit", 0, [0]], ["_equipmentRewards", [], [[]]], ["_supplyRewards", [], [[]]], ["_weaponRewards", [], [[]]], @@ -60,7 +60,7 @@ waitUntil { }; _cargo = GVAR(TaskStore) call ["getTaskEntities", ["cargo", _taskID]]; -private _startTime = if (_time isNotEqualTo -1) then { floor(time) } else { nil }; +private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil }; waitUntil { sleep 1; @@ -69,8 +69,8 @@ waitUntil { private _cargoDelivered = ({ _x inArea _deliveryZone && (damage _x) < 0.7 } count _cargo); private _cargoDamaged = ({ damage _x >= 0.7 } count _cargo); - if (_time isNotEqualTo -1) then { - private _timeExpired = (floor time - _startTime >= _time); + if (_timeLimit isNotEqualTo 0) then { + private _timeExpired = (floor time - _startTime >= _timeLimit); if (_cargoDamaged >= _limitFail) then { _result = 1; }; if (_cargoDelivered < _limitSuccess && _timeExpired) then { _result = 1; }; diff --git a/arma/server/addons/task/functions/fnc_deliveryModule.sqf b/arma/server/addons/task/functions/fnc_deliveryModule.sqf index 2a9c151..757a3f4 100644 --- a/arma/server/addons/task/functions/fnc_deliveryModule.sqf +++ b/arma/server/addons/task/functions/fnc_deliveryModule.sqf @@ -63,7 +63,7 @@ private _taskPos = if (_cargoEntities isNotEqualTo []) then { ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], ["endSuccess", _logic getVariable ["EndSuccess", false]], ["endFail", _logic getVariable ["EndFail", false]], - ["timeLimit", _logic getVariable ["TimeLimit", -1]], + ["timeLimit", _logic getVariable ["TimeLimit", 0]], ["deliveryZone", _logic getVariable ["DeliveryZone", ""]] ] ] call FUNC(startTask); diff --git a/arma/server/addons/task/functions/fnc_destroy.sqf b/arma/server/addons/task/functions/fnc_destroy.sqf index dcb1013..2284dda 100644 --- a/arma/server/addons/task/functions/fnc_destroy.sqf +++ b/arma/server/addons/task/functions/fnc_destroy.sqf @@ -13,7 +13,7 @@ * 5: Amount of rating the company and player recieve if the task is successful (default: 0) * 6: Should the mission end (MissionSuccess) if the task is successful (default: false) * 7: Should the mission end (MissionFailed) if the task is failed (default: false) - * 8: Amount of time before target(s) escape (default: -1) + * 8: Amount of time before target(s) escape (default: 0, 0 = no limit) * 9: Equipment rewards (default: []) * 10: Supply rewards (default: []) * 11: Weapon rewards (default: []) @@ -39,7 +39,7 @@ params [ ["_ratingSuccess", 0, [0]], ["_endSuccess", false, [false]], ["_endFail", false, [false]], - ["_time", -1, [0]], + ["_timeLimit", 0, [0]], ["_equipmentRewards", [], [[]]], ["_supplyRewards", [], [[]]], ["_weaponRewards", [], [[]]], @@ -58,7 +58,7 @@ waitUntil { }; _targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]]; -private _startTime = if (!isNil "_time") then { floor(time) } else { nil }; +private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil }; waitUntil { sleep 1; @@ -66,8 +66,8 @@ waitUntil { private _targetsDestroyed = ({ !alive _x } count _targets); - if (!isNil "_time") then { - private _timeExpired = (floor time - _startTime >= _time); + if (_timeLimit isNotEqualTo 0) then { + private _timeExpired = (floor time - _startTime >= _timeLimit); if (_targetsDestroyed < _limitSuccess && _timeExpired) then { _result = 1; }; diff --git a/arma/server/addons/task/functions/fnc_destroyModule.sqf b/arma/server/addons/task/functions/fnc_destroyModule.sqf index b3b2015..499d55d 100644 --- a/arma/server/addons/task/functions/fnc_destroyModule.sqf +++ b/arma/server/addons/task/functions/fnc_destroyModule.sqf @@ -53,7 +53,7 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then { ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], ["endSuccess", _logic getVariable ["EndSuccess", false]], ["endFail", _logic getVariable ["EndFail", false]], - ["timeLimit", _logic getVariable ["TimeLimit", -1]] + ["timeLimit", _logic getVariable ["TimeLimit", 0]] ] ] call FUNC(startTask); diff --git a/arma/server/addons/task/functions/fnc_hostage.sqf b/arma/server/addons/task/functions/fnc_hostage.sqf index ead6b2a..66c4071 100644 --- a/arma/server/addons/task/functions/fnc_hostage.sqf +++ b/arma/server/addons/task/functions/fnc_hostage.sqf @@ -15,7 +15,7 @@ * 7: Subcategory of task (default: [false, true]) * 8: Should the mission end (MissionSuccess) if the task is successful (default: false) * 9: Should the mission end (MissionFailed) if the task is failed (default: false) - * 10: Amount of time before hostage(s) die (default: -1) + * 10: Amount of time before hostage(s) are executed (default: 0, 0 = no limit) * 11: Marker name for the cbrn zone (default: "") * 12: Equipment rewards (default: []) * 13: Supply rewards (default: []) @@ -45,7 +45,7 @@ params [ ["_type", [["_cbrn", false, [false]], ["_hostage", true, [false]]]], ["_endSuccess", false, [false]], ["_endFail", false, [false]], - ["_time", -1, [0]], + ["_timeLimit", 0, [0]], ["_cbrnZone", "", [""]], ["_equipmentRewards", [], [[]]], ["_supplyRewards", [], [[]]], @@ -75,7 +75,7 @@ waitUntil { _hostages = GVAR(TaskStore) call ["getTaskEntities", ["hostages", _taskID]]; _shooters = GVAR(TaskStore) call ["getTaskEntities", ["shooters", _taskID]]; -private _startTime = if (_time isNotEqualTo -1) then { floor(time) } else { nil }; +private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil }; waitUntil { sleep 1; @@ -86,8 +86,8 @@ waitUntil { private _hostagesKilled = ({ !alive _x } count _hostages); private _shootersAlive = ({ alive _x } count _shooters); - if (_time isNotEqualTo -1) then { - private _timeExpired = (floor time - _startTime >= _time); + if (_timeLimit isNotEqualTo 0) then { + private _timeExpired = (floor time - _startTime >= _timeLimit); if (_hostagesFreed < _limitSuccess && _timeExpired) then { _result = 1; }; if (_hostagesKilled >= _limitFail) then { _result = 1; }; diff --git a/arma/server/addons/task/functions/fnc_hostageModule.sqf b/arma/server/addons/task/functions/fnc_hostageModule.sqf index b40de6c..8a90a5f 100644 --- a/arma/server/addons/task/functions/fnc_hostageModule.sqf +++ b/arma/server/addons/task/functions/fnc_hostageModule.sqf @@ -67,7 +67,7 @@ private _taskPos = if (_hostageEntities isNotEqualTo []) then { ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], ["endSuccess", _logic getVariable ["EndSuccess", false]], ["endFail", _logic getVariable ["EndFail", false]], - ["timeLimit", _logic getVariable ["TimeLimit", -1]], + ["timeLimit", _logic getVariable ["TimeLimit", 0]], ["extractionZone", _logic getVariable ["ExtZone", ""]], ["cbrn", _logic getVariable ["CBRN", false]], ["execution", _logic getVariable ["Execution", false]], diff --git a/arma/server/addons/task/functions/fnc_hvt.sqf b/arma/server/addons/task/functions/fnc_hvt.sqf index f763300..97f54c8 100644 --- a/arma/server/addons/task/functions/fnc_hvt.sqf +++ b/arma/server/addons/task/functions/fnc_hvt.sqf @@ -15,7 +15,7 @@ * 7: Subcategory of task (default: [true, false]) * 8: Should the mission end (MissionSuccess) if the task is successful (default: false) * 9: Should the mission end (MissionFailed) if the task is failed (default: false) - * 10: Amount of time before hvt(s) die (default: -1) + * 10: Amount of time before HVT task fails (default: 0, 0 = no limit) * 11: Equipment rewards (default: []) * 12: Supply rewards (default: []) * 13: Weapon rewards (default: []) @@ -45,7 +45,7 @@ params [ ["_type", [["_capture", true, [false]], ["_eliminate", false, [false]]]], ["_endSuccess", false, [false]], ["_endFail", false, [false]], - ["_time", -1, [0]], + ["_timeLimit", 0, [0]], ["_equipmentRewards", [], [[]]], ["_supplyRewards", [], [[]]], ["_weaponRewards", [], [[]]], @@ -66,7 +66,7 @@ waitUntil { }; _hvts = GVAR(TaskStore) call ["getTaskEntities", ["hvts", _taskID]]; -private _startTime = if (!isNil "_time") then { floor(time) } else { nil }; +private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil }; waitUntil { sleep 1; @@ -76,8 +76,8 @@ waitUntil { private _hvtsKilled = ({ !alive _x } count _hvts); private _hvtsInZone = ({ _x inArea _extZone } count _hvts); - if (!isNil "_time") then { - private _timeExpired = (floor time - _startTime >= _time); + 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; }; diff --git a/arma/server/addons/task/functions/fnc_hvtModule.sqf b/arma/server/addons/task/functions/fnc_hvtModule.sqf index fdc2672..58f242a 100644 --- a/arma/server/addons/task/functions/fnc_hvtModule.sqf +++ b/arma/server/addons/task/functions/fnc_hvtModule.sqf @@ -59,7 +59,7 @@ private _taskPos = if (_syncedEntities isNotEqualTo []) then { ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]], ["endSuccess", _logic getVariable ["EndSuccess", false]], ["endFail", _logic getVariable ["EndFail", false]], - ["timeLimit", _logic getVariable ["TimeLimit", -1]], + ["timeLimit", _logic getVariable ["TimeLimit", 0]], ["extractionZone", _logic getVariable ["ExtZone", ""]], ["captureHvt", _logic getVariable ["CaptureHVT", true]] ] diff --git a/arma/server/addons/task/functions/fnc_makeIED.sqf b/arma/server/addons/task/functions/fnc_makeIED.sqf index a4489c6..6f185ee 100644 --- a/arma/server/addons/task/functions/fnc_makeIED.sqf +++ b/arma/server/addons/task/functions/fnc_makeIED.sqf @@ -2,12 +2,12 @@ /* * Author: IDSolutions - * Assigns an IED to a task and starts countdown timer + * Assigns an IED to a task and starts its required countdown timer * * Arguments: * 0: The object * 1: ID of the task - * 2: The Countdown Timer + * 2: Countdown timer in seconds (must be greater than 0) * * Return Value: * None @@ -22,7 +22,7 @@ params [["_entity", objNull, [objNull]], ["_taskID", "", [""]], ["_time", 0, [0] if (isNull _entity) exitWith { ["ERROR", "Attempt to create entity from null object"] call EFUNC(common,log); }; if (_taskID == "") exitWith { ["ERROR", "No task ID provided for entity"] call EFUNC(common,log); }; -if (_time < 0) exitWith { ["ERROR", "Invalid time provided for IED"] call EFUNC(common,log); }; +if (_time <= 0) exitWith { ["ERROR", "Invalid time provided for IED"] call EFUNC(common,log); }; ["INFO", format ["Make IED: %1", _this]] call EFUNC(common,log); diff --git a/arma/server/addons/task/functions/fnc_startTask.sqf b/arma/server/addons/task/functions/fnc_startTask.sqf index 761bc9b..b108747 100644 --- a/arma/server/addons/task/functions/fnc_startTask.sqf +++ b/arma/server/addons/task/functions/fnc_startTask.sqf @@ -29,12 +29,12 @@ * "ratingSuccess" (default: 0) * "endSuccess" (default: false) * "endFail" (default: false) - * "timeLimit" (default: -1, -1 = no limit) + * "timeLimit" (default: 0, 0 = no limit) * Reward keys: * "equipment" , "supplies" , "weapons" , * "vehicles" , "special" * Type-specific keys: - * defuse: "iedTimer" -- IED countdown in seconds (default: 0) + * defuse: "iedTimer" -- required IED countdown in seconds (> 0) * delivery: "deliveryZone" -- marker name * hostage: "extractionZone" -- marker name * "cbrn" (default: false) @@ -138,7 +138,7 @@ private _ratingFail = _taskParams getOrDefault ["ratingFail", 0]; private _ratingSuccess = _taskParams getOrDefault ["ratingSuccess", 0]; private _endSuccess = _taskParams getOrDefault ["endSuccess", false]; private _endFail = _taskParams getOrDefault ["endFail", false]; -private _timeLimit = _taskParams getOrDefault ["timeLimit", -1]; +private _timeLimit = _taskParams getOrDefault ["timeLimit", 0]; private _equipRewards = _taskParams getOrDefault ["equipment", []]; private _supplyRewards = _taskParams getOrDefault ["supplies", []]; private _weaponRewards = _taskParams getOrDefault ["weapons", []]; @@ -150,8 +150,7 @@ private _rewardTail = [_equipRewards, _supplyRewards, _weaponRewards, _vehicleRe private _handlerArgs = switch (_taskType) do { case "attack"; case "destroy": { - private _args = [_taskID, _limitFail, _limitSuccess, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; - if (_timeLimit >= 0) then { _args pushBack _timeLimit; }; + private _args = [_taskID, _limitFail, _limitSuccess, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]; _args + _rewardTail }; case "defuse": { @@ -159,8 +158,7 @@ private _handlerArgs = switch (_taskType) do { }; case "delivery": { private _deliveryZone = _taskParams getOrDefault ["deliveryZone", ""]; - private _args = [_taskID, _limitFail, _limitSuccess, _deliveryZone, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; - if (_timeLimit >= 0) then { _args pushBack _timeLimit; }; + private _args = [_taskID, _limitFail, _limitSuccess, _deliveryZone, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]; _args + _rewardTail }; case "hostage": { @@ -168,18 +166,14 @@ private _handlerArgs = switch (_taskType) do { private _cbrn = _taskParams getOrDefault ["cbrn", false]; private _execution = _taskParams getOrDefault ["execution", false]; private _cbrnZone = _taskParams getOrDefault ["cbrnZone", ""]; - private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_cbrn, _execution], _endSuccess, _endFail]; - if (_timeLimit >= 0) then { - _args pushBack _timeLimit; - _args pushBack _cbrnZone; - }; + private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_cbrn, _execution], _endSuccess, _endFail, _timeLimit]; + _args pushBack _cbrnZone; _args + _rewardTail }; case "hvt": { private _extZone = _taskParams getOrDefault ["extractionZone", ""]; private _captureHvt = _taskParams getOrDefault ["captureHvt", true]; - private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_captureHvt, !_captureHvt], _endSuccess, _endFail]; - if (_timeLimit >= 0) then { _args pushBack _timeLimit; }; + private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_captureHvt, !_captureHvt], _endSuccess, _endFail, _timeLimit]; _args + _rewardTail }; case "defend": { @@ -188,8 +182,7 @@ 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 + [_taskID, _defenseZone, _defendTime, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _waveCount, _waveCooldown, _minBlufor] + _rewardTail }; default { ["ERROR", format ["startTask: unknown task type '%1'.", _taskType]] call EFUNC(common,log); diff --git a/docs/MODULE_REFERENCE.md b/docs/MODULE_REFERENCE.md index ce417d3..1b9ec1e 100644 --- a/docs/MODULE_REFERENCE.md +++ b/docs/MODULE_REFERENCE.md @@ -29,7 +29,7 @@ docs/ Framework-level documentation | Organization | Player organizations, membership, treasury, credit lines, shared assets, and fleet data. | `arma/client/addons/org` | `arma/server/addons/org` | `lib/models/src/org.rs`, `lib/services/src/org.rs` | `org:*`, `org:hot:*` | | Phone | Contacts, messages, and email state. | `arma/client/addons/phone` | `arma/server/addons/phone` | `lib/models/src/phone.rs`, `lib/services/src/phone.rs` | `phone:*` | | Store | Storefront entity setup, catalog hydration, checkout workflows, and checkout charging integration. | `arma/client/addons/store` | `arma/server/addons/store` | `lib/models/src/store.rs`, `lib/services/src/store.rs` | `store:checkout` | -| Task | Mission/task catalog, ownership, status, reward context, and task counters. | none | `arma/server/addons/task` | `lib/models/src/task.rs`, `lib/services/src/task.rs` | `task:*` | +| Task | Server-owned mission/task flows, catalog, ownership, status, participant tracking, rewards, and defuse counters. | none | `arma/server/addons/task` | `lib/models/src/task.rs`, `lib/services/src/task.rs` | `task:*` | | Owned Garage | Organization or owner-scoped vehicle unlock storage. | via garage/org UI | server extension only | `lib/models/src/v_garage.rs`, `lib/services/src/v_garage.rs` | `owned:garage:*` | | Owned Locker | Organization or owner-scoped arsenal unlock storage. | via locker/org UI | server extension only | `lib/models/src/v_locker.rs`, `lib/services/src/v_locker.rs` | `owned:locker:*` | diff --git a/docs/TASK_USAGE_GUIDE.md b/docs/TASK_USAGE_GUIDE.md index 446a0e8..6003e55 100644 --- a/docs/TASK_USAGE_GUIDE.md +++ b/docs/TASK_USAGE_GUIDE.md @@ -4,6 +4,20 @@ The task module stores transient mission task metadata for active server or mission lifecycle workflows. SQF still owns Arma-only runtime state such as objects and participants. +The server addon at `arma/server/addons/task` also owns task execution: +creating BIS tasks, registering task entities, tracking participants, binding +task ownership, applying player/org rewards, and clearing task state when a +task completes. + +Runtime dependencies: + +- `forge_server_extension` +- `forge_server_common` +- `forge_server_actor` +- `forge_server_bank` +- `forge_server_org` +- `forge_client_notifications` + ## Data Model Catalog entries are flexible JSON objects. The service normalizes these fields @@ -102,6 +116,78 @@ private _context = fromJSON (_result select 0); The reward context contains `requesterUid` and `orgId`. +## Server Task Flows + +The task addon provides these server-owned task flows: + +- `attack` +- `defend` +- `defuse` +- `delivery` +- `destroy` +- `hostage` +- `hvt` + +Use `forge_server_task_fnc_startTask` when creating tasks from modules, +mission scripts, or generated mission-manager content. It registers task +entities, creates the BIS task, stores the catalog entry, then dispatches +through `forge_server_task_fnc_handler`. + +```sqf +[ + "attack", + "compound_attack_01", + getPosATL leader1, + "Attack: East Compound", + "Eliminate all hostile forces.", + createHashMapFromArray [["targets", [unit1, unit2, unit3]]], + createHashMapFromArray [ + ["limitFail", 0], + ["limitSuccess", 3], + ["funds", 50000], + ["ratingFail", -10], + ["ratingSuccess", 20], + ["timeLimit", 900] + ], + 0, + getPlayerUID player, + "script" +] call forge_server_task_fnc_startTask; +``` + +Use `forge_server_task_fnc_handler` directly when the task entities are already +registered and you want reputation gating plus ownership binding: + +```sqf +[ + "delivery", + ["delivery_1", 1, 3, "delivery_zone", 250000, -75, 300, false, false, 900], + 250, + getPlayerUID player +] call forge_server_task_fnc_handler; +``` + +Direct task function calls still work for mission-authored or server-owned +tasks, but they do not provide a requester UID. Ownership falls back to the +`default` org. + +## Timer Semantics + +Task time limits use `0` for no limit: + +- attack `timeLimit` +- destroy `timeLimit` +- delivery `timeLimit` +- hostage `timeLimit` +- HVT `timeLimit` + +Positive values are measured in seconds. Do not pass `-1` as a no-limit value; +the task runtime treats any non-zero task time limit as active. + +Defuse IED timers are different. `iedTimer` must be greater than `0`, because +IEDs are expected to have an active countdown. The Eden defuse module defaults +to `300` seconds. + ## Defuse Counter ```sqf -- 2.47.2