Configuration

12 min readUpdated 2 weeks ago

Overview

  • shared/config.lua: turret blueprints, calibres, AI, zones, placement UI, ownership, destruction behaviour
  • server/config_server.lua: Discord webhooks and admin ACE permission

Two kinds of turrets exist: placeable (spawned by players from kit items, count toward a per-player quota) and fixed (admin-defined in Config.FixedTurrets, always present at the declared coords).

General

shared/config.lua
1Config.Debug = false
2Config.Locale = 'en'
3Config.Theme = 'military'
4Config.MaxTurretsPerPlayer = 3
5Config.InteractionDistance = 3.0
6Config.PlacementDistance = 5.0
7Config.DeleteOld = 10
  • Debug: enables /turret_compose, /turret_muzzles, /turret_camera, /turret_firetest, /turret_inspect, /turret_aim_viz and verbose logs. Leave false in production.
  • Locale: must match a file in locales/ (e.g. enlocales/en.lua)
  • Theme: UI theme. One of: military, wasteland, cyberpunk, noir, fantasy
  • MaxTurretsPerPlayer: quota for placeable turrets. Destroyed-but-rebuildable turrets still occupy a slot; permanently destroyed ones do not
  • InteractionDistance: metres within which the target option appears on a placed turret
  • PlacementDistance: max metres from the player where a new turret can be ghost-previewed
  • DeleteOld: days of inactivity before an unused turret is auto-deleted by the nightly sweep. Set false to disable
shared/config.lua
1Config.DeleteOptions = {
2    DeleteOnlyIfEmpty    = true,
3    DeleteOnlyIfBroken   = false,
4    DeleteOnlyIfUnfueled = false,
5}
  • DeleteOnlyIfEmpty: must have 0 loaded ammo to be eligible
  • DeleteOnlyIfBroken: must have 0 HP
  • DeleteOnlyIfUnfueled: must have 0 fuel. A turret needs to match ALL enabled flags to be deleted

Placement

shared/config.lua
1Config.PlacementHintPosition = 'right-center'
2
3Config.PlacementProgress = {
4    duration  = 5000,
5    labelKey  = 'placement_installing',
6    canCancel = true,
7    disable   = { car = true, combat = true, sprint = true, move = true },
8    anim      = { dict = 'amb@world_human_hammering@male@base', clip = 'base' },
9}
  • PlacementHintPosition: ox_lib textUI anchor. Accepted: top-center, top-right, top-left, right-center, left-center, bottom-center, bottom-right, bottom-left
  • PlacementProgress.duration: progress bar length in ms while the ghost is locked in place
  • PlacementProgress.labelKey: locale key shown on the bar (falls back to raw key if missing)
  • PlacementProgress.canCancel: when true, the player can abort with SHIFT/ESC
  • PlacementProgress.disable: ox_lib control-disable flags active during the bar
  • PlacementProgress.anim: animation played on the player (dict + clip)

No-place Zones

shared/config.lua
1Config.NoPlaceZones = {
2    -- { type = 'sphere', coords = vec3(298.6, -584.8, 43.3), radius = 80.0 },
3    -- { type = 'box',    coords = vec3(195.0, -933.0, 30.0), size = vec3(100.0, 100.0, 30.0) },
4    -- { type = 'poly',   points = { vec3(), vec3(), ... }, thickness = 60.0 },
5}

Shapes where placement is forbidden. The ghost turns red inside these zones and cannot be confirmed.

  • sphere: simple radius around a point
  • box: axis-aligned bounding box, rotation is ignored
  • poly: 2D XY polygon with optional Z thickness anchored on the first point

Leave the table empty to allow placement anywhere.

AI

shared/config.lua
1Config.AI = {
2    enabled               = true,
3    defaultScanRate       = 1500,
4    defaultScanRangeMax   = 35.0,
5    defaultScanRangeMin   = 0.0,
6    defaultSwitchCooldown = 800,
7}
  • enabled: master kill switch. When false, every turret becomes manual-only
  • defaultScanRate: ms between target scans per turret (type can override)
  • defaultScanRangeMax: engagement radius in metres
  • defaultScanRangeMin: dead-zone radius, targets closer are ignored
  • defaultSwitchCooldown: min ms between two target swaps, prevents servo flip-flop

Fuel

shared/config.lua
1Config.Fuel = {
2    enabled         = true,
3    drainWhenIdle   = false,
4    idleDrainRate   = 0.01,
5    activeDrainRate = 0.10,
6    shutdownOnEmpty = true,
7}
  • enabled: global master. Disables fuel for every turret when false
  • drainWhenIdle: if true, fuel drains even without an engaged target
  • idleDrainRate / activeDrainRate: fuel/second in each state
  • shutdownOnEmpty: turret goes offline when fuel reaches 0

