Upgrade Scripts Docs

#Shops & Loot

Shops are defined in Config.Shops, loot cases in Config.Cases. Eight shops ship by default, each unlocked at a higher level, but the list is entirely yours to change - add, remove or reorder them freely.

#A shop

{
    name = "Bum's Bargains",
    description = "Only the cheapest of items here. Nothing to write home about.",
    image = "./images/shops/bum.png",
    shopkeeper_image = "./images/shopkeepers/bum.png",
    shopkeeper_dialogue = "I find all my stuff in trash around the city! Best deals in town!",
    requiredLevel = 1,
    items = { ... }
}

requiredLevel is the level a player must reach before the shop appears in their menu. Images are relative to the resource's ui/images folder. If a shop leaves shopkeeper_image or shopkeeper_dialogue out, the defaults from Config.UI are used.

#Item types

Every item needs a type, a name, a label and a points price. Six types are supported.

item - gives an inventory item. name must match your inventory's item name, and count is how many.

{type = "item", name = "armor", label = "Armor", count = 10, points = 120}

cash - pays money into the account set by Config.CashRewardAccount. count is the amount of money.

{type = "cash", name = "cash_25k", label = "$25,000", count = 25000, points = 250}

vehicle - adds a vehicle to the player's garage. name is the spawn name. Optional vehicleType ('car', 'boat', 'aircraft') and garage override the defaults from Config.Garage for this one vehicle.

{type = "vehicle", name = "kamacho", label = "Kamacho", points = 375}

loot - sells a loot case from Config.Cases. name must match a case key.

{type = "loot", name = "low_loot", label = "Low Quality Case", points = 30, image = "./images/commoncase.png"}

points - grants reward points. Mostly useful inside loot cases rather than shops.

unjail - clears the player's remaining jail time. Only purchasable while they are actually in jail. Optional maxJailTime refuses sentences longer than that, so players cannot buy their way out of a long one.

{type = "unjail", name = "jail_release", label = "Get Out Of Jail", points = 500, maxJailTime = 100}

This reads the sentence from framework metadata, so it works with jail scripts that store it there - qb-policejob and qb-prison use injail, some ESX jails use jail. Point Config.Jail.MetadataKey at yours if it differs, and set Config.Jail.ReleaseClientEvent so your jail script knows to release the player.

#Purchase limits

Any item accepts two optional limits:

{type = "cash", name = "cash_5k", label = "$5,000", count = 5000, points = 50, maxPerDay = 2, cooldown = 3600}
  • maxPerDay - most purchases of this item in a rolling 24 hours
  • cooldown - seconds a player must wait between purchases of it

Both are enforced server-side and the player is told which limit they hit. Limits are tracked per shop and per item, keyed by the shop's name, so renaming a shop resets its counters.

#Item names can repeat

The same item name may appear in several shops at different quantities and prices. Purchases are keyed to the exact shop and slot the player clicked, so a 100-pack in a high-tier shop is never confused with a 5-pack of the same item lower down.

#Loot cases

Config.Cases = {
    low_loot = {
        label = "Low Quality Case",
        points = 30,
        items = {
            low  = { ... },
            high = { ... },
            rare = { ... },
        }
    },
}

Each case has three rarity pools. When a player buys a case, the server picks a pool, then picks one entry from it at random, and plays the reel animation before handing the reward over. The roll happens entirely on the server, so the result cannot be influenced from the client.

Entries look like shop items with one difference worth remembering: they use item, not name.

{type = "item",  item = "goldbar", label = "Gold Bar", count = 10},
{type = "cash",  item = "money",  label = "$15,000",  count = 15000},
{type = "points", label = "80 Rewards Points", count = 80},
{type = "vehicle", item = "impaler", label = "Drift Impaler", count = 1},

Use type = "cash" for money. Writing money as type = "item", item = "money" will not pay out, and the config check on start warns about exactly this.

#Rarity odds

Config.LootChances = { low = 80, high = 19, rare = 1 }

The default odds for every case. These are weights rather than percentages, so any numbers work - 80/19/1 happens to read as 80%, 19% and 1%.

An individual case can override them:

legendary_loot = {
    label = "Legendary Quality Case",
    points = 250,
    chances = { low = 60, high = 30, rare = 10 },
    items = { ... }
}

A pool with no entries is skipped and its weight ignored, so a case with an empty rare list still rolls correctly between the other two.

#If a claim fails

Points are taken when the case is bought. If the reward cannot be delivered - the claim expires, or the player's inventory is full - the points are refunded automatically and the refund appears in their history and in the Refunds webhook.