Wert Drug Sell System

You can access the setup information you need about this product


🚀 Installation

Welcome to the Wert Drug Selling System documentation. Follow these steps to install and configure the script on your server.

Note: This script is encrypted using FiveM Asset Escrow system. Ensure your server license key is linked to the same CFX account used for the purchase.

📋 Requirements

Before installing, ensure you have the following dependencies installed and started:

  • Framework: QBCore, ESX, or QBox ( Configurable )

  • ox_lib (Required for UI and Logic)


🛠️ Step-by-Step Installation

1. Download & Extract

Download the wert_drugsell package from your Keymaster assets. Extract the folder into your server's resources directory.

2. Configure Framework

Open shared/config.lua and ensure the framework settings match your server.

Lua

Config.Framework = 'qb' -- Options: 'qb', 'esx', 'qbox'

3. Database Setup (Auto-Install) ✨

You do not need to run any SQL files manually.

The script includes an Auto-Migration system. When you start the script for the first time, it will automatically create the necessary database tables (wert_drugsell_users, transactions, etc.) and load the dealers.

4. Add Items to Inventory

Since this script works seamlessly with ox_inventory, you need to register the drug items and the tablet.

Open ox_inventory/data/items.lua and paste the following codes:

Lua

['drugtablet'] = {
    label = 'Black Market Tablet',
    weight = 500,
    stack = false,
    close = true,
    description = 'An encrypted tablet used for illegal network communication.',
    client = {
        image = 'drugtablet.png', -- Make sure to add the image
    }
},

['weed_whitewidow'] = {
    label = 'White Widow (Pack)',
    weight = 100,
    stack = true,
    close = true,
    description = 'High quality strain.',
    client = {
        image = 'weed_baggy.png',
    }
},

['cokebaggy'] = {
    label = 'Bag of Coke',
    weight = 10,
    stack = true,
    close = true,
    description = 'Pure product.',
    client = {
        image = 'cocaine_baggy.png',
    }
},

['meth'] = {
    label = 'Meth Crystal',
    weight = 10,
    stack = true,
    close = true,
    description = 'Blue crystal.',
    client = {
        image = 'meth_baggy.png',
    }
},

{% hint style="warning" %}

Important: Ensure the item names match exactly what is in your Config.Drugs inside shared/config.lua. If you change item names there, change them here too.

{% endhint %}

5. Item Images

Copy the images provided in the assets/images folder of the script and paste them into:

ox_inventory/web/images/

6. Server Config

Add the script to your server.cfg file. Ensure it is started after the dependencies.

Kod snippet'i

ensure ox_lib
ensure ox_inventory
ensure oxmysql
ensure wert_drugsell

7. Restart Server

Restart your server. Watch the server console; you should see a message confirming that the database tables have been checked and dealers have been initialized.


🌍 Localization (Translation)

You can easily translate the entire interface without editing the source code.

  1. Navigate to wert_drugsell/web/build/locales.json.

  2. Open the file with any text editor (VS Code, Notepad++).

  3. Edit the values on the right side to your desired language.

  4. Restart the script.

Example:

JSON

"sidebar": {
    "home": "Ana Sayfa", 
    "chat": "Sohbet"
}

OPEN FILES

SHARED/CONFIG.LUA

Config = {}

Config.Debug = false
Config.Framework = 'qb' -- 'qb' or 'esx' or 'qbox'
Config.TabletItem = 'drugtablet'

Config.TabletStream = {
    dict = "amb@code_human_in_bus_passenger_idles@female@tablet@idle_a",
    clip = "idle_a",
    flag = 49,
    model = `prop_cs_tablet`,
    bone = 28422,
    pos = vector3(-0.05, 0.0, 0.0),
    rot = vector3(0.0, 0.0, 0.0)
}

Config.MoneyIcon = "nui://ox_inventory/web/images/money.png"

Config.PoliceAlertChance = {
    active = false,
    param = 10
}

Config.GangControl = { -- Do you want only the gangs to have access?
    active = false, -- active state?
    perms = { -- allowed gangs for system?
        ballas = true,
        vagos = true,
        families = true,
        -- If u want add more
    }
}

Config.CornerSellChance = 50
Config.CornerSellCooldown = 0

