Copy
return {
-- Use only if needed, directed by support or know what you're doing
-- Notice: enabling debug features will significantly increase resmon
-- And should always be disabled in production
debug = false,
-- Set your interaction system below
-- Available options are: 'auto', 'ox_target', 'qb-target', 'interact' & 'custom'
-- 'auto' will automatically detect and use the available interaction system
-- 'custom' needs to be added to client/utils/interact.lua
interact = 'auto',
-- Set your notification system below
-- Available options are: 'lation_ui', 'ox_lib', 'esx', 'qb', 'okok', 'sd-notify', 'wasabi_notify' & 'custom'
-- 'custom' needs to be added to client/utils/notify.lua
notify = 'ox_lib',
-- Do you want to be notified via server console if an update is available?
-- True if yes, false if no
version = true,
-- Set your society banking system below
-- Available options are: 'auto', 'qb-banking', 'qb-management', 'esx_addonaccount', 'Renewed-Banking',
-- 'okokBanking', 'fd_banking', 'tgg-banking' or 'custom'
-- 'auto' will automatically detect based on your framework and available banking resources
-- 'custom' needs to be added to server/utils/banking.lua
banking = 'auto',
}
Copy
Framework, Inventory = nil, nil
PlayerLoaded = false
-- Handle player login
function OnPlayerLoaded()
PlayerLoaded = true
TriggerEvent('lation_shops:playerLoaded')
end
-- Handle player logout
function OnPlayerLogout()
PlayerLoaded = false
TriggerEvent('lation_shops:playerDropped')
end
-- Initialize framework
local function InitFramework()
if GetResourceState('es_extended') == 'started' then
ESX = exports['es_extended']:getSharedObject()
Framework = 'esx'
RegisterNetEvent('esx:playerLoaded', function() OnPlayerLoaded() end)
RegisterNetEvent('esx:onPlayerLogout', function() OnPlayerLogout() end)
elseif GetResourceState('qbx_core') == 'started' then
Framework = 'qbx'
AddEventHandler('QBCore:Client:OnPlayerLoaded', function() OnPlayerLoaded() end)
RegisterNetEvent('qbx_core:client:playerLoggedOut', function() OnPlayerLogout() end)
elseif GetResourceState('qb-core') == 'started' then
QBCore = exports['qb-core']:GetCoreObject()
Framework = 'qb'
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() OnPlayerLoaded() end)
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() OnPlayerLogout() end)
elseif GetResourceState('ox_core') == 'started' then
Ox = require '@ox_core.lib.init'
Framework = 'ox'
AddEventHandler('ox:playerLoaded', function() OnPlayerLoaded() end)
AddEventHandler('ox:playerLogout', function() OnPlayerLogout() end)
else
-- Add custom framework here
end
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
OnPlayerLoaded()
end
end)
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
OnPlayerLogout()
end
end)
end
-- Initialize inventory
local function InitInventory()
if GetResourceState('ox_inventory') == 'started' then
Inventory = 'ox_inventory'
elseif GetResourceState('qb-inventory') == 'started' then
Inventory = 'qb-inventory'
elseif GetResourceState('qs-inventory') == 'started' then
Inventory = 'qs-inventory'
elseif GetResourceState('ps-inventory') == 'started' then
Inventory = 'ps-inventory'
elseif GetResourceState('origen_inventory') == 'started' then
Inventory = 'origen_inventory'
elseif GetResourceState('codem-inventory') == 'started' then
Inventory = 'codem-inventory'
elseif GetResourceState('core_inventory') == 'started' then
Inventory = 'core_inventory'
elseif GetResourceState('tgiann-inventory') == 'started' then
Inventory = 'tgiann-inventory'
elseif GetResourceState('jaksam_inventory') == 'started' then
Inventory = 'jaksam_inventory'
else
-- Add custom inventory here
end
end
-- Returns player data
function GetPlayerData()
if Framework == 'esx' then
return ESX.GetPlayerData()
elseif Framework == 'qb' then
return QBCore.Functions.GetPlayerData()
elseif Framework == 'qbx' then
return exports.qbx_core:GetPlayerData()
elseif Framework == 'ox' then
return Ox.GetPlayer()
else
-- Add custom framework here
end
end
-- Returns player licenses
--- @param license string|nil Specific licence to get
function GetPlayerLicense(license)
local player = GetPlayerData()
if not player then return end
if Framework == 'esx' then
return lib.callback.await('lation_shops:getPlayerLicense', false, license)
elseif Framework == 'qb' then
return player.metadata.licences[license] or false
elseif Framework == 'qbx' then
return player.metadata.licences[license] or false
elseif Framework == 'ox' then
-- TODO
else
-- Add custom framework here
end
end
-- Returns player job
function GetPlayerJob()
local player = GetPlayerData()
if not player then return {} end
if Framework == 'esx' then
return {
name = player.job.name,
label = player.job.label,
grade = player.job.grade
}
elseif Framework == 'qb' then
return {
name = player.job.name,
label = player.job.label,
grade = player.job.grade.level
}
elseif Framework == 'qbx' then
return {
name = player.job.name,
label = player.job.label,
grade = player.job.grade.level
}
else
-- Add custom framework here
end
return {}
end
-- Return all jobs
function GetAllJobs()
if Framework == 'esx' then
return lib.callback.await('lation_shops:getAllJobs', false)
elseif Framework == 'qb' then
return QBCore.Shared.Jobs
elseif Framework == 'qbx' then
return exports.qbx_core:GetJobs()
elseif Framework == 'ox' then
return Ox.GetGroupsByType('job')
else
-- Add custom framework here
end
return {}
end
-- Sanitize item data for certain inventories
local function sanitize(items)
local sanitized = {}
for itemName, itemData in pairs(items or {}) do
if type(itemData) == 'table' then
sanitized[itemName] = {}
for key, value in pairs(itemData) do
if type(key) == 'string' and not tonumber(key) and type(value) ~= 'function' then
if (key == 'label' or key == 'description') and type(value) == 'table' then
sanitized[itemName][key] = select(2, next(value)) or "INVALID"
elseif type(value) == 'table' then
local clean = {}
for k, v in pairs(value) do
if type(v) ~= 'function' then clean[k] = v end
end
if next(clean) then sanitized[itemName][key] = clean end
else
sanitized[itemName][key] = value
end
end
end
end
end
return sanitized
end
-- Return data for item and image path
function GetAllItems()
local items
if Inventory then
if Inventory == 'ox_inventory' then
items = exports[Inventory]:Items()
elseif Inventory == 'qb-inventory' then
items = QBCore.Shared.Items
elseif Inventory == 'ps-inventory' then
items = QBCore.Shared.Items
elseif Inventory == 'qs-inventory' then
items = exports[Inventory]:GetItemList()
elseif Inventory == 'origen_inventory' then
items = exports[Inventory]:GetItems()
elseif Inventory == 'codem-inventory' then
items = exports[Inventory]:GetItemList()
elseif Inventory == 'core_inventory' then
-- No available client-side export to get item list
if Framework == 'qb' then
items = QBCore.Shared.Items
else
print('^1[ERROR]: An issue has occured, please contact support at https://discord.gg/9EbY4nM5uu^0')
return nil
end
elseif Inventory == 'tgiann-inventory' then
items = exports[Inventory]:GetItemList()
elseif Inventory == 'jaksam_inventory' then
items = exports[Inventory]:getStaticItemsList()
else
-- Add custom inventory here
return nil
end
else
if Framework == 'esx' then
-- Unlikely to need anything here but.. just in case..
print('^1[ERROR]: An error has occured with lation_shops - please contact support^0')
return nil
elseif Framework == 'qb' then
items = QBCore.Shared.Items
elseif Framework == 'qbx' then
-- Unlikely to need anything here but.. just in case..
print('^1[ERROR]: Are you really not using ox_inventory? Contact support please lul.^0')
return nil
end
end
return sanitize(items)
end
-- Return image path for inventory items
function GetImagePath()
if Inventory then
if Inventory == 'ox_inventory' then
return 'nui://ox_inventory/web/images/'
elseif Inventory == 'qb-inventory' then
return 'nui://qb-inventory/html/images/'
elseif Inventory == 'ps-inventory' then
return 'nui://ps-inventory/html/images/'
elseif Inventory == 'qs-inventory' then
return 'nui://qs-inventory/html/images/'
elseif Inventory == 'origen_inventory' then
return 'nui://origen_inventory/html/images/'
elseif Inventory == 'codem-inventory' then
return 'nui://codem-inventory/html/itemimages/'
elseif Inventory == 'core_inventory' then
return 'nui://core_inventory/html/images/'
elseif Inventory == 'tgiann-inventory' then
return 'nui://inventory_images/images/'
elseif Inventory == 'jaksam_inventory' then
return 'nui://jaksam_inventory/_images/'
else
-- Add custom inventory here
end
end
end
-- Check if player has item
--- @param item string
--- @param amount number
--- @return boolean
function HasItem(item, amount)
if not item or not amount then return false end
if Inventory then
if Inventory == 'ox_inventory' then
return exports[Inventory]:Search('count', item) >= amount
elseif Inventory == 'core_inventory' then
return exports[Inventory]:hasItem(item, amount)
elseif Inventory == 'jaksam_inventory' then
return exports[Inventory]:getTotalItemAmount(item) >= amount
else
return exports[Inventory]:HasItem(item, amount)
end
else
local playerData = GetPlayerData()
if not playerData then return false end
local inventory = Framework == 'esx' and playerData.inventory or playerData.items
if not inventory then return false end
for _, itemData in pairs(inventory) do
if itemData and itemData.name == item then
local count = itemData.amount or itemData.count or 0
if count >= amount then
return true
end
end
end
return false
end
end
-- Disables access to open/view inventory
function DisableInventory()
if Inventory == 'ox_inventory' then
LocalPlayer.state.invBusy = true
elseif Inventory == 'qb-inventory' then
LocalPlayer.state.inv_busy = true
elseif Inventory == 'qs-inventory' then
exports[Inventory]:setInventoryDisabled(true)
elseif Inventory == 'core_inventory' then
exports[Inventory]:lockInventory()
elseif Inventory == 'tgiann-inventory' then
exports[Inventory]:SetInventoryActive(false)
else
-- Add custom inventory here
end
end
-- Enables access to open/view inventory
function EnableInventory()
if Inventory == 'ox_inventory' then
LocalPlayer.state.invBusy = false
elseif Inventory == 'qb-inventory' then
LocalPlayer.state.inv_busy = false
elseif Inventory == 'qs-inventory' then
exports[Inventory]:setInventoryDisabled(false)
elseif Inventory == 'core_inventory' then
exports[Inventory]:unlockInventory()
elseif Inventory == 'tgiann-inventory' then
exports[Inventory]:SetInventoryActive(true)
else
-- Add custom inventory here
end
end
InitFramework()
InitInventory()
Copy
local setup = require 'config.setup'
-- Auto-detect interaction system
local function DetectInteractSystem()
if GetResourceState('ox_target') == 'started' then
return 'ox_target'
elseif GetResourceState('qb-target') == 'started' then
return 'qb-target'
elseif GetResourceState('interact') == 'started' then
return 'interact'
else
print('^1[ERROR]: No interaction system was auto-detected.^0')
return nil
end
end
-- Get the interaction system to use
local interact = setup.interact == 'auto' and DetectInteractSystem() or setup.interact
-- Add interaction
--- @param data table
local function AddInteraction(data)
if interact == 'ox_target' then
exports.ox_target:addSphereZone(data)
elseif interact == 'qb-target' then
exports['qb-target']:AddCircleZone(data.name, data.coords, data.radius, {
name = data.name,
debugPoly = setup.debug}, {
options = data.options,
distance = 2,
})
elseif interact == 'interact' then
exports.interact:AddInteraction({
coords = data.coords,
interactDst = 2.0,
id = data.name,
options = data.options
})
elseif interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system defined in the config/setup.lua file^0')
end
end
-- Add entity interaction
--- @param entity number Entity number
--- @param data table Options table
local function AddEntityInteraction(entity, data)
if interact == 'ox_target' then
exports.ox_target:addLocalEntity(entity, data)
elseif interact == 'qb-target' then
exports['qb-target']:AddTargetEntity(entity, {options = data, distance = 1.75})
elseif interact == 'interact' then
exports.interact:AddLocalEntityInteraction({
entity = entity,
interactDst = 1.75,
options = data
})
elseif interact == 'custom' then
-- Add support for a custom interaction system here
else
print('^1[ERROR]: No interaction system defined in the config/setup.lua file^0')
end
end
-- Add model interaction
--- @param model string Model
--- @param data table Options table
local function AddModelInteraction(model, data)
if interact == 'ox_target' then
exports.ox_target:addModel(model, data)
elseif interact == 'qb-target' then
exports['qb-target']:AddTargetModel(model, {options = data, distance = 1.75})
elseif interact == 'interact' then
exports.interact:AddModelInteraction({
model = model,
id = model,
interactDst = 1.75,
options = data
})
elseif interact == 'custom' then
-- Add support for a custom interaction system here
else
print('^1[ERROR]: No interaction system defined in the config/setup.lua file^0')
end
end
-- Remove interaction
--- @param name string
local function RemoveInteraction(name)
if interact == 'ox_target' then
exports.ox_target:removeZone(name)
elseif interact == 'qb-target' then
exports['qb-target']:RemoveZone(name)
elseif interact == 'interact' then
exports.interact:RemoveInteraction(name)
elseif interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system defined in the config/setup.lua file^0')
end
end
-- Remove entity interaction
--- @param entity any|number
--- @param data table|string
local function RemoveEntityInteraction(entity, data)
if interact == 'ox_target' then
exports.ox_target:removeLocalEntity(entity, data)
elseif interact == 'qb-target' then
exports['qb-target']:RemoveTargetEntity(entity, data)
elseif interact == 'interact' then
exports.interact:RemoveLocalEntityInteraction(entity, data)
elseif interact == 'custom' then
-- Add support for a custom interaction system here
else
print('^1[ERROR]: No interaction system defined in the config/setup.lua file^0')
end
end
-- Remove model interaction
--- @param model string Model
local function RemoveModelInteraction(model)
if interact == 'ox_target' then
exports.ox_target:removeModel(model, nil)
elseif interact == 'qb-target' then
exports['qb-target']:RemoveTargetModel(model, nil)
elseif interact == 'interact' then
exports.interact:RemoveModelInteraction(model, model)
elseif interact == 'custom' then
-- Add support for a custom interaction system here
else
print('^1[ERROR]: No interaction system defined in the config/setup.lua file^0')
end
end
return {
AddInteraction = AddInteraction,
AddEntityInteraction = AddEntityInteraction,
AddModelInteraction = AddModelInteraction,
RemoveInteraction = RemoveInteraction,
RemoveEntityInteraction = RemoveEntityInteraction,
RemoveModelInteraction = RemoveModelInteraction
}
Copy
local setup = require 'config.setup'
-- Auto-detect banking system
local function DetectBankingSystem()
if GetResourceState('okokBanking') == 'started' then
return 'okokBanking'
elseif GetResourceState('Renewed-Banking') == 'started' then
return 'Renewed-Banking'
elseif GetResourceState('fd_banking') == 'started' then
return 'fd_banking'
elseif GetResourceState('tgg-banking') == 'started' then
return 'tgg-banking'
elseif GetResourceState('wasabi_banking') == 'started' then
return 'wasabi_banking'
end
if Framework == 'qb' then
if GetResourceState('qb-banking') == 'started' then
local version = GetResourceMetadata('qb-banking', 'version', 0)
if version and tonumber(string.sub(version, 1, 3)) >= 2 then
return 'qb-banking'
end
end
if GetResourceState('qb-management') == 'started' then
return 'qb-management'
end
elseif Framework == 'esx' then
if GetResourceState('esx_society') == 'started' then
return 'esx_addonaccount'
end
end
print('^1[ERROR]: No banking system auto-detected.^0')
return nil
end
-- Get the banking system to use
local banking = setup.banking == 'auto' and DetectBankingSystem() or setup.banking
-- Get society account balance
--- @param society string Society name
--- @param shopId string|nil Shop ID for tracking
--- @return number
local function GetSocietyBalance(society, shopId)
if banking == 'qb-banking' then
return exports['qb-banking']:GetAccountBalance(society) or 0
elseif banking == 'qb-management' then
return exports['qb-management']:GetAccount(society) or 0
elseif banking == 'esx_addonaccount' then
local balance = promise.new()
TriggerEvent("esx_society:getSociety", society, function(data)
if not data then return balance:resolve(0) end
TriggerEvent("esx_addonaccount:getSharedAccount", data.account, function(account)
return balance:resolve(account.money)
end)
end)
return Citizen.Await(balance)
elseif banking == 'Renewed-Banking' then
return exports['Renewed-Banking']:getAccountMoney(society) or 0
elseif banking == 'okokBanking' then
return exports.okokBanking:GetAccount(society) or 0
elseif banking == 'fd_banking' then
return exports.fd_banking:GetAccount(society) or 0
elseif banking == 'tgg-banking' then
return exports['tgg-banking']:GetSocietyAccountMoney(society) or 0
elseif banking == 'wasabi_banking' then
return exports.wasabi_banking:GetAccountBalance(society, 'society') or 0
else
-- Add custom banking here
end
return 0
end
-- Add money to society account
--- @param society string Society name
--- @param amount number Amount to add
--- @param shopId string|nil Shop ID for tracking
local function AddSocietyMoney(society, amount, shopId)
if banking == 'qb-banking' then
exports['qb-banking']:AddMoney(society, amount)
elseif banking == 'qb-management' then
exports['qb-management']:AddMoney(society, amount)
elseif banking == 'esx_addonaccount' then
TriggerEvent("esx_society:getSociety", society, function(data)
TriggerEvent("esx_addonaccount:getSharedAccount", data.account, function(account)
account.addMoney(amount)
end)
end)
elseif banking == 'Renewed-Banking' then
exports['Renewed-Banking']:addAccountMoney(society, amount)
elseif banking == 'okokBanking' then
exports.okokBanking:AddMoney(society, amount)
elseif banking == 'fd_banking' then
exports.fd_banking:AddMoney(society, amount)
elseif banking == 'tgg-banking' then
exports['tgg-banking']:AddSocietyMoney(society, amount)
elseif banking == 'wasabi_banking' then
exports.wasabi_banking:AddMoney('society', society, amount)
else
-- Add custom banking here
end
end
-- Remove money from society account
--- @param society string Society name
--- @param amount number Amount to remove
--- @param shopId string|nil Shop ID for tracking
local function RemoveSocietyMoney(society, amount, shopId)
if banking == 'qb-banking' then
exports['qb-banking']:RemoveMoney(society, amount)
elseif banking == 'qb-management' then
exports['qb-management']:RemoveMoney(society, amount)
elseif banking == 'esx_addonaccount' then
TriggerEvent("esx_society:getSociety", society, function(data)
TriggerEvent("esx_addonaccount:getSharedAccount", data.account, function(account)
account.removeMoney(amount)
end)
end)
elseif banking == 'Renewed-Banking' then
exports['Renewed-Banking']:removeAccountMoney(society, amount)
elseif banking == 'okokBanking' then
exports.okokBanking:RemoveMoney(society, amount)
elseif banking == 'fd_banking' then
exports.fd_banking:RemoveMoney(society, amount)
elseif banking == 'tgg-banking' then
exports['tgg-banking']:RemoveSocietyMoney(society, amount)
elseif banking == 'wasabi_banking' then
exports.wasabi_banking:RemoveMoney('society', society, amount)
else
-- Add custom banking here
end
end
-- Get custom payment method balance
--- @param source number Player ID
--- @param paymentId string Payment method ID
--- @param shopId string|nil Shop ID for tracking
--- @return number Balance
local function GetCustomBalance(source, paymentId, shopId)
-- Example: Handle special custom payment methods here
-- By default, paymentId is treated as an item name
if paymentId == 'example_special_currency' then
-- return exports.your_resource:GetBalance(source) or 0
else
-- Use paymentId as item name (e.g., 'black_money', 'crypto', 'gold')
return GetItemCount(source, paymentId) or 0
end
return 0
end
-- Remove custom payment method
--- @param source number Player ID
--- @param paymentId string Payment method ID
--- @param amount number Amount to remove
--- @param shopId string|nil Shop ID for tracking
local function RemoveCustomMoney(source, paymentId, amount, shopId)
-- Example: Handle special custom payment methods here
-- By default, paymentId is treated as an item name
if paymentId == 'example_special_currency' then
-- exports.your_resource:RemoveMoney(source, amount)
else
-- Use paymentId as item name (e.g., 'black_money', 'crypto', 'gold')
RemoveItem(source, paymentId, amount)
end
end
return {
GetSocietyBalance = GetSocietyBalance,
AddSocietyMoney = AddSocietyMoney,
RemoveSocietyMoney = RemoveSocietyMoney,
GetCustomBalance = GetCustomBalance,
RemoveCustomMoney = RemoveCustomMoney
}
