[Outdated] [PAID] [ESX] [UPADTE] Bryan Shops | Clean UI | Deliveries | Robberies | Co-Owners | Raids

Hello. This is my first Script Release on the Forums. I’m releasing my Shop script which includes many features that are perhaps desired by some people.
V2 Update has been released, all the features can be found below.

Tebex (Escrow): Unsupported
Tebex (Open Source): Unsupported
By purchasing either of these, you get access to both First and Second Versions

Requirements:

  • es_extended (1.2 / Legacy | Other versions haven not been tested)
  • mysql-async

Features:

  • Player Owned Shops
  • Description Editing
  • Shop Category Selection
  • Item Search
  • Resupplies
  • Shop Settings
  • Delivery Jobs

V2 Update:

  • Bigger UI
  • Fixed Sell Shop Bug
  • Fixed Notification Types
  • Player kicks from shop on shop close
  • Added Option To Change Text Colour
  • Added Co-Owner Option. Co-Owners can manage everything as an Owner, except sell the shop
  • Added Robberies. Each store’s robbery can be configured or disabled. Robbery takes items from shop’s inventory and balance
  • Added Police Raids. Now Police Jobs with certain grades can manage store’s inventory and settings. Few examples: for illegal items or inappropriate title
  • Added MF Inventory Compatibility
  • Added option to set up default store, when the shop hasn’t been purchased.
Config
Config = {}

Config.Locale = 'en'

Config.ShowOwners = true -- true = Let's owners select if they want their name displayed | false = disables all owner names displays

Config.MarkerDrawDistance = 15.0

Config.DatabaseUpdate = 5 -- Minutes, when database is automatically updated

Config.OpenStoreAfterRobbery = true -- Weather the store should automatically open after the robbery

Config.PoliceRaid = true -- Enable for police jobs to raid stores
Config.MinimumPolice = 1 -- How many police should be on for player to be able to rob

Config.UseMfInventory = false -- If your server is using MF-Inventory

Config.Locations = {
    [1] = {
        Name = 'General Store',
        Coords = vector3(-48.12, -1757.37, 28.58),
        Resupply = 'General', -- From Config.Resupplies
        Type = 'General', -- From Config.Types
        Price = 15000,
        Description = 'Store For People To Purchase',
        -- Owner = identifier, -- Preset Owner (If You Want)

        Robbery = { -- Can be set to false | Disable robbing
            Time = 1, -- Minutes
            GetMoney = 40, -- % Of The Store Balance
            MaxItems = 3, -- Maximum amount of items player can get from the store inventory
            Radius = 10.0, -- If radius is left, the robbery is canceled
            Cooldown = 30, -- Minutes
            CancelOnDeath = true, -- Cancel When Player Dies
        }
    },
    [2] = {
        Name = 'Hardware Store',
        Coords = vector3(-178.98, -1648.05, 32.35),
        Resupply = 'Hardware',
        Type = 'General',
        Price = 20000,
        Description = 'Store to purchase all the needed Hardware requirements',

        Robbery = false
    },
}

Config.DefaultSettings = {
    markerId = 1,
    markerSize = { x = 1.5, y = 1.5, z = 1.5 },
    isShopOpened = true,
    isMarkerEnabled = true,
    is3dTextEnabled = false,
    isShopOwnerEnabled = false,
    uiColor = '#919191',
    textColor = '#ffffff',
    markerColor = { r = 255, g = 255, b = 255, a = 255 },
}

-- What shops can resupply
Config.Resupplies = {
    ['General'] = {
        { item = 'bread', label = 'Bread', price = 3, time = 3 },
        { item = 'water', label = 'Water', price = 2, time = 5 },
        { item = 'burger', label = 'Burger', price = 5, time = 7 },
    },
    ['Hardware'] = {
        { item = 'phone', label = 'Phone', price = 200, time = 16 },
        { item = 'repairkit', label = 'Repair Kit', price = 150, time = 10 },
        { item = 'lockpick', label = 'Lockpick', price = 250, time = 8 },
    },
}

-- Categories for certain shops to choose
Config.Types = {
    ['General'] = {
        { name = 'food', label = 'Food', icon = 'fa-solid fa-burger' },
        { name = 'drinks', label = 'Drinks', icon = 'fa-solid fa-bottle-water' },
        { name = 'misc', label = 'Misc', icon = 'fa-solid fa-diamond' }
    }
}

-- Order Pickup Locations
Config.OrderLocations = {
    vector3(111.45, -1946.87, 20.73),
    vector3(233.96, -1735.29, 29.02)
}

Config.Blip = {
    Sprite = 59,
    Colour = 43,
    Scale = 0.8
}