Config.Levels = {
    [1] = { requiredXP = 0, label = "Street Rat" },
    [2] = { requiredXP = 500, label = "Runner" },
    [3] = { requiredXP = 1500, label = "Hustler" },
    [4] = { requiredXP = 3500, label = "Supplier" },
    [5] = { requiredXP = 7000, label = "Kingpin" },
}

Config.Drugs = {
    ['weed_ak47'] = {
        label = "Weed Baggy",
        price = { min = 150, max = 220 },
        xp = 10,
        risk = false,
        image = "nui://ox_inventory/web/images/weed_ak47.png"
    },
    ['cokebaggy'] = {
        label = "Bag of Coke",
        price = { min = 300, max = 450 },
        xp = 25,
        risk = true,
        image = "nui://ox_inventory/web/images/cokebaggy.png"
    },
    ['meth'] = {
        label = "Meth Crystal",
        price = { min = 400, max = 600 },
        xp = 35,
        risk = true,
        image = "nui://ox_inventory/web/images/meth_baggy.png"
    }
}

Config.Dealers = {
    ['gustavo'] = {
        name = "Gustavo",
        region = "South Side",
        coords = vector4(-92.3008, -2205.0381, 7.8117, 0.4437), -- Koordinatları ayarla
        model = 'g_m_y_mexgoon_01',
        image = "nui://wert_drugsell/web/images/dealers/gustavo.png"
    },
    ['vlad'] = {
        name = "Little Vlad",
        region = "Downtown",
        coords = vector4(-98.6998, -2205.0425, 7.8117, 2.4237),
        model = 'a_m_y_business_02',
        image = "nui://wert_drugsell/web/images/dealers/littlevlad.png"
    }
}

Config.MessageItemControl = true -- # For the get messages from buyer, player must be a drug tablet in inventory.
Config.MessageChance = { -- Do you want an extra chance check so that the message can be sent to the player?
    active = false,
    param = 10, -- Max 100
}
Config.MessageSettings = {
    minTime = 2, -- Min Time For Get Message 2
    maxTime = 5, -- Max Time For Get Message 5
    npcNames = { "Unknown", "Customer", "Junkie", "Blue", "Ghost", "T-Bone" },
    maxPendingLimit = 3,   -- How many unread messages can a player have?
    expirationTime = 30,    -- How many minutes should the message be deleted after?
    allowExpire = true, -- expirationTime? can work ?
    locations = {
        { label = "Grove St. Gas Station", coords = vector3(-62.8336, -1784.8881, 26.9940), heading = 141.2766 },
        { label = "Mirror Park", coords = vector3(1113.1360, -636.4269, 55.8128), heading = 99.8651 },
        { label = "Vespucci Canals", coords = vector3(-1100.9879, -1196.3208, 1.3549), heading = 303.2260 },
        -- Buraya daha fazla koordinat ekle
    }
}


Config.BuyerPeds = {
    'a_m_y_beach_01', 'a_m_y_genstreet_01', 'g_m_y_ballaeast_01', 
    'a_f_y_hipster_01',
}

Config.CornerPositions = {
    { pos = vector4(-1179.1772, -1509.5145, 4.3453, 205.0501), distance = 150.0 },
    { pos = vector4(14.3249, -1663.2289, 29.2768, 318.2131), distance = 150.0 },
    { pos = vector4(209.6659, -71.7806, 69.0762, 40.8674), distance = 150.0 }
}

Config.SomeLangs = {
    locationSet = "Buyer location set. Go meet them.",
    permission = 'You are not have permission for that!',
    goDealer = "Go to %s to collect cash.",
    itemError = "You don't have the items!",
    noFunds = "No funds to collect.",
    soldMessage = "Sold %sx %s for $%s",
    collectedMessage = "Collected $%s from %s",
    noNeedItem = 'You are not have enough item!',
}
CLIENT/EDITABLE_CLIENT.LUA

Framework = nil
local oxInventoryActive = GetResourceState('ox_inventory') == 'started'
if Config.Framework == 'qb' or Config.Framework == 'qbox' then
    Framework = exports['qb-core']:GetCoreObject()
elseif Config.Framework == 'esx' then
    Framework = exports["es_extended"]:getSharedObject()
end

-- # Global Variables

drugSellDuty = false
MyGang = 'nil'
drugSelectedItems = {}

local function LoadPlayerMetas()
    Wait(1000)
    local myPlayerData = lib.callback.await('wert_drugsell:server:load-player-meta', false)
    drugSellDuty = myPlayerData.drugSellDuty
    drugSelectedItems = myPlayerData.drugSelectedItems
    MyGang = myPlayerData.MyGang