Individual turret types opt in via requiresFuel = true. Types with requiresFuel = false ignore this block entirely.

Ownership and Invites

shared/config.lua
1Config.Ownership = {
2    maxGroupSize         = 5,
3    nearbyPlayerRange    = 10.0,
4    showRealName         = true,
5    playerNameFormat     = 'rp_full',
6    inviteTimeoutSeconds = 30,
7}
  • maxGroupSize: max members the owner can invite. Members share panel access, reload, repair and refuel rights. They cannot invite further members, transfer ownership or dismantle
  • nearbyPlayerRange: radius (m) used to list nearby players in the invite picker
  • showRealName: show RP name instead of raw FiveM username where supported
  • playerNameFormat: one of:

- 'fivem': FiveM username as seen in the player list

- 'rp_first': RP first name only (charinfo.firstname)

- 'rp_full': first + last name

  • inviteTimeoutSeconds: how long a pending invite stays valid before auto-expiring

Destruction

shared/config.lua
1Config.Destruction = {
2    canBeRepairedAfterDestruction = true,
3    destructionRepairItem         = 'turret_rebuild_kit',
4    rubbleDespawnTime             = false,
5}
  • canBeRepairedAfterDestruction: global default. Individual turret types can override with their own canBeRepairedAfterDestruction
  • destructionRepairItem: inventory item consumed to rebuild a destroyed turret from its rubble
  • rubbleDespawnTime: seconds after which an unrepaired rubble disappears and its DB row is wiped. Set false to keep rubble indefinitely

Ammo

shared/config.lua
1Config.Ammo = {
2    ['9mm'] = {
3        label         = 'ammo_9mm_label',
4        item          = 'ammo_9mm',
5        weaponHash    = `WEAPON_COMBATPISTOL`,
6        damage        = 8.0,
7        amountPerItem = 30,
8        reloadTime    = 2000,
9    },
10    -- ...
11}

Each entry is a calibre catalogue key referenced by turret types through acceptedCalibers.

  • label: locale key for the display name in the panel
  • item: inventory item consumed to reload. Must exist in your inventory definition
  • weaponHash: GTA weapon that drives ballistics, muzzle FX and native sound
  • damage: damage per bullet against targeted entities
  • amountPerItem: rounds yielded per consumed item (e.g. 1× ammo_762 = 30 rounds)
  • reloadTime: panel progress bar duration in ms

Defaults include: 9mm, 556x45, 762x39, 50cal, minigun. Copy an entry into open/shared.lua to add new calibres without touching the main config.

Turret Types

shared/config.lua
1Config.Turrets['bazq_heavy'] = {
2    label = 'bazq_heavy_label',
3    item  = 'bazq_heavy_kit',
4    manualControl = true,
5    exitTime      = 3000,
6
7    modelSetup = {
8        models = {
9            base   = 'bazq-turret1b_base',
10            gunner = 'bazq-turret1b_gunner',
11            barrel = 'bazq-turret1b_barrel',
12        },
13        aiming = {
14            yawPropKey   = 'gunner',
15            pitchPropKey = 'barrel',
16        },
17    },
18
19    offsets = {
20        attachGunner   = vec3(-0.102, 0.0, 1.790),
21        attachBarrel   = vec3(0.876,  0.0, 1.220),
22        camera         = vec3(1.360,  0.0, 0.060),
23        muzzles        = { vec3(4.0, 0.222, 0.235), --[[...]] },
24        rotationOffset = 90.0,
25    },
26    barrelForward = vec3(1.0, 0.0, 0.0),
27    aimCorrection = vec3(0.4, 0.0, 0.0),
28}
  • label: locale key shown on the HUD and panel header
  • item: placement kit item. Set to nil to make this type fixed-only
  • manualControl: allows the player to take the gunner seat
  • exitTime: ms shutdown animation before leaving manual control (anti-abuse)
  • modelSetup.models: prop chain: base always, gunner optional, barrel optional
  • modelSetup.aiming.yawPropKey / pitchPropKey: which prop rotates on which axis
  • offsets.attachGunner / attachBarrel: local attachment offsets
  • offsets.camera: local camera position when in manual control (relative to barrel)
  • offsets.muzzles: muzzle flash origins in local space. Iterated round-robin on fire
  • offsets.rotationOffset: compensates the prop's forward axis (Bazq forward is +X so needs 90°)
  • barrelForward: forward direction vector in local coords (matches the muzzle layout)
  • aimCorrection: micro-correction for the impact point in camera space
