Exports & API

legends_firefighter exposes client and server exports for integrations (admin panels, anti-cheat kicks, custom UI, analytics) plus editable hooks in client/editable.lua and server/editable.lua for deeper customization.

Client Exports

IsOnJob

Returns whether the local player is currently on a firefighter mission.

local onJob = exports.legends_firefighter:IsOnJob()

Returns: boolean

Example:

-- Block a different job from starting while on the firefighter run
RegisterCommand('start_mechanic', function()
    if exports.legends_firefighter:IsOnJob() then
        lib.notify({ description = 'Finish your firefighter shift first!', type = 'error' })
        return
    end
    -- ...
end)

GetJobState

Returns the current job state of the local player.

local state = exports.legends_firefighter:GetJobState()

Returns: string — one of:

  • 'idle' — not on a job
  • 'en_route' — traveling to a fire location
  • 'extinguishing' — at a fire location, putting fires out
  • 'searching' — searching the scene for salvage
  • 'returning' — returning the vehicle to the depot

CancelJob

Cancels the current firefighter mission (if any). Triggers the same cleanup as the player canceling from the in-game menu.

local ok = exports.legends_firefighter:CancelJob()

Returns: booleantrue if a job was canceled, false if the player wasn't on one.

Example:

-- Cancel on arrest
AddEventHandler('police:onArrested', function()
    exports.legends_firefighter:CancelJob()
end)

GetActiveFireCount

Returns the number of fires currently active at the player's current location.

local remaining = exports.legends_firefighter:GetActiveFireCount()

Returns: number

Use this for custom HUDs or when scripting branching objectives ("if more than 3 fires remain, trigger backup").


Server Exports

GetActiveJobCount

Returns how many firefighter jobs are currently running across the server.

local count = exports.legends_firefighter:GetActiveJobCount()

Returns: number

Example:

-- Simple load cap: block new firefighter jobs if the server already has 10 running
AddEventHandler('firefighter:onJobRequest', function(src)
    if exports.legends_firefighter:GetActiveJobCount() >= 10 then
        CancelEvent()
    end
end)

GetActiveJobs

Returns the full active-jobs table, keyed by player source.

local jobs = exports.legends_firefighter:GetActiveJobs()
-- jobs[source] = { ...internal job data... }

Returns: table<number, table>

Useful for admin panels or /who is firefighting commands.


ForceEndJob

Force-ends a specific player's active job. Cleans up the vehicle entity and notifies the client.

local ended = exports.legends_firefighter:ForceEndJob(source)

Returns: booleantrue if a job was ended, false if that player wasn't on one.

Example:

-- Admin command
RegisterCommand('endfirefighter', function(src, args)
    local target = tonumber(args[1])
    if target and exports.legends_firefighter:ForceEndJob(target) then
        TriggerClientEvent('chat:addMessage', src, { args = { 'ADMIN', 'Firefighter job ended.' } })
    end
end, true)

Editable Client Hooks

In client/editable.lua:

OnJobStart

function OnJobStart(routeId, routeData)
    -- routeId: numeric route ID (e.g., 201)
    -- routeData: full route table from config/routes.lua
end

Called client-side the moment a job starts — after payment for the rental (if any) and after the vehicle has spawned.

Example — play a radio sound:

function OnJobStart(routeId, routeData)
    PlaySoundFrontend(-1, 'Menu_Accept', 'Phone_SoundSet_Default', true)
    lib.notify({ description = ('Dispatched to: %s'):format(routeData.name), type = 'inform' })
end

OnFireExtinguished

function OnFireExtinguished(locationIndex, fireData)
    -- locationIndex: index into the current route's locations array
    -- fireData: { coords, fireType, fireCount, fireSize, ... }
end

Called every time a fire is fully put out.

Example — award XP in an external skill system:

function OnFireExtinguished(locationIndex, fireData)
    TriggerServerEvent('myskills:addXp', 'firefighting', 10)
end

OnJobComplete

function OnJobComplete(completed, locationsCompleted, reward)
    -- completed: true if the full route was finished, false if canceled early
    -- locationsCompleted: how many locations were cleared
    -- reward: total money paid out
end

Called when the job ends for any reason (natural finish, early return, cancel).


Editable Server Hooks

In server/editable.lua:

