Refactor task store into modular services
- Add task chain prerequisite parsing and startup gating - Split lifecycle, catalog, entity, participant, and reward logic into service objects - Update task modules and docs for chained task flow
This commit is contained in:
parent
08200488de
commit
12dc262136
@ -34,6 +34,7 @@ class CfgVehicles {
|
|||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
// defaultValue = """";
|
// defaultValue = """";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_Attack)
|
||||||
class LimitFail: Edit {
|
class LimitFail: Edit {
|
||||||
property = "FORGE_Module_Attack_LimitFail";
|
property = "FORGE_Module_Attack_LimitFail";
|
||||||
displayName = "Fail Limit";
|
displayName = "Fail Limit";
|
||||||
@ -292,6 +293,7 @@ class CfgVehicles {
|
|||||||
tooltip = "Unique identifier for this task";
|
tooltip = "Unique identifier for this task";
|
||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_Defend)
|
||||||
class DefenseZone: Edit {
|
class DefenseZone: Edit {
|
||||||
property = "FORGE_Module_Defend_DefenseZone";
|
property = "FORGE_Module_Defend_DefenseZone";
|
||||||
displayName = "Defense Zone Marker";
|
displayName = "Defense Zone Marker";
|
||||||
@ -417,6 +419,7 @@ class CfgVehicles {
|
|||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
// defaultValue = """";
|
// defaultValue = """";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_Defuse)
|
||||||
class LimitFail: Edit {
|
class LimitFail: Edit {
|
||||||
property = "FORGE_Module_Defuse_LimitFail";
|
property = "FORGE_Module_Defuse_LimitFail";
|
||||||
displayName = "Fail Limit";
|
displayName = "Fail Limit";
|
||||||
@ -529,6 +532,7 @@ class CfgVehicles {
|
|||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
// defaultValue = """";
|
// defaultValue = """";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_Destroy)
|
||||||
class LimitFail: Edit {
|
class LimitFail: Edit {
|
||||||
property = "FORGE_Module_Destroy_LimitFail";
|
property = "FORGE_Module_Destroy_LimitFail";
|
||||||
displayName = "Fail Limit";
|
displayName = "Fail Limit";
|
||||||
@ -641,6 +645,7 @@ class CfgVehicles {
|
|||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
// defaultValue = """";
|
// defaultValue = """";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_Hostage)
|
||||||
class LimitFail: Edit {
|
class LimitFail: Edit {
|
||||||
property = "FORGE_Module_Hostage_LimitFail";
|
property = "FORGE_Module_Hostage_LimitFail";
|
||||||
displayName = "Fail Limit";
|
displayName = "Fail Limit";
|
||||||
@ -789,6 +794,7 @@ class CfgVehicles {
|
|||||||
tooltip = "Unique identifier for this task";
|
tooltip = "Unique identifier for this task";
|
||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_Delivery)
|
||||||
class DeliveryZone: Edit {
|
class DeliveryZone: Edit {
|
||||||
property = "FORGE_Module_Delivery_DeliveryZone";
|
property = "FORGE_Module_Delivery_DeliveryZone";
|
||||||
displayName = "Delivery Zone Marker";
|
displayName = "Delivery Zone Marker";
|
||||||
@ -942,6 +948,7 @@ class CfgVehicles {
|
|||||||
typeName = "STRING";
|
typeName = "STRING";
|
||||||
// defaultValue = """";
|
// defaultValue = """";
|
||||||
};
|
};
|
||||||
|
TASK_CHAIN_ATTRIBUTES(FORGE_Module_HVT)
|
||||||
class LimitFail: Edit {
|
class LimitFail: Edit {
|
||||||
property = "FORGE_Module_HVT_LimitFail";
|
property = "FORGE_Module_HVT_LimitFail";
|
||||||
displayName = "Fail Limit";
|
displayName = "Fail Limit";
|
||||||
|
|||||||
@ -19,6 +19,7 @@ PREP(initTaskStore);
|
|||||||
PREP_SUBDIR(generators,attackMissionGenerator);
|
PREP_SUBDIR(generators,attackMissionGenerator);
|
||||||
|
|
||||||
PREP_SUBDIR(helpers,handleTaskRewards);
|
PREP_SUBDIR(helpers,handleTaskRewards);
|
||||||
|
PREP_SUBDIR(helpers,parseTaskChainAttributes);
|
||||||
PREP_SUBDIR(helpers,parseRewards);
|
PREP_SUBDIR(helpers,parseRewards);
|
||||||
PREP_SUBDIR(helpers,spawnEnemyWave);
|
PREP_SUBDIR(helpers,spawnEnemyWave);
|
||||||
PREP_SUBDIR(helpers,startTask);
|
PREP_SUBDIR(helpers,startTask);
|
||||||
@ -37,6 +38,12 @@ PREP_SUBDIR(modules,protectedModule);
|
|||||||
PREP_SUBDIR(modules,shootersModule);
|
PREP_SUBDIR(modules,shootersModule);
|
||||||
|
|
||||||
PREP_SUBDIR(objects,TaskInstanceBaseClass);
|
PREP_SUBDIR(objects,TaskInstanceBaseClass);
|
||||||
|
PREP_SUBDIR(objects,TaskStateGateway);
|
||||||
|
PREP_SUBDIR(objects,TaskLifecycleReporter);
|
||||||
|
PREP_SUBDIR(objects,TaskCatalogStore);
|
||||||
|
PREP_SUBDIR(objects,TaskEntityRegistry);
|
||||||
|
PREP_SUBDIR(objects,TaskParticipantTracker);
|
||||||
|
PREP_SUBDIR(objects,TaskRewardService);
|
||||||
PREP_SUBDIR(objects,EntityControllerBaseClass);
|
PREP_SUBDIR(objects,EntityControllerBaseClass);
|
||||||
PREP_SUBDIR(objects,AttackTaskBaseClass);
|
PREP_SUBDIR(objects,AttackTaskBaseClass);
|
||||||
PREP_SUBDIR(objects,HostageTaskBaseClass);
|
PREP_SUBDIR(objects,HostageTaskBaseClass);
|
||||||
|
|||||||
@ -8,6 +8,12 @@ private _category = [QUOTE(MOD_NAME), LLSTRING(displayName)];
|
|||||||
|
|
||||||
#include "initSettings.inc.sqf"
|
#include "initSettings.inc.sqf"
|
||||||
|
|
||||||
|
[] call FUNC(TaskStateGateway);
|
||||||
|
[] call FUNC(TaskLifecycleReporter);
|
||||||
|
[] call FUNC(TaskCatalogStore);
|
||||||
|
[] call FUNC(TaskEntityRegistry);
|
||||||
|
[] call FUNC(TaskParticipantTracker);
|
||||||
|
[] call FUNC(TaskRewardService);
|
||||||
[] call FUNC(TaskInstanceBaseClass);
|
[] call FUNC(TaskInstanceBaseClass);
|
||||||
[] call FUNC(EntityControllerBaseClass);
|
[] call FUNC(EntityControllerBaseClass);
|
||||||
[] call FUNC(AttackTaskBaseClass);
|
[] call FUNC(AttackTaskBaseClass);
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
params [["_taskType", "", [""]], ["_args", [], [[]]], ["_minRating", 0, [0]], ["_requesterUid", "", [""]]];
|
params [["_taskType", "", [""]], ["_args", [], [[]]], ["_minRating", 0, [0]], ["_requesterUid", "", [""]]];
|
||||||
|
|
||||||
private _taskID = "";
|
private _taskID = "";
|
||||||
|
private _shouldStartTaskLogic = true;
|
||||||
|
|
||||||
if (_minRating > 0) then {
|
if (_minRating > 0) then {
|
||||||
if (_requesterUid isEqualTo "") then {
|
if (_requesterUid isEqualTo "") then {
|
||||||
@ -71,9 +72,24 @@ if (_taskID isNotEqualTo "") then {
|
|||||||
]] call EFUNC(common,log);
|
]] call EFUNC(common,log);
|
||||||
};
|
};
|
||||||
|
|
||||||
GVAR(TaskStore) call ["setTaskStatus", [_taskID, "available"]];
|
private _initialStatus = GVAR(TaskStore) call ["resolveInitialTaskStatus", [_taskID, _catalogEntry]];
|
||||||
|
GVAR(TaskStore) call ["setTaskStatus", [_taskID, _initialStatus]];
|
||||||
|
if (_initialStatus isEqualTo "locked") then {
|
||||||
|
["INFO", format ["Task %1 is waiting for chained prerequisites before task logic starts.", _taskID]] call EFUNC(common,log);
|
||||||
|
waitUntil {
|
||||||
|
sleep 2;
|
||||||
|
private _status = GVAR(TaskStore) call ["getTaskStatus", [_taskID]];
|
||||||
|
_status isNotEqualTo "locked"
|
||||||
|
};
|
||||||
|
if ((GVAR(TaskStore) call ["getTaskStatus", [_taskID]]) isEqualTo "") then {
|
||||||
|
_shouldStartTaskLogic = false;
|
||||||
|
["WARNING", format ["Task %1 was cleared before its chained prerequisites unlocked.", _taskID]] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !(_shouldStartTaskLogic) exitWith {};
|
||||||
|
|
||||||
switch (_taskType) do {
|
switch (_taskType) do {
|
||||||
case "attack": {
|
case "attack": {
|
||||||
private _thread = _args spawn FUNC(attack);
|
private _thread = _args spawn FUNC(attack);
|
||||||
|
|||||||
@ -24,600 +24,119 @@
|
|||||||
#pragma hemtt ignore_variables ["_self"]
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
GVAR(TaskStore) = createHashMapObject [[
|
GVAR(TaskStore) = createHashMapObject [[
|
||||||
["#type", "TaskStore"],
|
["#type", "TaskStore"],
|
||||||
["#create", compileFinal {
|
["#create", compileFinal {}],
|
||||||
_self set ["participantRegistry", createHashMap];
|
|
||||||
_self set ["taskLifecycleRegistry", createHashMap];
|
|
||||||
_self set ["taskEntityRegistries", createHashMapFromArray [
|
|
||||||
["cargo", createHashMap],
|
|
||||||
["hostages", createHashMap],
|
|
||||||
["hvts", createHashMap],
|
|
||||||
["ieds", createHashMap],
|
|
||||||
["entities", createHashMap],
|
|
||||||
["shooters", createHashMap],
|
|
||||||
["targets", createHashMap]
|
|
||||||
]];
|
|
||||||
|
|
||||||
}],
|
|
||||||
["resetMissionState", compileFinal {
|
["resetMissionState", compileFinal {
|
||||||
_self set ["participantRegistry", createHashMap];
|
GVAR(TaskLifecycleReporter) call ["resetRuntimeState", []];
|
||||||
_self set ["taskLifecycleRegistry", createHashMap];
|
GVAR(TaskCatalogStore) call ["resetRuntimeState", []];
|
||||||
_self set ["taskEntityRegistries", createHashMapFromArray [
|
GVAR(TaskEntityRegistry) call ["resetRuntimeState", []];
|
||||||
["cargo", createHashMap],
|
GVAR(TaskParticipantTracker) call ["resetRuntimeState", []];
|
||||||
["hostages", createHashMap],
|
|
||||||
["hvts", createHashMap],
|
|
||||||
["ieds", createHashMap],
|
|
||||||
["entities", createHashMap],
|
|
||||||
["shooters", createHashMap],
|
|
||||||
["targets", createHashMap]
|
|
||||||
]];
|
|
||||||
|
|
||||||
["task:reset", []] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
GVAR(TaskStateGateway) call ["reset", []]
|
||||||
if (
|
|
||||||
!_isSuccess
|
|
||||||
|| { !(_result isEqualType "") }
|
|
||||||
|| { (_result find "Error:") == 0 }
|
|
||||||
) exitWith {
|
|
||||||
["WARNING", "Failed to reset task backend state during task store initialization."] call EFUNC(common,log);
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
["INFO", "Task backend state reset for mission lifecycle."] call EFUNC(common,log);
|
|
||||||
true
|
|
||||||
}],
|
|
||||||
["callTaskStateEnvelope", compileFinal {
|
|
||||||
params [["_function", "", [""]], ["_arguments", [], [[]]]];
|
|
||||||
|
|
||||||
private _envelope = createHashMapFromArray [
|
|
||||||
["success", false],
|
|
||||||
["error", ""]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_function isEqualTo "") exitWith { _envelope };
|
|
||||||
|
|
||||||
[_function, _arguments] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
|
||||||
if !_isSuccess exitWith {
|
|
||||||
_envelope set ["error", format ["Task backend call '%1' failed.", _function]];
|
|
||||||
_envelope
|
|
||||||
};
|
|
||||||
if !(_result isEqualType "") exitWith {
|
|
||||||
_envelope set ["error", format ["Task backend call '%1' returned an invalid response.", _function]];
|
|
||||||
_envelope
|
|
||||||
};
|
|
||||||
if ((_result find "Error:") == 0) exitWith {
|
|
||||||
["ERROR", format ["Task extension call '%1' failed: %2", _function, _result]] call EFUNC(common,log);
|
|
||||||
_envelope set ["error", _result select [7]];
|
|
||||||
_envelope
|
|
||||||
};
|
|
||||||
|
|
||||||
_envelope set ["success", true];
|
|
||||||
if (_result isNotEqualTo "") then {
|
|
||||||
_envelope set ["data", fromJSON _result];
|
|
||||||
};
|
|
||||||
|
|
||||||
_envelope
|
|
||||||
}],
|
|
||||||
["callTaskState", compileFinal {
|
|
||||||
params [["_function", "", [""]], ["_arguments", [], [[]]], ["_fallback", nil]];
|
|
||||||
|
|
||||||
private _envelope = _self call ["callTaskStateEnvelope", [_function, _arguments]];
|
|
||||||
if !(_envelope getOrDefault ["success", false]) exitWith { _fallback };
|
|
||||||
|
|
||||||
_envelope getOrDefault ["data", _fallback]
|
|
||||||
}],
|
}],
|
||||||
["bindTaskOwnership", compileFinal {
|
["bindTaskOwnership", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_requesterUid", "", [""]]];
|
params [["_taskID", "", [""]], ["_requesterUid", "", [""]]];
|
||||||
|
GVAR(TaskCatalogStore) call ["bindTaskOwnership", [_taskID, _requesterUid]]
|
||||||
private _result = createHashMapFromArray [
|
|
||||||
["success", false],
|
|
||||||
["requesterUid", _requesterUid],
|
|
||||||
["orgID", "default"],
|
|
||||||
["message", ""]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith {
|
|
||||||
_result set ["message", "Missing task ID."];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
private _orgID = "default";
|
|
||||||
|
|
||||||
if (_requesterUid isNotEqualTo "") then {
|
|
||||||
private _actor = EGVAR(actor,ActorStore) call ["load", [_requesterUid]];
|
|
||||||
|
|
||||||
if (_actor isEqualTo createHashMap) exitWith {
|
|
||||||
_result set ["message", format ["Failed to load actor for %1.", _requesterUid]];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
_orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
|
|
||||||
};
|
|
||||||
|
|
||||||
private _context = createHashMapFromArray [
|
|
||||||
["requesterUid", _requesterUid],
|
|
||||||
["orgId", _orgID]
|
|
||||||
];
|
|
||||||
private _envelope = _self call [
|
|
||||||
"callTaskStateEnvelope",
|
|
||||||
[
|
|
||||||
"task:ownership:bind",
|
|
||||||
[_taskID, toJSON _context]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if !(_envelope getOrDefault ["success", false]) exitWith {
|
|
||||||
_result set ["message", _envelope getOrDefault ["error", "Failed to bind task ownership."]];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
private _bindResult = _envelope getOrDefault ["data", createHashMap];
|
|
||||||
_result set ["success", true];
|
|
||||||
_result set ["message", _bindResult getOrDefault [
|
|
||||||
"message",
|
|
||||||
["No requester UID provided. Bound task to default organization.", "Task ownership updated."] select (_requesterUid isNotEqualTo "")
|
|
||||||
]];
|
|
||||||
_result set ["orgID", _bindResult getOrDefault ["orgId", _orgID]];
|
|
||||||
_result
|
|
||||||
}],
|
}],
|
||||||
["releaseTaskOwnership", compileFinal {
|
["releaseTaskOwnership", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
params [["_taskID", "", [""]]];
|
||||||
|
GVAR(TaskCatalogStore) call ["releaseTaskOwnership", [_taskID]]
|
||||||
if (_taskID isEqualTo "") exitWith { false };
|
|
||||||
|
|
||||||
private _envelope = _self call ["callTaskStateEnvelope", ["task:ownership:release", [_taskID]]];
|
|
||||||
_envelope getOrDefault ["success", false]
|
|
||||||
}],
|
}],
|
||||||
["buildTaskLifecycleEventPayload", compileFinal {
|
["buildTaskLifecycleEventPayload", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_status", "", [""]], ["_extra", createHashMap]];
|
GVAR(TaskLifecycleReporter) call ["buildTaskLifecycleEventPayload", _this]
|
||||||
|
|
||||||
if !(_extra isEqualType createHashMap) then {
|
|
||||||
_extra = createHashMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
private _catalogEntry = _self call ["getTaskCatalogEntry", [_taskID]];
|
|
||||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
|
||||||
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
|
||||||
private _startedAt = _lifecycle getOrDefault ["startedAt", -1];
|
|
||||||
private _finishedAt = _lifecycle getOrDefault ["finishedAt", -1];
|
|
||||||
|
|
||||||
createHashMapFromArray [
|
|
||||||
["taskID", _taskID],
|
|
||||||
["taskType", _catalogEntry getOrDefault ["type", ""]],
|
|
||||||
["title", _catalogEntry getOrDefault ["title", _taskID]],
|
|
||||||
["description", _catalogEntry getOrDefault ["description", ""]],
|
|
||||||
["position", +(_catalogEntry getOrDefault ["position", []])],
|
|
||||||
["status", _status],
|
|
||||||
["source", _catalogEntry getOrDefault ["source", "task"]],
|
|
||||||
["requesterUid", _catalogEntry getOrDefault ["requesterUid", ""]],
|
|
||||||
["orgID", _catalogEntry getOrDefault ["orgID", "default"]],
|
|
||||||
["startedAt", _startedAt],
|
|
||||||
["finishedAt", _finishedAt],
|
|
||||||
["duration", if (_startedAt >= 0 && { _finishedAt >= 0 }) then { _finishedAt - _startedAt } else { -1 }],
|
|
||||||
["failureReason", _extra getOrDefault ["failureReason", ""]],
|
|
||||||
["participants", _self call ["getTaskParticipantUids", [_taskID]]],
|
|
||||||
["rewardData", +(_extra getOrDefault ["rewardData", createHashMap])],
|
|
||||||
["resultSnapshot", +(_extra getOrDefault ["resultSnapshot", createHashMap])],
|
|
||||||
["catalogEntry", +_catalogEntry]
|
|
||||||
]
|
|
||||||
}],
|
}],
|
||||||
["emitTaskLifecycleEvent", compileFinal {
|
["emitTaskLifecycleEvent", compileFinal {
|
||||||
params [["_eventName", "", [""]], ["_taskID", "", [""]], ["_status", "", [""]], ["_extra", createHashMap]];
|
GVAR(TaskLifecycleReporter) call ["emitTaskLifecycleEvent", _this]
|
||||||
|
}],
|
||||||
if (_eventName isEqualTo "" || { _taskID isEqualTo "" }) exitWith { createHashMap };
|
["normalizePrerequisiteTaskIds", compileFinal {
|
||||||
if (isNil QEGVAR(common,EventBus)) exitWith { createHashMap };
|
GVAR(TaskCatalogStore) call ["normalizePrerequisiteTaskIds", _this]
|
||||||
|
}],
|
||||||
EGVAR(common,EventBus) call ["emit", [
|
["getTaskPrerequisites", compileFinal {
|
||||||
_eventName,
|
GVAR(TaskCatalogStore) call ["getTaskPrerequisites", _this]
|
||||||
_self call ["buildTaskLifecycleEventPayload", [_taskID, _status, _extra]],
|
}],
|
||||||
createHashMapFromArray [["source", "task"]]
|
["isTaskCompleted", compileFinal {
|
||||||
]]
|
GVAR(TaskCatalogStore) call ["isTaskCompleted", _this]
|
||||||
|
}],
|
||||||
|
["areTaskPrerequisitesSatisfied", compileFinal {
|
||||||
|
GVAR(TaskCatalogStore) call ["areTaskPrerequisitesSatisfied", _this]
|
||||||
|
}],
|
||||||
|
["resolveInitialTaskStatus", compileFinal {
|
||||||
|
GVAR(TaskCatalogStore) call ["resolveInitialTaskStatus", _this]
|
||||||
|
}],
|
||||||
|
["markTaskCompleted", compileFinal {
|
||||||
|
GVAR(TaskCatalogStore) call ["markTaskCompleted", _this]
|
||||||
|
}],
|
||||||
|
["unlockDependentTasks", compileFinal {
|
||||||
|
GVAR(TaskCatalogStore) call ["unlockDependentTasks", _this]
|
||||||
}],
|
}],
|
||||||
["registerTaskCatalogEntry", compileFinal {
|
["registerTaskCatalogEntry", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_entry", createHashMap, [createHashMap]]];
|
GVAR(TaskCatalogStore) call ["registerTaskCatalogEntry", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "" || { _entry isEqualTo createHashMap }) exitWith { false };
|
|
||||||
|
|
||||||
private _envelope = _self call [
|
|
||||||
"callTaskStateEnvelope",
|
|
||||||
[
|
|
||||||
"task:catalog:upsert",
|
|
||||||
[_taskID, toJSON _entry]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
private _registered = _envelope getOrDefault ["success", false];
|
|
||||||
|
|
||||||
if (_registered) then {
|
|
||||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
|
||||||
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
|
||||||
_lifecycle set ["createdAt", serverTime];
|
|
||||||
_lifecycleRegistry set [_taskID, _lifecycle];
|
|
||||||
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
|
||||||
|
|
||||||
_self call ["emitTaskLifecycleEvent", ["task.created", _taskID, "created", createHashMap]];
|
|
||||||
};
|
|
||||||
|
|
||||||
_registered
|
|
||||||
}],
|
}],
|
||||||
["getActiveTaskCatalog", compileFinal {
|
["getActiveTaskCatalog", compileFinal {
|
||||||
private _entries = _self call ["callTaskState", ["task:catalog:active", [], []]];
|
GVAR(TaskCatalogStore) call ["getActiveTaskCatalog", _this]
|
||||||
if !(_entries isEqualType []) exitWith { [] };
|
|
||||||
|
|
||||||
_entries
|
|
||||||
}],
|
}],
|
||||||
["hasTaskCatalogEntry", compileFinal {
|
["hasTaskCatalogEntry", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskCatalogStore) call ["hasTaskCatalogEntry", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { false };
|
|
||||||
|
|
||||||
private _entry = _self call ["callTaskState", ["task:catalog:get", [_taskID], objNull]];
|
|
||||||
_entry isEqualType createHashMap
|
|
||||||
}],
|
}],
|
||||||
["getTaskCatalogEntry", compileFinal {
|
["getTaskCatalogEntry", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskCatalogStore) call ["getTaskCatalogEntry", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { createHashMap };
|
|
||||||
|
|
||||||
[(_self call ["callTaskState", ["task:catalog:get", [_taskID], createHashMap]])] params [["_entry", createHashMap, [createHashMap]]];
|
|
||||||
if !(_entry isEqualType createHashMap) exitWith { createHashMap };
|
|
||||||
|
|
||||||
_entry
|
|
||||||
}],
|
}],
|
||||||
["isTaskAccepted", compileFinal {
|
["isTaskAccepted", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskCatalogStore) call ["isTaskAccepted", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { false };
|
|
||||||
|
|
||||||
[(_self call ["getTaskCatalogEntry", [_taskID]])] params [["_entry", createHashMap, [createHashMap]]];
|
|
||||||
if (_entry isEqualTo createHashMap) exitWith { false };
|
|
||||||
|
|
||||||
[(_entry getOrDefault ["accepted", false])] params [["_accepted", false, [false]]];
|
|
||||||
[(_entry getOrDefault ["requesterUid", ""])] params [["_requesterUid", "", [""]]];
|
|
||||||
|
|
||||||
_accepted || { _requesterUid isNotEqualTo "" }
|
|
||||||
}],
|
}],
|
||||||
["acceptTask", compileFinal {
|
["acceptTask", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_requesterUid", "", [""]]];
|
GVAR(TaskCatalogStore) call ["acceptTask", _this]
|
||||||
|
|
||||||
private _result = createHashMapFromArray [
|
|
||||||
["success", false],
|
|
||||||
["message", "Unable to accept task."],
|
|
||||||
["entry", createHashMap]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_taskID isEqualTo "" || { _requesterUid isEqualTo "" }) exitWith {
|
|
||||||
_result set ["message", "Missing task ID or requester UID."];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
private _actor = EGVAR(actor,ActorStore) call ["load", [_requesterUid]];
|
|
||||||
if (_actor isEqualTo createHashMap) exitWith {
|
|
||||||
_result set ["message", format ["Failed to load actor for %1.", _requesterUid]];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
|
|
||||||
|
|
||||||
private _context = createHashMapFromArray [
|
|
||||||
["requesterUid", _requesterUid],
|
|
||||||
["orgId", _orgID]
|
|
||||||
];
|
|
||||||
private _envelope = _self call [
|
|
||||||
"callTaskStateEnvelope",
|
|
||||||
[
|
|
||||||
"task:ownership:accept",
|
|
||||||
[_taskID, toJSON _context]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if !(_envelope getOrDefault ["success", false]) exitWith {
|
|
||||||
_result set ["message", _envelope getOrDefault ["error", "Unable to accept task."]];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
private _acceptResult = _envelope getOrDefault ["data", createHashMap];
|
|
||||||
private _entry = _acceptResult getOrDefault ["entry", createHashMap];
|
|
||||||
if !(_entry isEqualType createHashMap) then {
|
|
||||||
_entry = createHashMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
_result set ["success", true];
|
|
||||||
_result set ["message", _acceptResult getOrDefault ["message", "Task accepted."]];
|
|
||||||
_result set ["entry", _entry];
|
|
||||||
_result
|
|
||||||
}],
|
}],
|
||||||
["setTaskStatus", compileFinal {
|
["setTaskStatus", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_status", "", [""]]];
|
GVAR(TaskCatalogStore) call ["setTaskStatus", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "" || { _status isEqualTo "" }) exitWith { false };
|
|
||||||
|
|
||||||
private _envelope = _self call ["callTaskStateEnvelope", ["task:status:set", [_taskID, _status]]];
|
|
||||||
private _statusResult = _envelope getOrDefault ["success", false];
|
|
||||||
|
|
||||||
if (_statusResult) then {
|
|
||||||
private _normalizedStatus = toLowerANSI _status;
|
|
||||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
|
||||||
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
|
||||||
private _eventName = "";
|
|
||||||
|
|
||||||
switch (_normalizedStatus) do {
|
|
||||||
case "active": {
|
|
||||||
_lifecycle set ["startedAt", serverTime];
|
|
||||||
_eventName = "task.started";
|
|
||||||
};
|
|
||||||
case "succeeded": {
|
|
||||||
_lifecycle set ["finishedAt", serverTime];
|
|
||||||
_eventName = "task.completed";
|
|
||||||
};
|
|
||||||
case "failed": {
|
|
||||||
_lifecycle set ["finishedAt", serverTime];
|
|
||||||
_eventName = "task.failed";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
_lifecycleRegistry set [_taskID, _lifecycle];
|
|
||||||
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
|
||||||
|
|
||||||
if (_eventName isNotEqualTo "") then {
|
|
||||||
_self call ["emitTaskLifecycleEvent", [_eventName, _taskID, _normalizedStatus, createHashMap]];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
_statusResult
|
|
||||||
}],
|
}],
|
||||||
["getTaskStatus", compileFinal {
|
["getTaskStatus", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskCatalogStore) call ["getTaskStatus", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { "" };
|
|
||||||
|
|
||||||
private _status = _self call ["callTaskState", ["task:status:get", [_taskID], ""]];
|
|
||||||
if !(_status isEqualType "") exitWith { "" };
|
|
||||||
|
|
||||||
_status
|
|
||||||
}],
|
}],
|
||||||
["clearTaskStatus", compileFinal {
|
["clearTaskStatus", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskCatalogStore) call ["clearTaskStatus", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { false };
|
|
||||||
|
|
||||||
[(_self call ["callTaskState", ["task:status:clear", [_taskID], false]])] params [["_statusResult", false, [false]]];
|
|
||||||
|
|
||||||
_statusResult
|
|
||||||
}],
|
}],
|
||||||
["registerTaskEntity", compileFinal {
|
["registerTaskEntity", compileFinal {
|
||||||
params [["_registryKey", "", [""]], ["_taskID", "", [""]], ["_entity", objNull, [objNull]]];
|
GVAR(TaskEntityRegistry) call ["registerTaskEntity", _this]
|
||||||
|
|
||||||
if (_registryKey isEqualTo "" || { _taskID isEqualTo "" } || { isNull _entity }) exitWith { false };
|
|
||||||
|
|
||||||
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
|
||||||
private _registry = +(_taskEntityRegistries getOrDefault [_registryKey, createHashMap]);
|
|
||||||
private _entities = +(_registry getOrDefault [_taskID, []]);
|
|
||||||
_entities pushBackUnique _entity;
|
|
||||||
_registry set [_taskID, _entities];
|
|
||||||
_taskEntityRegistries set [_registryKey, _registry];
|
|
||||||
_self set ["taskEntityRegistries", _taskEntityRegistries];
|
|
||||||
|
|
||||||
true
|
|
||||||
}],
|
}],
|
||||||
["getTaskEntities", compileFinal {
|
["getTaskEntities", compileFinal {
|
||||||
params [["_registryKey", "", [""]], ["_taskID", "", [""]]];
|
GVAR(TaskEntityRegistry) call ["getTaskEntities", _this]
|
||||||
|
|
||||||
if (_registryKey isEqualTo "" || { _taskID isEqualTo "" }) exitWith { [] };
|
|
||||||
|
|
||||||
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
|
||||||
private _registry = _taskEntityRegistries getOrDefault [_registryKey, createHashMap];
|
|
||||||
|
|
||||||
+(_registry getOrDefault [_taskID, []])
|
|
||||||
}],
|
}],
|
||||||
["findTaskEntityOwner", compileFinal {
|
["findTaskEntityOwner", compileFinal {
|
||||||
params [["_registryKey", "", [""]], ["_entity", objNull, [objNull]]];
|
GVAR(TaskEntityRegistry) call ["findTaskEntityOwner", _this]
|
||||||
|
|
||||||
if (_registryKey isEqualTo "" || { isNull _entity }) exitWith { "" };
|
|
||||||
|
|
||||||
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
|
||||||
private _registry = _taskEntityRegistries getOrDefault [_registryKey, createHashMap];
|
|
||||||
private _resolvedTaskID = "";
|
|
||||||
|
|
||||||
{
|
|
||||||
private _taskID = _x;
|
|
||||||
private _entities = _y;
|
|
||||||
|
|
||||||
if (_entity in _entities) exitWith {
|
|
||||||
_resolvedTaskID = _taskID;
|
|
||||||
};
|
|
||||||
|
|
||||||
private _matchingEntity = _entities select {
|
|
||||||
!isNull _x
|
|
||||||
&& { (typeOf _x) isEqualTo (typeOf _entity) }
|
|
||||||
&& { _x distance _entity < 1 }
|
|
||||||
};
|
|
||||||
if (_matchingEntity isNotEqualTo []) exitWith {
|
|
||||||
_resolvedTaskID = _taskID;
|
|
||||||
};
|
|
||||||
} forEach _registry;
|
|
||||||
|
|
||||||
_resolvedTaskID
|
|
||||||
}],
|
}],
|
||||||
["clearTaskEntities", compileFinal {
|
["clearTaskEntities", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskEntityRegistry) call ["clearTaskEntities", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { false };
|
|
||||||
|
|
||||||
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
|
||||||
|
|
||||||
{
|
|
||||||
private _registry = +_y;
|
|
||||||
_registry deleteAt _taskID;
|
|
||||||
_taskEntityRegistries set [_x, _registry];
|
|
||||||
} forEach _taskEntityRegistries;
|
|
||||||
|
|
||||||
_self set ["taskEntityRegistries", _taskEntityRegistries];
|
|
||||||
true
|
|
||||||
}],
|
}],
|
||||||
["trackParticipants", compileFinal {
|
["trackParticipants", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_entities", [], [[]]], ["_marker", "", [""]], ["_radius", 300, [0]]];
|
GVAR(TaskParticipantTracker) call ["trackParticipants", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { createHashMap };
|
|
||||||
|
|
||||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
|
||||||
private _participantSnapshots = +(_participantRegistry getOrDefault [_taskID, createHashMap]);
|
|
||||||
private _activePlayers = allPlayers select {
|
|
||||||
alive _x
|
|
||||||
&& { side group _x isEqualTo west }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_marker isNotEqualTo "" && { markerShape _marker in ["RECTANGLE", "ELLIPSE"] }) then {
|
|
||||||
{
|
|
||||||
private _uid = getPlayerUID _x;
|
|
||||||
if (_uid isNotEqualTo "" && { _x inArea _marker }) then {
|
|
||||||
if !(_uid in _participantSnapshots) then {
|
|
||||||
_participantSnapshots set [_uid, createHashMapFromArray [
|
|
||||||
["startRating", rating _x]
|
|
||||||
]];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} forEach _activePlayers;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_radius > 0 && { _entities isNotEqualTo [] }) then {
|
|
||||||
{
|
|
||||||
private _entity = _x;
|
|
||||||
if (isNull _entity) then { continue; };
|
|
||||||
|
|
||||||
{
|
|
||||||
private _uid = getPlayerUID _x;
|
|
||||||
if (_uid isNotEqualTo "" && { (_x distance2D _entity) <= _radius }) then {
|
|
||||||
if !(_uid in _participantSnapshots) then {
|
|
||||||
_participantSnapshots set [_uid, createHashMapFromArray [
|
|
||||||
["startRating", rating _x]
|
|
||||||
]];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} forEach _activePlayers;
|
|
||||||
} forEach _entities;
|
|
||||||
};
|
|
||||||
|
|
||||||
_participantRegistry set [_taskID, _participantSnapshots];
|
|
||||||
_self set ["participantRegistry", _participantRegistry];
|
|
||||||
|
|
||||||
_participantSnapshots
|
|
||||||
}],
|
}],
|
||||||
["getTaskParticipants", compileFinal {
|
["getTaskParticipants", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskParticipantTracker) call ["getTaskParticipants", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { createHashMap };
|
|
||||||
|
|
||||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
|
||||||
+(_participantRegistry getOrDefault [_taskID, createHashMap])
|
|
||||||
}],
|
}],
|
||||||
["getTaskParticipantUids", compileFinal {
|
["getTaskParticipantUids", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskParticipantTracker) call ["getTaskParticipantUids", _this]
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { [] };
|
|
||||||
|
|
||||||
keys (_self call ["getTaskParticipants", [_taskID]])
|
|
||||||
}],
|
}],
|
||||||
["resolveRewardContext", compileFinal {
|
["resolveRewardContext", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
GVAR(TaskRewardService) call ["resolveRewardContext", _this]
|
||||||
|
|
||||||
private _result = createHashMapFromArray [
|
|
||||||
["requesterUid", ""],
|
|
||||||
["orgID", ""],
|
|
||||||
["memberUids", []]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { _result };
|
|
||||||
|
|
||||||
private _rewardState = _self call ["callTaskState", ["task:ownership:reward_context", [_taskID], createHashMap]];
|
|
||||||
if (_rewardState isEqualTo createHashMap) exitWith { _result };
|
|
||||||
|
|
||||||
private _requesterUid = _rewardState getOrDefault ["requesterUid", ""];
|
|
||||||
private _resolvedOrgID = _rewardState getOrDefault ["orgId", ""];
|
|
||||||
if (_resolvedOrgID isEqualTo "") exitWith { _result };
|
|
||||||
|
|
||||||
private _org = EGVAR(org,OrgStore) call ["loadById", [_resolvedOrgID]];
|
|
||||||
private _memberUids = [];
|
|
||||||
if (_org isNotEqualTo createHashMap) then {
|
|
||||||
private _members = _org getOrDefault ["members", createHashMap];
|
|
||||||
if (_members isEqualType createHashMap) then {
|
|
||||||
_memberUids = keys _members;
|
|
||||||
};
|
|
||||||
if (_requesterUid isNotEqualTo "" && { !(_requesterUid in _memberUids) }) then {
|
|
||||||
_memberUids pushBack _requesterUid;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
_result set ["requesterUid", _requesterUid];
|
|
||||||
_result set ["orgID", _resolvedOrgID];
|
|
||||||
_result set ["memberUids", _memberUids];
|
|
||||||
_result
|
|
||||||
}],
|
}],
|
||||||
["incrementDefuseCount", compileFinal {
|
["incrementDefuseCount", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { 0 };
|
if (_taskID isEqualTo "") exitWith { 0 };
|
||||||
|
|
||||||
["task:defuse:increment", [_taskID]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
[GVAR(TaskStateGateway) call ["callTaskState", ["task:defuse:increment", [_taskID], 0]]] params [["_count", 0, [0]]];
|
||||||
|
_count
|
||||||
if !_isSuccess exitWith { 0 };
|
|
||||||
if !(_result isEqualType "") exitWith { 0 };
|
|
||||||
if ((_result find "Error:") == 0) exitWith {
|
|
||||||
["ERROR", format ["Task extension call 'task:defuse:increment' failed: %1", _result]] call EFUNC(common,log);
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
parseNumber _result
|
|
||||||
}],
|
}],
|
||||||
["getDefuseCount", compileFinal {
|
["getDefuseCount", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { 0 };
|
if (_taskID isEqualTo "") exitWith { 0 };
|
||||||
|
|
||||||
["task:defuse:get", [_taskID]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
[GVAR(TaskStateGateway) call ["callTaskState", ["task:defuse:get", [_taskID], 0]]] params [["_count", 0, [0]]];
|
||||||
if !_isSuccess exitWith { 0 };
|
_count
|
||||||
if !(_result isEqualType "") exitWith { 0 };
|
|
||||||
if ((_result find "Error:") == 0) exitWith {
|
|
||||||
["ERROR", format ["Task extension call 'task:defuse:get' failed: %1", _result]] call EFUNC(common,log);
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
parseNumber _result
|
|
||||||
}],
|
}],
|
||||||
["notifyParticipants", compileFinal {
|
["notifyParticipants", compileFinal {
|
||||||
params [
|
GVAR(TaskParticipantTracker) call ["notifyParticipants", _this]
|
||||||
["_taskID", "", [""]],
|
|
||||||
["_type", "info", [""]],
|
|
||||||
["_title", "Tasks", [""]],
|
|
||||||
["_message", "", [""]]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_taskID isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
|
||||||
|
|
||||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
|
||||||
private _participantSnapshots = +(_participantRegistry getOrDefault [_taskID, createHashMap]);
|
|
||||||
if (_participantSnapshots isEqualTo createHashMap) exitWith { false };
|
|
||||||
|
|
||||||
private _participantUids = keys _participantSnapshots;
|
|
||||||
if (_participantUids isEqualTo []) exitWith { false };
|
|
||||||
|
|
||||||
if (isNil QEGVAR(common,EventBus)) exitWith {
|
|
||||||
{
|
|
||||||
private _player = [_x] call EFUNC(common,getPlayer);
|
|
||||||
if (isNull _player) then { continue; };
|
|
||||||
[CRPC(notifications,recieveNotification), [_type, _title, _message], _player] call CFUNC(targetEvent);
|
|
||||||
} forEach _participantUids;
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
EGVAR(common,EventBus) call ["emit", [
|
|
||||||
"task.notification.requested",
|
|
||||||
createHashMapFromArray [
|
|
||||||
["taskID", _taskID],
|
|
||||||
["notificationType", _type],
|
|
||||||
["title", _title],
|
|
||||||
["message", _message],
|
|
||||||
["participantUids", _participantUids]
|
|
||||||
],
|
|
||||||
createHashMapFromArray [["source", "task"]]
|
|
||||||
]];
|
|
||||||
|
|
||||||
true
|
|
||||||
}],
|
}],
|
||||||
["clearTask", compileFinal {
|
["clearTask", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
params [["_taskID", "", [""]]];
|
||||||
@ -626,246 +145,14 @@ GVAR(TaskStore) = createHashMapObject [[
|
|||||||
|
|
||||||
_self call ["emitTaskLifecycleEvent", ["task.cleared", _taskID, "cleared", createHashMap]];
|
_self call ["emitTaskLifecycleEvent", ["task.cleared", _taskID, "cleared", createHashMap]];
|
||||||
|
|
||||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
GVAR(TaskLifecycleReporter) call ["clearTaskLifecycle", [_taskID]];
|
||||||
_participantRegistry deleteAt _taskID;
|
GVAR(TaskParticipantTracker) call ["clearTaskParticipants", [_taskID]];
|
||||||
_self set ["participantRegistry", _participantRegistry];
|
GVAR(TaskStateGateway) call ["callTaskState", ["task:clear", [_taskID], false]];
|
||||||
|
|
||||||
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
|
||||||
_lifecycleRegistry deleteAt _taskID;
|
|
||||||
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
|
||||||
|
|
||||||
_self call ["callTaskState", ["task:clear", [_taskID], false]];
|
|
||||||
_self call ["clearTaskEntities", [_taskID]];
|
_self call ["clearTaskEntities", [_taskID]];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["applyRatingOutcome", compileFinal {
|
["applyRatingOutcome", compileFinal {
|
||||||
params [["_taskID", "", [""]], ["_delta", 0, [0]]];
|
GVAR(TaskRewardService) call ["applyRatingOutcome", _this]
|
||||||
|
|
||||||
private _emitRatingEvent = {
|
|
||||||
params [["_eventName", "", [""]], ["_payload", createHashMap, [createHashMap]]];
|
|
||||||
|
|
||||||
if (_eventName isEqualTo "" || { isNil QEGVAR(common,EventBus) }) exitWith { createHashMap };
|
|
||||||
|
|
||||||
private _eventPayload = +_payload;
|
|
||||||
_eventPayload set ["taskID", _taskID];
|
|
||||||
_eventPayload set ["ratingDelta", _delta];
|
|
||||||
|
|
||||||
EGVAR(common,EventBus) call ["emit", [
|
|
||||||
_eventName,
|
|
||||||
_eventPayload,
|
|
||||||
createHashMapFromArray [["source", "task"]]
|
|
||||||
]]
|
|
||||||
};
|
|
||||||
|
|
||||||
private _result = createHashMapFromArray [
|
|
||||||
["participantUids", []],
|
|
||||||
["orgIds", []],
|
|
||||||
["contributions", createHashMap],
|
|
||||||
["success", true],
|
|
||||||
["mutationFailures", []],
|
|
||||||
["persistenceFailures", []],
|
|
||||||
["message", ""]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_taskID isEqualTo "" || { _delta isEqualTo 0 }) exitWith { _result };
|
|
||||||
|
|
||||||
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
|
||||||
private _participantSnapshots = +(_participantRegistry getOrDefault [_taskID, createHashMap]);
|
|
||||||
if (_participantSnapshots isEqualTo createHashMap) exitWith { _result };
|
|
||||||
|
|
||||||
private _rewardContext = _self call ["resolveRewardContext", [_taskID]];
|
|
||||||
private _participantUids = keys _participantSnapshots;
|
|
||||||
if (_participantUids isEqualTo [] && { _delta > 0 }) then {
|
|
||||||
private _requesterUid = _rewardContext getOrDefault ["requesterUid", ""];
|
|
||||||
if (_requesterUid isNotEqualTo "") then {
|
|
||||||
private _requesterPlayer = [_requesterUid] call EFUNC(common,getPlayer);
|
|
||||||
if (!isNull _requesterPlayer) then {
|
|
||||||
_participantUids pushBack _requesterUid;
|
|
||||||
_participantSnapshots set [_requesterUid, createHashMapFromArray [
|
|
||||||
["startRating", rating _requesterPlayer]
|
|
||||||
]];
|
|
||||||
_participantRegistry set [_taskID, _participantSnapshots];
|
|
||||||
_self set ["participantRegistry", _participantRegistry];
|
|
||||||
["WARNING", format ["Task %1 had no tracked participants at payout time; falling back to requester %2 for personal earnings.", _taskID, _requesterUid]] call EFUNC(common,log);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
if (_participantUids isEqualTo []) exitWith {
|
|
||||||
_result set ["success", false];
|
|
||||||
_result set ["message", "No task participants were available for rating outcome."];
|
|
||||||
["task.rating.failed", createHashMapFromArray [
|
|
||||||
["participantUids", []],
|
|
||||||
["orgIds", []],
|
|
||||||
["contributions", createHashMap],
|
|
||||||
["mutationFailures", []],
|
|
||||||
["persistenceFailures", []],
|
|
||||||
["message", _result get "message"]
|
|
||||||
]] call _emitRatingEvent;
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
private _orgIds = [];
|
|
||||||
private _contributions = createHashMap;
|
|
||||||
private _totalContribution = 0;
|
|
||||||
private _mutationFailures = [];
|
|
||||||
private _persistenceFailures = [];
|
|
||||||
|
|
||||||
if (_delta > 0) then {
|
|
||||||
{
|
|
||||||
private _uid = _x;
|
|
||||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
|
||||||
if (isNull _player) then { continue; };
|
|
||||||
|
|
||||||
_contributions set [_uid, 1];
|
|
||||||
_totalContribution = _totalContribution + 1;
|
|
||||||
} forEach _participantUids;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_totalContribution <= 0) exitWith {
|
|
||||||
_result set ["success", false];
|
|
||||||
_result set ["message", "No eligible participant contribution was available for rating outcome."];
|
|
||||||
["task.rating.failed", createHashMapFromArray [
|
|
||||||
["participantUids", +_participantUids],
|
|
||||||
["orgIds", +_orgIds],
|
|
||||||
["contributions", +_contributions],
|
|
||||||
["mutationFailures", []],
|
|
||||||
["persistenceFailures", []],
|
|
||||||
["message", _result get "message"]
|
|
||||||
]] call _emitRatingEvent;
|
|
||||||
_self call ["clearTask", [_taskID]];
|
|
||||||
_result
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
private _uid = _x;
|
|
||||||
private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid, ""]];
|
|
||||||
if (_orgID isNotEqualTo "") then {
|
|
||||||
_orgIds pushBackUnique _orgID;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_delta > 0) then {
|
|
||||||
private _contribution = _contributions getOrDefault [_uid, 0];
|
|
||||||
if (_contribution <= 0) then { continue; };
|
|
||||||
|
|
||||||
private _account = EGVAR(bank,BankStore) call ["get", [_uid, ""]];
|
|
||||||
if (_account isEqualTo createHashMap) then {
|
|
||||||
_account = EGVAR(bank,BankStore) call ["init", [_uid]];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_account isNotEqualTo createHashMap) then {
|
|
||||||
private _earnings = _account getOrDefault ["earnings", 0];
|
|
||||||
private _earningsDelta = round ((_delta * _contribution) / _totalContribution);
|
|
||||||
if (_earningsDelta <= 0) then { continue; };
|
|
||||||
|
|
||||||
private _patch = EGVAR(bank,BankStore) call [
|
|
||||||
"mset",
|
|
||||||
[
|
|
||||||
_uid,
|
|
||||||
createHashMapFromArray [["earnings", (_earnings + _earningsDelta)]],
|
|
||||||
false
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if !(_patch isEqualType createHashMap) then { continue; };
|
|
||||||
if (_patch isEqualTo createHashMap) then { continue; };
|
|
||||||
|
|
||||||
if (isNil QEGVAR(common,EventBus)) then {
|
|
||||||
EGVAR(bank,BankMessenger) call ["sendAccountSync", [_uid, _patch]];
|
|
||||||
} else {
|
|
||||||
EGVAR(common,EventBus) call ["emit", [
|
|
||||||
"bank.account.sync.requested",
|
|
||||||
createHashMapFromArray [
|
|
||||||
["uid", _uid],
|
|
||||||
["account", +_patch]
|
|
||||||
],
|
|
||||||
createHashMapFromArray [["source", "task"]]
|
|
||||||
]];
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((EGVAR(bank,BankStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
|
|
||||||
_persistenceFailures pushBackUnique format ["bank:%1", _uid];
|
|
||||||
["ERROR", format ["Task %1 updated bank earnings for %2, but durable save failed.", _taskID, _uid]] call EFUNC(common,log);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} forEach _participantUids;
|
|
||||||
|
|
||||||
private _ownerOrgID = _rewardContext getOrDefault ["orgID", ""];
|
|
||||||
if (_ownerOrgID isNotEqualTo "") then {
|
|
||||||
private _org = EGVAR(org,OrgStore) call ["loadById", [_ownerOrgID]];
|
|
||||||
|
|
||||||
if (_org isNotEqualTo createHashMap) then {
|
|
||||||
private _reputation = _org getOrDefault ["reputation", 0];
|
|
||||||
private _nextReputation = round (_reputation + _delta);
|
|
||||||
_org set ["reputation", _nextReputation];
|
|
||||||
private _updatedOrg = EGVAR(org,OrgStore) call [
|
|
||||||
"callHotOrg",
|
|
||||||
[
|
|
||||||
"org:hot:override",
|
|
||||||
[_ownerOrgID, toJSON _org]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
if (_updatedOrg isNotEqualTo createHashMap) then {
|
|
||||||
private _patch = createHashMapFromArray [["reputation", _nextReputation]];
|
|
||||||
private _memberUids = _rewardContext getOrDefault ["memberUids", []];
|
|
||||||
if (isNil QEGVAR(common,EventBus)) then {
|
|
||||||
{
|
|
||||||
private _player = [_x] call EFUNC(common,getPlayer);
|
|
||||||
if (isNull _player) then { continue; };
|
|
||||||
[CRPC(org,responseSyncOrg), [_patch], _player] call CFUNC(targetEvent);
|
|
||||||
} forEach _memberUids;
|
|
||||||
} else {
|
|
||||||
EGVAR(common,EventBus) call ["emit", [
|
|
||||||
"org.sync.requested",
|
|
||||||
createHashMapFromArray [
|
|
||||||
["orgID", _ownerOrgID],
|
|
||||||
["memberUids", +_memberUids],
|
|
||||||
["patch", +_patch]
|
|
||||||
],
|
|
||||||
createHashMapFromArray [["source", "task"]]
|
|
||||||
]];
|
|
||||||
};
|
|
||||||
|
|
||||||
_orgIds = [_ownerOrgID];
|
|
||||||
if ((EGVAR(org,OrgStore) call ["saveById", [_ownerOrgID]]) isEqualTo createHashMap) then {
|
|
||||||
_persistenceFailures pushBackUnique format ["organization:%1", _ownerOrgID];
|
|
||||||
["ERROR", format ["Task %1 updated reputation for organization %2, but durable save failed.", _taskID, _ownerOrgID]] call EFUNC(common,log);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
["ERROR", format ["Failed to update organization %1 reputation for task %2.", _ownerOrgID, _taskID]] call EFUNC(common,log);
|
|
||||||
_mutationFailures pushBackUnique format ["organization:%1", _ownerOrgID];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
_result set ["participantUids", _participantUids];
|
|
||||||
_result set ["orgIds", _orgIds];
|
|
||||||
_result set ["contributions", _contributions];
|
|
||||||
_result set ["success", (_mutationFailures isEqualTo []) && { _persistenceFailures isEqualTo [] }];
|
|
||||||
_result set ["mutationFailures", _mutationFailures];
|
|
||||||
_result set ["persistenceFailures", _persistenceFailures];
|
|
||||||
if (_mutationFailures isNotEqualTo [] || { _persistenceFailures isNotEqualTo [] }) then {
|
|
||||||
private _messageParts = [];
|
|
||||||
if (_mutationFailures isNotEqualTo []) then {
|
|
||||||
_messageParts pushBack format ["mutation failures: %1", _mutationFailures joinString ", "];
|
|
||||||
};
|
|
||||||
if (_persistenceFailures isNotEqualTo []) then {
|
|
||||||
_messageParts pushBack format ["persistence failures: %1", _persistenceFailures joinString ", "];
|
|
||||||
};
|
|
||||||
_result set ["message", _messageParts joinString "; "];
|
|
||||||
};
|
|
||||||
|
|
||||||
private _eventName = ["task.rating.failed", "task.rating.applied"] select (_result getOrDefault ["success", false]);
|
|
||||||
[_eventName, createHashMapFromArray [
|
|
||||||
["participantUids", +(_result getOrDefault ["participantUids", []])],
|
|
||||||
["orgIds", +(_result getOrDefault ["orgIds", []])],
|
|
||||||
["contributions", +(_result getOrDefault ["contributions", createHashMap])],
|
|
||||||
["mutationFailures", +(_result getOrDefault ["mutationFailures", []])],
|
|
||||||
["persistenceFailures", +(_result getOrDefault ["persistenceFailures", []])],
|
|
||||||
["message", _result getOrDefault ["message", ""]]
|
|
||||||
]] call _emitRatingEvent;
|
|
||||||
|
|
||||||
_result
|
|
||||||
}]
|
}]
|
||||||
]];
|
]];
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Author: OpenAI
|
* Author: OpenAI
|
||||||
* Parses an Eden module reward-array string into a SQF array.
|
* Parses an Eden module reward string into a SQF array.
|
||||||
|
*
|
||||||
|
* Supports both the preferred comma-separated format:
|
||||||
|
* ItemGPS, FirstAidKit
|
||||||
|
* and the legacy SQF array string format:
|
||||||
|
* ["ItemGPS","FirstAidKit"]
|
||||||
*
|
*
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* 0: Raw value <STRING>
|
* 0: Raw value <STRING>
|
||||||
@ -13,28 +18,35 @@
|
|||||||
* Parsed reward array <ARRAY>
|
* Parsed reward array <ARRAY>
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* [_logic getVariable ["EquipmentRewards", "[]"], "attack_01", "equipment"] call forge_server_task_fnc_parseRewards;
|
* [_logic getVariable ["EquipmentRewards", ""], "attack_01", "equipment"] call forge_server_task_fnc_parseRewards;
|
||||||
*
|
*
|
||||||
* Public: No
|
* Public: No
|
||||||
*/
|
*/
|
||||||
|
|
||||||
params [
|
params [["_rawValue", "", [""]], ["_taskLabel", "", [""]], ["_rewardKey", "", [""]]];
|
||||||
["_rawValue", "[]", [""]],
|
|
||||||
["_taskLabel", "", [""]],
|
|
||||||
["_rewardKey", "", [""]]
|
|
||||||
];
|
|
||||||
|
|
||||||
private _trimmed = trim _rawValue;
|
private _trimmed = trim _rawValue;
|
||||||
if (_trimmed isEqualTo "") exitWith { [] };
|
if (_trimmed isEqualTo "") exitWith { [] };
|
||||||
|
if ((_trimmed select [0, 1]) isEqualTo "[") then {
|
||||||
|
private _parsed = parseSimpleArray _trimmed;
|
||||||
|
if (_parsed isEqualType []) exitWith { _parsed };
|
||||||
|
|
||||||
private _parsed = parseSimpleArray _trimmed;
|
["WARNING", format [
|
||||||
if (_parsed isEqualType []) exitWith { _parsed };
|
"Task module '%1' reward input '%2' is invalid: %3. Expected comma-separated class names like ItemGPS, FirstAidKit or SQF array syntax like [""ItemGPS"",""FirstAidKit""].",
|
||||||
|
_taskLabel,
|
||||||
|
_rewardKey,
|
||||||
|
_rawValue
|
||||||
|
]] call EFUNC(common,log);
|
||||||
|
|
||||||
["WARNING", format [
|
[]
|
||||||
"Task module '%1' reward input '%2' is invalid: %3. Expected SQF array syntax like [""ItemGPS"",""FirstAidKit""].",
|
};
|
||||||
_taskLabel,
|
|
||||||
_rewardKey,
|
|
||||||
_rawValue
|
|
||||||
]] call EFUNC(common,log);
|
|
||||||
|
|
||||||
[]
|
private _parsedRewards = [];
|
||||||
|
{
|
||||||
|
private _reward = trim _x;
|
||||||
|
if (_reward isEqualTo "") then { continue; };
|
||||||
|
|
||||||
|
_parsedRewards pushBackUnique _reward;
|
||||||
|
} forEach (_trimmed splitString ",");
|
||||||
|
|
||||||
|
_parsedRewards
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Reads shared Eden task chain attributes and returns startTask parameter pairs.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* 0: Logic <OBJECT>
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task parameter pairs <ARRAY>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
params [["_logic", objNull, [objNull]]];
|
||||||
|
|
||||||
|
private _prerequisiteRaw = _logic getVariable ["PrerequisiteTaskIds", ""];
|
||||||
|
private _prerequisiteTaskIds = [];
|
||||||
|
|
||||||
|
if (_prerequisiteRaw isEqualType []) then {
|
||||||
|
{
|
||||||
|
if !(_x isEqualType "") then { continue; };
|
||||||
|
if (_x isEqualTo "") then { continue; };
|
||||||
|
_prerequisiteTaskIds pushBackUnique _x;
|
||||||
|
} forEach _prerequisiteRaw;
|
||||||
|
} else {
|
||||||
|
if (_prerequisiteRaw isEqualType "") then {
|
||||||
|
{
|
||||||
|
if (_x isEqualTo "") then { continue; };
|
||||||
|
_prerequisiteTaskIds pushBackUnique _x;
|
||||||
|
} forEach (_prerequisiteRaw splitString ", ");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
[
|
||||||
|
["prerequisiteTaskIds", _prerequisiteTaskIds]
|
||||||
|
]
|
||||||
@ -24,6 +24,7 @@
|
|||||||
* Common keys:
|
* Common keys:
|
||||||
* "limitFail" <NUMBER> (default: -1)
|
* "limitFail" <NUMBER> (default: -1)
|
||||||
* "limitSuccess" <NUMBER> (default: -1)
|
* "limitSuccess" <NUMBER> (default: -1)
|
||||||
|
* "prerequisiteTaskIds" <ARRAY|STRING> (default: []) -- task IDs that must succeed before this task is available
|
||||||
* "funds" <NUMBER> (default: 0)
|
* "funds" <NUMBER> (default: 0)
|
||||||
* "ratingFail" <NUMBER> (default: 0)
|
* "ratingFail" <NUMBER> (default: 0)
|
||||||
* "ratingSuccess" <NUMBER> (default: 0)
|
* "ratingSuccess" <NUMBER> (default: 0)
|
||||||
@ -123,7 +124,17 @@ private _iedTimer = _taskParams getOrDefault ["iedTimer", 0];
|
|||||||
|
|
||||||
// --- 3. Register catalog entry ---
|
// --- 3. Register catalog entry ---
|
||||||
|
|
||||||
|
private _prerequisiteTaskIds = _taskParams getOrDefault [
|
||||||
|
"prerequisiteTaskIds",
|
||||||
|
_taskParams getOrDefault [
|
||||||
|
"prerequisiteTaskIDs",
|
||||||
|
_taskParams getOrDefault ["requiresTaskIds", []]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArray [
|
GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArray [
|
||||||
|
["taskID", _taskID],
|
||||||
|
["taskId", _taskID],
|
||||||
["type", _taskType],
|
["type", _taskType],
|
||||||
["title", _title],
|
["title", _title],
|
||||||
["description", _description],
|
["description", _description],
|
||||||
@ -131,7 +142,8 @@ GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArr
|
|||||||
["accepted", false],
|
["accepted", false],
|
||||||
["requesterUid", _requesterUid],
|
["requesterUid", _requesterUid],
|
||||||
["orgID", "default"],
|
["orgID", "default"],
|
||||||
["source", _source]
|
["source", _source],
|
||||||
|
["prerequisiteTaskIds", _prerequisiteTaskIds]
|
||||||
]]];
|
]]];
|
||||||
|
|
||||||
// --- 4. Assemble type-specific handler args ---
|
// --- 4. Assemble type-specific handler args ---
|
||||||
|
|||||||
@ -41,6 +41,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"attack",
|
"attack",
|
||||||
@ -51,7 +52,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
createHashMapFromArray [
|
createHashMapFromArray [
|
||||||
["targets", _syncedEntities]
|
["targets", _syncedEntities]
|
||||||
],
|
],
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["limitFail", _logic getVariable ["LimitFail", -1]],
|
["limitFail", _logic getVariable ["LimitFail", -1]],
|
||||||
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
@ -65,7 +66,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -84,6 +84,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"defend",
|
"defend",
|
||||||
@ -92,7 +93,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
format ["Defend: %1", _taskID],
|
format ["Defend: %1", _taskID],
|
||||||
"Hold the defense zone against incoming enemy forces.",
|
"Hold the defense zone against incoming enemy forces.",
|
||||||
createHashMap,
|
createHashMap,
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
["ratingFail", _logic getVariable ["RatingFail", 0]],
|
["ratingFail", _logic getVariable ["RatingFail", 0]],
|
||||||
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
|
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
|
||||||
@ -109,7 +110,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -58,6 +58,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"defuse",
|
"defuse",
|
||||||
@ -69,7 +70,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["ieds", _iedEntities],
|
["ieds", _iedEntities],
|
||||||
["protected", _protectedEntities]
|
["protected", _protectedEntities]
|
||||||
],
|
],
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["limitFail", _logic getVariable ["LimitFail", -1]],
|
["limitFail", _logic getVariable ["LimitFail", -1]],
|
||||||
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
@ -83,7 +84,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -51,6 +51,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"delivery",
|
"delivery",
|
||||||
@ -61,7 +62,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
createHashMapFromArray [
|
createHashMapFromArray [
|
||||||
["cargo", _cargoEntities]
|
["cargo", _cargoEntities]
|
||||||
],
|
],
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["limitFail", _logic getVariable ["LimitFail", -1]],
|
["limitFail", _logic getVariable ["LimitFail", -1]],
|
||||||
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
@ -76,7 +77,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -41,6 +41,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"destroy",
|
"destroy",
|
||||||
@ -51,7 +52,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
createHashMapFromArray [
|
createHashMapFromArray [
|
||||||
["targets", _syncedEntities]
|
["targets", _syncedEntities]
|
||||||
],
|
],
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["limitFail", _logic getVariable ["LimitFail", -1]],
|
["limitFail", _logic getVariable ["LimitFail", -1]],
|
||||||
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
@ -65,7 +66,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -66,6 +66,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"hostage",
|
"hostage",
|
||||||
@ -77,7 +78,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["hostages", _hostageEntities],
|
["hostages", _hostageEntities],
|
||||||
["shooters", _shooterEntities]
|
["shooters", _shooterEntities]
|
||||||
],
|
],
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["limitFail", _logic getVariable ["LimitFail", -1]],
|
["limitFail", _logic getVariable ["LimitFail", -1]],
|
||||||
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
@ -95,7 +96,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -47,6 +47,7 @@ private _supplyRewards = [_logic getVariable ["SupplyRewards", "[]"], _taskID, "
|
|||||||
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
private _weaponRewards = [_logic getVariable ["WeaponRewards", "[]"], _taskID, "weapons"] call FUNC(parseRewards);
|
||||||
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
private _vehicleRewards = [_logic getVariable ["VehicleRewards", "[]"], _taskID, "vehicles"] call FUNC(parseRewards);
|
||||||
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID, "special"] call FUNC(parseRewards);
|
||||||
|
private _taskChainParams = [_logic] call FUNC(parseTaskChainAttributes);
|
||||||
|
|
||||||
[
|
[
|
||||||
"hvt",
|
"hvt",
|
||||||
@ -57,7 +58,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
createHashMapFromArray [
|
createHashMapFromArray [
|
||||||
["hvts", _syncedEntities]
|
["hvts", _syncedEntities]
|
||||||
],
|
],
|
||||||
createHashMapFromArray [
|
createHashMapFromArray ([
|
||||||
["limitFail", _logic getVariable ["LimitFail", -1]],
|
["limitFail", _logic getVariable ["LimitFail", -1]],
|
||||||
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
|
||||||
["funds", _logic getVariable ["CompanyFunds", 0]],
|
["funds", _logic getVariable ["CompanyFunds", 0]],
|
||||||
@ -73,7 +74,7 @@ private _specialRewards = [_logic getVariable ["SpecialRewards", "[]"], _taskID,
|
|||||||
["weapons", _weaponRewards],
|
["weapons", _weaponRewards],
|
||||||
["vehicles", _vehicleRewards],
|
["vehicles", _vehicleRewards],
|
||||||
["special", _specialRewards]
|
["special", _specialRewards]
|
||||||
]
|
] + _taskChainParams)
|
||||||
] call FUNC(startTask);
|
] call FUNC(startTask);
|
||||||
|
|
||||||
deleteVehicle _logic;
|
deleteVehicle _logic;
|
||||||
|
|||||||
@ -0,0 +1,356 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Catalog/status/chaining store for task metadata.
|
||||||
|
*
|
||||||
|
* TaskStore keeps the public facade used by the rest of the task system. This
|
||||||
|
* object owns catalog persistence calls, active status, acceptance, and chained
|
||||||
|
* task availability.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task catalog store object <HASHMAP OBJECT>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(TaskCatalogStore) = createHashMapObject [[
|
||||||
|
["#type", "TaskCatalogStore"],
|
||||||
|
["#create", compileFinal {
|
||||||
|
_self call ["resetRuntimeState", []];
|
||||||
|
}],
|
||||||
|
["resetRuntimeState", compileFinal {
|
||||||
|
_self set ["completedTaskRegistry", createHashMap];
|
||||||
|
_self set ["taskDependencyRegistry", createHashMap];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["bindTaskOwnership", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_requesterUid", "", [""]]];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["requesterUid", _requesterUid],
|
||||||
|
["orgID", "default"],
|
||||||
|
["message", ""]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith {
|
||||||
|
_result set ["message", "Missing task ID."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _orgID = "default";
|
||||||
|
|
||||||
|
if (_requesterUid isNotEqualTo "") then {
|
||||||
|
private _actor = EGVAR(actor,ActorStore) call ["load", [_requesterUid]];
|
||||||
|
|
||||||
|
if (_actor isEqualTo createHashMap) exitWith {
|
||||||
|
_result set ["message", format ["Failed to load actor for %1.", _requesterUid]];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
_orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
|
||||||
|
};
|
||||||
|
|
||||||
|
private _context = createHashMapFromArray [
|
||||||
|
["requesterUid", _requesterUid],
|
||||||
|
["orgId", _orgID]
|
||||||
|
];
|
||||||
|
private _envelope = GVAR(TaskStateGateway) call [
|
||||||
|
"callTaskStateEnvelope",
|
||||||
|
[
|
||||||
|
"task:ownership:bind",
|
||||||
|
[_taskID, toJSON _context]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if !(_envelope getOrDefault ["success", false]) exitWith {
|
||||||
|
_result set ["message", _envelope getOrDefault ["error", "Failed to bind task ownership."]];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _bindResult = _envelope getOrDefault ["data", createHashMap];
|
||||||
|
_result set ["success", true];
|
||||||
|
_result set ["message", _bindResult getOrDefault [
|
||||||
|
"message",
|
||||||
|
["No requester UID provided. Bound task to default organization.", "Task ownership updated."] select (_requesterUid isNotEqualTo "")
|
||||||
|
]];
|
||||||
|
_result set ["orgID", _bindResult getOrDefault ["orgId", _orgID]];
|
||||||
|
_result
|
||||||
|
}],
|
||||||
|
["releaseTaskOwnership", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _envelope = GVAR(TaskStateGateway) call ["callTaskStateEnvelope", ["task:ownership:release", [_taskID]]];
|
||||||
|
_envelope getOrDefault ["success", false]
|
||||||
|
}],
|
||||||
|
["normalizePrerequisiteTaskIds", compileFinal {
|
||||||
|
params [["_value", [], [[], ""]]];
|
||||||
|
|
||||||
|
if (_value isEqualType "") then { _value = [_value]; };
|
||||||
|
if !(_value isEqualType []) exitWith { [] };
|
||||||
|
|
||||||
|
private _taskIDs = [];
|
||||||
|
{
|
||||||
|
if !(_x isEqualType "") then { continue; };
|
||||||
|
if (_x isEqualTo "") then { continue; };
|
||||||
|
_taskIDs pushBackUnique _x;
|
||||||
|
} forEach _value;
|
||||||
|
|
||||||
|
_taskIDs
|
||||||
|
}],
|
||||||
|
["getTaskPrerequisites", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { [] };
|
||||||
|
|
||||||
|
private _dependencyRegistry = _self getOrDefault ["taskDependencyRegistry", createHashMap];
|
||||||
|
+(_dependencyRegistry getOrDefault [_taskID, []])
|
||||||
|
}],
|
||||||
|
["isTaskCompleted", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _completedRegistry = _self getOrDefault ["completedTaskRegistry", createHashMap];
|
||||||
|
if (_completedRegistry getOrDefault [_taskID, false]) exitWith { true };
|
||||||
|
|
||||||
|
(_self call ["getTaskStatus", [_taskID]]) isEqualTo "succeeded"
|
||||||
|
}],
|
||||||
|
["areTaskPrerequisitesSatisfied", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_entry", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
private _prerequisites = _self call ["getTaskPrerequisites", [_taskID]];
|
||||||
|
if (_prerequisites isEqualTo [] && { _entry isNotEqualTo createHashMap }) then {
|
||||||
|
_prerequisites = _self call ["normalizePrerequisiteTaskIds", [_entry getOrDefault ["prerequisiteTaskIds", []]]];
|
||||||
|
};
|
||||||
|
if (_prerequisites isEqualTo []) exitWith { true };
|
||||||
|
|
||||||
|
private _satisfied = true;
|
||||||
|
{
|
||||||
|
if !(_self call ["isTaskCompleted", [_x]]) exitWith { _satisfied = false; };
|
||||||
|
} forEach _prerequisites;
|
||||||
|
|
||||||
|
_satisfied
|
||||||
|
}],
|
||||||
|
["resolveInitialTaskStatus", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_entry", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
if (_self call ["areTaskPrerequisitesSatisfied", [_taskID, _entry]]) exitWith { "available" };
|
||||||
|
|
||||||
|
"locked"
|
||||||
|
}],
|
||||||
|
["markTaskCompleted", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _completedRegistry = _self getOrDefault ["completedTaskRegistry", createHashMap];
|
||||||
|
_completedRegistry set [_taskID, true];
|
||||||
|
_self set ["completedTaskRegistry", _completedRegistry];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["unlockDependentTasks", compileFinal {
|
||||||
|
params [["_completedTaskID", "", [""]]];
|
||||||
|
|
||||||
|
private _dependencyRegistry = _self getOrDefault ["taskDependencyRegistry", createHashMap];
|
||||||
|
{
|
||||||
|
private _dependentTaskID = _x;
|
||||||
|
private _prerequisites = _y;
|
||||||
|
|
||||||
|
if !(_completedTaskID in _prerequisites) then { continue; };
|
||||||
|
if ((_self call ["getTaskStatus", [_dependentTaskID]]) isNotEqualTo "locked") then { continue; };
|
||||||
|
if !(_self call ["areTaskPrerequisitesSatisfied", [_dependentTaskID]]) then { continue; };
|
||||||
|
|
||||||
|
_self call ["setTaskStatus", [_dependentTaskID, "available"]];
|
||||||
|
["INFO", format ["Unlocked chained task '%1' after prerequisite '%2' completed.", _dependentTaskID, _completedTaskID]] call EFUNC(common,log);
|
||||||
|
} forEach _dependencyRegistry;
|
||||||
|
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["registerTaskCatalogEntry", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_entry", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _entry isEqualTo createHashMap }) exitWith { false };
|
||||||
|
|
||||||
|
_entry set ["taskID", _taskID];
|
||||||
|
_entry set ["taskId", _taskID];
|
||||||
|
|
||||||
|
private _prerequisiteTaskIds = _self call ["normalizePrerequisiteTaskIds", [_entry getOrDefault ["prerequisiteTaskIds", []]]];
|
||||||
|
_entry set ["prerequisiteTaskIds", _prerequisiteTaskIds];
|
||||||
|
|
||||||
|
private _dependencyRegistry = _self getOrDefault ["taskDependencyRegistry", createHashMap];
|
||||||
|
if (_prerequisiteTaskIds isEqualTo []) then {
|
||||||
|
_dependencyRegistry deleteAt _taskID;
|
||||||
|
} else {
|
||||||
|
_dependencyRegistry set [_taskID, _prerequisiteTaskIds];
|
||||||
|
};
|
||||||
|
_self set ["taskDependencyRegistry", _dependencyRegistry];
|
||||||
|
|
||||||
|
private _initialStatus = ["available", "locked"] select !(_self call ["areTaskPrerequisitesSatisfied", [_taskID, _entry]]);
|
||||||
|
_entry set ["locked", _initialStatus isEqualTo "locked"];
|
||||||
|
|
||||||
|
private _envelope = GVAR(TaskStateGateway) call [
|
||||||
|
"callTaskStateEnvelope",
|
||||||
|
[
|
||||||
|
"task:catalog:upsert",
|
||||||
|
[_taskID, toJSON _entry]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
private _registered = _envelope getOrDefault ["success", false];
|
||||||
|
|
||||||
|
if (_registered) then {
|
||||||
|
GVAR(TaskLifecycleReporter) call ["recordTaskCreated", [_taskID]];
|
||||||
|
GVAR(TaskLifecycleReporter) call ["emitTaskLifecycleEvent", ["task.created", _taskID, "created", createHashMap]];
|
||||||
|
_self call ["setTaskStatus", [_taskID, _initialStatus]];
|
||||||
|
};
|
||||||
|
|
||||||
|
_registered
|
||||||
|
}],
|
||||||
|
["getActiveTaskCatalog", compileFinal {
|
||||||
|
private _entries = GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:active", [], []]];
|
||||||
|
if !(_entries isEqualType []) exitWith { [] };
|
||||||
|
|
||||||
|
private _visibleEntries = [];
|
||||||
|
{
|
||||||
|
if !(_x isEqualType createHashMap) then { continue; };
|
||||||
|
|
||||||
|
private _taskID = _x getOrDefault ["taskID", _x getOrDefault ["taskId", ""]];
|
||||||
|
if (_taskID isEqualTo "") then { continue; };
|
||||||
|
|
||||||
|
private _status = _self call ["getTaskStatus", [_taskID]];
|
||||||
|
if !(_status in ["available", "assigned", "active"]) then { continue; };
|
||||||
|
if !(_self call ["areTaskPrerequisitesSatisfied", [_taskID, _x]]) then { continue; };
|
||||||
|
|
||||||
|
_visibleEntries pushBack _x;
|
||||||
|
} forEach _entries;
|
||||||
|
|
||||||
|
_visibleEntries
|
||||||
|
}],
|
||||||
|
["hasTaskCatalogEntry", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _entry = GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:get", [_taskID], objNull]];
|
||||||
|
_entry isEqualType createHashMap
|
||||||
|
}],
|
||||||
|
["getTaskCatalogEntry", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { createHashMap };
|
||||||
|
|
||||||
|
[(GVAR(TaskStateGateway) call ["callTaskState", ["task:catalog:get", [_taskID], createHashMap]])] params [["_entry", createHashMap, [createHashMap]]];
|
||||||
|
if !(_entry isEqualType createHashMap) exitWith { createHashMap };
|
||||||
|
|
||||||
|
_entry
|
||||||
|
}],
|
||||||
|
["isTaskAccepted", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
[(_self call ["getTaskCatalogEntry", [_taskID]])] params [["_entry", createHashMap, [createHashMap]]];
|
||||||
|
if (_entry isEqualTo createHashMap) exitWith { false };
|
||||||
|
|
||||||
|
[(_entry getOrDefault ["accepted", false])] params [["_accepted", false, [false]]];
|
||||||
|
[(_entry getOrDefault ["requesterUid", ""])] params [["_requesterUid", "", [""]]];
|
||||||
|
|
||||||
|
_accepted || { _requesterUid isNotEqualTo "" }
|
||||||
|
}],
|
||||||
|
["acceptTask", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_requesterUid", "", [""]]];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["message", "Unable to accept task."],
|
||||||
|
["entry", createHashMap]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _requesterUid isEqualTo "" }) exitWith {
|
||||||
|
_result set ["message", "Missing task ID or requester UID."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _actor = EGVAR(actor,ActorStore) call ["load", [_requesterUid]];
|
||||||
|
if (_actor isEqualTo createHashMap) exitWith {
|
||||||
|
_result set ["message", format ["Failed to load actor for %1.", _requesterUid]];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_requesterUid]];
|
||||||
|
|
||||||
|
private _context = createHashMapFromArray [
|
||||||
|
["requesterUid", _requesterUid],
|
||||||
|
["orgId", _orgID]
|
||||||
|
];
|
||||||
|
private _envelope = GVAR(TaskStateGateway) call [
|
||||||
|
"callTaskStateEnvelope",
|
||||||
|
[
|
||||||
|
"task:ownership:accept",
|
||||||
|
[_taskID, toJSON _context]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if !(_envelope getOrDefault ["success", false]) exitWith {
|
||||||
|
_result set ["message", _envelope getOrDefault ["error", "Unable to accept task."]];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _acceptResult = _envelope getOrDefault ["data", createHashMap];
|
||||||
|
private _entry = _acceptResult getOrDefault ["entry", createHashMap];
|
||||||
|
if !(_entry isEqualType createHashMap) then { _entry = createHashMap; };
|
||||||
|
|
||||||
|
_result set ["success", true];
|
||||||
|
_result set ["message", _acceptResult getOrDefault ["message", "Task accepted."]];
|
||||||
|
_result set ["entry", _entry];
|
||||||
|
_result
|
||||||
|
}],
|
||||||
|
["setTaskStatus", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_status", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _status isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
private _envelope = GVAR(TaskStateGateway) call ["callTaskStateEnvelope", ["task:status:set", [_taskID, _status]]];
|
||||||
|
private _statusResult = _envelope getOrDefault ["success", false];
|
||||||
|
|
||||||
|
if (_statusResult) then {
|
||||||
|
private _normalizedStatus = toLowerANSI _status;
|
||||||
|
private _eventName = GVAR(TaskLifecycleReporter) call ["recordTaskStatus", [_taskID, _normalizedStatus]];
|
||||||
|
|
||||||
|
if (_eventName isNotEqualTo "") then {
|
||||||
|
GVAR(TaskLifecycleReporter) call ["emitTaskLifecycleEvent", [_eventName, _taskID, _normalizedStatus, createHashMap]];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_normalizedStatus isEqualTo "succeeded") then {
|
||||||
|
_self call ["markTaskCompleted", [_taskID]];
|
||||||
|
_self call ["unlockDependentTasks", [_taskID]];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
_statusResult
|
||||||
|
}],
|
||||||
|
["getTaskStatus", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { "" };
|
||||||
|
|
||||||
|
[(GVAR(TaskStateGateway) call ["callTaskState", ["task:status:get", [_taskID], ""]])] params [["_status", "", [""]]];
|
||||||
|
_status
|
||||||
|
}],
|
||||||
|
["clearTaskStatus", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
[(GVAR(TaskStateGateway) call ["callTaskState", ["task:status:clear", [_taskID], false]])] params [["_statusResult", false, [false]]];
|
||||||
|
|
||||||
|
_statusResult
|
||||||
|
}]
|
||||||
|
]];
|
||||||
|
|
||||||
|
GVAR(TaskCatalogStore)
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Runtime entity registry for task-owned Arma objects.
|
||||||
|
*
|
||||||
|
* Stores object references by registry key and task ID. TaskStore remains the
|
||||||
|
* public facade, while this object owns entity storage and lookup behavior.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task entity registry object <HASHMAP OBJECT>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(TaskEntityRegistry) = createHashMapObject [[
|
||||||
|
["#type", "TaskEntityRegistry"],
|
||||||
|
["#create", compileFinal {
|
||||||
|
_self call ["resetRuntimeState", []];
|
||||||
|
}],
|
||||||
|
["resetRuntimeState", compileFinal {
|
||||||
|
_self set ["taskEntityRegistries", createHashMapFromArray [
|
||||||
|
["cargo", createHashMap],
|
||||||
|
["hostages", createHashMap],
|
||||||
|
["hvts", createHashMap],
|
||||||
|
["ieds", createHashMap],
|
||||||
|
["entities", createHashMap],
|
||||||
|
["shooters", createHashMap],
|
||||||
|
["targets", createHashMap]
|
||||||
|
]];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["registerTaskEntity", compileFinal {
|
||||||
|
params [["_registryKey", "", [""]], ["_taskID", "", [""]], ["_entity", objNull, [objNull]]];
|
||||||
|
|
||||||
|
if (_registryKey isEqualTo "" || { _taskID isEqualTo "" } || { isNull _entity }) exitWith { false };
|
||||||
|
|
||||||
|
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
||||||
|
private _registry = +(_taskEntityRegistries getOrDefault [_registryKey, createHashMap]);
|
||||||
|
private _entities = +(_registry getOrDefault [_taskID, []]);
|
||||||
|
_entities pushBackUnique _entity;
|
||||||
|
_registry set [_taskID, _entities];
|
||||||
|
_taskEntityRegistries set [_registryKey, _registry];
|
||||||
|
_self set ["taskEntityRegistries", _taskEntityRegistries];
|
||||||
|
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["getTaskEntities", compileFinal {
|
||||||
|
params [["_registryKey", "", [""]], ["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_registryKey isEqualTo "" || { _taskID isEqualTo "" }) exitWith { [] };
|
||||||
|
|
||||||
|
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
||||||
|
private _registry = _taskEntityRegistries getOrDefault [_registryKey, createHashMap];
|
||||||
|
|
||||||
|
+(_registry getOrDefault [_taskID, []])
|
||||||
|
}],
|
||||||
|
["findTaskEntityOwner", compileFinal {
|
||||||
|
params [["_registryKey", "", [""]], ["_entity", objNull, [objNull]]];
|
||||||
|
|
||||||
|
if (_registryKey isEqualTo "" || { isNull _entity }) exitWith { "" };
|
||||||
|
|
||||||
|
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
||||||
|
private _registry = _taskEntityRegistries getOrDefault [_registryKey, createHashMap];
|
||||||
|
private _resolvedTaskID = "";
|
||||||
|
|
||||||
|
{
|
||||||
|
private _taskID = _x;
|
||||||
|
private _entities = _y;
|
||||||
|
|
||||||
|
if (_entity in _entities) exitWith { _resolvedTaskID = _taskID; };
|
||||||
|
|
||||||
|
private _matchingEntity = _entities select {
|
||||||
|
!isNull _x
|
||||||
|
&& { (typeOf _x) isEqualTo (typeOf _entity) }
|
||||||
|
&& { _x distance _entity < 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_matchingEntity isNotEqualTo []) exitWith { _resolvedTaskID = _taskID; };
|
||||||
|
} forEach _registry;
|
||||||
|
|
||||||
|
_resolvedTaskID
|
||||||
|
}],
|
||||||
|
["clearTaskEntities", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _taskEntityRegistries = _self getOrDefault ["taskEntityRegistries", createHashMap];
|
||||||
|
|
||||||
|
{
|
||||||
|
private _registry = +_y;
|
||||||
|
_registry deleteAt _taskID;
|
||||||
|
_taskEntityRegistries set [_x, _registry];
|
||||||
|
} forEach _taskEntityRegistries;
|
||||||
|
|
||||||
|
_self set ["taskEntityRegistries", _taskEntityRegistries];
|
||||||
|
true
|
||||||
|
}]
|
||||||
|
]];
|
||||||
|
|
||||||
|
GVAR(TaskEntityRegistry)
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Task lifecycle timestamp tracking and event reporting.
|
||||||
|
*
|
||||||
|
* Owns task lifecycle timestamps and emits task lifecycle events through the
|
||||||
|
* common event bus. TaskStore remains the public facade.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task lifecycle reporter object <HASHMAP OBJECT>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(TaskLifecycleReporter) = createHashMapObject [[
|
||||||
|
["#type", "TaskLifecycleReporter"],
|
||||||
|
["#create", compileFinal {
|
||||||
|
_self call ["resetRuntimeState", []];
|
||||||
|
}],
|
||||||
|
["resetRuntimeState", compileFinal {
|
||||||
|
_self set ["taskLifecycleRegistry", createHashMap];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["recordTaskCreated", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||||
|
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
_lifecycle set ["createdAt", serverTime];
|
||||||
|
_lifecycleRegistry set [_taskID, _lifecycle];
|
||||||
|
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["recordTaskStatus", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_status", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _status isEqualTo "" }) exitWith { "" };
|
||||||
|
|
||||||
|
private _normalizedStatus = toLowerANSI _status;
|
||||||
|
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||||
|
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
private _eventName = "";
|
||||||
|
|
||||||
|
switch (_normalizedStatus) do {
|
||||||
|
case "active": {
|
||||||
|
_lifecycle set ["startedAt", serverTime];
|
||||||
|
_eventName = "task.started";
|
||||||
|
};
|
||||||
|
case "succeeded": {
|
||||||
|
_lifecycle set ["finishedAt", serverTime];
|
||||||
|
_eventName = "task.completed";
|
||||||
|
};
|
||||||
|
case "failed": {
|
||||||
|
_lifecycle set ["finishedAt", serverTime];
|
||||||
|
_eventName = "task.failed";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
_lifecycleRegistry set [_taskID, _lifecycle];
|
||||||
|
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
||||||
|
|
||||||
|
_eventName
|
||||||
|
}],
|
||||||
|
["clearTaskLifecycle", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||||
|
_lifecycleRegistry deleteAt _taskID;
|
||||||
|
_self set ["taskLifecycleRegistry", _lifecycleRegistry];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["buildTaskLifecycleEventPayload", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_status", "", [""]], ["_extra", createHashMap]];
|
||||||
|
|
||||||
|
if !(_extra isEqualType createHashMap) then {
|
||||||
|
_extra = createHashMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
private _catalogEntry = GVAR(TaskCatalogStore) call ["getTaskCatalogEntry", [_taskID]];
|
||||||
|
private _lifecycleRegistry = _self getOrDefault ["taskLifecycleRegistry", createHashMap];
|
||||||
|
private _lifecycle = +(_lifecycleRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
private _startedAt = _lifecycle getOrDefault ["startedAt", -1];
|
||||||
|
private _finishedAt = _lifecycle getOrDefault ["finishedAt", -1];
|
||||||
|
|
||||||
|
createHashMapFromArray [
|
||||||
|
["taskID", _taskID],
|
||||||
|
["taskType", _catalogEntry getOrDefault ["type", ""]],
|
||||||
|
["title", _catalogEntry getOrDefault ["title", _taskID]],
|
||||||
|
["description", _catalogEntry getOrDefault ["description", ""]],
|
||||||
|
["position", +(_catalogEntry getOrDefault ["position", []])],
|
||||||
|
["status", _status],
|
||||||
|
["source", _catalogEntry getOrDefault ["source", "task"]],
|
||||||
|
["requesterUid", _catalogEntry getOrDefault ["requesterUid", ""]],
|
||||||
|
["orgID", _catalogEntry getOrDefault ["orgID", "default"]],
|
||||||
|
["startedAt", _startedAt],
|
||||||
|
["finishedAt", _finishedAt],
|
||||||
|
["duration", if (_startedAt >= 0 && { _finishedAt >= 0 }) then { _finishedAt - _startedAt } else { -1 }],
|
||||||
|
["failureReason", _extra getOrDefault ["failureReason", ""]],
|
||||||
|
["participants", GVAR(TaskParticipantTracker) call ["getTaskParticipantUids", [_taskID]]],
|
||||||
|
["rewardData", +(_extra getOrDefault ["rewardData", createHashMap])],
|
||||||
|
["resultSnapshot", +(_extra getOrDefault ["resultSnapshot", createHashMap])],
|
||||||
|
["catalogEntry", +_catalogEntry]
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
["emitTaskLifecycleEvent", compileFinal {
|
||||||
|
params [["_eventName", "", [""]], ["_taskID", "", [""]], ["_status", "", [""]], ["_extra", createHashMap]];
|
||||||
|
|
||||||
|
if (_eventName isEqualTo "" || { _taskID isEqualTo "" }) exitWith { createHashMap };
|
||||||
|
if (isNil QEGVAR(common,EventBus)) exitWith { createHashMap };
|
||||||
|
|
||||||
|
EGVAR(common,EventBus) call ["emit", [
|
||||||
|
_eventName,
|
||||||
|
_self call ["buildTaskLifecycleEventPayload", [_taskID, _status, _extra]],
|
||||||
|
createHashMapFromArray [["source", "task"]]
|
||||||
|
]]
|
||||||
|
}]
|
||||||
|
]];
|
||||||
|
|
||||||
|
GVAR(TaskLifecycleReporter)
|
||||||
@ -0,0 +1,155 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Runtime participant tracking and task notification fanout.
|
||||||
|
*
|
||||||
|
* TaskStore remains the public facade, while this object owns participant
|
||||||
|
* snapshots keyed by task ID.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task participant tracker object <HASHMAP OBJECT>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(TaskParticipantTracker) = createHashMapObject [[
|
||||||
|
["#type", "TaskParticipantTracker"],
|
||||||
|
["#create", compileFinal {
|
||||||
|
_self call ["resetRuntimeState", []];
|
||||||
|
}],
|
||||||
|
["resetRuntimeState", compileFinal {
|
||||||
|
_self set ["participantRegistry", createHashMap];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["trackParticipants", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_entities", [], [[]]], ["_marker", "", [""]], ["_radius", 300, [0]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { createHashMap };
|
||||||
|
|
||||||
|
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
||||||
|
private _participantSnapshots = +(_participantRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
private _activePlayers = allPlayers select {
|
||||||
|
alive _x
|
||||||
|
&& { side group _x isEqualTo west }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_marker isNotEqualTo "" && { markerShape _marker in ["RECTANGLE", "ELLIPSE"] }) then {
|
||||||
|
{
|
||||||
|
private _uid = getPlayerUID _x;
|
||||||
|
if (_uid isNotEqualTo "" && { _x inArea _marker }) then {
|
||||||
|
if !(_uid in _participantSnapshots) then {
|
||||||
|
_participantSnapshots set [_uid, createHashMapFromArray [
|
||||||
|
["startRating", rating _x]
|
||||||
|
]];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} forEach _activePlayers;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_radius > 0 && { _entities isNotEqualTo [] }) then {
|
||||||
|
{
|
||||||
|
private _entity = _x;
|
||||||
|
if (isNull _entity) then { continue; };
|
||||||
|
|
||||||
|
{
|
||||||
|
private _uid = getPlayerUID _x;
|
||||||
|
if (_uid isNotEqualTo "" && { (_x distance2D _entity) <= _radius }) then {
|
||||||
|
if !(_uid in _participantSnapshots) then {
|
||||||
|
_participantSnapshots set [_uid, createHashMapFromArray [
|
||||||
|
["startRating", rating _x]
|
||||||
|
]];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} forEach _activePlayers;
|
||||||
|
} forEach _entities;
|
||||||
|
};
|
||||||
|
|
||||||
|
_participantRegistry set [_taskID, _participantSnapshots];
|
||||||
|
_self set ["participantRegistry", _participantRegistry];
|
||||||
|
|
||||||
|
_participantSnapshots
|
||||||
|
}],
|
||||||
|
["recordParticipant", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_uid", "", [""]], ["_snapshot", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _uid isEqualTo "" }) exitWith { createHashMap };
|
||||||
|
|
||||||
|
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
||||||
|
private _participantSnapshots = +(_participantRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
_participantSnapshots set [_uid, +_snapshot];
|
||||||
|
_participantRegistry set [_taskID, _participantSnapshots];
|
||||||
|
_self set ["participantRegistry", _participantRegistry];
|
||||||
|
|
||||||
|
_participantSnapshots
|
||||||
|
}],
|
||||||
|
["getTaskParticipants", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { createHashMap };
|
||||||
|
|
||||||
|
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
||||||
|
+(_participantRegistry getOrDefault [_taskID, createHashMap])
|
||||||
|
}],
|
||||||
|
["getTaskParticipantUids", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { [] };
|
||||||
|
|
||||||
|
keys (_self call ["getTaskParticipants", [_taskID]])
|
||||||
|
}],
|
||||||
|
["clearTaskParticipants", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _participantRegistry = _self getOrDefault ["participantRegistry", createHashMap];
|
||||||
|
_participantRegistry deleteAt _taskID;
|
||||||
|
_self set ["participantRegistry", _participantRegistry];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["notifyParticipants", compileFinal {
|
||||||
|
params [
|
||||||
|
["_taskID", "", [""]],
|
||||||
|
["_type", "info", [""]],
|
||||||
|
["_title", "Tasks", [""]],
|
||||||
|
["_message", "", [""]]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
private _participantSnapshots = _self call ["getTaskParticipants", [_taskID]];
|
||||||
|
if (_participantSnapshots isEqualTo createHashMap) exitWith { false };
|
||||||
|
|
||||||
|
private _participantUids = keys _participantSnapshots;
|
||||||
|
if (_participantUids isEqualTo []) exitWith { false };
|
||||||
|
if (isNil QEGVAR(common,EventBus)) exitWith {
|
||||||
|
{
|
||||||
|
private _player = [_x] call EFUNC(common,getPlayer);
|
||||||
|
if (isNull _player) then { continue; };
|
||||||
|
[CRPC(notifications,recieveNotification), [_type, _title, _message], _player] call CFUNC(targetEvent);
|
||||||
|
} forEach _participantUids;
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
EGVAR(common,EventBus) call ["emit", [
|
||||||
|
"task.notification.requested",
|
||||||
|
createHashMapFromArray [
|
||||||
|
["taskID", _taskID],
|
||||||
|
["notificationType", _type],
|
||||||
|
["title", _title],
|
||||||
|
["message", _message],
|
||||||
|
["participantUids", _participantUids]
|
||||||
|
],
|
||||||
|
createHashMapFromArray [["source", "task"]]
|
||||||
|
]];
|
||||||
|
|
||||||
|
true
|
||||||
|
}]
|
||||||
|
]];
|
||||||
|
|
||||||
|
GVAR(TaskParticipantTracker)
|
||||||
@ -0,0 +1,276 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Task reward and rating outcome service.
|
||||||
|
*
|
||||||
|
* Resolves task ownership reward context and applies player earnings plus
|
||||||
|
* organization reputation outcomes. TaskStore remains the public facade.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task reward service object <HASHMAP OBJECT>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(TaskRewardService) = createHashMapObject [[
|
||||||
|
["#type", "TaskRewardService"],
|
||||||
|
["resolveRewardContext", compileFinal {
|
||||||
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["requesterUid", ""],
|
||||||
|
["orgID", ""],
|
||||||
|
["memberUids", []]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { _result };
|
||||||
|
|
||||||
|
private _rewardState = GVAR(TaskStateGateway) call ["callTaskState", ["task:ownership:reward_context", [_taskID], createHashMap]];
|
||||||
|
if (_rewardState isEqualTo createHashMap) exitWith { _result };
|
||||||
|
|
||||||
|
private _requesterUid = _rewardState getOrDefault ["requesterUid", ""];
|
||||||
|
private _resolvedOrgID = _rewardState getOrDefault ["orgId", ""];
|
||||||
|
if (_resolvedOrgID isEqualTo "") exitWith { _result };
|
||||||
|
|
||||||
|
private _org = EGVAR(org,OrgStore) call ["loadById", [_resolvedOrgID]];
|
||||||
|
private _memberUids = [];
|
||||||
|
if (_org isNotEqualTo createHashMap) then {
|
||||||
|
private _members = _org getOrDefault ["members", createHashMap];
|
||||||
|
|
||||||
|
if (_members isEqualType createHashMap) then { _memberUids = keys _members; };
|
||||||
|
if (_requesterUid isNotEqualTo "" && { !(_requesterUid in _memberUids) }) then { _memberUids pushBack _requesterUid; };
|
||||||
|
};
|
||||||
|
|
||||||
|
_result set ["requesterUid", _requesterUid];
|
||||||
|
_result set ["orgID", _resolvedOrgID];
|
||||||
|
_result set ["memberUids", _memberUids];
|
||||||
|
_result
|
||||||
|
}],
|
||||||
|
["applyRatingOutcome", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_delta", 0, [0]]];
|
||||||
|
|
||||||
|
private _emitRatingEvent = {
|
||||||
|
params [["_eventName", "", [""]], ["_payload", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
if (_eventName isEqualTo "" || { isNil QEGVAR(common,EventBus) }) exitWith { createHashMap };
|
||||||
|
|
||||||
|
private _eventPayload = +_payload;
|
||||||
|
_eventPayload set ["taskID", _taskID];
|
||||||
|
_eventPayload set ["ratingDelta", _delta];
|
||||||
|
|
||||||
|
EGVAR(common,EventBus) call ["emit", [
|
||||||
|
_eventName,
|
||||||
|
_eventPayload,
|
||||||
|
createHashMapFromArray [["source", "task"]]
|
||||||
|
]]
|
||||||
|
};
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["participantUids", []],
|
||||||
|
["orgIds", []],
|
||||||
|
["contributions", createHashMap],
|
||||||
|
["success", true],
|
||||||
|
["mutationFailures", []],
|
||||||
|
["persistenceFailures", []],
|
||||||
|
["message", ""]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _delta isEqualTo 0 }) exitWith { _result };
|
||||||
|
|
||||||
|
private _participantSnapshots = GVAR(TaskParticipantTracker) call ["getTaskParticipants", [_taskID]];
|
||||||
|
if (_participantSnapshots isEqualTo createHashMap) exitWith { _result };
|
||||||
|
|
||||||
|
private _rewardContext = _self call ["resolveRewardContext", [_taskID]];
|
||||||
|
private _participantUids = keys _participantSnapshots;
|
||||||
|
if (_participantUids isEqualTo [] && { _delta > 0 }) then {
|
||||||
|
private _requesterUid = _rewardContext getOrDefault ["requesterUid", ""];
|
||||||
|
if (_requesterUid isNotEqualTo "") then {
|
||||||
|
private _requesterPlayer = [_requesterUid] call EFUNC(common,getPlayer);
|
||||||
|
if (!isNull _requesterPlayer) then {
|
||||||
|
_participantUids pushBack _requesterUid;
|
||||||
|
_participantSnapshots = GVAR(TaskParticipantTracker) call ["recordParticipant", [_taskID, _requesterUid, createHashMapFromArray [
|
||||||
|
["startRating", rating _requesterPlayer]
|
||||||
|
]]];
|
||||||
|
["WARNING", format ["Task %1 had no tracked participants at payout time; falling back to requester %2 for personal earnings.", _taskID, _requesterUid]] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (_participantUids isEqualTo []) exitWith {
|
||||||
|
_result set ["success", false];
|
||||||
|
_result set ["message", "No task participants were available for rating outcome."];
|
||||||
|
["task.rating.failed", createHashMapFromArray [
|
||||||
|
["participantUids", []],
|
||||||
|
["orgIds", []],
|
||||||
|
["contributions", createHashMap],
|
||||||
|
["mutationFailures", []],
|
||||||
|
["persistenceFailures", []],
|
||||||
|
["message", _result get "message"]
|
||||||
|
]] call _emitRatingEvent;
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _orgIds = [];
|
||||||
|
private _contributions = createHashMap;
|
||||||
|
private _totalContribution = 0;
|
||||||
|
private _mutationFailures = [];
|
||||||
|
private _persistenceFailures = [];
|
||||||
|
|
||||||
|
if (_delta > 0) then {
|
||||||
|
{
|
||||||
|
private _uid = _x;
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (isNull _player) then { continue; };
|
||||||
|
|
||||||
|
_contributions set [_uid, 1];
|
||||||
|
_totalContribution = _totalContribution + 1;
|
||||||
|
} forEach _participantUids;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_totalContribution <= 0) exitWith {
|
||||||
|
_result set ["success", false];
|
||||||
|
_result set ["message", "No eligible participant contribution was available for rating outcome."];
|
||||||
|
["task.rating.failed", createHashMapFromArray [
|
||||||
|
["participantUids", +_participantUids],
|
||||||
|
["orgIds", +_orgIds],
|
||||||
|
["contributions", +_contributions],
|
||||||
|
["mutationFailures", []],
|
||||||
|
["persistenceFailures", []],
|
||||||
|
["message", _result get "message"]
|
||||||
|
]] call _emitRatingEvent;
|
||||||
|
GVAR(TaskStore) call ["clearTask", [_taskID]];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
private _uid = _x;
|
||||||
|
private _orgID = EGVAR(actor,ActorStore) call ["getOrganization", [_uid, ""]];
|
||||||
|
if (_orgID isNotEqualTo "") then { _orgIds pushBackUnique _orgID; };
|
||||||
|
if (_delta > 0) then {
|
||||||
|
private _contribution = _contributions getOrDefault [_uid, 0];
|
||||||
|
if (_contribution <= 0) then { continue; };
|
||||||
|
|
||||||
|
private _account = EGVAR(bank,BankStore) call ["get", [_uid, ""]];
|
||||||
|
if (_account isEqualTo createHashMap) then { _account = EGVAR(bank,BankStore) call ["init", [_uid]]; };
|
||||||
|
if (_account isNotEqualTo createHashMap) then {
|
||||||
|
private _earnings = _account getOrDefault ["earnings", 0];
|
||||||
|
private _earningsDelta = round ((_delta * _contribution) / _totalContribution);
|
||||||
|
if (_earningsDelta <= 0) then { continue; };
|
||||||
|
|
||||||
|
private _patch = EGVAR(bank,BankStore) call [
|
||||||
|
"mset",
|
||||||
|
[
|
||||||
|
_uid,
|
||||||
|
createHashMapFromArray [["earnings", (_earnings + _earningsDelta)]],
|
||||||
|
false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if !(_patch isEqualType createHashMap) then { continue; };
|
||||||
|
if (_patch isEqualTo createHashMap) then { continue; };
|
||||||
|
if (isNil QEGVAR(common,EventBus)) then {
|
||||||
|
EGVAR(bank,BankMessenger) call ["sendAccountSync", [_uid, _patch]];
|
||||||
|
} else {
|
||||||
|
EGVAR(common,EventBus) call ["emit", [
|
||||||
|
"bank.account.sync.requested",
|
||||||
|
createHashMapFromArray [
|
||||||
|
["uid", _uid],
|
||||||
|
["account", +_patch]
|
||||||
|
],
|
||||||
|
createHashMapFromArray [["source", "task"]]
|
||||||
|
]];
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((EGVAR(bank,BankStore) call ["save", [_uid]]) isEqualTo createHashMap) then {
|
||||||
|
_persistenceFailures pushBackUnique format ["bank:%1", _uid];
|
||||||
|
["ERROR", format ["Task %1 updated bank earnings for %2, but durable save failed.", _taskID, _uid]] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} forEach _participantUids;
|
||||||
|
|
||||||
|
private _ownerOrgID = _rewardContext getOrDefault ["orgID", ""];
|
||||||
|
if (_ownerOrgID isNotEqualTo "") then {
|
||||||
|
private _org = EGVAR(org,OrgStore) call ["loadById", [_ownerOrgID]];
|
||||||
|
|
||||||
|
if (_org isNotEqualTo createHashMap) then {
|
||||||
|
private _reputation = _org getOrDefault ["reputation", 0];
|
||||||
|
private _nextReputation = round (_reputation + _delta);
|
||||||
|
_org set ["reputation", _nextReputation];
|
||||||
|
private _updatedOrg = EGVAR(org,OrgStore) call [
|
||||||
|
"callHotOrg",
|
||||||
|
[
|
||||||
|
"org:hot:override",
|
||||||
|
[_ownerOrgID, toJSON _org]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_updatedOrg isNotEqualTo createHashMap) then {
|
||||||
|
private _patch = createHashMapFromArray [["reputation", _nextReputation]];
|
||||||
|
private _memberUids = _rewardContext getOrDefault ["memberUids", []];
|
||||||
|
if (isNil QEGVAR(common,EventBus)) then {
|
||||||
|
{
|
||||||
|
private _player = [_x] call EFUNC(common,getPlayer);
|
||||||
|
if (isNull _player) then { continue; };
|
||||||
|
[CRPC(org,responseSyncOrg), [_patch], _player] call CFUNC(targetEvent);
|
||||||
|
} forEach _memberUids;
|
||||||
|
} else {
|
||||||
|
EGVAR(common,EventBus) call ["emit", [
|
||||||
|
"org.sync.requested",
|
||||||
|
createHashMapFromArray [
|
||||||
|
["orgID", _ownerOrgID],
|
||||||
|
["memberUids", +_memberUids],
|
||||||
|
["patch", +_patch]
|
||||||
|
],
|
||||||
|
createHashMapFromArray [["source", "task"]]
|
||||||
|
]];
|
||||||
|
};
|
||||||
|
|
||||||
|
_orgIds = [_ownerOrgID];
|
||||||
|
if ((EGVAR(org,OrgStore) call ["saveById", [_ownerOrgID]]) isEqualTo createHashMap) then {
|
||||||
|
_persistenceFailures pushBackUnique format ["organization:%1", _ownerOrgID];
|
||||||
|
["ERROR", format ["Task %1 updated reputation for organization %2, but durable save failed.", _taskID, _ownerOrgID]] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
["ERROR", format ["Failed to update organization %1 reputation for task %2.", _ownerOrgID, _taskID]] call EFUNC(common,log);
|
||||||
|
_mutationFailures pushBackUnique format ["organization:%1", _ownerOrgID];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
_result set ["participantUids", _participantUids];
|
||||||
|
_result set ["orgIds", _orgIds];
|
||||||
|
_result set ["contributions", _contributions];
|
||||||
|
_result set ["success", (_mutationFailures isEqualTo []) && { _persistenceFailures isEqualTo [] }];
|
||||||
|
_result set ["mutationFailures", _mutationFailures];
|
||||||
|
_result set ["persistenceFailures", _persistenceFailures];
|
||||||
|
if (_mutationFailures isNotEqualTo [] || { _persistenceFailures isNotEqualTo [] }) then {
|
||||||
|
private _messageParts = [];
|
||||||
|
if (_mutationFailures isNotEqualTo []) then {
|
||||||
|
_messageParts pushBack format ["mutation failures: %1", _mutationFailures joinString ", "];
|
||||||
|
};
|
||||||
|
if (_persistenceFailures isNotEqualTo []) then {
|
||||||
|
_messageParts pushBack format ["persistence failures: %1", _persistenceFailures joinString ", "];
|
||||||
|
};
|
||||||
|
_result set ["message", _messageParts joinString "; "];
|
||||||
|
};
|
||||||
|
|
||||||
|
private _eventName = ["task.rating.failed", "task.rating.applied"] select (_result getOrDefault ["success", false]);
|
||||||
|
[_eventName, createHashMapFromArray [
|
||||||
|
["participantUids", +(_result getOrDefault ["participantUids", []])],
|
||||||
|
["orgIds", +(_result getOrDefault ["orgIds", []])],
|
||||||
|
["contributions", +(_result getOrDefault ["contributions", createHashMap])],
|
||||||
|
["mutationFailures", +(_result getOrDefault ["mutationFailures", []])],
|
||||||
|
["persistenceFailures", +(_result getOrDefault ["persistenceFailures", []])],
|
||||||
|
["message", _result getOrDefault ["message", ""]]
|
||||||
|
]] call _emitRatingEvent;
|
||||||
|
|
||||||
|
_result
|
||||||
|
}]
|
||||||
|
]];
|
||||||
|
|
||||||
|
GVAR(TaskRewardService)
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Gateway for task hot-state extension calls.
|
||||||
|
*
|
||||||
|
* TaskStore owns gameplay/runtime behavior. This gateway owns the transport
|
||||||
|
* boundary to the extension-backed task state service.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* Task state gateway object <HASHMAP OBJECT>
|
||||||
|
*
|
||||||
|
* Public: No
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(TaskStateGateway) = createHashMapObject [[
|
||||||
|
["#type", "TaskStateGateway"],
|
||||||
|
["reset", compileFinal {
|
||||||
|
["task:reset", []] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
||||||
|
if (
|
||||||
|
!_isSuccess
|
||||||
|
|| { !(_result isEqualType "") }
|
||||||
|
|| { (_result find "Error:") == 0 }
|
||||||
|
) exitWith {
|
||||||
|
["WARNING", "Failed to reset task backend state during task store initialization."] call EFUNC(common,log);
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
["INFO", "Task backend state reset for mission lifecycle."] call EFUNC(common,log);
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["callTaskStateEnvelope", compileFinal {
|
||||||
|
params [["_function", "", [""]], ["_arguments", [], [[]]]];
|
||||||
|
|
||||||
|
private _envelope = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["error", ""]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_function isEqualTo "") exitWith { _envelope };
|
||||||
|
|
||||||
|
[_function, _arguments] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
||||||
|
if !_isSuccess exitWith {
|
||||||
|
_envelope set ["error", format ["Task backend call '%1' failed.", _function]];
|
||||||
|
_envelope
|
||||||
|
};
|
||||||
|
if !(_result isEqualType "") exitWith {
|
||||||
|
_envelope set ["error", format ["Task backend call '%1' returned an invalid response.", _function]];
|
||||||
|
_envelope
|
||||||
|
};
|
||||||
|
if ((_result find "Error:") == 0) exitWith {
|
||||||
|
["ERROR", format ["Task extension call '%1' failed: %2", _function, _result]] call EFUNC(common,log);
|
||||||
|
_envelope set ["error", _result select [7]];
|
||||||
|
_envelope
|
||||||
|
};
|
||||||
|
|
||||||
|
_envelope set ["success", true];
|
||||||
|
if (_result isNotEqualTo "") then { _envelope set ["data", fromJSON _result]; };
|
||||||
|
|
||||||
|
_envelope
|
||||||
|
}],
|
||||||
|
["callTaskState", compileFinal {
|
||||||
|
params [["_function", "", [""]], ["_arguments", [], [[]]], ["_fallback", nil]];
|
||||||
|
|
||||||
|
private _envelope = _self call ["callTaskStateEnvelope", [_function, _arguments]];
|
||||||
|
if !(_envelope getOrDefault ["success", false]) exitWith { _fallback };
|
||||||
|
if (isNil { _envelope get "data" }) exitWith { _fallback };
|
||||||
|
|
||||||
|
_envelope get "data"
|
||||||
|
}]
|
||||||
|
]];
|
||||||
|
|
||||||
|
GVAR(TaskStateGateway)
|
||||||
@ -12,30 +12,38 @@
|
|||||||
class EquipmentRewards: Edit { \
|
class EquipmentRewards: Edit { \
|
||||||
property = QUOTE(DOUBLES(PREFIX,EquipmentRewards)); \
|
property = QUOTE(DOUBLES(PREFIX,EquipmentRewards)); \
|
||||||
displayName = "Equipment Rewards"; \
|
displayName = "Equipment Rewards"; \
|
||||||
tooltip = "SQF array string for equipment rewards, e.g. [""ItemGPS"",""ItemCompass""]"; \
|
tooltip = "Comma-separated equipment class names, e.g. ItemGPS, ItemCompass. Legacy SQF arrays still work."; \
|
||||||
typeName = "STRING"; \
|
typeName = "STRING"; \
|
||||||
}; \
|
}; \
|
||||||
class SupplyRewards: Edit { \
|
class SupplyRewards: Edit { \
|
||||||
property = QUOTE(DOUBLES(PREFIX,SupplyRewards)); \
|
property = QUOTE(DOUBLES(PREFIX,SupplyRewards)); \
|
||||||
displayName = "Supply Rewards"; \
|
displayName = "Supply Rewards"; \
|
||||||
tooltip = "SQF array string for supply rewards, e.g. [""FirstAidKit"",""Medikit""]"; \
|
tooltip = "Comma-separated supply class names, e.g. FirstAidKit, Medikit. Legacy SQF arrays still work."; \
|
||||||
typeName = "STRING"; \
|
typeName = "STRING"; \
|
||||||
}; \
|
}; \
|
||||||
class WeaponRewards: Edit { \
|
class WeaponRewards: Edit { \
|
||||||
property = QUOTE(DOUBLES(PREFIX,WeaponRewards)); \
|
property = QUOTE(DOUBLES(PREFIX,WeaponRewards)); \
|
||||||
displayName = "Weapon Rewards"; \
|
displayName = "Weapon Rewards"; \
|
||||||
tooltip = "SQF array string for weapon rewards, e.g. [""arifle_MX_F""]"; \
|
tooltip = "Comma-separated weapon class names, e.g. arifle_MX_F, arifle_Katiba_F. Legacy SQF arrays still work."; \
|
||||||
typeName = "STRING"; \
|
typeName = "STRING"; \
|
||||||
}; \
|
}; \
|
||||||
class VehicleRewards: Edit { \
|
class VehicleRewards: Edit { \
|
||||||
property = QUOTE(DOUBLES(PREFIX,VehicleRewards)); \
|
property = QUOTE(DOUBLES(PREFIX,VehicleRewards)); \
|
||||||
displayName = "Vehicle Rewards"; \
|
displayName = "Vehicle Rewards"; \
|
||||||
tooltip = "SQF array string for vehicle rewards, e.g. [""B_MRAP_01_F""]"; \
|
tooltip = "Comma-separated vehicle class names, e.g. B_MRAP_01_F, B_Quadbike_01_F. Legacy SQF arrays still work."; \
|
||||||
typeName = "STRING"; \
|
typeName = "STRING"; \
|
||||||
}; \
|
}; \
|
||||||
class SpecialRewards: Edit { \
|
class SpecialRewards: Edit { \
|
||||||
property = QUOTE(DOUBLES(PREFIX,SpecialRewards)); \
|
property = QUOTE(DOUBLES(PREFIX,SpecialRewards)); \
|
||||||
displayName = "Special Rewards"; \
|
displayName = "Special Rewards"; \
|
||||||
tooltip = "SQF array string for special rewards, e.g. [""B_UAV_01_F""]"; \
|
tooltip = "Comma-separated special reward class names, e.g. B_UAV_01_F, B_Heli_Light_01_F. Legacy SQF arrays still work."; \
|
||||||
|
typeName = "STRING"; \
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TASK_CHAIN_ATTRIBUTES(PREFIX) \
|
||||||
|
class PrerequisiteTaskIds: Edit { \
|
||||||
|
property = QUOTE(DOUBLES(PREFIX,PrerequisiteTaskIds)); \
|
||||||
|
displayName = "Prerequisite Task IDs"; \
|
||||||
|
tooltip = "Comma-separated task IDs that must succeed before this task appears in CAD or can be assigned"; \
|
||||||
typeName = "STRING"; \
|
typeName = "STRING"; \
|
||||||
};
|
};
|
||||||
|
|||||||
@ -295,7 +295,19 @@ General task rules:
|
|||||||
4. Use Forge grouping modules where required.
|
4. Use Forge grouping modules where required.
|
||||||
5. Sync task modules to real world objects, units, vehicles, or grouping
|
5. Sync task modules to real world objects, units, vehicles, or grouping
|
||||||
modules.
|
modules.
|
||||||
6. Test that the task appears in CAD before relying on dispatch assignment.
|
6. To chain tasks, set `Prerequisite Task IDs` on the dependent task module to
|
||||||
|
a comma-separated list of task IDs that must succeed first.
|
||||||
|
7. Reward class fields use comma-separated class names without brackets, such
|
||||||
|
as `ItemGPS, FirstAidKit`. Existing SQF array strings such as
|
||||||
|
`["ItemGPS","FirstAidKit"]` still work for older missions.
|
||||||
|
8. Test that unchained tasks appear in CAD immediately and chained tasks appear
|
||||||
|
only after their prerequisite tasks succeed.
|
||||||
|
|
||||||
|
Task chaining uses only task IDs. The dependent task is still registered during
|
||||||
|
mission setup, but it stays hidden from CAD, cannot be assigned, and does not
|
||||||
|
start its task logic until every prerequisite task has completed successfully.
|
||||||
|
If any prerequisite task fails or never completes, the dependent task remains
|
||||||
|
locked.
|
||||||
|
|
||||||
Zone fields that must reference area markers:
|
Zone fields that must reference area markers:
|
||||||
|
|
||||||
@ -333,7 +345,9 @@ Setup:
|
|||||||
5. Set `LimitFail` if the mission should fail after too many losses.
|
5. Set `LimitFail` if the mission should fail after too many losses.
|
||||||
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
7. Sync the attack module directly to the target units or vehicles.
|
7. Set `Prerequisite Task IDs` only if this attack task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
8. Sync the attack module directly to the target units or vehicles.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -362,7 +376,9 @@ Setup:
|
|||||||
or failed conditions.
|
or failed conditions.
|
||||||
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
7. Sync the destroy module directly to the targets.
|
7. Set `Prerequisite Task IDs` only if this destroy task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
8. Sync the destroy module directly to the targets.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -408,8 +424,10 @@ Setup:
|
|||||||
failure.
|
failure.
|
||||||
11. Set `TimeLimit` to the IED countdown in seconds.
|
11. Set `TimeLimit` to the IED countdown in seconds.
|
||||||
12. Set reward funds, rating gain/loss, and end-state behavior.
|
12. Set reward funds, rating gain/loss, and end-state behavior.
|
||||||
13. Sync `FORGE_Module_Defuse` to `FORGE_Module_Explosives`.
|
13. Set `Prerequisite Task IDs` only if this defuse task should unlock after
|
||||||
14. Sync `FORGE_Module_Defuse` to `FORGE_Module_Protected` if used.
|
other tasks succeed.
|
||||||
|
14. Sync `FORGE_Module_Defuse` to `FORGE_Module_Explosives`.
|
||||||
|
15. Sync `FORGE_Module_Defuse` to `FORGE_Module_Protected` if used.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -455,7 +473,9 @@ Setup:
|
|||||||
fail threshold.
|
fail threshold.
|
||||||
10. Set reward funds, rating gain/loss, end-state behavior, and optional
|
10. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
11. Sync `FORGE_Module_Delivery` to `FORGE_Module_Cargo`.
|
11. Set `Prerequisite Task IDs` only if this delivery task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
12. Sync `FORGE_Module_Delivery` to `FORGE_Module_Cargo`.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -510,8 +530,10 @@ Setup:
|
|||||||
15. If `CBRN Attack` is enabled, set `CBRNZone`.
|
15. If `CBRN Attack` is enabled, set `CBRNZone`.
|
||||||
16. Set reward funds, rating gain/loss, end-state behavior, and optional
|
16. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
17. Sync `FORGE_Module_Hostage` to `FORGE_Module_Hostages`.
|
17. Set `Prerequisite Task IDs` only if this hostage task should unlock after
|
||||||
18. Sync `FORGE_Module_Hostage` to `FORGE_Module_Shooters`.
|
other tasks succeed.
|
||||||
|
18. Sync `FORGE_Module_Hostage` to `FORGE_Module_Hostages`.
|
||||||
|
19. Sync `FORGE_Module_Hostage` to `FORGE_Module_Shooters`.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -562,7 +584,9 @@ Setup:
|
|||||||
capture mode.
|
capture mode.
|
||||||
9. Set reward funds, rating gain/loss, end-state behavior, and optional
|
9. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
10. Sync the HVT module directly to the HVT unit or units.
|
10. Set `Prerequisite Task IDs` only if this HVT task should unlock after other
|
||||||
|
tasks succeed.
|
||||||
|
11. Sync the HVT module directly to the HVT unit or units.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -600,6 +624,8 @@ Setup:
|
|||||||
9. Place one or more enemy groups or units to use as wave templates.
|
9. Place one or more enemy groups or units to use as wave templates.
|
||||||
10. Sync any unit from each enemy group to the defend module.
|
10. Sync any unit from each enemy group to the defend module.
|
||||||
11. Set reward funds, rating gain/loss, and end-state behavior.
|
11. Set reward funds, rating gain/loss, and end-state behavior.
|
||||||
|
12. Set `Prerequisite Task IDs` only if this defend task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -652,7 +678,8 @@ Before publishing a mission, verify:
|
|||||||
- Grouping modules are synced in the correct direction.
|
- Grouping modules are synced in the correct direction.
|
||||||
- Success and fail limits match the number of required entities.
|
- Success and fail limits match the number of required entities.
|
||||||
- Reward funds and rating changes are intentional.
|
- Reward funds and rating changes are intentional.
|
||||||
- The task appears in CAD when created.
|
- Unchained tasks appear in CAD when created.
|
||||||
|
- Chained tasks remain hidden until all prerequisite task IDs succeed.
|
||||||
- Assigned CAD tasks can be acknowledged, declined, and completed.
|
- Assigned CAD tasks can be acknowledged, declined, and completed.
|
||||||
|
|
||||||
## Mission Validation Checklist
|
## Mission Validation Checklist
|
||||||
|
|||||||
@ -28,6 +28,7 @@ when a catalog entry is inserted or ownership changes:
|
|||||||
- `accepted`
|
- `accepted`
|
||||||
- `requesterUid`
|
- `requesterUid`
|
||||||
- `orgID`
|
- `orgID`
|
||||||
|
- `prerequisiteTaskIds`
|
||||||
|
|
||||||
Ownership context:
|
Ownership context:
|
||||||
|
|
||||||
@ -209,6 +210,10 @@ Available task modules:
|
|||||||
|
|
||||||
These modules delegate to `forge_server_task_fnc_startTask`.
|
These modules delegate to `forge_server_task_fnc_startTask`.
|
||||||
|
|
||||||
|
Each task module also includes an optional chain field:
|
||||||
|
|
||||||
|
- `Prerequisite Task IDs`: comma-separated task IDs that must succeed first.
|
||||||
|
|
||||||
## Mission Designer Guide
|
## Mission Designer Guide
|
||||||
|
|
||||||
This section is the practical Eden setup guide for mission designers.
|
This section is the practical Eden setup guide for mission designers.
|
||||||
@ -230,6 +235,13 @@ Use these rules for every Forge task:
|
|||||||
6. Grouping modules such as `Explosive Entities`, `Protected Entities`,
|
6. Grouping modules such as `Explosive Entities`, `Protected Entities`,
|
||||||
`Cargo`, `Hostages`, and `Shooters` should be synced to real world objects,
|
`Cargo`, `Hostages`, and `Shooters` should be synced to real world objects,
|
||||||
not other logic modules.
|
not other logic modules.
|
||||||
|
7. To chain tasks, set `Prerequisite Task IDs` on the dependent task module.
|
||||||
|
Use comma-separated IDs such as `attack_01, delivery_02`. The dependent
|
||||||
|
task stays hidden from CAD and cannot be assigned until every listed task
|
||||||
|
succeeds.
|
||||||
|
8. Reward class fields accept comma-separated class names without brackets,
|
||||||
|
such as `ItemGPS, FirstAidKit`. Legacy SQF array strings such as
|
||||||
|
`["ItemGPS","FirstAidKit"]` are still supported.
|
||||||
|
|
||||||
### Attack Task
|
### Attack Task
|
||||||
|
|
||||||
@ -473,6 +485,7 @@ through `forge_server_task_fnc_handler`.
|
|||||||
createHashMapFromArray [
|
createHashMapFromArray [
|
||||||
["limitFail", 0],
|
["limitFail", 0],
|
||||||
["limitSuccess", 3],
|
["limitSuccess", 3],
|
||||||
|
["prerequisiteTaskIds", ["recon_01"]],
|
||||||
["funds", 50000],
|
["funds", 50000],
|
||||||
["ratingFail", -10],
|
["ratingFail", -10],
|
||||||
["ratingSuccess", 20],
|
["ratingSuccess", 20],
|
||||||
@ -484,6 +497,37 @@ through `forge_server_task_fnc_handler`.
|
|||||||
] call forge_server_task_fnc_startTask;
|
] call forge_server_task_fnc_startTask;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Chained Tasks
|
||||||
|
|
||||||
|
Use `prerequisiteTaskIds` when a task should stay hidden until one or more
|
||||||
|
other tasks succeed. The task is still registered during mission setup, but it
|
||||||
|
is stored with `locked` status, filtered out of CAD, blocked from assignment,
|
||||||
|
and its task logic does not start until every prerequisite task has completed
|
||||||
|
with `succeeded`.
|
||||||
|
|
||||||
|
```sqf
|
||||||
|
[
|
||||||
|
"delivery",
|
||||||
|
"supply_delivery_02",
|
||||||
|
getMarkerPos "delivery_zone_02",
|
||||||
|
"Deliver Medical Supplies",
|
||||||
|
"Move the cargo into the marked delivery area.",
|
||||||
|
createHashMapFromArray [["cargo", [cargoBox1, cargoBox2]]],
|
||||||
|
createHashMapFromArray [
|
||||||
|
["deliveryZone", "delivery_zone_02"],
|
||||||
|
["limitSuccess", 2],
|
||||||
|
["prerequisiteTaskIds", ["compound_attack_01"]],
|
||||||
|
["funds", 30000]
|
||||||
|
]
|
||||||
|
] call forge_server_task_fnc_startTask;
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- `prerequisiteTaskIds` accepts either a string or an array of task ID strings.
|
||||||
|
- All prerequisite tasks must succeed before the chained task unlocks.
|
||||||
|
- If a prerequisite fails or never completes, the chained task remains locked.
|
||||||
|
|
||||||
## Handler Calls
|
## Handler Calls
|
||||||
|
|
||||||
Use `forge_server_task_fnc_handler` directly when the task entities are already
|
Use `forge_server_task_fnc_handler` directly when the task entities are already
|
||||||
|
|||||||
@ -295,7 +295,19 @@ General task rules:
|
|||||||
4. Use Forge grouping modules where required.
|
4. Use Forge grouping modules where required.
|
||||||
5. Sync task modules to real world objects, units, vehicles, or grouping
|
5. Sync task modules to real world objects, units, vehicles, or grouping
|
||||||
modules.
|
modules.
|
||||||
6. Test that the task appears in CAD before relying on dispatch assignment.
|
6. To chain tasks, set `Prerequisite Task IDs` on the dependent task module to
|
||||||
|
a comma-separated list of task IDs that must succeed first.
|
||||||
|
7. Reward class fields use comma-separated class names without brackets, such
|
||||||
|
as `ItemGPS, FirstAidKit`. Existing SQF array strings such as
|
||||||
|
`["ItemGPS","FirstAidKit"]` still work for older missions.
|
||||||
|
8. Test that unchained tasks appear in CAD immediately and chained tasks appear
|
||||||
|
only after their prerequisite tasks succeed.
|
||||||
|
|
||||||
|
Task chaining uses only task IDs. The dependent task is still registered during
|
||||||
|
mission setup, but it stays hidden from CAD, cannot be assigned, and does not
|
||||||
|
start its task logic until every prerequisite task has completed successfully.
|
||||||
|
If any prerequisite task fails or never completes, the dependent task remains
|
||||||
|
locked.
|
||||||
|
|
||||||
Zone fields that must reference area markers:
|
Zone fields that must reference area markers:
|
||||||
|
|
||||||
@ -333,7 +345,9 @@ Setup:
|
|||||||
5. Set `LimitFail` if the mission should fail after too many losses.
|
5. Set `LimitFail` if the mission should fail after too many losses.
|
||||||
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
7. Sync the attack module directly to the target units or vehicles.
|
7. Set `Prerequisite Task IDs` only if this attack task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
8. Sync the attack module directly to the target units or vehicles.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -362,7 +376,9 @@ Setup:
|
|||||||
or failed conditions.
|
or failed conditions.
|
||||||
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
6. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
7. Sync the destroy module directly to the targets.
|
7. Set `Prerequisite Task IDs` only if this destroy task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
8. Sync the destroy module directly to the targets.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -408,8 +424,10 @@ Setup:
|
|||||||
failure.
|
failure.
|
||||||
11. Set `TimeLimit` to the IED countdown in seconds.
|
11. Set `TimeLimit` to the IED countdown in seconds.
|
||||||
12. Set reward funds, rating gain/loss, and end-state behavior.
|
12. Set reward funds, rating gain/loss, and end-state behavior.
|
||||||
13. Sync `FORGE_Module_Defuse` to `FORGE_Module_Explosives`.
|
13. Set `Prerequisite Task IDs` only if this defuse task should unlock after
|
||||||
14. Sync `FORGE_Module_Defuse` to `FORGE_Module_Protected` if used.
|
other tasks succeed.
|
||||||
|
14. Sync `FORGE_Module_Defuse` to `FORGE_Module_Explosives`.
|
||||||
|
15. Sync `FORGE_Module_Defuse` to `FORGE_Module_Protected` if used.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -455,7 +473,9 @@ Setup:
|
|||||||
fail threshold.
|
fail threshold.
|
||||||
10. Set reward funds, rating gain/loss, end-state behavior, and optional
|
10. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
11. Sync `FORGE_Module_Delivery` to `FORGE_Module_Cargo`.
|
11. Set `Prerequisite Task IDs` only if this delivery task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
12. Sync `FORGE_Module_Delivery` to `FORGE_Module_Cargo`.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -510,8 +530,10 @@ Setup:
|
|||||||
15. If `CBRN Attack` is enabled, set `CBRNZone`.
|
15. If `CBRN Attack` is enabled, set `CBRNZone`.
|
||||||
16. Set reward funds, rating gain/loss, end-state behavior, and optional
|
16. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
17. Sync `FORGE_Module_Hostage` to `FORGE_Module_Hostages`.
|
17. Set `Prerequisite Task IDs` only if this hostage task should unlock after
|
||||||
18. Sync `FORGE_Module_Hostage` to `FORGE_Module_Shooters`.
|
other tasks succeed.
|
||||||
|
18. Sync `FORGE_Module_Hostage` to `FORGE_Module_Hostages`.
|
||||||
|
19. Sync `FORGE_Module_Hostage` to `FORGE_Module_Shooters`.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -562,7 +584,9 @@ Setup:
|
|||||||
capture mode.
|
capture mode.
|
||||||
9. Set reward funds, rating gain/loss, end-state behavior, and optional
|
9. Set reward funds, rating gain/loss, end-state behavior, and optional
|
||||||
`TimeLimit`.
|
`TimeLimit`.
|
||||||
10. Sync the HVT module directly to the HVT unit or units.
|
10. Set `Prerequisite Task IDs` only if this HVT task should unlock after other
|
||||||
|
tasks succeed.
|
||||||
|
11. Sync the HVT module directly to the HVT unit or units.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -600,6 +624,8 @@ Setup:
|
|||||||
9. Place one or more enemy groups or units to use as wave templates.
|
9. Place one or more enemy groups or units to use as wave templates.
|
||||||
10. Sync any unit from each enemy group to the defend module.
|
10. Sync any unit from each enemy group to the defend module.
|
||||||
11. Set reward funds, rating gain/loss, and end-state behavior.
|
11. Set reward funds, rating gain/loss, and end-state behavior.
|
||||||
|
12. Set `Prerequisite Task IDs` only if this defend task should unlock after
|
||||||
|
other tasks succeed.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
@ -652,7 +678,8 @@ Before publishing a mission, verify:
|
|||||||
- Grouping modules are synced in the correct direction.
|
- Grouping modules are synced in the correct direction.
|
||||||
- Success and fail limits match the number of required entities.
|
- Success and fail limits match the number of required entities.
|
||||||
- Reward funds and rating changes are intentional.
|
- Reward funds and rating changes are intentional.
|
||||||
- The task appears in CAD when created.
|
- Unchained tasks appear in CAD when created.
|
||||||
|
- Chained tasks remain hidden until all prerequisite task IDs succeed.
|
||||||
- Assigned CAD tasks can be acknowledged, declined, and completed.
|
- Assigned CAD tasks can be acknowledged, declined, and completed.
|
||||||
|
|
||||||
## Mission Validation Checklist
|
## Mission Validation Checklist
|
||||||
|
|||||||
@ -3,9 +3,6 @@ title: "Player Guide"
|
|||||||
description: "Use this guide as the player-facing overview for Forge systems. It explains what players interact with during normal missions, how task assignment works, and what persistent storage limits apply."
|
description: "Use this guide as the player-facing overview for Forge systems. It explains what players interact with during normal missions, how task assignment works, and what persistent storage limits apply."
|
||||||
---
|
---
|
||||||
|
|
||||||
Player-guide screenshots are stored as JPG files under
|
|
||||||
`docus/public/images/player`.
|
|
||||||
|
|
||||||
## Opening Forge Interactions
|
## Opening Forge Interactions
|
||||||
|
|
||||||
Most Forge actions are opened from the actor interaction menu while standing
|
Most Forge actions are opened from the actor interaction menu while standing
|
||||||
|
|||||||
@ -27,6 +27,7 @@ when a catalog entry is inserted or ownership changes:
|
|||||||
- `accepted`
|
- `accepted`
|
||||||
- `requesterUid`
|
- `requesterUid`
|
||||||
- `orgID`
|
- `orgID`
|
||||||
|
- `prerequisiteTaskIds`
|
||||||
|
|
||||||
Ownership context:
|
Ownership context:
|
||||||
|
|
||||||
@ -208,6 +209,10 @@ Available task modules:
|
|||||||
|
|
||||||
These modules delegate to `forge_server_task_fnc_startTask`.
|
These modules delegate to `forge_server_task_fnc_startTask`.
|
||||||
|
|
||||||
|
Each task module also includes an optional chain field:
|
||||||
|
|
||||||
|
- `Prerequisite Task IDs`: comma-separated task IDs that must succeed first.
|
||||||
|
|
||||||
## Mission Designer Guide
|
## Mission Designer Guide
|
||||||
|
|
||||||
This section is the practical Eden setup guide for mission designers.
|
This section is the practical Eden setup guide for mission designers.
|
||||||
@ -229,6 +234,13 @@ Use these rules for every Forge task:
|
|||||||
6. Grouping modules such as `Explosive Entities`, `Protected Entities`,
|
6. Grouping modules such as `Explosive Entities`, `Protected Entities`,
|
||||||
`Cargo`, `Hostages`, and `Shooters` should be synced to real world objects,
|
`Cargo`, `Hostages`, and `Shooters` should be synced to real world objects,
|
||||||
not other logic modules.
|
not other logic modules.
|
||||||
|
7. To chain tasks, set `Prerequisite Task IDs` on the dependent task module.
|
||||||
|
Use comma-separated IDs such as `attack_01, delivery_02`. The dependent
|
||||||
|
task stays hidden from CAD and cannot be assigned until every listed task
|
||||||
|
succeeds.
|
||||||
|
8. Reward class fields accept comma-separated class names without brackets,
|
||||||
|
such as `ItemGPS, FirstAidKit`. Legacy SQF array strings such as
|
||||||
|
`["ItemGPS","FirstAidKit"]` are still supported.
|
||||||
|
|
||||||
### Attack Task
|
### Attack Task
|
||||||
|
|
||||||
@ -472,6 +484,7 @@ through `forge_server_task_fnc_handler`.
|
|||||||
createHashMapFromArray [
|
createHashMapFromArray [
|
||||||
["limitFail", 0],
|
["limitFail", 0],
|
||||||
["limitSuccess", 3],
|
["limitSuccess", 3],
|
||||||
|
["prerequisiteTaskIds", ["recon_01"]],
|
||||||
["funds", 50000],
|
["funds", 50000],
|
||||||
["ratingFail", -10],
|
["ratingFail", -10],
|
||||||
["ratingSuccess", 20],
|
["ratingSuccess", 20],
|
||||||
@ -483,6 +496,37 @@ through `forge_server_task_fnc_handler`.
|
|||||||
] call forge_server_task_fnc_startTask;
|
] call forge_server_task_fnc_startTask;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Chained Tasks
|
||||||
|
|
||||||
|
Use `prerequisiteTaskIds` when a task should stay hidden until one or more
|
||||||
|
other tasks succeed. The task is still registered during mission setup, but it
|
||||||
|
is stored with `locked` status, filtered out of CAD, blocked from assignment,
|
||||||
|
and its task logic does not start until every prerequisite task has completed
|
||||||
|
with `succeeded`.
|
||||||
|
|
||||||
|
```sqf
|
||||||
|
[
|
||||||
|
"delivery",
|
||||||
|
"supply_delivery_02",
|
||||||
|
getMarkerPos "delivery_zone_02",
|
||||||
|
"Deliver Medical Supplies",
|
||||||
|
"Move the cargo into the marked delivery area.",
|
||||||
|
createHashMapFromArray [["cargo", [cargoBox1, cargoBox2]]],
|
||||||
|
createHashMapFromArray [
|
||||||
|
["deliveryZone", "delivery_zone_02"],
|
||||||
|
["limitSuccess", 2],
|
||||||
|
["prerequisiteTaskIds", ["compound_attack_01"]],
|
||||||
|
["funds", 30000]
|
||||||
|
]
|
||||||
|
] call forge_server_task_fnc_startTask;
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- `prerequisiteTaskIds` accepts either a string or an array of task ID strings.
|
||||||
|
- All prerequisite tasks must succeed before the chained task unlocks.
|
||||||
|
- If a prerequisite fails or never completes, the chained task remains locked.
|
||||||
|
|
||||||
## Handler Calls
|
## Handler Calls
|
||||||
|
|
||||||
Use `forge_server_task_fnc_handler` directly when the task entities are already
|
Use `forge_server_task_fnc_handler` directly when the task entities are already
|
||||||
|
|||||||
@ -45,18 +45,14 @@ impl<R: TaskRepository> TaskStateService<R> {
|
|||||||
|
|
||||||
pub fn list_active_catalog(&self) -> Result<Vec<Value>, String> {
|
pub fn list_active_catalog(&self) -> Result<Vec<Value>, String> {
|
||||||
let catalog = self.repository.list_catalog()?;
|
let catalog = self.repository.list_catalog()?;
|
||||||
let active_statuses = self.repository.list_active_statuses()?;
|
|
||||||
let mut active_entries = Vec::new();
|
let mut active_entries = Vec::new();
|
||||||
|
|
||||||
for (task_id, status) in active_statuses {
|
for (task_id, entry) in catalog {
|
||||||
|
let status = self.derive_catalog_status(&task_id, &entry)?;
|
||||||
if !matches!(status.as_str(), "available" | "assigned" | "active") {
|
if !matches!(status.as_str(), "available" | "assigned" | "active") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(entry) = catalog.get(&task_id) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut entry = entry.fields.clone();
|
let mut entry = entry.fields.clone();
|
||||||
entry.insert("taskId".to_string(), Value::String(task_id.clone()));
|
entry.insert("taskId".to_string(), Value::String(task_id.clone()));
|
||||||
entry.insert("taskID".to_string(), Value::String(task_id));
|
entry.insert("taskID".to_string(), Value::String(task_id));
|
||||||
@ -173,10 +169,15 @@ impl<R: TaskRepository> TaskStateService<R> {
|
|||||||
return Ok(status);
|
return Ok(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self
|
if let Some(status) = self.repository.get_completed_status(&entry_id)? {
|
||||||
.repository
|
return Ok(status);
|
||||||
.get_completed_status(&entry_id)?
|
}
|
||||||
.unwrap_or_default())
|
|
||||||
|
let Some(entry) = self.repository.get_catalog_entry(&entry_id)? else {
|
||||||
|
return Ok(String::new());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self::default_catalog_status(&entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_status(&self, entry_id: String) -> Result<bool, String> {
|
pub fn clear_status(&self, entry_id: String) -> Result<bool, String> {
|
||||||
@ -245,6 +246,31 @@ impl<R: TaskRepository> TaskStateService<R> {
|
|||||||
Ok(entry.into_value())
|
Ok(entry.into_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn derive_catalog_status(&self, entry_id: &str, entry: &TaskRecord) -> Result<String, String> {
|
||||||
|
if let Some(status) = self.repository.get_active_status(entry_id)? {
|
||||||
|
return Ok(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(status) = self.repository.get_completed_status(entry_id)? {
|
||||||
|
return Ok(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::default_catalog_status(entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_catalog_status(entry: &TaskRecord) -> String {
|
||||||
|
if entry
|
||||||
|
.fields
|
||||||
|
.get("locked")
|
||||||
|
.and_then(Value::as_bool)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
"locked".to_string()
|
||||||
|
} else {
|
||||||
|
"available".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_catalog_entry(entry: &mut TaskRecord, entry_id: &str) {
|
fn normalize_catalog_entry(entry: &mut TaskRecord, entry_id: &str) {
|
||||||
let fields = &mut entry.fields;
|
let fields = &mut entry.fields;
|
||||||
fields
|
fields
|
||||||
@ -383,6 +409,34 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_status_defaults_catalog_entries_to_available_or_locked() {
|
||||||
|
let service = TaskStateService::new(InMemoryTaskRepository::new());
|
||||||
|
|
||||||
|
service
|
||||||
|
.upsert_catalog_entry("task-open".to_string(), r#"{"title":"Open"}"#.to_string())
|
||||||
|
.expect("open catalog upsert should succeed");
|
||||||
|
service
|
||||||
|
.upsert_catalog_entry(
|
||||||
|
"task-locked".to_string(),
|
||||||
|
r#"{"title":"Locked","locked":true}"#.to_string(),
|
||||||
|
)
|
||||||
|
.expect("locked catalog upsert should succeed");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.get_status("task-open".to_string())
|
||||||
|
.expect("open status lookup should succeed"),
|
||||||
|
"available"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
service
|
||||||
|
.get_status("task-locked".to_string())
|
||||||
|
.expect("locked status lookup should succeed"),
|
||||||
|
"locked"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_active_catalog_returns_assignable_and_active_entries() {
|
fn list_active_catalog_returns_assignable_and_active_entries() {
|
||||||
let service = TaskStateService::new(InMemoryTaskRepository::new());
|
let service = TaskStateService::new(InMemoryTaskRepository::new());
|
||||||
@ -435,4 +489,31 @@ mod tests {
|
|||||||
assert!(task_ids.contains(&"task-assigned"));
|
assert!(task_ids.contains(&"task-assigned"));
|
||||||
assert!(task_ids.contains(&"task-active"));
|
assert!(task_ids.contains(&"task-active"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_active_catalog_includes_unstatused_unlocked_entries() {
|
||||||
|
let service = TaskStateService::new(InMemoryTaskRepository::new());
|
||||||
|
|
||||||
|
service
|
||||||
|
.upsert_catalog_entry("task-open".to_string(), r#"{"title":"Open"}"#.to_string())
|
||||||
|
.expect("open catalog upsert should succeed");
|
||||||
|
service
|
||||||
|
.upsert_catalog_entry(
|
||||||
|
"task-locked".to_string(),
|
||||||
|
r#"{"title":"Locked","locked":true}"#.to_string(),
|
||||||
|
)
|
||||||
|
.expect("locked catalog upsert should succeed");
|
||||||
|
|
||||||
|
let active_catalog = service
|
||||||
|
.list_active_catalog()
|
||||||
|
.expect("active catalog should build");
|
||||||
|
|
||||||
|
let task_ids: Vec<_> = active_catalog
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry| entry.get("taskId").and_then(Value::as_str))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(active_catalog.len(), 1);
|
||||||
|
assert!(task_ids.contains(&"task-open"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user