Config
Explanations and examples for the current laptop configuration.
The laptop is configured in several files under zyke_laptop/shared/unlocked/ and one under zyke_laptop/server/unlocked/:
| File | Table | What it holds |
|---|---|---|
shared/unlocked/config.lua | Config.Settings | Every setting of the laptop itself, from the item and props to the apps and sites built into it. |
shared/unlocked/shop.lua | Config.Shop | The Digital Den shop: its counter, staff shelves, door locks, display laptops and everything it sells. |
shared/unlocked/hardware.lua | Config.Prebuilts | The laptops as they leave the factory, the parts each one is built with. |
shared/unlocked/hotspots.lua | Config.Hotspots | The public Wi-Fi hotspots on the map. |
server/unlocked/hotspots.lua | HotspotPasswords | The passwords of the secured hotspots. This file only runs on the server. |
shared/unlocked/examples.lua | Config.Examples | The example sites that ship with the laptop. |
shared/unlocked/casino/*.lua | Config.Casino | One file per casino game, plus the case catalogue and its items. |
Distances are metres. Durations are in the unit their key names, such as Ms for milliseconds, Seconds, Minutes, Hours or Days, unless the entry says otherwise. Sizes in bytes are bytes, shop prices are whole dollars and casino amounts are credits.
The interact keybind can be changed in GTA / FiveM settings. Framework, inventory, target, phone, language and permission settings live in zyke_lib. Discord logs are set per action in zyke_lib/webhooks/laptop.lua; an empty webhook sends nothing.
Rules of your own, such as keeping a hotspot for a job or refusing a placement spot, go in the hooks in client/unlocked/hooks.lua and server/unlocked/hooks.lua. See Can-Check Hooks.
#Full Default Config
#General
#debug
Registers the
laptop_holdcommand in the F8 console, which tries other hold poses live and prints the lines to paste intohandheld. Leave it off on a live server.Example:
luadebug = false
#item
Usable inventory item that takes the laptop out. Using it again puts the laptop away. The item definition is in
extras/items/.Set it to
falsefor no laptop item. Laptops then only open through exports such asOpenScreen, and each character has one laptop of their own, with its parts and ports kept in the server's KVP by its serial. Without a laptop item:
- desktop computers are off, they need the laptop item,
- the shop sells no laptops, refinishes or backplates,
- every skin is free to pick in Settings,
- only the character can pick up their own laptop once it is put down.
Example:
luaitem = "laptop"
#interaction
How players use a laptop or computer that is put down, browse the shop, open the staff shelves and read the spec signs of the display laptops.
"point"puts azyke_libinterest point on the middle of the keyboard, the shop keeper, the shelves and the signs. The mouse wheel picks an option and E does it."target"lists the same options in the target menu, throughox_targetorqb-target. Without either running it falls back to interest points and prints a warning in the F8 console.Example:
luainteraction = "point"
#clock
Time the OS clocks, lock screens, calendars and the terminal's
dateshow.
"game"follows the game's clock, as your time script sets it, frozen time included, on the real date. Every player sees the same day."real"shows each player's own computer time.Example:
luaclock = "game"
#renderDistance
Metres from a laptop other players still see it. The display laptops in the shop also only exist within this distance.
Example:
luarenderDistance = 60.0
#Models & Lid
#models.base
Prop of the laptop's base, the keyboard half. The finishes, ports and screen are built into the laptop's own props, so only change it for a replacement built the same way.
Example:
luabase = "zyke_laptop_base"
#models.lid
Prop of the laptop's lid. The same note applies as for
models.base.Example:
lualid = "zyke_laptop_lid"
#skins
The covers for the back of the lid, listed by category in the order Settings and the shop studio show them, after the stock lid. The default list has 115 skins in 12 categories:
solid,gradient,pattern,camo,material,animal,art,nature,space,tech,streetandretro.
- Delete an id to take that skin away. Laptops that had it show the stock lid again.
- Move ids or whole categories to reorder them. A category without ids is hidden.
- To add a skin, stream a model named
zyke_laptop_skin_<id>, put a<id>.webpthumbnail innui/skins/, add a"shell:skin:<id>"label to everylocales/*.luafile and list the id under a category. A new category needs a"shell:skinCategory:<id>"label too.The skin is stored in the laptop item's metadata, so it goes with the laptop. Whether players buy skins at the shop or pick them for free is set by
skins.boughtin Config.Shop.CanUseLaptopSkininserver/unlocked/hooks.luacan keep skins for a job or donors. See Using the Laptop and Shop.Example:
luaskins = { {category = "camo", skins = {"camo_urban", "camo_woodland", "camo_digital"}}, {category = "material", skins = {"carbon", "walnut", "marble"}}, }
#finishes
The anodised colours a laptop's chassis can have, next to the stock graphite:
silver,black,midnight,sky_blue,sage,gold,rose_gold,redandlavender. The colours are built into the props, so only these ids work.Players don't pick a finish themselves. A laptop gets one at the shop, or from another resource through the
SetLaptopFinishserver export. Delete an id to take that finish away; laptops that had it show graphite again, and the shop stops selling it.Example:
luafinishes = {"silver", "black", "midnight", "sky_blue", "sage", "gold", "rose_gold", "red", "lavender"}
#lid.openAngle
Degrees the lid opens to, measured from closed. The hold animation is made for the default of 110 degrees.
Example:
luaopenAngle = 110.0
#lid.openDuration
Milliseconds the lid takes to swing open.
Example:
luaopenDuration = 900
#lid.closeDuration
Milliseconds the lid takes to swing shut.
Example:
luacloseDuration = 650
#Handheld
Holding the open laptop in the left hand. The default animation is made for this laptop: the take out, the idle and the put away are one clip, played on the upper body only so the legs still walk, and it fits both freemode peds.
#handheld.anim
Animation played while the laptop is held.
flag = 50plays it on the upper body and keeps it looping.Example:
luaanim = {dict = "zyke_laptop_held", clip = "hold", flag = 50}
#handheld.bone
Ped bone the laptop is attached to, the left hand's prop bone.
Example:
luabone = 60309
#handheld.offset
Offset of the laptop from the bone, in metres. The bone's X runs to the fingertips. The default clip is made around this offset. With
debugon,laptop_holdtries other values live.Example:
luaoffset = vec3(0.1439, -0.0825, 0.0528)
#handheld.rotation
Rotation of the laptop from the bone, in degrees.
Example:
luarotation = vec3(-51.9, -40.65, -51.86)
#handheld.disableSprint
Keeps the player walking while they hold the laptop. Sprinting and jumping would break the pose.
Example:
luadisableSprint = true
#Placement
A laptop put down leaves the inventory and stays where it is, through disconnects and resource and server restarts, until someone picks it up. It is kept in the zyke_laptop_placed table, created on start, and comes back powered off, charged for the time it stood there. Anyone can use a placed laptop; its account's password is what keeps it safe. See Using the Laptop.
#placement.maxDistance
Metres from the player a laptop can be put down.
Example:
luamaxDistance = 2.0
#placement.maxPerCharacter
Laptops one character can have put down at once, outside the placement areas.
falsefor no limit.Example:
luamaxPerCharacter = 30
#placement.areas
Places with a laptop limit of their own, such as a server room in an MLO, a warehouse or a housing interior. Laptops put down in an area count only against that area's limit, never against
maxPerCharacteror another area's. A character can fill a server room with a hundred and still put thirty down elsewhere. The first area that matches is used.
Field Description idUnique text of up to 64 characters. It is stored with each laptop put down in the area. coordsCentre of the sphere or box, a vec3.radiusMakes the area a sphere of this many metres around coords.sizeMakes the area a box of this size in metres, a vec3, for a room.headingOptional turn of the box in degrees. bucketsOptional list of routing buckets the area is limited to, any bucket when left out. An area with bucketsand no shape covers the whole of those buckets, for housing that puts each property in a bucket of its own.limitLaptops one character may have put down in the area, falseor left out for no limit.The server can't see interiors, so an MLO's room is matched by its coordinates. Laptops only stay put down across restarts in
persistentBuckets, so add an area's buckets there too. An area with a broken entry is left out with a warning in the server console.For your own rules, such as asking a housing or mining script, replace
GetLaptopPlacementArea(plyId, coords, bucket)inserver/unlocked/hooks.lua. It returns the area id and its limit. See Can-Check Hooks.Example:
luaareas = { {id = "serverroom", coords = vec3(1003.0, -3108.0, -39.0), size = vec3(20.0, 12.0, 6.0), limit = 100}, {id = "warehouse", coords = vec3(-80.0, -1800.0, 28.0), radius = 25.0, limit = 60}, {id = "apartments", buckets = {1001, 1002, 1003}, limit = 50}, }
#placement.ownerOnlyPickUp
Only the laptop's owner can pick it up, or the character who put it down when it has no owner. Off, anyone can carry a laptop off, as the recovery toolkit expects of stolen laptops. Without a laptop
itemonly the owner can, either way. See Ownership & Recovery.Example:
luaownerOnlyPickUp = false
#placement.removeAfterDays
Days a placed laptop nobody used or opened stays before it is removed, with its item and whatever is plugged into it. Each removal prints a line in the server console with the laptop's serial, owner, place and item metadata, enough to give it back. Checked on start and every hour. Desktop computers are never removed this way.
falsekeeps laptops until someone picks them up.Example:
luaremoveAfterDays = false
#placement.persistentBuckets
Routing buckets laptops stay put down in across restarts. Housing scripts hand out instance buckets whose numbers are reused, so a laptop left in one could stand in a stranger's instance later.
A laptop put down in any other bucket stays only while the character who put it down is in that bucket, and the player is told it is temporary. Once they leave or disconnect, or after a restart, its item goes back into their inventory, at their next login when they are offline. Desktop computers follow the same rule.
Add the buckets your housing keeps per property, or decide per bucket in
IsLaptopBucketPersistent(bucket)inserver/unlocked/hooks.lua.Example:
luapersistentBuckets = {0}
#placement.clearProps.permission
Who clears map props from around a laptop put down or a computer set up, such as the monitor, keyboard and mouse already on a police station desk. An ACE permission or ESX group,
truefor everyone,falsefor nobody. While the spot is picked the props that will go are outlined, and they stay hidden for everyone until the laptop is picked up or the computer removed. SetclearProps = falseto turn clearing off altogether.Example:
luapermission = "command"
#placement.clearProps.radius
Metres around the spot the listed props are cleared from.
Example:
luaradius = 1.0
#placement.clearProps.models
Map prop models that are cleared. Only these go; the laptop can still stand on any other prop, such as the desk. The default list has 101 models in five groups: monitors, keyboards, mice and mouse mats, computer towers, and laptops. Add the models of an MLO's own desk equipment to clear those too.
Example:
luamodels = { -- Monitors "prop_monitor_01a", "prop_monitor_01b", "prop_monitor_02", -- Keyboards "prop_keyboard_01a", "prop_keyboard_01b", -- Mice and mouse mats "prop_mouse_01", "v_res_mousemat", }
#placement.maxSlope
Degrees a surface may tilt for the laptop to stand on it.
Example:
luamaxSlope = 12.0
#placement.lowHeight
Metres above the player's feet that count as the floor. A laptop or computer below it uses the low animations to put down and pick up, and the player kneels to use it.
Example:
lualowHeight = 0.5
#placement.lowAnim
Animation for putting a laptop or computer down on the floor.
durationis in milliseconds.Example:
lualowAnim = {dict = "pickup_object", clip = "putdown_low", duration = 1000}
#placement.highAnim
Animation for putting a laptop or computer down at table height.
Example:
luahighAnim = {dict = "mp_common", clip = "givetake1_a", duration = 900}
#placement.lowPickupAnim
Animation for picking a laptop up from the floor, or removing a computer there.
Example:
lualowPickupAnim = {dict = "pickup_object", clip = "pickup_low", duration = 1000}
#placement.highPickupAnim
Animation for picking a laptop up from table height, or removing a computer there.
Example:
luahighPickupAnim = {dict = "mp_common", clip = "givetake1_a", duration = 900}
#Use
#use.distance
Metres from a placed laptop's keyboard the player can use it, open its lid or ports, service it and pick it up.
Example:
luadistance = 1.5
#use.standOffset
Where the player stands in front of a placed laptop while using it, in the laptop's own space. Use laptop walks the player there.
Example:
luastandOffset = vec3(0.0, -0.62, 0.0)
#use.standingAnim
Animation played while the player uses a laptop at table height.
Example:
luastandingAnim = {dict = "anim@heists@prison_heiststation@cop_reactions", clip = "cop_b_idle", flag = 1}
#use.kneelingAnim
Animation played while the player uses a laptop on the floor, below
placement.lowHeight.Example:
luakneelingAnim = {dict = "amb@medic@standing@tendtodead@idle_a", clip = "idle_a", flag = 1}
#Desktop Computer
The desktop computer is a display with a keyboard and mouse that stays where it is set up, for an office or a business. It runs every OS a laptop does, on mains power, so it never needs charging and its parts never wear. It is kept in zyke_laptop_placed like a laptop put down. See Using the Laptop.
#computer.enabled
Enables desktop computers. They also need the laptop
itemandcomputer.item; without either they stay off and the server console says so.Example:
luaenabled = true
#computer.item
Usable item that sets a computer up where the player picks. Its hardware, OS and devices are kept in the item's metadata, so removing the computer gives the same item back with everything on it.
Example:
luaitem = "desktop_computer"
#computer.jobs
Jobs that may set a computer up, as job names or
{name = "police", minGrade = 2}.false, the default, lets anyone.CanPlaceComputerinserver/unlocked/hooks.luadecides further.Example:
luajobs = {"police", {name = "mechanic", minGrade = 2}}
#computer.maxPerCharacter
Computers one character can have set up at once.
falsefor no limit.Example:
luamaxPerCharacter = 3
#computer.staffPermission
ACE permission, or ESX group, of the staff who may remove anyone's computer.
falsefor nobody besides the character who set it up. Staff with the ACEcommand.laptop_computerset computers up without an item with/laptop_computer [player id], see Commands.Example:
luastaffPermission = "command"
#computer.prebuilt
Prebuilt from
Config.Prebuiltsa new computer is built as. The defaultdesktophas no battery.Example:
luaprebuilt = "desktop"
#Ports
The ports on the laptop's sides: two USB-C ports and an HDMI port on the left, a USB-A port and an audio jack on the right. A plugged device stays in the laptop, saved in the laptop item's metadata, until someone pulls it out. Other resources add devices with the RegisterDeviceType export. See Ports & Devices.
#ports.others
Lets players other than the character who put a laptop down plug devices into it and pull them out, as with a real laptop.
CanPlugLaptopDeviceandCanUnplugLaptopDeviceinserver/unlocked/hooks.lualimit it further.Example:
luaothers = true
#ports.anim
Animation played while a device is plugged in or pulled out. The server is asked halfway through.
Example:
luaanim = {dict = "mp_common", clip = "givetake1_a", duration = 900}
#ports.dataStick
The data stick players keep files on. Its files are stored on the server by the stick's serial and go wherever the item goes. Creates the
zyke_laptop_stickstable on start. Set tofalsefor no data stick.
Field Description itemThe stick's item. modelProp shown in the laptop's USB-A port. maxBytesBytes its files may take encoded, checked on the server. Every save sends them all to the server, so keep it under 327680. capacitySize in bytes shown to players, 8 GB by default. The free space shown is scaled to it. Example:
luadataStick = {item = "usb_drive", model = "zyke_laptop_usb_blank", maxBytes = 131072, capacity = 8589934592}
#ports.powerBank
The power bank. Plugged into a USB-C port of a laptop in the hands it powers the laptop and charges its battery until the bank runs flat, also while the laptop is put away with it in. Plugged into a laptop put down, it fills from mains through the laptop. Set to
falsefor none, and take it out of the shop's accessories too.Its charge is kept in the item's metadata as
charge, from 0 to 1, and a bank without one is full. Each bank gets aserial, such asPB-7F3K-92QD,durabilityfollows its charge from 0 to 100 for the inventory's durability bar, anddescriptionshows its capacity and charge.
Field Description itemThe bank's item. whWatt-hours it holds. A 20,000 mAh bank holds about 72. chargeMinutesMinutes it takes to charge an empty laptop battery, slower than mains ( hardware.wear.chargeMinutes).fillMinutesMinutes a laptop put down takes to fill an empty bank. efficiencyShare of the bank's energy that reaches the laptop. Example:
luapowerBank = {item = "laptop_powerbank", wh = 72, chargeMinutes = 35, fillMinutes = 45, efficiency = 0.85}
#Hardware
The parts inside every laptop: a mainboard with its cooler, two memory sockets, an SSD, a Wi-Fi card and a battery. Each part is the exact part item, kept in the laptop item's metadata as hardware, or in the server's KVP by the laptop's serial without a laptop item. Laptops come as one of the prebuilts. See Hardware & Servicing for the parts, what they do and the service view.
#hardware.default
Prebuilt a laptop item without hardware becomes the first time it is taken out, such as one given without metadata. Shops give the others with the
CreateLaptopMetadataexport.Example:
luadefault = "basic"
#hardware.migrated
Prebuilt that laptops from before hardware get on their next take-out. Their drive is kept as the SSD, and their serial, OS, skin, finish and ports stay as they were. The default
legacyhas the specs those laptops always showed.Example:
luamigrated = "legacy"
#hardware.items
One item per kind of part. The exact model is in the item's
metadata.model. Rename them to match your inventory; the definitions are inextras/items/.Example:
luaitems = {board = "laptop_mainboard", cooler = "laptop_cooler", ram = "laptop_ram", ssd = "laptop_ssd", wifi = "laptop_wifi", battery = "laptop_battery"}
#hardware.used
Parts the
CreatePartMetadataexport makes withused = true: a random condition betweenminConditionandmaxCondition, 1 being as new, and memory that is faultyfaultChanceof the time. A fault stays hidden until the stick is tested.Example:
luaused = {minCondition = 0.55, maxCondition = 0.9, faultChance = 0.15}
#hardware.leads
Hotspot range with 0, 1 or 2 of the Wi-Fi card's antenna leads plugged in, times the hotspot's own range. A Wi-Fi 7 card reaches a tenth further on top.
Example:
lualeads = {[0] = 0.25, [1] = 0.6, [2] = 1.0}
#hardware.needsBattery
Whether laptops need their battery. Set to
falseand a laptop in the hands runs and charges as if plugged in: its battery never drains, a laptop without a battery still powers on, and a power bank in it is left alone. Nothing needs the power bank then, so take it out of the shop's accessories as well.Example:
luaneedsBattery = true
#hardware.wear.dustHours
Powered hours until the fan is fully clogged with dust. Powered hours count while an OS runs on the laptop. Dust and dried paste make a laptop run hot, which slows it down.
Example:
luadustHours = 40
#hardware.wear.pasteDays
Days thermal paste takes to dry from new down to
pasteFloor, counted by the server's clock whether the laptop runs or not.Example:
luapasteDays = 30
#hardware.wear.padDays
Days a phase-change pad, on the coolers that have one, takes to dry down to
pasteFloor.Example:
luapadDays = 90
#hardware.wear.pasteFloor
Lowest the paste dries to, from 0 to 1.
Example:
luapasteFloor = 0.3
#hardware.wear.batteryMinutes
Minutes a full battery at full health lasts in the hands; a worn battery lasts as much less as it lost. It only drains while the laptop is powered on, so the default of 12 hours means a charge about every 12 hours of near constant use, and a laptop that is barely used can go days without one.
Example:
luabatteryMinutes = 720
#hardware.wear.chargeMinutes
Minutes an empty battery takes to charge full while the laptop is put down.
Example:
luachargeMinutes = 20
#hardware.wear.healthPerCycle
Battery health lost per full charge cycle.
Example:
luahealthPerCycle = 0.002
#hardware.wear.minHealth
Health a battery never drops under.
Example:
luaminHealth = 0.5
#hardware.wear.ramFaultChance
Chance each memory stick turns faulty per 10 powered hours. An SSD wears out over the powered hours its model is rated for and then turns read-only; that is set per model in the parts catalogue, not here.
Example:
luaramFaultChance = 0.005
#hardware.service.enabled
Enables the service view, Service in a placed laptop's options: the laptop turns over for its bottom screws, then opens up so parts come out and go in, the fan is cleaned and the processor re-pasted. Every OS on the laptop has to be shut down first.
Example:
luaenabled = true
#hardware.service.others
Lets players service laptops they didn't put down. The
CanServiceLaptophook inserver/unlocked/hooks.luadecides who, for example a repair shop's job.Example:
luaothers = false
#hardware.service.items
Items the service view uses:
toolto open the laptop,pasteto re-paste the processor andairto clean the fan. Set one tofalsefor no item needed for that step.Example:
luaitems = {tool = "laptop_screwdriver", paste = "thermal_paste", air = "compressed_air"}
#hardware.service.pasteUses
Uses in a new tube of paste. An item without a
usesvalue in its metadata is new.Example:
luapasteUses = 5
#hardware.service.airUses
Uses in a new can of compressed air.
Example:
luaairUses = 10
#hardware.service.blowOut
Share of the fan's dust a blow-out takes with the cooler in place. A cooler that is lifted off is cleaned fully.
Example:
luablowOut = 0.7
#Mirror
The screen of an open laptop shows the players near it a simple copy of what is on that laptop: black while it is off, the OS's boot and lock screens, or its desktop with the open windows as outlines in their places, each with its app's icon and name and the browser's site. Never the page itself or anything typed.
#mirror.enabled
Enables the mirrored screens. Off, every laptop's screen shows a stock lock screen to other players.
Example:
luaenabled = true
#mirror.range
Metres from a laptop its screen is mirrored.
Example:
luarange = 6.0
#mirror.maxScreens
Laptops one player sees mirrored at once, the nearest open ones in view. Each takes a DUI of its own. At most 8. Laptops further away, out of view or past this number show the lock screen of the OS the watching player runs.
Example:
luamaxScreens = 8
#mirror.titles
Shows window names and the browser's page title and site address.
falseleaves only the app icons.Example:
luatitles = true
#mirror.veilPrivacy
Leaves out the names, page titles and site addresses while the laptop runs Veil, the privacy OS, even with
titleson. Its windows and app icons still show.Example:
luaveilPrivacy = true
#Casting
Casting a window to a screen in the world, such as a TV or a briefing room's screen, for everyone near it to see. Only that window's content goes to the screen, and every player's game draws it itself, so text stays sharp. Placed screens are kept in the zyke_laptop_cast_screens table, created on start. The CanCastToScreen and CanCreateScreen hooks in server/unlocked/hooks.lua limit who may do what. See Casting.
#cast.enabled
Enables casting. Off, the Cast button is gone and nothing of it runs.
Example:
luaenabled = true
#cast.liveScreens
Screens one player sees a cast on at once, the nearest in front of them. Each costs a browser in the game of every player who sees it, so keep it low.
Example:
lualiveScreens = 2
#cast.viewRange
Metres from a screen its cast is seen.
Example:
luaviewRange = 25.0
#cast.cableReach
Metres of HDMI cable between a laptop and a screen, for screens that don't set their own
reach.Example:
luacableReach = 5.0
#cast.wirelessRange
How far a wireless screen takes a cast from a laptop, for screens that don't set their own
range.Example:
luawirelessRange = 8.0
#cast.hdmi
The HDMI cable item, plugged into a placed laptop's HDMI port from the Ports prompt. Set
hdmi = falsefor wireless screens only.Example:
luahdmi = {item = "hdmi_cable"}
#cast.resolution
Pixels across the picture of a cast is drawn at. Its height follows the screen's shape.
Example:
luaresolution = 1280
#cast.limits
How much one player may send while casting. A snapshot is the window's whole content, a delta a change on it such as a pen stroke, and a signal a passing thing such as a pointer.
snapshotBytes,deltaBytesandsignalBytesare the most one may take as JSON.snapshot,deltaandsignalallow a burst ofcountand thenratea second. The server drops what goes over them.Example:
lualimits = { snapshotBytes = 32768, deltaBytes = 2048, signalBytes = 256, snapshot = {count = 3, rate = 1}, delta = {count = 30, rate = 15}, signal = {count = 10, rate = 8}, }
#cast.imageHosts
Hosts whose images a cast may show besides the laptop's own pictures. Every viewer's game loads them, which tells that host who looked at the screen. Empty by default, which allows none.
Example:
luaimageHosts = {"r2.fivemanage.com"}
#cast.creator
The screen creator's command and the ACE permission
CanCreateScreenchecks by default. Admins aim at a wall or a TV to place a screen and set its label, input, cable reach and wireless range.Example:
luacreator = {command = "laptopscreens", permission = "command"}
#cast.screens
Screens fixed in the config. They show in the creator's menu but are only changed here.
Field Description idUnique id. labelName shown in the Cast menu. cornersThree corners as seen from in front: topLeft,topRightandbottomLeft.coords,width,heightInstead of corners: the centre of the picture with the heading it faces as avec4, and its size in metres.input"hdmi","wireless"or"both".reachOptional cable reach, at most 30 metres. rangeOptional wireless range, at most 60 metres. bucketOptional routing bucket, 0 when left out. Screens are 0.2 to 12 metres wide and tall. A screen the config gets wrong is skipped with a warning in the server console.
Example:
luascreens = { -- Michael's living room, on the pull-down projector screen above the fireplace, by its corners {id = "michael_projector", label = "Living room projector", corners = {topLeft = vec3(-800.667, 174.880, 75.145), topRight = vec3(-800.165, 173.573, 75.145), bottomLeft = vec3(-800.667, 174.880, 74.355)}, input = "both", reach = 6.0}, }
#Boot
The startup sequence of the OS.
#boot.bootloader
Shows the OS's own boot loader screen when the player boots it from the laptop's boot menu (F12 at the startup screen).
falsegoes straight on to the OS's boot screen.Example:
luabootloader = true
#boot.greeter
Shows each OS's greeter after it boots: the first-run setup and the sign-in and lock screens of Facade, Lumen and Solace, and Veil's Welcome Screen.
falseskips it and goes straight to the desktop, so accounts are not asked for their password. Leave it on for a normal server.Example:
luagreeter = true
#boot.speed
Multiplies the length of every boot and shutdown step. The laptop's own hardware speeds it up or slows it down on top.
0skips straight to the desktop.Example:
luaspeed = 1.0
#Standby
A laptop stays on like a real one that sleeps: closed, put away, through a relog or a server restart it wakes on its lock screen with the windows and browser tabs it had. Only Facade, Lumen and Solace booted from the drive stay on; a USB stick leaves with its player. Powering off or restarting, an empty battery, a service or a change of parts turns it off. Creates the zyke_laptop_standby table on start. See Using the Laptop.
#standby.enabled
Enables standby. Off, a laptop that is put away, or left through a relog or a restart, boots fresh the next time it is opened.
Example:
luaenabled = true
#standby.days
Days a laptop nobody woke stays on. After that it boots again.
Example:
luadays = 30
#Connection
Milliseconds connecting to the Onion network takes. The Wi-Fi card's latency stretches the whole connection, so a Wi-Fi 7 card connects faster.
#connection.minDuration
Shortest the connection takes, in milliseconds. Each attempt picks a time between
minDurationandmaxDuration.Example:
luaminDuration = 4000
#connection.maxDuration
Longest the connection takes, in milliseconds.
Example:
luamaxDuration = 9000
#connection.bridgeDelay
Milliseconds added when the player connects through bridges in the connection app's settings.
Example:
luabridgeDelay = 3000
#Wi-Fi
The laptop is online through a hotspot in range, listed in Config.Hotspots, or through the player's phone. See Wi-Fi & Browsing.
#wifi.phone.enabled
Lets players tether to their phone anywhere, under a name like "Linus's Phone". Set to
falseto only be online at hotspots.Example:
luaenabled = true
#wifi.phone.item
Item the player has to carry to tether.
false, the default, for none.Example:
luaitem = "phone"
#Files
Like Tails, Veil forgets every file when it shuts down except those in ~/Persistent. See Operating Systems.
#files.storage
Where
~/Persistentis kept.
"character"stores it in the database per character, in thezyke_laptop_filestable created on start. It follows the character to any PC and survives a cleared cache."client"stores it on the player's PC, shared by every character played there. It is gone when the player clears their FiveM cache.Example:
luastorage = "character"
#files.maxBytes
Bytes one player's
~/Persistentmay take. Client storage is capped at 120000, under the 128 KiB a client keeps under one key, and character storage at 327680, as every save sends the whole folder to the server. Past it the OS tells the player the storage is full.Example:
luamaxBytes = 262144
#OS
The operating systems a laptop runs. Facade, Lumen and Solace are installed on the laptop's drive, Veil boots live from a USB stick and never touches the drive. Pressing F12 at the laptop's startup screen opens its boot menu. See Operating Systems.
#os.default
OS installed on new laptops and on laptop items from before operating systems:
"facade","lumen","solace"or"veil".Example:
luadefault = "facade"
#os.usb
Items that show up in the boot menu while the player carries them, by OS.
veilboots Veil live;lumen,solaceandfacadeerase the drive and install that OS. Set one tofalseto offer that option to everyone without an item.Example:
luausb = {veil = "veil_usb", lumen = "lumen_installer", solace = "solace_installer", facade = "facade_installer"}
#os.carriedSticks
Lets the boot menu offer the sticks a player carries, as well as the ones plugged into the laptop's USB port. Set to
falseto make players plug the stick in first.Example:
luacarriedSticks = true
#App Store
The app store on the desktop of Facade, Lumen and Solace. The system apps come installed; the rest are downloaded from it: Shroud, Natter, the Onion network app, the drawing and slides apps, and the apps other resources register. Veil and the Crowbar recovery toolkit run live with every app. See Operating Systems.
#appStore.enabled
Enables the app store.
falseinstalls every app from the start.Example:
luaenabled = true
#appStore.preinstalled
Apps a new account starts with besides the system apps:
"connection"(the Onion network app),"chat"(Shroud),"messenger"(Natter),"paint","slides"or the id of a registered app. Empty by default. A resource can also register its app withpreinstalled = trueto skip the store, see Apps & Sites. Accounts from before the store keep every app they had.Example:
luapreinstalled = {"messenger", "paint"}
#Accounts
The accounts of Facade, Lumen and Solace. The account the first-run setup makes is the drive's administrator. In Settings it can add more accounts, turn on a Guest account and let anyone sign in with their own Nimbus account as Other user, as a business's computer needs.
#accounts.maxAttempts
Wrong passwords in a row before the laptop locks. The count is kept per laptop, so it carries over to the next player who tries.
Example:
luamaxAttempts = 5
#accounts.lockoutSeconds
Seconds the laptop refuses passwords once it locked.
Example:
lualockoutSeconds = 60
#accounts.maxUsers
Accounts one drive keeps besides the one setup made, the Nimbus users who signed in as Other user included.
Example:
luamaxUsers = 10
#Cloud
Where the Facade, Lumen and Solace account and everything it keeps are stored. Creates the zyke_laptop_cloud_accounts, zyke_laptop_cloud_data and zyke_laptop_cloud_devices tables on start. See Operating Systems.
#cloud.mode
"synced": the account is a Nimbus account that belongs to the character and follows them to every laptop they sign in on, so a lost laptop loses nothing."drive": everything stays on the laptop's SSD, and losing the SSD loses it all.Example:
luamode = "synced"
#cloud.localAccounts
Lets players set up a local account kept only on the drive while
modeis"synced", the hardcore choice.Example:
lualocalAccounts = true
#cloud.maxDevices
Laptops one account can be signed in on at once.
Example:
luamaxDevices = 10
#cloud.reset
How "Forgot password" proves the player is the account's character.
"phone"texts a code to their phone, and works as"instant"without a phone resource that takes texts."instant"resets at once.falseturns resetting off. Only works whilemodeis"synced".Example:
luareset = "phone"
#cloud.alerts
Texts the account's character when a new laptop signs in, a reset is asked for or a laptop is signed out. Replace
SendLaptopCloudAlertinserver/unlocked/hooks.luato send these another way.Example:
luaalerts = true
#Recovery
Crowbar, the recovery toolkit a thief boots from a USB stick to get into a laptop's drive. Facade and Solace show the account's files at once, and their timer only gates setting a new password. Lumen keeps its home folder and history locked until the timer finishes. Veil never writes to the drive, so there is nothing to find. See Ownership & Recovery.
#recovery.enabled
Enables the recovery toolkit. Off, it never shows in the boot menu.
Example:
luaenabled = true
#recovery.item
Item that shows up in the boot menu while the player carries it.
falseoffers the toolkit to everyone.Example:
luaitem = "recovery_usb"
#recovery.durations
Seconds the toolkit works on a password, by the OS on the drive and how strong the password is:
trivial,weak,fairorstrong. The timer only runs while a player keeps the toolkit open on that laptop, and keeps its progress in between.Example:
luadurations = { facade = {trivial = 30, weak = 45, fair = 60, strong = 180}, lumen = {trivial = 120, weak = 300, fair = 1200, strong = 1200}, solace = {trivial = 30, weak = 45, fair = 60, strong = 180}, }
#recovery.exhausts
Strength tiers the toolkit gives up on once their duration is over, so the drive stays locked. By default a strong Lumen password is never cracked.
Example:
luaexhausts = { lumen = {strong = true}, }
#recovery.maxLogs
Lines each drive's system log keeps. The oldest go first.
Example:
luamaxLogs = 100
#recovery.repair
OSes whose installer stick can reset the local password of a drive running that OS without erasing it. Leave an OS out, or set it to
false, to have its installer only erase and reinstall.Example:
luarepair = {facade = true, solace = true}
#Shroud (chat)
Shroud, the onion chat app. Players chat under anonymous handles, and other resources register contacts such as dealers and fixers through exports. Creates the zyke_laptop_chat_* tables on start. See Messaging and Shroud Contacts.
#chat.enabled
Enables Shroud. Off, the app is gone from every OS, nothing of it runs on the server, its tables aren't created and its exports, such as
RegisterChatContact, aren't registered.Example:
luaenabled = true
#chat.maxLength
Characters one message may have.
Example:
luamaxLength = 500
#chat.maxIdentities
Handles one character may create. They are counted per character, but which handles belong to whom is only stored when the creating OS leaves the
charactertrace.Example:
luamaxIdentities = 3
#chat.retentionDays
Days messages and traces are kept before they are deleted. Checked on start and once a day.
Example:
luaretentionDays = 14
#chat.rateLimits
How often a player may do something,
counttimes perseconds:sendmessages, create anidentity,signIn, andactionfor card buttons, forms and purchases.Example:
luarateLimits = { send = {count = 8, seconds = 10}, identity = {count = 3, seconds = 3600}, signIn = {count = 5, seconds = 60}, action = {count = 10, seconds = 10}, }
#chat.rooms
Public rooms every player can join. A room is named by the
"chatServer:room:<id>"and"chatServer:roomDescription:<id>"locale keys, or by thenameanddescriptiongiven here.listed = falsekeeps a room out of the directory. Resources add more with theRegisterChatRoomexport.Example:
luarooms = { {id = "lobby"}, {id = "market"}, {id = "garage", name = "The Garage", description = "Parts and rides"}, }
#chat.traces
What the server records when a player sends a message or creates a handle, by the OS their laptop runs.
"character"is the character identifier,"serial"the laptop's serial,"network"the hotspot or phone it is online through and"coords"where the player stood. Players never see traces; only server resources read them, through theGetChatTracesexport.Example:
luatraces = { facade = {"character", "serial", "network", "coords"}, solace = {"character", "serial", "network", "coords"}, lumen = {"serial", "network"}, veil = {}, }
#Natter (messenger)
Natter, the messaging app. Accounts belong to the character's phone number, read through zyke_lib, and a laptop stays linked to its account. Creates the zyke_laptop_messenger_* tables on start. See Messaging.
#messenger.enabled
Enables Natter. Off, the app, its requests, its exports and its tables are gone.
Example:
luaenabled = true
#messenger.maxLength
Characters one text or caption may have.
Example:
luamaxLength = 2000
#messenger.maxDevices
Laptops one account may be linked to at once, Veil links included.
Example:
luamaxDevices = 4
#messenger.numberFormat
Number given to characters when the server has no phone resource at all.
#is a random digit.Example:
luanumberFormat = "555-####"
#messenger.codeSender
Short code the link code text comes from on the phone, digits only.
Example:
luacodeSender = "6283"
#messenger.linkCodeSeconds
Seconds a link code sent to the phone stays valid.
Example:
lualinkCodeSeconds = 300
#messenger.retentionDays
Days messages are kept. Checked on start and once a day.
Example:
luaretentionDays = 30
#messenger.metadataRetentionDays
Days who messaged whom and when stays available to police tools through
GetMessengerMetadata. The content of messages is never part of it.Example:
luametadataRetentionDays = 30
#messenger.editMinutes
Minutes a message can be edited.
Example:
luaeditMinutes = 15
#messenger.deleteForEveryoneHours
Hours a message can be deleted for everyone.
Example:
luadeleteForEveryoneHours = 48
#messenger.disappearing
Times in seconds the app offers for disappearing messages. The defaults are 24 hours, 7 days and 90 days.
Example:
luadisappearing = {86400, 604800, 7776000}
#messenger.groups.maxMembers
Members a group may have.
Example:
luagroups = {maxMembers = 64}
#messenger.attachments.maxFileBytes
Bytes a file sent from the drive may have.
Example:
luamaxFileBytes = 262144
#messenger.attachments.imageHosts
Hosts images may come from. Empty, the default, allows any
httpshost.Example:
luaimageHosts = {"r2.fivemanage.com"}
#messenger.rateLimits
How often a player may do something,
counttimes perseconds:sendmessages,action(edits, deletes, reactions, stars, votes, settings, contacts, blocks and group changes),lookup(opening chats by number, profile lookups, search and invite previews),link(link codes and attempts at them),group(creating and joining groups) andtyping.Example:
luarateLimits = { send = {count = 10, seconds = 10}, action = {count = 30, seconds = 10}, lookup = {count = 15, seconds = 60}, link = {count = 5, seconds = 300}, group = {count = 5, seconds = 600}, typing = {count = 20, seconds = 10}, }
#Browsing
The browser. On Facade, Lumen and Solace ordinary sites open over the plain internet, .onion sites need the Onion network on every OS, and Veil sends everything through it. A VPN and the Onion network are optional protection that changes what the server records about a visit. Creates the zyke_laptop_site_visits table on start. See Wi-Fi & Browsing.
#browsing.vpn.enabled
Enables the one-click VPN in Facade's quick settings, Lumen's system menu, Solace's Quick Controls and the browser's menu. Veil has no VPN; everything already goes through the Onion network.
Example:
luaenabled = true
#browsing.vpn.connectDuration
Milliseconds the VPN takes to connect.
Example:
luaconnectDuration = 2500
#browsing.record
Which visits are recorded.
"monitored"only records the sites inmonitored, sites a resource registered withmonitored = trueand sites marked with theSetSiteMonitoredserver export."all"records every site.Example:
luarecord = "monitored"
#browsing.monitored
Addresses recorded with
record = "monitored". Empty by default.Example:
luamonitored = {"lifeinvader.com", "ammu-nation.com"}
#browsing.retentionDays
Days visit traces are kept before they are deleted. Checked on start and once a day.
Example:
luaretentionDays = 14
#browsing.rateLimit
How often a player's visits are taken in,
counttimes perseconds. Visits over the limit are dropped.Example:
luarateLimit = {count = 20, seconds = 10}
#browsing.traces
What the server records about a visit, by how the player was protected:
direct,vpnoronion. The fields are the same as Shroud'straces. Veil is alwaysonion. Only server resources read them, through theGetSiteVisitTracesexport.Example:
luatraces = { direct = {"character", "serial", "network", "coords"}, vpn = {"serial"}, onion = {}, }
#Casino
Rollhaus, the online casino in the browser. Players deposit bank money as credits, play the games in shared/unlocked/casino/ with them and withdraw them back to the bank. Results come from the server and can be checked by the player. The CanUseCasino hook in server/unlocked/hooks.lua adds your own rules. Creates the zyke_laptop_casino_* tables on start. See Casino.
#casino.enabled
Enables the casino. Off, the site is gone from the browser, search and LS Pages, every casino request is refused, nothing of it runs on the server and none of its tables are created, for example on servers with young players or where even simulated gambling isn't allowed. The
IsCasinoEnabledexport still answers, withfalse.Example:
luaenabled = true
#casino.site
The site's name and address in the browser.
Example:
luasite = {name = "Rollhaus", host = "rollhaus.gg"}
#casino.creditRate
Dollars one credit costs. Deposits round down to whole cents of credit and withdrawals pay whole dollars, so cents left over stay on the site.
Example:
luacreditRate = 1.0
#casino.deposit
Whole dollars one deposit may be, taken from the bank account.
Example:
luadeposit = {min = 10, max = 250000}
#casino.withdraw
Whole dollars one withdrawal may be, paid to the bank account, and
cooldown, the seconds between two withdrawals.Example:
luawithdraw = {min = 10, max = 250000, cooldown = 0}
#casino.minAge
Years a character must be, from the date of birth the framework stores.
0, the default, turns the check off, and characters without a readable date of birth are let in.Example:
luaminAge = 18
#casino.bots.enabled
Generated players who fill the seats a real site would have other people in. Their money is the house's: nobody is paid when a bot wins. Off, no bots play anywhere, whatever the switches below and in the game files say.
Example:
luaenabled = true
#casino.bots.feed
Mixes bot bets into the live bets feed so a quiet server still looks alive. They are never in anyone's history.
Example:
luafeed = true
#casino.bots.feedInterval
Seconds between two bot bets in the feed.
Example:
luafeedInterval = {min = 4, max = 14}
#casino.bots.coinflip
Lets the creator of a coinflip call a bot to take the other side.
Example:
luacoinflip = true
#casino.bots.battles
Lets bots fill the empty seats of a case battle.
Example:
luabattles = true
#casino.bots.blackjack
Bots playing basic strategy at the live blackjack tables, up to the game's
botSeats.Example:
luablackjack = false
#casino.bots.crash
Bots betting in each Crash round so its players list still moves.
Example:
luacrash = true
#casino.feed.size
Rows each list of the live bets feed keeps.
Example:
luasize = 30
#casino.feed.highRoller
Credits a bet has to stake to show under High rollers.
Example:
luahighRoller = 1000
#casino.historyDays
Days settled bets and revealed round seeds are kept for history and bet details. The ledger of every balance change is never deleted.
Example:
luahistoryDays = 30
#casino.rateLimits
How often a player may do something,
counttimes perseconds.betcovers every action that takes a stake,actioneverything else on the site,walletdeposits and withdrawals,accountpicking a username and changing settings, andpasswordthe passwords tried on player blackjack tables.Example:
luarateLimits = { bet = {count = 20, seconds = 5}, action = {count = 30, seconds = 10}, wallet = {count = 6, seconds = 60}, account = {count = 5, seconds = 60}, password = {count = 5, seconds = 60}, }
#Job Board
Hireline, a job board in the browser. Businesses post listings for their job, players apply with their profile, talk to the employer in a message thread and book interview slots. Hiring does not change anyone's job: the server fires zyke_laptop:jobs:ApplicantHired for your own script to do that. Creates the zyke_laptop_jobs_* tables on start. See Job Board.
#jobs.enabled
Enables Hireline. Off, the site, every action and every table it has are gone.
Example:
luaenabled = true
#jobs.site
The site's name and address in the browser.
Example:
luasite = {name = "Hireline", host = "hireline.com"}
#jobs.posting.bossRanks
Lets the job's boss grades in the framework post listings and handle applicants for their current job.
Example:
luabossRanks = true
#jobs.posting.grades
Lowest grade level of a job that may post as well, by job name. Empty by default.
Example:
luagrades = {police = 3, mechanic = 2}
#jobs.posting.excluded
Jobs that never post, such as the framework's default job. The
CanPostJobListing(plyId, jobName, ranked)hook inserver/unlocked/hooks.luagets the last word for every other job;rankedis whether the player passesbossRanksorgrades.Example:
luaexcluded = {"unemployed"}
#jobs.listingDays
Days an open listing stays up before it closes, unless an employer renews it.
Example:
lualistingDays = 14
#jobs.maxOpenListings
Listings one job may have open at once.
Example:
luamaxOpenListings = 10
#jobs.maxApplicationsPerDay
Applications one character may send in 24 hours.
Example:
luamaxApplicationsPerDay = 15
#jobs.historyDays
Days closed listings and finished applications are kept, then deleted with their messages. Checked on start and once a day.
Example:
luahistoryDays = 60
#jobs.interviewLockMinutes
Minutes before an interview from which it can no longer be moved or cancelled.
Example:
luainterviewLockMinutes = 30
#jobs.reminderMinutes
Minutes before an interview its applicant and the job's online employers get a reminder.
Example:
luareminderMinutes = 15
#jobs.phoneReminders
Also texts the reminder to the applicant's phone, when the phone resource takes texts.
Example:
luaphoneReminders = true
#jobs.limits
Most characters each text may have, and most entries of each list, on listings, applications and profiles.
payMaxis the highest pay a listing may offer in dollars,slotMinutesthe length of one interview slot andinterviewDaysthe upcoming interview days one listing may have.Example:
lualimits = { title = 80, aboutRole = 2000, cover = 1500, message = 1000, payMax = 100000, slotMinutes = {min = 10, max = 60}, interviewDays = 8, }
#jobs.rateLimits
How often a player may do something,
counttimes perseconds.readcovers looking around,writesaving anything,applysending applications andmessagesending messages.Example:
luarateLimits = { read = {count = 60, seconds = 10}, write = {count = 15, seconds = 20}, apply = {count = 4, seconds = 60}, message = {count = 10, seconds = 20}, }
#Config.Shop
shared/unlocked/shop.lua holds the laptop shop: a counter where players buy laptops, refinish and put backplates on the ones they carry, and buy parts, tools and accessories, each shown on the counter in a studio as they browse. Every offer and price is here, and the server checks each purchase against it. CanBuyLaptopShopItem in server/unlocked/hooks.lua adds your own rules. See Shop.
The location, storages, door locks and displays stand in Digital Den, the free zyke_digitalden interior at the Morningwood strip mall. Run it alongside, or move them all to a shop of your own when another map fills that unit; see Dependencies.
#enabled
Enables the shop counters. Set to
falseto sell only through a real person, such as a player-run Digital Den, with the server exports. The storages, door locks and display laptops stay; the displays then show no price.Example:
luaenabled = true
#accounts
Accounts a purchase is paid from, the first one that holds the price.
Example:
luaaccounts = {"cash", "bank"}
#locations
Shop counters. Each gets a shop keeper and a blip, browsed through an interest point or the target menu, as
interactionpicks. Buyers have to stand within 5 metres ofcoords.
Field Description labelName of the shop, also the blip's name. modelPed model of the shop keeper. Leave it out for a counter without a ped. coordsWhere the ped stands, at eye height, and the heading it faces, a vec4.scenarioScenario the ped plays. blip{sprite, color, scale}. Leave it out for no blip.studioOptional vec4: the spot on the counter the studio shows its laptop or item on, and the heading from it towards the customer. Set it when the studio can't find the counter top in front of the shop keeper; the client console says so.Example:
lualocations = { { label = "Digital Den", model = "a_m_y_business_02", coords = vec4(-1318.56, -401.81, 36.64, 33.95), scenario = "WORLD_HUMAN_STAND_IMPATIENT", blip = {sprite = 521, color = 3, scale = 0.7}, }, }
#storages
Shelves the shop's staff keep stock on, each one a stash shared by everyone who may open it. They are opened the same way as the shop, within 4 metres, and stay when
enabledisfalse.CanOpenLaptopStorageinserver/unlocked/hooks.luaadds your own rule, such as being on duty.
Field Description idNames the stash in your inventory as zyke_laptop_<id>, so changing it starts an empty one.labelName shown on the shelf and the stash. coordsSpot on the shelf that is marked. jobsJob names that may open it. Leave it out to let anyone. The digitaldenjob is inextras/jobs/.slotsSlots of the stash, 50 when left out. weightWeight of the stash in grams, 200000 when left out. Example:
luastorages = { {id = "digitalden_workshop", label = "Workshop Shelf", coords = vec3(-1318.59, -406.73, 36.84), jobs = {"digitalden"}, slots = 50, weight = 200000}, {id = "digitalden_stock", label = "Stock Room Shelf", coords = vec3(-1312.39, -402.99, 36.84), jobs = {"digitalden"}, slots = 100, weight = 500000}, }
#doorlock.enabled
Adds the shop's doors to
ox_doorlockorrcore_doorlockwhen the resource or the door lock starts, unless the door lock already has a door by that name, so a door you change there keeps your change. By default Digital Den's front doors, repair room and storage open for thedigitaldenjob only, and the front doors start unlocked so customers can walk in.
qb-doorlockcan't take doors from another resource: copyextras/doorlock/qb-doorlock.luaintoqb-doorlock/configs/and setenabled = false. Forcd_doorlock, importextras/doorlock/ox_doorlock.sqlwith its migration tool and setenabled = false. Set it tofalsefor your own door setup too. Seeextras/doorlock/README.md.Example:
luaenabled = true
#doorlock.pin
PIN the doors open with on
rcore_doorlock, which can't take a job from another resource. Change it before you start the resource withrcore_doorlock, then give the doors their job in its/doorsmenu.Example:
luapin = "1234"
#doorlock.locks
The doors.
Field Description nameUnique name the door lock knows the door by. labelName shown in the door lock. jobsJob names, or {name = "digitalden", minGrade = 2}for a lowest grade.lockedHow a new door starts. distanceMetres a player has to stand within. Optional. doorsOne door, or two for a double door, each {model, coords, heading}.Example:
lualocks = { { name = "digitalden front doors", label = "Digital Den Front Doors", jobs = {"digitalden"}, locked = false, distance = 2.5, doors = { {model = "v_ilev_csr_door_l", coords = vec3(-1320.2831, -398.3739, 36.8857), heading = 31.1}, {model = "v_ilev_csr_door_r", coords = vec3(-1318.6459, -397.3864, 36.8911), heading = 31.06}, }, }, }
#displays
Laptops on show: the real laptop standing open on a table or shelf, with an acrylic spec sign on its right that shows the model, its colour, its parts and its price at the shop. Players walk up to a sign and look closer at it. They are props only, nobody can use one or carry it off, and they exist within
renderDistance. They stay whenenabledisfalse, then without a price; the price also only shows while the shop sells that laptop in that finish. The defaults fill Digital Den's showroom with 27 laptops. Setdisplays = {}for none.
Field Description coordsWhere the laptop stands and the heading someone using it would face, a vec4. Keep about 0.7 metres between two laptops in a row so the sign fits.prebuiltAn id from Config.Prebuilts.finishOne of finishes. Leave it out for the stock graphite.skinA backplate from skins. Leave it out for the stock lid.replacesA map prop model, or a list of them, standing within 0.6 metres, such as the laptop an MLO has on its desk. It is hidden while the resource runs. Example:
luadisplays = { {coords = vec4(-1327.918, -406.048, 36.540, 211.1), prebuilt = "basic", finish = "silver", replaces = "prop_laptop_01a"}, {coords = vec4(-1327.236, -406.138, 36.540, 31.1), prebuilt = "standard", replaces = {"gr_prop_gr_laptop_01c", "ex_prop_ex_laptop_01a"}}, {coords = vec4(-1325.236, -402.156, 36.090, 211.1), prebuilt = "performance", finish = "silver", skin = "grad_sunset", replaces = "gr_prop_gr_laptop_01c"}, }
#laptops
New laptops, one entry per prebuilt. Each comes in the stock graphite and in every one of
finishes, and a backplate from the shop'sskinsbelow can go on in the same purchase at its own price. Needs a laptopitem.
Field Description enabledfalsetakes the whole category out.finishMarkupAdded to the price for a finish other than graphite. falsesells graphite only.items{prebuilt, price}per laptop.enabled = falseon an entry takes it out. A prebuilt of your own needs a"shop:prebuilt:<id>"label in every locale file.Example:
lualaptops = { enabled = true, finishMarkup = 150, items = { {prebuilt = "basic", price = 900}, {prebuilt = "standard", price = 1600}, {prebuilt = "performance", price = 2800}, {prebuilt = "flagship", price = 4500}, {prebuilt = "diy", price = 650}, }, }
#refinish
A new finish on a laptop the player carries, graphite included. Every finish costs
price. A laptop that already has that finish is refused.Example:
luarefinish = {enabled = true, price = 400}
#skins
Putting a backplate, one of the
skinsinconfig.lua, on a laptop the player carries. Each one is paid in full, even one the laptop had before; a laptop that already wears that backplate is refused.
Field Description enabledfalsestops selling backplates.pricePrice of every backplate. pricesAnother price for a whole category, such as {material = 400}. Empty by default.boughttruemakes backplates something players buy: Settings only offers the stock lid and the backplate on that laptop, from here or theSetLaptopSkinexport, which also holds with the shop disabled.falselets players pick any skin in Settings for free.Example:
luaskins = {enabled = true, price = 250, prices = {material = 400}, bought = true}
#parts
New parts at full condition, made by
CreatePartMetadata. Each entry is{item, model, price}, whereitemis one ofhardware.itemsandmodelan id from the parts catalogue.enabled = falseon an entry or on the category takes it out. The defaults sell 6 mainboards, the 3 coolers, 7 memory sticks, 7 SSDs, 4 Wi-Fi cards and 2 batteries; see Hardware & Servicing for what each model is.Example:
luaparts = { enabled = true, items = { {item = "laptop_mainboard", model = "kc13_k7", price = 800}, {item = "laptop_cooler", model = "dual", price = 60}, {item = "laptop_ram", model = "d5_16", price = 140}, {item = "laptop_ssd", model = "nv4_1tb", price = 150}, {item = "laptop_wifi", model = "kw7", price = 90}, {item = "laptop_battery", model = "zb61", price = 160}, }, }
#tools
Tools bought by the amount, up to 10 at a time, new with all their uses. Each entry is
{item, price}. The studio shows the laptop's own tools as their props; give an item of your ownprop = "model"to show it too, and an item without one shows its inventory image.enabled = falseon an entry or on the category takes it out.Example:
luatools = { enabled = true, items = { {item = "laptop_screwdriver", price = 45}, {item = "thermal_paste", price = 25}, {item = "compressed_air", price = 15}, {item = "my_antistatic_mat", price = 30, prop = "my_antistatic_mat_prop"}, }, }
#accessories
Accessories bought by the amount, up to 10 at a time, in the same format as
tools: the data stick, the HDMI cable, the power bank and the OS sticks. The recovery stick is a thief's tool, so it is listed withenabled = false.Example:
luaaccessories = { enabled = true, items = { {item = "usb_drive", price = 40}, {item = "hdmi_cable", price = 30}, {item = "laptop_powerbank", price = 85}, {item = "facade_installer", price = 150}, {item = "solace_installer", price = 120}, {item = "lumen_installer", price = 60}, {item = "veil_usb", price = 60}, {item = "recovery_usb", price = 2500, enabled = false}, }, }
Prices have to be whole numbers of 0 or more; an offer with any other price is left out. The offer ids CanBuyLaptopShopItem receives are laptop:<prebuilt>:<finish or stock>, refinish:<finish or stock>, skin:<id>, part:<item>:<model>, tools:<item> and accessories:<item>.
#Prebuilts
shared/unlocked/hardware.lua lists the laptops as they leave the factory, in Config.Prebuilts. Every part gets its own serial when the laptop is made, and a board always comes with the cooler made for it. hardware.default picks the one a laptop item without hardware becomes, hardware.migrated the one laptops from before hardware get, and computer.prebuilt the desktop computer's. Shops and other resources give any of them with the CreateLaptopMetadata server export. See Hardware & Servicing.
| Field | Description |
|---|---|
board | The mainboard and its processor, such as "kc13_k7" for the Kestrel Talon 13 with the K7-Q360. |
ram | The sticks in socket 0 and socket 1, of the memory type the board takes. One stick runs in single channel. |
ssd, wifi, battery | One model each. Leave one out for a laptop without it. |
The server prints a warning on start for a prebuilt with a model the catalogue doesn't have, or with parts that don't fit together, and for hardware.default, hardware.migrated or computer.prebuilt naming a prebuilt that doesn't exist.
| Prebuilt | Board | Memory | SSD | Wi-Fi | Battery |
|---|---|---|---|---|---|
legacy | kc13_k7, Kestrel Talon 13 K7-Q360 | 2 x 16 GB DDR4 | Nordwell NV4 1 TB | kw6e | zb61, 61 Wh |
basic | kc12_k5, Kestrel Talon 12 K5-P220 | 1 x 8 GB DDR4 | Nordwell NV3 512 GB | kw6e | zb55, 55 Wh |
standard | kc13_k7, Kestrel Talon 13 K7-Q360 | 2 x 8 GB DDR4 | Nordwell NV4 512 GB | kw6e | zb61, 61 Wh |
performance | av7_7, Arden Vela 4 Vela 7 V480 | 2 x 16 GB DDR5 | Nordwell NV4 1 TB | mr6e | zb61, 61 Wh |
flagship | ava_9, Arden Vela AI 9 X570 | 2 x 32 GB DDR5 | Nordwell NV4 2 TB | mr7 | zb61, 61 Wh |
diy | kc13_k5, Kestrel Talon 13 K5-Q340 | None | None, and no OS | kw6e | zb55, 55 Wh |
desktop | kc13_k7, Kestrel Talon 13 K7-Q360 | 2 x 16 GB DDR4 | Nordwell NV4 1 TB | kw6e | None, it runs on mains |
Example:
luaConfig.Prebuilts = {
-- A mid-range laptop for a police department's fleet
police = {board = "ka1_7", ram = {"d5_16", "d5_16"}, ssd = "nv5_1tb", wifi = "kw7", battery = "zb61"},
}
#Hotspots
shared/unlocked/hotspots.lua lists the public Wi-Fi networks at places on the map, in Config.Hotspots. The laptop lists the ones in range of the player and joins them through the server; anywhere else it is online through the player's phone, see wifi.phone. Hotspots need the laptop's Wi-Fi card, whose generation and antenna leads set how far they reach. The defaults are 39 hotspots, 22 of them secured. See Wi-Fi & Browsing.
| Field | Description |
|---|---|
id | Players' laptops remember networks by it, so renaming one makes them forget it. Also the key of its password. |
ssid | The name players see, the same in every language. |
coords | Where the router sits. Height counts too, so a rooftop can be out of reach of the street. |
range | Metres the signal reaches. The bars drop with the distance. |
secured | Needs the password set in server/unlocked/hotspots.lua. |
Example:
luaConfig.Hotspots = {
{id = "lsia_terminal", ssid = "LSIA Free WiFi", coords = vec3(-1037.0, -2737.0, 20.2), range = 90.0, secured = false},
{id = "mission_row_pd", ssid = "LSPD-MissionRow", coords = vec3(425.1, -979.5, 30.7), range = 60.0, secured = true},
}
shared/unlocked/hotspots.lua reaches every client, so never put a password there. The passwords of the secured hotspots are in server/unlocked/hotspots.lua, in HotspotPasswords by hotspot id, and only run on the server. Hand them out in roleplay: a barista writing it on a cup, a job sharing its office's network, a contact selling it.
luaHotspotPasswords = {
lsia_pegasus = "firstclassonly",
mission_row_pd = "Pr0tect&Serve",
}
The server checks every join: the hotspot exists, the player is in range, the password matches for a secured hotspot, and CanJoinHotspot in server/unlocked/hooks.lua allows it. A secured hotspot without a password in HotspotPasswords can never be joined, like the FIB surveillance van and Fort Zancudo in the defaults. A password given for a hotspot that isn't secured is never asked for.
#Example Sites
shared/unlocked/examples.lua holds the example sites that come with the laptop, in Config.Examples. Each is a folder under examples/ with its code, pages and text, to use as it is or to copy as the start of your own site. Set enabled = false to leave one out. See Example Sites.
#Config.Examples.mechanic
lscustoms.com, a business site with photos and a button that sets a GPS route to the nearest shop.
Key Description enabledEnables the site. site.urlWhat players type into the address bar. It has to be unique across every site on the laptop. site.titleThe site's title. site.iconLucide icon name shown in search results and on the notification. locationsThe shops listed on the site, {label, coords}. The route button picks the one closest to the player.Example:
luamechanic = { enabled = true, site = {url = "lscustoms.com", title = "Los Santos Customs", icon = "wrench"}, locations = { {label = "LS Customs Burton", coords = vec3(-337.38, -136.92, 38.57)}, {label = "Beeker's Garage Paleto Bay", coords = vec3(110.99, 6626.39, 30.89)}, }, }
#Config.Examples.police
lspd.gov, a police academy application form, and an "LSPD Recruitment" app for officers to accept or decline the applications. Stored in thezyke_laptop_police_applicationstable. To hire accepted applicants automatically, listen for the server eventzyke_laptop:examples:police:ApplicationReviewed.
Key Description enabledEnables the site and the app. site.url,site.titleThe site's address and title. jobJob whose members review applications. reviewGradeLowest grade of that job that gets the recruitment app. cooldownHoursHours a character waits after a declined application before applying again. fieldsLength limits in characters, {min, max}, checked on the page and again on the server:age(in years),phone,availability,experience,motivation, andnote, the reviewer's note, required when declining.Example:
luapolice = { enabled = true, site = {url = "lspd.gov", title = "Los Santos Police Department"}, job = "police", reviewGrade = 3, cooldownHours = 48, }
#Config.Examples.arcade
Snake at
snake.comand 2048 at2048.com, with an all-time leaderboard per game in thezyke_laptop_arcade_scorestable. The scores come from the player's browser, so don't hang money or items on them.
Key Description enabledEnables the arcade. debugPrints rejected scores and why to the server console. boardSizeEntries on each game's all-time board. submitCooldownMilliseconds a player waits between two submitted scores. clockToleranceMilliseconds a game may claim to have lasted beyond what the server measured since it started, for latency. gamesOne entry per game, each a site: id,url,title,page,ruleshanded to the page as they are, andlimits.The
limitsof a game reject scores that aren't possible:maxScore,minMsPerPointwithgracePointsfor the fastest a score can be reached, andcheck, an optional function of(score, duration, stats)that returns whether the score is plausible. Snake's board and speed (cols,rows,startLength,startStepMs,minStepMs,speedUpMs) and 2048's (size,moveMs) are set at the top of the file. To add a game, add an entry, anexamples/arcade/web/<id>/folder with its page and"<id>:"keys in everyexamples/arcade/locales/*.luafile, at least"<id>:description"for the search results.Example:
luaarcade = { enabled = true, debug = false, boardSize = 10, submitCooldown = 4000, clockTolerance = 2000, games = { { id = "snake", url = "snake.com", title = "Snake", page = "examples/arcade/web/snake/index.html", rules = snake, limits = { maxScore = snake.cols * snake.rows - snake.startLength, gracePoints = 3, minMsPerPoint = snake.minStepMs * 3, }, }, }, }
#Casino Games & Cases
Each game of the casino has its own file in shared/unlocked/casino/, which adds it as Config.Casino.games.<id>. They only matter while casino.enabled is on. Every file has enabled, and enabled = false there removes only that game. Stakes and wins are in credits.
Most games share these keys:
| Key | Description |
|---|---|
enabled | Enables the game. |
edge | Share of every bet the house keeps on average, 0.01 being 1%. Coinflip has rake instead, the share of the pot the house keeps. |
minBet | Fewest credits one bet may be. Payouts round down to whole cents, so stakes below 1 credit lose a little more than the edge in most games. |
maxBet | Most credits one bet may be. |
maxProfit | Most credits one bet or round may win on top of its stake. Dice, Keno, Plinko and Wheel refuse a bet that could win more, so the player lowers the stake. Crash, Hilo, Limbo, Mines and Tower pay the cap instead, cashing out at once where the game can. |
| Game | File | Edge | minBet | maxBet | maxProfit | Game-specific keys |
|---|---|---|---|---|---|---|
| Blackjack | blackjack.lua | From the rules | 1 | 10000 | — | tables (id, name, minBet, maxBet, seats 5 to 7), playerTables (enabled, max 20, closeSeconds 120), rules (decks 6, penetration, blackjackPays 1.5, dealerHitsSoft17, doubleAfterSplit, maxHands 4, insurance, surrender), bettingSeconds 15, decisionSeconds 12, insuranceSeconds 8, idleHands 5, botSeats 3 |
| Case battles | battles.lua | The cases' own | — | — | — | maxCases 25, maxCost 100000 per seat, maxOpen 3, expireMinutes 30, modes (1v1, 1v1v1, 1v1v1v1, 2v2, 3v3), crazy (lowest total wins), terminal (only the last case counts), botFeed false |
| Cases | cases.lua | About 8%, from the catalogue | — | — | — | maxOpen 4 at once, demo free demo spins, vaultLimit 500, botFeed false |
| Coinflip | coinflip.lua | rake 0.05 | 0.1 | 10000 | — | maxOpen 3, expireMinutes 30 |
| Crash | crash.lua | 0.01 | 1 | 10000 | 100000 | minTarget 1.01, maxTarget 1000000, bettingSeconds 7, pauseSeconds 3, growth 0.00006 (2x after 11.6 seconds), bots (enabled, min 3, max 10) |
| Dice | dice.lua | 0.01 | 1 | 10000 | 100000 | minChance 0.01, maxChance 98 (percent) |
| Hilo | hilo.lua | 0.01 per guess | 1 | 10000 | 100000 | — |
| Keno | keno.lua | About 1%, built into its payout tables | 1 | 10000 | 100000 | — |
| Limbo | limbo.lua | 0.01 | 1 | 10000 | 100000 | minTarget 1.01, maxTarget 1000000 |
| Mines | mines.lua | 0.01 | 1 | 10000 | 100000 | — |
| Plinko | plinko.lua | About 1%, built into its payout tables | 1 | 10000 | 100000 | — |
| Roulette | roulette.lua | 2.70%, single zero | 0.1 | 10000 for all of one player's bets on a spin | — | limits per bet type (straight 250 up to even 5000), chips, interval 25 seconds, statsSpins 100, bots (enabled, min 1, max 4) |
| Tower | tower.lua | 0.02 | 1 | 10000 | 100000 | — |
| Wheel | wheel.lua | 0.01 | 1 | 10000 | 100000 | The layouts are built for 0.01; another edge scales every multiplier |
The bots in Blackjack, Crash and Roulette also need casino.bots.enabled, and Blackjack's casino.bots.blackjack.
Example:
luaConfig.Casino.games.dice = {
enabled = true,
edge = 0.02,
minBet = 1,
maxBet = 5000,
maxProfit = 50000,
minChance = 0.01,
maxChance = 98,
}
#Cases and items
The cases are Config.Casino.cases in shared/unlocked/casino/cases.lua, below the game's settings, and the items they drop are Config.Casino.items in shared/unlocked/casino/items.lua. Won items go to the player's vault and sell back for their full value in credits. The defaults are 8 cases, from Street Starter at 100 credits to High Roller at 50000, and 69 items.
- A case is
{id, label, price, image, drops}, with itspricein credits. Each drop is{item, weight}. The weights are relative, and each default case totals about 100000, so a weight of 1 is a 0.001% chance. - An item is
id = {label, category, rarity, value, image}, with itsvaluein credits. The categories group items on the site (clothing,shoes,jewelry,watch,accessory,electronics,cashandvehicle), and the rarities arecommon,uncommon,rare,epic,legendaryandmythic. - Images are paths in the casino's image folder,
nui/casino/in the built UI, such ascases/tech_drop.webpanditems/cut_ruby.webp. - A drop naming an item that isn't in
items.lua, or with a weight that isn't a whole number of at least 1, leaves its whole case out with a warning in the server console.
Keep each case's expected value, the sum of weight * value divided by the case's total weight, at about 92% of its price. That 8% is the house edge; a case worth more than it costs gives credits away.
Example:
luaConfig.Casino.items = {
cut_ruby = {label = "Cut Ruby", category = "jewelry", rarity = "uncommon", value = 320, image = "items/cut_ruby.webp"},
}
Config.Casino.cases = {
{id = "tech_drop", label = "Tech Drop", price = 300, image = "cases/tech_drop.webp", drops = {
{item = "arcwave_controller", weight = 41224},
{item = "veyra_boombox", weight = 27506},
{item = "halden_telephoto", weight = 558},
}},
}