Updated Documentation & Refactor Task Module #8

Merged
J.Schmidt92 merged 3 commits from master into feature/surrealdb-storage 2026-04-18 11:57:33 -05:00
33 changed files with 1177 additions and 384 deletions

View File

@ -1,3 +1,32 @@
# forge_server_actor # Forge Server Actor
Description for this addon ## Overview
The actor addon is the server-side bridge for player identity and character
state. It keeps Arma-facing actor snapshots in SQF while durable and hot actor
state are owned by the Rust extension.
Actor records include UID, name, loadout, position, direction, stance, rank,
life state, phone number, email, organization, and holster state.
## Dependencies
- `forge_server_main`
- `forge_server_common`
- `forge_server_extension` at runtime for actor extension calls
- `forge_client_actor` for response RPCs
## Main Components
- `fnc_initActorStore.sqf` initializes `ActorModel` and `ActorStore`.
- `ActorModel` provides defaults, player snapshot conversion, migration, and
validation.
- `ActorStore` wraps extension hot-state calls and exposes event-facing actor
operations.
## Runtime Behavior
- Missing persistent actors can be created from live player snapshots.
- Hot actor reads are migrated and hydrated before use.
- `saveHotState` in the main addon snapshots and saves actor state on player
disconnect and mission end.
## Event Surface
The addon handles server events for actor init, get, set, multi-set, save, and
remove requests, then replies to the requesting player through client actor RPCs.

View File

@ -1,3 +1,39 @@
# forge_server_bank # Forge Server Bank
Description for this addon ## Overview
The bank addon owns the SQF bridge for player accounts, cash and bank balances,
PIN/session handling, transfers, checkout charging, earnings deposits, and
credit-line repayment.
Account truth lives in the extension hot cache. SQF handles Arma-facing
validation, client messaging, session state, and payment integration with other
server addons.
## Dependencies
- `forge_server_main`
- `forge_server_common`
- `forge_server_extension` at runtime for bank extension calls
- `forge_server_org` at runtime for credit-line repayment
- `forge_client_bank` and `forge_client_notifications` for response RPCs
## Main Components
- `fnc_initBank.sqf` initializes all bank stores and helpers.
- `fnc_initModel.sqf` defines account defaults and migration behavior.
- `fnc_initPayloadBuilder.sqf` builds UI, checkout, and organization payment
context.
- `fnc_initSessionManager.sqf` manages PIN and authorization session state.
- `fnc_initMessenger.sqf` sends account syncs, alerts, and notifications.
- `fnc_initStore.sqf` wraps hot bank calls and account mutations.
## Supported Operations
- initialize and hydrate player bank state
- deposit, withdraw, transfer, and deposit earnings
- validate PIN-backed sessions
- charge checkout previews and committed purchases
- repay organization credit lines with rollback on failure
- save hot bank state to durable storage
## Runtime Notes
`forge_server_main_fnc_saveHotState` saves bank hot state on disconnect and
mission shutdown. Store checkout and task rewards use this addon for
authoritative player balance changes.

View File

@ -0,0 +1,38 @@
# Forge Server CAD
## Overview
The CAD addon coordinates dispatch-facing operational state: groups,
assignments, dispatch orders, support requests, task assignment, permissions,
hydrate payloads, and recent activity.
CAD state is extension-backed but intentionally transient. It is scoped to the
active server or mission lifecycle and starts fresh after restart.
## Dependencies
- `forge_server_main`
- `forge_server_common`
- `forge_server_actor`
- `forge_server_org`
- `forge_server_task`
- `forge_server_extension` at runtime for CAD extension calls
- `forge_client_cad` and `forge_client_notifications` for response RPCs
## Main Components
- `fnc_initCadStore.sqf` coordinates repositories and request handling.
- `fnc_initActivityRepository.sqf` records recent CAD activity.
- `fnc_initAssignmentRepository.sqf` manages task assignments and dispatch
orders.
- `fnc_initGroupRepository.sqf` manages group membership, role, and status.
- `fnc_initPermissionService.sqf` resolves dispatch permissions.
- `fnc_initPersistenceService.sqf` bridges SQF state to extension hot CAD
storage.
- `fnc_initRequestRepository.sqf` manages support requests.
## Event Surface
The addon handles hydrate, task assignment, dispatch order, support request,
task acknowledge/decline, and group update events. Successful mutations can
invalidate CAD state globally so clients refresh their views.
## Notes
CAD hydrate payloads include active task catalog entries from `TaskStore` and
organization context from `ActorStore`.

View File

@ -1,3 +1,23 @@
# forge_server_common # Forge Server Common
Common functionality shared between addons. ## Overview
The common addon provides shared SQF utilities used by server-side Forge
addons. It contains lightweight helpers only; gameplay domain state belongs in
the specific domain addons or the Rust extension.
## Dependencies
- `forge_server_main`
## Main Components
- `fnc_baseStore.sqf` provides shared hash-map object behavior such as JSON
conversion.
- `fnc_log.sqf` standardizes server log messages.
- `fnc_getPlayer.sqf` resolves online players by UID.
- `fnc_formatNumber.sqf` formats numeric values for notifications and UI text.
- `fnc_generateHash.sqf` and `fnc_generateSecureData.sqf` provide hashing and
random data helpers.
- `fnc_timeToSeconds.sqf` converts time values into seconds.
## Notes
Keep this addon free of domain-specific behavior. If a helper needs actor,
bank, org, task, store, or CAD state, it belongs in that addon instead.

View File

@ -1,3 +1,31 @@
# forge_server_economy # Forge Server Economy
Description for this addon ## Overview
The economy addon contains server-side systems for world economic interactions
that are still implemented in SQF.
Current stores cover fuel tracking, medical service behavior, and a placeholder
service economy store.
## Dependencies
- `forge_server_main`
- `forge_server_common` at runtime for logging, formatting, and player lookup
- `forge_server_bank` at runtime for medical service charges
- `forge_client_actor` and `forge_client_notifications` for response RPCs
## Main Components
- `fnc_initFEconomyStore.sqf` tracks active refueling sessions and reports fuel
totals.
- `fnc_initMEconomyStore.sqf` manages medical spawn occupancy, healing charges,
respawn placement, death inventory handling, and body-bag transfer.
- `fnc_initSEconomyStore.sqf` initializes the service economy placeholder.
## Event Surface
The addon registers CBA server events for fuel start/tick/stop, player killed,
player respawn, and healing. Medical store initialization runs after post-init
to discover configured medical spawn objects.
## Notes
The service economy store is currently a stub. Fuel and medical behavior should
stay server-authoritative because they mutate money, inventory, and respawn
state.

View File

@ -1,3 +1,29 @@
# forge_server_extension # Forge Server Extension
Extension functionality shared between addons. ## Overview
The extension addon is the SQF bridge to the `forge_server` arma-rs extension.
It normalizes `callExtension` responses, routes large payloads through the
transport layer, and exposes helper functions used by extension-backed server
addons.
## Dependencies
- `forge_server_main`
## Main Components
- `fnc_extCall.sqf` is the primary extension call wrapper.
- `fnc_transport.sqf` stages large requests and assembles chunked responses.
- `fnc_setHandler.sqf` registers local SQF handlers for extension callback
integration.
## Transport Behavior
Most commands use direct `callExtension`. Commands that can return large
payloads, or requests whose encoded arguments exceed the chunk threshold, are
routed through `transport:invoke` or staged transport requests.
The wrapper falls back to direct calls if the transport route is unsupported and
the request was not chunked.
## Notes
Domain addons should call `EFUNC(extension,extCall)` instead of calling the
extension directly. This keeps response handling, chunking, and error logging
consistent.

View File

@ -1,3 +1,30 @@
# forge_server_garage # Forge Server Garage
Description for this addon ## Overview
The garage addon is the server-side bridge for player vehicle storage and
owner-scoped vehicle unlock storage.
Garage hot state is owned by the extension. SQF validates Arma-facing requests,
serializes vehicle payloads, sends client syncs, and marks editor-placed garage
objects.
## Dependencies
- `forge_server_main`
- `forge_server_common`
- `forge_server_extension` at runtime for garage extension calls
- `forge_client_garage` for response RPCs
## Main Components
- `fnc_initGarage.sqf` initializes garage world objects.
- `fnc_initGarageStore.sqf` manages player garage hot state.
- `fnc_initVGStore.sqf` manages owner-scoped vehicle unlock state.
## Supported Operations
- initialize player garage data
- save player and owner-scoped garage state
- store and retrieve player vehicles
- initialize and save owner-scoped vehicle storage
## Runtime Notes
`forge_server_main_fnc_saveHotState` saves both `GarageStore` and
`VGarageStore` on disconnect and mission shutdown.

View File

@ -1,3 +1,30 @@
# forge_server_locker # Forge Server Locker
Description for this addon ## Overview
The locker addon is the server-side bridge for player item storage and
owner-scoped arsenal unlock storage.
Locker hot state is owned by the extension. SQF handles client events, payload
validation, synchronization, and save calls.
## Dependencies
- `forge_server_main`
- `forge_server_common`
- `forge_server_extension` at runtime for locker extension calls
- `forge_client_locker` for response RPCs
## Main Components
- `fnc_initLocker.sqf` initializes locker world objects.
- `fnc_initLockerStore.sqf` manages player locker hot state.
- `fnc_initVAStore.sqf` manages owner-scoped arsenal unlock state.
## Supported Operations
- initialize player locker data
- save player and owner-scoped locker state
- override locker data from trusted server-side callers
- initialize and save owner-scoped arsenal storage
## Runtime Notes
`forge_server_main_fnc_saveHotState` saves both `LockerStore` and `VAStore` on
disconnect and mission shutdown. Store checkout and task rewards can grant
assets into organization-owned storage through the org addon.

View File

@ -1,3 +1,29 @@
# forge_server_main # Forge Server Main
Main Addon for forge-server ## Overview
The main addon owns server-side bootstrap behavior for Forge. It prepares
functions, wires extension callbacks and ICom events, initializes shared stores,
and flushes hot state when players disconnect or the mission ends.
## Dependencies
- `cba_main`
- `ace_main`
## Main Components
- `fnc_initStores.sqf` initializes core server stores in dependency order.
- `fnc_saveHotState.sqf` snapshots and saves extension-backed hot state.
- `fnc_initValidationHarness.sqf` provides targeted runtime smoke checks for
multi-module flows.
- `XEH_preInit.sqf` registers ICom and extension callback handlers.
- `XEH_postInit.sqf` starts store initialization.
## Store Initialization
The main addon initializes shared base stores, actor, bank, garage, locker,
organization, store, and validation harness state. Some addons initialize their
own state in pre-init or post-init when they are intentionally independent of
the main bootstrap flow.
## Hot State Flush
On player disconnect, mission ended, and MP ended events, `saveHotState`
persists actor, bank, locker, virtual arsenal, garage, virtual garage, and
organization hot state for the relevant UID or all known UIDs.