CanStartJob

function CanStartJob(source)
    -- return false to block
    return true
end

Gate job entry on inventory checks, job whitelist, cooldowns, metadata — anything server-side.

Example — require a firefighter badge item:

function CanStartJob(source)
    local count = exports.ox_inventory:Search(source, 'count', 'firefighter_badge')
    if count < 1 then
        Framework.notify(source, 'You need a firefighter badge to start this job!', 'error')
        return false
    end
    return true
end

CalculateBonus

function CalculateBonus(source, locationsCompleted, totalLocations, defaultBonus)
    return defaultBonus
end

Modify the completion bonus per player/per event.

Example — weekend double-bonus:

function CalculateBonus(source, locationsCompleted, totalLocations, defaultBonus)
    local day = os.date('%A')
    if day == 'Saturday' or day == 'Sunday' then
        return defaultBonus * 2
    end
    return defaultBonus
end

OnRewardGiven

function OnRewardGiven(source, totalReward, locationsCompleted, totalLocations, lootCollected)
    -- Post-payment hook
end

Fires after the player has been paid. Use for:

  • Discord webhook logs
  • Metadata updates (fires_extinguished, firefighter_shifts)
  • Reputation or prestige systems
  • Long-term database logging

Example — bump a metadata counter:

function OnRewardGiven(source, totalReward, locationsCompleted, totalLocations, lootCollected)
    local player = Framework.getPlayer(source)
    if player and player.Functions and player.Functions.SetMetaData then
        local current = player.PlayerData.metadata.fires_extinguished or 0
        player.Functions.SetMetaData('fires_extinguished', current + locationsCompleted)
    end
end

Custom System Implementations

Both client/editable.lua and server/editable.lua include commented-out templates for swapping in your own notification / progress bar / menu / textui / inventory / money / callback backends. To enable a custom backend:

  1. Uncomment the relevant function (CustomNotify, CustomInventory, etc.)
  2. Set the matching ClientSystems or ServerSystems entry to 'custom'

Example:

-- server/editable.lua
 
ServerSystems = {
    inventory = { system = 'custom' },  -- swap from 'auto'
    money     = { system = 'auto' },
    callbacks = { system = 'auto' },
}
 
CustomInventory = {
    addItem = function(source, item, amount, metadata)
        return exports.my_inventory:AddItem(source, item, amount, metadata) == true
    end,
    removeItem = function(source, item, amount)
        return exports.my_inventory:RemoveItem(source, item, amount) == true
    end,
    getItemCount = function(source, item)
        return exports.my_inventory:GetItemCount(source, item) or 0
    end,
    hasItem = function(source, item, amount)
        return (exports.my_inventory:GetItemCount(source, item) or 0) >= (amount or 1)
    end,
}

Localization

Language strings live in locales/en.json. The file is grouped by feature area:

{
    "menu":       { "title": "Fire Department", "rent_truck": "Rent Firetruck", ... },
    "npc":        { "talk_to": "Talk to Firefighter" },
    "info":       { "job_started": "...", "fire_extinguished": "...", ... },
    "error":      { "equip_extinguisher": "...", "vehicle_destroyed": "...", ... },
    "progress":   { "extinguishing": "Extinguishing Fire...", "searching": "Searching Area..." },
    "loot":       { "found": "Salvage Found", "nothing_found": "Nothing of value found." },
    "rewards":    { "received": "...", "bonus": "..." },
    "depot":      { "return_truck": "...", "finish_mission": "..." },
    "leveling":   { "xp_gained": "+%s XP", "level_up": "..." },
    "waterbomber":{ "tank_empty": "...", "tank_full": "...", ... },
    "helicopter": { "spraying": "...", "too_low": "...", ... }
}

Adding a New Language

  1. Copy locales/en.json to locales/<lang>.json (e.g. locales/es.json)
  2. Translate the values, keep the keys
  3. ox_lib picks the correct file automatically based on your server/player locale (the resource declares ox_lib 'locale' in fxmanifest.lua)

If you're not using ox_lib, the resource still loads en.json as the default. You can swap the default file path in bridge/shared/init.lua if needed.

Need Help?

Join our Discord for support: discord.gg/lgnds (opens in a new tab)


Legends Store - Premium FiveM Scripts