Kartik Scripts

Installation

Requirements

  • ox_lib
  • ox_target or qb-target (optional: without one, interactions use on-screen prompts)
  • oxmysql (staff, shifts, reports, inspections, certificates and finances; without it those features switch off)
  • A framework: qbx_core, qb-core or es_extended
  • ox_inventory (qb-inventory, qs-inventory, codem-inventory, tgiann-inventory and origen_inventory also work)
  • Optional: screenshot-basic (photos, see Photo uploads), kartik-mdt (MDT V2), kartik-ambulancejob, kartik-evidence, kartik-ers, pma-voice, illenium-appearance, qbx_management, a vehicle keys resource and a banking resource (see Works with)

Steps

  1. Download kartik-firefighterjob from your Cfx portal.
  2. Put it in your resources folder.
  3. Start it after your framework, inventory and MDT in server.cfg:
ensure ox_lib
ensure oxmysql
ensure ox_target
ensure qbx_core
ensure ox_inventory
ensure kartik-mdt
ensure kartik-firefighterjob
  1. Add the job, the items and the server.cfg lines below.
  2. Restart the server once so the job and items load. The database tables are created by themselves.
  3. Join as an admin and follow First Setup.

The fire job

On Qbox, paste into qbx_core/shared/jobs.lua, inside the jobs table (on QBCore, qb-core/shared/jobs.lua takes the same block). On ESX, use the SQL below. Grade 4 (Chief) and above can use station setup, incident commands and the boss pages of the tablet (Config.BossGrade).

['fire'] = {
    label = 'Fire Department',
    type = 'fire',
    defaultDuty = false,
    offDutyPay = false,
    grades = {
        [0] = { name = 'Probationary Firefighter', payment = 50 },
        [1] = { name = 'Firefighter', payment = 75 },
        [2] = { name = 'Engineer', payment = 100 },
        [3] = { name = 'Lieutenant', payment = 125 },
        [4] = { name = 'Chief', isboss = true, bankAuth = true, payment = 150 },
    },
},

Already have a fire job under another name? Add its name to Config.Jobs in shared/config.lua (default { 'fire', 'safd' }).

Items

ImageItemWhat it is
hydraulic_spreadershydraulic_spreadersHydraulic Spreaders: Forces vehicle doors open
rescue_sawrescue_sawRescue Saw: Cuts through door hinges
hydraulic_cutterhydraulic_cutterHydraulic Cutter: Cuts door hinges and roof pillars
rescue_ramrescue_ramRescue Ram: Rolls the dashboard off trapped legs
lifting_airbagslifting_airbagsLifting Airbags: Lifts a vehicle or load off a casualty
hose_packhose_packWildland Hose Pack: 30 m of hose to extend an attack line across rough ground
portable_reservoirportable_reservoirPortable Reservoir: Fold-a-tank for tanker shuttles and drafting
halliganhalliganHalligan Bar: Smashes windows and forces locks
stabiliserstabiliserVehicle Stabiliser: Stops a crashed vehicle from moving
ppv_fanppv_fanVentilation Fan: Clears smoke from a fire scene
portable_pumpportable_pumpPortable Pump: Draws water from rivers and lakes
drip_torchdrip_torchDrip Torch: Lays controlled burn lines
fire_rakefire_rakeFire Rake: Clears firebreaks a wildfire cannot cross
fire_conefire_coneFire Dept. Cone: Closes off lanes around a scene
fire_barrierfire_barrierFire Dept. Barrier: Blocks a road
portable_ladderportable_ladderPortable Ladder: Reach roofs and ledges
air_cushionair_cushionRescue Air Cushion: Catches someone falling from height
fire_diving_gearfire_diving_gearRescue Diving Gear: Scuba set and dive torch for water rescues
fire_axefire_axeFire Axe: Forces doors and breaks windows
pike_polepike_polePike Pole: Pulls ceilings to find hidden fire and vent smoke
fire_chainsawfire_chainsawRescue Chainsaw: Cuts ventilation holes and forces doors
thermal_camerathermal_cameraThermal Imaging Camera: See heat and people through smoke
gas_detectorgas_detector4-Gas Detector: Reads oxygen, flammable gas, CO and H2S
scba_kitscba_kitSCBA Mask Kit: Harness, air cylinder and facepiece. Use it to put it on or take it off
scba_cylinderscba_cylinderSCBA Cylinder: Full spare air cylinder
burn_dressingburn_dressingBurns Dressing: Sterile dressing for burns
spill_kitspill_kitHazmat Spill Kit: Absorbent booms and pads to contain a chemical spill
fire_tabletfire_tabletFire Department Tablet: Calls, scene map, teams and apparatus
ground_monitorGround Monitor: Placed nozzle for heavy, unmanned water flow
oil_absorbentOil Absorbent: Granules that soak up oil on the road
push_broomPush Broom: Sweeps up soaked absorbent
wildland_maskWildland Mask: Cuts smoke on grass fires. No air supply
glass_hammerGlass Hammer: Breaks car windows cleanly before extrication
extinguisher_refillExtinguisher Refill: Refills an empty wall extinguisher
extinguisher_mountExtinguisher Mount: Hangs a new extinguisher on a wall or floor