shared/config.lua
1rotation = {
2    yawLimits     = nil,
3    pitchLimits   = { min = -25, max = 55 },
4    yawSpeed      = 120.0,
5    pitchSpeed    = 60.0,
6    snapThreshold = 0.5,
7    accelTime     = 0.35,
8    decelZone     = 18.0,
9    minVelFrac    = 0.06,
10    settleEps     = 0.08,
11    aimDeadZone   = 1.2,
12}

Servo model for target tracking. Gives the turret a mechanical feel instead of teleporting onto the target.

  • yawLimits: nil for unlimited 360° rotation, or { min, max } in degrees relative to base heading
  • pitchLimits: vertical arc in degrees
  • yawSpeed / pitchSpeed: max deg/sec
  • snapThreshold: alignment tolerance for the fire gate
  • accelTime: seconds to reach max speed from rest (lower = snappier)
  • decelZone: degrees from target where braking starts
  • minVelFrac: floor velocity as a fraction of max (prevents total stall)
  • settleEps: below this angular error the servo holds position (no hunting)
  • aimDeadZone: horizontal distance (m) below which the turret stops tracking (targets directly under it)
shared/config.lua
1targeting = {
2    switchCooldown = 800,
3    targetPlayers  = true,
4    targetHumans   = false,
5    targetZombies  = true,
6}
  • switchCooldown: min ms between two target swaps. Overrides Config.AI.defaultSwitchCooldown
  • targetPlayers: engage online players in range
  • targetHumans: engage non-zombie NPCs
  • targetZombies: engage peds flagged via Config.HRS
shared/config.lua
1health   = 1000.0,
2fireRate = 100,
3
4overheat = {
5    enabled         = true,
6    maxHeat         = 100.0,
7    perShot         = 2.2,
8    coolPerSecond   = 8.0,
9    cooldownSeconds = 5,
10}
  • health: hull HP
  • fireRate: ms between shots
  • overheat.enabled: toggle the heat subsystem
  • overheat.maxHeat / perShot / coolPerSecond: heat accumulation model
  • overheat.cooldownSeconds: lockout duration once the cap is hit
shared/config.lua
1acceptedCalibers = { '762x39', '50cal', 'minigun' },
2defaultCaliber   = '762x39',
3maxAmmoCapacity  = 500,
4
5requiresFuel = true,
6fuelCapacity = 100,
7fuelItems    = { ['diesel'] = 10, ['jerrycan_large'] = 100 },
8
9canBeRepaired = true,
10repairItems   = {
11    ['repair_kit']    = { addHealth = 300.0 },
12    ['welding_torch'] = { addHealth = 150.0 },
13}
  • acceptedCalibers: array of keys from Config.Ammo. The panel only lets the player load these
  • defaultCaliber: preloaded at spawn (used by fixed turrets mostly)
  • maxAmmoCapacity: magazine size
  • requiresFuel: when false, fuel UI is hidden and the turret never runs out
  • fuelCapacity: tank size
  • fuelItems: map of inventory item → units of fuel added per item
  • canBeRepaired: shows/hides the Repair tab
  • repairItems: map of inventory item → { addHealth = N } (HP restored per consumed item)
shared/config.lua
1permissions = {
2    canBeStolen        = false,
3    canBeUsedByAll     = false,
4    canBeDamagedByAll  = true,
5    canBeRefueledByAll = false,
6}
  • canBeStolen: non-owner can dismantle the rubble for materials
  • canBeUsedByAll: non-owner can open the panel, reload, take manual control
  • canBeDamagedByAll: non-owner weapons can damage the hull
  • canBeRefueledByAll: non-owner can pour fuel in

Fixed turrets can additionally set canBeUsedByJob = { 'police', 'sheriff' } to gate access by job name.

shared/config.lua
1weaponDamage = {
2    [`WEAPON_PISTOL`]          = 5,
3    [`WEAPON_ASSAULTRIFLE`]    = 20,
4    [`WEAPON_SNIPERRIFLE`]     = 40,
5    [`WEAPON_HEAVYSNIPER`]     = 80,
6    [`WEAPON_RPG`]             = 500,
7    [`WEAPON_UNARMED`]         = 0,
8}

Absolute HP damage per hit per weapon. Whitelist, a weapon NOT in this table cannot damage the turret (anti-forge exploit). 0 means explicit immunity. Backticks convert the weapon name to its hash at load time.

shared/config.lua
1destroyedProp   = 'prop_rub_scrap_02',
2canBeDismantled = true,
3dismantleReturn = {
4    { item = 'steel_scrap', min = 3, max = 6 },
5    { item = 'electronics', min = 1, max = 2 },
6},
7dismantleTime = 8000,
  • destroyedProp: rubble prop spawned when the turret dies. Set nil for no rubble (cinders + explosion only)
  • canBeDismantled: shows the Dismantle option on the rubble
  • dismantleReturn: array of { item, min, max }. Each entry rolls a random quantity and gives it to the dismantler
  • dismantleTime: progress bar duration in ms

