Exports & API Reference

The developer side of oxide-fire — server exports to start, read and clear fires and calls, report lightning and read settings, plus global state, state bags, events and integrations.

The developer side of oxide-fire: what other resources can read, call and listen to. This page is for developers who connect their own scripts. Server owners do not need it to install or run the resource.

oxide-fire uses o-link, but does not add anything to it. There is no olink.fire namespace. Integration happens through the exports, events and state bags below.

Server exports

All exports are server-side.

CreateIncident

Starts a fire at a spot. This is a bare fire: it raises no dispatch alert, does not ring the station bell, does not appear on the call board, has no casualties, and does not end on its own after 30 minutes. Use StartCall for a full call.

local id, reason = exports['oxide-fire']:CreateIncident(vector3(215.0, -810.0, 30.7), {
    nodes = 8,       -- burning points to start with (default 5)
    class = 'B',     -- 'A', 'B', 'vehicle' or 'brush' (default 'A')
    spread = true,   -- true or false; leave out to follow the config
    bucket = 0,      -- routing bucket (default 0)
})
if not id then
    print('fire not started: ' .. tostring(reason))
end

Parameters:

NameTypeDescription
coordsvector3, vector4 or { x, y, z }Where the fire burns, at ground level
optstable (optional)nodes, class, spread, bucket as above

Returns: number incident id, or false, reason. reason is 'invalid' (bad coordinates) or 'cap' (the fire limits are reached).

An unknown class falls back to 'A'. The number of points is capped by Config.Fire.maxNodesPerIncident and maxNodesTotal.

StartCall

Starts a fire call, exactly like the call generator does: the fire, the dispatch alert, the station bell, the page-outs, the call board entry and, for structure fires, the chance of trapped casualties.

-- A structure fire at incident site 4 (set up in /fire admin)
local id, why = exports['oxide-fire']:StartCall('structure', { siteId = 4 })

-- A brush fire somewhere inside wildfire zone 2
local id2 = exports['oxide-fire']:StartCall('brush', { zoneId = 2 })

-- A vehicle fire at a spot of your choosing
local id3 = exports['oxide-fire']:StartCall('vehicle', {
    coords = vector3(-1037.0, -2738.0, 20.2),
    heading = 240.0,
})

Parameters:

NameTypeDescription
callTypestring'vehicle', 'brush' or 'structure'
opts.siteIdnumber (optional)An incident site id. Required for 'structure', which burns at the site's fire points. For 'vehicle', the wreck spawns at the site's spot
opts.zoneIdnumber (optional)A wildfire zone id, for 'brush'. The fire starts at a random spot inside it and only spreads inside it
opts.coordsvector3 or { x, y, z } (optional)Where to start a 'vehicle' or 'brush' call with no site or zone
opts.headingnumber (optional)The wreck's heading for a 'vehicle' call at coords
opts.bucketnumber (optional)Routing bucket (default 0)

Returns: number incident id, or false, reason. reason is one of 'type' (unknown call type), 'no_site', 'no_zone', 'vehicle' (the wreck could not be spawned), 'cap' (the fire limits are reached) or 'invalid'.

Calls started this way end on their own after Config.Calls.expireMs (30 minutes) like any other call.

ClearIncident

Puts a fire out at once and closes its alert. Casualties still inside are removed.

local done = exports['oxide-fire']:ClearIncident(id)

Parameters:

NameTypeDescription
idnumberThe incident id

Returns: boolean. false when no fire has that id.

The oxide-fire:server:incidentEnded event fires with your resource's name as the reason.

GetIncident

Reads one fire.