View File

@ -1,3 +1,36 @@
# forge_server_org # Forge Server Organization
Description for this addon ## Overview
The organization addon is the server-side bridge for player organizations,
membership, treasury funds, reputation, credit lines, shared assets, fleet
entries, and invitations.
Organization hot state is owned by the extension. SQF coordinates Arma-facing
events, UI payloads, membership syncs, and integration with actor, bank, store,
and task flows.
## Dependencies
- `forge_server_main`
- `forge_server_common`
- `forge_server_extension` at runtime for organization extension calls
- `forge_server_actor` at runtime for organization membership lookups
- `forge_client_org` and `forge_client_notifications` for response RPCs
## Main Components
- `fnc_initOrgStore.sqf` initializes `OrgModel` and `OrgStore`.
- `fnc_initPayloadBuilder.sqf` builds portal, organization, member, asset, and
fleet payloads.
## Supported Operations
- initialize and hydrate organization portal data
- register, leave, and disband organizations
- invite, accept, and decline members
- assign and repay credit lines
- update funds and reputation
- grant assets and fleet vehicles
- save organization hot state
## Runtime Notes
The addon ensures the `default` organization exists during store creation.
Task rewards and store checkout both rely on `OrgStore` for authoritative
organization-owned state.

View File

@ -1,19 +1,33 @@
forge_server_phone # Forge Server Phone
===================
This addon provides the phone user interface and functionality for the in-game phone system. It handles all phone-related features including the UI display, interactions, and core phone operations. ## Overview
The phone addon is the server-side bridge for contacts, SMS messages, and email.
Phone runtime state is owned by the extension. SQF stores preserve the
event-facing API and synchronize client UI state.
Server State ## Dependencies
------------ - `forge_server_main`
- `forge_server_common` at runtime for online player lookup
- `forge_server_extension` at runtime for phone extension calls
- `forge_client_phone` for response RPCs
Phone contacts, messages, and emails are owned by the server extension. SQF phone stores act as bridge objects for CBA events and UI sync only. ## Main Components
- `fnc_initPhoneStore.sqf` coordinates the phone facade.
- `fnc_initContactStore.sqf` manages contacts.
- `fnc_initMessageStore.sqf` manages SMS messages and threads.
- `fnc_initEmailStore.sqf` manages email messages.
- `fnc_initPlayer.sqf` initializes phone data for a player.
Persistent SurrealDB tables: ## Persistent Extension Tables
- `phone_user`: owner row for an initialized phone profile
- `phone_contact`: per-owner contact rows keyed by owner UID and contact UID
- `phone_message`: shared message records
- `phone_message_index`: per-owner message visibility and read state
- `phone_email`: shared email records
- `phone_email_index`: per-owner email visibility and read state
- `phone_sequence`: global sequence state for generated message and email IDs
- `phone_user`: owner row for an initialized phone profile. ## Event Surface
- `phone_contact`: per-owner contact rows keyed by owner UID and contact UID. The addon handles client requests to initialize phone state, add/remove/refresh
- `phone_message`: shared message records. contacts, send/read/delete messages, send/read/delete emails, and remove phone
- `phone_message_index`: per-owner message visibility and read state. state.
- `phone_email`: shared email records.
- `phone_email_index`: per-owner email visibility and read state.
- `phone_sequence`: global sequence state for generated message and email IDs.

View File

@ -1,18 +1,35 @@
# forge_server_store # Forge Server Store
Server-side SQF module for storefront entities, catalog hydration, and checkout ## Overview
coordination. The store addon manages server-side storefront entities, catalog hydration, and
checkout coordination.
## Stores and Services SQF owns Arma-facing storefront discovery and request validation. The Rust
extension owns authoritative checkout calculation through `store:checkout`.
- `StorefrontStore` builds storefront hydrate payloads, validates checkout ## Dependencies
requests, calls the Rust `store:checkout` backend, syncs UI patches, and saves - `forge_server_main`
hot state for related modules. - `forge_server_common`
- `StoreCatalogService` scans live Arma config categories, builds catalog - `forge_server_extension` at runtime for checkout calls
responses, resolves checkout entries, and calculates authoritative prices. - `forge_server_actor`, `forge_server_bank`, and `forge_server_org` at runtime
for checkout context and payment state
- `forge_client_store` for response RPCs
## Main Components
- `fnc_initStore.sqf` marks editor-placed store objects with `isStore = true`.
- `fnc_initCatalogService.sqf` scans live Arma config categories, builds
catalog responses, resolves checkout entries, and calculates authoritative
catalog prices.
- `fnc_initStorefrontStore.sqf` builds hydrate payloads, validates checkout
requests, calls `store:checkout`, syncs client patches, and coordinates
related bank/org persistence.
## Editor Entities ## Editor Entities
`fnc_initStore` matches non-null mission namespace objects whose variable names
contain `store`, mirroring the garage entity initialization pattern.
`fnc_initStore` marks editor-placed store objects with `isStore = true`. It ## Checkout Flow
matches non-null mission namespace objects whose variable names contain Store checkout can charge cash, bank balance, organization funds, or approved
`store`, mirroring the garage entity initialization pattern. credit lines depending on the hydrated session context. Checkout results can
grant locker assets, organization assets, and fleet vehicles through the
related domain stores.

View File

