Refactor CAD sidepanel to hydrate board data
- Replace task-only refresh flow with hydrate/assignment/group events - Add contracts, groups, and activity tabs to the client sidepanel - Introduce server-side CAD store and request handlers
This commit is contained in:
parent
45a4f7460a
commit
0e9d0d3dc4
@ -1,7 +1,6 @@
|
|||||||
class Extended_PreInit_EventHandlers {
|
class Extended_PreInit_EventHandlers {
|
||||||
class ADDON {
|
class ADDON {
|
||||||
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
|
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
|
||||||
clientInit = QUOTE(call COMPILE_SCRIPT(XEH_preInitClient));
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,18 +7,20 @@ if (isNil QGVAR(CADUIBridge)) then { call FUNC(initUIBridge); };
|
|||||||
call FUNC(openUI);
|
call FUNC(openUI);
|
||||||
}] call CFUNC(addEventHandler);
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
[QGVAR(responseTaskCatalog), {
|
[QGVAR(responseHydrateCad), {
|
||||||
params [["_entries", [], [[]]]];
|
params [["_payload", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
if !(isNil QGVAR(CADRepository)) then {
|
GVAR(CADUIBridge) call ["handleHydrateResponse", [_payload]];
|
||||||
GVAR(CADRepository) call ["setTaskCatalog", [_entries]];
|
|
||||||
};
|
|
||||||
|
|
||||||
GVAR(CADUIBridge) call ["refreshTaskCatalog", []];
|
|
||||||
}] call CFUNC(addEventHandler);
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
[QGVAR(responseTaskAccept), {
|
[QGVAR(responseCadAssignment), {
|
||||||
params [["_result", createHashMap, [createHashMap]]];
|
params [["_result", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
GVAR(CADUIBridge) call ["handleTaskAcceptResponse", [_result]];
|
GVAR(CADUIBridge) call ["handleAssignmentResponse", [_result]];
|
||||||
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
|
[QGVAR(responseCadGroupUpdate), {
|
||||||
|
params [["_result", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
GVAR(CADUIBridge) call ["handleGroupUpdateResponse", [_result]];
|
||||||
}] call CFUNC(addEventHandler);
|
}] call CFUNC(addEventHandler);
|
||||||
|
|||||||
@ -35,16 +35,46 @@ switch (_event) do {
|
|||||||
case "cad::ready": {
|
case "cad::ready": {
|
||||||
GVAR(CADUIBridge) call ["handleReady", [_control, _data]];
|
GVAR(CADUIBridge) call ["handleReady", [_control, _data]];
|
||||||
};
|
};
|
||||||
case "cad::tasks::refresh": {
|
case "cad::refresh": {
|
||||||
GVAR(CADUIBridge) call ["requestTaskCatalog", []];
|
GVAR(CADUIBridge) call ["requestHydrate", []];
|
||||||
};
|
};
|
||||||
case "cad::tasks::accept": {
|
case "cad::tasks::assign": {
|
||||||
|
private _taskID = "";
|
||||||
|
private _groupID = "";
|
||||||
|
private _note = "";
|
||||||
|
if (_data isEqualType createHashMap) then {
|
||||||
|
_taskID = _data getOrDefault ["taskID", ""];
|
||||||
|
_groupID = _data getOrDefault ["groupID", ""];
|
||||||
|
_note = _data getOrDefault ["note", ""];
|
||||||
|
};
|
||||||
|
|
||||||
|
GVAR(CADUIBridge) call ["requestAssignTask", [_taskID, _groupID, _note]];
|
||||||
|
};
|
||||||
|
case "cad::tasks::acknowledge": {
|
||||||
private _taskID = "";
|
private _taskID = "";
|
||||||
if (_data isEqualType createHashMap) then {
|
if (_data isEqualType createHashMap) then {
|
||||||
_taskID = _data getOrDefault ["taskID", ""];
|
_taskID = _data getOrDefault ["taskID", ""];
|
||||||
};
|
};
|
||||||
|
|
||||||
GVAR(CADUIBridge) call ["requestTaskAccept", [_taskID]];
|
GVAR(CADUIBridge) call ["requestAcknowledgeTask", [_taskID]];
|
||||||
|
};
|
||||||
|
case "cad::tasks::decline": {
|
||||||
|
private _taskID = "";
|
||||||
|
if (_data isEqualType createHashMap) then {
|
||||||
|
_taskID = _data getOrDefault ["taskID", ""];
|
||||||
|
};
|
||||||
|
|
||||||
|
GVAR(CADUIBridge) call ["requestDeclineTask", [_taskID]];
|
||||||
|
};
|
||||||
|
case "cad::groups::status": {
|
||||||
|
private _groupID = "";
|
||||||
|
private _status = "";
|
||||||
|
if (_data isEqualType createHashMap) then {
|
||||||
|
_groupID = _data getOrDefault ["groupID", ""];
|
||||||
|
_status = _data getOrDefault ["status", ""];
|
||||||
|
};
|
||||||
|
|
||||||
|
GVAR(CADUIBridge) call ["requestGroupStatus", [_groupID, _status]];
|
||||||
};
|
};
|
||||||
case "map::zoomIn": {
|
case "map::zoomIn": {
|
||||||
private _mapCtrl = uiNamespace getVariable [QGVAR(MapCtrl), controlNull];
|
private _mapCtrl = uiNamespace getVariable [QGVAR(MapCtrl), controlNull];
|
||||||
|
|||||||
@ -25,21 +25,33 @@ GVAR(CADRepository) = createHashMapObject [[
|
|||||||
["#create", compileFinal {
|
["#create", compileFinal {
|
||||||
_self set ["isLoaded", true];
|
_self set ["isLoaded", true];
|
||||||
_self set ["isOpen", false];
|
_self set ["isOpen", false];
|
||||||
_self set ["taskCatalog", []];
|
_self set ["groups", []];
|
||||||
|
_self set ["contracts", []];
|
||||||
|
_self set ["assignments", []];
|
||||||
|
_self set ["activity", []];
|
||||||
|
_self set ["session", createHashMap];
|
||||||
}],
|
}],
|
||||||
["pushTaskCatalog", compileFinal {
|
["pushHydratePayload", compileFinal {
|
||||||
params [["_bridge", createHashMap, [createHashMap]]];
|
params [["_bridge", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
if (_bridge isEqualTo createHashMap) exitWith { false };
|
if (_bridge isEqualTo createHashMap) exitWith { false };
|
||||||
|
|
||||||
_bridge call ["sendEvent", ["cad::tasks::hydrate", createHashMapFromArray [
|
_bridge call ["sendEvent", ["cad::hydrate", createHashMapFromArray [
|
||||||
["tasks", +(_self getOrDefault ["taskCatalog", []])]
|
["groups", +(_self getOrDefault ["groups", []])],
|
||||||
|
["contracts", +(_self getOrDefault ["contracts", []])],
|
||||||
|
["assignments", +(_self getOrDefault ["assignments", []])],
|
||||||
|
["activity", +(_self getOrDefault ["activity", []])],
|
||||||
|
["session", +(_self getOrDefault ["session", createHashMap])]
|
||||||
]]]
|
]]]
|
||||||
}],
|
}],
|
||||||
["setTaskCatalog", compileFinal {
|
["setHydratePayload", compileFinal {
|
||||||
params [["_entries", [], [[]]]];
|
params [["_payload", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
_self set ["taskCatalog", +_entries];
|
_self set ["groups", +(_payload getOrDefault ["groups", []])];
|
||||||
|
_self set ["contracts", +(_payload getOrDefault ["contracts", []])];
|
||||||
|
_self set ["assignments", +(_payload getOrDefault ["assignments", []])];
|
||||||
|
_self set ["activity", +(_payload getOrDefault ["activity", []])];
|
||||||
|
_self set ["session", +(_payload getOrDefault ["session", createHashMap])];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["setOpen", compileFinal {
|
["setOpen", compileFinal {
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
* Public: No
|
* Public: No
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Initializes the CAD UI bridge for sidepanel browser state and task event routing.
|
* Initializes the CAD UI bridge for sidepanel browser state and CAD event routing.
|
||||||
*
|
*
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* None
|
* None
|
||||||
@ -50,33 +50,73 @@ GVAR(CADUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
|||||||
_screen call ["markReady", [true]];
|
_screen call ["markReady", [true]];
|
||||||
_self call ["flushPendingEvents", []];
|
_self call ["flushPendingEvents", []];
|
||||||
|
|
||||||
_self call ["requestTaskCatalog", []];
|
_self call ["requestHydrate", []];
|
||||||
_self call ["refreshTaskCatalog", []];
|
_self call ["refreshHydrate", []];
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["requestTaskCatalog", compileFinal {
|
["requestHydrate", compileFinal {
|
||||||
[SRPC(task,requestTaskCatalog), [getPlayerUID player]] call CFUNC(serverEvent);
|
[SRPC(cad,requestHydrateCad), [getPlayerUID player]] call CFUNC(serverEvent);
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["requestTaskAccept", compileFinal {
|
["requestAssignTask", compileFinal {
|
||||||
|
params [["_taskID", "", [""]], ["_groupID", "", [""]], ["_note", "", [""]]];
|
||||||
|
|
||||||
|
if (_taskID isEqualTo "" || { _groupID isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
[SRPC(cad,requestAssignCadTask), [getPlayerUID player, _taskID, _groupID, _note]] call CFUNC(serverEvent);
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["requestAcknowledgeTask", compileFinal {
|
||||||
params [["_taskID", "", [""]]];
|
params [["_taskID", "", [""]]];
|
||||||
|
|
||||||
if (_taskID isEqualTo "") exitWith { false };
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
[SRPC(task,requestAcceptTask), [getPlayerUID player, _taskID]] call CFUNC(serverEvent);
|
[SRPC(cad,requestAcknowledgeCadTask), [getPlayerUID player, _taskID]] call CFUNC(serverEvent);
|
||||||
true
|
true
|
||||||
}],
|
}],
|
||||||
["refreshTaskCatalog", compileFinal {
|
["requestDeclineTask", compileFinal {
|
||||||
if (isNil QGVAR(CADRepository)) exitWith { false };
|
params [["_taskID", "", [""]]];
|
||||||
GVAR(CADRepository) call ["pushTaskCatalog", [_self]]
|
|
||||||
|
if (_taskID isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
[SRPC(cad,requestDeclineCadTask), [getPlayerUID player, _taskID]] call CFUNC(serverEvent);
|
||||||
|
true
|
||||||
}],
|
}],
|
||||||
["handleTaskAcceptResponse", compileFinal {
|
["requestGroupStatus", compileFinal {
|
||||||
|
params [["_groupID", "", [""]], ["_status", "", [""]]];
|
||||||
|
|
||||||
|
if (_groupID isEqualTo "" || { _status isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
[SRPC(cad,requestUpdateCadGroupStatus), [getPlayerUID player, _groupID, _status]] call CFUNC(serverEvent);
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["refreshHydrate", compileFinal {
|
||||||
|
if (isNil QGVAR(CADRepository)) exitWith { false };
|
||||||
|
GVAR(CADRepository) call ["pushHydratePayload", [_self]]
|
||||||
|
}],
|
||||||
|
["handleHydrateResponse", compileFinal {
|
||||||
|
params [["_payload", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
if (isNil QGVAR(CADRepository)) exitWith { false };
|
||||||
|
|
||||||
|
GVAR(CADRepository) call ["setHydratePayload", [_payload]];
|
||||||
|
_self call ["refreshHydrate", []]
|
||||||
|
}],
|
||||||
|
["handleAssignmentResponse", compileFinal {
|
||||||
params [["_result", createHashMap, [createHashMap]]];
|
params [["_result", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
_self call ["sendEvent", ["cad::tasks::accept::response", createHashMapFromArray [
|
_self call ["sendEvent", ["cad::assignment::response", createHashMapFromArray [
|
||||||
["message", _result getOrDefault ["message", "Task request processed."]],
|
["message", _result getOrDefault ["message", "Task request processed."]],
|
||||||
["success", _result getOrDefault ["success", false]]
|
["success", _result getOrDefault ["success", false]]
|
||||||
]]]
|
]]]
|
||||||
|
}],
|
||||||
|
["handleGroupUpdateResponse", compileFinal {
|
||||||
|
params [["_result", createHashMap, [createHashMap]]];
|
||||||
|
|
||||||
|
_self call ["sendEvent", ["cad::group::response", createHashMapFromArray [
|
||||||
|
["message", _result getOrDefault ["message", "Group update processed."]],
|
||||||
|
["success", _result getOrDefault ["success", false]]
|
||||||
|
]]]
|
||||||
}]
|
}]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
html,body{background:var(--panel);border-left:1px solid var(--stroke);width:100%;height:100%;box-shadow:var(--shadow);-webkit-backdrop-filter:blur(12px);margin:0;padding:0;overflow:hidden}body{opacity:1;visibility:visible}.panel-header{border-bottom:1px solid var(--stroke);background:linear-gradient(#ffffff0d,#0000);justify-content:space-between;align-items:center;padding:14px;display:flex}.panel-header h3{color:var(--accent);text-transform:uppercase;letter-spacing:.8px;font-size:14px;font-weight:650}.panel-content{height:calc(100% - 56px);padding:14px;overflow:auto}.placeholder-message{text-align:center;padding:20px}.placeholder-message p{color:var(--muted);font-size:13px;font-style:italic}.task-toolbar{margin-bottom:10px}.task-toolbar button,.task-accept-btn{color:#f3f6f9;cursor:pointer;background:#1e252be6;border:1px solid #fff3;width:100%;padding:8px 10px}.task-toolbar button:hover,.task-accept-btn:hover{background:#2e3942f2}.task-toolbar button:disabled,.task-accept-btn:disabled{opacity:.55;cursor:default}.task-status-message{color:#cdd6dd;min-height:18px;margin-bottom:10px;font-size:12px}.task-status-message[data-type=success]{color:#79d28a}.task-status-message[data-type=error]{color:#ff8a80}.task-list{flex-direction:column;gap:10px;display:flex}.task-card{background:#0c10149e;border:1px solid #ffffff14;padding:10px}.task-card-header{justify-content:space-between;gap:8px;margin-bottom:8px;display:flex}.task-type{opacity:.7;text-transform:uppercase;font-size:11px}.task-description{margin:0 0 8px;font-size:12px;line-height:1.4}.task-meta{opacity:.8;justify-content:space-between;gap:8px;margin-bottom:8px;font-size:11px;display:flex}
|
html,body{background:var(--panel);border-left:1px solid var(--stroke);width:100%;height:100%;box-shadow:var(--shadow);-webkit-backdrop-filter:blur(12px);margin:0;padding:0;overflow:hidden}body{opacity:1;visibility:visible}.panel-header{border-bottom:1px solid var(--stroke);background:linear-gradient(#ffffff0d,#0000);justify-content:space-between;align-items:center;padding:14px;display:flex}.panel-header h3{color:var(--accent);text-transform:uppercase;letter-spacing:.8px;font-size:14px;font-weight:650}.panel-content{height:calc(100% - 56px);padding:14px;overflow:auto}.placeholder-message{text-align:center;padding:20px}.placeholder-message p{color:var(--muted);font-size:13px;font-style:italic}.task-toolbar{margin-bottom:10px}.cad-tabs{grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:12px;display:grid}.cad-tab{color:#f3f6f9c7;text-transform:uppercase;letter-spacing:.08em;cursor:pointer;background:#141b21e0;border:1px solid #ffffff24;padding:8px 10px;font-size:11px}.cad-tab:hover{color:#f3f6f9;background:#1f282ff0}.cad-tab.is-active{color:var(--accent);background:#0f283af5;border-color:#5bbbff6b}.cad-tab-panels{min-height:0}.cad-section{display:none}.cad-section.is-active{display:block}.cad-section-header{color:var(--accent);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-size:12px;font-weight:700}.task-toolbar button,.task-accept-btn,.task-secondary-btn,.cad-select{color:#f3f6f9;background:#1e252be6;border:1px solid #fff3;width:100%;padding:8px 10px}.task-toolbar button,.task-accept-btn,.task-secondary-btn{cursor:pointer}.task-toolbar button:hover,.task-accept-btn:hover,.task-secondary-btn:hover{background:#2e3942f2}.task-toolbar button:disabled,.task-accept-btn:disabled,.task-secondary-btn:disabled{opacity:.55;cursor:default}.task-status-message{color:#cdd6dd;min-height:18px;margin-bottom:10px;font-size:12px}.task-status-message[data-type=success]{color:#79d28a}.task-status-message[data-type=error]{color:#ff8a80}.task-list{flex-direction:column;gap:10px;display:flex}.task-action-stack,.task-action-row{flex-direction:column;gap:8px;display:flex}.task-action-row{flex-direction:row}.task-card{background:#0c10149e;border:1px solid #ffffff14;padding:10px}.task-card-header{justify-content:space-between;gap:8px;margin-bottom:8px;display:flex}.task-type{opacity:.7;text-transform:uppercase;font-size:11px}.task-description{margin:0 0 8px;font-size:12px;line-height:1.4}.task-meta{opacity:.8;justify-content:space-between;gap:8px;margin-bottom:8px;font-size:11px;display:flex}.task-secondary-btn{background:#3c302deb}
|
||||||
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
<!doctype html><html><head><meta charset="UTF-8"></head><body><div class="panel-header"><h3>CAD System</h3></div><div class="panel-content"><div class="task-toolbar"><button id="refreshTasksBtn" type="button">Refresh Tasks</button></div><div id="taskStatusMessage" class="task-status-message"></div><div id="taskList" class="task-list"><div class="placeholder-message"><p>Loading available tasks...</p></div></div></div><script>window.MapLoader={loadCSS:e=>A3API.RequestFile(e).then(e=>{const d=document.createElement("style");d.textContent=e,document.head.appendChild(d)}),loadJS(path){return A3API.RequestFile(path).then(js=>{eval(js)})},loadAll(e){return e.reduce((e,d)=>e.then(()=>d.endsWith(".css")?this.loadCSS(d):d.endsWith(".js")?this.loadJS(d):Promise.resolve()),Promise.resolve())}},MapLoader.loadAll(["forge\\forge_client\\addons\\cad\\ui\\_site\\cad-common.css","forge\\forge_client\\addons\\cad\\ui\\_site\\cad-sidepanel.css","forge\\forge_client\\addons\\cad\\ui\\_site\\cad-shared.js","forge\\forge_client\\addons\\cad\\ui\\_site\\cad-sidepanel.js"]).catch(e=>console.error("[SIDEPANEL] Load error:",e))</script></body></html>
|
<!doctype html><html><head><meta charset="UTF-8"></head><body><div class="panel-header"><h3>CAD System</h3></div><div class="panel-content"><div class="task-toolbar"><button id="refreshCadBtn" type="button">Refresh Board</button></div><div id="cadStatusMessage" class="task-status-message"></div><div class="cad-tabs" role="tablist" aria-label="CAD Sections"><button id="tabContractsBtn" class="cad-tab is-active" type="button" data-tab="contracts">Contracts</button> <button id="tabGroupsBtn" class="cad-tab" type="button" data-tab="groups">Groups</button> <button id="tabActivityBtn" class="cad-tab" type="button" data-tab="activity">Activity</button></div><div class="cad-tab-panels"><div id="contractsPanel" class="cad-section is-active" data-panel="contracts"><div class="cad-section-header">Contracts</div><div id="taskList" class="task-list"><div class="placeholder-message"><p>Loading contracts...</p></div></div></div><div id="groupsPanel" class="cad-section" data-panel="groups"><div class="cad-section-header">Groups</div><div id="groupList" class="task-list"><div class="placeholder-message"><p>Loading groups...</p></div></div></div><div id="activityPanel" class="cad-section" data-panel="activity"><div class="cad-section-header">Activity</div><div id="activityList" class="task-list"><div class="placeholder-message"><p>No recent activity.</p></div></div></div></div></div><script>window.MapLoader={loadCSS:e=>A3API.RequestFile(e).then(e=>{const d=document.createElement("style");d.textContent=e,document.head.appendChild(d)}),loadJS(path){return A3API.RequestFile(path).then(js=>{eval(js)})},loadAll(e){return e.reduce((e,d)=>e.then(()=>d.endsWith(".css")?this.loadCSS(d):d.endsWith(".js")?this.loadJS(d):Promise.resolve()),Promise.resolve())}},MapLoader.loadAll(["forge\\forge_client\\addons\\cad\\ui\\_site\\cad-common.css","forge\\forge_client\\addons\\cad\\ui\\_site\\cad-sidepanel.css","forge\\forge_client\\addons\\cad\\ui\\_site\\cad-shared.js","forge\\forge_client\\addons\\cad\\ui\\_site\\cad-sidepanel.js"]).catch(e=>console.error("[SIDEPANEL] Load error:",e))</script></body></html>
|
||||||
@ -9,14 +9,67 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-content">
|
<div class="panel-content">
|
||||||
<div class="task-toolbar">
|
<div class="task-toolbar">
|
||||||
<button id="refreshTasksBtn" type="button">
|
<button id="refreshCadBtn" type="button">Refresh Board</button>
|
||||||
Refresh Tasks
|
</div>
|
||||||
|
<div id="cadStatusMessage" class="task-status-message"></div>
|
||||||
|
<div class="cad-tabs" role="tablist" aria-label="CAD Sections">
|
||||||
|
<button
|
||||||
|
id="tabContractsBtn"
|
||||||
|
class="cad-tab is-active"
|
||||||
|
type="button"
|
||||||
|
data-tab="contracts"
|
||||||
|
>
|
||||||
|
Contracts
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="tabGroupsBtn"
|
||||||
|
class="cad-tab"
|
||||||
|
type="button"
|
||||||
|
data-tab="groups"
|
||||||
|
>
|
||||||
|
Groups
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="tabActivityBtn"
|
||||||
|
class="cad-tab"
|
||||||
|
type="button"
|
||||||
|
data-tab="activity"
|
||||||
|
>
|
||||||
|
Activity
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="taskStatusMessage" class="task-status-message"></div>
|
<div class="cad-tab-panels">
|
||||||
<div id="taskList" class="task-list">
|
<div
|
||||||
<div class="placeholder-message">
|
id="contractsPanel"
|
||||||
<p>Loading available tasks...</p>
|
class="cad-section is-active"
|
||||||
|
data-panel="contracts"
|
||||||
|
>
|
||||||
|
<div class="cad-section-header">Contracts</div>
|
||||||
|
<div id="taskList" class="task-list">
|
||||||
|
<div class="placeholder-message">
|
||||||
|
<p>Loading contracts...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="groupsPanel" class="cad-section" data-panel="groups">
|
||||||
|
<div class="cad-section-header">Groups</div>
|
||||||
|
<div id="groupList" class="task-list">
|
||||||
|
<div class="placeholder-message">
|
||||||
|
<p>Loading groups...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="activityPanel"
|
||||||
|
class="cad-section"
|
||||||
|
data-panel="activity"
|
||||||
|
>
|
||||||
|
<div class="cad-section-header">Activity</div>
|
||||||
|
<div id="activityList" class="task-list">
|
||||||
|
<div class="placeholder-message">
|
||||||
|
<p>No recent activity.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,94 +1,300 @@
|
|||||||
window.cadTasks = {
|
window.cadTasks = {
|
||||||
tasks: [],
|
contracts: [],
|
||||||
|
groups: [],
|
||||||
|
activity: [],
|
||||||
|
session: {},
|
||||||
|
activeTab: "contracts",
|
||||||
|
statuses: [
|
||||||
|
"available",
|
||||||
|
"en_route",
|
||||||
|
"on_task",
|
||||||
|
"holding",
|
||||||
|
"danger",
|
||||||
|
"refit",
|
||||||
|
"offline",
|
||||||
|
],
|
||||||
init() {
|
init() {
|
||||||
const refreshBtn = document.getElementById("refreshTasksBtn");
|
const refreshBtn = document.getElementById("refreshCadBtn");
|
||||||
if (refreshBtn) {
|
if (refreshBtn) {
|
||||||
refreshBtn.addEventListener("click", () => this.refresh());
|
refreshBtn.addEventListener("click", () => this.refresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
window.ForgeBridge.on("cad::tasks::hydrate", (payload) => {
|
document.querySelectorAll(".cad-tab").forEach((tab) => {
|
||||||
this.setTasks(payload.tasks || []);
|
tab.addEventListener("click", () => {
|
||||||
|
this.setActiveTab(tab.dataset.tab || "contracts");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
window.ForgeBridge.on("cad::tasks::accept::response", (payload) => {
|
window.ForgeBridge.on("cad::hydrate", (payload) => {
|
||||||
this.handleAcceptResponse(!!payload.success, payload.message || "");
|
this.setHydratePayload(payload || {});
|
||||||
|
});
|
||||||
|
|
||||||
|
window.ForgeBridge.on("cad::assignment::response", (payload) => {
|
||||||
|
this.handleServerResponse(!!payload.success, payload.message || "");
|
||||||
|
});
|
||||||
|
|
||||||
|
window.ForgeBridge.on("cad::group::response", (payload) => {
|
||||||
|
this.handleServerResponse(!!payload.success, payload.message || "");
|
||||||
});
|
});
|
||||||
|
|
||||||
window.ForgeBridge.ready({ loaded: true });
|
window.ForgeBridge.ready({ loaded: true });
|
||||||
},
|
},
|
||||||
setTasks(tasks) {
|
setActiveTab(tabName) {
|
||||||
this.tasks = Array.isArray(tasks) ? tasks : [];
|
this.activeTab = tabName || "contracts";
|
||||||
const statusEl = document.getElementById("taskStatusMessage");
|
|
||||||
|
document.querySelectorAll(".cad-tab").forEach((tab) => {
|
||||||
|
tab.classList.toggle(
|
||||||
|
"is-active",
|
||||||
|
tab.dataset.tab === this.activeTab,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll("[data-panel]").forEach((panel) => {
|
||||||
|
panel.classList.toggle(
|
||||||
|
"is-active",
|
||||||
|
panel.dataset.panel === this.activeTab,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setHydratePayload(payload) {
|
||||||
|
this.contracts = Array.isArray(payload.contracts)
|
||||||
|
? payload.contracts
|
||||||
|
: [];
|
||||||
|
this.groups = Array.isArray(payload.groups) ? payload.groups : [];
|
||||||
|
this.activity = Array.isArray(payload.activity) ? payload.activity : [];
|
||||||
|
this.session =
|
||||||
|
payload.session && typeof payload.session === "object"
|
||||||
|
? payload.session
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const statusEl = document.getElementById("cadStatusMessage");
|
||||||
if (
|
if (
|
||||||
statusEl &&
|
statusEl &&
|
||||||
(!statusEl.dataset.type || statusEl.dataset.type === "info")
|
(!statusEl.dataset.type || statusEl.dataset.type === "info")
|
||||||
) {
|
) {
|
||||||
this.setStatus("", "");
|
this.setStatus("", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
setStatus(message, type) {
|
setStatus(message, type) {
|
||||||
const statusEl = document.getElementById("taskStatusMessage");
|
const statusEl = document.getElementById("cadStatusMessage");
|
||||||
if (!statusEl) {
|
if (!statusEl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
statusEl.textContent = message || "";
|
statusEl.textContent = message || "";
|
||||||
statusEl.dataset.type = type || "info";
|
statusEl.dataset.type = type || "";
|
||||||
},
|
},
|
||||||
handleAcceptResponse(success, message) {
|
handleServerResponse(success, message) {
|
||||||
this.setStatus(
|
this.setStatus(
|
||||||
message || (success ? "Task accepted." : "Unable to accept task."),
|
message ||
|
||||||
|
(success ? "CAD update succeeded." : "CAD update failed."),
|
||||||
success ? "success" : "error",
|
success ? "success" : "error",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
refresh() {
|
refresh() {
|
||||||
this.setStatus("Refreshing tasks...", "info");
|
this.setStatus("Refreshing board...", "info");
|
||||||
window.mapUI.sendEvent("cad::tasks::refresh", {});
|
window.mapUI.sendEvent("cad::refresh", {});
|
||||||
},
|
},
|
||||||
acceptTask(taskID) {
|
assignTask(taskID) {
|
||||||
this.setStatus("Submitting acceptance...", "info");
|
const selector = document.getElementById(`assign-group-${taskID}`);
|
||||||
window.mapUI.sendEvent("cad::tasks::accept", { taskID: taskID });
|
if (!selector || !selector.value) {
|
||||||
|
this.setStatus(
|
||||||
|
"Select a group before assigning a contract.",
|
||||||
|
"error",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setStatus("Submitting assignment...", "info");
|
||||||
|
window.mapUI.sendEvent("cad::tasks::assign", {
|
||||||
|
taskID: taskID,
|
||||||
|
groupID: selector.value,
|
||||||
|
note: "",
|
||||||
|
});
|
||||||
},
|
},
|
||||||
render() {
|
acknowledgeTask(taskID) {
|
||||||
|
this.setStatus("Acknowledging contract...", "info");
|
||||||
|
window.mapUI.sendEvent("cad::tasks::acknowledge", { taskID: taskID });
|
||||||
|
},
|
||||||
|
declineTask(taskID) {
|
||||||
|
this.setStatus("Declining contract...", "info");
|
||||||
|
window.mapUI.sendEvent("cad::tasks::decline", { taskID: taskID });
|
||||||
|
},
|
||||||
|
updateGroupStatus(groupID, status) {
|
||||||
|
this.setStatus("Updating group status...", "info");
|
||||||
|
window.mapUI.sendEvent("cad::groups::status", {
|
||||||
|
groupID: groupID,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getPlayerGroupId() {
|
||||||
|
return this.session.groupId || "";
|
||||||
|
},
|
||||||
|
canDispatch() {
|
||||||
|
return !!this.session.isDispatcher;
|
||||||
|
},
|
||||||
|
isLeader() {
|
||||||
|
return !!this.session.isLeader;
|
||||||
|
},
|
||||||
|
renderContracts() {
|
||||||
const listEl = document.getElementById("taskList");
|
const listEl = document.getElementById("taskList");
|
||||||
if (!listEl) {
|
if (!listEl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.tasks.length) {
|
if (!this.contracts.length) {
|
||||||
listEl.innerHTML =
|
listEl.innerHTML =
|
||||||
'<div class="placeholder-message"><p>No active tasks are available.</p></div>';
|
'<div class="placeholder-message"><p>No active contracts are available.</p></div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
listEl.innerHTML = this.tasks
|
const currentGroupId = this.getPlayerGroupId();
|
||||||
|
listEl.innerHTML = this.contracts
|
||||||
.map((task) => {
|
.map((task) => {
|
||||||
|
const taskId = task.taskId || task.taskID || "";
|
||||||
const position = Array.isArray(task.position)
|
const position = Array.isArray(task.position)
|
||||||
? task.position
|
? task.position
|
||||||
: [0, 0, 0];
|
: [0, 0, 0];
|
||||||
const accepted = !!task.accepted;
|
const assignedGroupId = task.assignedGroupId || "";
|
||||||
const ownerLabel = accepted
|
const assignmentState = task.assignmentState || "unassigned";
|
||||||
? `Assigned: ${task.orgID || "Unknown"}`
|
const assignedGroup = this.groups.find(
|
||||||
: "Available";
|
(group) => group.groupId === assignedGroupId,
|
||||||
|
);
|
||||||
|
const isAssignedToLeader =
|
||||||
|
this.isLeader() && assignedGroupId === currentGroupId;
|
||||||
|
const groupOptions = this.groups
|
||||||
|
.map(
|
||||||
|
(group) =>
|
||||||
|
`<option value="${group.groupId}">${group.callsign || group.groupId}</option>`,
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="task-card" data-task-id="${task.taskID}">
|
<div class="task-card" data-task-id="${taskId}">
|
||||||
<div class="task-card-header">
|
<div class="task-card-header">
|
||||||
<strong>${task.title || task.taskID}</strong>
|
<strong>${task.title || taskId}</strong>
|
||||||
<span class="task-type">${task.type || "task"}</span>
|
<span class="task-type">${task.type || "task"}</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="task-description">${task.description || ""}</p>
|
<p class="task-description">${task.description || ""}</p>
|
||||||
<div class="task-meta">
|
<div class="task-meta">
|
||||||
<span>${ownerLabel}</span>
|
<span>${assignmentState === "unassigned" ? "Available" : `${assignmentState}: ${assignedGroup ? assignedGroup.callsign : assignedGroupId}`}</span>
|
||||||
<span>X: ${Math.round(position[0] || 0)} Y: ${Math.round(position[1] || 0)}</span>
|
<span>X: ${Math.round(position[0] || 0)} Y: ${Math.round(position[1] || 0)}</span>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="task-accept-btn" ${accepted ? "disabled" : ""} onclick="window.cadTasks.acceptTask('${task.taskID}')">${accepted ? "Accepted" : "Accept"}</button>
|
${
|
||||||
|
this.canDispatch()
|
||||||
|
? `<div class="task-action-stack">
|
||||||
|
<select id="assign-group-${taskId}" class="cad-select">
|
||||||
|
<option value="">Assign to group</option>
|
||||||
|
${groupOptions}
|
||||||
|
</select>
|
||||||
|
<button type="button" class="task-accept-btn" onclick="window.cadTasks.assignTask('${taskId}')">Assign Contract</button>
|
||||||
|
</div>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
isAssignedToLeader && assignmentState === "assigned"
|
||||||
|
? `<div class="task-action-row">
|
||||||
|
<button type="button" class="task-accept-btn" onclick="window.cadTasks.acknowledgeTask('${taskId}')">Acknowledge</button>
|
||||||
|
<button type="button" class="task-secondary-btn" onclick="window.cadTasks.declineTask('${taskId}')">Decline</button>
|
||||||
|
</div>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
},
|
},
|
||||||
|
renderGroups() {
|
||||||
|
const listEl = document.getElementById("groupList");
|
||||||
|
if (!listEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.groups.length) {
|
||||||
|
listEl.innerHTML =
|
||||||
|
'<div class="placeholder-message"><p>No active groups are available.</p></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentGroupId = this.getPlayerGroupId();
|
||||||
|
listEl.innerHTML = this.groups
|
||||||
|
.map((group) => {
|
||||||
|
const canUpdate =
|
||||||
|
this.canDispatch() ||
|
||||||
|
(this.isLeader() && group.groupId === currentGroupId);
|
||||||
|
const statusOptions = this.statuses
|
||||||
|
.map(
|
||||||
|
(status) =>
|
||||||
|
`<option value="${status}" ${status === group.status ? "selected" : ""}>${status.replaceAll("_", " ")}</option>`,
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="task-card" data-group-id="${group.groupId}">
|
||||||
|
<div class="task-card-header">
|
||||||
|
<strong>${group.callsign || group.groupId}</strong>
|
||||||
|
<span class="task-type">${group.role || "group"}</span>
|
||||||
|
</div>
|
||||||
|
<div class="task-meta">
|
||||||
|
<span>Leader: ${group.leaderName || "Unknown"}</span>
|
||||||
|
<span>Status: ${group.status || "unknown"}</span>
|
||||||
|
</div>
|
||||||
|
<div class="task-meta">
|
||||||
|
<span>Org: ${group.orgId || "default"}</span>
|
||||||
|
<span>Task: ${group.currentTaskId || "None"}</span>
|
||||||
|
</div>
|
||||||
|
${
|
||||||
|
canUpdate
|
||||||
|
? `<div class="task-action-stack">
|
||||||
|
<select id="group-status-${group.groupId}" class="cad-select">
|
||||||
|
${statusOptions}
|
||||||
|
</select>
|
||||||
|
<button type="button" class="task-accept-btn" onclick="window.cadTasks.updateGroupStatus('${group.groupId}', document.getElementById('group-status-${group.groupId}').value)">Update Status</button>
|
||||||
|
</div>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
},
|
||||||
|
renderActivity() {
|
||||||
|
const listEl = document.getElementById("activityList");
|
||||||
|
if (!listEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.activity.length) {
|
||||||
|
listEl.innerHTML =
|
||||||
|
'<div class="placeholder-message"><p>No recent activity.</p></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listEl.innerHTML = this.activity
|
||||||
|
.slice()
|
||||||
|
.reverse()
|
||||||
|
.slice(0, 8)
|
||||||
|
.map(
|
||||||
|
(entry) => `
|
||||||
|
<div class="task-card">
|
||||||
|
<div class="task-card-header">
|
||||||
|
<strong>${entry.type || "activity"}</strong>
|
||||||
|
<span class="task-type">${Math.round(entry.timestamp || 0)}s</span>
|
||||||
|
</div>
|
||||||
|
<p class="task-description">${entry.message || ""}</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
this.renderContracts();
|
||||||
|
this.renderGroups();
|
||||||
|
this.renderActivity();
|
||||||
|
this.setActiveTab(this.activeTab);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
window.cadTasks.init();
|
window.cadTasks.init();
|
||||||
|
|||||||
@ -59,23 +59,82 @@ body {
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cad-tabs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-tab {
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
background: rgba(20, 27, 33, 0.88);
|
||||||
|
color: rgba(243, 246, 249, 0.78);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-tab:hover {
|
||||||
|
background: rgba(31, 40, 47, 0.94);
|
||||||
|
color: #f3f6f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-tab.is-active {
|
||||||
|
border-color: rgba(91, 187, 255, 0.42);
|
||||||
|
background: rgba(15, 40, 58, 0.96);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-tab-panels {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-section {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-section.is-active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cad-section-header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
.task-toolbar button,
|
.task-toolbar button,
|
||||||
.task-accept-btn {
|
.task-accept-btn,
|
||||||
|
.task-secondary-btn,
|
||||||
|
.cad-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
background: rgba(30, 37, 43, 0.9);
|
background: rgba(30, 37, 43, 0.9);
|
||||||
color: #f3f6f9;
|
color: #f3f6f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-toolbar button,
|
||||||
|
.task-accept-btn,
|
||||||
|
.task-secondary-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-toolbar button:hover,
|
.task-toolbar button:hover,
|
||||||
.task-accept-btn:hover {
|
.task-accept-btn:hover,
|
||||||
|
.task-secondary-btn:hover {
|
||||||
background: rgba(46, 57, 66, 0.95);
|
background: rgba(46, 57, 66, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-toolbar button:disabled,
|
.task-toolbar button:disabled,
|
||||||
.task-accept-btn:disabled {
|
.task-accept-btn:disabled,
|
||||||
|
.task-secondary-btn:disabled {
|
||||||
opacity: 0.55;
|
opacity: 0.55;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
@ -101,6 +160,17 @@ body {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-action-stack,
|
||||||
|
.task-action-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-action-row {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.task-card {
|
.task-card {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
@ -134,3 +204,7 @@ body {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-secondary-btn {
|
||||||
|
background: rgba(60, 48, 45, 0.92);
|
||||||
|
}
|
||||||
|
|||||||
1
arma/server/addons/cad/$PBOPREFIX$
Normal file
1
arma/server/addons/cad/$PBOPREFIX$
Normal file
@ -0,0 +1 @@
|
|||||||
|
forge\forge_server\addons\cad
|
||||||
5
arma/server/addons/cad/CfgEventHandlers.hpp
Normal file
5
arma/server/addons/cad/CfgEventHandlers.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class Extended_PreInit_EventHandlers {
|
||||||
|
class ADDON {
|
||||||
|
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
|
||||||
|
};
|
||||||
|
};
|
||||||
1
arma/server/addons/cad/XEH_PREP.hpp
Normal file
1
arma/server/addons/cad/XEH_PREP.hpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
PREP(initCadStore);
|
||||||
86
arma/server/addons/cad/XEH_preInit.sqf
Normal file
86
arma/server/addons/cad/XEH_preInit.sqf
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
|
PREP_RECOMPILE_START;
|
||||||
|
#include "XEH_PREP.hpp"
|
||||||
|
PREP_RECOMPILE_END;
|
||||||
|
|
||||||
|
call FUNC(initCadStore);
|
||||||
|
|
||||||
|
[QGVAR(requestHydrateCad), {
|
||||||
|
params [["_uid", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "") exitWith {
|
||||||
|
["WARNING", "CAD hydrate request received with empty UID."] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith {};
|
||||||
|
|
||||||
|
private _payload = GVAR(CadStore) call ["buildHydratePayload", [_uid]];
|
||||||
|
[CRPC(cad,responseHydrateCad), [_payload], _player] call CFUNC(targetEvent);
|
||||||
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
|
[QGVAR(requestAssignCadTask), {
|
||||||
|
params [
|
||||||
|
["_uid", "", [""]],
|
||||||
|
["_taskID", "", [""]],
|
||||||
|
["_groupID", "", [""]],
|
||||||
|
["_note", "", [""]]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "" || { _taskID isEqualTo "" } || { _groupID isEqualTo "" }) exitWith {
|
||||||
|
["WARNING", "Invalid CAD task assignment payload."] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith {};
|
||||||
|
|
||||||
|
private _result = GVAR(CadStore) call ["assignTaskToGroup", [_uid, _taskID, _groupID, _note]];
|
||||||
|
[CRPC(cad,responseCadAssignment), [_result], _player] call CFUNC(targetEvent);
|
||||||
|
[CRPC(cad,responseHydrateCad), [GVAR(CadStore) call ["buildHydratePayload", [_uid]]], _player] call CFUNC(targetEvent);
|
||||||
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
|
[QGVAR(requestAcknowledgeCadTask), {
|
||||||
|
params [["_uid", "", [""]], ["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "" || { _taskID isEqualTo "" }) exitWith {
|
||||||
|
["WARNING", "Invalid CAD acknowledge payload."] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith {};
|
||||||
|
|
||||||
|
private _result = GVAR(CadStore) call ["acknowledgeTask", [_uid, _taskID]];
|
||||||
|
[CRPC(cad,responseCadAssignment), [_result], _player] call CFUNC(targetEvent);
|
||||||
|
[CRPC(cad,responseHydrateCad), [GVAR(CadStore) call ["buildHydratePayload", [_uid]]], _player] call CFUNC(targetEvent);
|
||||||
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
|
[QGVAR(requestDeclineCadTask), {
|
||||||
|
params [["_uid", "", [""]], ["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "" || { _taskID isEqualTo "" }) exitWith {
|
||||||
|
["WARNING", "Invalid CAD decline payload."] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith {};
|
||||||
|
|
||||||
|
private _result = GVAR(CadStore) call ["declineTask", [_uid, _taskID]];
|
||||||
|
[CRPC(cad,responseCadAssignment), [_result], _player] call CFUNC(targetEvent);
|
||||||
|
[CRPC(cad,responseHydrateCad), [GVAR(CadStore) call ["buildHydratePayload", [_uid]]], _player] call CFUNC(targetEvent);
|
||||||
|
}] call CFUNC(addEventHandler);
|
||||||
|
|
||||||
|
[QGVAR(requestUpdateCadGroupStatus), {
|
||||||
|
params [["_uid", "", [""]], ["_groupID", "", [""]], ["_status", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "" || { _groupID isEqualTo "" } || { _status isEqualTo "" }) exitWith {
|
||||||
|
["WARNING", "Invalid CAD group status payload."] call EFUNC(common,log);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith {};
|
||||||
|
|
||||||
|
private _result = GVAR(CadStore) call ["updateGroupStatus", [_uid, _groupID, _status]];
|
||||||
|
[CRPC(cad,responseCadGroupUpdate), [_result], _player] call CFUNC(targetEvent);
|
||||||
|
[CRPC(cad,responseHydrateCad), [GVAR(CadStore) call ["buildHydratePayload", [_uid]]], _player] call CFUNC(targetEvent);
|
||||||
|
}] call CFUNC(addEventHandler);
|
||||||
23
arma/server/addons/cad/config.cpp
Normal file
23
arma/server/addons/cad/config.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
|
class CfgPatches {
|
||||||
|
class ADDON {
|
||||||
|
author = AUTHOR;
|
||||||
|
authors[] = {"IDSolutions"};
|
||||||
|
url = ECSTRING(main,url);
|
||||||
|
name = COMPONENT_NAME;
|
||||||
|
requiredVersion = REQUIRED_VERSION;
|
||||||
|
requiredAddons[] = {
|
||||||
|
"forge_server_main",
|
||||||
|
"forge_server_common",
|
||||||
|
"forge_server_actor",
|
||||||
|
"forge_server_org",
|
||||||
|
"forge_server_task"
|
||||||
|
};
|
||||||
|
units[] = {};
|
||||||
|
weapons[] = {};
|
||||||
|
VERSION_CONFIG;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "CfgEventHandlers.hpp"
|
||||||
498
arma/server/addons/cad/functions/fnc_initCadStore.sqf
Normal file
498
arma/server/addons/cad/functions/fnc_initCadStore.sqf
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
#include "..\script_component.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File: fnc_initCadStore.sqf
|
||||||
|
* Author: IDSolutions
|
||||||
|
* Date: 2026-03-29
|
||||||
|
* Public: Yes
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Initializes the CAD store for group tracking, assignment state,
|
||||||
|
* activity history, and CAD hydrate payloads.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* CAD store object [HASHMAP OBJECT]
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* call forge_server_cad_fnc_initCadStore
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma hemtt ignore_variables ["_self"]
|
||||||
|
GVAR(CadStoreBaseClass) = compileFinal createHashMapFromArray [
|
||||||
|
["#type", "CadStoreBaseClass"],
|
||||||
|
["#create", compileFinal {
|
||||||
|
_self set ["groupRegistry", createHashMap];
|
||||||
|
_self set ["assignmentRegistry", createHashMap];
|
||||||
|
_self set ["activityRegistry", []];
|
||||||
|
_self set ["validStatuses", [
|
||||||
|
"available",
|
||||||
|
"en_route",
|
||||||
|
"on_task",
|
||||||
|
"holding",
|
||||||
|
"danger",
|
||||||
|
"refit",
|
||||||
|
"offline"
|
||||||
|
]];
|
||||||
|
["INFO", "CAD Store Initialized!"] call EFUNC(common,log);
|
||||||
|
}],
|
||||||
|
["appendActivity", compileFinal {
|
||||||
|
params [
|
||||||
|
["_type", "", [""]],
|
||||||
|
["_message", "", [""]],
|
||||||
|
["_taskID", "", [""]],
|
||||||
|
["_groupID", "", [""]],
|
||||||
|
["_actorUid", "", [""]]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_type isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
private _activityRegistry = +(_self getOrDefault ["activityRegistry", []]);
|
||||||
|
_activityRegistry pushBack createHashMapFromArray [
|
||||||
|
["type", _type],
|
||||||
|
["message", _message],
|
||||||
|
["timestamp", serverTime],
|
||||||
|
["taskId", _taskID],
|
||||||
|
["groupId", _groupID],
|
||||||
|
["actorUid", _actorUid]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ((count _activityRegistry) > 50) then {
|
||||||
|
_activityRegistry deleteRange [0, (count _activityRegistry) - 50];
|
||||||
|
};
|
||||||
|
|
||||||
|
_self set ["activityRegistry", _activityRegistry];
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["resolveGroupId", compileFinal {
|
||||||
|
params [["_group", grpNull, [grpNull]]];
|
||||||
|
|
||||||
|
if (isNull _group) exitWith { "" };
|
||||||
|
|
||||||
|
private _leader = leader _group;
|
||||||
|
private _leaderUid = if (isNull _leader) then { "" } else { getPlayerUID _leader };
|
||||||
|
if (_leaderUid isNotEqualTo "") exitWith { format ["group:%1", _leaderUid] };
|
||||||
|
|
||||||
|
private _groupLabel = groupId _group;
|
||||||
|
if (_groupLabel isNotEqualTo "") exitWith { format ["group:%1", _groupLabel] };
|
||||||
|
|
||||||
|
str _group
|
||||||
|
}],
|
||||||
|
["canDispatch", compileFinal {
|
||||||
|
params [["_uid", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "") exitWith { false };
|
||||||
|
|
||||||
|
private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
|
||||||
|
if (_actor isEqualTo createHashMap) then {
|
||||||
|
_actor = EGVAR(actor,ActorStore) call ["init", [_uid]];
|
||||||
|
};
|
||||||
|
|
||||||
|
private _orgID = _actor getOrDefault ["organization", "default"];
|
||||||
|
private _org = EGVAR(org,Registry) getOrDefault [_orgID, createHashMap];
|
||||||
|
if (_org isEqualTo createHashMap) then {
|
||||||
|
_org = EGVAR(org,OrgStore) call ["loadById", [_orgID]];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_org getOrDefault ["owner", ""] isEqualTo _uid) exitWith { true };
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith { false };
|
||||||
|
|
||||||
|
(_orgID isEqualTo "default") && { vehicleVarName _player isEqualTo "ceo" }
|
||||||
|
}],
|
||||||
|
["getCurrentTaskIdForGroup", compileFinal {
|
||||||
|
params [["_groupID", "", [""]]];
|
||||||
|
|
||||||
|
if (_groupID isEqualTo "") exitWith { "" };
|
||||||
|
|
||||||
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
||||||
|
private _taskID = "";
|
||||||
|
{
|
||||||
|
if ((_y getOrDefault ["groupId", ""]) isNotEqualTo _groupID) then { continue; };
|
||||||
|
if !((_y getOrDefault ["state", ""]) in ["assigned", "acknowledged"]) then { continue; };
|
||||||
|
if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_x]]) isNotEqualTo "active") then { continue; };
|
||||||
|
|
||||||
|
_taskID = _x;
|
||||||
|
} forEach _assignmentRegistry;
|
||||||
|
|
||||||
|
_taskID
|
||||||
|
}],
|
||||||
|
["syncGroups", compileFinal {
|
||||||
|
private _previousRegistry = _self getOrDefault ["groupRegistry", createHashMap];
|
||||||
|
private _nextRegistry = createHashMap;
|
||||||
|
|
||||||
|
{
|
||||||
|
if (side _x isNotEqualTo west) then { continue; };
|
||||||
|
|
||||||
|
private _members = (units _x) select { isPlayer _x };
|
||||||
|
if (_members isEqualTo []) then { continue; };
|
||||||
|
|
||||||
|
private _leader = leader _x;
|
||||||
|
if (isNull _leader || { !isPlayer _leader }) then {
|
||||||
|
_leader = _members # 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
private _groupID = _self call ["resolveGroupId", [_x]];
|
||||||
|
if (_groupID isEqualTo "") then { continue; };
|
||||||
|
|
||||||
|
private _leaderUid = getPlayerUID _leader;
|
||||||
|
private _actor = EGVAR(actor,Registry) getOrDefault [_leaderUid, createHashMap];
|
||||||
|
if (_actor isEqualTo createHashMap && { _leaderUid isNotEqualTo "" }) then {
|
||||||
|
_actor = EGVAR(actor,ActorStore) call ["init", [_leaderUid]];
|
||||||
|
};
|
||||||
|
|
||||||
|
private _orgID = _actor getOrDefault ["organization", "default"];
|
||||||
|
if (_orgID isEqualTo "") then { _orgID = "default"; };
|
||||||
|
|
||||||
|
private _existingRecord = +(_previousRegistry getOrDefault [_groupID, createHashMap]);
|
||||||
|
private _memberUids = [];
|
||||||
|
{
|
||||||
|
private _memberUid = getPlayerUID _x;
|
||||||
|
if (_memberUid isNotEqualTo "") then {
|
||||||
|
_memberUids pushBack _memberUid;
|
||||||
|
};
|
||||||
|
} forEach _members;
|
||||||
|
|
||||||
|
private _record = createHashMapFromArray [
|
||||||
|
["groupId", _groupID],
|
||||||
|
["callsign", [groupId _x, _groupID] select ((groupId _x) isEqualTo "")],
|
||||||
|
["leaderUid", _leaderUid],
|
||||||
|
["leaderName", name _leader],
|
||||||
|
["memberUids", _memberUids],
|
||||||
|
["orgId", _orgID],
|
||||||
|
["role", _existingRecord getOrDefault ["role", "infantry"]],
|
||||||
|
["status", _existingRecord getOrDefault ["status", "available"]],
|
||||||
|
["position", getPosATL _leader],
|
||||||
|
["currentTaskId", _self call ["getCurrentTaskIdForGroup", [_groupID]]],
|
||||||
|
["lastUpdate", serverTime]
|
||||||
|
];
|
||||||
|
|
||||||
|
_nextRegistry set [_groupID, _record];
|
||||||
|
} forEach allGroups;
|
||||||
|
|
||||||
|
_self set ["groupRegistry", _nextRegistry];
|
||||||
|
_nextRegistry
|
||||||
|
}],
|
||||||
|
["getGroupRecord", compileFinal {
|
||||||
|
params [["_groupID", "", [""]]];
|
||||||
|
|
||||||
|
if (_groupID isEqualTo "") exitWith { createHashMap };
|
||||||
|
|
||||||
|
private _groupRegistry = _self call ["syncGroups", []];
|
||||||
|
+(_groupRegistry getOrDefault [_groupID, createHashMap])
|
||||||
|
}],
|
||||||
|
["getPlayerGroupId", compileFinal {
|
||||||
|
params [["_uid", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "") exitWith { "" };
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith { "" };
|
||||||
|
|
||||||
|
_self call ["resolveGroupId", [group _player]]
|
||||||
|
}],
|
||||||
|
["isGroupLeader", compileFinal {
|
||||||
|
params [["_uid", "", [""]], ["_groupID", "", [""]]];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "" || { _groupID isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
private _groupRecord = _self call ["getGroupRecord", [_groupID]];
|
||||||
|
(_groupRecord getOrDefault ["leaderUid", ""]) isEqualTo _uid
|
||||||
|
}],
|
||||||
|
["pruneAssignments", compileFinal {
|
||||||
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
||||||
|
private _keysToRemove = [];
|
||||||
|
|
||||||
|
{
|
||||||
|
private _status = EGVAR(task,TaskStore) call ["getTaskStatus", [_x]];
|
||||||
|
if !(_status in ["active", ""]) then {
|
||||||
|
_keysToRemove pushBack _x;
|
||||||
|
};
|
||||||
|
} forEach _assignmentRegistry;
|
||||||
|
|
||||||
|
{
|
||||||
|
_assignmentRegistry deleteAt _x;
|
||||||
|
} forEach _keysToRemove;
|
||||||
|
|
||||||
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
||||||
|
count _keysToRemove
|
||||||
|
}],
|
||||||
|
["buildContracts", compileFinal {
|
||||||
|
_self call ["pruneAssignments", []];
|
||||||
|
|
||||||
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
||||||
|
private _contracts = [];
|
||||||
|
|
||||||
|
{
|
||||||
|
private _taskID = _x getOrDefault ["taskID", ""];
|
||||||
|
if (_taskID isEqualTo "") then { continue; };
|
||||||
|
|
||||||
|
private _assignment = _assignmentRegistry getOrDefault [_taskID, createHashMap];
|
||||||
|
private _entry = +_x;
|
||||||
|
_entry set ["taskId", _taskID];
|
||||||
|
_entry set ["assignedGroupId", _assignment getOrDefault ["groupId", ""]];
|
||||||
|
_entry set ["assignmentState", [_assignment getOrDefault ["state", ""], "unassigned"] select (_assignment isEqualTo createHashMap)];
|
||||||
|
_contracts pushBack _entry;
|
||||||
|
} forEach (EGVAR(task,TaskStore) call ["getActiveTaskCatalog", []]);
|
||||||
|
|
||||||
|
_contracts
|
||||||
|
}],
|
||||||
|
["buildGroups", compileFinal {
|
||||||
|
private _groupRegistry = _self call ["syncGroups", []];
|
||||||
|
private _groups = [];
|
||||||
|
|
||||||
|
{
|
||||||
|
_groups pushBack +_y;
|
||||||
|
} forEach _groupRegistry;
|
||||||
|
|
||||||
|
_groups
|
||||||
|
}],
|
||||||
|
["buildHydratePayload", compileFinal {
|
||||||
|
params [["_uid", "", [""]]];
|
||||||
|
|
||||||
|
private _activity = +(_self getOrDefault ["activityRegistry", []]);
|
||||||
|
private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
|
||||||
|
if (_actor isEqualTo createHashMap && { _uid isNotEqualTo "" }) then {
|
||||||
|
_actor = EGVAR(actor,ActorStore) call ["init", [_uid]];
|
||||||
|
};
|
||||||
|
|
||||||
|
createHashMapFromArray [
|
||||||
|
["groups", _self call ["buildGroups", []]],
|
||||||
|
["contracts", _self call ["buildContracts", []]],
|
||||||
|
["assignments", values (_self getOrDefault ["assignmentRegistry", createHashMap])],
|
||||||
|
["activity", _activity],
|
||||||
|
["session", createHashMapFromArray [
|
||||||
|
["uid", _uid],
|
||||||
|
["orgId", _actor getOrDefault ["organization", "default"]],
|
||||||
|
["isDispatcher", _self call ["canDispatch", [_uid]]],
|
||||||
|
["groupId", _self call ["getPlayerGroupId", [_uid]]],
|
||||||
|
["isLeader", _self call ["isGroupLeader", [_uid, _self call ["getPlayerGroupId", [_uid]]]]]
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
["notifyPlayer", compileFinal {
|
||||||
|
params [
|
||||||
|
["_uid", "", [""]],
|
||||||
|
["_type", "info", [""]],
|
||||||
|
["_title", "CAD", [""]],
|
||||||
|
["_message", "", [""]]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_uid isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
||||||
|
|
||||||
|
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||||
|
if (_player isEqualTo objNull) exitWith { false };
|
||||||
|
|
||||||
|
[CRPC(notifications,recieveNotification), [_type, _title, _message], _player] call CFUNC(targetEvent);
|
||||||
|
true
|
||||||
|
}],
|
||||||
|
["assignTaskToGroup", compileFinal {
|
||||||
|
params [
|
||||||
|
["_requesterUid", "", [""]],
|
||||||
|
["_taskID", "", [""]],
|
||||||
|
["_groupID", "", [""]],
|
||||||
|
["_note", "", [""]]
|
||||||
|
];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["message", "Unable to assign task."],
|
||||||
|
["assignment", createHashMap]
|
||||||
|
];
|
||||||
|
|
||||||
|
if !(_self call ["canDispatch", [_requesterUid]]) exitWith {
|
||||||
|
_result set ["message", "You are not authorized to assign contracts."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((EGVAR(task,TaskStore) call ["getTaskStatus", [_taskID]]) isNotEqualTo "active") exitWith {
|
||||||
|
_result set ["message", "Task is no longer active."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _groupRecord = _self call ["getGroupRecord", [_groupID]];
|
||||||
|
if (_groupRecord isEqualTo createHashMap) exitWith {
|
||||||
|
_result set ["message", "Selected group is unavailable."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _leaderUid = _groupRecord getOrDefault ["leaderUid", ""];
|
||||||
|
if (_leaderUid isEqualTo "") exitWith {
|
||||||
|
_result set ["message", "Selected group has no online leader."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _requesterPlayer = [_requesterUid] call EFUNC(common,getPlayer);
|
||||||
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
||||||
|
private _assignment = createHashMapFromArray [
|
||||||
|
["taskId", _taskID],
|
||||||
|
["groupId", _groupID],
|
||||||
|
["assignedByUid", _requesterUid],
|
||||||
|
["assignedByName", ["Dispatcher", name _requesterPlayer] select (_requesterPlayer isNotEqualTo objNull)],
|
||||||
|
["assignedAt", serverTime],
|
||||||
|
["state", "assigned"],
|
||||||
|
["note", _note]
|
||||||
|
];
|
||||||
|
|
||||||
|
_assignmentRegistry set [_taskID, _assignment];
|
||||||
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
||||||
|
|
||||||
|
_self call ["appendActivity", [
|
||||||
|
"task_assigned",
|
||||||
|
format ["%1 assigned %2 to %3.", _assignment get "assignedByName", _taskID, _groupRecord getOrDefault ["callsign", _groupID]],
|
||||||
|
_taskID,
|
||||||
|
_groupID,
|
||||||
|
_requesterUid
|
||||||
|
]];
|
||||||
|
|
||||||
|
_self call ["notifyPlayer", [
|
||||||
|
_leaderUid,
|
||||||
|
"info",
|
||||||
|
"Tasks",
|
||||||
|
format ["Contract assigned: %1. Open CAD to review and acknowledge.", _taskID]
|
||||||
|
]];
|
||||||
|
|
||||||
|
_result set ["success", true];
|
||||||
|
_result set ["message", "Task assigned."];
|
||||||
|
_result set ["assignment", _assignment];
|
||||||
|
_result
|
||||||
|
}],
|
||||||
|
["acknowledgeTask", compileFinal {
|
||||||
|
params [["_requesterUid", "", [""]], ["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["message", "Unable to acknowledge task."],
|
||||||
|
["assignment", createHashMap]
|
||||||
|
];
|
||||||
|
|
||||||
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
||||||
|
private _assignment = +(_assignmentRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
if (_assignment isEqualTo createHashMap) exitWith {
|
||||||
|
_result set ["message", "Task is not assigned."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _groupID = _assignment getOrDefault ["groupId", ""];
|
||||||
|
if !(_self call ["isGroupLeader", [_requesterUid, _groupID]]) exitWith {
|
||||||
|
_result set ["message", "Only the assigned group leader can acknowledge this task."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _bindResult = EGVAR(task,TaskStore) call ["bindTaskOwnership", [_taskID, _requesterUid]];
|
||||||
|
if !(_bindResult getOrDefault ["success", false]) exitWith {
|
||||||
|
_result set ["message", _bindResult getOrDefault ["message", "Failed to bind task ownership."]];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
_assignment set ["state", "acknowledged"];
|
||||||
|
_assignment set ["acknowledgedAt", serverTime];
|
||||||
|
_assignmentRegistry set [_taskID, _assignment];
|
||||||
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
||||||
|
|
||||||
|
_self call ["appendActivity", [
|
||||||
|
"task_acknowledged",
|
||||||
|
format ["%1 acknowledged %2.", _requesterUid, _taskID],
|
||||||
|
_taskID,
|
||||||
|
_groupID,
|
||||||
|
_requesterUid
|
||||||
|
]];
|
||||||
|
|
||||||
|
_result set ["success", true];
|
||||||
|
_result set ["message", "Task acknowledged."];
|
||||||
|
_result set ["assignment", _assignment];
|
||||||
|
_result
|
||||||
|
}],
|
||||||
|
["declineTask", compileFinal {
|
||||||
|
params [["_requesterUid", "", [""]], ["_taskID", "", [""]]];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["message", "Unable to decline task."],
|
||||||
|
["assignment", createHashMap]
|
||||||
|
];
|
||||||
|
|
||||||
|
private _assignmentRegistry = _self getOrDefault ["assignmentRegistry", createHashMap];
|
||||||
|
private _assignment = +(_assignmentRegistry getOrDefault [_taskID, createHashMap]);
|
||||||
|
if (_assignment isEqualTo createHashMap) exitWith {
|
||||||
|
_result set ["message", "Task is not assigned."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _groupID = _assignment getOrDefault ["groupId", ""];
|
||||||
|
if !(_self call ["isGroupLeader", [_requesterUid, _groupID]]) exitWith {
|
||||||
|
_result set ["message", "Only the assigned group leader can decline this task."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
_assignment set ["state", "declined"];
|
||||||
|
_assignment set ["declinedAt", serverTime];
|
||||||
|
_assignmentRegistry set [_taskID, _assignment];
|
||||||
|
_self set ["assignmentRegistry", _assignmentRegistry];
|
||||||
|
|
||||||
|
_self call ["appendActivity", [
|
||||||
|
"task_declined",
|
||||||
|
format ["%1 declined %2.", _requesterUid, _taskID],
|
||||||
|
_taskID,
|
||||||
|
_groupID,
|
||||||
|
_requesterUid
|
||||||
|
]];
|
||||||
|
|
||||||
|
_result set ["success", true];
|
||||||
|
_result set ["message", "Task declined."];
|
||||||
|
_result set ["assignment", _assignment];
|
||||||
|
_result
|
||||||
|
}],
|
||||||
|
["updateGroupStatus", compileFinal {
|
||||||
|
params [["_requesterUid", "", [""]], ["_groupID", "", [""]], ["_status", "", [""]]];
|
||||||
|
|
||||||
|
private _result = createHashMapFromArray [
|
||||||
|
["success", false],
|
||||||
|
["message", "Unable to update group status."],
|
||||||
|
["group", createHashMap]
|
||||||
|
];
|
||||||
|
|
||||||
|
private _finalStatus = toLowerANSI _status;
|
||||||
|
if !(_finalStatus in (_self getOrDefault ["validStatuses", []])) exitWith {
|
||||||
|
_result set ["message", "Invalid group status."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _isAuthorized = (_self call ["isGroupLeader", [_requesterUid, _groupID]]) || { _self call ["canDispatch", [_requesterUid]] };
|
||||||
|
if !_isAuthorized exitWith {
|
||||||
|
_result set ["message", "You are not authorized to update that group."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
private _groupRegistry = _self call ["syncGroups", []];
|
||||||
|
private _groupRecord = +(_groupRegistry getOrDefault [_groupID, createHashMap]);
|
||||||
|
if (_groupRecord isEqualTo createHashMap) exitWith {
|
||||||
|
_result set ["message", "Group could not be resolved."];
|
||||||
|
_result
|
||||||
|
};
|
||||||
|
|
||||||
|
_groupRecord set ["status", _finalStatus];
|
||||||
|
_groupRecord set ["lastUpdate", serverTime];
|
||||||
|
_groupRegistry set [_groupID, _groupRecord];
|
||||||
|
_self set ["groupRegistry", _groupRegistry];
|
||||||
|
|
||||||
|
_self call ["appendActivity", [
|
||||||
|
"group_status",
|
||||||
|
format ["%1 updated %2 to %3.", _requesterUid, _groupRecord getOrDefault ["callsign", _groupID], _finalStatus],
|
||||||
|
"",
|
||||||
|
_groupID,
|
||||||
|
_requesterUid
|
||||||
|
]];
|
||||||
|
|
||||||
|
_result set ["success", true];
|
||||||
|
_result set ["message", "Group status updated."];
|
||||||
|
_result set ["group", _groupRecord];
|
||||||
|
_result
|
||||||
|
}]
|
||||||
|
];
|
||||||
|
|
||||||
|
GVAR(CadStore) = createHashMapObject [GVAR(CadStoreBaseClass)];
|
||||||
|
GVAR(CadStore)
|
||||||
9
arma/server/addons/cad/script_component.hpp
Normal file
9
arma/server/addons/cad/script_component.hpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#define COMPONENT cad
|
||||||
|
#define COMPONENT_BEAUTIFIED CAD
|
||||||
|
#include "\forge\forge_server\addons\main\script_mod.hpp"
|
||||||
|
|
||||||
|
// #define DEBUG_MODE_FULL
|
||||||
|
// #define DISABLE_COMPILE_CACHE
|
||||||
|
// #define ENABLE_PERFORMANCE_COUNTERS
|
||||||
|
|
||||||
|
#include "\forge\forge_server\addons\main\script_macros.hpp"
|
||||||
Loading…
x
Reference in New Issue
Block a user