end

function SelectCustomNotify(text, style, duration)
    lib.notify({ title = 'Drug Sell', description = text, type = style, duration = duration })
end

function CheckPoliceAlert(data)
    if not data then return end
    -- data table inners if u want use in police call
    --[[ data.label -> Label
    data.image -> Image
    data.amount -> Amount
    data.price -> Price ]]
    -- data.type -> 'mission_sell' or 'dealer_collect' or 'corner_sell'
    local pos = GetEntityCoords(cache.ped) -- # Alert Position

    -- Add ur police alert export
end

function CheckHasItem(key)
    if oxInventoryActive then
        local itemCount = exports.ox_inventory:GetItemCount(key)
        if itemCount and itemCount > 0 then
            return true
        end
        return false
    end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return QBCore.Functions.HasItem(key)
    end
    return false
end

function WertGetPeds(ignoreList)
    ignoreList = ignoreList or {}
    local ents = GetGamePool('CPed')
    local entities = {}
    local ignoreMap = {}
    for i = 1, #ignoreList do
        ignoreMap[ignoreList[i]] = true
    end
    for i = 1, #ents do
        local entity = ents[i]
        if not ignoreMap[entity] then
            entities[#entities + 1] = entity
        end
    end
    return entities
end

RegisterNetEvent('wert_drugsell:client:custom-select-notify', SelectCustomNotify)

AddEventHandler('onResourceStart', function(resource)
    if resource ~= GetCurrentResourceName() then return end
    LoadPlayerMetas()
end)

RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    LoadPlayerMetas()
end)

RegisterNetEvent('esx:playerLoaded', function()
    LoadPlayerMetas()
end)
SERVER/EDITABLE_SERVER.LUA

Framework = nil
Bridge = {}
if Config.Framework == 'qb' or Config.Framework == 'qbox' then
    Framework = exports['qb-core']:GetCoreObject()
elseif Config.Framework == 'esx' then
    Framework = exports["es_extended"]:getSharedObject()
end

local function SaveMetadataToSQL(citizenid, data)
    if not citizenid then return end
    MySQL.update('UPDATE wert_drugsell_users SET metadata = ? WHERE citizenid = ?', { json.encode(data or {}), citizenid })
end

local function GetMetadaFromSQL(citizenid, includeUserData)
    local defaultMeta = { drug_duty = false, drug_selecteditems = {} }
    local userData = MySQL.single.await('SELECT * FROM wert_drugsell_users WHERE citizenid = ?', {citizenid})
    if not userData then
        MySQL.insert.await('INSERT INTO wert_drugsell_users (citizenid, metadata) VALUES (?, ?)', { citizenid, json.encode(defaultMeta) })
        if includeUserData then
            userData = { xp = 0, level = 1, settings = nil, metadata = defaultMeta }
            return userData
        end
        return defaultMeta
    end
    userData.metadata = userData.metadata and json.decode(userData.metadata) or defaultMeta
    if includeUserData then
        userData.settings = userData.settings and json.decode(userData.settings) or nil
        return userData
    end
    return userData.metadata
end

function Bridge.GetPlayer(src)
    if not src then return nil end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return Framework.Functions.GetPlayer(src)
    else
        return Framework.GetPlayerFromId(src)
    end
end

function Bridge.GetPlayerByIdentifier(identifier)
    if not identifier then return nil end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return Framework.Functions.GetPlayerByCitizenId(identifier)
    else
        return Framework.GetPlayerFromIdentifier(identifier)
    end
end


function Bridge.GetPlayerIdentifier(ply)
    if not ply then return nil end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return ply.PlayerData.citizenid
    end
    return ply.getIdentifier()
end

function Bridge.AddMoney(src, account, amount)
    local ply = Bridge.GetPlayer(src)
    if not ply then return end
    amount = tonumber(amount)
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        ply.Functions.AddMoney(account, amount)
    else
        if account == 'cash' then
            ply.addMoney(amount, "Drug Sell System")
        else
            ply.addAccountMoney(account, amount, "Drug Sell System")
        end
    end
end