-- Items which not should be shown when owner is trying to put in shop
Config.ItemsToExclude = {
    'money',
    'black_money'
}

-- Jobs that can deliver shop's orders
Config.AnyoneCanDeliver = false
Config.DeliveryJobs = {
    'ambulance'
}

-- Jobs able to raid stores
Config.PoliceJobs = {
    { name = 'police', minGrade = 2 },
}
Client Functions
FrameworkObject = 'esx:getSharedObject'

Notification = function(msg, type)
    ESX.ShowNotification(msg)
end

ShowHelpNotification = function(msg)
    ESX.ShowHelpNotification(msg)
end

Show3DText = function(msg, coords)
    ESX.Game.Utils.DrawText3D(coords + vector3(0.0, 0.0, 0.8), msg, 0.8, 4)
end

Show2DText = function(text)
    SetTextFont(4)
    SetTextScale(1.0, 0.5)
    SetTextWrap(0.0, 1.0)
    SetTextCentre(true)
    SetTextColour(255, 255, 255, 255)
    BeginTextCommandDisplayText('STRING')
    AddTextComponentSubstringPlayerName(text)
    EndTextCommandDisplayText(0.5, 0.95)
end

CreateShopBlip = function(coords, name)
    local blip = AddBlipForCoord(coords.x, coords.y, coords.z)

    SetBlipSprite(blip, Config.Blip.Sprite)
    SetBlipColour(blip, Config.Blip.Colour)
    SetBlipScale(blip, Config.Blip.Scale)
    SetBlipDisplay(blip, 4)
    SetBlipAsShortRange(blip, true)

    BeginTextCommandSetBlipName('STRING')
    AddTextComponentString(name)
    EndTextCommandSetBlipName(blip)

    return blip
end

CreateRouteBlip = function(coords)
    local blip = AddBlipForCoord(coords)
    SetBlipRoute(blip, true)
    SetBlipRouteColour(blip, 24)
    SetBlipColour(blip, 24)

    BeginTextCommandSetBlipName('STRING')
    AddTextComponentString('Order')
    EndTextCommandSetBlipName(blip)

    return blip
end

CanRobStore = function()
    -- Can add checks if cuffed or not

    if IsPedArmed(PlayerPedId(), 4) then
        return true
    end

    return false
end

IsDead = function()
    return IsPedDeadOrDying(PlayerPedId(), 1)
end

CanRaidStore = function()
    local job = ESX.GetPlayerData().job
    
    for k, v in pairs(Config.PoliceJobs) do
        if v.name == job.name and job.grade >= v.minGrade then
            return true
        end
    end

    return false
end

GetClosestPlayers = function()
    local data = {}
    local coords = GetEntityCoords(PlayerPedId())

    for k, v in pairs(GetGamePool('CPed')) do
        if #(coords - GetEntityCoords(v)) <= 10.0 and NetworkGetPlayerIndexFromPed(v) and GetPlayerServerId(NetworkGetPlayerIndexFromPed(v)) > 0 and NetworkGetPlayerIndexFromPed(v) ~= PlayerId() then
            table.insert(data, GetPlayerServerId(NetworkGetPlayerIndexFromPed(v)))
        end
    end

    return data
end
Server Functions
ESX = nil

TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)

RegisterNetEvent('esx:playerLoaded', function(playerId, xPlayer)
    UpdateClientShops(playerId)
end)

RegisterNetEvent('esx:playerDropped', function(playerId)
    RemovePlayerFromOffers(playerId)
end)

AddEventHandler('onResourceStop', function(resourceName)
    if resourceName == GetCurrentResourceName() then
        UpdateShopsDatabase()
    end
end)

Citizen.CreateThread(function()
    while true do
        Citizen.Wait(Config.DatabaseUpdate * 60 * 1000)

        UpdateShopsDatabase()
    end
end)

PoliceRaidedStore = function(officerName, shopName)
    -- Some kind of log to server console or discord
end

NotificationForPolice = function(coords)
    for k, v in pairs(ESX.GetPlayers()) do
        local job = ESX.GetPlayerFromId(v).getJob().name

        for _, j in pairs(Config.PoliceJobs) do
            if job == j.name then
                Notification(v, _U('announce_robbery_in_progress'), 'info')
                break
            end
        end
    end
end

NotificationForJobs = function(name, price)
    for k, v in pairs(ESX.GetPlayers()) do
        local job = ESX.GetPlayerFromId(v).getJob().name

        for _, j in pairs(Config.DeliveryJobs) do
            if job == j then
                Notification(v, _U('announce_shop_offer_placed', name, price), 'info')
                break
            end
        end
    end
