Configuration
Overview
shared/config.lua: turret blueprints, calibres, AI, zones, placement UI, ownership, destruction behaviourserver/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
1Config.Debug = false
2Config.Locale = 'en'
3Config.Theme = 'military'
4Config.MaxTurretsPerPlayer = 3
5Config.InteractionDistance = 3.0
6Config.PlacementDistance = 5.0
7Config.DeleteOld = 10Debug: enables/turret_compose,/turret_muzzles,/turret_camera,/turret_firetest,/turret_inspect,/turret_aim_vizand verbose logs. Leavefalsein production.Locale: must match a file inlocales/(e.g.en→locales/en.lua)Theme: UI theme. One of:military,wasteland,cyberpunk,noir,fantasyMaxTurretsPerPlayer: quota for placeable turrets. Destroyed-but-rebuildable turrets still occupy a slot; permanently destroyed ones do notInteractionDistance: metres within which the target option appears on a placed turretPlacementDistance: max metres from the player where a new turret can be ghost-previewedDeleteOld: days of inactivity before an unused turret is auto-deleted by the nightly sweep. Setfalseto disable
1Config.DeleteOptions = {
2 DeleteOnlyIfEmpty = true,
3 DeleteOnlyIfBroken = false,
4 DeleteOnlyIfUnfueled = false,
5}DeleteOnlyIfEmpty: must have 0 loaded ammo to be eligibleDeleteOnlyIfBroken: must have 0 HPDeleteOnlyIfUnfueled: must have 0 fuel. A turret needs to match ALL enabled flags to be deleted
Placement
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-leftPlacementProgress.duration: progress bar length in ms while the ghost is locked in placePlacementProgress.labelKey: locale key shown on the bar (falls back to raw key if missing)PlacementProgress.canCancel: whentrue, the player can abort with SHIFT/ESCPlacementProgress.disable: ox_lib control-disable flags active during the barPlacementProgress.anim: animation played on the player (dict + clip)
No-place Zones
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 pointbox: axis-aligned bounding box, rotation is ignoredpoly: 2D XY polygon with optional Z thickness anchored on the first point
Leave the table empty to allow placement anywhere.
AI
1Config.AI = {
2 enabled = true,
3 defaultScanRate = 1500,
4 defaultScanRangeMax = 35.0,
5 defaultScanRangeMin = 0.0,
6 defaultSwitchCooldown = 800,
7}enabled: master kill switch. Whenfalse, every turret becomes manual-onlydefaultScanRate: ms between target scans per turret (type can override)defaultScanRangeMax: engagement radius in metresdefaultScanRangeMin: dead-zone radius, targets closer are ignoreddefaultSwitchCooldown: min ms between two target swaps, prevents servo flip-flop
Fuel
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 whenfalsedrainWhenIdle: iftrue, fuel drains even without an engaged targetidleDrainRate/activeDrainRate: fuel/second in each stateshutdownOnEmpty: 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
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 dismantlenearbyPlayerRange: radius (m) used to list nearby players in the invite pickershowRealName: show RP name instead of raw FiveM username where supportedplayerNameFormat: 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
1Config.Destruction = {
2 canBeRepairedAfterDestruction = true,
3 destructionRepairItem = 'turret_rebuild_kit',
4 rubbleDespawnTime = false,
5}canBeRepairedAfterDestruction: global default. Individual turret types can override with their owncanBeRepairedAfterDestructiondestructionRepairItem: inventory item consumed to rebuild a destroyed turret from its rubblerubbleDespawnTime: seconds after which an unrepaired rubble disappears and its DB row is wiped. Setfalseto keep rubble indefinitely
Ammo
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 panelitem: inventory item consumed to reload. Must exist in your inventory definitionweaponHash: GTA weapon that drives ballistics, muzzle FX and native sounddamage: damage per bullet against targeted entitiesamountPerItem: 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
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 headeritem: placement kit item. Set tonilto make this type fixed-onlymanualControl: allows the player to take the gunner seatexitTime: ms shutdown animation before leaving manual control (anti-abuse)modelSetup.models: prop chain:basealways,gunneroptional,barreloptionalmodelSetup.aiming.yawPropKey/pitchPropKey: which prop rotates on which axisoffsets.attachGunner/attachBarrel: local attachment offsetsoffsets.camera: local camera position when in manual control (relative to barrel)offsets.muzzles: muzzle flash origins in local space. Iterated round-robin on fireoffsets.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
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:nilfor unlimited 360° rotation, or{ min, max }in degrees relative to base headingpitchLimits: vertical arc in degreesyawSpeed/pitchSpeed: max deg/secsnapThreshold: alignment tolerance for the fire gateaccelTime: seconds to reach max speed from rest (lower = snappier)decelZone: degrees from target where braking startsminVelFrac: 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)
1targeting = {
2 switchCooldown = 800,
3 targetPlayers = true,
4 targetHumans = false,
5 targetZombies = true,
6}switchCooldown: min ms between two target swaps. OverridesConfig.AI.defaultSwitchCooldowntargetPlayers: engage online players in rangetargetHumans: engage non-zombie NPCstargetZombies: engage peds flagged viaConfig.HRS
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 HPfireRate: ms between shotsoverheat.enabled: toggle the heat subsystemoverheat.maxHeat/perShot/coolPerSecond: heat accumulation modeloverheat.cooldownSeconds: lockout duration once the cap is hit
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 fromConfig.Ammo. The panel only lets the player load thesedefaultCaliber: preloaded at spawn (used by fixed turrets mostly)maxAmmoCapacity: magazine sizerequiresFuel: whenfalse, fuel UI is hidden and the turret never runs outfuelCapacity: tank sizefuelItems: map of inventory item → units of fuel added per itemcanBeRepaired: shows/hides the Repair tabrepairItems: map of inventory item →{ addHealth = N }(HP restored per consumed item)
1permissions = {
2 canBeStolen = false,
3 canBeUsedByAll = false,
4 canBeDamagedByAll = true,
5 canBeRefueledByAll = false,
6}canBeStolen: non-owner can dismantle the rubble for materialscanBeUsedByAll: non-owner can open the panel, reload, take manual controlcanBeDamagedByAll: non-owner weapons can damage the hullcanBeRefueledByAll: non-owner can pour fuel in
Fixed turrets can additionally set canBeUsedByJob = { 'police', 'sheriff' } to gate access by job name.
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.
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. Setnilfor no rubble (cinders + explosion only)canBeDismantled: shows the Dismantle option on the rubbledismantleReturn: array of{ item, min, max }. Each entry rolls a random quantity and gives it to the dismantlerdismantleTime: progress bar duration in ms
Invulnerability Flags
Optional type-wide flags. Fixed turrets can override per-instance.
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. MakescanBeRepairedAfterDestruction,permanentDestruction,stayDestroyedanddestroyedPropirrelevant for this typeinfiniteAmmo: ammo never depletesinfiniteFuel: fuel never depletes
Post-destruction Behaviour
1-- Config.Turrets[type]
2-- canBeRepairedAfterDestruction = false,
3-- permanentDestruction = true,Per-type overrides for the destruction flow.
canBeRepairedAfterDestruction: overrides the globalConfig.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
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 asfixed_config_idin the DB. Requiredtype: must match aConfig.Turrets[type]keycoords/heading: spawn transforminfiniteHealth/infiniteAmmo/infiniteFuel: per-instance overrides of the type-level flagsrefillOnRestart: wipes the DB row at boot, so health/ammo/fuel reset to max every restartstayDestroyed: iftrueand the turret was destroyed last session (is_destroyed=1in DB), skip spawn on restart. Admin must run/turret_revive <id>to bring it back. Takes precedence overrefillOnRestartdefaultCaliber: calibre loaded at spawnpermissions: per-instance override of the type's permission block (e.g. make this specific turret public)targeting: per-instance override oftargetPlayers/targetHumans/targetZombiesactivationZone: boundary (sphere/box/poly) where the AI will engage. Independent from scan rangeai.enabled: AI master switch for this instanceai.toggleableBy: ACE or job policy for who can toggle the AI from the panel
Discord Webhooks
1Config.Server.webhooks = {
2 placed = '',
3 destroyed = '',
4 dismantled = '',
5 repaired = '',
6 ammoLoaded = '',
7 fueled = '',
8 aiToggle = '',
9 exploits = '',
10}placed: fires onturret_placed(new turret spawned from a kit)destroyed: fires onturret_destroyed(HP reached 0)dismantled: fires onturret_dismantled(rubble smashed for parts)repaired: fires onrepair_applied(HP restored via repair item)ammoLoaded: fires onammo_loaded(rounds loaded from the panel)fueled: fires onfuel_added(fuel poured in)aiToggle: fires onai_toggle(AI turned on/off)exploits: fires onexploit_attempt(security flag threshold reached)
Set any webhook to false to disable that log entirely.
Admin Override
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 whenConfig.Debug = falsefallbackIdentifiers: identifier whitelist checked when ACE is not available (e.g.license:abc...,discord:...)