Add player guide and roster member map focus
- add player-facing docs and synced screenshots - let CAD roster entries center the map on a member - refresh garage and economy UI bridges and docs
@ -172,6 +172,14 @@ switch (_event) do {
|
||||
|
||||
GVAR(CADUIBridge) call ["focusGroup", [_groupID]];
|
||||
};
|
||||
case "cad::members::focus": {
|
||||
private _uid = "";
|
||||
if (_data isEqualType createHashMap) then {
|
||||
_uid = _data getOrDefault ["uid", ""];
|
||||
};
|
||||
|
||||
GVAR(CADUIBridge) call ["focusMember", [_uid]];
|
||||
};
|
||||
case "cad::tasks::focus": {
|
||||
private _taskID = "";
|
||||
if (_data isEqualType createHashMap) then {
|
||||
|
||||
@ -319,6 +319,33 @@ GVAR(CADUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
ctrlMapAnimCommit _mapCtrl;
|
||||
true
|
||||
}],
|
||||
["focusMember", compileFinal {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
if (_uid isEqualTo "") exitWith { false };
|
||||
if (isNil QGVAR(CADRepository)) exitWith { false };
|
||||
|
||||
private _groups = GVAR(CADRepository) getOrDefault ["groups", []];
|
||||
private _position = [];
|
||||
{
|
||||
private _members = _x getOrDefault ["members", []];
|
||||
private _memberIndex = _members findIf { (_x getOrDefault ["uid", ""]) isEqualTo _uid };
|
||||
if (_memberIndex >= 0) exitWith {
|
||||
_position = (_members # _memberIndex) getOrDefault ["position", []];
|
||||
};
|
||||
} forEach _groups;
|
||||
|
||||
if !(_position isEqualType []) exitWith { false };
|
||||
if ((count _position) < 2) exitWith { false };
|
||||
|
||||
private _mapCtrl = _self call ["getMapControl", []];
|
||||
if (isNull _mapCtrl) exitWith { false };
|
||||
|
||||
private _targetPosition = [_position # 0, _position # 1, 0];
|
||||
_mapCtrl ctrlMapAnimAdd [0.35, ctrlMapScale _mapCtrl, _targetPosition];
|
||||
ctrlMapAnimCommit _mapCtrl;
|
||||
true
|
||||
}],
|
||||
["focusTask", compileFinal {
|
||||
params [["_taskID", "", [""]]];
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ window.cadTasks = {
|
||||
selectedDispatchGroupId: "",
|
||||
selectedDispatchTaskId: "",
|
||||
selectedDispatchRequestId: "",
|
||||
selectedRosterMemberUid: "",
|
||||
focusStatusTimer: null,
|
||||
requestModalType: "",
|
||||
statuses: [
|
||||
@ -431,6 +432,19 @@ window.cadTasks = {
|
||||
this.selectedDispatchGroupId = "";
|
||||
}
|
||||
|
||||
if (this.selectedRosterMemberUid) {
|
||||
const memberExists = this.groups.some((group) =>
|
||||
this.normalizeCollection(group.members).some(
|
||||
(member) =>
|
||||
(member.uid || "") === this.selectedRosterMemberUid,
|
||||
),
|
||||
);
|
||||
|
||||
if (!memberExists) {
|
||||
this.selectedRosterMemberUid = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.selectedDispatchTaskId &&
|
||||
!this.contracts.some((task) => {
|
||||
@ -746,8 +760,18 @@ window.cadTasks = {
|
||||
const requestActionLabel = this.isDispatchMode()
|
||||
? "Close"
|
||||
: "Cancel";
|
||||
const requestID = request.requestId || "";
|
||||
const isSelected =
|
||||
requestID === this.selectedDispatchRequestId;
|
||||
return `
|
||||
<div class="task-card cad-request-card">
|
||||
<div
|
||||
class="task-card cad-request-card dispatch-map-card ${isSelected ? "is-selected" : ""}"
|
||||
data-request-id="${requestID}"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick="window.cadTasks.focusRequest('${requestID}')"
|
||||
onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.cadTasks.focusRequest('${requestID}'); }"
|
||||
>
|
||||
<div class="task-card-header">
|
||||
<strong>${request.title || this.getRequestTypeLabel(request.type || "")}</strong>
|
||||
<span class="task-type">${(request.priority || "priority").replaceAll("_", " ")}</span>
|
||||
@ -760,7 +784,7 @@ window.cadTasks = {
|
||||
${
|
||||
canClose
|
||||
? `<div class="task-action-row">
|
||||
<button type="button" class="task-secondary-btn" onclick="window.cadTasks.closeSupportRequest('${request.requestId || ""}')">${requestActionLabel}</button>
|
||||
<button type="button" class="task-secondary-btn" onclick="event.stopPropagation(); window.cadTasks.closeSupportRequest('${requestID}')">${requestActionLabel}</button>
|
||||
</div>`
|
||||
: ""
|
||||
}
|
||||
@ -875,6 +899,7 @@ window.cadTasks = {
|
||||
this.selectedDispatchGroupId = groupID;
|
||||
this.selectedDispatchTaskId = "";
|
||||
this.selectedDispatchRequestId = "";
|
||||
this.selectedRosterMemberUid = "";
|
||||
const statusMessage = `Centering map on ${group.callsign || group.groupId || "group"}...`;
|
||||
this.setStatus(statusMessage, "info");
|
||||
this.clearFocusStatusSoon(statusMessage);
|
||||
@ -883,6 +908,51 @@ window.cadTasks = {
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
focusMember(uid) {
|
||||
let selectedMember = null;
|
||||
|
||||
this.groups.some((group) =>
|
||||
this.normalizeCollection(group.members).some((member) => {
|
||||
if ((member.uid || "") !== uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedMember = member;
|
||||
return true;
|
||||
}),
|
||||
);
|
||||
|
||||
if (!selectedMember) {
|
||||
this.setStatus(
|
||||
"Selected group member is no longer available.",
|
||||
"error",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const position = Array.isArray(selectedMember.position)
|
||||
? selectedMember.position
|
||||
: [];
|
||||
if (position.length < 2) {
|
||||
this.setStatus(
|
||||
"Selected group member has no map position.",
|
||||
"error",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedRosterMemberUid = uid;
|
||||
this.selectedDispatchGroupId = "";
|
||||
this.selectedDispatchTaskId = "";
|
||||
this.selectedDispatchRequestId = "";
|
||||
const statusMessage = `Centering map on ${selectedMember.name || "group member"}...`;
|
||||
this.setStatus(statusMessage, "info");
|
||||
this.clearFocusStatusSoon(statusMessage);
|
||||
window.mapUI.sendEvent("cad::members::focus", {
|
||||
uid: uid,
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
focusTask(taskID) {
|
||||
const task = this.contracts.find((entry) => {
|
||||
const entryTaskID = entry.taskId || entry.taskID || "";
|
||||
@ -899,6 +969,7 @@ window.cadTasks = {
|
||||
this.selectedDispatchTaskId = taskID;
|
||||
this.selectedDispatchGroupId = "";
|
||||
this.selectedDispatchRequestId = "";
|
||||
this.selectedRosterMemberUid = "";
|
||||
const statusMessage = `Centering map on ${task.title || taskID}...`;
|
||||
this.setStatus(statusMessage, "info");
|
||||
this.clearFocusStatusSoon(statusMessage);
|
||||
@ -927,6 +998,7 @@ window.cadTasks = {
|
||||
this.selectedDispatchRequestId = requestID;
|
||||
this.selectedDispatchGroupId = "";
|
||||
this.selectedDispatchTaskId = "";
|
||||
this.selectedRosterMemberUid = "";
|
||||
const statusMessage = `Centering map on ${request.title || requestID}...`;
|
||||
this.setStatus(statusMessage, "info");
|
||||
this.clearFocusStatusSoon(statusMessage);
|
||||
@ -1067,9 +1139,17 @@ window.cadTasks = {
|
||||
);
|
||||
const isAssignedToLeader =
|
||||
this.isLeader() && assignedGroupId === currentGroupId;
|
||||
const isSelected = taskId === this.selectedDispatchTaskId;
|
||||
|
||||
return `
|
||||
<div class="task-card" data-task-id="${taskId}">
|
||||
<div
|
||||
class="task-card dispatch-map-card ${isSelected ? "is-selected" : ""}"
|
||||
data-task-id="${taskId}"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick="window.cadTasks.focusTask('${taskId}')"
|
||||
onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.cadTasks.focusTask('${taskId}'); }"
|
||||
>
|
||||
<div class="task-card-header">
|
||||
<strong>${task.title || taskId}</strong>
|
||||
<span class="task-type">${this.formatTypeLabel(task)}</span>
|
||||
@ -1082,8 +1162,8 @@ window.cadTasks = {
|
||||
${
|
||||
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>
|
||||
<button type="button" class="task-accept-btn" onclick="event.stopPropagation(); window.cadTasks.acknowledgeTask('${taskId}')">Acknowledge</button>
|
||||
<button type="button" class="task-secondary-btn" onclick="event.stopPropagation(); window.cadTasks.declineTask('${taskId}')">Decline</button>
|
||||
</div>`
|
||||
: ""
|
||||
}
|
||||
@ -1177,9 +1257,19 @@ window.cadTasks = {
|
||||
const leaderBadge = member.isLeader
|
||||
? '<span class="roster-leader-badge">Leader</span>'
|
||||
: "";
|
||||
const memberUid = member.uid || "";
|
||||
const isSelected =
|
||||
memberUid && memberUid === this.selectedRosterMemberUid;
|
||||
|
||||
return `
|
||||
<div class="task-card roster-member-card" data-member-id="${member.uid || ""}">
|
||||
<div
|
||||
class="task-card roster-member-card dispatch-map-group-card ${isSelected ? "is-selected" : ""}"
|
||||
data-member-id="${memberUid}"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick="window.cadTasks.focusMember('${memberUid}')"
|
||||
onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); window.cadTasks.focusMember('${memberUid}'); }"
|
||||
>
|
||||
<div class="task-card-header">
|
||||
<strong>${member.name || "Unknown Operator"}</strong>
|
||||
<span class="task-type">${lifeState}</span>
|
||||
|
||||
@ -63,6 +63,11 @@ switch (_event) do {
|
||||
GVAR(GarageActionService) call ["handleRepairRequest", [_data]];
|
||||
};
|
||||
};
|
||||
case "garage::vehicle::rearm::request": {
|
||||
if !(isNil QGVAR(GarageActionService)) then {
|
||||
GVAR(GarageActionService) call ["handleRearmRequest", [_data]];
|
||||
};
|
||||
};
|
||||
case "garage::refresh": {
|
||||
if !(isNil QGVAR(GarageUIBridge)) then {
|
||||
GVAR(GarageUIBridge) call ["refreshGarage", []];
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the garage action service for retrieve, store, refuel, and
|
||||
* repair world actions.
|
||||
* Initializes the garage action service for retrieve, store, refuel, rearm,
|
||||
* and repair world actions.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
@ -184,6 +184,17 @@ GVAR(GarageActionServiceBaseClass) = compileFinal createHashMapFromArray [
|
||||
_self call ["refreshAfterService", []];
|
||||
true
|
||||
}],
|
||||
["handleRearmRequest", compileFinal {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
private _vehicle = _self call ["resolveServiceVehicle", [_data, "rearm"]];
|
||||
if (isNull _vehicle) exitWith { false };
|
||||
|
||||
[SRPC(economy,RearmService), [_vehicle, player, -1]] call CFUNC(serverEvent);
|
||||
_self call ["sendServiceResult", ["rearm", true, "Rearm request sent. Billing result will appear as a notification."]];
|
||||
_self call ["refreshAfterService", []];
|
||||
true
|
||||
}],
|
||||
["handleActionResponse", compileFinal {
|
||||
params [["_payload", createHashMap, [createHashMap]]];
|
||||
|
||||
|
||||
@ -31,6 +31,10 @@
|
||||
return bridge.send("garage::vehicle::repair::request", payload);
|
||||
}
|
||||
|
||||
function requestRearm(payload) {
|
||||
return bridge.send("garage::vehicle::rearm::request", payload);
|
||||
}
|
||||
|
||||
function notifyReady() {
|
||||
return bridge.ready({ loaded: true });
|
||||
}
|
||||
@ -108,6 +112,7 @@
|
||||
receive: bridge.receive,
|
||||
requestClose,
|
||||
requestRefresh,
|
||||
requestRearm,
|
||||
requestRefuel,
|
||||
requestRepair,
|
||||
requestRetrieve,
|
||||
|
||||
@ -353,6 +353,7 @@
|
||||
!isStored &&
|
||||
Number(currentSelection.health || 0) < 0.999 &&
|
||||
!isBusy;
|
||||
const canRearm = !isStored && !isBusy;
|
||||
|
||||
return h(
|
||||
"section",
|
||||
@ -500,6 +501,20 @@
|
||||
type: "button",
|
||||
className:
|
||||
"garage-btn garage-btn-secondary",
|
||||
disabled: !canRearm,
|
||||
onClick: () =>
|
||||
actions.requestRearmSelected(),
|
||||
},
|
||||
pendingAction === "rearm"
|
||||
? "Rearming..."
|
||||
: "Rearm",
|
||||
),
|
||||
h(
|
||||
"button",
|
||||
{
|
||||
type: "button",
|
||||
className:
|
||||
"garage-btn garage-btn-secondary garage-action-refresh",
|
||||
disabled: isBusy,
|
||||
onClick: () => actions.refreshGarage(),
|
||||
},
|
||||
@ -512,10 +527,10 @@
|
||||
isStored
|
||||
? session.spawnBlocked
|
||||
? "The garage spawn lane is currently blocked."
|
||||
: "Retrieve this stored vehicle into the active spawn lane before refuel or repair service."
|
||||
: "Retrieve this stored vehicle into the active spawn lane before refuel, rearm, or repair service."
|
||||
: currentSelection.isEmpty === false
|
||||
? "Only empty nearby vehicles can be stored."
|
||||
: "Store this nearby vehicle or request organization-billed refuel and repair service.",
|
||||
: "Store this nearby vehicle or request organization-billed refuel, rearm, and repair service.",
|
||||
),
|
||||
),
|
||||
h(
|
||||
|
||||
@ -223,6 +223,33 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
function requestRearmSelected() {
|
||||
const selectedEntry = getSelectedEntry();
|
||||
if (!selectedEntry || selectedEntry.entryKind !== "nearby") {
|
||||
showNotice("error", "Select a nearby vehicle to rearm.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bridge = GarageApp.bridge;
|
||||
if (!bridge || typeof bridge.requestRearm !== "function") {
|
||||
showNotice("error", "Garage rearm bridge is unavailable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
store.startAction("rearm");
|
||||
const sent = bridge.requestRearm({
|
||||
netId: selectedEntry.netId || "",
|
||||
});
|
||||
|
||||
if (!sent) {
|
||||
store.finishAction();
|
||||
showNotice("error", "Garage rearm bridge is unavailable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GarageApp.actions = {
|
||||
showNotice,
|
||||
closeGarage,
|
||||
@ -232,6 +259,7 @@
|
||||
selectCategory,
|
||||
selectEntry,
|
||||
getSelectedEntry,
|
||||
requestRearmSelected,
|
||||
requestRefuelSelected,
|
||||
requestRepairSelected,
|
||||
requestRetrieveSelected,
|
||||
|
||||
@ -217,6 +217,10 @@ button:disabled {
|
||||
gap: 0.65rem;
|
||||
}
|
||||
|
||||
.garage-action-refresh {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.garage-footer-bar {
|
||||
width: 100%;
|
||||
border-top: 1px solid rgb(18 54 93 / 0.1);
|
||||
|
||||
@ -99,7 +99,8 @@ GVAR(GroupRepositoryBaseClass) = compileFinal createHashMapFromArray [
|
||||
["uid", _memberUid],
|
||||
["name", name _x],
|
||||
["lifeState", _memberState],
|
||||
["isLeader", _x isEqualTo _leader]
|
||||
["isLeader", _x isEqualTo _leader],
|
||||
["position", getPosATL _x]
|
||||
]);
|
||||
} forEach _members;
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ refueling sessions, medical spawn occupancy, respawn placement, and death
|
||||
inventory handling.
|
||||
|
||||
Current stores cover fuel tracking, medical service behavior, and service
|
||||
charges such as repairs.
|
||||
charges such as repairs and rearming.
|
||||
|
||||
## Dependencies
|
||||
- `forge_server_main`
|
||||
@ -27,8 +27,9 @@ Note: Bank and Org are runtime-only dependencies (not compile-time requiredAddon
|
||||
respawn placement, death inventory handling, and body-bag transfer. Medical
|
||||
charges use player bank/cash first, then organization funds with repayable
|
||||
member debt only when the player cannot cover the service.
|
||||
- `fnc_initSEconomyStore.sqf` handles organization-funded service charges and
|
||||
repairs. Repairs only apply after the organization charge succeeds. The
|
||||
- `fnc_initSEconomyStore.sqf` handles organization-funded service charges,
|
||||
repairs, and rearming. Vehicle services only apply after the organization
|
||||
charge succeeds. The
|
||||
shared org-charge helper can also record member debt for medical fallback.
|
||||
|
||||
## Event Surface
|
||||
@ -50,6 +51,16 @@ Repair service requests use:
|
||||
|
||||
`_cost` is optional. Passing `-1` uses the configured service repair cost.
|
||||
|
||||
Rearm service requests use:
|
||||
|
||||
```sqf
|
||||
[QEGVAR(economy,RearmService), [_target, _unit, _cost]] call CBA_fnc_serverEvent;
|
||||
```
|
||||
|
||||
`_cost` is optional. Passing `-1` uses the configured service rearm cost.
|
||||
`setVehicleAmmo` has global effects, but only adds ammo to local turrets, so
|
||||
the ammo reset is broadcast after billing succeeds.
|
||||
|
||||
Garage refuel service requests use:
|
||||
|
||||
```sqf
|
||||
@ -70,7 +81,7 @@ Fuel and repair services are organization-funded:
|
||||
`commit = true`, and member service charging enabled.
|
||||
4. Send the returned organization patch to online members.
|
||||
5. If the charge fails, do not complete the service. Refueling rolls the target
|
||||
back to its starting fuel level; repairs are not applied.
|
||||
back to its starting fuel level; repairs and rearming are not applied.
|
||||
|
||||
Direct refuel service requests, such as those from the garage UI, calculate
|
||||
the missing fuel from `fuelCapacity`, charge the organization, and fill the
|
||||
|
||||
@ -33,6 +33,11 @@ if (isNil QGVAR(SEconomyStore)) then { call FUNC(initSEconomyStore); };
|
||||
GVAR(SEconomyStore) call ["repair", [_target, _unit, _cost]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(RearmService), {
|
||||
params ["_target", "_unit", ["_cost", -1, [0]]];
|
||||
GVAR(SEconomyStore) call ["rearm", [_target, _unit, _cost]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(RefuelService), {
|
||||
params ["_target", "_unit"];
|
||||
GVAR(FEconomyStore) call ["refuel", [_target, _unit]];
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* File: fnc_initSEconomyStore.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2025-12-20
|
||||
* Last Update: 2026-05-15
|
||||
* Last Update: 2026-05-19
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
@ -27,6 +27,7 @@ GVAR(SEconomyStore) = createHashMapObject [[
|
||||
["#type", "IServiceEconomy"],
|
||||
["#create", {
|
||||
GVAR(ServiceRepairCost) = 500;
|
||||
GVAR(ServiceRearmCost) = 500;
|
||||
["INFO", "Service Store Initialized!", nil, nil] call EFUNC(common,log);
|
||||
}],
|
||||
["notify", {
|
||||
@ -158,6 +159,22 @@ GVAR(SEconomyStore) = createHashMapObject [[
|
||||
_self call ["notify", [_unit, "info", "Repair", format ["Repair complete. Organization charged $%1.", [_repairCost] call EFUNC(common,formatNumber)]]];
|
||||
true
|
||||
}],
|
||||
["rearm", {
|
||||
params [["_target", objNull, [objNull]], ["_unit", objNull, [objNull]], ["_cost", -1, [0]]];
|
||||
|
||||
if (isNull _target || { isNull _unit }) exitWith { false };
|
||||
|
||||
private _rearmCost = [_cost, GVAR(ServiceRearmCost)] select (_cost < 0);
|
||||
private _charge = _self call ["chargeOrg", [_unit, _rearmCost, "Rearm"]];
|
||||
if !(_charge getOrDefault ["success", false]) exitWith {
|
||||
_self call ["notify", [_unit, "danger", "Rearm", _charge getOrDefault ["message", "Organization funds cannot cover this rearm."]]];
|
||||
false
|
||||
};
|
||||
|
||||
[_target, 1] remoteExecCall ["setVehicleAmmo", 0];
|
||||
_self call ["notify", [_unit, "info", "Rearm", format ["Rearm complete. Organization charged $%1.", [_rearmCost] call EFUNC(common,formatNumber)]]];
|
||||
true
|
||||
}],
|
||||
["init", {}]
|
||||
]];
|
||||
|
||||
|
||||
@ -37,6 +37,16 @@ assignments.
|
||||
- group status, role, and profile requests
|
||||
- map focus actions
|
||||
|
||||
## Map Focus Behavior
|
||||
|
||||
CAD list entries can drive the native map position without duplicating map
|
||||
logic in the browser UI. In operations mode, assigned or accepted task cards,
|
||||
roster member cards, and support request cards send focus events. In dispatch
|
||||
map mode, group, contract, and support request cards use the same focus path.
|
||||
|
||||
Task and support request focus uses the stored record position. Roster member
|
||||
focus uses the member position included in the hydrated group roster.
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
@ -58,6 +68,7 @@ assignments.
|
||||
| `cad::groups::role` | Update group role. |
|
||||
| `cad::groups::profile` | Update status and role together. |
|
||||
| `cad::groups::focus` | Center map on a group. |
|
||||
| `cad::members::focus` | Center map on a group member. |
|
||||
| `cad::tasks::focus` | Center map on a task. |
|
||||
| `cad::requests::focus` | Center map on a support request. |
|
||||
| `map::zoomIn` | Zoom native map in. |
|
||||
|
||||
@ -56,21 +56,21 @@ is finalized and spawned onto the resolved lane.
|
||||
| --- | --- |
|
||||
| `garage::hydrate` | Initial vehicle and session payload. |
|
||||
| `garage::sync` | Refreshed vehicle payload. |
|
||||
| `garage::service::success` | Browser notice for accepted refuel/repair requests. |
|
||||
| `garage::service::failure` | Browser notice for rejected refuel/repair requests. |
|
||||
| `garage::service::success` | Browser notice for accepted refuel/rearm/repair requests. |
|
||||
| `garage::service::failure` | Browser notice for rejected refuel/rearm/repair requests. |
|
||||
|
||||
Server action responses are handled by the action service and notification
|
||||
flow.
|
||||
|
||||
## Vehicle Service
|
||||
|
||||
The selected vehicle detail panel includes refuel and repair actions for nearby
|
||||
The selected vehicle detail panel includes refuel, rearm, and repair actions for nearby
|
||||
world vehicles. Stored records must be retrieved first because server economy
|
||||
services operate on live vehicle objects, not stored garage records.
|
||||
|
||||
Refuel requests use the server economy `RefuelService` event. Repair requests
|
||||
use the server economy `RepairService` event. Both services are billed by the
|
||||
server economy addon through organization funds.
|
||||
Refuel requests use the server economy `RefuelService` event. Rearm requests
|
||||
use `RearmService`. Repair requests use `RepairService`. These services are
|
||||
billed by the server economy addon through organization funds.
|
||||
|
||||
## Mission Setup
|
||||
|
||||
|
||||
@ -45,6 +45,24 @@ The target is only repaired after the organization charge succeeds.
|
||||
The client garage UI forwards selected nearby vehicle repair requests through
|
||||
the same event.
|
||||
|
||||
## Rearm
|
||||
|
||||
Rearm is organization-funded.
|
||||
|
||||
Use the rearm service event:
|
||||
|
||||
```sqf
|
||||
[QEGVAR(economy,RearmService), [_target, _unit, _cost]] call CBA_fnc_serverEvent;
|
||||
```
|
||||
|
||||
`_cost` is optional. Passing `-1` uses the configured service rearm cost.
|
||||
The target is only rearmed after the organization charge succeeds.
|
||||
`setVehicleAmmo` has global effects, but the ammo is only added to local
|
||||
turrets, so the service broadcasts the ammo reset after billing succeeds.
|
||||
|
||||
The client garage UI forwards selected nearby vehicle rearm requests through
|
||||
the same event.
|
||||
|
||||
## Medical
|
||||
|
||||
Medical is player-funded first.
|
||||
|
||||
321
docs/PLAYER_GUIDE.md
Normal file
@ -0,0 +1,321 @@
|
||||
# Player Guide
|
||||
|
||||
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
|
||||
|
||||
Most Forge actions are opened from the actor interaction menu while standing
|
||||
near a configured mission object.
|
||||
|
||||

|
||||
|
||||
Press `Tab` by default to open the custom interaction menu. Server settings or
|
||||
local keybind changes may use a different key.
|
||||
|
||||
Known current behavior: after closing the custom interaction menu, players may
|
||||
need to press `Tab` twice before it opens again. Treat this as a temporary
|
||||
workaround until the interaction menu focus behavior is investigated further.
|
||||
|
||||
Players usually need to be within 5 meters of an interaction object such as a
|
||||
bank terminal, ATM, store counter, garage terminal, or locker.
|
||||
|
||||
## CAD and Tasks
|
||||
|
||||
CAD is the main task and dispatch system. It is used for mission contracts,
|
||||
group status, support requests, dispatch orders, and task assignment.
|
||||
|
||||

|
||||
|
||||
Player workflow:
|
||||
|
||||
1. Open CAD from the available interaction path.
|
||||
2. Review available or assigned tasks.
|
||||
3. If a dispatcher assigns a task to your group, the group leader must
|
||||
acknowledge or decline it.
|
||||
4. Once acknowledged, the task becomes active for the assigned group.
|
||||
5. Complete the task objective shown by CAD, map task state, and mission
|
||||
instructions.
|
||||
|
||||
Map focus behavior:
|
||||
|
||||
- Click an assigned or accepted task in the operations task board to center the
|
||||
map on that task.
|
||||
- Click a roster member to center the map on that player.
|
||||
- Click a support request to center the map on the request location.
|
||||
- Dispatch map mode supports the same focus behavior for groups, contracts,
|
||||
and support requests.
|
||||
|
||||
Dispatch workflow:
|
||||
|
||||

|
||||
|
||||
1. Open CAD with a dispatcher-enabled slot or permission.
|
||||
2. Use dispatch mode to review groups, open contracts, assigned contracts, and
|
||||
support requests.
|
||||
3. Assign available contracts to active groups.
|
||||
4. Send dispatch orders or close completed orders as needed.
|
||||
5. Track group status and recent CAD activity.
|
||||
|
||||
Dispatch access:
|
||||
|
||||
- The CEO slot can administer the default organization and use CAD dispatch
|
||||
permissions.
|
||||
- The Dispatch slot grants CAD dispatch permissions without default
|
||||
organization administration rights.
|
||||
- Players who are the CEO or owner of their own organization also receive CAD
|
||||
dispatch permissions.
|
||||
|
||||
Important task behavior:
|
||||
|
||||
- CAD assignment reserves a task for a group.
|
||||
- The task starts after the assigned group leader acknowledges it.
|
||||
- If the leader declines, the task returns to the open contract board.
|
||||
- Some task timers wait for group-leader acknowledgment before counting down.
|
||||
|
||||
## Phone
|
||||
|
||||
The phone provides contacts, messages, email, and local utility apps.
|
||||
|
||||

|
||||
|
||||
### Contacts
|
||||
|
||||
Use Contacts to keep track of other players by phone number or email address.
|
||||
Adding contacts makes it easier to start messages and emails without manually
|
||||
entering recipient details every time.
|
||||
|
||||

|
||||
|
||||
### Messages
|
||||
|
||||
Messages are short player-to-player conversations.
|
||||
|
||||

|
||||
|
||||
Use Messages to:
|
||||
|
||||
- start or continue a conversation with a contact
|
||||
- read incoming messages
|
||||
- mark messages as read
|
||||
- delete messages you no longer need
|
||||
|
||||
### Email
|
||||
|
||||
Email is used for longer player-to-player communication.
|
||||
|
||||

|
||||
|
||||
Use Email to:
|
||||
|
||||
- send a subject and body to another player
|
||||
- read incoming mail
|
||||
- mark email as read
|
||||
- delete old email
|
||||
|
||||
### Local Phone Apps
|
||||
|
||||
Notes, calendar events, clocks, alarms, and theme preferences are local utility
|
||||
features. They are saved for the local player profile and should not be treated
|
||||
as shared multiplayer data.
|
||||
|
||||
## Bank and ATM
|
||||
|
||||
Bank and ATM access are separate.
|
||||
|
||||
Use a bank object for full banking:
|
||||
|
||||

|
||||
|
||||
- view account information
|
||||
- transfer funds
|
||||
- deposit earnings
|
||||
- change PIN
|
||||
|
||||
Use an ATM for limited account access:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- PIN-gated account actions
|
||||
- ATM banking workflows
|
||||
- no PIN changes
|
||||
|
||||
If a PIN prompt appears, enter the correct PIN before attempting account
|
||||
actions.
|
||||
|
||||
## Organizations
|
||||
|
||||
Players start in the default organization. A player can create a player-owned
|
||||
organization only if they have `$50,000` available for the registration fee.
|
||||
Organization access depends on the player's role.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Default organization:
|
||||
|
||||
- The `ceo` slot can administer the default organization.
|
||||
- The `dispatch` slot receives CAD dispatch permissions, but does not receive
|
||||
default organization administration rights.
|
||||
|
||||
Player-owned organizations:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- The player who created the organization is its owner or CEO.
|
||||
- The owner can administer the organization, including treasury and roster
|
||||
actions exposed by the organization interface.
|
||||
- Organization owners can invite players, manage members, assign credit lines,
|
||||
transfer funds or run payroll when funds are available, and disband the
|
||||
organization.
|
||||
- Organization owners can use organization funds for supported store purchases.
|
||||
- Members may receive assigned credit lines, accept or decline organization
|
||||
invites, and leave the organization.
|
||||
- The organization CEO or owner cannot leave their own organization directly.
|
||||
They must disband the organization if they want to leave it.
|
||||
|
||||
Organization actions are server-authoritative. If an organization action fails,
|
||||
check that the player has the correct role, the player or organization has
|
||||
enough funds, and the target player is eligible for the action.
|
||||
|
||||
## Store
|
||||
|
||||
Stores sell unlocks and equipment through the configured server-side catalog.
|
||||
|
||||

|
||||
|
||||
Store purchases may grant:
|
||||
|
||||
- items or equipment added to the locker
|
||||
- matching gear unlocks in the virtual arsenal
|
||||
- vehicle unlocks in the virtual garage
|
||||
- other mission-configured rewards
|
||||
|
||||
Store purchases are server-authoritative. If a purchase succeeds, the relevant
|
||||
bank, locker, virtual arsenal, virtual garage, or organization state updates
|
||||
from the server.
|
||||
|
||||

|
||||
|
||||
Vehicle purchases unlock the vehicle in the virtual garage. They do not place a
|
||||
physical vehicle into the player's 5-slot garage. Use the virtual garage to
|
||||
spawn an unlocked vehicle, and use the garage to store or retrieve live world
|
||||
vehicles.
|
||||
|
||||
## Locker and Virtual Arsenal
|
||||
|
||||
The locker is personal item storage.
|
||||
|
||||

|
||||
|
||||
Locker rules:
|
||||
|
||||
- Up to 25 items can be stored.
|
||||
- The locker saves when the locker container is closed.
|
||||
- Over-capacity storage can warn or fail depending on server handling.
|
||||
|
||||
The virtual arsenal is locked down. Players only see gear they have been
|
||||
granted or have unlocked through systems such as the store. The virtual arsenal
|
||||
is not intended to expose the full unrestricted Arma arsenal.
|
||||
|
||||

|
||||
|
||||
## Garage and Virtual Garage
|
||||
|
||||
The garage stores physical player vehicles that have been saved from the world.
|
||||
|
||||

|
||||
|
||||
Garage rules:
|
||||
|
||||
- Up to 5 vehicles can be stored.
|
||||
- Stored vehicles can be retrieved from a garage interaction point.
|
||||
- Retrieved vehicles become live world vehicles again.
|
||||
- Vehicle service actions operate on live nearby vehicles, not vehicles that
|
||||
are still stored.
|
||||
|
||||
The virtual garage is locked down. Players only see vehicles they have been
|
||||
granted or have unlocked through systems such as the store. Virtual garage
|
||||
unlocks are separate from the 5 physical vehicle slots in the garage. The
|
||||
virtual garage uses mission-configured spawn lanes, and spawning may be blocked
|
||||
if the spawn position is occupied.
|
||||
|
||||

|
||||
|
||||
## Economy Services
|
||||
|
||||
Economy services are server-controlled. Charges must succeed before the world
|
||||
effect is applied.
|
||||
|
||||

|
||||
|
||||
### Medical
|
||||
|
||||
Medical services are player-funded first.
|
||||
|
||||

|
||||
|
||||
Billing order:
|
||||
|
||||
1. Player bank balance.
|
||||
2. Player cash.
|
||||
3. Organization funds, when allowed by the server.
|
||||
4. Organization credit-line debt for the player when organization fallback is
|
||||
used.
|
||||
|
||||
Medical respawn placement uses mission-configured medical spawn objects.
|
||||
|
||||
### Refuel
|
||||
|
||||
Refuel service is organization-funded. If the organization cannot cover the
|
||||
cost, the vehicle is not refueled or the fuel level is rolled back.
|
||||
|
||||
Refuel is available from the garage app dashboard shown above.
|
||||
|
||||
### Repair
|
||||
|
||||
Repair service is organization-funded. The repair is only applied after the
|
||||
organization charge succeeds.
|
||||
|
||||
Repair is available from the garage app dashboard shown above.
|
||||
|
||||
### Rearm
|
||||
|
||||
If the mission exposes rearm service through the economy or support workflow,
|
||||
expect it to follow the same server-authoritative pattern: the service request
|
||||
must be accepted and billed before equipment or vehicle state changes are
|
||||
applied.
|
||||
|
||||
Rearm is available from the garage app dashboard shown above.
|
||||
|
||||
## Common Player Checks
|
||||
|
||||
If a system does not appear or does not work:
|
||||
|
||||
- Move closer to the interaction object.
|
||||
- Confirm you are using the correct object type, such as ATM vs bank.
|
||||
- Confirm your group leader has acknowledged an assigned CAD task.
|
||||
- Confirm the needed store unlock has been purchased before checking VA or VG.
|
||||
- Confirm the garage spawn point is clear before using the virtual garage.
|
||||
- Confirm your player, cash, bank, or organization funds can cover the service.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Mission Designer Guide](./MISSION_DESIGNER_GUIDE.md)
|
||||
- [Client CAD Usage Guide](./CLIENT_CAD_USAGE_GUIDE.md)
|
||||
- [Client Phone Usage Guide](./CLIENT_PHONE_USAGE_GUIDE.md)
|
||||
- [Client Bank Usage Guide](./CLIENT_BANK_USAGE_GUIDE.md)
|
||||
- [Client Garage Usage Guide](./CLIENT_GARAGE_USAGE_GUIDE.md)
|
||||
- [Client Locker Usage Guide](./CLIENT_LOCKER_USAGE_GUIDE.md)
|
||||
- [Organization Usage Guide](./ORG_USAGE_GUIDE.md)
|
||||
- [Store Usage Guide](./STORE_USAGE_GUIDE.md)
|
||||
- [Economy Usage Guide](./ECONOMY_USAGE_GUIDE.md)
|
||||
@ -30,6 +30,8 @@ See [SurrealDB Setup](./surrealdb-setup.md) for the full setup path.
|
||||
- [Mission Designer Guide](./MISSION_DESIGNER_GUIDE.md): how to place Eden
|
||||
objects, garage markers, and CAD-compatible task modules for playable
|
||||
missions.
|
||||
- [Player Guide](./PLAYER_GUIDE.md): how players use CAD, phone, bank, store,
|
||||
locker, garage, and economy services during missions.
|
||||
- [SurrealDB Setup](./surrealdb-setup.md): where to get SurrealDB or
|
||||
Surrealist and how to connect Forge to it for local or live use.
|
||||
|
||||
|
||||
@ -70,6 +70,16 @@ npm run build:webui
|
||||
playable missions.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-user-round-check
|
||||
title: Player Guide
|
||||
to: /getting-started/player-guide
|
||||
---
|
||||
Learn the player-facing CAD, phone, bank, store, locker, garage, and economy
|
||||
workflows.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-database
|
||||
|
||||
320
docus/content/1.getting-started/5.player-guide.md
Normal file
@ -0,0 +1,320 @@
|
||||
---
|
||||
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."
|
||||
---
|
||||
|
||||
Player-guide screenshots are stored as JPG files under
|
||||
`docus/public/images/player`.
|
||||
|
||||
## Opening Forge Interactions
|
||||
|
||||
Most Forge actions are opened from the actor interaction menu while standing
|
||||
near a configured mission object.
|
||||
|
||||

|
||||
|
||||
Press `Tab` by default to open the custom interaction menu. Server settings or
|
||||
local keybind changes may use a different key.
|
||||
|
||||
Known current behavior: after closing the custom interaction menu, players may
|
||||
need to press `Tab` twice before it opens again. Treat this as a temporary
|
||||
workaround until the interaction menu focus behavior is investigated further.
|
||||
|
||||
Players usually need to be within 5 meters of an interaction object such as a
|
||||
bank terminal, ATM, store counter, garage terminal, or locker.
|
||||
|
||||
## CAD and Tasks
|
||||
|
||||
CAD is the main task and dispatch system. It is used for mission contracts,
|
||||
group status, support requests, dispatch orders, and task assignment.
|
||||
|
||||

|
||||
|
||||
Player workflow:
|
||||
|
||||
1. Open CAD from the available interaction path.
|
||||
2. Review available or assigned tasks.
|
||||
3. If a dispatcher assigns a task to your group, the group leader must
|
||||
acknowledge or decline it.
|
||||
4. Once acknowledged, the task becomes active for the assigned group.
|
||||
5. Complete the task objective shown by CAD, map task state, and mission
|
||||
instructions.
|
||||
|
||||
Map focus behavior:
|
||||
|
||||
- Click an assigned or accepted task in the operations task board to center the
|
||||
map on that task.
|
||||
- Click a roster member to center the map on that player.
|
||||
- Click a support request to center the map on the request location.
|
||||
- Dispatch map mode supports the same focus behavior for groups, contracts,
|
||||
and support requests.
|
||||
|
||||
Dispatch workflow:
|
||||
|
||||

|
||||
|
||||
1. Open CAD with a dispatcher-enabled slot or permission.
|
||||
2. Use dispatch mode to review groups, open contracts, assigned contracts, and
|
||||
support requests.
|
||||
3. Assign available contracts to active groups.
|
||||
4. Send dispatch orders or close completed orders as needed.
|
||||
5. Track group status and recent CAD activity.
|
||||
|
||||
Dispatch access:
|
||||
|
||||
- The CEO slot can administer the default organization and use CAD dispatch
|
||||
permissions.
|
||||
- The Dispatch slot grants CAD dispatch permissions without default
|
||||
organization administration rights.
|
||||
- Players who are the CEO or owner of their own organization also receive CAD
|
||||
dispatch permissions.
|
||||
|
||||
Important task behavior:
|
||||
|
||||
- CAD assignment reserves a task for a group.
|
||||
- The task starts after the assigned group leader acknowledges it.
|
||||
- If the leader declines, the task returns to the open contract board.
|
||||
- Some task timers wait for group-leader acknowledgment before counting down.
|
||||
|
||||
## Phone
|
||||
|
||||
The phone provides contacts, messages, email, and local utility apps.
|
||||
|
||||

|
||||
|
||||
### Contacts
|
||||
|
||||
Use Contacts to keep track of other players by phone number or email address.
|
||||
Adding contacts makes it easier to start messages and emails without manually
|
||||
entering recipient details every time.
|
||||
|
||||

|
||||
|
||||
### Messages
|
||||
|
||||
Messages are short player-to-player conversations.
|
||||
|
||||

|
||||
|
||||
Use Messages to:
|
||||
|
||||
- start or continue a conversation with a contact
|
||||
- read incoming messages
|
||||
- mark messages as read
|
||||
- delete messages you no longer need
|
||||
|
||||
### Email
|
||||
|
||||
Email is used for longer player-to-player communication.
|
||||
|
||||

|
||||
|
||||
Use Email to:
|
||||
|
||||
- send a subject and body to another player
|
||||
- read incoming mail
|
||||
- mark email as read
|
||||
- delete old email
|
||||
|
||||
### Local Phone Apps
|
||||
|
||||
Notes, calendar events, clocks, alarms, and theme preferences are local utility
|
||||
features. They are saved for the local player profile and should not be treated
|
||||
as shared multiplayer data.
|
||||
|
||||
## Bank and ATM
|
||||
|
||||
Bank and ATM access are separate.
|
||||
|
||||
Use a bank object for full banking:
|
||||
|
||||

|
||||
|
||||
- view account information
|
||||
- transfer funds
|
||||
- deposit earnings
|
||||
- change PIN
|
||||
|
||||
Use an ATM for limited account access:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- PIN-gated account actions
|
||||
- ATM banking workflows
|
||||
- no PIN changes
|
||||
|
||||
If a PIN prompt appears, enter the correct PIN before attempting account
|
||||
actions.
|
||||
|
||||
## Organizations
|
||||
|
||||
Players start in the default organization. A player can create a player-owned
|
||||
organization only if they have `$50,000` available for the registration fee.
|
||||
Organization access depends on the player's role.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Default organization:
|
||||
|
||||
- The `ceo` slot can administer the default organization.
|
||||
- The `dispatch` slot receives CAD dispatch permissions, but does not receive
|
||||
default organization administration rights.
|
||||
|
||||
Player-owned organizations:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- The player who created the organization is its owner or CEO.
|
||||
- The owner can administer the organization, including treasury and roster
|
||||
actions exposed by the organization interface.
|
||||
- Organization owners can invite players, manage members, assign credit lines,
|
||||
transfer funds or run payroll when funds are available, and disband the
|
||||
organization.
|
||||
- Organization owners can use organization funds for supported store purchases.
|
||||
- Members may receive assigned credit lines, accept or decline organization
|
||||
invites, and leave the organization.
|
||||
- The organization CEO or owner cannot leave their own organization directly.
|
||||
They must disband the organization if they want to leave it.
|
||||
|
||||
Organization actions are server-authoritative. If an organization action fails,
|
||||
check that the player has the correct role, the player or organization has
|
||||
enough funds, and the target player is eligible for the action.
|
||||
|
||||
## Store
|
||||
|
||||
Stores sell unlocks and equipment through the configured server-side catalog.
|
||||
|
||||

|
||||
|
||||
Store purchases may grant:
|
||||
|
||||
- items or equipment added to the locker
|
||||
- matching gear unlocks in the virtual arsenal
|
||||
- vehicle unlocks in the virtual garage
|
||||
- other mission-configured rewards
|
||||
|
||||
Store purchases are server-authoritative. If a purchase succeeds, the relevant
|
||||
bank, locker, virtual arsenal, virtual garage, or organization state updates
|
||||
from the server.
|
||||
|
||||

|
||||
|
||||
Vehicle purchases unlock the vehicle in the virtual garage. They do not place a
|
||||
physical vehicle into the player's 5-slot garage. Use the virtual garage to
|
||||
spawn an unlocked vehicle, and use the garage to store or retrieve live world
|
||||
vehicles.
|
||||
|
||||
## Locker and Virtual Arsenal
|
||||
|
||||
The locker is personal item storage.
|
||||
|
||||

|
||||
|
||||
Locker rules:
|
||||
|
||||
- Up to 25 items can be stored.
|
||||
- The locker saves when the locker container is closed.
|
||||
- Over-capacity storage can warn or fail depending on server handling.
|
||||
|
||||
The virtual arsenal is locked down. Players only see gear they have been
|
||||
granted or have unlocked through systems such as the store. The virtual arsenal
|
||||
is not intended to expose the full unrestricted Arma arsenal.
|
||||
|
||||

|
||||
|
||||
## Garage and Virtual Garage
|
||||
|
||||
The garage stores physical player vehicles that have been saved from the world.
|
||||
|
||||

|
||||
|
||||
Garage rules:
|
||||
|
||||
- Up to 5 vehicles can be stored.
|
||||
- Stored vehicles can be retrieved from a garage interaction point.
|
||||
- Retrieved vehicles become live world vehicles again.
|
||||
- Vehicle service actions operate on live nearby vehicles, not vehicles that
|
||||
are still stored.
|
||||
|
||||
The virtual garage is locked down. Players only see vehicles they have been
|
||||
granted or have unlocked through systems such as the store. Virtual garage
|
||||
unlocks are separate from the 5 physical vehicle slots in the garage. The
|
||||
virtual garage uses mission-configured spawn lanes, and spawning may be blocked
|
||||
if the spawn position is occupied.
|
||||
|
||||

|
||||
|
||||
## Economy Services
|
||||
|
||||
Economy services are server-controlled. Charges must succeed before the world
|
||||
effect is applied.
|
||||
|
||||

|
||||
|
||||
### Medical
|
||||
|
||||
Medical services are player-funded first.
|
||||
|
||||

|
||||
|
||||
Billing order:
|
||||
|
||||
1. Player bank balance.
|
||||
2. Player cash.
|
||||
3. Organization funds, when allowed by the server.
|
||||
4. Organization credit-line debt for the player when organization fallback is
|
||||
used.
|
||||
|
||||
Medical respawn placement uses mission-configured medical spawn objects.
|
||||
|
||||
### Refuel
|
||||
|
||||
Refuel service is organization-funded. If the organization cannot cover the
|
||||
cost, the vehicle is not refueled or the fuel level is rolled back.
|
||||
|
||||
Refuel is available from the garage app dashboard shown above.
|
||||
|
||||
### Repair
|
||||
|
||||
Repair service is organization-funded. The repair is only applied after the
|
||||
organization charge succeeds.
|
||||
|
||||
Repair is available from the garage app dashboard shown above.
|
||||
|
||||
### Rearm
|
||||
|
||||
If the mission exposes rearm service through the economy or support workflow,
|
||||
expect it to follow the same server-authoritative pattern: the service request
|
||||
must be accepted and billed before equipment or vehicle state changes are
|
||||
applied.
|
||||
|
||||
Rearm is available from the garage app dashboard shown above.
|
||||
|
||||
## Common Player Checks
|
||||
|
||||
If a system does not appear or does not work:
|
||||
|
||||
- Move closer to the interaction object.
|
||||
- Confirm you are using the correct object type, such as ATM vs bank.
|
||||
- Confirm your group leader has acknowledged an assigned CAD task.
|
||||
- Confirm the needed store unlock has been purchased before checking VA or VG.
|
||||
- Confirm the garage spawn point is clear before using the virtual garage.
|
||||
- Confirm your player, cash, bank, or organization funds can cover the service.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Mission Designer Guide](/getting-started/mission-designer)
|
||||
- [Client CAD Usage Guide](/client-addons/cad)
|
||||
- [Client Phone Usage Guide](/client-addons/phone)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client Garage Usage Guide](/client-addons/garage)
|
||||
- [Client Locker Usage Guide](/client-addons/locker)
|
||||
- [Organization Usage Guide](/server-modules/organization)
|
||||
- [Store Usage Guide](/server-modules/store)
|
||||
- [Economy Usage Guide](/server-modules/economy)
|
||||
@ -43,6 +43,24 @@ The target is only repaired after the organization charge succeeds.
|
||||
The client garage UI forwards selected nearby vehicle repair requests through
|
||||
the same event.
|
||||
|
||||
## Rearm
|
||||
|
||||
Rearm is organization-funded.
|
||||
|
||||
Use the rearm service event:
|
||||
|
||||
```sqf
|
||||
[QEGVAR(economy,RearmService), [_target, _unit, _cost]] call CBA_fnc_serverEvent;
|
||||
```
|
||||
|
||||
`_cost` is optional. Passing `-1` uses the configured service rearm cost.
|
||||
The target is only rearmed after the organization charge succeeds.
|
||||
`setVehicleAmmo` has global effects, but the ammo is only added to local
|
||||
turrets, so the service broadcasts the ammo reset after billing succeeds.
|
||||
|
||||
The client garage UI forwards selected nearby vehicle rearm requests through
|
||||
the same event.
|
||||
|
||||
## Medical
|
||||
|
||||
Medical is player-funded first.
|
||||
|
||||
@ -36,6 +36,16 @@ assignments.
|
||||
- group status, role, and profile requests
|
||||
- map focus actions
|
||||
|
||||
## Map Focus Behavior
|
||||
|
||||
CAD list entries can drive the native map position without duplicating map
|
||||
logic in the browser UI. In operations mode, assigned or accepted task cards,
|
||||
roster member cards, and support request cards send focus events. In dispatch
|
||||
map mode, group, contract, and support request cards use the same focus path.
|
||||
|
||||
Task and support request focus uses the stored record position. Roster member
|
||||
focus uses the member position included in the hydrated group roster.
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
@ -57,6 +67,7 @@ assignments.
|
||||
| `cad::groups::role` | Update group role. |
|
||||
| `cad::groups::profile` | Update status and role together. |
|
||||
| `cad::groups::focus` | Center map on a group. |
|
||||
| `cad::members::focus` | Center map on a group member. |
|
||||
| `cad::tasks::focus` | Center map on a task. |
|
||||
| `cad::requests::focus` | Center map on a support request. |
|
||||
| `map::zoomIn` | Zoom native map in. |
|
||||
|
||||
@ -55,21 +55,21 @@ is finalized and spawned onto the resolved lane.
|
||||
| --- | --- |
|
||||
| `garage::hydrate` | Initial vehicle and session payload. |
|
||||
| `garage::sync` | Refreshed vehicle payload. |
|
||||
| `garage::service::success` | Browser notice for accepted refuel/repair requests. |
|
||||
| `garage::service::failure` | Browser notice for rejected refuel/repair requests. |
|
||||
| `garage::service::success` | Browser notice for accepted refuel/rearm/repair requests. |
|
||||
| `garage::service::failure` | Browser notice for rejected refuel/rearm/repair requests. |
|
||||
|
||||
Server action responses are handled by the action service and notification
|
||||
flow.
|
||||
|
||||
## Vehicle Service
|
||||
|
||||
The selected vehicle detail panel includes refuel and repair actions for nearby
|
||||
The selected vehicle detail panel includes refuel, rearm, and repair actions for nearby
|
||||
world vehicles. Stored records must be retrieved first because server economy
|
||||
services operate on live vehicle objects, not stored garage records.
|
||||
|
||||
Refuel requests use the server economy `RefuelService` event. Repair requests
|
||||
use the server economy `RepairService` event. Both services are billed by the
|
||||
server economy addon through organization funds.
|
||||
Refuel requests use the server economy `RefuelService` event. Rearm requests
|
||||
use `RearmService`. Repair requests use `RepairService`. These services are
|
||||
billed by the server economy addon through organization funds.
|
||||
|
||||
## Mission Setup
|
||||
|
||||
|
||||
BIN
docus/public/images/player/atm_app_home.jpg
Normal file
|
After Width: | Height: | Size: 974 KiB |
BIN
docus/public/images/player/atm_app_pin.jpg
Normal file
|
After Width: | Height: | Size: 964 KiB |
BIN
docus/public/images/player/bank_app.jpg
Normal file
|
After Width: | Height: | Size: 183 KiB |
BIN
docus/public/images/player/cad_dispatch_board.jpg
Normal file
|
After Width: | Height: | Size: 450 KiB |
BIN
docus/public/images/player/cad_ops_board.jpg
Normal file
|
After Width: | Height: | Size: 569 KiB |
BIN
docus/public/images/player/garage.jpg
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
docus/public/images/player/interaction_menu.jpg
Normal file
|
After Width: | Height: | Size: 417 KiB |
BIN
docus/public/images/player/locker.jpg
Normal file
|
After Width: | Height: | Size: 383 KiB |
BIN
docus/public/images/player/medical_respawn.jpg
Normal file
|
After Width: | Height: | Size: 759 KiB |
BIN
docus/public/images/player/org_dashboard.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
docus/public/images/player/org_home.jpg
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
docus/public/images/player/org_registration.jpg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
docus/public/images/player/org_treasury.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
docus/public/images/player/phone_contacts.jpg
Normal file
|
After Width: | Height: | Size: 954 KiB |
BIN
docus/public/images/player/phone_email.jpg
Normal file
|
After Width: | Height: | Size: 949 KiB |
BIN
docus/public/images/player/phone_home.jpg
Normal file
|
After Width: | Height: | Size: 966 KiB |
BIN
docus/public/images/player/phone_messages.jpg
Normal file
|
After Width: | Height: | Size: 951 KiB |
BIN
docus/public/images/player/store_catalog.jpg
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
docus/public/images/player/store_checkout.jpg
Normal file
|
After Width: | Height: | Size: 248 KiB |
BIN
docus/public/images/player/virtual_arsenal.jpg
Normal file
|
After Width: | Height: | Size: 654 KiB |
BIN
docus/public/images/player/virtual_garage.jpg
Normal file
|
After Width: | Height: | Size: 1020 KiB |
@ -23,9 +23,13 @@ const generatedPages = [
|
||||
source: 'docs/MISSION_DESIGNER_GUIDE.md',
|
||||
target: '1.getting-started/4.mission-designer.md'
|
||||
},
|
||||
{
|
||||
source: 'docs/PLAYER_GUIDE.md',
|
||||
target: '1.getting-started/5.player-guide.md'
|
||||
},
|
||||
{
|
||||
source: 'docs/surrealdb-setup.md',
|
||||
target: '1.getting-started/5.surrealdb-setup.md'
|
||||
target: '1.getting-started/6.surrealdb-setup.md'
|
||||
},
|
||||
{
|
||||
source: 'arma/server/docs/README.md',
|
||||
@ -435,6 +439,16 @@ npm run build:webui
|
||||
playable missions.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-user-round-check
|
||||
title: Player Guide
|
||||
to: /getting-started/player-guide
|
||||
---
|
||||
Learn the player-facing CAD, phone, bank, store, locker, garage, and economy
|
||||
workflows.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-database
|
||||
|
||||