Shop
Digital Den, the shop studio, backplates and finishes, laptops on show, staff storages and door locks.
Players buy laptops, parts, tools and accessories at a shop counter, and put a new finish or backplate on a laptop they carry. Every offer and price is in shared/unlocked/shop.lua, under Config.Shop, and the server checks each purchase against it.
#Digital Den
The default shop is Digital Den, a laptop showroom at the Morningwood strip mall. It stands inside zyke_digitalden, a free interior that comes with the laptop. The counter, the staff shelves under storages, the doors under doorlock and the laptops under displays are all placed for that interior.
zyke_digitalden is optional. The laptop starts without it, so a server whose map already fills that unit can leave it out and move the shop.
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},
},
},
Each location gets a shop keeper and a blip. coords is where the shop keeper stands, at eye height, and the heading it faces. Leave model out for a counter without a shop keeper, and blip out for no blip.
With interaction = "point" in shared/unlocked/config.lua, the shop keeper has an interaction point where E opens the shop. With "target", Browse laptops is in the target menu instead, the same setting placed laptops use.
#The Shop Studio
The shop opens as a studio. What the player looks at stands on the counter in front of the shop keeper: a new laptop in the finish and backplate they pick, their own laptop, a part or an accessory. A side panel lists the offers, with the player's money in each account the shop takes.
The studio closes when the player walks more than 4 metres from the counter, dies or falls over.
| Section | What it sells |
|---|---|
| Laptops | A new laptop, built in three steps: Model with its specs, Finish, and Backplate. Each finish and backplate is priced on top of the laptop and paid in the same purchase. I looks inside the laptop at the Model step. |
| Customise | A new backplate or finish for a laptop the player carries. Pick the laptop first; the one in the player's hands is listed first. The Backplates and Finish tabs each show only while that service is on. |
| Parts | New parts at full condition, grouped by kind. Pointing at another part compares their specs. Memory and coolers show Fits your laptop or Doesn't fit your laptop against the laptops the player carries. |
| Extras | Tools and accessories, bought by the amount, up to 10 at a time. |
A section the shop sells nothing in is hidden. Customise shows a hint to buy a laptop first when the player carries none.
After each purchase a short moment plays: what was bought turns once under a light, and a new laptop or item is lifted off the counter. Any key skips it.
#Studio Keys
The keys are shown on screen. Only the keys the open section uses are listed.
| Action | Key |
|---|---|
| Rotate | Mouse drag |
| Zoom | Scroll wheel |
| Browse | Arrow keys |
| Change the amount, when buying by the amount | Left and right arrows (up and down browse) |
| Previous or next category | Q or E |
| Next step, or switch between Backplates and Finish | Tab |
| Look inside a new laptop, or close it up | I |
| Open or close the lid | L |
| Auto-turn, or stop turning | T |
| Buy | Enter |
| Back | Backspace |
Escape clears the backplate search, or closes the studio when the search is empty.
#Items in the Studio
Parts and the laptop's own tools and accessories are shown as their props. A tool or accessory of your own shows its inventory image, or a prop when its entry sets prop:
luatools = {
enabled = true,
items = {
{item = "laptop_screwdriver", price = 45},
{item = "my_toolkit", price = 80, prop = "prop_tool_box_04"},
},
},
The studio finds the counter top in front of the shop keeper by itself. When it can't, set studio = vec4(x, y, z, heading) on the location: the spot on the counter, and the heading from it towards the customer.
#Offers & Prices
luaConfig.Shop = {
enabled = true,
accounts = {"cash", "bank"},
laptops = {enabled = true, finishMarkup = 150, items = {{prebuilt = "basic", price = 900}, ...}},
refinish = {enabled = true, price = 400},
skins = {enabled = true, price = 250, prices = {}, bought = true},
parts = {enabled = true, items = {{item = "laptop_ram", model = "d5_16", price = 140}, ...}},
tools = {enabled = true, items = {{item = "thermal_paste", price = 25}, ...}},
accessories = {enabled = true, items = {{item = "usb_drive", price = 40}, {item = "recovery_usb", price = 2500, enabled = false}, ...}},
}
| Key | What it sells |
|---|---|
laptops | One laptop per entry, built as a prebuilt from shared/unlocked/hardware.lua. Each comes in the stock graphite and in every finish for finishMarkup more. finishMarkup = false sells graphite only. See Hardware & Servicing. |
refinish | A new finish, graphite included, on a laptop the player carries. |
skins | A backplate on a laptop the player carries, or on a new laptop. See Backplates. |
parts | New parts at full condition. model is the part's id in the board catalogue. |
tools, accessories | Items bought by the amount, new and with all their uses. A power bank comes fully charged. |
A category or a single entry with enabled = false is gone from the studio and refused by the server. Prices are whole numbers of 0 or more; an entry without a valid price is not sold. The recovery stick is listed but off by default.
Laptops, finishes and backplates live on the laptop item, so with item = false they are not sold.
#Payment & Server Checks
accounts lists the accounts a purchase is paid from. The first account that holds the full price pays it. cash and bank show as Cash and Bank in the studio; other accounts show their id.
The server checks every purchase:
- The player stands within 5 metres of the location.
- The offer is in the config, and the price is the config's, never the client's.
CanBuyLaptopShopItemallows it. A backplate bought with a new laptop is checked as well, and so isCanUseLaptopSkinfor any backplate.- The player can carry the item.
The money is taken first and the balance read back, so a framework that allows an overdraft can't be tricked. The item is given after. Whatever did not reach the inventory is refunded, and so is a finish or backplate the laptop item refused. A finish or backplate the laptop already has is refused before any money is taken.
A laptop bought at the counter is registered to the buyer once it is in their inventory. See Ownership & Recovery.
Each purchase fires the server event zyke_laptop:LaptopShopBought(plyId, offerId, total) and is logged as ShopPurchase. A new laptop bought with a backplate fires it twice, once for each offer at its own price.
#Backplates: Free or Bought
A backplate is a skin on the back of the lid, picked by category; see Using the Laptop. It is kept on the laptop item as skin, so it goes with the laptop when it is handed over.
skins.bought = true, the default, makes backplates something players buy. Settings only offers the stock lid and the backplate on that laptop, with a hint to get more at an electronics store, and the server refuses any other. Every backplate is paid in full, also one the laptop had before: one taken off in Settings, or replaced by another, has to be bought again.
skins.bought = false makes every skin free to pick in Settings. The shop still puts one on for a player who would rather pay than open Settings. Without a laptop item, item = false, skins are always free.
Every backplate costs skins.price. skins.prices sets another price for whole categories:
luaskins = {enabled = true, price = 250, prices = {material = 400, space = 350}, bought = true},
#Finishes
The chassis comes in anodised colours next to the stock Graphite:
| Id | Name |
|---|---|
silver | Silver |
black | Black |
midnight | Midnight Blue |
sky_blue | Sky Blue |
sage | Sage Green |
gold | Gold |
rose_gold | Rose Gold |
red | Red |
lavender | Lavender |
The finish is kept on the laptop item as finish and everyone nearby sees it, held or put down. Players do not pick a finish themselves; it comes from the shop or from the SetLaptopFinish export.
The shop offers the ids under finishes in shared/unlocked/config.lua. Delete an id to stop selling it; laptops that had it show graphite again. Only the finishes above exist, as each is a colour row built into the laptop's props.
#Laptops on Show
displays stands real laptops open on the shop's tables and shelves, each built as a prebuilt in a finish and, optionally, with a backplate. The defaults fill Digital Den's showroom with 27 of them.
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(-1327.643, -403.607, 36.090, 211.1), prebuilt = "flagship", skin = "carbon", replaces = "prop_laptop_01a"},
},
| Field | Description |
|---|---|
coords | Where the laptop stands, on the surface, and the heading someone using it would face. The screen faces the other way. |
prebuilt | A prebuilt id from shared/unlocked/hardware.lua. |
finish | One of finishes. Leave it out for the stock graphite. |
skin | A backplate id from skins. Leave it out for the stock lid. |
replaces | A map prop model, or a list of them, standing within 0.6 metres, such as the laptop an interior has on its desk. Hidden while the resource runs. |
Keep about 0.7 metres between two laptops in a row, so the sign beside each one fits.
#Spec Signs
Each display has an acrylic spec sign standing on its right. Its card shows the model, colour, processor, graphics, memory, storage, Wi-Fi, battery, backplate, and the shop's price, read from the prebuilt in shared/unlocked/hardware.lua. The price only shows while the shop is enabled and sells that laptop in that finish.
Players walk up to a sign and press E on Read spec card, or use the target menu with interaction = "target". The camera eases in to rest in front of the card. Backspace, E or the right mouse button steps back, and so does walking off.
The first 32 displays also show a small copy of their card from a distance; signs further down the list show their card once the player is near.
The displays are local props that nobody can use, pick up or carry. They exist on a client only within renderDistance from shared/unlocked/config.lua. They stay when the shop has enabled = false, without a price. Set displays = {} to have none.
#Staff Storages
storages puts a shared stash on a shelf for the shop's staff. The defaults are Digital Den's rack in the repair workshop and the one in the stock room:
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},
},
A storage is opened like the shop, with E on its interaction point or from the target menu, and only players with one of its jobs see it. Leave jobs out to let anyone open it. weight is in grams. The id names the stash in your inventory as zyke_laptop_<id>, so a new id starts an empty stash. Storages stay when the shop has enabled = false.
The server checks the player's place at the shelf and their job before the stash opens. CanOpenLaptopStorage adds your own rule on top, such as being on duty or a rank.
#Staff Job
extras/jobs/ has the digitalden job. It has five grades: Trainee, Technician, Senior Technician, Manager, and Owner as the boss.
| File | Framework | How |
|---|---|---|
esx.sql | ESX Legacy | Import it into your database, then run /refreshjobs or restart the server. |
qb-core.txt | QBCore | Paste the entry into qb-core/shared/jobs.lua and restart qb-core. |
qbx_core.txt | Qbox | Paste the entry into qbx_core/shared/jobs.lua and restart qbx_core. |
Give the job with /setjob <playerId> digitalden <grade>. To use a job you already have, put its name in jobs on each storage and door instead.
#Door Locks
doorlock locks Digital Den's doors to the digitalden job:
| Door | Starts |
|---|---|
| Front double doors | Unlocked, so customers can walk in |
| Repair room | Locked |
| Storage | Locked |
When the resource starts, the doors are added to ox_doorlock or rcore_doorlock, unless it already has a door by that name. Lock grades, auto-locking or anything else you change in your door lock stays.
| Door lock | What to do |
|---|---|
| ox_doorlock | Nothing. The doors are added when the resource starts. |
| rcore_doorlock | Change pin in doorlock first. rcore_doorlock can't take a job from another resource, so the doors open with that PIN until you give them the job in its /doors menu. |
| qb-doorlock | Copy extras/doorlock/qb-doorlock.lua into qb-doorlock/configs/, restart qb-doorlock, and set doorlock.enabled = false. |
| cd_doorlock | Paste extras/doorlock/ox_doorlock.sql into cd_doorlock's doorlock migration tool, and set doorlock.enabled = false. |
luadoorlock = {
enabled = true,
pin = "1234",
locks = {
{
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},
},
},
},
},
jobs takes job names, or {name = "digitalden", minGrade = 2} for a lowest grade. locked is how a new door starts, and distance, in metres, how close a player has to stand. Each door is its model and the coords and heading it stands at, two for a double door. Set enabled = false to use your own door setup; extras/doorlock/ox_doorlock.sql has the same doors to import yourself.
#Your Own Rules
CanBuyLaptopShopItem(plyId, offerId) in server/unlocked/hooks.lua is asked before the money is taken. Return false and a locale key to refuse, for example to keep flagships for donors:
luafunction CanBuyLaptopShopItem(plyId, offerId)
if (offerId:find("^laptop:flagship:") and not IsPlayerAceAllowed(plyId, "laptop.flagship")) then return false, "shopNotAllowed" end
return true
end
| Offer | Id |
|---|---|
| New laptop | laptop:<prebuilt>:<finish or stock>, such as laptop:performance:midnight |
| Refinish | refinish:<finish or stock> |
| Backplate | skin:<id> |
| Part | part:<item>:<model>, such as part:laptop_ram:d5_16 |
| Tool | tools:<item> |
| Accessory | accessories:<item> |
#Moving the Shop
To run the shop somewhere other than Digital Den, such as when your map fills that unit:
- Set the location's
coordsto where the shop keeper stands, at eye height. Addstudioif the laptop doesn't stand on the counter. - Move or remove the
storages. - Replace the
displayswith your own, or setdisplays = {}. - Change the doors under
doorlock.locks, or setdoorlock.enabled = false. - Leave
zyke_digitaldenout of your server config.
Add more entries to locations for more counters; each sells the same offers.
#A Player-Run Shop
Set enabled = false to take the counters away and leave the selling to players, such as a player-run Digital Den. Storages, displays and door locks stay. Your business script takes the money its own way and gives the goods with the server exports:
lua-- A laptop built as a prebuilt, in a finish and with a backplate. owner registers it to that character
local metadata = exports["zyke_laptop"]:CreateLaptopMetadata("performance", {finish = "midnight", skin = "carbon", owner = identifier})
exports.ox_inventory:AddItem(plyId, "laptop", 1, metadata)
-- A new part at full condition
exports.ox_inventory:AddItem(plyId, "laptop_ssd", 1, exports["zyke_laptop"]:CreatePartMetadata("laptop_ssd", "nv4_1tb"))
-- Tools and sticks need no metadata. A power bank gets its serial and charge on its own
exports.ox_inventory:AddItem(plyId, "thermal_paste", 2)
-- A new finish or backplate on the laptop item in the customer's slot, nil puts the stock one back
exports["zyke_laptop"]:SetLaptopFinish(plyId, slot, "gold")
exports["zyke_laptop"]:SetLaptopSkin(plyId, slot, "carbon")
With skins.bought = true, the backplate SetLaptopSkin puts on is the one Settings offers next to the stock lid, so it works the same with the shop turned off.
See Create Laptop Metadata, Create Part Metadata, Set Laptop Finish and Set Laptop Skin.