end

GetPoliceCount = function()
    local count = 0

    for k, v in pairs(ESX.GetPlayers()) do
        local job = GetPlayerJob(v)

        for _, j in pairs(Config.PoliceJobs) do
            if job == j.name then
                count = count + 1
                break
            end
        end
    end

    return count
end

GetPlayerMoney = function(source)
    return ESX.GetPlayerFromId(source).getAccount('money').money
end

AddPlayerMoney = function(source, value)
    ESX.GetPlayerFromId(source).addAccountMoney('money', value)
end

RemovePlayerMoney = function(source, value)
    ESX.GetPlayerFromId(source).removeAccountMoney('money', value)
end

GetIdentifier = function(source)
    return ESX.GetPlayerFromId(source).getIdentifier()
end

GetPlayerInventory = function(source)
    local data = {}

    for k, v in pairs(ESX.GetPlayerFromId(source).getInventory()) do
        local available = true
        
        for _, j in pairs(Config.ItemsToExclude) do
            if j == v.name then available = false; break; end
        end

        if available and v.count > 0 then
            table.insert(data, v)
        end
    end

    return data
end

GetInventoryItem = function(source, item)
    return ESX.GetPlayerFromId(source).getInventoryItem(item)
end

RemovePlayerItem = function(source, item, count)
    ESX.GetPlayerFromId(source).removeInventoryItem(item, count)
end

AddPlayerItem = function(source, item, count)
    ESX.GetPlayerFromId(source).addInventoryItem(item, count)
end

CanPlayerCarry = function(source, item, count)
    return ESX.GetPlayerFromId(source).canCarryItem(item, count)
end

GetPlayerJob = function(source)
    return ESX.GetPlayerFromId(source).getJob().name
end

GetPlayerNameSrc = function(source)
    return ESX.GetPlayerFromId(source).getName()
end

GetPlayerNameIdentifier = function(identifier)
    local result = MySQL.Sync.fetchAll('SELECT firstname, lastname FROM users WHERE identifier = @identifier', {['@identifier'] = identifier})
    local name = 'N/A'

    if result and result[1] then
        name = string.format('%s %s.', result[1].firstname, result[1].lastname:sub(1, 1))
    end

    return name
end

GetStoreItemRandomId = function(storeId)
    local randomId = math.random(1000, 9999)

    while FindStoreItem(storeId, randomId) ~= -1 do
        randomId = math.random(1000, 9999)
        Citizen.Wait(10)
    end

    return randomId
end

GetStoreOrderRandomId = function(storeId)
    local randomId = math.random(100000, 999999)

    while FindOrder(storeId, randomId) ~= -1 do
        randomId = math.random(1000, 9999)
        Citizen.Wait(10)
    end

    return randomId
end

RoundNumber = function(number)
    return ESX.Math.Round(number)
end
Pictures V1








Preview Video V1

https://streamable.com/od5nur

V2 Update’s Pictures and Video will be posted soon.

This script is OUTDATED! It won’t work on newer ESX versions. Working on an update to fix that

Code is accessible No / Yes
Subscription-based No
Lines (approximately) >1600(LUA) 1500(JS)
Requirements es_extended mysql-async
Support Yes
2 Likes

Can it be supplied entirely from items in the player’s inventory?

Really Great script bought it for my server, so easy to use and has great customisation! Has more features than any shop script ive seen before Highly recommend to anyone thinking of buying this!

2 Likes

yea it can

This thing is sooooooo dope and good better than all the other ones out there
And the dev is helpful too

1 Like

Yes. Today I will have video posted.

This is “YOUR” script??? I know that the SAME ( UI THE SAME AND ALL FEATURES ) system was released a while ago called esx_stores… :thinking:

Yes. Design was a little bit inspired by S4 Shops. Not sure of which esx_stores you’re talking about, but all the code was written entirely by me.

2 Likes


Why can’t I download?

Not sure. I will check in an hour, perhaps I uploaded it incorrectly.

Fixed the issue. You can download it now :+1:

1 Like

V2 is 100 percent a great addition to this already amazing script! The robberies and police raids already add way more roleplay possibility! Great work again Bryan. I recommend this to anyone looking for a new shop script hands down the best there is!

1 Like

This is really dope and optimised

Broski helps and replies quick and fixes stuff quicker than flash

is it possible that every job can deliver without adding every single job?

You mean like it wouldn’t require any job to do delivery?

yes thats what i mean

I can make that a feature :+1:

nice how long would it take ? i will buy it today or tommorrow

Tomorrow i could do it

Can order delivery be turned into actual item delivery?