🛒SLEEK SHOP SYSTEM
Here check the installation guide
INSTALLATION GUIDE
Download from KEYMASTER and Unzip the
forge-shops.pack.zip
and place this folder in your server's resource folder.Add the resource to your server start config:
ensure forge-shops
,the name of the folder must not be changed or the resource will not function correctly.Clear the cache of your server and also of your own FiveM.
Reboot the entire server with the forge script well ensured in your server.cfg.
This script does not have any SQL to install.
Do not rename this script, this may cause it to fail when opening the interface.
CONFIG
The following explains all the settings. Take a few minutes to understand them to provide your users with the best experience.
Store Customization: Each store is unique. Edit the item list in each store to add, remove, or modify items as you wish. You can use the default items from the config or add your own.
Job Restrictions: To restrict a store so that only specific jobs can open it, add a variable like this:
allowedJobs = {"police"}
This example is used in Store 1, but feel free to add or remove it in any store to limit access to one or more jobs.
Fill all the CONFIG very carefully.
Config, Lang, Noti = {}, {}, {}
-- _____ _____ _ _ ______ _____ _____ _ _______ ___ _____ _____ _____ _ _
-- / __ \ _ | \ | || ___|_ _| __ \ | | | ___ \/ _ \_ _|_ _| _ | \ | |
-- | / \/ | | | \| || |_ | | | | \/ | | | |_/ / /_\ \| | | | | | | | \| |
-- | | | | | | . ` || _| | | | | __| | | | /| _ || | | | | | | | . ` |
-- | \__/\ \_/ / |\ || | _| |_| |_\ \ |_| | |\ \| | | || | _| |_\ \_/ / |\ |
-- \____/\___/\_| \_/\_| \___/ \____/\___/\_| \_\_| |_/\_/ \___/ \___/\_| \_/
-- Use "esx" or "qb"
Config.Framework = "esx"
-- If you are using one of the most recent versions of ESX, set the script name. Default = "es_extended"
Config.ESXExport = "es_extended"
-- Default ESX: "esx:getSharedObject" | Default QB: "qb-core"
Config.Core = "esx:getSharedObject"
-- Font for alerts. If you use Chinese language set to 0
Config.alertTextFont = 4
-- Account you use in esx/qb to cash at the bank
Config.bankaccount = "bank"
-- Account you use in esx/qb to collect cash
Config.cashaccount = "cash"
-- Choose if you want to use target (put the name of the one you have, it can be qb-target, ox-target...)
Config.UseTarget = {false,"ox-target","OPEN SHOP"}
-- The distance from the player at which the peds (if you decide to put shop NPCs) will spawn. By spawning based on proximity, it makes everything run smoother since there won't be many PEDs spawned at once
Config.PedSpawnRadius = 50
-- Maximum quantity that can be selected in the shop UI popup (does not override server-side stack limits)
Config.UIShopMaxQuantity = 20
-- _ _ _____ _____ ___________ _____ _____ ___ _____ _____ _____ _ _ _____
-- | \ | | _ |_ _|_ _| ___|_ _/ __ \ / _ \_ _|_ _| _ | \ | |/ ___|
-- | \| | | | | | | | | | |_ | | | / \// /_\ \| | | | | | | | \| |\ `--.
-- | . ` | | | | | | | | | _| | | | | | _ || | | | | | | | . ` | `--. \
-- | |\ \ \_/ / | | _| |_| | _| |_| \__/\| | | || | _| |_\ \_/ / |\ |/\__/ /
-- \_| \_/\___/ \_/ \___/\_| \___/ \____/\_| |_/\_/ \___/ \___/\_| \_/\____/
function notifications(notitype, message, time)
--Change this trigger for your notification system keeping the variables
exports['forge-notify']:ShowNotification(notitype, message, time)
end
--Notifications types:
Noti.info = 'info'
Noti.check = 'success'
Noti.error = 'error'
--Notification time:
Noti.time = 5000
-- _ ___ _ _ _____ _ _ ___ _____ _____
-- | | / _ \ | \ | | __ \ | | |/ _ \| __ \| ___|
-- | | / /_\ \| \| | | \/ | | / /_\ \ | \/| |__
-- | | | _ || . ` | | __| | | | _ | | __ | __|
-- | |____| | | || |\ | |_\ \ |_| | | | | |_\ \| |___
-- \_____/\_| |_/\_| \_/\____/\___/\_| |_/\____/\____/
Lang.pressE = "Press ~b~[~w~E~b~]~w~ to access shop"
Lang.noMoney = "You don't have enough money"
Lang.addedItems = "Products have been added to your inventory"
Lang.cart = "CART"
Lang.totalPrice = "TOTAL PRICE"
Lang.PayByBank = "PAY BY BANK"
Lang.PayByCash = "PAY BY CASH"
Lang.noJobAccess = "You don't have the required job to access this shop"
-- Popup texts
Lang.PopupTitle = "Select Quantity"
Lang.PopupAddButton = "Add"
Lang.PopupCloseButton = "Cancel"
Lang.PopupUpdateButton = "Update"
-- _____ _ _ ___________ _____
-- / ___| | | | _ | ___ \/ ___|
-- \ `--.| |_| | | | | |_/ /\ `--.
-- `--. \ _ | | | | __/ `--. \
-- /\__/ / | | \ \_/ / | /\__/ /
-- \____/\_| |_/\___/\_| \____/
Config.animations = { -- Add as many animations as you want. Each NPC shopkeeper will do one of these animations.
{
name = "air_wave",
dict = "anim@amb@waving@female"
},
{
name = "ground_wave",
dict = "anim@amb@waving@male"
},
{
name = "base",
dict = "amb@world_human_hang_out_street@female_arms_crossed@base"
},
{
name = "rcmme_amanda1_stand_loop_cop",
dict = "anim@amb@nightclub@peds@"
},
{
name = "idle_reject_loop_a",
dict = "mini@hookers_spvanilla"
},
{
name = "friends@fra@ig_1",
dict = "amb@world_human_hang_out_street@male_b@idle_a"
},
{
name = "idle",
dict = "mp_move@prostitute@m@french"
},
{
name = "celebration_idle_f_a",
dict = "anim@mp_celebration@idles@female"
},
{
name = "idle_a",
dict = "anim@mp_corona_idles@male_d@idle_a"
},
{
name = "idle_a",
dict = "amb@world_human_hang_out_street@female_hold_arm@idle_a"
},
{
name = "drunk_driver_stand_loop_dd2",
dict = "random@drunk_driver_1"
},
{
name = "idle_a",
dict = "amb@world_human_hang_out_street@Female_arm_side@idle_a"
},
{
name = "idle",
dict = "rcmjosh1"
},
{
name = "ig_3_base_tracy",
dict = "timetable@amanda@ig_3"
},
{
name = "knuckle_crunch",
dict = "anim@mp_player_intcelebrationfemale@knuckle_crunch"
},
{
name = "fidget_sniff_fingers",
dict = "move_p_m_two_idles@generic"
},
{
name = "idle_ped06",
dict = "anim@miss@low@fin@vagos@"
},
{
name = "rehearsal_base_idle_director",
dict = "misscarsteal4@aliens"
},
--{
--name = "ground_wave",
--dict = "anim@amb@waving@male"
--}
}
Shops = {
-- SUPER-MARKETS
["Supermarket 24/7 Hollywood"] = { --[1]
label = "Shop", -- Add this to any shop where you need it to have a different label than the shop name.
ped = { use = true, model = "CS_Josh", coords = vector4(24.5, -1346.19, 28.5, 266.78), useAnimations = true }, -- This will be the ped that will appear in the store, as shopkeeper. You can set false if you don't want there to be a ped.
coord = vector3(4.5, -1346.19, 29.5),
marker = {use = true, style = 23, r = 51, g = 175, b = 213},
blip = {use = true, style = 52, color = 3, scale = 0.8, text = "Shop"},
allowedJobs = {"police"}, -- If you want to restrict access to the shop to certain jobs, add the job name here. If you want everyone to have access, delete this line.
items = {
["Water"] = {
itemName = "water_bottle",
itemImage = "water.png",
itemPrice = 10,
special = false
},
["Coffee"] = {
itemName = "coffee",
itemImage = "coffee.png",
itemPrice = 10,
special = false
},
["Cola"] = {
itemName = "kurkakola",
itemImage = "cola.png",
itemPrice = 10,
special = false
},
["Bandage"] = {
itemName = "bandage",
itemImage = "bandage.png",
itemPrice = 10,
special = false
},
["Lighter"] = {
itemName = "lighter",
itemImage = "lighter.png",
itemPrice = 10,
special = false
},
["Snickers"] = {
itemName = "snickers",
itemImage = "snickers.png",
itemPrice = 10,
special = false
},
["Twix"] = {
itemName = "twix",
itemImage = "twix.png",
itemPrice = 10,
special = false
},
["Vodka"] = {
itemName = "vodka",
itemImage = "vodka.png",
itemPrice = 10,
special = false
},
["Whiskey"] = {
itemName = "whiskey",
itemImage = "whiskey.png",
itemPrice = 10,
special = false
},
["Sandwich"] = {
itemName = "sandwich",
itemImage = "sandwich.png",
itemPrice = 10,
special = false
},
-- specials
["Advanced Kit"] = {
itemName = "advancedkit",
itemImage = "advancedkit.png",
itemPrice = 10,
special = true
},
["Binoculars"] = {
itemName = "binoculars",
itemImage = "binoculars.png",
itemPrice = 10,
special = true
},
["Cleaning Kit"] = {
itemName = "cleaningkit",
itemImage = "cleaningkit.png",
itemPrice = 10,
special = true
},
["Diving Gear"] = {
itemName = "divinggear",
itemImage = "divinggear.png",
itemPrice = 10,
special = true
},
["First Aid"] = {
itemName = "firstaid",
itemImage = "firstaid.png",
itemPrice = 10,
special = true
},
}
},
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
Here is everything related to open functions, such as target functions, in case you use a target script other than OX or QB.
Functions = Functions or {}
if Config.Framework == "esx" then
if Config.ESXExport ~= "" then
ESX = exports[Config.ESXExport]:getSharedObject()
else
Citizen.CreateThread(function()
while ESX == nil do
TriggerEvent(Config.Core, function(obj) ESX = obj end)
Citizen.Wait(0)
end
end)
end
elseif Config.Framework == "qb" then
QBCore = exports[Config.Core]:GetCoreObject()
else
print("Unrecognized framework")
end
Functions.CreateTarget = function()
if Config.UseTarget[1] then
for shopName, shop in pairs(Shops) do
local _options = {
{
name = 'shop' .. shopName,
event = 'forge:1',
icon = 'fa-solid fa-shop',
distance = 2.5,
label = Config.UseTarget[3],
shopName = shopName
}
}
if Config.UseTarget[2] == "qb-target" then
_options = {
{
name = 'shop' .. shopName,
event = 'forge:1',
icon = 'fa-solid fa-shop',
label = Config.UseTarget[3],
shopName = shopName
}
}
end
local data = {
targetType = 'AddBoxZone',
identifier = 'shop' .. shopName,
coords = vec3(shop.coord.x, shop.coord.y, shop.coord.z),
heading = 10.0,
width = 2.0,
length = 1.0,
minZ = shop.coord.z - 0.9,
maxZ = shop.coord.z + 0.9,
options = _options
}
CreateTarget(data)
end
AddEventHandler('forge:1', function(data)
local shopName = data.shopName
local playerJob = GetPlayerJob()
local allowed = true
if Shops[shopName].allowedJobs and #Shops[shopName].allowedJobs > 0 then
allowed = false
for _, job in ipairs(Shops[shopName].allowedJobs) do
if job == playerJob then
allowed = true
break
end
end
end
if allowed then
actualShop = shopName
visibleAlert = false
items = ""
specialitems = ""
cartitems = ""
totalamount = 0
shopcartlist = {}
for name, itemdata in pairs(Shops[shopName].items) do
if itemdata.special then
specialitems = specialitems..'<div class="forge-shops-specialitem" data-value="'..name..'"><div class="forge-shops-box-background1"></div><div class="forge-shops-text-background"></div><span class="forge-shops-text16"><span>'..name..'</span></span><img src="public/items/'..itemdata.itemImage..'" alt="IMGSPECIALITEM1634" class="forge-shops-i-m-g-s-p-e-c-i-a-l-i-t-e-m"/></div>'
else
items = items..'<div class="forge-shops-item" data-value="'..name..'"><div class="forge-shops-box-background2"></div><div class="forge-shops-itemname"><div class="forge-shops-text-background1"></div><span class="forge-shops-text18"><span>'..name..'</span></span></div><img src="public/items/'..itemdata.itemImage..'" alt="IMGITEM325" class="forge-shops-i-m-g-i-t-e-m1"/><img src="public/playground_assets/addbackground1746-zw1-200h.png" alt="AddBackground1746" class="forge-shops-add-background"/><img src="public/playground_assets/addvector1119-bsxo.svg" alt="AddVector1119" class="forge-shops-add-vector"/></div>'
end
end
StartScreenEffect(blur, 1, true)
SetDisplay(not display, items, specialitems, Shops[shopName].label ~= nil and Shops[shopName].label or shopName)
else
TriggerEvent("forge-shops:notifications", Noti.error, Lang.noJobAccess)
end
end)
else
local shopsRendered = {}
while true do
local playerCoords = GetEntityCoords(PlayerPedId())
for shopName, shop in pairs(Shops) do
if #(playerCoords - shop.coord) < 15 then
if not shopsRendered[shopName] then
shopsRendered[shopName] = true
Citizen.CreateThread(function()
while true do
local pedcoords = GetEntityCoords(PlayerPedId())
local distance = #(pedcoords - shop.coord)
if distance > 15 then
shopsRendered[shopName] = false
break
end
if shop.marker.use then
DrawMarker(shop.marker.style, shop.coord.x, shop.coord.y, shop.coord.z, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 2.0, 2.0, 2.0, shop.marker.r, shop.marker.g, shop.marker.b, 50, false, true, 2, nil, nil, false)
end
if distance < 2 then
if visibleAlert then
drawTxt(Lang.pressE, Config.alertTextFont, 1, 0.5, 0.8, 0.6, 255, 255, 255, 255)
end
if IsControlJustPressed(0, 38) then
local playerJob = GetPlayerJob()
local allowed = true
if shop.allowedJobs and #shop.allowedJobs > 0 then
allowed = false
for _, job in ipairs(shop.allowedJobs) do
if job == playerJob then
allowed = true
break
end
end
end
if allowed then
actualShop = shopName
visibleAlert = false
items = ""
specialitems = ""
cartitems = ""
totalamount = 0
shopcartlist = {}
for name, itemdata in pairs(shop.items) do
if itemdata.special then
specialitems = specialitems..'<div class="forge-shops-specialitem" data-value="'..name..'"><div class="forge-shops-box-background1"></div><div class="forge-shops-text-background"></div><span class="forge-shops-text16"><span>'..name..'</span></span><img src="public/items/'..itemdata.itemImage..'" alt="IMGSPECIALITEM1634" class="forge-shops-i-m-g-s-p-e-c-i-a-l-i-t-e-m"/></div>'
else
items = items..'<div class="forge-shops-item" data-value="'..name..'"><div class="forge-shops-box-background2"></div><div class="forge-shops-itemname"><div class="forge-shops-text-background1"></div><span class="forge-shops-text18"><span>'..name..'</span></span></div><img src="public/items/'..itemdata.itemImage..'" alt="IMGITEM325" class="forge-shops-i-m-g-i-t-e-m1"/><img src="public/playground_assets/addbackground1746-zw1-200h.png" alt="AddBackground1746" class="forge-shops-add-background"/><img src="public/playground_assets/addvector1119-bsxo.svg" alt="AddVector1119" class="forge-shops-add-vector"/></div>'
end
end
StartScreenEffect(blur, 1, true)
SetDisplay(not display, items, specialitems, shop.label ~= nil and shop.label or shopName)
shopsRendered[shopName] = false
break
else
notifications(Noti.error, Lang.noJobAccess, Noti.time)
end
end
Citizen.Wait(0) -- comprobación rápida cuando está muy cerca
else
Citizen.Wait(500) -- reduce consumo si no está tan cerca
end
end
end)
end
end
end
Citizen.Wait(1000)
end
end
end
function CreateTarget(data)
if Config.UseTarget[2] == "qb-target" then
if data.targetType == 'AddBoxZone' then
exports.qtarget:AddBoxZone(data.identifier, data.coords, data.length, data.width, {
name = data.identifier,
heading = data.heading,
debugPoly = false,
minZ = data.coords.z - 0.7,
maxZ = data.coords.z + 1.5,
}, {
options = data.options,
distance = 3.0,
})
elseif data.targetType == 'entity' then
exports.qtarget:AddTargetEntity(data.entitys, {
options = data.options,
label = data.identifier,
distance = 2.5
})
end
elseif Config.UseTarget[2] == "ox-target" then
if data.targetType == 'AddBoxZone' then
exports.ox_target:addBoxZone({ coords = data.coords, options = data.options })
elseif data.targetType == 'entity' then
exports.ox_target:addLocalEntity(data.entitys, data.options)
end
end
end
function GetPlayerJob()
if Config.Framework == "esx" then
local playerData = ESX.GetPlayerData()
return playerData.job.name
elseif Config.Framework == "qb" then
local playerData = QBCore.Functions.GetPlayerData()
return playerData.job.name
end
return nil
end
function CanCarryItem(source, item, amount)
-- ESX
-- local esxPlayer = ESX.GetPlayerFromId(source)
-- return esxPlayer.canCarryItem(item, amount)
-- QBCore
-- local canAdd, reason = exports['qb-inventory']:CanAddItem(source, item, amount)
-- return canAdd
-- Ox
return exports['ox_inventory']:CanCarryItem(source, item, amount)
-- Quasar
-- return exports['qs-inventory']:CanCarryItem(source, item, amount)
end
If you want to edit the aesthetics or design. You have the HTML open so you can modify the style and everything as you want.
The script is RESPONSIVE for all resolutions as well.
Last updated