Battle Royale: config files
These files stay readable in every build (escrow too), so you can change them. Shown as shipped in version 1.1.1.
config.lua
lua
-- ============================================================================
-- Lumo Battle Royale — main config
-- Maps live in config/maps.lua, loot in config/loot.lua.
-- ============================================================================
Config = {}
Config.Debug = false -- testing: /brdebug (coords + state to F8). Set false for a live server.
Config.Framework = 'auto' -- 'auto' | 'qbx' | 'qb' | 'esx' | 'standalone'
Config.Target = 'auto' -- 'auto' | 'qb' | 'ox' | 'none' (none = walk up and press E)
Config.Admin = {
permissions = { 'admin', 'god' }, -- framework groups allowed to use the admin panel / admin commands
ace = 'lumo.battleroyale.admin', -- or grant this ACE
}
-- ── Lobby ───────────────────────────────────────────────────────────────────
Config.Lobby = {
command = false, -- players use the hub terminal; set e.g. 'br' to also allow a command
leaveCommand = 'brleave', -- leave the queue or the match (the results screen has a button too)
adminCommand = 'bradmin', -- admins: open the admin panel anywhere, also inside a match
minPlayers = 1, -- humans needed to start the countdown (bots fill the rest)
maxPlayers = 32, -- humans per match
countdown = 45, -- seconds from minPlayers to launch
fullCountdown = 10, -- countdown drops to this once maxPlayers are queued
matchSize = 100, -- humans + bots per match (bots fill up to this)
allowConcurrentMatches = true, -- a new queue can launch while another match is running
}
-- ── Deployment hub (walk-up lobby in the city) ──────────────────────────────
-- A military staging camp: crates, sandbags, floodlights, an instructor, a
-- video wall of real TV props showing the live status board, and a row of
-- desktop computers (deployment terminals) you interact with.
-- Admins can move it: admin panel › "Move hub here" (saved to data/hub.json).
-- Ground Z is found at runtime, so Z can be rough.
--
-- Screens are drawn into the props' own render targets (the same way GTA's TVs
-- work), so the picture sits exactly on the glass. GTA props face their -Y
-- side, so h = 0 turns a screen towards the people standing in the camp.
Config.Hub = {
coords = vec4(-2126.79, 3268.84, 32.81, 146.16), -- Fort Zancudo
blip = {
sprite = 377, colour = 2, scale = 0.95, label = 'Lumo Royale', -- 377 = radar_parachute
shortRange = true, -- true: minimap shows it only nearby (the pause map always does)
launchColour = 46, -- gold + flashing while a queue is counting down
},
ped = 's_m_y_marine_01',
drawDistance = 70.0, -- props/screens exist only while a player is this close
-- Offsets are relative to the hub: x = right, y = forward, z = up; h = heading offset.
props = {
{ model = 'prop_gazebo_02', x = 0.0, y = 2.3, z = 0.0, h = 0 },
{ model = 'prop_mil_crate_01', x = -3.9, y = 6.6, z = 0.0, h = 0 },
{ model = 'prop_mil_crate_02', x = 3.9, y = 6.6, z = 0.0, h = 0 },
{ model = 'prop_box_ammo03a', x = -3.9, y = 6.6, z = 0.95, h = 15 },
{ model = 'gr_prop_gr_crates_weapon_mix_01a', x = 4.8, y = 3.0, z = 0.0, h = 80 },
{ model = 'prop_drop_armscrate_01', x = -4.8, y = 3.2, z = 0.0, h = -70 },
{ model = 'prop_mb_sandblock_01', x = -4.4, y = -1.0, z = 0.0, h = 90 },
{ model = 'prop_mb_sandblock_01', x = 4.4, y = -1.0, z = 0.0, h = -90 },
{ model = 'prop_worklight_03b', x = -3.6, y = 4.6, z = 0.0, h = 20 }, -- lamps face the camp, toed in
{ model = 'prop_worklight_03b', x = 3.6, y = 4.6, z = 0.0, h = -20 },
{ model = 'prop_generator_03b', x = 5.4, y = 7.4, z = 0.0, h = 160 }, -- light tower: its lamps face +Y, so 160 aims them at the camp
{ model = 'prop_parachute', x = 3.6, y = 0.4, z = 0.0, h = 30 },
{ model = 'prop_bodyarmour_03', x = 3.9, y = 6.6, z = 0.95, h = 20 },
},
-- Big status screen: a real screen prop (solid, lit by the game) with the
-- board drawn on its glass at full resolution. glass = the share of the
-- prop's bounding box trimmed off each side to reach the screen area.
-- If the prop can't load, a drawn billboard of `size` stands in for it.
wall = {
x = 0.0, y = 5.6, z = 1.4, -- z = bottom edge of the fallback billboard above the ground
screen = {
model = 'prop_huge_display_01', target = 'big_disp', lift = 0.0, h = 0,
glass = { left = 0.058, right = 0.072, top = 0.075, bottom = 0.10 },
},
brightness = 225, -- 0-255; the picture is unlit, full white is glaring
size = { w = 5.6, h = 3.15 }, -- fallback billboard, metres
housing = { margin = 0.16, depth = 0.22, leg = 0.18 },
},
-- Deployment terminals: desk + monitor + keyboard + mouse + tower per station.
-- All monitors share one render target, so they show the same terminal page.
computers = {
desk = 'prop_table_03b',
monitor = { model = 'prop_monitor_w_large', target = 'tvscreen', y = 0.12, h = 0, brightness = 150 },
extras = {
{ model = 'prop_keyboard_01a', x = 0.0, y = -0.16, h = 0 },
{ model = 'prop_mouse_01a', x = 0.30, y = -0.16, h = 0 },
{ model = 'prop_pc_01a', x = 0.62, y = 0.10, h = 0, floor = true },
},
stations = { { x = -1.2, y = 2.5 }, { x = 0.0, y = 2.5 }, { x = 1.2, y = 2.5 } },
stand = -0.75, -- where the player stands, relative to the desk (y)
radius = 1.5,
-- Using a terminal: the camera moves onto the monitor and you click the
-- lobby on its screen with the mouse. Right click / Esc / Backspace leaves.
camera = { fov = 38.0, fill = 0.92, blend = 650 },
anim = { dict = 'anim@heists@prison_heiststation@cop_reactions', clip = 'cop_b_idle' }, -- standing, typing (false = none) -- fill = share of the view height the screen takes
screen = { width = 0.94, top = 0.03 }, -- monitor glass: share of the model width, gap from the top (m)
lobby = { w = 1280, h = 720, brightness = 235 },
},
instructor = { x = 2.7, y = 1.6, z = 0.0, h = 180 },
}
-- ── Modes ───────────────────────────────────────────────────────────────────
Config.Modes = {
solo = { label = 'Solo', teamSize = 1 },
duo = { label = 'Duo', teamSize = 2 },
squad = { label = 'Squad', teamSize = 4 },
}
-- ── Match ───────────────────────────────────────────────────────────────────
Config.Match = {
bucketBase = 7300, -- routing buckets 7301, 7302 … (one per match)
warmup = 30, -- seconds of free-fire warm-up at the map's staging area
warmupWeapon = 'WEAPON_PISTOL',
endDelay = 15, -- seconds on the results screen before everyone is sent home
maxDuration = 30 * 60, -- hard stop
friendlyFire = false,
knockedBleedout = 30, -- duo/squad: seconds a downed player lasts
reviveTime = 6, -- seconds holding E to pick a teammate up
reviveHealth = 30,
spectate = true,
spectateBots = true, -- keep a bots-only match running while an eliminated player is still watching
teamRadio = true, -- pma-voice radio channel per team (no-op without pma-voice)
radioBase = 900, -- channels 900+ are reserved for match teams
aiDamage = 0.6, -- damage bots deal to players (1.0 = full GTA damage)
}
-- Damage model on top of GTA's own numbers (applied on the victim's client).
Config.Damage = {
headshot = 1.5, -- extra multiplier for head hits
helmet = { [1] = 0.30, [2] = 0.45, [3] = 0.60 }, -- head damage removed per helmet level
vest = { [1] = 0.30, [2] = 0.45, [3] = 0.60 }, -- body damage removed per vest level, while it has durability
vestDurability = 100, -- durability shown as the armour bar; absorbed damage wears it down
fall = 0.5, -- fall damage multiplier
-- Damage this client deals with each weapon class (SetWeaponDamageModifier).
classes = { pistol = 1.0, smg = 0.9, shotgun = 1.0, rifle = 1.0, sniper = 1.4, melee = 1.6, rocket = 1.0 },
}
-- Warm-up practice targets: local bots only you see, respawning, weak aim.
Config.Practice = {
enabled = true,
count = 6,
radius = 45.0,
accuracy = 8,
respawn = 4, -- seconds
models = { 's_m_y_marine_03', 'g_m_y_lost_01', 'a_m_y_hiker_01', 's_m_y_blackops_01' },
weapons = { 'WEAPON_PISTOL', 'WEAPON_MICROSMG', 'WEAPON_PUMPSHOTGUN' },
}
-- HUD helpers that make fights readable.
Config.Hud = {
gunfire = true, -- enemy shots: red ticks on the compass + a gun mark on the minimap and the pause map
gunfireRange = 250.0, -- unsilenced shots are heard this far
-- "Visual sound": what you would hear shows up on the minimap, the pause map and the compass.
sound = {
silencedRange = 45.0, -- silenced shots are heard this close (0 = never)
footsteps = true, -- orange footprints for enemies moving near you
sprintRange = 45.0,
runRange = 30.0,
walkRange = 12.0, -- crouched / stealth movement is silent
vehicles = true, -- enemy vehicles with the engine running
vehicleRange = 140.0,
blur = 0.08, -- far marks sit up to this share of the distance off the real spot
pauseMap = true, -- false = minimap only
},
minimap = true, -- keep the minimap on during a match (some city HUDs hide it on foot)
crosshair = true, -- small crosshair while aiming (replaces GTA's reticle dot, which some servers hide)
aimTags = true, -- name + distance of the enemy you aim at
damageDirection = true, -- red arc showing where hits come from
teammateMarkers = true, -- chevrons over teammates + minimap blips
lootBeams = true, -- light beam over rare+ loot (visible from 70 m)
mph = false, -- speedometer in MPH instead of KM/H
}
-- City HUD on/off (client). The match has its own HUD, so hunger/thirst/money bars
-- should hide. Every player in a match carries the `lumo_quiet` statebag; HUDs without
-- a switch need the one-line guard in docs/INSTALL.md (qbx_hud, qb-hud, ps-hud).
-- For a HUD that has an export or event, call it here (show = false on entry, true on exit):
Config.CityHud = function(show)
-- exports['my-hud']:toggleHud(show)
end
-- ── Item images ─────────────────────────────────────────────────────────────
-- Pictures in the loadout, HUD and pickup list. Every item in config/loot.lua has
-- an `image`: a file name (joined to `base`) or a full link, used as is.
-- A picture that is missing or fails to load shows a lettered tile instead.
Config.Images = {
-- 'auto' the installed inventory's image folder (lumo-inventory, ox_inventory,
-- qb / ps / lj / qs-inventory, core_inventory, codem-inventory), else 'local'
-- 'local' this resource's html/images/ folder: drop your own PNG / WEBP / JPG there
-- a link any folder URL ending in '/': 'https://cdn.example.com/items/'
-- or 'nui://my-inventory/html/images/'
-- false lettered tiles only
base = 'auto',
-- Per-item pictures without touching config/loot.lua: file name or full link.
items = {
-- helmet1 = 'helmet_light.png',
-- heavysniper = 'https://cdn.example.com/items/heavysniper.webp',
},
}
-- Loot amount: multiplies every map's POI loot and scatter counts.
Config.LootDensity = 2.2
-- Small caches on a grid over the whole map, so the ground between named places has
-- loot too (spots that land in water are skipped by the client).
Config.LootCaches = {
spacing = 220.0, -- metres between grid cells
chance = 0.7, -- share of cells that get a cache
items = { 2, 4 }, -- items per cache (× LootDensity)
spread = 14.0, -- cache radius
}
-- Visible skydivers: bots leaving the aircraft are shown falling and gliding away
-- (cosmetic, local peds; the real bot takes over near the ground).
Config.Skydivers = {
enabled = true,
max = 14, -- shown at once
range = 1100.0, -- only drops this close to your camera
}
-- Health model: the ped is kept far above GTA's death threshold so the framework's
-- death/last-stand never fires inside a match. Match HP = ped health - floor.
Config.Health = { max = 800, floor = 700 }
-- ── Aircraft & drop ─────────────────────────────────────────────────────────
Config.Plane = {
model = 'titan',
altitude = 600.0, -- metres above sea level
speed = 75.0, -- m/s
edge = 1.25, -- path starts/ends this many map radii from the centre
autoOpenAt = 160.0, -- forced parachute height above ground
}
-- ── Zone ────────────────────────────────────────────────────────────────────
-- radius is a fraction of the map radius. wait = circle shown, shrink = closing time.
Config.Zone = {
phases = {
{ wait = 120, shrink = 60, radius = 0.62, dps = 1 },
{ wait = 75, shrink = 50, radius = 0.38, dps = 2 },
{ wait = 60, shrink = 45, radius = 0.22, dps = 4 },
{ wait = 45, shrink = 40, radius = 0.12, dps = 6 },
{ wait = 35, shrink = 35, radius = 0.05, dps = 9 },
{ wait = 25, shrink = 40, radius = 0.0, dps = 14 },
},
poiBias = 0.7, -- chance the next circle centres near a named location (keeps finals on land)
colour = { r = 60, g = 130, b = 255, a = 70 },
}
-- ── Airdrops ────────────────────────────────────────────────────────────────
Config.Airdrops = {
enabled = true,
fromPhase = 2, -- first phase that drops one
perPhase = 1,
fallTime = 40, -- seconds from announcement to landing
items = 3, -- rolls from the 'airdrop' pool
}
-- ── Vehicles ────────────────────────────────────────────────────────────────
Config.Vehicles = {
enabled = true,
spawnRadius = 250.0, -- spawned when a player first comes this close
models = {
{ model = 'sanchez', type = 'bike' },
{ model = 'bf400', type = 'bike' },
{ model = 'mesa', type = 'automobile' },
{ model = 'bodhi2', type = 'automobile' },
{ model = 'kalahari',type = 'automobile' },
{ model = 'rebel', type = 'automobile' },
},
}
-- ── Bots ────────────────────────────────────────────────────────────────────
Config.Bots = {
enabled = true,
maxBots = 99,
fillTeams = true, -- duo/squad: bot teammates fill empty seats, follow you, ride along and revive you
materializeRadius = 350.0, -- a bot becomes a real ped when a player is this close
dematerializeRadius = 420.0, -- keep below OneSync's ~424 m culling radius
simTick = 1000, -- ms between server simulation steps
skirmishRange = 80.0, -- off-screen bots this close can eliminate each other
skirmishChance = 0.02, -- per pair per tick, scaled up as the zone closes
lootChance = 0.35, -- chance a real bot detours to nearby loot (unarmed bots always look)
landWithPistol = 0.08, -- bots land empty-handed like players; this share happens to have a pistol
hunterShare = 0.35, -- share of bots that drift toward the nearest player (so fights find you)
hunterRange = 700.0,
chuteRange = 450.0, -- bots landing this close to a player arrive by visible parachute
-- Story/ambient models only: freemode peds spawn without clothes unless dressed.
models = {
's_m_y_marine_03', 's_m_m_marine_01', 's_m_y_blackops_01', 's_m_y_armymech_01', 'csb_mweather',
'g_m_y_lost_01', 'g_m_y_mexgoon_02', 'g_m_y_ballasout_01', 'g_m_m_chicold_01', 'g_f_y_vagos_01',
'a_m_y_hiker_01', 'a_f_y_hiker_01', 'a_m_m_hillbilly_01', 'a_m_y_hipster_02', 's_m_m_chemsec_01',
'a_m_y_runner_01', 'u_m_y_militarybum',
},
-- hearing: reacts to unsilenced gunfire this close. shootRate: GTA shoot rate (100 = default).
skills = {
-- reaction: ms between spotting you and opening fire (they aim first). Armour comes from looted vests.
{ label = 'rookie', weight = 40, accuracy = 22, seeing = 80.0, hearing = 70.0, combatAbility = 0, shootRate = 300, reaction = 1400 },
{ label = 'regular', weight = 42, accuracy = 34, seeing = 110.0, hearing = 100.0, combatAbility = 1, shootRate = 450, reaction = 1000 },
{ label = 'veteran', weight = 18, accuracy = 48, seeing = 140.0, hearing = 140.0, combatAbility = 2, shootRate = 650, reaction = 700 },
},
accuracyPerPhase = 2, -- bots sharpen up as the circle closes
landingGrace = 30, -- seconds after landing with half accuracy and slower reactions
healBelow = 55, -- a bot with no target patches up under this health %
-- Called "<prefix><name>" in the kill feed so players can tell them apart.
tag = '',
names = {
'Bishal', 'Aayush', 'Sagun', 'Prabin', 'Rojina', 'Sushant', 'Nirajan', 'Kritika', 'Anish',
'Ghost', 'Viper', 'Nomad', 'Raven', 'Specter', 'Havoc', 'Bandit', 'Cinder', 'Talon',
'Yeti', 'Everest', 'Kukri', 'Gorkha', 'Sherpa', 'Tenzing', 'Rhino', 'Monsoon', 'Lhotse',
'Phantom', 'Saber', 'Drifter', 'Echo', 'Wraith', 'Juno', 'Orbit', 'Pulse', 'Rook',
'Tundra', 'Vandal', 'Warden', 'Zenith', 'Comet', 'Blaze',
},
}
-- ── Rewards ─────────────────────────────────────────────────────────────────
-- Cash needs a framework; XP and stats work everywhere.
Config.Rewards = {
enabled = true,
account = 'cash',
participation = 50,
perKill = 150, -- per real player eliminated
perBotKill = 40, -- per bot (kept low so farming bots isn't a money printer)
placement = { [1] = 2500, [2] = 1200, [3] = 800, [5] = 400, [10] = 150 }, -- best matching tier
items = { [1] = {} }, -- e.g. [1] = { { name = 'goldbar', amount = 1 } }
xp = { participation = 20, perKill = 40, win = 300, perMinute = 4 },
}
-- ── Keys (players can rebind in Settings › Key Bindings › FiveM) ───────────
Config.Keys = {
jump = 'F', -- leave the aircraft
pickup = 'E',
inventory = 'TAB',
heal1 = '7', -- bandage
heal2 = '8', -- first aid / med kit (best you carry)
heal3 = '9', -- energy drink
heal4 = '0', -- painkillers
plate = 'G', -- armour plate
ping = 'Z', -- mark a spot for your team
mark = 'MOUSE_MIDDLE', -- middle click: mark a spot · double click: enemy spotted
leave = 'BACK', -- while eliminated/spectating: return to the city
spectateNext = 'RIGHT',
spectatePrev = 'LEFT',
}config/loot.lua
lua
-- ============================================================================
-- Match loot. None of this touches the framework inventory: it is a separate
-- loadout that exists only inside the match and is wiped when you leave.
--
-- type weapon | ammo | heal | boost | plate | armor | helmet | backpack | throw | attach
-- slot weapons: primary (2 slots) | side | melee
-- ammo pistol | smg | rifle | shotgun | sniper | rocket
-- class damage class for Config.Damage.classes
-- tier 1 common · 2 uncommon · 3 rare · 4 epic · 5 legendary
-- image file name in the image folder (Config.Images.base) or a full https:// / nui:// link;
-- Config.Images.items can override any item without editing this file
-- ============================================================================
Loot = {}
Loot.tiers = {
{ label = 'Common', color = '#c9d1c4' },
{ label = 'Uncommon', color = '#7dff8a' },
{ label = 'Rare', color = '#4fb4ff' },
{ label = 'Epic', color = '#c77dff' },
{ label = 'Legendary', color = '#ffb53a' },
}
local function W(slot, weapon, ammo, class, tier, label)
return { type = 'weapon', slot = slot, weapon = weapon, ammo = ammo, class = class, tier = tier, label = label, image = weapon:lower() .. '.png' }
end
Loot.items = {
-- Sidearms
pistol = W('side', 'WEAPON_PISTOL', 'pistol', 'pistol', 1, 'Pistol'),
combatpistol = W('side', 'WEAPON_COMBATPISTOL', 'pistol', 'pistol', 2, 'Combat Pistol'),
heavypistol = W('side', 'WEAPON_HEAVYPISTOL', 'pistol', 'pistol', 3, 'Heavy Pistol'),
revolver = W('side', 'WEAPON_REVOLVER', 'pistol', 'pistol', 4, 'Heavy Revolver'),
-- SMGs
microsmg = W('primary', 'WEAPON_MICROSMG', 'smg', 'smg', 1, 'Micro SMG'),
smg = W('primary', 'WEAPON_SMG', 'smg', 'smg', 2, 'SMG'),
assaultsmg = W('primary', 'WEAPON_ASSAULTSMG', 'smg', 'smg', 3, 'Assault SMG'),
-- Shotguns
sawnoff = W('primary', 'WEAPON_SAWNOFFSHOTGUN', 'shotgun', 'shotgun', 1, 'Sawed-Off'),
pumpshotgun = W('primary', 'WEAPON_PUMPSHOTGUN', 'shotgun', 'shotgun', 2, 'Pump Shotgun'),
assaultshotgun = W('primary', 'WEAPON_ASSAULTSHOTGUN', 'shotgun', 'shotgun', 4, 'Assault Shotgun'),
-- Rifles
compactrifle = W('primary', 'WEAPON_COMPACTRIFLE', 'rifle', 'rifle', 2, 'Compact Rifle'),
assaultrifle = W('primary', 'WEAPON_ASSAULTRIFLE', 'rifle', 'rifle', 2, 'Assault Rifle'),
carbinerifle = W('primary', 'WEAPON_CARBINERIFLE', 'rifle', 'rifle', 3, 'Carbine Rifle'),
specialcarbine = W('primary', 'WEAPON_SPECIALCARBINE', 'rifle', 'rifle', 4, 'Special Carbine'),
-- Long range
marksmanrifle = W('primary', 'WEAPON_MARKSMANRIFLE', 'sniper', 'sniper', 3, 'Marksman Rifle'),
sniperrifle = W('primary', 'WEAPON_SNIPERRIFLE', 'sniper', 'sniper', 4, 'Sniper Rifle'),
heavysniper = W('primary', 'WEAPON_HEAVYSNIPER', 'sniper', 'sniper', 5, 'Heavy Sniper'),
rpg = W('primary', 'WEAPON_RPG', 'rocket', 'rocket', 5, 'RPG'),
-- Melee
knife = W('melee', 'WEAPON_KNIFE', nil, 'melee', 1, 'Knife'),
bat = W('melee', 'WEAPON_BAT', nil, 'melee', 1, 'Bat'),
machete = W('melee', 'WEAPON_MACHETE', nil, 'melee', 2, 'Machete'),
-- Throwables (one stack, one kind at a time)
grenade = { type = 'throw', weapon = 'WEAPON_GRENADE', tier = 3, label = 'Grenade', amount = 2, image = 'weapon_grenade.png' },
molotov = { type = 'throw', weapon = 'WEAPON_MOLOTOV', tier = 2, label = 'Molotov', amount = 2, image = 'weapon_molotov.png' },
smoke = { type = 'throw', weapon = 'WEAPON_SMOKEGRENADE', tier = 1, label = 'Smoke', amount = 2, image = 'weapon_smokegrenade.png' },
stickybomb = { type = 'throw', weapon = 'WEAPON_STICKYBOMB', tier = 4, label = 'Sticky Bomb', amount = 1, image = 'weapon_stickybomb.png' },
-- Ammo
ammo_pistol = { type = 'ammo', ammo = 'pistol', amount = 40, tier = 1, label = 'Pistol Ammo', image = 'pistol_ammo.png' },
ammo_smg = { type = 'ammo', ammo = 'smg', amount = 60, tier = 1, label = 'SMG Ammo', image = 'smg_ammo.png' },
ammo_rifle = { type = 'ammo', ammo = 'rifle', amount = 50, tier = 1, label = 'Rifle Ammo', image = 'rifle_ammo.png' },
ammo_shotgun = { type = 'ammo', ammo = 'shotgun', amount = 14, tier = 1, label = 'Shells', image = 'shotgun_ammo.png' },
ammo_sniper = { type = 'ammo', ammo = 'sniper', amount = 12, tier = 2, label = 'Sniper Rounds', image = 'mg_ammo.png' },
ammo_rocket = { type = 'ammo', ammo = 'rocket', amount = 2, tier = 5, label = 'Rockets', image = 'weapon_rpg.png' },
-- Healing. heal = HP restored, cap = will not heal past this, time = seconds to use.
bandage = { type = 'heal', heal = 20, cap = 75, time = 3, amount = 3, tier = 1, label = 'Bandage', image = 'bandage.png' },
firstaid = { type = 'heal', heal = 75, cap = 100, time = 5, amount = 1, tier = 2, label = 'First Aid Kit', image = 'firstaid.png' },
medkit = { type = 'heal', heal = 100, cap = 100, time = 7, amount = 1, tier = 3, label = 'Med Kit', image = 'medkit.png' },
-- Boosts heal over time (regen HP/s for duration s); speed = sprint multiplier while active.
energy = { type = 'boost', regen = 1.5, duration = 25, speed = 1.12, time = 2, amount = 2, tier = 1, label = 'Energy Drink', image = 'kurkakola.png' },
painkillers = { type = 'boost', regen = 3, duration = 20, time = 4, amount = 1, tier = 2, label = 'Painkillers', image = 'painkillers.png' },
-- Armour plate: restores vest durability (needs a vest).
plate = { type = 'plate', armour = 34, time = 2, amount = 2, tier = 2, label = 'Armour Plate', image = 'armor_plate.png' },
-- Vests (reduce body damage while durable), helmets (reduce head damage), packs (carry limits)
vest1 = { type = 'armor', level = 1, tier = 2, label = 'Light Vest', image = 'armor.png' },
vest2 = { type = 'armor', level = 2, tier = 3, label = 'Tactical Vest', image = 'armor.png' },
vest3 = { type = 'armor', level = 3, tier = 4, label = 'Heavy Vest', image = 'heavyarmor.png' },
helmet1 = { type = 'helmet', level = 1, tier = 2, label = 'Light Helmet' },
helmet2 = { type = 'helmet', level = 2, tier = 3, label = 'Combat Helmet' },
helmet3 = { type = 'helmet', level = 3, tier = 4, label = 'Spec-Ops Helmet' },
bag1 = { type = 'backpack', level = 1, tier = 2, label = 'Small Pack', image = 'backpack_small.png' },
bag2 = { type = 'backpack', level = 2, tier = 3, label = 'Field Pack', image = 'backpack_medium.png' },
bag3 = { type = 'backpack', level = 3, tier = 4, label = 'Raid Pack', image = 'backpack_large.png' },
-- Attachments: one per kind, auto-fitted to every weapon that takes it.
att_reddot = { type = 'attach', kind = 'scope', level = 1, tier = 2, label = 'Red Dot Sight', image = 'holoscope_attachment.png' },
att_scope = { type = 'attach', kind = 'scope', level = 2, tier = 3, label = '4x Scope', image = 'medscope_attachment.png' },
att_suppressor = { type = 'attach', kind = 'muzzle', level = 1, tier = 3, label = 'Suppressor', image = 'suppressor_attachment.png' },
att_grip = { type = 'attach', kind = 'grip', level = 1, tier = 2, label = 'Vertical Grip', image = 'grip_attachment.png' },
att_extmag = { type = 'attach', kind = 'mag', level = 1, tier = 2, label = 'Extended Mag', image = 'clip_attachment.png' },
att_drum = { type = 'attach', kind = 'mag', level = 2, tier = 4, label = 'Drum Mag', image = 'drum_attachment.png' },
att_flashlight = { type = 'attach', kind = 'light', level = 1, tier = 1, label = 'Flashlight', image = 'flashlight_attachment.png' },
}
-- Which GTA components each attachment level tries, first match wins
-- (checked with DoesWeaponTakeWeaponComponent). Mags are per-weapon:
-- COMPONENT_<NAME>_CLIP_02 / _CLIP_03 are tried automatically.
Loot.components = {
scope = {
[1] = { 'COMPONENT_AT_SCOPE_MACRO', 'COMPONENT_AT_SCOPE_MACRO_02', 'COMPONENT_AT_PI_RAIL' },
[2] = { 'COMPONENT_AT_SCOPE_MEDIUM', 'COMPONENT_AT_SCOPE_SMALL', 'COMPONENT_AT_SCOPE_SMALL_02', 'COMPONENT_AT_SCOPE_MACRO', 'COMPONENT_AT_SCOPE_MACRO_02' },
},
muzzle = { [1] = { 'COMPONENT_AT_PI_SUPP', 'COMPONENT_AT_PI_SUPP_02', 'COMPONENT_AT_AR_SUPP', 'COMPONENT_AT_AR_SUPP_02', 'COMPONENT_AT_SR_SUPP', 'COMPONENT_AT_SR_SUPP_03' } },
grip = { [1] = { 'COMPONENT_AT_AR_AFGRIP' } },
light = { [1] = { 'COMPONENT_AT_PI_FLSH', 'COMPONENT_AT_AR_FLSH' } },
}
-- Carry limits by backpack level (0 = none).
Loot.capacity = {
[0] = { ammo = 150, bandage = 8, firstaid = 2, medkit = 1, energy = 3, painkillers = 2, plate = 3, throw = 3 },
[1] = { ammo = 210, bandage = 12, firstaid = 3, medkit = 2, energy = 4, painkillers = 3, plate = 5, throw = 4 },
[2] = { ammo = 280, bandage = 16, firstaid = 4, medkit = 3, energy = 5, painkillers = 4, plate = 7, throw = 5 },
[3] = { ammo = 360, bandage = 20, firstaid = 5, medkit = 4, energy = 6, painkillers = 5, plate = 9, throw = 6 },
}
Loot.ammoCapScale = { pistol = 1.0, smg = 1.25, rifle = 1.0, shotgun = 0.3, sniper = 0.2, rocket = 0.03 }
-- Ammo handed out with a freshly picked-up weapon.
Loot.startAmmo = { pistol = 36, smg = 60, rifle = 45, shotgun = 12, sniper = 8, rocket = 1 }
-- Weighted pools. A weapon roll also drops a matching ammo stack beside it.
Loot.pools = {
ground = {
pistol = 7, combatpistol = 5, heavypistol = 2.5, revolver = 0.8,
microsmg = 6, smg = 5, assaultsmg = 2,
sawnoff = 4, pumpshotgun = 5, assaultshotgun = 0.8,
compactrifle = 4, assaultrifle = 4.5, carbinerifle = 2.5, specialcarbine = 0.9,
marksmanrifle = 1.5, sniperrifle = 0.7,
knife = 1.5, bat = 1.5, machete = 1,
smoke = 2, molotov = 2, grenade = 2, stickybomb = 0.4,
ammo_pistol = 8, ammo_smg = 8, ammo_rifle = 9, ammo_shotgun = 6, ammo_sniper = 3,
bandage = 11, firstaid = 5, medkit = 1.5, energy = 7, painkillers = 5, plate = 7,
vest1 = 5, vest2 = 2.5, vest3 = 0.7,
helmet1 = 5, helmet2 = 2.5, helmet3 = 0.7,
bag1 = 4, bag2 = 2, bag3 = 0.6,
att_reddot = 3, att_scope = 1.5, att_suppressor = 1.5, att_grip = 2.5, att_extmag = 3, att_drum = 0.6, att_flashlight = 2,
},
airdrop = {
heavysniper = 3, rpg = 1.5, specialcarbine = 3, assaultshotgun = 2, revolver = 2,
vest3 = 3, helmet3 = 3, bag3 = 2, medkit = 3, plate = 2, stickybomb = 2,
att_scope = 2, att_drum = 2, att_suppressor = 2, ammo_sniper = 2, ammo_rocket = 1,
},
}
-- Bot loadouts by skill index (Config.Bots.skills). One primary + one sidearm.
Loot.botLoadouts = {
{ primary = { 'microsmg', 'sawnoff', 'pumpshotgun', 'compactrifle' }, side = { 'pistol' } },
{ primary = { 'smg', 'pumpshotgun', 'assaultrifle', 'compactrifle' }, side = { 'pistol', 'combatpistol' } },
{ primary = { 'carbinerifle', 'assaultsmg', 'specialcarbine', 'marksmanrifle' }, side = { 'heavypistol' } },
}
-- World props for loot that is not a weapon (weapons use their own world model).
Loot.props = {
ammo = 'prop_ld_ammo_pack_01',
heal = 'prop_ld_health_pack',
boost = 'prop_cs_pills',
plate = 'prop_ld_armour',
armor = 'prop_armour_pickup',
helmet = 'ba_prop_battle_sports_helmet',
backpack = 'p_michael_backpack_s',
attach = 'prop_ld_case_01',
airdrop = 'prop_drop_armscrate_01',
chute = 'p_cargo_chute_s',
}
-- Per-item prop overrides.
Loot.itemProps = { energy = 'prop_energy_drink' }config/maps.lua
lua
-- ============================================================================
-- Arenas. Every map here is part of the base game (no download, no licence):
-- the circle, loot and vehicles are placed at runtime, so a map is only a
-- centre, a radius and some named locations.
--
-- center/radius playable circle (x, y) in metres
-- warmup staging point for the pre-match warm-up (ground Z found at runtime)
-- pois named drops: loot clusters there and late circles prefer them
-- (keeps the final circle on land). loot = item spots in that POI.
-- scatter extra loot spots spread over the whole map
-- vehicles vehicles placed near POIs
-- island 'HeistIsland' loads Cayo Perico for match players only
--
-- To add a map: stand at a spot, /brdebug coords (Config.Debug = true) prints it.
-- ============================================================================
Config.Maps = {
{
id = 'cayo',
label = 'Cayo Perico',
description = 'Private island: airstrip, compound, jungle and docks.',
center = vec2(4900.0, -5150.0), radius = 1050.0,
warmup = vec3(4455.0, -4480.0, 4.2),
island = 'HeistIsland',
weather = 'EXTRASUNNY', hour = 15,
vehicleModels = {
{ model = 'winky', type = 'automobile' }, { model = 'manchez2', type = 'bike' },
{ model = 'squaddie', type = 'automobile' }, { model = 'vetir', type = 'automobile' },
},
scatter = 120, vehicles = 10,
pois = {
{ label = 'Airstrip', coords = vec2(4460.0, -4500.0), r = 110.0, loot = 26 },
{ label = 'El Rubio Compound', coords = vec2(5010.0, -5750.0), r = 120.0, loot = 40 },
{ label = 'Main Dock', coords = vec2(4900.0, -5180.0), r = 90.0, loot = 22 },
{ label = 'North Dock', coords = vec2(5130.0, -4640.0), r = 80.0, loot = 18 },
{ label = 'Radio Tower', coords = vec2(5260.0, -5420.0), r = 70.0, loot = 14 },
{ label = 'Crop Fields', coords = vec2(5330.0, -5260.0), r = 100.0, loot = 18 },
{ label = 'West Beach', coords = vec2(4290.0, -4580.0), r = 90.0, loot = 12 },
{ label = 'Jungle Ruins', coords = vec2(5150.0, -5050.0), r = 90.0, loot = 14 },
{ label = 'Drainage Outfall', coords = vec2(5045.0, -5815.0), r = 45.0, loot = 8 },
},
},
{
id = 'sandy',
label = 'Grand Senora',
description = 'Desert open ground: Sandy Shores, airfield, prison and Harmony.',
center = vec2(1400.0, 3300.0), radius = 1250.0,
warmup = vec3(1720.0, 3255.0, 41.0),
weather = 'CLEAR', hour = 17,
scatter = 140, vehicles = 14,
pois = {
{ label = 'Sandy Shores', coords = vec2(1860.0, 3700.0), r = 160.0, loot = 40 },
{ label = 'Sandy Airfield', coords = vec2(1720.0, 3260.0), r = 120.0, loot = 26 },
{ label = 'Bolingbroke', coords = vec2(1690.0, 2600.0), r = 150.0, loot = 34 },
{ label = 'Harmony', coords = vec2(590.0, 2730.0), r = 110.0, loot = 24 },
{ label = 'Yellow Jack', coords = vec2(1990.0, 3050.0), r = 60.0, loot = 10 },
{ label = 'Senora Scrapyard',coords = vec2(2400.0, 3110.0), r = 90.0, loot = 18 },
{ label = 'Alamo Shore', coords = vec2(1300.0, 3650.0), r = 100.0, loot = 14 },
},
},
{
id = 'paleto',
label = 'Paleto Bay',
description = 'Small town, forest and the sawmill. Close-quarters fights.',
center = vec2(-300.0, 6000.0), radius = 850.0,
warmup = vec3(-560.0, 5330.0, 70.0),
weather = 'OVERCAST', hour = 12,
scatter = 90, vehicles = 10,
pois = {
{ label = 'Paleto Town', coords = vec2(-150.0, 6380.0), r = 170.0, loot = 44 },
{ label = 'Sawmill', coords = vec2(-560.0, 5320.0), r = 110.0, loot = 26 },
{ label = 'Bay Care', coords = vec2(-250.0, 6320.0), r = 60.0, loot = 12 },
{ label = 'Chicken Farm', coords = vec2(-80.0, 6220.0), r = 90.0, loot = 20 },
{ label = 'Paleto Forest', coords = vec2(-700.0, 5800.0), r = 150.0, loot = 14 },
},
},
{
id = 'port',
label = 'Port of Los Santos',
description = 'Containers, cranes and warehouses. Tight lines of sight.',
center = vec2(950.0, -3050.0), radius = 650.0,
warmup = vec3(850.0, -2950.0, 6.0),
weather = 'SMOG', hour = 19,
scatter = 90, vehicles = 8,
pois = {
{ label = 'Terminal', coords = vec2(1100.0, -3100.0), r = 140.0, loot = 36 },
{ label = 'Container Yard', coords = vec2(1000.0, -3220.0), r = 110.0, loot = 30 },
{ label = 'Crane Row', coords = vec2(800.0, -3000.0), r = 100.0, loot = 24 },
{ label = 'Elysian Island', coords = vec2(470.0, -2850.0), r = 130.0, loot = 28 },
},
},
{
id = 'zancudo',
label = 'Fort Zancudo',
description = 'Military base: hangars, runway and the control tower.',
center = vec2(-2250.0, 3150.0), radius = 800.0,
warmup = vec3(-1820.0, 2960.0, 32.8),
weather = 'CLEAR', hour = 9,
scatter = 100, vehicles = 10,
vehicleModels = {
{ model = 'crusader', type = 'automobile' }, { model = 'mesa3', type = 'automobile' },
{ model = 'sanchez', type = 'bike' },
},
pois = {
{ label = 'Hangars', coords = vec2(-1850.0, 3000.0), r = 140.0, loot = 36 },
{ label = 'Control Tower', coords = vec2(-2360.0, 3250.0), r = 90.0, loot = 22 },
{ label = 'Runway', coords = vec2(-2300.0, 3050.0), r = 160.0, loot = 18 },
{ label = 'Barracks', coords = vec2(-2100.0, 3260.0), r = 100.0, loot = 26 },
{ label = 'Lago Zancudo', coords = vec2(-2600.0, 2900.0), r = 110.0, loot = 12 },
},
},
}