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
Purchase
Advanced Turnout Gear is available through the BC Designs store.
Requirements
Required
ox_liboxmysql- MariaDB or MySQL database
Optional
- QBCore, QBox, ESX, custom framework, or standalone mode
- SmartFires or z_fire for fire/smoke detection
@ox_lib/init.lua as a shared script and @oxmysql/lib/MySQL.lua as a server script. Start both dependencies before ATG.Installation
- Place the resource folder in your server resources directory. Recommended folder name:
advanced_turnout_gear. - Import the SQL file if using gear wear. See SQL Setup.
- Review
config.luaandrank_config.lua. - Add the resource and dependencies to
server.cfg. - Grant admin ACE permission.
- 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".
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:
rank_config.lua
station_data/stations.json
outfit_data/outfits.json
outfit_data/player_outfits.json
rank_data/ranks.json
General Settings
| Setting | Default | Purpose |
|---|---|---|
Config.InteractKey | 38 | E key interaction. |
Config.DrawDistance | 25.0 | Distance for station marker drawing. |
Config.InteractDistance | 2.0 | Distance needed to interact with station lockers. |
Config.UseBlips | false | Global station blip toggle. |
Config.UseMarkers | true | Global marker toggle. |
Config.UI.footerLabel | CHANGE ME | Footer 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
| Setting | Default | Purpose |
|---|---|---|
Config.VehicleGearAccess.enabled | true | Enables mobile vehicle lockers. |
requireStopped | true | Vehicle must be stopped. |
maxSpeed | 0.5 | Maximum speed before access is blocked. |
blockDriverSeat | true | Driver seat cannot open vehicle locker. |
allowedSeats | 0-5 | Passenger/rear seats allowed when seat access is enabled. |
rearDrawDistance | 10.0 | Draw distance for trunk/rear locker marker. |
rearInteractDistance | 2.0 | Distance needed to access rear gear compartment. |
useBootBone | true | Attempts 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 Components | 1 mask, 3 arms, 4 pants, 5 bag, 6 shoes, 7 accessories, 8 undershirt, 9 vest, 10 decals, 11 jacket |
|---|---|
| Allowed Props | 0 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
| Value | Meaning |
|---|---|
standalone | ATG standalone rank/duty only. |
auto | Detects qbx_core, qb-core, or es_extended; falls back to standalone. |
qbcore | Forces QBCore integration. |
qbx | Forces QBox integration. |
esx | Forces ESX integration. |
custom | Uses 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
| Value | Behavior |
|---|---|
framework | Uses framework grade/rank when available. |
standalone | Uses ATG /setrank only. |
hybrid | Uses 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 Name
- Department Logo file name
- Station Name
- Suit-up / locker coordinates
- Decon / washer coordinates
- Clock-in/out coordinates
- Marker enabled toggle
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
| Field | Purpose |
|---|---|
id | Numeric turnout ID. Default examples use 1001 and 1002. |
name | Display name shown in the locker UI. |
image | Image path, usually under html/images/. |
contaminationEnabled | Whether cleanliness/fire/smoke contamination applies to this outfit. |
vehicleAccess | Whether this turnout can appear in vehicle lockers. |
allowedRanks | Array of rank numbers allowed to access the outfit. |
assignedStations | Array of station IDs. Empty array means available at all stations. |
male.components / female.components | Ped component drawable/texture values. |
male.props / female.props | Ped 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
| ID | Name | Ranks | Image |
|---|---|---|---|
| 1001 | Firefighter Turnout | 1, 2, 3, 4, 5, 6 | images/firefighter.png |
| 1002 | Command Turnout | 3, 4, 5, 6 | images/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
| Setting | Default | Meaning |
|---|---|---|
enabled | true | Enables the wear system. |
useSql | true | Saves wear records to SQL. |
tableName | atg_gear_wear | SQL table name. |
lifespanHours | 48 | Total service life of a turnout set. |
tickSeconds | 60 | Wear check interval while gear is worn. Minimum enforced by code is 60 seconds. |
washAfterHours | 8 | Time worn before wash is required. |
requireWashBeforeUse | true | Locks wash-required gear until cleaned. |
contaminationWearMinutesPerPercent | 0.5 | Extra wear minutes added per 1% cleanliness lost. |
fireWearMinutes | 3 | Fallback fire exposure wear when contamination is disabled. |
smokeWearMinutes | 1 | Fallback smoke exposure wear when contamination is disabled. |
requireServiceBeforeUse | true | Locks gear when replacement/service is required. |
serviceAt | 0 | Durability percentage where replacement is required. |
warningAt | 45 | Durability percentage where the UI shows worn status. |
replaceExpiredAtWasher | true | Expired gear is exchanged for a fresh set at a washer. |
Status Logic
| Status | When It Appears |
|---|---|
| Serviceable | Gear is usable and not dirty enough to require washing. |
| Wash required | seconds_since_wash reaches washAfterHours or the record is marked wash required. |
| Worn | Durability is at or below warningAt. |
| Replacement required | Durability 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
| Setting | Default | Meaning |
|---|---|---|
Config.Cleanliness.enabled | true | Enables cleanliness tracking. |
smartfires | true | Enables SmartFires export checks. |
z_fire | false | Enables z_fire export checks. |
smartFiresResource | SmartFires | Resource name for SmartFires. |
zFireResource | z_fire | Resource name for z_fire. |
fireLoss | 5 | Cleanliness lost per check when fire is nearby. |
smokeLoss | 2 | Cleanliness lost per check when smoke is nearby. |
requireCleaningBeforeUse | true | Locks contaminated gear until cleaned. |
Hardcoded Exposure Tuning In 1.0.0
| Value | Setting |
|---|---|
| Check interval | 60 seconds |
| Fire range | 5.0 meters |
| Smoke range | 25.0 meters |
| Cleanliness lock threshold | 25% or lower |
| Cleaning duration | 11 seconds |
Status Labels
| Cleanliness | Status |
|---|---|
| 80-100 | Clean |
| 60-79 | Smoke Exposed |
| 40-59 | Dirty |
| 25-39 | Heavily Soiled |
| 0-24 | Contaminated |
Exports Checked
- SmartFires:
IsFireNearby(5.0),IsSmokeNearby(25.0) - z_fire:
getFiresInRange(coords, 5.0),getClosestFire(coords),getSmokeInRange(coords, 25.0),getClosestSmoke(coords)
Commands
| Command | Side | Permission | Purpose |
|---|---|---|---|
/stationmanager | Client request / server permission | Config.AdminAce | Opens Station Manager. |
/turnoutmanager | Client request / server permission | Config.AdminAce | Opens Turnout Manager. |
/outfitconfigurator | Client | None by default | Opens the standalone outfit configurator. |
/exportoutfit | Client | None by default | Copies the current ped outfit component/prop data to clipboard. |
/atgreloadstations | Server | Config.AdminAce | Reloads station JSON data. |
/atgreloadoutfits | Server | Config.AdminAce | Reloads outfit JSON data. |
/setrank [server id] [rank] | Server | Config.AdminAce | Sets a player's standalone ATG rank. |
/atgrank | Server | Player | Shows the player's current ATG rank. |
File Reference
| File | Purpose |
|---|---|
fxmanifest.lua | Resource manifest. Version is 1.0.0. |
config.lua | Main settings for UI, framework, duty, cleanliness, wear, vehicles, and player variations. |
rank_config.lua | Standalone rank system and rank labels. |
client.lua | Main client locker, manager, vehicle locker, and NUI logic. |
client_framework.lua | Framework job/duty/rank/player info integration. |
client_cleanliness.lua | Fire/smoke exposure, cleanliness storage, washer interaction. |
client_wear.lua | Client wear cache, wear checks, and wear-related server calls. |
server.lua | JSON persistence, admin permission checks, station/outfit/rank events. |
server_wear.lua | SQL wear table setup, wear sessions, washing/service events. |
html/index.html, style.css, script.js | NUI interface. |
Troubleshooting
Station or Turnout Manager says you do not have permission
- Add
add_ace group.admin atg.stationmanager allowtoserver.cfg. - Confirm your player is in the admin group.
- Confirm
Config.AdminAcehas not been changed to a different ACE string.
Players cannot open station lockers
- Check
Config.Duty.requireOnDutyForGearAccess. - If
Config.Framework.restrictAccess = true, confirm the player's job is listed inallowedJobs. - Confirm the player is close enough to station
coords. - Make sure the station exists in
station_data/stations.json.
Vehicle lockers do not work
- Confirm
Config.VehicleGearAccess.enabled = true. - Check that the vehicle model/spawn name exactly matches the key in
vehicles. - Make sure the vehicle is stopped if
requireStopped = true. - Driver seat access is blocked when
blockDriverSeat = true. - Make sure the turnout has
vehicleAccess = truein the Turnout Manager.
Gear never gets dirty
- Confirm
Config.Cleanliness.enabled = true. - Enable only the fire system you actually run.
- Confirm
smartFiresResourceorzFireResourcematches the resource folder name. - Confirm the outfit has
contaminationEnabled = true. - Enable
Config.Cleanliness.debug = truewhile testing.
Gear wear does not save
- Make sure
oxmysqlstarts before ATG. - Import
sql/atg_gear_wear.sql. - Confirm
Config.WearAndTear.enabled = trueanduseSql = true. - Check the SQL console for table or permission errors.
Outfit images are missing
- Place image files in
html/images/. - Use PNG, JPG, or WEBP.
- Update the
imagefield in the Turnout Manager. - The sample package references
images/firefighter.pngandimages/command.png, which are not included in the uploaded package.
Rank data contains test information
rank_data/ranks.json stores standalone rank assignments for players when you are not using framework-provided ranks.
{}