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
Preview Video V1
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 |