Configuration
Configuration is split across configs/, scoped by folder. configs/shared/ is read by both the client and the server, configs/client/ is client-only, and configs/server/ is excluded from the manifest's files{} so it never reaches a client.
Why the split matters
Drop coordinates, the radio-mast pool, the prop-to-item loot map and the whole fence economy live in configs/server/. Moving any of them into a shared file would make them dumpable by a client.
Root
configs/config.lua
Locale = 'en',
Debug = true,
DebugPrints = false,| Key | Type | Default | Description |
|---|---|---|---|
Locale | string | 'en' | Loads locales/<Locale>.json, falling back to English per key |
Debug | boolean | true | Developer logging and target zone outlines |
DebugPrints | boolean | false | Very verbose per-call prints from the framework, inventory and target bridge |
DebugCategories | table | commented out | Filters bridge prints by tag, e.g. { ['bridge:inventory'] = true } |
DANGER
Debug ships as true. Set it to false on a production server.
Intrusion
configs/shared/trigger.lua
This is the block that defines the co-op shape of the heist.
Intrusion = {
Flush = 300,
Required = 2,
Defenses = 2,
Cooldown = 30,
CoopBreach = false,
TriangulateWindow = 15,
Triangulate = 8,
},| Key | Type | Default | Description |
|---|---|---|---|
Flush | number | 300 | Seconds from the first intrusion before the network flushes everyone off the towers. One shared window for the whole crew |
Required | number | 2 | Relays that must be fully breached inside that window to call a drop |
Defenses | number | 2 | Defence layers to crack per relay. Each layer is one successful daemon run |
Cooldown | number | 30 | Seconds a daemon is benched after a failed run before the same player can retry it |
CoopBreach | boolean | false | true lets several players pool progress on the same relay. false means one intruder per relay |
TriangulateWindow | number | 15 | Seconds for the whole crew to run calldrop together |
Triangulate | number | 8 | Duration of the shared triangulation bar. Closing the terminal aborts it for everyone |
Tuning for your playerbase
Required and CoopBreach are the two dials that set crew size. Leaving CoopBreach off and raising Required to 3 means three players in three different places. Setting CoopBreach = true lets a smaller crew share a mast, and Required = 1 makes the heist soloable.
Masts
Prop = 'prop_radiomast02',
SpawnDistance = 500.0,
Blip = { enabled = true, sprite = 866, color = 1, scale = 0.8, shortRange = true, display = 4, alpha = 255 },| Key | Type | Default | Description |
|---|---|---|---|
Prop | string | 'prop_radiomast02' | Mast prop spawned at each tower |
SpawnDistance | number | 500.0 | Metres at which a mast streams in, so the whole set never loads at once |
Blip.enabled | boolean | true | Master switch for mast blips |
Blip.shortRange | boolean | true | false makes masts visible across the whole map |
The candidate tower positions themselves are server-only, in configs/server/trigger.lua.
USB Kits
Tablet = {
StashSlots = 8,
StashWeight = 4000,
USBHacks = {
deaddrop_usb_1 = { 'maze', 'cipher' },
deaddrop_usb_2 = { 'capture', 'typing' },
deaddrop_usb_3 = { 'evasion', 'signal', 'pipe' },
deaddrop_usb_4 = { 'snake', 'firewall', 'breakout' },
},
Durability = {
Hack = { Enabled = true, Cost = 10 },
Practice = { Enabled = true, Cost = 5 },
RemoveAtZero = false,
},
},| Key | Type | Default | Description |
|---|---|---|---|
StashSlots | number | 8 | Slots in the tablet's USB storage |
StashWeight | number | 4000 | Capacity of that stash in grams |
USBHacks | table | 4 kits | Which daemon modules each USB item provides while inserted |
Durability.Hack.Cost | number | 10 | Durability spent per daemon run against a relay |
Durability.Practice.Cost | number | 5 | Durability spent per practice run |
Durability.RemoveAtZero | boolean | false | true deletes a stick worn to 0 instead of leaving it fried in the stash |
Keep item descriptions in sync
Which daemons a USB provides is config, not a property of the item. If you retune USBHacks, update the matching item description in your inventory so players are not told the wrong thing.
Proof of Play
Proof = {
MinFloor = 3.0,
Grace = 2.0,
Safety = 0.85,
},| Key | Type | Default | Description |
|---|---|---|---|
MinFloor | number | 3.0 | No daemon is ever accepted faster than this, whatever the per-game floor works out to |
Grace | number | 2.0 | Survival games use timeLimit - Grace as their floor |
Safety | number | 0.85 | Throughput floors are multiplied by this, so lag never rejects a genuine run |
Per-game tuning keys sit under the same block. The floors are derived from each minigame's own difficulty settings, so raising a minigame's difficulty raises its floor automatically.
Event Pacing
configs/shared/event.lua
PublicDropIntel = false,
Containers = { Min = 2, Max = 3, MinDistance = 500 },
Phases = {
LeadSeconds = 200,
FlightSeconds = 120,
DropSeconds = 110,
SettleSeconds = 15,
ResolveSeconds = 8,
},| Key | Type | Default | Description |
|---|---|---|---|
PublicDropIntel | boolean | false | false marks the search area only for the triggering crew and snitch buyers. true marks it for everyone |
Containers.Min / Max | number | 2 / 3 | Containers per drop, rolled between the two |
Containers.MinDistance | number | 500 | Minimum metres between any two landing spots |
Phases.LeadSeconds | number | 200 | Seconds from the signal going out to the plane launching |
Phases.FlightSeconds | number | 120 | Corridor flight time to the drop point |
Phases.DropSeconds | number | 110 | Package glide time. Fall speed follows from this and the drop altitude |
Phases.SettleSeconds | number | 15 | Dwell on the ground before the contest clock arms. Breaching is already allowed |
ContestSeconds | number | 1200 | Maximum contested lifetime before the event abandons itself |
CooldownMinutes | number | 30 | Global lockout after a resolve. Relays report "high security" until it elapses |
Breach
Breach = {
BreachTime = 10000,
RequiredItem = 'powersaw',
RequiredItemLabel = 'Power Saw',
NameRequiredItem = false,
Wear = { Mode = 'durability', Durability = 10, BreakChance = 0.33, RemoveAtZero = false },
},| Key | Type | Default | Description |
|---|---|---|---|
BreachTime | number | 10000 | Milliseconds the breach scene runs. Matches the animation length |
RequiredItem | string | 'powersaw' | Tool required to breach, re-checked server-side. false requires no tool |
NameRequiredItem | boolean | false | true names the item in the refusal message |
Wear.Mode | string | 'durability' | One of 'off', 'durability', 'remove', 'chance' |
Wear.Durability | number | 10 | Durability points docked per breach in 'durability' mode |
Wear.BreakChance | number | 0.33 | Odds the tool breaks in 'chance' mode |
WARNING
Do not change BreachTime unless you are also changing the animation. The value matches the clip length.
The Drop
configs/shared/world.lua
Drop = {
Altitude = 700.0,
RenderDistance = 1000.0,
Release = { Behind = 8.0, Below = 4.0, Lead = 250.0 },
Sway = { Amplitude = 4.0, Speed = 700.0 },
Chute = { Count = 1, Height = 6.0, CutHeight = 5.0, FadeMs = 800 },
},
Wind = { Speed = 6.0, DriftScale = 1.0 },| Key | Type | Default | Description |
|---|---|---|---|
Drop.Altitude | number | 700.0 | Metres the plane cruises at, which is the height packages drop from |
Drop.RenderDistance | number | 1000.0 | Packages inside this range animate per frame. Beyond it they step every 250 ms |
Release.Lead | number | 250.0 | Metres before its landing point a package releases, so it glides in on a diagonal. 0 releases directly overhead |
Sway.Amplitude | number | 4.0 | Maximum pendulum tilt in degrees. 0 disables sway |
Chute.Count | number | 1 | Canopies per container |
Chute.CutHeight | number | 5.0 | Metres above ground the canopies are cut free |
Wind.Speed | number | 6.0 | Horizontal drift in m/s during descent. 0 falls straight down |
Drop Intel
DropBlip = { radius = 250.0, offset = 110.0, color = 1, alpha = 80 },| Key | Type | Default | Description |
|---|---|---|---|
radius | number | 250.0 | Minimum search-circle radius, grown to enclose a wide cluster |
offset | number | 110.0 | How far off-centre the circle sits from the real cluster |
TIP
The offset is deliberate. It marks an area to search rather than pinpointing the crates.
Flight corridors and the curated landing spots are server-only, in configs/server/world.lua.
Containers and Carry
configs/shared/containers.lua
Carry = {
Enabled = true,
OneAtATime = true,
Props = { ['deaddrop_item_1'] = 'prop_mil_crate_01', ... },
},| Key | Type | Default | Description |
|---|---|---|---|
Carry.Enabled | boolean | true | false disables the held prop, the animation and the control lock |
Carry.OneAtATime | boolean | true | At most one carry item per player. Handing a crate to someone else is still allowed |
Carry.Props | table | 12 entries | Maps each loot item to the prop held in the hands |
Carry.Offsets | table | per prop | Per-prop attachment overrides, tuned in-game with /deaddrop_carrytune |
WARNING
If you turn OneAtATime off, turn Enabled off too. Several held crates would still visibly haul only one, which reads wrong.
Layouts in the same file holds the cosmetic interior prop sets, one picked per container. Capture new ones in-game with /deaddrop_loadout.
Contact
configs/shared/contact.lua holds the display and the on/off switches. The economy is server-only in configs/server/contact.lua.
PedModel = 's_m_y_dealer_01',
GreetSpeech = 'GENERIC_HI',
Scenario = 'PROP_HUMAN_SEAT_CHAIR',
SpawnDistance = 50.0,| Key | Type | Default | Description |
|---|---|---|---|
PedModel | string | 's_m_y_dealer_01' | Contact ped model |
Scenario | string | 'PROP_HUMAN_SEAT_CHAIR' | Ambient scenario. false stands him frozen |
SpawnDistance | number | 50.0 | Metres at which the ped streams in |
Buy.Enabled | boolean | true | Whether the contact sells gear |
Selling.Enabled | boolean | true | false hides every sell option and the backend refuses the callbacks |
Selling.Trunk.Enabled | boolean | true | Sell a parked vehicle's whole trunk in one deal. ox_inventory only |
Snitch.Enabled | boolean | true | Whether drop locations can be bought |
Notify.Enabled | boolean | true | Whether drop-alert subscriptions can be bought |
Reputation.Rewards | boolean | true | Master switch for one-time level-up rewards |
Economy
configs/server/contact.lua
| Key | Default | Description |
|---|---|---|
PayoutType | 'clean' | 'clean' credits an account, 'dirty' pays an item |
CleanAccount | 'cash' | Account credited when paying clean |
ClaimDistance | 5.0 | Metres the player must be within for buy, snitch, notify and reward claims |
Heat.Max | 100 | The heat scale items are stamped on |
Heat.Floor | 0.25 | Never sell below this fraction of base value, however hot |
Snitch.MinLevel | 3 | Reputation level required to buy drop intel |
Snitch.Fee | 5000 | Flat fee per successful snitch |
Notify.MinLevel | 3 | Reputation level required to subscribe to drop alerts |
Notify.Fee | 10000 | One-time subscription fee |
Buy.Account | 'bank' | Account gear purchases come from |
Sellables sets the price and XP of every sellable item, Levels is the five-rung reputation ladder with its XP thresholds and reward items, and Catalog is the gear list with its per-item level gates.
Wiring drop alerts to your phone
Notify.OnDrop is a server-side hook called for every subscriber when a drop is called. Point it at your phone resource to deliver the ping however you like.
Heat
configs/server/heat.lua
Decay = { Amount = 10, Every = 300 },
StartingHeat = { Default = 60, Items = {} },| Key | Type | Default | Description |
|---|---|---|---|
Decay.Amount | number | 10 | Heat points removed per interval while the item is held |
Decay.Every | number | 300 | Seconds per decay tick. Either value at 0 disables decay |
StartingHeat.Default | number | 60 | Heat stamped on any item without its own entry |
StartingHeat.Items | table | empty | Per-item overrides |
Police and Rivals
configs/shared/pressure.lua
Police Response
Police = {
Enabled = true,
MinCops = 0,
DispatchEvent = 'sd-deaddrop:police:alert',
Title = 'Containers being breached',
Blip = { sprite = 161, color = 1, scale = 1.1, time = 300 },
},| Key | Type | Default | Description |
|---|---|---|---|
Enabled | boolean | true | Fires once when a container is first breached |
MinCops | number | 0 | 0 always alerts, leaving cop counting to your dispatch |
DispatchEvent | string | see above | Server event fired with { coords, title }. Bridge it to your dispatch, or set false to skip |
A broadcast map blip is drawn regardless of the dispatch event.
Rival Crew
Rivals = {
Enabled = false,
Min = 4,
Max = 6,
Radius = 120.0,
MinPlayerDistance = 100.0,
Loot = { Enabled = true },
},| Key | Type | Default | Description |
|---|---|---|---|
Enabled | boolean | false | Spawns a Merryweather recovery crew when the first container is breached |
Min / Max | number | 4 / 6 | Rivals per drop, rolled between the two |
Radius | number | 120.0 | Spawn scatter around the breached container |
MinPlayerDistance | number | 100.0 | Never spawn within this of any player |
Loot.Enabled | boolean | true | Whether dead rivals can be searched |
WARNING
Keep Radius above MinPlayerDistance, or no spawn point can ever qualify.
Archetypes defines each rival type with its weight, model, weapon, health, armour, accuracy and combat ability. criticalHits = false prevents one-shot headshots while letting headshots land as normal damage.
Minigames
configs/client/minigame.lua exposes every difficulty knob for all ten daemons. Keys match the module names shown by the terminal's games command.
| Module | Daemon | Main difficulty levers |
|---|---|---|
maze | net-maze.exe | width, height, visibility, dataNodes, timeLimit |
capture | node-grab.exe | gridSize, nodeDuration, capturesRequired, maxMisses |
cipher | cipher-lock.exe | timeLimit, speed, spawnRate, playerLives |
snake | data-worm.exe | nodesRequired, initialSpeed, grid size |
firewall | fw-defense.exe | rows, cols, waves, fireRate, shootCooldown |
evasion | highway-evade.exe | timeLimit, speed, spawnRate, roadWidth |
typing | code-inject.exe | stringLength, timePerString, stringsRequired |
signal | sig-replay.exe | sequenceLength, startLength, flashSpeed, inputWindow |
pipe | pipe-route.exe | gridWidth, gridHeight, timeLimit, rounds |
breakout | ice-break.exe | rows, cols, ballSpeed, paddleWidth |
Difficulty and anti-cheat move together
Proof-of-play floors are derived from these values, so raising a minigame's difficulty raises its accepted minimum completion time automatically. You do not need to retune the anti-cheat by hand.
Terminal
configs/client/terminal.lua
| Key | Type | Default | Description |
|---|---|---|---|
Watermark | boolean | true | Faded monogram behind the console text. false gives a plain background |
Banner.color | string | '#f14c4c' | Any CSS colour for the ASCII boot banner |
Banner.lines | table | ASCII art | Drawn top to bottom. Keep every line the same character width. Banner = false disables it |
Audit Logging
configs/server/logs.lua
Enabled = true,
Events = { event_start = true, breach = true, loot = true, fence_sell = true, ... },Logging is routed through ox_lib's logger, so the backend is whatever your ox:logger convar points at. Enabled = false disables all of it, and each event has its own switch. A missing key counts as on.
Notable events include suspect for server-side rejects that look like exploit attempts, usb_fried, defense_knock and admin_command.
Loot
configs/server/loot.lua holds the reveal-grid pools, grid sizes and reward tables. configs/server/containers.lua maps each prop model to the item it yields when looted, with DefaultItem as the fallback.
Entries accept three forms:
['prop_gold_bar'] = 'goldbar',
['prop_cash_case_01'] = { item = 'black_money', min = 10000, max = 15000 },
['prop_mil_crate_01'] = { item = 'deaddrop_item_1', count = 1 },WARNING
Keep the deaddrop_item_* carry crates at a plain 1x entry. Carry.OneAtATime expects a single crate per grab.
