BC Designs FiveM Resource

Advanced Turnout Gear Documentation

Station-based turnout gear, EUP outfit management, duty and rank access, vehicle gear lockers, cleanliness tracking, and SQL-backed gear wear for FiveM fire roleplay servers.

Overview

Advanced Turnout Gear is a FiveM turnout gear resource built around stations, turnout sets, rank access, duty checks, personal gear variations, fire/smoke contamination, and gear wear tracking.

Core Systems

  • Station lockers
  • Turnout Manager
  • Station Manager
  • Outfit Configurator
  • Vehicle gear lockers
  • Personal gear variations

Optional Realism

  • Cleanliness tracking
  • SmartFires or z_fire detection
  • SQL-backed wear state
  • Wash required state
  • Replacement/service required state

Requirements

Required

Optional

Start order matters: ATG loads @ox_lib/init.lua as a shared script and @oxmysql/lib/MySQL.lua as a server script. Start both dependencies before ATG.

Installation

  1. Place the resource folder in your server resources directory. Recommended folder name: advanced_turnout_gear.
  2. Import the SQL file if using gear wear. See SQL Setup.
  3. Review config.lua and rank_config.lua.
  4. Add the resource and dependencies to server.cfg.
  5. Grant admin ACE permission.
  6. Restart the server.

server.cfg

ensure ox_lib
ensure oxmysql
ensure advanced_turnout_gear

Admin Permission

add_ace group.admin atg.stationmanager allow

The default ACE permission is controlled by Config.AdminAce = "atg.stationmanager".

Framework users: Start your framework before ATG if you use Config.Framework.type = "auto", qbcore, qbx, or esx.

SQL Setup

Gear wear is stored in SQL when Config.WearAndTear.enabled = true and Config.WearAndTear.useSql = true.

Included SQL file:

sql/atg_gear_wear.sql

Default Table Name

Config.WearAndTear.tableName = "atg_gear_wear"

Table Schema