The item images are in images/inventory/ of the resource. Copy every PNG to ox_inventory/web/images/.

The fire tools use their own names (fire_cone, fire_barrier, fire_diving_gear) so they don't clash with a police job's cones or qbx_divegear.

Paste into ox_inventory/data/items.lua, inside the returned table:

['hydraulic_spreaders'] = { label = 'Hydraulic Spreaders', weight = 9000, stack = false, close = true, description = 'Forces vehicle doors open', client = { export = 'kartik-firefighterjob.useItem' } },
['rescue_saw']          = { label = 'Rescue Saw', weight = 7000, stack = false, close = true, description = 'Cuts through door hinges', client = { export = 'kartik-firefighterjob.useItem' } },
['hydraulic_cutter']    = { label = 'Hydraulic Cutter', weight = 9000, stack = false, close = true, description = 'Cuts door hinges and roof pillars', client = { export = 'kartik-firefighterjob.useItem' } },
['rescue_ram']          = { label = 'Rescue Ram', weight = 8000, stack = false, close = true, description = 'Rolls the dashboard off trapped legs', client = { export = 'kartik-firefighterjob.useItem' } },
['lifting_airbags']     = { label = 'Lifting Airbags', weight = 12000, stack = false, close = true, description = 'Lifts a vehicle or load off a casualty', client = { export = 'kartik-firefighterjob.useItem' } },
['hose_pack']           = { label = 'Wildland Hose Pack', weight = 6000, stack = true, close = true, description = '30 m of hose to extend an attack line across rough ground', client = { export = 'kartik-firefighterjob.useItem' } },
['portable_reservoir']  = { label = 'Portable Reservoir', weight = 20000, stack = false, close = true, description = 'Fold-a-tank for tanker shuttles and drafting', client = { export = 'kartik-firefighterjob.useItem' } },
['halligan']            = { label = 'Halligan Bar', weight = 4000, stack = false, close = true, description = 'Smashes windows and forces locks', client = { export = 'kartik-firefighterjob.useItem' } },
['stabiliser']          = { label = 'Vehicle Stabiliser', weight = 3000, stack = true, close = true, description = 'Stops a crashed vehicle from moving', client = { export = 'kartik-firefighterjob.useItem' } },
['ppv_fan']             = { label = 'Ventilation Fan', weight = 12000, stack = false, close = true, description = 'Clears smoke from a fire scene', client = { export = 'kartik-firefighterjob.useItem' } },
['portable_pump']       = { label = 'Portable Pump', weight = 15000, stack = false, close = true, description = 'Draws water from rivers and lakes', client = { export = 'kartik-firefighterjob.useItem' } },
['drip_torch']          = { label = 'Drip Torch', weight = 3000, stack = false, close = true, description = 'Lays controlled burn lines', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_rake']           = { label = 'Fire Rake', weight = 2500, stack = false, close = true, description = 'Clears firebreaks a wildfire cannot cross', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_cone']           = { label = 'Fire Dept. Cone', weight = 1500, stack = true, close = true, description = 'Closes off lanes around a scene', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_barrier']        = { label = 'Fire Dept. Barrier', weight = 4000, stack = true, close = true, description = 'Blocks a road', client = { export = 'kartik-firefighterjob.useItem' } },
['portable_ladder']     = { label = 'Portable Ladder', weight = 12000, stack = false, close = true, description = 'Reach roofs and ledges', client = { export = 'kartik-firefighterjob.useItem' } },
['air_cushion']         = { label = 'Rescue Air Cushion', weight = 15000, stack = false, close = true, description = 'Catches someone falling from height', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_diving_gear']    = { label = 'Rescue Diving Gear', weight = 9000, stack = false, close = true, description = 'Scuba set and dive torch for water rescues', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_axe']            = { label = 'Fire Axe', weight = 3500, stack = false, close = true, description = 'Forces doors and breaks windows', client = { export = 'kartik-firefighterjob.useItem' } },
['pike_pole']           = { label = 'Pike Pole', weight = 3000, stack = false, close = true, description = 'Pulls ceilings to find hidden fire and vent smoke', client = { export = 'kartik-firefighterjob.useItem' } },
['fire_chainsaw']       = { label = 'Rescue Chainsaw', weight = 8000, stack = false, close = true, description = 'Cuts ventilation holes and forces doors', client = { export = 'kartik-firefighterjob.useItem' } },
['thermal_camera']      = { label = 'Thermal Imaging Camera', weight = 1500, stack = false, close = true, description = 'See heat and people through smoke', client = { export = 'kartik-firefighterjob.useItem' } },
['gas_detector']        = { label = '4-Gas Detector', weight = 400, stack = false, close = true, description = 'Reads oxygen, flammable gas, CO and H2S', client = { export = 'kartik-firefighterjob.useItem' } },
['scba_kit']            = { label = 'SCBA Mask Kit', weight = 12000, stack = false, close = true, description = 'Harness, air cylinder and facepiece. Use it to put it on or take it off', client = { export = 'kartik-firefighterjob.useItem' } },
['scba_cylinder']       = { label = 'SCBA Cylinder', weight = 6000, stack = true, close = true, description = 'Full spare air cylinder', client = { export = 'kartik-firefighterjob.useItem' } },
['burn_dressing']       = { label = 'Burns Dressing', weight = 200, stack = true, close = true, description = 'Sterile dressing for burns' },
['spill_kit']           = { label = 'Hazmat Spill Kit', weight = 7000, stack = true, close = true, description = 'Absorbent booms and pads to contain a chemical spill' },
['fire_tablet']        = { label = 'Fire Department Tablet', weight = 600, stack = false, close = true, description = 'Calls, scene map, teams and apparatus', client = { export = 'kartik-firefighterjob.useTablet' } },
['ground_monitor']   = { label = 'Ground Monitor', weight = 18000, stack = false, close = true, description = 'Placed nozzle for heavy, unmanned water flow', client = { export = 'kartik-firefighterjob.useItem' } },
['oil_absorbent']    = { label = 'Oil Absorbent', weight = 5000, stack = true, close = true, description = 'Granules that soak up oil on the road' },
['push_broom']       = { label = 'Push Broom', weight = 1500, stack = false, close = true, description = 'Sweeps up soaked absorbent' },
['wildland_mask']    = { label = 'Wildland Mask', weight = 200, stack = false, close = true, description = 'Cuts smoke on grass fires. No air supply', client = { export = 'kartik-firefighterjob.useItem' } },
['glass_hammer']     = { label = 'Glass Hammer', weight = 500, stack = false, close = true, description = 'Breaks car windows cleanly before extrication', client = { export = 'kartik-firefighterjob.useItem' } },
['extinguisher_refill']  = { label = 'Extinguisher Refill', weight = 3000, stack = true, close = true, description = 'Refills an empty wall extinguisher' },
['extinguisher_mount']  = { label = 'Extinguisher Mount', weight = 4000, stack = true, close = true, description = 'Hangs a new extinguisher on a wall or floor', client = { export = 'kartik-firefighterjob.useItem' } },

Every item above must exist in your inventory. The truck compartment only hands out items the inventory knows, so a missing entry means that tool silently doesn't come out of the truck.

Lighter (world fires): anyone with a lighter can set cars and bins alight through the target eye. To light dry grass from the inventory, give your existing lighter item the export on ox_inventory: client = { export = 'kartik-firefighterjob.useLighter' }. Other inventories: set Config.WorldFires.arson.lighter.registerUsable = true if no other resource uses the lighter.

Firefighters don't need to carry these all the time: every fire truck has an equipment compartment to take them from and put them back.

server.cfg lines

# The hose uses the fire extinguisher for the nozzle animation and the halligan uses the crowbar.
# ox_inventory must not strip them (already have this convar? add both names to your list):
setr inventory:ignoreweapons ["WEAPON_FIREEXTINGUISHER", "WEAPON_CROWBAR"]

# Admin access to /fireadmin
add_ace group.admin kfj.admin allow

# Optional: token for photo uploads (Config.Media.upload)
set kfj_media_token "your-token"

ox_inventory wears the fire extinguisher out quickly (durability = 0.006 per use by default). For a firefighter that empties it in seconds, so lower it in ox_inventory/data/weapons.lua:

['WEAPON_FIREEXTINGUISHER'] = {
    label = 'Fire Extinguisher',
    weight = 8616,
    durability = 0.0015, -- lasts about 4 times as long
},

Firefighters can also refill it at any fire truck (target the truck → Refill extinguisher).

The ace name is Config.AdminAce in shared/config.lua. Players with the ace can always use the admin and Chief commands.

Fires can throw real game explosions (car cook-off, gas cans, propane, the gas station call). If your anti-cheat blocks script explosions, whitelist explosion types 7, 9, 23, 28 and 34, or turn the hazards off in Config.FireHazards.

Database

Everything is created on first start. oxmysql is required: everything the job saves goes into these tables.

TableWhat it holds
kfj_shiftsClocked shifts (hours this week, shift history)
kfj_reportsIncident reports, draft and filed
kfj_report_extraCall timeline and fire investigation for each report
kfj_inspectionsFire inspections and their result
kfj_certs, kfj_cert_theoryCertificates and passed theory tests
kfj_transactionsDepartment account movements
kfj_auditHires, promotions, dismissals, certificates, money and deleted reports
kfj_storeThe setup: station hardware placed in /fireadmin (lights, speakers, TVs, lockers, garage), turnout outfits, station readiness (truck and equipment condition) and scenes. One row per station, gender or scene

Player XP and stats are kept in the resource KVP.

Language

Set Config.Locale in shared/config.lua: en, es, fr, de, ar or zh. The interface uses the same file. Locale files are in locales/ (config text in locales/config/). Copy en.json to your language code and translate the values.

ESX job

INSERT INTO jobs (name, label) VALUES ('fire', 'Fire Department');
INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES
  ('fire', 0, 'recruit', 'Probationary Firefighter', 50, '{}', '{}'),
  ('fire', 1, 'firefighter', 'Firefighter', 75, '{}', '{}'),
  ('fire', 2, 'engineer', 'Engineer', 100, '{}', '{}'),
  ('fire', 3, 'lieutenant', 'Lieutenant', 125, '{}', '{}'),
  ('fire', 4, 'boss', 'Chief', 150, '{}', '{}');

Changing the interface

The interface is React + Tailwind in source/ and ships built in web/. To change it:

cd source && npm install && npm run build

npm run dev previews every screen in a browser with fake data (?view=hud, ?view=tablet&page=calls, ?view=panel, ?view=negotiation, ?view=debrief). source/node_modules is only needed for building, you can delete it.

On this page