local inc = exports['oxide-fire']:GetIncident(id)
if inc then
    print(inc.type, inc.class, #inc.nodes)
end

Returns: table or nil when no fire has that id.

FieldTypeDescription
idnumberThe incident id
bucketnumberRouting bucket
classstring'A', 'B', 'vehicle' or 'brush'
typestring'vehicle', 'brush' or 'structure' for calls
centervector3The fire's centre. For a vehicle fire, it follows the car
vehNetnumber or nilNetwork id of the burning car, for vehicle fires
nodestable[]Every burning or smouldering point: { id, x, y, z, class, hp, s, gs, o }. hp is the strength left, from 0 to 100 (percent). s is 1 while the point smoulders

GetStats

Counters for the fire system since the resource started. Useful for monitoring.

local stats = exports['oxide-fire']:GetStats()
print(('%d fires, %d points, %.3f ms/s'):format(stats.incidents, stats.nodes, stats.cpuMsPerSecond))

Returns: table with incidents, nodes, created, cleared, extinguished, spread, knockedDown, soaked, overhauled, reignited, burntOut, hits and damage (each split by hose, extinguisher and cannon), the server tick timings (ticks, tickAvgMs, tickMaxMs, flushes, flushAvgMs, flushMaxMs, cpuMsPerSecond, clock), and fireEvents (how many times the game tried to copy these fires between players, and how many of those were stopped).

LightningStrike

Tells oxide-fire that lightning struck. If the strike lands inside an enabled wildfire zone, a brush fire call starts there.

local id = exports['oxide-fire']:LightningStrike(vector3(1500.0, 4500.0, 50.0), 0)

Parameters:

NameTypeDescription
coordsvector3 or { x, y, z }Where the lightning struck
bucketnumber (optional)Routing bucket (default 0)

Returns: number incident id, or false when the strike is outside every enabled wildfire zone, or the fire could not start.

This export does not apply the Lightning fires settings (on/off, chance, following the call generator). The calling resource decides when a strike should start a fire. oxide-weather does not need this export: oxide-fire listens for its strikes directly, and those strikes do follow the settings. See Weather.

GetSetting

Reads a setting's current value.

local generator = exports['oxide-fire']:GetSetting('FireCalls')
print(generator.enabled, generator.minFirefighters)

Parameters:

NameTypeDescription
keystringA top-level config key, for example 'FireCalls', 'Scba', 'Rescue', 'FireDifficulty'

Returns: the whole value (table or plain value). For settings that live in the database, this is the database value, not the file default. It also reads file-owned settings such as 'Fire' or 'Hose'.

SetSetting

Writes a setting that lives in the database. It saves, mirrors the value to every player and applies straight away.

local ok, err = exports['oxide-fire']:SetSetting('FireDifficulty', 'realistic')

Parameters:

NameTypeDescription
keystringA key that /fire settings edits (see Configuration)
valueanyThe whole new value. For a group such as 'Scba', pass the full table

Returns: true, or false, reason. reason is 'not an editable setting' (a file-owned or unknown key), 'settings are still loading, try again shortly', 'contains a number that is not finite' or 'could not be saved, see the server console'.

SetSetting does not check the value's type or range. Prefer /fire settings for anything a person is doing. This export is for scripted setup.

Global state

Written by the server, readable from anywhere.

KeyValueNotes
oxide:fire:ondutynumberTotal on-duty firefighters across every department
oxide-fire:configstring[]The setting keys mirrored to players
oxide-fire:config:<key>variesOne setting's current value, mirrored per key
oxide-fire:apparatusModelstableHose outlet and intake positions admins set per model in the Models screen

Player state bags

Replicated, so any client can read them for any player.

oxide:fire:duty

Player(src).state['oxide:fire:duty']
-- { deptId, deptName, gradeRank, stationId }   -- on duty
-- nil                                           -- off duty

The duty state. Present means on duty. This is the right thing for a HUD or another script to read. Do not keep your own copy: duty can be cleared by the server (a dismissal, a department deletion, a logout) without the player's game asking for it.

oxide:fire:scba

Player(src).state['oxide:fire:scba']
-- { on = true|false, air = seconds left, max = seconds in a full cylinder, stage = 'ok'|'warn'|'alarm'|'empty' }

Set once a player has used their SCBA. air is the value when the bag was last written. While on is true, the air keeps running down between updates. The bag only changes when the mask goes on or off, the stage changes, or the cylinder is refilled.

oxide:fire:hose

Player(src).state['oxide:fire:hose']
-- { id = line source, on = spraying, pat = pattern number }   -- holding a line
-- nil                                                          -- not holding one

pat is the position of the pattern in Config.Hose.patterns (1 Jet, 2 Fog, 3 Foam by default).

oxide:carryingPlayer and oxide:carriedBy

Set during a rescue carry or drag: oxide:carryingPlayer on the carrier (holding the casualty's server id) and oxide:carriedBy on the casualty (holding the carrier's server id). These are the same bags oxide-police uses for its carry, so neither resource picks up a player the other is already carrying.

Entity state bags

Fleet apparatus

Written by the server on every fleet unit. Clients never write these.

KeyValueNotes
oxide:fire:apparatusnumberThe unit's database id. This is the unit's identity. Never trust a bare network id to identify a unit across sessions
oxide:fire:fleetStatetablePlate, saved properties, livery, extras, health and class. Applied once by the first player's game that owns the vehicle
oxide:fire:fleetDecoratedtrueSet once that look has been applied
oxide:fire:bodyHealth / oxide:fire:engineHealthnumberDamage reported by the driver and written by the server
oxide:platestringThe plate the server issued. A convention shared with other Oxide resources

Water

KeyValueNotes
oxide:fire:tank{ level, cap, foam, foamCap }Litres of water and foam, and the tank sizes, on any vehicle used as a water source. Updated in whole-percent steps
oxide:fire:supply{ x, y, z } or nilThe hydrant a vehicle's supply line is connected to

Casualties

KeyValueNotes
oxide:fire:victim{ id, state, pos, heading, carrier }On each trapped person. state is 'trapped', 'carried', 'rescued' or 'lost'

Events

Server-side, safe to listen to

AddEventHandler('oxide:fire:server:initialized', function() end)
AddEventHandler('oxide:fire:server:settingsReady', function() end)
AddEventHandler('oxide:fire:server:settingsApplied', function(keys) end)
AddEventHandler('oxide:fire:server:departmentsChanged', function(reason, data) end)
AddEventHandler('oxide:fire:firefighterJobMirror', function(action, charId, jobName, gradeRank, jobLabel) end)
AddEventHandler('oxide-fire:server:incidentEnded', function(id, reason, summary) end)
EventFires when
oxide:fire:server:initializedThe departments, firefighters, fleet and bays have loaded and the resource is ready
oxide:fire:server:settingsReadySettings have been rebuilt from the database. Anything that reads a setting at start-up should wait for this
oxide:fire:server:settingsAppliedSettings were saved. keys lists the setting keys that changed
oxide:fire:server:departmentsChangedA department, station, grade or bay was created, changed or removed. reason is a short description ('department created', 'station updated', 'bay removed', ...). data carries the ids involved
oxide:fire:firefighterJobMirrorA firefighter was hired, changed grade or was dismissed. action is 'hired', 'gradeChanged' or 'fired'. Only fires with Multi-department enrollment on. This is what the g-multijob bridge listens to
oxide-fire:server:incidentEndedA fire ended. reason is 'extinguished', 'burnt out', 'expired', 'vehicle gone', 'admin', or the name of the resource that called ClearIncident. summary is { id, bucket, class, type, center, count }

Server-side, listened to

EventSent byWhat oxide-fire does
oxide:weather:lightningStrike (data)oxide-weather, for every lightning strikeReads data.coords ({ x, y, z }) and data.id. A strike inside an enabled wildfire zone may start a brush fire call, following the Lightning fires settings

Client-side, safe to listen to

RegisterNetEvent('oxide:fire:departmentsUpdated', function() end)
RegisterNetEvent('oxide:fire:firefighterDataChanged', function() end)
RegisterNetEvent('oxide:fire:forceClockOut', function() end)
AddEventHandler('oxide-fire:client:configUpdated', function(key) end)
AddEventHandler('oxide-fire:client:scbaMask', function(on) end)
EventFires when
oxide:fire:departmentsUpdatedDepartment data changed. Clients rebuild their station points, bays and blips
oxide:fire:firefighterDataChangedThis player's employment changed (hired, dismissed, promoted, badge changed)
oxide:fire:forceClockOutThe player went off duty for any reason: their own clock-out, a dismissal or a department deletion
oxide-fire:client:configUpdatedA setting changed. key is the setting that changed, or nil on the first full load. Local to the player's game
oxide-fire:client:scbaMaskSee below

oxide-fire:client:scbaMask

Fires on the wearer's own game, after the face piece goes on (on = true) or comes off (on = false). Clothing scripts can use it to swap in a full SCBA outfit:

-- In your clothing resource, client side
AddEventHandler('oxide-fire:client:scbaMask', function(on)
    if on then
        -- put on your SCBA variant of the turnout gear
    else
        -- put the normal turnout gear back
    end
end)

It is a local event (TriggerEvent), so listen with AddEventHandler, not RegisterNetEvent.

Internal events: do not trigger these

These carry the resource's own state between its server and client sides. They check what they receive, so triggering them from outside will not break anything, but they are not an API and can change:

every oxide-fire:client:*, oxide-fire:server:* and oxide-fire:hose:* net event, the oxide:fire:server:* net events, oxide:fire:openAdminPanel and oxide:fire:client:openSettings.

Integrations

Dispatch

oxide-fire raises alerts through olink.dispatch.CreateAlert on the server, and closes them with olink.dispatch.CloseAlert when the fire ends.

  • With oxide-dispatch, or with no dispatch resource (o-link's built-in fallback), the server-side alert works directly.
  • With another dispatch resource, CreateAlert returns nothing, so the alert is sent through olink.dispatch.SendAlert on the game of one on-duty firefighter. If no firefighter is on duty, the alert is skipped.
AlertCodeSent to
Vehicle fire10-73Every enabled fire department's job name
Brush fire10-74Every enabled fire department's job name
Structure fire10-70Every enabled fire department's job name
Fire rescue casualty10-52The Medic jobs setting (ambulance, ems by default)

With no fire department set up, fire alerts go to the job fire.

Weather

oxide-fire reads the wind and rain at each fire through olink.weather.GetWindAt and olink.weather.GetConditionsAt. Today only oxide-weather answers those. Without it, each fire gets a random wind and no rain.

Lightning comes from the oxide:weather:lightningStrike server event above. Another weather resource can send strikes with the LightningStrike export instead.

Smoke

Smoke is internal to oxide-fire. There is no export to add smoke zones from another resource.

Phones and the tablet

Page-outs use olink.phone.SendNotification when olink.phone.IsAvailable() says a phone resource runs. The boss desktop registers a tablet app (fire_boss, "Fire Command") through olink.tablet when oxide-tablet is running.

Integration examples

Start a structure fire from a scenario script

-- server side, in your own resource
local function startArson(siteId)
    local id, why = exports['oxide-fire']:StartCall('structure', { siteId = siteId })
    if not id then
        print(('[arson] no fire: %s'):format(why))
        return nil
    end
    return id   -- keep it if you want to ClearIncident(id) later
end

Site ids are shown on the Incident Sites screen of /fire admin.

Pay a bonus when a fire is put out

-- server side
AddEventHandler('oxide-fire:server:incidentEnded', function(id, reason, summary)
    if reason ~= 'extinguished' then return end
    print(('Fire %d (%s) put out at %s'):format(id, summary.type, tostring(summary.center)))
end)

Show the fire department's on-duty count on a HUD

-- client side
CreateThread(function()
    while true do
        local onDuty = GlobalState['oxide:fire:onduty'] or 0
        -- draw onDuty on your HUD
        Wait(5000)
    end
end)