@ -355,9 +355,9 @@ class CfgVehicles {
class TimeLimit: Edit { class TimeLimit: Edit {
property = "FORGE_Module_Defuse_TimeLimit"; property = "FORGE_Module_Defuse_TimeLimit";
displayName = "Time Limit"; displayName = "Time Limit";
tooltip = "Time in seconds before detenation (0 for no limit)"; tooltip = "Time in seconds before detonation; must be greater than 0";
typeName = "NUMBER"; typeName = "NUMBER";
defaultValue = 0; defaultValue = 300;
}; };
}; };
@ -608,7 +608,7 @@ class CfgVehicles {
class TimeLimit: Edit { class TimeLimit: Edit {
property = "FORGE_Module_Hostage_TimeLimit"; property = "FORGE_Module_Hostage_TimeLimit";
displayName = "Time Limit"; displayName = "Time Limit";
tooltip = "Time in seconds before HVTs escape (0 for no limit)"; tooltip = "Time in seconds before hostages are executed (0 for no limit)";
typeName = "NUMBER"; typeName = "NUMBER";
defaultValue = 0; defaultValue = 0;
}; };
@ -638,6 +638,156 @@ class CfgVehicles {
}; };
}; };
class FORGE_Module_Delivery: Module_F {
scope = 2;
displayName = "Delivery Task";
category = "FORGE_Modules";
function = QFUNC(deliveryModule);
functionPriority = 1;
isGlobal = 1;
isTriggerActivated = 1;
isDisposable = 1;
is3DEN = 0;
canSetArea = 0;
canSetAreaShape = 0;
canSetAreaHeight = 0;
class AttributeValues {};
class Attributes: AttributesBase {
class TaskID: Edit {
property = "FORGE_Module_Delivery_TaskID";
displayName = "Task ID";
tooltip = "Unique identifier for this task";
typeName = "STRING";
};
class DeliveryZone: Edit {
property = "FORGE_Module_Delivery_DeliveryZone";
displayName = "Delivery Zone Marker";
tooltip = "Name of the marker defining the delivery destination";
typeName = "STRING";
};
class LimitFail: Edit {
property = "FORGE_Module_Delivery_LimitFail";
displayName = "Fail Limit";
tooltip = "Number of cargo items damaged or lost before failing";
typeName = "NUMBER";
defaultValue = 1;
};
class LimitSuccess: Edit {
property = "FORGE_Module_Delivery_LimitSuccess";
displayName = "Success Limit";
tooltip = "Number of cargo items that must reach the delivery zone";
typeName = "NUMBER";
defaultValue = 1;
};
class CompanyFunds: Edit {
property = "FORGE_Module_Delivery_CompanyFunds";
displayName = "Reward Funds";
tooltip = "Amount of funds awarded on success";
typeName = "NUMBER";
defaultValue = 0;
};
class RatingFail: Edit {
property = "FORGE_Module_Delivery_RatingFail";
displayName = "Rating Loss";
tooltip = "Amount of rating lost on failure";
typeName = "NUMBER";
defaultValue = 0;
};
class RatingSuccess: Edit {
property = "FORGE_Module_Delivery_RatingSuccess";
displayName = "Rating Gain";
tooltip = "Amount of rating gained on success";
typeName = "NUMBER";
defaultValue = 0;
};
class EndSuccess: Combo {
property = "FORGE_Module_Delivery_EndSuccess";
displayName = "End on Success";
tooltip = "End mission when task is completed successfully";
typeName = "BOOL";
defaultValue = 0;
class Values {
class EnableEndSuccess { name = "Enable"; value = 1; };
class DisableEndSuccess { name = "Disable"; value = 0; };
};
};
class EndFail: Combo {
property = "FORGE_Module_Delivery_EndFail";
displayName = "End on Failure";
tooltip = "End mission when task fails";
typeName = "BOOL";
defaultValue = 0;
class Values {
class EnableEndFail { name = "Enable"; value = 1; };
class DisableEndFail { name = "Disable"; value = 0; };
};
};
class TimeLimit: Edit {
property = "FORGE_Module_Delivery_TimeLimit";
displayName = "Time Limit";
tooltip = "Seconds to complete delivery (0 for no limit)";
typeName = "NUMBER";
defaultValue = 0;
};
};
class ModuleDescription: ModuleDescription {
description = "Creates a delivery task. Sync with a FORGE_Module_Cargo to register cargo objects.";
sync[] = { "Anything" };
class Anything {
description[] = {
"Delivery task module",
"Sync with FORGE_Module_Cargo which groups the cargo objects"
};
position = 1;
direction = 1;
optional = 1;
duplicate = 1;
};
};
};
class FORGE_Module_Cargo: Module_F {
scope = 2;
displayName = "Cargo Entities";
category = "FORGE_Modules";
function = QFUNC(cargoModule);
functionPriority = 1;
isGlobal = 1;
isTriggerActivated = 0;
isDisposable = 1;
is3DEN = 0;
canSetArea = 0;
canSetAreaShape = 0;
canSetAreaHeight = 0;
class AttributeValues {};
class Attributes: AttributesBase {};
class ModuleDescription: ModuleDescription {
description = "Grouping module for cargo objects in a delivery task. Sync with objects to mark as cargo, then sync this module to FORGE_Module_Delivery.";
sync[] = { "Anything" };
class Anything {
description[] = {
"Cargo entities module",
"Sync with objects to mark as delivery cargo"
};
position = 1;
direction = 1;
optional = 1;
duplicate = 1;
};
};
};
class FORGE_Module_HVT: Module_F { class FORGE_Module_HVT: Module_F {
scope = 2; scope = 2;
displayName = "HVT Task"; displayName = "HVT Task";
@ -757,7 +907,7 @@ class CfgVehicles {
class TimeLimit: Edit { class TimeLimit: Edit {
property = "FORGE_Module_HVT_TimeLimit"; property = "FORGE_Module_HVT_TimeLimit";
displayName = "Time Limit"; displayName = "Time Limit";
tooltip = "Time in seconds before HVTs escape (0 for no limit)"; tooltip = "Time in seconds before the HVT task fails (0 for no limit)";
typeName = "NUMBER"; typeName = "NUMBER";
defaultValue = 0; defaultValue = 0;
}; };

View File

@ -16,6 +16,7 @@ system intentionally starts clean after each server or mission restart.
- notify task participants and sync org updates to online members - notify task participants and sync org updates to online members
## Dependencies ## Dependencies
- `forge_server_extension`
- `forge_server_common` - `forge_server_common`
- `forge_server_actor` - `forge_server_actor`
- `forge_server_bank` - `forge_server_bank`
@ -58,6 +59,10 @@ Org rewards always go to the bound owner org. Player earnings still use per-play
## Usage ## Usage
Task time limits use `0` for no limit on attack, destroy, delivery, hostage,
and HVT tasks. Defuse IED timers are different: each IED must have a positive
countdown value.
### Start Through The Handler ### Start Through The Handler
Use the handler when you want reputation gating and task ownership binding. Use the handler when you want reputation gating and task ownership binding.

View File

@ -1,5 +1,6 @@
PREP(attack); PREP(attack);
PREP(attackModule); PREP(attackModule);
PREP(cargoModule);
PREP(defend); PREP(defend);
PREP(defendModule); PREP(defendModule);
PREP(defuse); PREP(defuse);
@ -29,3 +30,4 @@ PREP(initTaskStore);
PREP(protectedModule); PREP(protectedModule);
PREP(shootersModule); PREP(shootersModule);
PREP(spawnEnemyWave); PREP(spawnEnemyWave);
PREP(startTask);

View File

@ -13,7 +13,7 @@
* 5: Amount of rating the company and player recieve if the task is successful <NUMBER> (default: 0) * 5: Amount of rating the company and player recieve if the task is successful <NUMBER> (default: 0)
* 6: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false) * 6: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false)
* 7: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false) * 7: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false)
* 8: Amount of time before target(s) escape <NUMBER> (default: -1) * 8: Amount of time before target(s) escape <NUMBER> (default: 0, 0 = no limit)
* 9: Equipment rewards <ARRAY> (default: []) * 9: Equipment rewards <ARRAY> (default: [])
* 10: Supply rewards <ARRAY> (default: []) * 10: Supply rewards <ARRAY> (default: [])
* 11: Weapon rewards <ARRAY> (default: []) * 11: Weapon rewards <ARRAY> (default: [])
@ -39,7 +39,7 @@ params [
["_ratingSuccess", 0, [0]], ["_ratingSuccess", 0, [0]],
["_endSuccess", false, [false]], ["_endSuccess", false, [false]],
["_endFail", false, [false]], ["_endFail", false, [false]],
["_time", -1, [0]], ["_timeLimit", 0, [0]],
["_equipmentRewards", [], [[]]], ["_equipmentRewards", [], [[]]],
["_supplyRewards", [], [[]]], ["_supplyRewards", [], [[]]],
["_weaponRewards", [], [[]]], ["_weaponRewards", [], [[]]],
@ -58,7 +58,7 @@ waitUntil {
}; };
_targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]]; _targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]];
private _startTime = if (!isNil "_time") then { floor(time) } else { nil }; private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
waitUntil { waitUntil {
sleep 1; sleep 1;
@ -66,8 +66,8 @@ waitUntil {
private _targetsKilled = ({ !alive _x } count _targets); private _targetsKilled = ({ !alive _x } count _targets);
if (_time isNotEqualTo -1) then { if (_timeLimit isNotEqualTo 0) then {
private _timeExpired = (floor time - _startTime >= _time); private _timeExpired = (floor time - _startTime >= _timeLimit);
if (_targetsKilled < _limitSuccess && _timeExpired) then { _result = 1; }; if (_targetsKilled < _limitSuccess && _timeExpired) then { _result = 1; };

View File

@ -23,29 +23,38 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru
if !(_activated) exitWith {}; if !(_activated) exitWith {};
private _taskID = _logic getVariable ["TaskID", ""]; private _taskID = _logic getVariable ["TaskID", ""];
private _limitFail = _logic getVariable ["LimitFail", -1]; if (_taskID isEqualTo "") exitWith {
private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; ["ERROR", "Attack module: no task ID configured."] call EFUNC(common,log);
private _companyFunds = _logic getVariable ["CompanyFunds", 0];
private _ratingFail = _logic getVariable ["RatingFail", 0];
private _ratingSuccess = _logic getVariable ["RatingSuccess", 0];
private _endSuccess = _logic getVariable ["EndSuccess", false];
private _endFail = _logic getVariable ["EndFail", false];
private _timeLimit = _logic getVariable ["TimeLimit", 0];
["INFO", format ["Attack Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, Funds: %4, RatingFail: %5, RatingSuccess: %6, EndSuccess: %7, EndFail: %8, Time: %9", _taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]] call EFUNC(common,log);
private _syncedEntities = synchronizedObjects _logic;
["INFO", format ["Attack Module Synced Entities: %1", _syncedEntities]] call EFUNC(common,log);
{
[_x, _taskID] spawn FUNC(makeTarget);
} forEach _syncedEntities;
private _params = [_taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail];
if (_timeLimit != 0) then {
_params pushBack _timeLimit;
}; };
["attack", _params, 0, ""] spawn FUNC(handler); private _syncedEntities = synchronizedObjects _logic;
["INFO", format ["Attack Module: TaskID: %1, Synced entities: %2", _taskID, count _syncedEntities]] call EFUNC(common,log);
private _taskPos = if (_syncedEntities isNotEqualTo []) then {
getPosATL (_syncedEntities select 0)
} else {
getPosATL _logic
};
[
"attack",
_taskID,
_taskPos,
format ["Attack: %1", _taskID],
"Eliminate all hostile forces in the area.",
createHashMapFromArray [
["targets", _syncedEntities]
],
createHashMapFromArray [
["limitFail", _logic getVariable ["LimitFail", -1]],
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["timeLimit", _logic getVariable ["TimeLimit", 0]]
]
] call FUNC(startTask);
deleteVehicle _logic; deleteVehicle _logic;

View File

@ -0,0 +1,23 @@
#include "..\script_component.hpp"
/*
* Author: IDSolutions
* Grouping module for cargo entities in a delivery task.
* This module has no logic of its own — it acts as a sync target so that
* cargo objects can be grouped and discovered by the parent delivery module
* via synchronizedObjects + typeOf "FORGE_Module_Cargo".
*
* Arguments:
* 0: Logic <OBJECT>
* 1: Units <ARRAY>
* 2: Activated <BOOL>
*
* Return Value:
* None
*
* Public: No
*/
params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", true, [true]]];
if !(_activated) exitWith {};

View File

@ -2,60 +2,65 @@
/* /*
* Author: IDSolutions * Author: IDSolutions
* Creates a defend task module * Initializes the defend task module.
* Reads parameters from the logic object and delegates to fnc_startTask.
* The designer must place a named marker in Eden for the defense zone.
* Enemy waves are spawned automatically by fnc_defend — no entities need
* to be synced to this module.
* *
* Arguments: * Arguments:
* None * 0: Logic <OBJECT>
* 1: Units <ARRAY>
* 2: Activated <BOOL>
* *
* Return Value: * Return Value:
* None * None
* *
* Example:
* call forge_server_task_fnc_defendModule;
*
* Public: No * Public: No
*/ */
// Module category params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", true, [true]]];
private _category = "Forge Tasks";
private _subCategory = "Defense Tasks";
// Create the module if !(_activated) exitWith {};
private _module = createDialog "RscDisplayAttributes";
_module setVariable ["category", _category];
_module setVariable ["subcategory", _subCategory];
_module setVariable ["description", "Configure a defend task"];
// Add fields for task configuration private _taskID = _logic getVariable ["TaskID", ""];
[_module, "Task ID", "taskID", "", true] call BIS_fnc_addAttribute; private _defenseZone = _logic getVariable ["DefenseZone", ""];
[_module, "Defense Zone Marker", "defenseZone", "", true] call BIS_fnc_addAttribute;
[_module, "Defense Time (seconds)", "defendTime", "600", true] call BIS_fnc_addAttribute;
[_module, "Min BLUFOR in Zone", "minBlufor", "1", true] call BIS_fnc_addAttribute;
[_module, "Company Funds Reward", "companyFunds", "500000", true] call BIS_fnc_addAttribute;
[_module, "Rating Loss on Fail", "ratingFail", "-100", true] call BIS_fnc_addAttribute;
[_module, "Rating Gain on Success", "ratingSuccess", "400", true] call BIS_fnc_addAttribute;
[_module, "End Mission on Success", "endSuccess", "false", false] call BIS_fnc_addAttribute;
[_module, "End Mission on Fail", "endFail", "false", false] call BIS_fnc_addAttribute;
[_module, "Enemy Wave Count", "waveCount", "3", false] call BIS_fnc_addAttribute;
[_module, "Time Between Waves (seconds)", "waveCooldown", "300", false] call BIS_fnc_addAttribute;
// Add confirm button handler if (_taskID isEqualTo "") exitWith {
_module setVariable ["onConfirm", { ["ERROR", "Defend module: no task ID configured."] call EFUNC(common,log);
params ["_module"]; };
private _taskID = _module getVariable ["taskID", ""]; if (_defenseZone isEqualTo "" || { markerShape _defenseZone isEqualTo "" }) exitWith {
private _defenseZone = _module getVariable ["defenseZone", ""]; ["ERROR", format ["Defend module '%1': DefenseZone marker '%2' is missing or invalid.", _taskID, _defenseZone]] call EFUNC(common,log);
private _defendTime = parseNumber (_module getVariable ["defendTime", "600"]); };
private _companyFunds = parseNumber (_module getVariable ["companyFunds", "500000"]);
private _ratingFail = parseNumber (_module getVariable ["ratingFail", "-100"]);
private _ratingSuccess = parseNumber (_module getVariable ["ratingSuccess", "400"]);
private _endSuccess = _module getVariable ["endSuccess", "false"] == "true";
private _endFail = _module getVariable ["endFail", "false"] == "true";
private _waveCount = parseNumber (_module getVariable ["waveCount", "3"]);
private _waveCooldown = parseNumber (_module getVariable ["waveCooldown", "300"]);
private _minBlufor = parseNumber (_module getVariable ["minBlufor", "1"]);
// Create the task ["INFO", format [
private _params = [_taskID, _defenseZone, _defendTime, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _waveCount, _waveCooldown, _minBlufor]; "Defend Module Parameters: TaskID: %1, DefenseZone: %2, DefendTime: %3, WaveCount: %4, WaveCooldown: %5, MinBlufor: %6",
private _requesterUid = ["", getPlayerUID player] select hasInterface; _taskID, _defenseZone,
["defend", _params, 0, _requesterUid] spawn FUNC(handler); _logic getVariable ["DefendTime", 600],
}]; _logic getVariable ["WaveCount", 3],
_logic getVariable ["WaveCooldown", 300],
_logic getVariable ["MinBlufor", 1]
]] call EFUNC(common,log);
[
"defend",
_taskID,
getMarkerPos _defenseZone,
format ["Defend: %1", _taskID],
"Hold the defense zone against incoming enemy forces.",
createHashMap,
createHashMapFromArray [
["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["defenseZone", _defenseZone],
["defendTime", _logic getVariable ["DefendTime", 600]],
["waveCount", _logic getVariable ["WaveCount", 3]],
["waveCooldown", _logic getVariable ["WaveCooldown", 300]],
["minBlufor", _logic getVariable ["MinBlufor", 1]]
]
] call FUNC(startTask);
deleteVehicle _logic;

View File

@ -23,42 +23,48 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru
if !(_activated) exitWith {}; if !(_activated) exitWith {};
private _taskID = _logic getVariable ["TaskID", ""]; private _taskID = _logic getVariable ["TaskID", ""];
private _limitFail = _logic getVariable ["LimitFail", -1]; if (_taskID isEqualTo "") exitWith {
private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; ["ERROR", "Defuse module: no task ID configured."] call EFUNC(common,log);
private _companyFunds = _logic getVariable ["CompanyFunds", 0]; };
private _ratingFail = _logic getVariable ["RatingFail", 0];
private _ratingSuccess = _logic getVariable ["RatingSuccess", 0];
private _endSuccess = _logic getVariable ["EndSuccess", false];
private _endFail = _logic getVariable ["EndFail", false];
private _timeLimit = _logic getVariable ["TimeLimit", 0];
["INFO", format ["Defuse Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, Funds: %4, RatingFail: %5, RatingSuccess: %6, EndSuccess: %7, EndFail: %8, Time: %9", _taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]] call EFUNC(common,log);
private _syncedModules = synchronizedObjects _logic; private _syncedModules = synchronizedObjects _logic;
["INFO", format ["Defuse Module Synced Modules: %1", _syncedModules]] call EFUNC(common,log); private _iedModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Explosives" }) param [0, objNull];
private _protectedModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Protected" }) param [0, objNull];
private _iedEntities = if (!isNull _iedModule) then { synchronizedObjects _iedModule } else { [] };
private _protectedEntities = if (!isNull _protectedModule) then { synchronizedObjects _protectedModule } else { [] };
private _iedModule = (_syncedModules select { typeOf _x == "FORGE_Module_Explosives" }) select 0; ["INFO", format [
private _protectedModule = (_syncedModules select { typeOf _x == "FORGE_Module_Protected" }) select 0; "Defuse Module: TaskID: %1, IEDs: %2, Protected: %3, IED timer: %4s",
_taskID, count _iedEntities, count _protectedEntities,
_logic getVariable ["TimeLimit", 300]
]] call EFUNC(common,log);
private _explosiveEntities = synchronizedObjects _iedModule; private _taskPos = if (_iedEntities isNotEqualTo []) then {
["INFO", format ["Defuse Module Explosive Entites: %1", _explosiveEntities]] call EFUNC(common,log); getPosATL (_iedEntities select 0)
} else {
getPosATL _logic
};
private _protectedEntities = synchronizedObjects _protectedModule; [
["INFO", format ["Defuse Module Protected Entities: %1", _protectedEntities]] call EFUNC(common,log); "defuse",
_taskID,
{ _taskPos,
if (!isNull _x) then { format ["Defuse: %1", _taskID],
[_x, _taskID, _timeLimit] spawn FUNC(makeIED); "Locate and defuse all explosive devices before they detonate.",
}; createHashMapFromArray [
} forEach _explosiveEntities; ["ieds", _iedEntities],
["protected", _protectedEntities]
{ ],
if (!isNull _x) then { createHashMapFromArray [
[_x, _taskID] spawn FUNC(makeObject); ["limitFail", _logic getVariable ["LimitFail", -1]],
}; ["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
} forEach _protectedEntities; ["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
private _params = [_taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; ["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["defuse", _params, 0, ""] spawn FUNC(handler); ["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["iedTimer", _logic getVariable ["TimeLimit", 300]]
]
] call FUNC(startTask);
deleteVehicle _logic; deleteVehicle _logic;

View File

@ -14,7 +14,7 @@
* 6: Amount of rating the company and player receive if the task is successful <NUMBER> (default: 0) * 6: Amount of rating the company and player receive if the task is successful <NUMBER> (default: 0)
* 7: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false) * 7: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false)
* 8: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false) * 8: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false)
* 9: Amount of time to complete delivery <NUMBER> (default: -1) * 9: Amount of time to complete delivery <NUMBER> (default: 0)
* 10: Equipment rewards <ARRAY> (default: []) * 10: Equipment rewards <ARRAY> (default: [])
* 11: Supply rewards <ARRAY> (default: []) * 11: Supply rewards <ARRAY> (default: [])
* 12: Weapon rewards <ARRAY> (default: []) * 12: Weapon rewards <ARRAY> (default: [])
@ -41,7 +41,7 @@ params [
["_ratingSuccess", 0, [0]], ["_ratingSuccess", 0, [0]],
["_endSuccess", false, [false]], ["_endSuccess", false, [false]],
["_endFail", false, [false]], ["_endFail", false, [false]],
["_time", -1, [0]], ["_timeLimit", 0, [0]],
["_equipmentRewards", [], [[]]], ["_equipmentRewards", [], [[]]],
["_supplyRewards", [], [[]]], ["_supplyRewards", [], [[]]],
["_weaponRewards", [], [[]]], ["_weaponRewards", [], [[]]],
@ -60,7 +60,7 @@ waitUntil {
}; };
_cargo = GVAR(TaskStore) call ["getTaskEntities", ["cargo", _taskID]]; _cargo = GVAR(TaskStore) call ["getTaskEntities", ["cargo", _taskID]];
private _startTime = if (_time isNotEqualTo -1) then { floor(time) } else { nil }; private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
waitUntil { waitUntil {
sleep 1; sleep 1;
@ -69,8 +69,8 @@ waitUntil {
private _cargoDelivered = ({ _x inArea _deliveryZone && (damage _x) < 0.7 } count _cargo); private _cargoDelivered = ({ _x inArea _deliveryZone && (damage _x) < 0.7 } count _cargo);
private _cargoDamaged = ({ damage _x >= 0.7 } count _cargo); private _cargoDamaged = ({ damage _x >= 0.7 } count _cargo);
if (_time isNotEqualTo -1) then { if (_timeLimit isNotEqualTo 0) then {
private _timeExpired = (floor time - _startTime >= _time); private _timeExpired = (floor time - _startTime >= _timeLimit);
if (_cargoDamaged >= _limitFail) then { _result = 1; }; if (_cargoDamaged >= _limitFail) then { _result = 1; };
if (_cargoDelivered < _limitSuccess && _timeExpired) then { _result = 1; }; if (_cargoDelivered < _limitSuccess && _timeExpired) then { _result = 1; };

View File

@ -2,66 +2,70 @@
/* /*
* Author: IDSolutions * Author: IDSolutions
* Creates a delivery task module * Initializes the delivery task module.
* Reads parameters from the logic object, collects cargo from the synced
* FORGE_Module_Cargo grouping module, and delegates to fnc_startTask.
*
* Eden layout:
* [FORGE_Module_Delivery] --sync--> [FORGE_Module_Cargo] --sync--> cargo_obj1, cargo_obj2
* *
* Arguments: * Arguments:
* None * 0: Logic <OBJECT>
* 1: Units <ARRAY>
* 2: Activated <BOOL>
* *
* Return Value: * Return Value:
* None * None
* *
* Example:
* call forge_server_task_fnc_deliveryModule;
*
* Public: No * Public: No
*/ */
// Module category params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", true, [true]]];
private _category = "Forge Tasks";
private _subCategory = "Delivery Tasks";
// Create the module if !(_activated) exitWith {};
private _module = createDialog "RscDisplayAttributes";
_module setVariable ["category", _category];
_module setVariable ["subcategory", _subCategory];
_module setVariable ["description", "Configure a delivery task"];
// Add fields for task configuration private _taskID = _logic getVariable ["TaskID", ""];
[_module, "Task ID", "taskID", "", true] call BIS_fnc_addAttribute; if (_taskID isEqualTo "") exitWith {
[_module, "Fail Limit", "limitFail", "1", true] call BIS_fnc_addAttribute; ["ERROR", "Delivery module: no task ID configured."] call EFUNC(common,log);
[_module, "Success Count", "limitSuccess", "3", true] call BIS_fnc_addAttribute; };
[_module, "Delivery Zone", "deliveryZone", "", true] call BIS_fnc_addAttribute;
[_module, "Company Funds Reward", "companyFunds", "250000", true] call BIS_fnc_addAttribute;
[_module, "Rating Loss on Fail", "ratingFail", "-75", true] call BIS_fnc_addAttribute;
[_module, "Rating Gain on Success", "ratingSuccess", "300", true] call BIS_fnc_addAttribute;
[_module, "End Mission on Success", "endSuccess", "false", false] call BIS_fnc_addAttribute;
[_module, "End Mission on Fail", "endFail", "false", false] call BIS_fnc_addAttribute;
[_module, "Time Limit (seconds)", "timeLimit", "", false] call BIS_fnc_addAttribute;
// Add confirm button handler private _syncedModules = synchronizedObjects _logic;
_module setVariable ["onConfirm", { private _cargoModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Cargo" }) param [0, objNull];
params ["_module"]; private _cargoEntities = if (!isNull _cargoModule) then { synchronizedObjects _cargoModule } else { [] };
private _taskID = _module getVariable ["taskID", ""]; ["INFO", format [
private _limitFail = parseNumber (_module getVariable ["limitFail", "1"]); "Delivery Module Parameters: TaskID: %1, DeliveryZone: %2, Cargo count: %3",
private _limitSuccess = parseNumber (_module getVariable ["limitSuccess", "3"]); _taskID,
private _deliveryZone = _module getVariable ["deliveryZone", ""]; _logic getVariable ["DeliveryZone", ""],
private _companyFunds = parseNumber (_module getVariable ["companyFunds", "250000"]); count _cargoEntities
private _ratingFail = parseNumber (_module getVariable ["ratingFail", "-75"]); ]] call EFUNC(common,log);
private _ratingSuccess = parseNumber (_module getVariable ["ratingSuccess", "300"]);
private _endSuccess = _module getVariable ["endSuccess", "false"] == "true";
private _endFail = _module getVariable ["endFail", "false"] == "true";
private _timeLimit = _module getVariable ["timeLimit", ""];
// Convert time limit to number or nil private _taskPos = if (_cargoEntities isNotEqualTo []) then {
private _timeLimitNum = if (_timeLimit == "") then { nil } else { parseNumber _timeLimit }; getPosATL (_cargoEntities select 0)
} else {
getPosATL _logic
};
// Create the task [
private _params = [_taskID, _limitFail, _limitSuccess, _deliveryZone, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail]; "delivery",
if (!isNil "_timeLimitNum") then { _taskID,
_params pushBack _timeLimitNum; _taskPos,
}; format ["Delivery: %1", _taskID],
"Transport all cargo to the designated delivery zone.",
createHashMapFromArray [
["cargo", _cargoEntities]
],
createHashMapFromArray [
["limitFail", _logic getVariable ["LimitFail", -1]],
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["timeLimit", _logic getVariable ["TimeLimit", 0]],
["deliveryZone", _logic getVariable ["DeliveryZone", ""]]
]
] call FUNC(startTask);
private _requesterUid = ["", getPlayerUID player] select hasInterface; deleteVehicle _logic;
["delivery", _params, 0, _requesterUid] spawn FUNC(handler);
}];

View File

@ -13,7 +13,7 @@
* 5: Amount of rating the company and player recieve if the task is successful <NUMBER> (default: 0) * 5: Amount of rating the company and player recieve if the task is successful <NUMBER> (default: 0)
* 6: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false) * 6: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false)
* 7: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false) * 7: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false)
* 8: Amount of time before target(s) escape <NUMBER> (default: -1) * 8: Amount of time before target(s) escape <NUMBER> (default: 0, 0 = no limit)
* 9: Equipment rewards <ARRAY> (default: []) * 9: Equipment rewards <ARRAY> (default: [])
* 10: Supply rewards <ARRAY> (default: []) * 10: Supply rewards <ARRAY> (default: [])
* 11: Weapon rewards <ARRAY> (default: []) * 11: Weapon rewards <ARRAY> (default: [])
@ -39,7 +39,7 @@ params [
["_ratingSuccess", 0, [0]], ["_ratingSuccess", 0, [0]],
["_endSuccess", false, [false]], ["_endSuccess", false, [false]],
["_endFail", false, [false]], ["_endFail", false, [false]],
["_time", -1, [0]], ["_timeLimit", 0, [0]],
["_equipmentRewards", [], [[]]], ["_equipmentRewards", [], [[]]],
["_supplyRewards", [], [[]]], ["_supplyRewards", [], [[]]],
["_weaponRewards", [], [[]]], ["_weaponRewards", [], [[]]],
@ -58,7 +58,7 @@ waitUntil {
}; };
_targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]]; _targets = GVAR(TaskStore) call ["getTaskEntities", ["targets", _taskID]];
private _startTime = if (!isNil "_time") then { floor(time) } else { nil }; private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
waitUntil { waitUntil {
sleep 1; sleep 1;
@ -66,8 +66,8 @@ waitUntil {
private _targetsDestroyed = ({ !alive _x } count _targets); private _targetsDestroyed = ({ !alive _x } count _targets);
if (!isNil "_time") then { if (_timeLimit isNotEqualTo 0) then {
private _timeExpired = (floor time - _startTime >= _time); private _timeExpired = (floor time - _startTime >= _timeLimit);
if (_targetsDestroyed < _limitSuccess && _timeExpired) then { _result = 1; }; if (_targetsDestroyed < _limitSuccess && _timeExpired) then { _result = 1; };

View File

@ -23,29 +23,38 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru
if !(_activated) exitWith {}; if !(_activated) exitWith {};
private _taskID = _logic getVariable ["TaskID", ""]; private _taskID = _logic getVariable ["TaskID", ""];
private _limitFail = _logic getVariable ["LimitFail", -1]; if (_taskID isEqualTo "") exitWith {
private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; ["ERROR", "Destroy module: no task ID configured."] call EFUNC(common,log);
private _companyFunds = _logic getVariable ["CompanyFunds", 0];
private _ratingFail = _logic getVariable ["RatingFail", 0];
private _ratingSuccess = _logic getVariable ["RatingSuccess", 0];
private _endSuccess = _logic getVariable ["EndSuccess", false];
private _endFail = _logic getVariable ["EndFail", false];
private _timeLimit = _logic getVariable ["TimeLimit", 0];
["INFO", format ["Destroy Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, Funds: %4, RatingFail: %5, RatingSuccess: %6, EndSuccess: %7, EndFail: %8, Time: %9", _taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit]] call EFUNC(common,log);
private _syncedEntities = synchronizedObjects _logic;
["INFO", format ["Destroy Module Synced Entities: %1", _syncedEntities]] call EFUNC(common,log);
{
[_x, _taskID] spawn FUNC(makeTarget);
} forEach _syncedEntities;
private _params = [_taskID, _limitFail, _limitSuccess, _companyFunds, _ratingFail, _ratingSuccess, _endSuccess, _endFail];
if (_timeLimit != 0) then {
_params pushBack _timeLimit;
}; };
["destroy", _params, 0, ""] spawn FUNC(handler); private _syncedEntities = synchronizedObjects _logic;
["INFO", format ["Destroy Module: TaskID: %1, Synced entities: %2", _taskID, count _syncedEntities]] call EFUNC(common,log);
private _taskPos = if (_syncedEntities isNotEqualTo []) then {
getPosATL (_syncedEntities select 0)
} else {
getPosATL _logic
};
[
"destroy",
_taskID,
_taskPos,
format ["Destroy: %1", _taskID],
"Locate and destroy all designated targets.",
createHashMapFromArray [
["targets", _syncedEntities]
],
createHashMapFromArray [
["limitFail", _logic getVariable ["LimitFail", -1]],
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["timeLimit", _logic getVariable ["TimeLimit", 0]]
]
] call FUNC(startTask);
deleteVehicle _logic; deleteVehicle _logic;

View File

@ -15,7 +15,7 @@
* 7: Subcategory of task <ARRAY> (default: [false, true]) * 7: Subcategory of task <ARRAY> (default: [false, true])
* 8: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false) * 8: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false)
* 9: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false) * 9: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false)
* 10: Amount of time before hostage(s) die <NUMBER> (default: -1) * 10: Amount of time before hostage(s) are executed <NUMBER> (default: 0, 0 = no limit)
* 11: Marker name for the cbrn zone <STRING> (default: "") * 11: Marker name for the cbrn zone <STRING> (default: "")
* 12: Equipment rewards <ARRAY> (default: []) * 12: Equipment rewards <ARRAY> (default: [])
* 13: Supply rewards <ARRAY> (default: []) * 13: Supply rewards <ARRAY> (default: [])
@ -45,7 +45,7 @@ params [
["_type", [["_cbrn", false, [false]], ["_hostage", true, [false]]]], ["_type", [["_cbrn", false, [false]], ["_hostage", true, [false]]]],
["_endSuccess", false, [false]], ["_endSuccess", false, [false]],
["_endFail", false, [false]], ["_endFail", false, [false]],
["_time", -1, [0]], ["_timeLimit", 0, [0]],
["_cbrnZone", "", [""]], ["_cbrnZone", "", [""]],
["_equipmentRewards", [], [[]]], ["_equipmentRewards", [], [[]]],
["_supplyRewards", [], [[]]], ["_supplyRewards", [], [[]]],
@ -75,7 +75,7 @@ waitUntil {
_hostages = GVAR(TaskStore) call ["getTaskEntities", ["hostages", _taskID]]; _hostages = GVAR(TaskStore) call ["getTaskEntities", ["hostages", _taskID]];
_shooters = GVAR(TaskStore) call ["getTaskEntities", ["shooters", _taskID]]; _shooters = GVAR(TaskStore) call ["getTaskEntities", ["shooters", _taskID]];
private _startTime = if (_time isNotEqualTo -1) then { floor(time) } else { nil }; private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
waitUntil { waitUntil {
sleep 1; sleep 1;
@ -86,8 +86,8 @@ waitUntil {
private _hostagesKilled = ({ !alive _x } count _hostages); private _hostagesKilled = ({ !alive _x } count _hostages);
private _shootersAlive = ({ alive _x } count _shooters); private _shootersAlive = ({ alive _x } count _shooters);
if (_time isNotEqualTo -1) then { if (_timeLimit isNotEqualTo 0) then {
private _timeExpired = (floor time - _startTime >= _time); private _timeExpired = (floor time - _startTime >= _timeLimit);
if (_hostagesFreed < _limitSuccess && _timeExpired) then { _result = 1; }; if (_hostagesFreed < _limitSuccess && _timeExpired) then { _result = 1; };
if (_hostagesKilled >= _limitFail) then { _result = 1; }; if (_hostagesKilled >= _limitFail) then { _result = 1; };

View File

@ -23,54 +23,56 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru
if !(_activated) exitWith {}; if !(_activated) exitWith {};
private _taskID = _logic getVariable ["TaskID", ""]; private _taskID = _logic getVariable ["TaskID", ""];
private _limitFail = _logic getVariable ["LimitFail", -1]; if (_taskID isEqualTo "") exitWith {
private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; ["ERROR", "Hostage module: no task ID configured."] call EFUNC(common,log);
private _extraction = _logic getVariable ["ExtZone", ""];
private _companyFunds = _logic getVariable ["CompanyFunds", 0];
private _ratingFail = _logic getVariable ["RatingFail", 0];
private _ratingSuccess = _logic getVariable ["RatingSuccess", 0];
private _cbrn = _logic getVariable ["CBRN", false];
private _execution = _logic getVariable ["Execution", false];
private _endSuccess = _logic getVariable ["EndSuccess", false];
private _endFail = _logic getVariable ["EndFail", false];
private _timeLimit = _logic getVariable ["TimeLimit", 0];
private _cbrnZone = _logic getVariable ["CBRNZone", ""];
["INFO", format [
"Hostage Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, ExtractionZone: %4, Funds: %5, RatingFail: %6, RatingSuccess: %7, CBRN: %8, Execution: %9, EndSuccess: %10, EndFail: %11, Time: %12, CBRNZone: %13",
_taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, _cbrn, _execution, _endSuccess, _endFail, _timeLimit, _cbrnZone
]] call EFUNC(common,log);
private _syncedModules = synchronizedObjects _logic;
["INFO", format ["Hostage Module Synced Entities: %1", _syncedModules]] call EFUNC(common,log);
private _hostageModule = (_syncedModules select { typeOf _x == "FORGE_Module_Hostages" }) select 0;
private _shooterModule = (_syncedModules select { typeOf _x == "FORGE_Module_Shooters" }) select 0;
private _hostageEntities = synchronizedObjects _hostageModule;
["INFO", format ["Hostage Module Hostage Entities: %1", _hostageEntities]] call EFUNC(common,log);
private _shooterEntities = synchronizedObjects _shooterModule;
["INFO", format ["Hostage Module Shooter Entities: %1", _shooterEntities]] call EFUNC(common,log);
{
if (!isNull _x && (_x isNotEqualTo str objNull)) then {
[_x, _taskID] spawn FUNC(makeHostage);
};
} forEach _hostageEntities;
{
if (!isNull _x && (_x isNotEqualTo str objNull)) then {
[_x, _taskID] spawn FUNC(makeShooter);
};
} forEach _shooterEntities;
private _params = [_taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, [_cbrn, _execution], _endSuccess, _endFail];
if (_timeLimit != 0) then {
_params pushBack _timeLimit;
_params pushBack _cbrnZone;
}; };
["hostage", _params, 0, ""] spawn FUNC(handler); private _syncedModules = synchronizedObjects _logic;
private _hostageModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Hostages" }) param [0, objNull];
private _shooterModule = (_syncedModules select { typeOf _x isEqualTo "FORGE_Module_Shooters" }) param [0, objNull];
private _hostageEntities = if (!isNull _hostageModule) then { synchronizedObjects _hostageModule } else { [] };
private _shooterEntities = if (!isNull _shooterModule) then { synchronizedObjects _shooterModule } else { [] };
["INFO", format [
"Hostage Module: TaskID: %1, ExtZone: %2, Hostages: %3, Shooters: %4, CBRN: %5, Execution: %6",
_taskID,
_logic getVariable ["ExtZone", ""],
count _hostageEntities,
count _shooterEntities,
_logic getVariable ["CBRN", false],
_logic getVariable ["Execution", false]
]] call EFUNC(common,log);
private _taskPos = if (_hostageEntities isNotEqualTo []) then {
getPosATL (_hostageEntities select 0)
} else {
getPosATL _logic
};
[
"hostage",
_taskID,
_taskPos,
format ["Hostage Rescue: %1", _taskID],
"Locate and rescue the hostages and bring them to the extraction zone.",
createHashMapFromArray [
["hostages", _hostageEntities],
["shooters", _shooterEntities]
],
createHashMapFromArray [
["limitFail", _logic getVariable ["LimitFail", -1]],
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["timeLimit", _logic getVariable ["TimeLimit", 0]],
["extractionZone", _logic getVariable ["ExtZone", ""]],
["cbrn", _logic getVariable ["CBRN", false]],
["execution", _logic getVariable ["Execution", false]],
["cbrnZone", _logic getVariable ["CBRNZone", ""]]
]
] call FUNC(startTask);
deleteVehicle _logic; deleteVehicle _logic;

View File

@ -15,7 +15,7 @@
* 7: Subcategory of task <ARRAY> (default: [true, false]) * 7: Subcategory of task <ARRAY> (default: [true, false])
* 8: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false) * 8: Should the mission end (MissionSuccess) if the task is successful <BOOL> (default: false)
* 9: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false) * 9: Should the mission end (MissionFailed) if the task is failed <BOOL> (default: false)
* 10: Amount of time before hvt(s) die <NUMBER> (default: -1) * 10: Amount of time before HVT task fails <NUMBER> (default: 0, 0 = no limit)
* 11: Equipment rewards <ARRAY> (default: []) * 11: Equipment rewards <ARRAY> (default: [])
* 12: Supply rewards <ARRAY> (default: []) * 12: Supply rewards <ARRAY> (default: [])
* 13: Weapon rewards <ARRAY> (default: []) * 13: Weapon rewards <ARRAY> (default: [])
@ -45,7 +45,7 @@ params [
["_type", [["_capture", true, [false]], ["_eliminate", false, [false]]]], ["_type", [["_capture", true, [false]], ["_eliminate", false, [false]]]],
["_endSuccess", false, [false]], ["_endSuccess", false, [false]],
["_endFail", false, [false]], ["_endFail", false, [false]],
["_time", -1, [0]], ["_timeLimit", 0, [0]],
["_equipmentRewards", [], [[]]], ["_equipmentRewards", [], [[]]],
["_supplyRewards", [], [[]]], ["_supplyRewards", [], [[]]],
["_weaponRewards", [], [[]]], ["_weaponRewards", [], [[]]],
@ -66,7 +66,7 @@ waitUntil {
}; };
_hvts = GVAR(TaskStore) call ["getTaskEntities", ["hvts", _taskID]]; _hvts = GVAR(TaskStore) call ["getTaskEntities", ["hvts", _taskID]];
private _startTime = if (!isNil "_time") then { floor(time) } else { nil }; private _startTime = if (_timeLimit isNotEqualTo 0) then { floor(time) } else { nil };
waitUntil { waitUntil {
sleep 1; sleep 1;
@ -76,8 +76,8 @@ waitUntil {
private _hvtsKilled = ({ !alive _x } count _hvts); private _hvtsKilled = ({ !alive _x } count _hvts);
private _hvtsInZone = ({ _x inArea _extZone } count _hvts); private _hvtsInZone = ({ _x inArea _extZone } count _hvts);
if (!isNil "_time") then { if (_timeLimit isNotEqualTo 0) then {
private _timeExpired = (floor time - _startTime >= _time); private _timeExpired = (floor time - _startTime >= _timeLimit);
if (_capture && _hvtsKilled >= _limitFail) then { _result = 1; }; if (_capture && _hvtsKilled >= _limitFail) then { _result = 1; };
if (_capture && _hvtsCaptive < _limitSuccess && _timeExpired) then { _result = 1; }; if (_capture && _hvtsCaptive < _limitSuccess && _timeExpired) then { _result = 1; };

View File

@ -23,37 +23,46 @@ params [["_logic", objNull, [objNull]], ["_units", [], [[]]], ["_activated", tru
if !(_activated) exitWith {}; if !(_activated) exitWith {};
private _taskID = _logic getVariable ["TaskID", ""]; private _taskID = _logic getVariable ["TaskID", ""];
private _limitFail = _logic getVariable ["LimitFail", -1]; if (_taskID isEqualTo "") exitWith {
private _limitSuccess = _logic getVariable ["LimitSuccess", -1]; ["ERROR", "HVT module: no task ID configured."] call EFUNC(common,log);
private _extraction = _logic getVariable ["ExtZone", ""];
private _companyFunds = _logic getVariable ["CompanyFunds", 0];
private _ratingFail = _logic getVariable ["RatingFail", 0];
private _ratingSuccess = _logic getVariable ["RatingSuccess", 0];
private _capture = _logic getVariable ["CaptureHVT", true];
private _eliminate = _logic getVariable ["EliminateHVT", false];
private _endSuccess = _logic getVariable ["EndSuccess", false];
private _endFail = _logic getVariable ["EndFail", false];
private _timeLimit = _logic getVariable ["TimeLimit", 0];
["INFO", format [
"HVT Module Parameters: TaskID: %1, LimitFail: %2, LimitSuccess: %3, ExtractionZone: %4, Funds: %5, RatingFail: %6, RatingSuccess: %7, CaptureHvt: %8, EliminateHvt: %9, EndSuccess: %10, EndFail: %11, Time: %12",
_taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, _capture, _eliminate, _endSuccess, _endFail, _timeLimit
]] call EFUNC(common,log);
private _syncedEntities = synchronizedObjects _logic;
["INFO", format ["HVT Module Synced Entities: %1", _syncedEntities]] call EFUNC(common,log);
{
if (!isNull _x && (_x isNotEqualTo str objNull)) then {
[_x, _taskID] spawn FUNC(makeHVT);
};
} forEach _syncedEntities;
private _params = [_taskID, _limitFail, _limitSuccess, _extraction, _companyFunds, _ratingFail, _ratingSuccess, [_capture, _eliminate], _endSuccess, _endFail];
if (_timeLimit != 0) then {
_params pushBack _timeLimit;
}; };
["hvt", _params, 0, ""] spawn FUNC(handler); private _syncedEntities = synchronizedObjects _logic;
["INFO", format [
"HVT Module: TaskID: %1, ExtZone: %2, CaptureHVT: %3, HVTs: %4",
_taskID,
_logic getVariable ["ExtZone", ""],
_logic getVariable ["CaptureHVT", true],
count _syncedEntities
]] call EFUNC(common,log);
private _taskPos = if (_syncedEntities isNotEqualTo []) then {
getPosATL (_syncedEntities select 0)
} else {
getPosATL _logic
};
[
"hvt",
_taskID,
_taskPos,
format ["HVT: %1", _taskID],
"Locate and capture or eliminate the high-value target.",
createHashMapFromArray [
["hvts", _syncedEntities]
],
createHashMapFromArray [
["limitFail", _logic getVariable ["LimitFail", -1]],
["limitSuccess", _logic getVariable ["LimitSuccess", -1]],
["funds", _logic getVariable ["CompanyFunds", 0]],
["ratingFail", _logic getVariable ["RatingFail", 0]],
["ratingSuccess", _logic getVariable ["RatingSuccess", 0]],
["endSuccess", _logic getVariable ["EndSuccess", false]],
["endFail", _logic getVariable ["EndFail", false]],
["timeLimit", _logic getVariable ["TimeLimit", 0]],
["extractionZone", _logic getVariable ["ExtZone", ""]],
["captureHvt", _logic getVariable ["CaptureHVT", true]]
]
] call FUNC(startTask);
deleteVehicle _logic; deleteVehicle _logic;

View File

@ -2,12 +2,12 @@
/* /*
* Author: IDSolutions * Author: IDSolutions
* Assigns an IED to a task and starts countdown timer * Assigns an IED to a task and starts its required countdown timer
* *
* Arguments: * Arguments:
* 0: The object <OBJECT> * 0: The object <OBJECT>
* 1: ID of the task <STRING> * 1: ID of the task <STRING>
* 2: The Countdown Timer <NUMBER> * 2: Countdown timer in seconds <NUMBER> (must be greater than 0)
* *
* Return Value: * Return Value:
* None * None
@ -22,7 +22,7 @@ params [["_entity", objNull, [objNull]], ["_taskID", "", [""]], ["_time", 0, [0]
if (isNull _entity) exitWith { ["ERROR", "Attempt to create entity from null object"] call EFUNC(common,log); }; if (isNull _entity) exitWith { ["ERROR", "Attempt to create entity from null object"] call EFUNC(common,log); };
if (_taskID == "") exitWith { ["ERROR", "No task ID provided for entity"] call EFUNC(common,log); }; if (_taskID == "") exitWith { ["ERROR", "No task ID provided for entity"] call EFUNC(common,log); };
if (_time < 0) exitWith { ["ERROR", "Invalid time provided for IED"] call EFUNC(common,log); }; if (_time <= 0) exitWith { ["ERROR", "Invalid time provided for IED"] call EFUNC(common,log); };
["INFO", format ["Make IED: %1", _this]] call EFUNC(common,log); ["INFO", format ["Make IED: %1", _this]] call EFUNC(common,log);

View File

@ -244,42 +244,11 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [
["special", _specialRewards] ["special", _specialRewards]
] ]
}], }],
["createAttackTask", compileFinal {
params [
["_taskID", "", [""]],
["_position", [0, 0, 0], [[]]],
["_locationConfig", configNull, [configNull]]
];
if (_taskID isEqualTo "" || { isNull _locationConfig }) exitWith { false };
private _locationKey = configName _locationConfig;
private _locationType = getText (_locationConfig >> "type");
if (_locationType isEqualTo "") then { _locationType = "area"; };
[
west,
_taskID,
[
format ["Eliminate hostile forces operating near %1.", _locationKey],
format ["Attack: %1", _locationKey],
_locationType
],
_position,
"CREATED",
1,
true,
"attack"
] call BFUNC(taskCreate);
true
}],
["startAttackMission", compileFinal { ["startAttackMission", compileFinal {
private _attackConfig = _self getOrDefault ["attackConfig", configNull]; private _attackConfig = _self getOrDefault ["attackConfig", configNull];
private _locationData = _self call ["selectAttackLocation"]; private _locationData = _self call ["selectAttackLocation"];
if (_locationData isEqualTo createHashMap) exitWith { "" }; if (_locationData isEqualTo createHashMap) exitWith { "" };
private _location = _locationData getOrDefault ["config", configNull];
private _locationKey = _locationData getOrDefault ["key", ""]; private _locationKey = _locationData getOrDefault ["key", ""];
private _position = _locationData getOrDefault ["position", [0, 0, 0]]; private _position = _locationData getOrDefault ["position", [0, 0, 0]];
private _group = _self call ["spawnAttackGroup", [_position]]; private _group = _self call ["spawnAttackGroup", [_position]];
@ -292,45 +261,40 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [
}; };
private _taskID = format ["task_attack_%1", round (diag_tickTime * 1000)]; private _taskID = format ["task_attack_%1", round (diag_tickTime * 1000)];
{
[_x, _taskID] call FUNC(makeTarget);
} forEach _units;
_self call ["createAttackTask", [_taskID, _position, _location]];
GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArray [
["type", "attack"],
["title", format ["Attack: %1", _locationKey]],
["description", format ["Eliminate hostile forces operating near %1.", _locationKey]],
["position", _position],
["locationKey", _locationKey],
["accepted", false],
["requesterUid", ""],
["orgID", "default"],
["source", "mission_manager"]
]]];
private _rewardRange = getArray (_attackConfig >> "Rewards" >> "money"); private _rewardRange = getArray (_attackConfig >> "Rewards" >> "money");
private _reputationRange = getArray (_attackConfig >> "Rewards" >> "reputation"); private _reputationRange = getArray (_attackConfig >> "Rewards" >> "reputation");
private _penaltyRange = getArray (_attackConfig >> "penalty"); private _penaltyRange = getArray (_attackConfig >> "penalty");
private _timeRange = getArray (_attackConfig >> "timeLimit"); private _timeRange = getArray (_attackConfig >> "timeLimit");
private _rewards = _self call ["rollRewards"]; private _rewards = _self call ["rollRewards"];
private _params = [ private _success = [
"attack",
_taskID, _taskID,
_position,
format ["Attack: %1", _locationKey],
format ["Eliminate hostile forces operating near %1.", _locationKey],
createHashMapFromArray [["targets", _units]],
createHashMapFromArray [
["limitFail", 0],
["limitSuccess", count _units],
["funds", _rewardRange call BFUNC(randomNum)],
["ratingFail", _penaltyRange call BFUNC(randomNum)],
["ratingSuccess", _reputationRange call BFUNC(randomNum)],
["endSuccess", false],
["endFail", false],
["timeLimit", _timeRange call BFUNC(randomNum)],
["equipment", _rewards get "equipment"],
["supplies", _rewards get "supplies"],
["weapons", _rewards get "weapons"],
["vehicles", _rewards get "vehicles"],
["special", _rewards get "special"]
],
0, 0,
count _units, "",
_rewardRange call BFUNC(randomNum), "mission_manager"
_penaltyRange call BFUNC(randomNum), ] call FUNC(startTask);
_reputationRange call BFUNC(randomNum),
false, if !(_success) exitWith { "" };
false,
_timeRange call BFUNC(randomNum),
_rewards get "equipment",
_rewards get "supplies",
_rewards get "weapons",
_rewards get "vehicles",
_rewards get "special"
];
private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap]; private _activeMissionRegistry = _self getOrDefault ["activeMissionRegistry", createHashMap];
_activeMissionRegistry set [_taskID, createHashMapFromArray [ _activeMissionRegistry set [_taskID, createHashMapFromArray [
@ -339,7 +303,6 @@ GVAR(MissionManagerBaseClass) = compileFinal createHashMapFromArray [
]]; ]];
_self set ["activeMissionRegistry", _activeMissionRegistry]; _self set ["activeMissionRegistry", _activeMissionRegistry];
["attack", _params, 0, ""] spawn FUNC(handler);
_taskID _taskID
}], }],
["completeMission", compileFinal { ["completeMission", compileFinal {

View File

@ -0,0 +1,199 @@
#include "..\script_component.hpp"
/*
* Author: IDSolutions
* Unified task initializer used by both Eden modules and missionManager-generated
* tasks. Registers entities, creates the BIS task, registers the catalog entry,
* and dispatches to the task handler.
*
* Arguments:
* 0: Task type <STRING> ("attack"|"defuse"|"destroy"|"delivery"|"hostage"|"hvt"|"defend")
* 1: Task ID <STRING>
* 2: Task position <ARRAY>
* 3: Task title <STRING>
* 4: Task description <STRING>
* 5: Task entities <HASHMAP>
* Keys: "targets" <ARRAY> -- attack, destroy
* "hostages" <ARRAY> -- hostage
* "shooters" <ARRAY> -- hostage
* "hvts" <ARRAY> -- hvt
* "ieds" <ARRAY> -- defuse
* "protected" <ARRAY> -- defuse
* "cargo" <ARRAY> -- delivery
* 6: Task parameters <HASHMAP>
* Common keys:
* "limitFail" <NUMBER> (default: -1)
* "limitSuccess" <NUMBER> (default: -1)
* "funds" <NUMBER> (default: 0)
* "ratingFail" <NUMBER> (default: 0)
* "ratingSuccess" <NUMBER> (default: 0)
* "endSuccess" <BOOL> (default: false)
* "endFail" <BOOL> (default: false)
* "timeLimit" <NUMBER> (default: 0, 0 = no limit)
* Reward keys:
* "equipment" <ARRAY>, "supplies" <ARRAY>, "weapons" <ARRAY>,
* "vehicles" <ARRAY>, "special" <ARRAY>
* Type-specific keys:
* defuse: "iedTimer" <NUMBER> -- required IED countdown in seconds (> 0)
* delivery: "deliveryZone" <STRING> -- marker name
* hostage: "extractionZone" <STRING> -- marker name
* "cbrn" <BOOL> (default: false)
* "execution" <BOOL> (default: false)
* "cbrnZone" <STRING> (default: "")
* hvt: "extractionZone" <STRING> -- marker name (capture mode only)
* "captureHvt" <BOOL> (default: true)
* defend: "defenseZone" <STRING> -- marker name
* "defendTime" <NUMBER> (default: 600)
* "waveCount" <NUMBER> (default: 3)
* "waveCooldown" <NUMBER> (default: 300)
* "minBlufor" <NUMBER> (default: 1)
* 7: Minimum org reputation required <NUMBER> (default: 0)
* 8: Requester UID <STRING> (default: "")
* 9: Source tag <STRING> (default: "eden") -- "eden"|"mission_manager"|"script"
*
* Return Value:
* Success <BOOL>
*
* Examples:
* // From a unit init field -- register entity first, then start task from trigger/init.sqf:
* [this, "compound_attack_01"] call forge_server_task_fnc_makeTarget;
*
* // From a trigger or init.sqf (all-in-one):
* [
* "attack", "compound_attack_01", getPosATL leader1,
* "Attack: East Compound", "Eliminate all hostile forces.",
* createHashMapFromArray [["targets", [unit1, unit2, unit3]]],
* createHashMapFromArray [
* ["limitFail", 0], ["limitSuccess", 3],
* ["funds", 50000], ["ratingFail", -10], ["ratingSuccess", 20],
* ["timeLimit", 900]
* ]
* ] call forge_server_task_fnc_startTask;
*
* Public: Yes
*/
params [
["_taskType", "", [""]],
["_taskID", "", [""]],
["_position", [0, 0, 0], [[]]],
["_title", "", [""]],
["_description", "", [""]],
["_entities", createHashMap, [createHashMap]],
["_taskParams", createHashMap, [createHashMap]],
["_minRating", 0, [0]],
["_requesterUid", "", [""]],
["_source", "eden", [""]]
];
if (_taskType isEqualTo "" || { _taskID isEqualTo "" }) exitWith {
["ERROR", "startTask: missing task type or task ID."] call EFUNC(common,log);
false
};
// --- 1. Register task entities ---
private _iedTimer = _taskParams getOrDefault ["iedTimer", 0];
{
private _role = _x;
private _objects = _entities getOrDefault [_role, []];
{
if (isNull _x) then { continue; };
switch (_role) do {
case "targets": { [_x, _taskID] call FUNC(makeTarget); };
case "hostages": { [_x, _taskID] call FUNC(makeHostage); };
case "shooters": { [_x, _taskID] call FUNC(makeShooter); };
case "hvts": { [_x, _taskID] call FUNC(makeHVT); };
case "ieds": { [_x, _taskID, _iedTimer] call FUNC(makeIED); };
case "protected": { [_x, _taskID] call FUNC(makeObject); };
case "cargo": { [_x, _taskID] call FUNC(makeCargo); };
};
} forEach _objects;
} forEach ["targets", "hostages", "shooters", "hvts", "ieds", "protected", "cargo"];
// --- 2. Create BIS task ---
[west, _taskID, [_description, _title, _taskType], _position, "CREATED", 1, true, _taskType] call BFUNC(taskCreate);
// --- 3. Register catalog entry ---
GVAR(TaskStore) call ["registerTaskCatalogEntry", [_taskID, createHashMapFromArray [
["type", _taskType],
["title", _title],
["description", _description],
["position", _position],
["accepted", false],
["requesterUid", _requesterUid],
["orgID", "default"],
["source", _source]
]]];
// --- 4. Assemble type-specific handler args ---
private _limitFail = _taskParams getOrDefault ["limitFail", -1];
private _limitSuccess = _taskParams getOrDefault ["limitSuccess", -1];
private _funds = _taskParams getOrDefault ["funds", 0];
private _ratingFail = _taskParams getOrDefault ["ratingFail", 0];
private _ratingSuccess = _taskParams getOrDefault ["ratingSuccess", 0];
private _endSuccess = _taskParams getOrDefault ["endSuccess", false];
private _endFail = _taskParams getOrDefault ["endFail", false];
private _timeLimit = _taskParams getOrDefault ["timeLimit", 0];
private _equipRewards = _taskParams getOrDefault ["equipment", []];
private _supplyRewards = _taskParams getOrDefault ["supplies", []];
private _weaponRewards = _taskParams getOrDefault ["weapons", []];
private _vehicleRewards = _taskParams getOrDefault ["vehicles", []];
private _specialRewards = _taskParams getOrDefault ["special", []];
private _rewardTail = [_equipRewards, _supplyRewards, _weaponRewards, _vehicleRewards, _specialRewards];
private _handlerArgs = switch (_taskType) do {
case "attack";
case "destroy": {
private _args = [_taskID, _limitFail, _limitSuccess, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit];
_args + _rewardTail
};
case "defuse": {
[_taskID, _limitFail, _limitSuccess, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail] + _rewardTail
};
case "delivery": {
private _deliveryZone = _taskParams getOrDefault ["deliveryZone", ""];
private _args = [_taskID, _limitFail, _limitSuccess, _deliveryZone, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _timeLimit];
_args + _rewardTail
};
case "hostage": {
private _extZone = _taskParams getOrDefault ["extractionZone", ""];
private _cbrn = _taskParams getOrDefault ["cbrn", false];
private _execution = _taskParams getOrDefault ["execution", false];
private _cbrnZone = _taskParams getOrDefault ["cbrnZone", ""];
private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_cbrn, _execution], _endSuccess, _endFail, _timeLimit];
_args pushBack _cbrnZone;
_args + _rewardTail
};
case "hvt": {
private _extZone = _taskParams getOrDefault ["extractionZone", ""];
private _captureHvt = _taskParams getOrDefault ["captureHvt", true];
private _args = [_taskID, _limitFail, _limitSuccess, _extZone, _funds, _ratingFail, _ratingSuccess, [_captureHvt, !_captureHvt], _endSuccess, _endFail, _timeLimit];
_args + _rewardTail
};
case "defend": {
private _defenseZone = _taskParams getOrDefault ["defenseZone", ""];
private _defendTime = _taskParams getOrDefault ["defendTime", 600];
private _waveCount = _taskParams getOrDefault ["waveCount", 3];
private _waveCooldown = _taskParams getOrDefault ["waveCooldown", 300];
private _minBlufor = _taskParams getOrDefault ["minBlufor", 1];
[_taskID, _defenseZone, _defendTime, _funds, _ratingFail, _ratingSuccess, _endSuccess, _endFail, _waveCount, _waveCooldown, _minBlufor] + _rewardTail
};
default {
["ERROR", format ["startTask: unknown task type '%1'.", _taskType]] call EFUNC(common,log);
[]
};
};
if (_handlerArgs isEqualTo []) exitWith { false };
// --- 5. Dispatch handler ---
[_taskType, _handlerArgs, _minRating, _requesterUid] spawn FUNC(handler);
true

View File

@ -29,7 +29,7 @@ docs/ Framework-level documentation
| Organization | Player organizations, membership, treasury, credit lines, shared assets, and fleet data. | `arma/client/addons/org` | `arma/server/addons/org` | `lib/models/src/org.rs`, `lib/services/src/org.rs` | `org:*`, `org:hot:*` | | Organization | Player organizations, membership, treasury, credit lines, shared assets, and fleet data. | `arma/client/addons/org` | `arma/server/addons/org` | `lib/models/src/org.rs`, `lib/services/src/org.rs` | `org:*`, `org:hot:*` |
| Phone | Contacts, messages, and email state. | `arma/client/addons/phone` | `arma/server/addons/phone` | `lib/models/src/phone.rs`, `lib/services/src/phone.rs` | `phone:*` | | Phone | Contacts, messages, and email state. | `arma/client/addons/phone` | `arma/server/addons/phone` | `lib/models/src/phone.rs`, `lib/services/src/phone.rs` | `phone:*` |
| Store | Storefront entity setup, catalog hydration, checkout workflows, and checkout charging integration. | `arma/client/addons/store` | `arma/server/addons/store` | `lib/models/src/store.rs`, `lib/services/src/store.rs` | `store:checkout` | | Store | Storefront entity setup, catalog hydration, checkout workflows, and checkout charging integration. | `arma/client/addons/store` | `arma/server/addons/store` | `lib/models/src/store.rs`, `lib/services/src/store.rs` | `store:checkout` |
| Task | Mission/task catalog, ownership, status, reward context, and task counters. | none | `arma/server/addons/task` | `lib/models/src/task.rs`, `lib/services/src/task.rs` | `task:*` | | Task | Server-owned mission/task flows, catalog, ownership, status, participant tracking, rewards, and defuse counters. | none | `arma/server/addons/task` | `lib/models/src/task.rs`, `lib/services/src/task.rs` | `task:*` |
| Owned Garage | Organization or owner-scoped vehicle unlock storage. | via garage/org UI | server extension only | `lib/models/src/v_garage.rs`, `lib/services/src/v_garage.rs` | `owned:garage:*` | | Owned Garage | Organization or owner-scoped vehicle unlock storage. | via garage/org UI | server extension only | `lib/models/src/v_garage.rs`, `lib/services/src/v_garage.rs` | `owned:garage:*` |
| Owned Locker | Organization or owner-scoped arsenal unlock storage. | via locker/org UI | server extension only | `lib/models/src/v_locker.rs`, `lib/services/src/v_locker.rs` | `owned:locker:*` | | Owned Locker | Organization or owner-scoped arsenal unlock storage. | via locker/org UI | server extension only | `lib/models/src/v_locker.rs`, `lib/services/src/v_locker.rs` | `owned:locker:*` |

View File

@ -4,6 +4,20 @@ The task module stores transient mission task metadata for active server or
mission lifecycle workflows. SQF still owns Arma-only runtime state such as mission lifecycle workflows. SQF still owns Arma-only runtime state such as
objects and participants. objects and participants.
The server addon at `arma/server/addons/task` also owns task execution:
creating BIS tasks, registering task entities, tracking participants, binding
task ownership, applying player/org rewards, and clearing task state when a
task completes.
Runtime dependencies:
- `forge_server_extension`
- `forge_server_common`
- `forge_server_actor`
- `forge_server_bank`
- `forge_server_org`
- `forge_client_notifications`
## Data Model ## Data Model
Catalog entries are flexible JSON objects. The service normalizes these fields Catalog entries are flexible JSON objects. The service normalizes these fields
@ -102,6 +116,78 @@ private _context = fromJSON (_result select 0);
The reward context contains `requesterUid` and `orgId`. The reward context contains `requesterUid` and `orgId`.
## Server Task Flows
The task addon provides these server-owned task flows:
- `attack`
- `defend`
- `defuse`
- `delivery`
- `destroy`
- `hostage`
- `hvt`
Use `forge_server_task_fnc_startTask` when creating tasks from modules,
mission scripts, or generated mission-manager content. It registers task
entities, creates the BIS task, stores the catalog entry, then dispatches
through `forge_server_task_fnc_handler`.
```sqf
[
"attack",
"compound_attack_01",
getPosATL leader1,
"Attack: East Compound",
"Eliminate all hostile forces.",
createHashMapFromArray [["targets", [unit1, unit2, unit3]]],
createHashMapFromArray [
["limitFail", 0],
["limitSuccess", 3],
["funds", 50000],
["ratingFail", -10],
["ratingSuccess", 20],
["timeLimit", 900]
],
0,
getPlayerUID player,
"script"
] call forge_server_task_fnc_startTask;
```
Use `forge_server_task_fnc_handler` directly when the task entities are already
registered and you want reputation gating plus ownership binding:
```sqf
[
"delivery",
["delivery_1", 1, 3, "delivery_zone", 250000, -75, 300, false, false, 900],
250,
getPlayerUID player
] call forge_server_task_fnc_handler;
```
Direct task function calls still work for mission-authored or server-owned
tasks, but they do not provide a requester UID. Ownership falls back to the
`default` org.
## Timer Semantics
Task time limits use `0` for no limit:
- attack `timeLimit`
- destroy `timeLimit`
- delivery `timeLimit`
- hostage `timeLimit`
- HVT `timeLimit`
Positive values are measured in seconds. Do not pass `-1` as a no-limit value;
the task runtime treats any non-zero task time limit as active.
Defuse IED timers are different. `iedTimer` must be greater than `0`, because
IEDs are expected to have an active countdown. The Eden defuse module defaults
to `300` seconds.
## Defuse Counter ## Defuse Counter
```sqf ```sqf