Invulnerability Flags

Optional type-wide flags. Fixed turrets can override per-instance.

shared/config.lua
1-- Config.Turrets[type]
2-- infiniteHealth = true,
3-- infiniteAmmo   = true,
4-- infiniteFuel   = true,
  • infiniteHealth: turret never takes damage. Short-circuits the damage pipeline server-side. Makes canBeRepairedAfterDestruction, permanentDestruction, stayDestroyed and destroyedProp irrelevant for this type
  • infiniteAmmo: ammo never depletes
  • infiniteFuel: fuel never depletes

Post-destruction Behaviour

shared/config.lua
1-- Config.Turrets[type]
2-- canBeRepairedAfterDestruction = false,
3-- permanentDestruction = true,

Per-type overrides for the destruction flow.

  • canBeRepairedAfterDestruction: overrides the global Config.Destruction.canBeRepairedAfterDestruction:

- true: rubble can be rebuilt in place with destructionRepairItem (restores 50% HP)

- false: rubble still spawns but the Repair option is hidden (dismantle-only)

- nil: inherit the global value

  • permanentDestruction: overrides everything else:

- true: death = game over. No rubble, no repair option. DB row deleted instantly, player quota frees up right away. On fixed turrets, persists is_destroyed=1 so the turret stays gone across restarts

Fixed Turrets

shared/config.lua
1Config.FixedTurrets = {
2    {
3        id      = 'paleto_police_roof',
4        type    = 'bazq_heavy',
5        coords  = vec3(-443.06, 6034.73, 30.34),
6        heading = 270.0,
7
8        infiniteHealth = true,
9        infiniteAmmo   = false,
10        infiniteFuel   = false,
11
12        refillOnRestart = true,
13        stayDestroyed   = false,
14
15        defaultCaliber = '50cal',
16        permissions    = { canBeUsedByAll = true },
17        targeting      = { targetPlayers = false, targetZombies = true },
18        activationZone = { type = 'sphere', radius = 70.0 },
19        ai             = { enabled = true, toggleableBy = { jobs = { 'police' } } },
20    },
21}

Admin-defined permanent turrets. Each entry spawns on every server start.

  • id: unique string used as fixed_config_id in the DB. Required
  • type: must match a Config.Turrets[type] key
  • coords / heading: spawn transform
  • infiniteHealth / infiniteAmmo / infiniteFuel: per-instance overrides of the type-level flags
  • refillOnRestart: wipes the DB row at boot, so health/ammo/fuel reset to max every restart
  • stayDestroyed: if true and the turret was destroyed last session (is_destroyed=1 in DB), skip spawn on restart. Admin must run /turret_revive <id> to bring it back. Takes precedence over refillOnRestart
  • defaultCaliber: calibre loaded at spawn
  • permissions: per-instance override of the type's permission block (e.g. make this specific turret public)
  • targeting: per-instance override of targetPlayers / targetHumans / targetZombies
  • activationZone: boundary (sphere/box/poly) where the AI will engage. Independent from scan range
  • ai.enabled: AI master switch for this instance
  • ai.toggleableBy: ACE or job policy for who can toggle the AI from the panel

Discord Webhooks

server/config_server.lua
1Config.Server.webhooks = {
2    placed     = '',
3    destroyed  = '',
4    dismantled = '',
5    repaired   = '',
6    ammoLoaded = '',
7    fueled     = '',
8    aiToggle   = '',
9    exploits   = '',
10}
  • placed: fires on turret_placed (new turret spawned from a kit)
  • destroyed: fires on turret_destroyed (HP reached 0)
  • dismantled: fires on turret_dismantled (rubble smashed for parts)
  • repaired: fires on repair_applied (HP restored via repair item)
  • ammoLoaded: fires on ammo_loaded (rounds loaded from the panel)
  • fueled: fires on fuel_added (fuel poured in)
  • aiToggle: fires on ai_toggle (AI turned on/off)
  • exploits: fires on exploit_attempt (security flag threshold reached)

Set any webhook to false to disable that log entirely.

Admin Override

server/config_server.lua
1Config.Server.admin = {
2    acePermission       = 'group.admin',
3    fallbackIdentifiers = {},  -- { 'license:abc123...' }
4}
  • acePermission: ACE permission that enables debug commands (/turret_compose, /turret_muzzles, /turret_revive, ...) even when Config.Debug = false
  • fallbackIdentifiers: identifier whitelist checked when ACE is not available (e.g. license:abc..., discord:...)