CREATE TABLE IF NOT EXISTS `atg_gear_wear` (
    `owner_identifier` VARCHAR(120) NOT NULL,
    `outfit_id` VARCHAR(80) NOT NULL,
    `durability` INT NOT NULL DEFAULT 100,
    `uses` INT NOT NULL DEFAULT 0,
    `minutes_worn` INT NOT NULL DEFAULT 0,
    `used_seconds` BIGINT NOT NULL DEFAULT 0,
    `seconds_since_wash` BIGINT NOT NULL DEFAULT 0,
    `wash_required` TINYINT(1) NOT NULL DEFAULT 0,
    `state_version` TINYINT NOT NULL DEFAULT 1,
    `last_used` BIGINT NULL,
    `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`owner_identifier`, `outfit_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

On startup, server_wear.lua also attempts to create the table and add missing columns if needed.

Configuration

Main config files:

config.lua
rank_config.lua
station_data/stations.json
outfit_data/outfits.json
outfit_data/player_outfits.json
rank_data/ranks.json

General Settings

SettingDefaultPurpose
Config.InteractKey38E key interaction.
Config.DrawDistance25.0Distance for station marker drawing.
Config.InteractDistance2.0Distance needed to interact with station lockers.
Config.UseBlipsfalseGlobal station blip toggle.
Config.UseMarkerstrueGlobal marker toggle.
Config.UI.footerLabelCHANGE MEFooter text shown in the locker UI.

Data File Settings

Config.StationDataFile = "station_data/stations.json"
Config.OutfitDataFile = "outfit_data/outfits.json"
Config.RankSystem.dataFile = "rank_data/ranks.json"

Vehicle Gear Access Defaults

SettingDefaultPurpose
Config.VehicleGearAccess.enabledtrueEnables mobile vehicle lockers.
requireStoppedtrueVehicle must be stopped.
maxSpeed0.5Maximum speed before access is blocked.
blockDriverSeattrueDriver seat cannot open vehicle locker.
allowedSeats0-5Passenger/rear seats allowed when seat access is enabled.
rearDrawDistance10.0Draw distance for trunk/rear locker marker.
rearInteractDistance2.0Distance needed to access rear gear compartment.
useBootBonetrueAttempts to use vehicle boot/trunk bone first.

Example Vehicle Entry From 1.0.0

vehicles = {
    ["engine113"] = {
        station = "Station 113",
        label = "Engine 113",
        seatAccess = true,
        trunkAccess = true,
        customOffset = false,
        rearOffset = vector3(0.0, -4.6, 0.35)
    },
    ["medic113"] = {
        station = "Station 113",
        label = "Medic 113",
        seatAccess = true,
        trunkAccess = true,
        customOffset = false,
        rearOffset = vector3(0.0, -3.4, 0.25)
    }
}

The station value can match either the station id or the visible stationName.

Player Gear Variations

Player variations are enabled by default and saved in outfit_data/player_outfits.json. Allowed override IDs are configured in Config.PlayerVariations.allowedComponents and allowedProps.

Allowed Components1 mask, 3 arms, 4 pants, 5 bag, 6 shoes, 7 accessories, 8 undershirt, 9 vest, 10 decals, 11 jacket
Allowed Props0 hat/helmet, 1 glasses, 2 ear, 6 watch, 7 bracelet

Framework Setup

Framework settings are in Config.Framework and duty settings are in Config.Duty.

Framework Types

ValueMeaning
standaloneATG standalone rank/duty only.
autoDetects qbx_core, qb-core, or es_extended; falls back to standalone.
qbcoreForces QBCore integration.
qbxForces QBox integration.
esxForces ESX integration.
customUses the custom hooks in config.lua.

Default Framework Config

Config.Framework = {
    type = "standalone",
    restrictAccess = false,
    allowedJobs = { "fire", "ambulance" },
    gradeOffset = 1,
    rankSource = "hybrid",
    characterIdentifierSource = "character",
    showCharacterName = true,
    showJob = true,
    showCallsign = true
}

Rank Source

ValueBehavior
frameworkUses framework grade/rank when available.
standaloneUses ATG /setrank only.
hybridUses framework rank when available, otherwise standalone rank.

Duty Settings

Config.Duty = {
    enabled = true,
    source = "standalone",
    requireOnDutyForGearAccess = true,
    esxJobMeansOnDuty = true
}

Duty source can be standalone, framework, or hybrid. Station clock-in/out points can be configured per station through the Station Manager.

Standalone Ranks

Standalone rank behavior is configured in rank_config.lua.

Config.RankSystem = {
    enabled = true,
    defaultRank = 1,
    dataFile = "rank_data/ranks.json",
    setRankCommand = "setrank"
}

Config.Ranks = {
    [1] = "Probationary",
    [2] = "Firefighter",
    [3] = "Lieutenant",
    [4] = "Captain",
    [5] = "Battalion Chief",
    [6] = "Chief"
}

Stations

Stations are stored in station_data/stations.json and can be managed in-game with /stationmanager.

Actual Station Schema

{
  "id": "station_113",
  "departmentName": "San Andreas Fire Rescue",
  "stationName": "Station 113",
  "departmentLogo": "department_placeholder.png",
  "coords": { "x": 2239.96, "y": 4932.91, "z": 41.45 },
  "markerEnabled": true,
  "washer": {
    "name": "Station 113 Gear Extractor",
    "coords": { "x": 2234.44, "y": 4942.43, "z": 41.45 }
  },
  "clock": {
    "name": "Station 113 Clock In / Out",
    "coords": { "x": 0.0, "y": 0.0, "z": 0.0 }
  }
}

The uploaded 1.0.0 package ships with Station 113 as the default sample station. The clock block is optional and is only present if configured through the manager.

Station Manager Fields

Department Logos

Station logos are loaded from station_images/. The default fallback is department_placeholder.png.

Outfits

Outfits are stored in outfit_data/outfits.json and managed in-game with /turnoutmanager.

Actual Outfit Fields

FieldPurpose
idNumeric turnout ID. Default examples use 1001 and 1002.
nameDisplay name shown in the locker UI.
imageImage path, usually under html/images/.
contaminationEnabledWhether cleanliness/fire/smoke contamination applies to this outfit.
vehicleAccessWhether this turnout can appear in vehicle lockers.
allowedRanksArray of rank numbers allowed to access the outfit.
assignedStationsArray of station IDs. Empty array means available at all stations.
male.components / female.componentsPed component drawable/texture values.
male.props / female.propsPed prop drawable/texture values.

Example Outfit From 1.0.0

{
  "id": 1001,
  "name": "Firefighter Turnout",
  "image": "images/firefighter.png",
  "contaminationEnabled": true,
  "allowedRanks": [1, 2, 3, 4, 5, 6],
  "assignedStations": [],
  "male": {
    "components": {
      "3": { "drawable": 17, "texture": 0 },
      "4": { "drawable": 120, "texture": 0 },
      "6": { "drawable": 25, "texture": 0 },
      "8": { "drawable": 15, "texture": 0 },
      "9": { "drawable": 0, "texture": 0 },
      "10": { "drawable": 0, "texture": 0 },
      "11": { "drawable": 250, "texture": 0 }
    },
    "props": {
      "0": { "drawable": 45, "texture": 0 },
      "1": { "drawable": -1, "texture": 0 },
      "2": { "drawable": -1, "texture": 0 },
      "6": { "drawable": -1, "texture": 0 },
      "7": { "drawable": -1, "texture": 0 }
    }
  }
}

Default Outfits In Package

IDNameRanksImage
1001Firefighter Turnout1, 2, 3, 4, 5, 6images/firefighter.png
1002Command Turnout3, 4, 5, 6images/command.png

Outfit Assignment Behavior

If assignedStations is empty, the outfit is treated as available at every station. If it contains station IDs, the outfit only appears at matching stations.

Gear Wear

Gear wear is enabled by default in the uploaded 1.0.0 config.

Actual Wear Settings

SettingDefaultMeaning
enabledtrueEnables the wear system.
useSqltrueSaves wear records to SQL.
tableNameatg_gear_wearSQL table name.
lifespanHours48Total service life of a turnout set.
tickSeconds60Wear check interval while gear is worn. Minimum enforced by code is 60 seconds.
washAfterHours8Time worn before wash is required.
requireWashBeforeUsetrueLocks wash-required gear until cleaned.
contaminationWearMinutesPerPercent0.5Extra wear minutes added per 1% cleanliness lost.
fireWearMinutes3Fallback fire exposure wear when contamination is disabled.
smokeWearMinutes1Fallback smoke exposure wear when contamination is disabled.
requireServiceBeforeUsetrueLocks gear when replacement/service is required.
serviceAt0Durability percentage where replacement is required.
warningAt45Durability percentage where the UI shows worn status.
replaceExpiredAtWashertrueExpired gear is exchanged for a fresh set at a washer.

Status Logic

StatusWhen It Appears
ServiceableGear is usable and not dirty enough to require washing.
Wash requiredseconds_since_wash reaches washAfterHours or the record is marked wash required.
WornDurability is at or below warningAt.
Replacement requiredDurability is at or below serviceAt.

Washer Behavior

Using a configured station washer cleans all configured outfits and services their wear state. If replaceExpiredAtWasher is true, expired gear is reset to a fresh set when processed at the washer.

SmartFires / z_fire

Cleanliness and exposure detection is handled in client_cleanliness.lua.

Actual Cleanliness Config

SettingDefaultMeaning
Config.Cleanliness.enabledtrueEnables cleanliness tracking.
smartfirestrueEnables SmartFires export checks.
z_firefalseEnables z_fire export checks.
smartFiresResourceSmartFiresResource name for SmartFires.
zFireResourcez_fireResource name for z_fire.
fireLoss5Cleanliness lost per check when fire is nearby.
smokeLoss2Cleanliness lost per check when smoke is nearby.
requireCleaningBeforeUsetrueLocks contaminated gear until cleaned.

Hardcoded Exposure Tuning In 1.0.0

ValueSetting
Check interval60 seconds
Fire range5.0 meters
Smoke range25.0 meters
Cleanliness lock threshold25% or lower
Cleaning duration11 seconds

Status Labels

CleanlinessStatus
80-100Clean
60-79Smoke Exposed
40-59Dirty
25-39Heavily Soiled
0-24Contaminated

Exports Checked

Commands

CommandSidePermissionPurpose
/stationmanagerClient request / server permissionConfig.AdminAceOpens Station Manager.
/turnoutmanagerClient request / server permissionConfig.AdminAceOpens Turnout Manager.
/outfitconfiguratorClientNone by defaultOpens the standalone outfit configurator.
/exportoutfitClientNone by defaultCopies the current ped outfit component/prop data to clipboard.
/atgreloadstationsServerConfig.AdminAceReloads station JSON data.
/atgreloadoutfitsServerConfig.AdminAceReloads outfit JSON data.
/setrank [server id] [rank]ServerConfig.AdminAceSets a player's standalone ATG rank.
/atgrankServerPlayerShows the player's current ATG rank.

File Reference

FilePurpose
fxmanifest.luaResource manifest. Version is 1.0.0.
config.luaMain settings for UI, framework, duty, cleanliness, wear, vehicles, and player variations.
rank_config.luaStandalone rank system and rank labels.
client.luaMain client locker, manager, vehicle locker, and NUI logic.
client_framework.luaFramework job/duty/rank/player info integration.
client_cleanliness.luaFire/smoke exposure, cleanliness storage, washer interaction.
client_wear.luaClient wear cache, wear checks, and wear-related server calls.
server.luaJSON persistence, admin permission checks, station/outfit/rank events.
server_wear.luaSQL wear table setup, wear sessions, washing/service events.
html/index.html, style.css, script.jsNUI interface.

Troubleshooting

Station or Turnout Manager says you do not have permission

Players cannot open station lockers

Vehicle lockers do not work

Gear never gets dirty

Gear wear does not save

Outfit images are missing

Rank data contains test information

rank_data/ranks.json stores standalone rank assignments for players when you are not using framework-provided ranks.

{}