function Bridge.RemoveItem(src, item, amount)
    local ply = Bridge.GetPlayer(src)
    if not ply then return end
    amount = tonumber(amount)
    if GetResourceState('ox_inventory') == 'started' then
        local success = exports.ox_inventory:RemoveItem(src, item, amount)
        return success
    end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        local item = ply.Functions.GetItemByName(item)
        if item and item.amount >= amount then
            ply.Functions.RemoveItem(item, amount)
            return true
        end
    else
        local item = ply.getInventoryItem(item)
        if item and item.count >= amount then
            ply.removeInventoryItem(item, amount)
            return true
        end
    end
    return false
end

function Bridge.CanRemoveItem(src, item, amount)
    local ply = Bridge.GetPlayer(src)
    if not ply then return end
    amount = tonumber(amount)
    if GetResourceState('ox_inventory') == 'started' then
        local itemCountt = exports.ox_inventory:GetItemCount(src, item)
        if not itemCountt then itemCountt = 0 end
        if itemCountt >= amount then
            return true
        end
        return false
    end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        local item = ply.Functions.GetItemByName(item)
        if item and item.amount >= amount then
            return true
        end
    else
        local item = ply.getInventoryItem(item)
        if item and item.count >= amount then
            return true
        end
    end
    return false
end

function Bridge.SetDuty(src, state)
    local ply = Bridge.GetPlayer(src)
    if not ply then return end
    local citizenid = Bridge.GetPlayerIdentifier(ply)
    local metadata = GetMetadaFromSQL(citizenid)
    metadata.drug_duty = state
    SaveMetadataToSQL(citizenid, metadata)
end

function Bridge.SetSelectedItems(src, data)
    local ply = Bridge.GetPlayer(src)
    if not ply then return end
    local citizenid = Bridge.GetPlayerIdentifier(ply)
    local metadata = GetMetadaFromSQL(citizenid)
    metadata.drug_selecteditems = data
    SaveMetadataToSQL(citizenid, metadata)
end

function Bridge.GetUIComponents(ply, includeUserData)
    if not ply then return nil end
    local citizenid = Bridge.GetPlayerIdentifier(ply)
    local userData = GetMetadaFromSQL(citizenid, includeUserData)
    local metadata = includeUserData and userData.metadata or userData
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return {
            name = ply.PlayerData.charinfo.firstname .. " " .. ply.PlayerData.charinfo.lastname,
            cash = ply.PlayerData.money['cash'],
            isDuty = metadata.drug_duty,
            selectedItems = metadata.drug_selecteditems or {},
            gang = ply.PlayerData.gang.name,
            userData = includeUserData and userData or nil
        }
    else
        return {
            name = ply.getName(),
            cash = ply.getMoney(),
            isDuty = metadata.drug_duty,
            selectedItems = metadata.drug_selecteditems or {},
            gang = 'none', -- If u use any gang system please provide export !IMPORTANT FOR ESX USERS
            userData = includeUserData and userData or nil
        }
    end
end

function Bridge.FramePlayers()
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return Framework.Functions.GetPlayers()
    else
        return Framework.GetPlayers()
    end
end

function Bridge.Source(ply)
    if not ply then return 0 end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        return ply.PlayerData.source
    else
        return ply.source
    end
end

function Bridge.CheckTablet(ply, src)
    if not ply then return false end
    if GetResourceState('ox_inventory') == 'started' then
        local tabletCount = exports.ox_inventory:GetItemCount(src, Config.TabletItem)
        if not tabletCount then tabletCount = 0 end
        if tabletCount > 0 then return true end
        return false
    end
    if Config.Framework == 'qb' or Config.Framework == 'qbox' then
        local item = ply.Functions.GetItemByName(Config.TabletItem)
        if item and item.amount > 0 then
            return true
        end
    else
        local item = ply.getInventoryItem(Config.TabletItem)
        if item and item.count > 0 then
            return true
        end
    end
    return false
end

-- # Item Use

if Config.Framework == 'qb' or Config.Framework == 'qbox' then
    Framework.Functions.CreateUseableItem(Config.TabletItem, function(source)
        TriggerClientEvent('wert_drugsell:client:openTablet', source)
    end)
else
    Framework.RegisterUsableItem(Config.TabletItem, function(playerId)
        TriggerClientEvent('wert_drugsell:client:openTablet', tonumber(playerId))
    end)
end

🆘 Support

If you encounter any issues during installation, please check the server console for errors first. For further assistance, join our Discord Server and open a ticket.

Last updated