ALiVE.OS · ALiVE 3 Development
The events ALiVE raises while a mission runs, and how to listen for them from your own scripts.
Scripting reference · ALiVE 3
ALiVE 3 is in active development. This briefing describes work in progress, and the behaviour it covers is subject to change as testing continues. Check the changelog at the foot of the page for what has moved recently.
Overview
ALiVE runs an internal event log. Every time a commander confirms an operation, a task is created, a resupply lands or a profiled unit is killed, ALiVE raises an event describing it. Your mission can register a listener against the event types it cares about and be called back whenever one of them fires, which lets mission scripts react to what ALiVE is doing without polling or reading its internals.
This page lists every event type ALiVE currently raises, along with the data each one carries, taken directly from the code that raises them. It is a reference for mission makers writing their own scripts. Nothing here is required to play or to build a mission in the editor.
How it works
Getting started
// in initServer.sqf, after ALiVE has finished starting up
[] spawn {
waitUntil {!isNil "ALiVE_REQUIRE_INITIALISED"};
MyMission_fnc_opcomListener = compile preprocessFileLineNumbers "opcomListener.sqf";
myListener = [nil, "create", ["MyMission_fnc_opcomListener"]] call MyMission_fnc_opcomListener;
};
And the listener itself:
// opcomListener.sqf
params ["_logic", "_operation", "_args"];
private "_result";
switch (_operation) do {
case "create": {
_args params ["_functionName"];
private _logic = [[["class", _functionName]]] call ALiVE_fnc_hashCreate;
private _listenerID = [
ALiVE_eventLog,
"addListener",
[_logic, ["OPCOM_CAPTURE", "TACOM_ORDER_COMPLETE"]]
] call ALiVE_fnc_eventLog;
[_logic, "listenerID", _listenerID] call ALiVE_fnc_hashSet;
_result = _logic;
};
case "handleEvent": {
private _event = _args;
private _type = [_event, "type"] call ALiVE_fnc_hashGet;
private _data = [_event, "data"] call ALiVE_fnc_hashGet;
switch (_type) do {
case "OPCOM_CAPTURE": {
_data params ["_side", "_objective"];
private _pos = [_objective, "center"] call ALiVE_fnc_hashGet;
// react here
};
case "TACOM_ORDER_COMPLETE": {
_data params ["_opcomID", "_objective", "_tacomState", "_side", "_factions", "_success"];
// react here
};
};
};
};
if (!isNil "_result") then {_result} else {nil};
Worked example
A common request. The commander events describe intent and orders rather than an objective quietly changing hands, so for ownership this reads control directly and keeps each sector in step with its nearest objective.
// Pairing Arma's Sector modules with ALiVE objectives.
// Place your sector modules in the editor and name them. For each sector this
// finds every commander's nearest objective, then hands the sector to the
// side whose commander holds its objective there. Run it on the server.
[] spawn {
waitUntil {!isNil "ALiVE_REQUIRE_INITIALISED"};
waitUntil {!isNil "OPCOM_instances" && {count OPCOM_instances > 0}};
private _sectors = [sector1, sector2, sector3]; // your editor-placed modules
private _pairs = [];
// once: for each sector, each commander's closest objective
{
private _sector = _x;
private _candidates = [];
{
private _side = [_x, "side", ""] call ALiVE_fnc_hashGet;
private _objectives = [_x, "objectives", []] call ALiVE_fnc_hashGet;
private _best = [];
private _bestDist = 1e9;
{
private _centre = [_x, "center"] call ALiVE_fnc_hashGet;
if (!isNil "_centre" && {count _centre > 1}) then {
private _d = _sector distance2D _centre;
if (_d < _bestDist) then {_bestDist = _d; _best = _x};
};
} forEach _objectives;
// only an objective that is really at this sector
if (_best isNotEqualTo [] && {_bestDist < 300}) then {_candidates pushBack [_side, _best]};
} forEach OPCOM_instances;
if (_candidates isNotEqualTo []) then {_pairs pushBack [_sector, _candidates]};
} forEach _sectors;
// then keep them in step
while {true} do {
{
_x params ["_sector", "_candidates"];
private _owner = sideUnknown;
{
_x params ["_side", "_objective"];
if ([_objective, _side, 300, false] call ALiVE_fnc_isHeldObjective) exitWith {
_owner = [_side] call ALiVE_fnc_sideTextToObject;
};
} forEach _candidates;
if (_owner isNotEqualTo sideUnknown) then {
// hand _owner to your sector here
};
} forEach _pairs;
sleep 30;
};
};
Reference
46 event types, grouped by the module that raises them. The data column is the array you get back from the event's data key.
These describe what the AI commander intends and what its subordinate orders did. They do not fire on an objective silently changing hands, so if you want to mirror ownership onto something else, poll instead. See the note below the tables.
| Event | Data | Raised when |
|---|---|---|
| OPCOM_ORDER_CONFIRMED | [opcomID, objective, operation, side, factions] | The commander has settled on an operation for an objective. |
| TACOM_ORDER_ISSUED | [opcomID, objective, operation, side, factions, args] | The order has gone out to the groups that will carry it out. |
| TACOM_ORDER_COMPLETE | [opcomID, objective, tacomState, side, factions, success, args] | An order finished. The success flag says whether it achieved its aim. |
| OPCOM_CAPTURE | [side, objective] | An objective has moved into the capture state for that side. |
| OPCOM_DEFEND | [side, objective] | An objective has moved into the defend state. |
| OPCOM_RECON | [side, objective] | An objective has moved into the recon state. |
| OPCOM_RESERVE | [side, objective] | An objective is being held as a reserve. |
| OPCOM_TERRORIZE | [side, objective] | Asymmetric commanders only. An objective is being targeted for intimidation. |
| ASYMM_INSTALLATION_DESTROYED | [installationType, building, killer] | An insurgent installation has been destroyed. It also goes out as a CBA event of the same name on every machine, so ["ASYMM_INSTALLATION_DESTROYED", {...}] call CBA_fnc_addEventHandler hears it on clients too. |
The task engine raises these as tasks are requested, created, changed and finished. TASK_GENERATE is the request channel other modules use to ask for a task; TASK_CREATE is the task actually existing.
| Event | Data | Raised when |
|---|---|---|
| TASK_GENERATE | [requestID, playerID, side, faction, type, location, destination, players, enemyFaction, current, apply, ...] | Something has asked for a task to be generated. playerID is a player's ID for a player's request, otherwise the module asking: CP, ARTY, OPCOM, LOGCOM, ATO or C2ISTAR. Some task types add more after apply, most often a list of targets. |
| TASK_CREATE | [requestID, playerID, side, destination, faction, title, description, players, state, apply, current, parent, source, editingDisabled, rev, id] | A task has been created. |
| TASK_UPDATE | [taskID, playerID, side, destination, faction, title, description, players, state, apply, current, parent, source, editingDisabled, rev, id] | An existing task has changed. The layout matches TASK_CREATE, except that it starts with the task's ID rather than the request's. |
| TASK_DELETE | [taskID, playerID, side] | A task has been removed, by C2ISTAR, by a commander clearing its own task, or by a player's order replacing an earlier one. |
| TASK_SUCCEEDED | [taskDialog, side, faction, rewardData] | A task was completed successfully and a reward is being handed out. |
| TASKS_AUTO_GENERATE | [requestID, playerID, side, faction, enemyFaction, autoGenerate] | Automatic task generation settings for a side have been applied. |
| TASKS_UPDATE | ["", playerID, side] | A refresh of the task list has been asked for. |
| TASKS_UPDATED | [playerID, [sideTasks, autoGenerateSide]] | The task list has been rebuilt and is being pushed out. |
| TASKS_SYNC | [playerID, groupID] | A joining player needs the current task list. |
The replies carry their outcome in the event's message key: [_event, "message"] call ALiVE_fnc_hashGet. LOGCOM_RESPONSE sends ACKNOWLEDGED, REQUEST_INSERTION, REQUEST_ENROUTE, REQUEST_ARRIVED, REQUEST_DELIVERED, REQUEST_LOST, CANCEL_OK, CANCEL_FAILED, STATUS, DENIED_WAITING_INIT, DENIED_FACTION_HANDLER_NOT_FOUND, DENIED_FORCEPOOL, DENIED_FORCE_CREATION and DENIED_NOT_AVAILABLE. With STATUS, the answer itself is the third element of data.
| Event | Data | Raised when |
|---|---|---|
| LOGCOM_REQUEST | [position, faction, side, forceMakeup, deliveryType, (playerID)] | Reinforcements or supplies have been requested, by a commander, a player, C2ISTAR, the air commander or a Start From Ship wave. deliveryType is STANDARD, AIRDROP, HELI_INSERT, HELI_PARADROP or SEA_INSERT. |
| LOGCOM_RESPONSE | [requestID, playerID, (response)] | The logistics commander has answered a request; read the message key for the outcome. STATUS and the cancel replies add the response, and REQUEST_DELIVERED carries [requestID, playerID, destination, delivered]. The air commander also answers an air cancel this way. |
| LOGCOM_STATUS_REQUEST | [faction, side, requestID, playerID] | Someone has asked for the state of an outstanding request. |
| LOGCOM_CANCEL_REQUEST | [faction, side, requestID, playerID, selectedRequestID] | A request is being cancelled. |
| LOGCOM_RESUPPLY | [position, side, assetType, callsign, source, [needsAmmo, needsFuel, needsRepair], vehicle] | A resupply run has been asked for, by a Combat Support asset or by an aircraft the air commander has brought home. |
| LOGISTICS_INSERTION | [position, faction, side, eventID, eventType] | A delivery has begun its insertion. |
| LOGISTICS_DESTINATION | [position, faction, side, eventID, eventType] | A delivery is heading for its destination. |
| LOGISTICS_COMPLETE | [destination, faction, side, eventID, eventType, (deliveredByAir)] | A delivery has finished. |
| FORCEPOOL_UPDATED | [faction, forcePool] | A faction's available force pool has changed. |
| LOGCOM_FOB_RESUPPLY | [position, side, "FOB", name, fobID] | A Player FOB is running low on supply and asks Military Logistics for a truck. |
| LOGCOM_FOB_RESUPPLY_ARRIVED | [fobID, side] | That truck reached the FOB. The FOB takes the supply only if it still belongs to that side. |
ATO_RESPONSE carries its outcome in the message key too, for example DENIED_ATO_UNAVAILABLE, or STATUS with the answer as the third element of data. It only answers requests made by players.
| Event | Data | Raised when |
|---|---|---|
| ATO_REQUEST | [type, side, faction, position, args, (requestID, playerID)] | An air mission has been requested. Type is the mission kind, for example Strike, Recce or OCA. The trailing pair is only present when a player raised the request. |
| ATO_RESPONSE | [requestID, playerID, (response)] | The air commander has answered a player's request; read the message key for the outcome. The response is only there when it has something in it. |
| ATO_STATUS_REQUEST | [faction, side, requestID, playerID] | Not raised by ALiVE. The air commander listens for it, so a mission can raise it, and answers with an ATO_RESPONSE whose message is STATUS and whose response lists [missionType, requestID, state, positions]. |
| ATO_CANCEL_REQUEST | [faction, side, requestID, playerID, selectedRequestID] | Not raised by ALiVE. The air commander listens for it, so a mission can raise it, and answers with a LOGCOM_RESPONSE whose message is CANCEL_OK or CANCEL_FAILED. |
| ATO_DESTINATION | [position, faction, side, eventID] | Not raised. Nothing in the mod fires this event any more, so a handler registered on it never runs. |
| ATO_RETURN | [position, faction, side, eventID] | Not raised. Nothing in the mod fires this event any more, so a handler registered on it never runs. |
| ATO_SEAD_ON_STATION | [sortieID, zone, positions] | A suppression (SEAD) sortie reached its area. positions are the targets it was sent against. |
| Event | Data | Raised when |
|---|---|---|
| ARTY_REQUEST | [contactID, position, contacts, side, faction, asymmetric] | A fire mission has been requested. |
| ARTY_RESUPPLY_REQUEST | [position, side, "ARTY", callsign, entityID] | A gun has run low and asked for more rounds. |
| ARTY_RESUPPLY_ARRIVED | [entityID] | The resupply reached the gun. |
These fire for virtualised profiles as well as spawned units, so they cover things happening far away from any player.
| Event | Data | Raised when |
|---|---|---|
| PROFILE_KILLED | [position, faction, side, killerSide, profile, killerProfile, victimProfileID, victimObjectType] | A profiled unit or vehicle was killed, spawned or virtual, including by artillery. killerProfile is nil when the killer had no profile, a player for example. |
| PROFILE_ATTACK_START | [attackID, attackerID, targets, position, attackerSide, maxRange, cyclesLeft] | A virtual engagement has begun. |
| PROFILE_ATTACK_END | [attackID, attackerID, targetsLeft, targetsKilled, position, attackerSide, timeStarted, maxRange, cyclesLeft] | A virtual engagement has finished. |
| AGENT_KILLED | [position, faction, side, killerSide] | A civilian agent was killed. |
| Event | Data | Raised when |
|---|---|---|
| GROUP_JOIN | [requestID, playerID, unit, group] | A player joined a group through the group manager. |
| GROUP_LEAVE | [requestID, playerID, unit] | A player left a group. |
| GROUPS_UPDATED | [playerID, []] | The group list has been rebuilt. |
Worth knowing
ALiVE_REQUIRE_INITIALISED before registering avoids the case where your listener attaches before the event log exists.opcomEventListener.sqf in the Hue Defence and Operation Cobra scenario missions. They are the same shape as the example above and are kept working alongside the code.Something here wrong or missing? Please raise it on the ALiVE issue tracker on GitHub. This list is written from the code that raises the events, so it should stay accurate, but the data an event carries can change between development builds.
Alternative
// Run on the server: OPCOM_instances only exists there.
// isHeldObjective asks whether an objective is still held and not overrun,
// from the side you name. It doesn't check who owns the objective, so ask it
// with the side of the commander the objective belongs to.
// The final false skips the stricter reserve-only test.
{
private _side = [_x, "side", ""] call ALiVE_fnc_hashGet;
private _objectives = [_x, "objectives", []] call ALiVE_fnc_hashGet;
{
if ([_x, _side, 300, false] call ALiVE_fnc_isHeldObjective) then {
// this commander's side holds this objective right now
};
} forEach _objectives;
} forEach OPCOM_instances;
Changelog