Copy
return {
-- ⚠️ WARNING: When you are working with this script, never do "restart lation_chopshop"
-- ⚠️ This will cause issues, data loss & more! You must restart the script like this:
-- ⚠️ "stop lation_chopshop" ..wait a couple seconds.. then "ensure lation_chopshop"
----------------------------------------------
-- 🛠️ Setup the basics below
----------------------------------------------
setup = {
-- 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: 'ox_target', 'qb-target', 'interact' & 'custom'
-- 'custom' needs to be added to client/functions.lua
interact = 'ox_target',
-- 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/functions.lua
notify = 'ox_lib',
-- Set your progress bar system below
-- Available options are: 'lation_ui', 'ox_lib', 'qbcore' & 'custom'
-- 'custom' needs to be added to client/functions.lua
-- Any custom progress bar must also support animations
progress = 'ox_lib',
-- Set your minigame (skillcheck) system below
-- Available options are: 'lation_ui', 'ox_lib' & 'custom'
minigame = 'ox_lib',
-- Do you want to use Lith Studios Bolt Minigame?
-- This is a free, interactive minigame for removing wheels
-- Learn more here: https://lith.store/package/6174416
ls_bolt_minigame = false,
-- Set your context menu system below
-- Available options are: 'lation_ui', 'ox_lib' & 'custom'
menu = 'ox_lib',
-- Set your alert & input dialog system below
-- Available options are: 'lation_ui', 'ox_lib' & 'custom'
dialogs = 'ox_lib',
-- Do you want to hide player names in the group menu?
-- If true, names will instead be replaced with their Player IDs
-- If false it will display their character names as normal
hideNames = false,
-- Do you want to be notified via server console if an update is available?
-- True if yes, false if no
version = true,
-- Input all your police jobs below
police = { 'police', 'sheriff' }
},
----------------------------------------------
-- 📍 Activity start settings
----------------------------------------------
start = {
-- Where to spawn the main ped to start chopping
-- If you wish to disable the starting ped, set coords = false
coords = vec4(-169.0171, -1352.3877, 29.9817, 91.8764),
-- The ped model used
-- More models: https://docs.fivem.net/docs/game-references/ped-models/
model = 'a_m_m_bevhills_01',
-- The scenario assigned to the ped (or scenario = false for no scenario)
-- More scenarios: https://github.com/DioneB/gtav-scenarios
scenario = 'WORLD_HUMAN_CLIPBOARD',
-- You can limit the hours at which the ped is available here
-- By default, this ped is available 24/7
-- Min is the earliest the ped is available (in 24hr format)
-- Max is the latest the ped is available (in 24hr format)
-- For example, if you want the ped only available during daytime set min = 6 & max = 21
hours = { min = 0, max = 24 },
-- How many police must be online in order to start a chop job?
police = 0,
-- How long (in seconds) until a vehicle is assigned after requesting a job
-- Set min & max to 0 to disable the cooldown and instantly assign a vehicle
cooldown = { min = 5, max = 25 },
-- When a chop job is completed, do you want to display the "Continue Chopping?"
-- Dialog to the group owner? True if yes, false if no
continue = true,
-- Easy mode is an optional mode that highlights matching vehicle models nearby
-- Radius is the distance from the player to search for & highlight vehicles
-- 424 is the maximum "focus zone" with FiveM OneSync anything higher than this will not work
easyMode = { enable = false, radius = 424 },
-- This option will REMOVE the ability to start the chopping activity from the main menu
-- This is useful if you want to use your own custom method to start the chop job
-- Do not set this option to true if you do not plan to implement a custom method
exportOnly = false,
-- Do you want to allow players to chop owned vehicles?
-- If true, players can chop all vehicles, including owned vehicles
-- If false, players can only chop unowned vehicles
allowOwned = true,
-- ⛔ DANGER: THIS IS A DESTRUCTIVE SETTING
-- ⛔ DANGER: deleteOwned is only used if allowOwned = true above
-- ⛔ DANGER: This will delete (permanently remove) an owned vehicle when chopped
-- ℹ️ INFO: Deleted vehicles are stored in the "deleted_vehicles.sql" file
-- ℹ️ INFO: This is merely a backup safety measure used for restoration if needed
deleteOwned = false
},
----------------------------------------------
-- 📈 Customize the XP system
----------------------------------------------
experience = {
-- The number in these [brackets] are the level
-- The number after = is the exp required to reach that level
-- Be sure levels *always* start at level 1 with 0 exp
[1] = 0,
[2] = 5000,
[3] = 10000,
[4] = 25000,
[5] = 75000,
-- You can add or remove levels as you wish
},
----------------------------------------------
-- 🚗 Customize chop vehicles
----------------------------------------------
vehicles = {
"alpha", "asea", "baller", "banshee", "bjxl", "buccaneer", "bullet", "carbonizzare", "cavalcade2", "coquette",
"dubsta", "dukes", "emperor", "exemplar", "f620", "felon", "felon2", "furoregt", "futo", "glendale",
"huntley", "ingot", "intruder", "jackal", "jester", "manana", "massacro", "ninef", "patriot", "peyote", "phoenix",
"picador", "premier", "primo", "radi", "rapidgt", "rapidgt2", "regina", "rhapsody", "rocoto", "sabregt", "schafter2",
"schwarzer", "sentinel", "stanier", "stratum", "sultan", "superd", "surano", "tornado", "vigero", "voltic"
},
----------------------------------------------
-- 🔨 Customize chopping
----------------------------------------------
chopping = {
wheels = {
[0] = { -- Front left tire
difficulty = { 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 10000
},
[1] = { -- Front right tire
difficulty = { 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 10000
},
[2] = { -- Rear left tire
difficulty = { 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 10000
},
[3] = { -- Rear right tire
difficulty = { 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 10000
}
},
doors = {
[0] = { -- Front driver door
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 12500
},
[1] = { -- Front passenger door
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 12500
},
[2] = { -- Rear driver door
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 12500
},
[3] = { -- Rear passenger door
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 12500
},
[4] = { -- Hood
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 15000
},
[5] = { -- Trunk
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 15000
},
},
frame = { -- The frame/chassis
difficulty = { 'easy', 'easy', 'easy', 'easy', 'easy', 'easy' },
inputs = { 'E' },
duration = 25000
}
},
----------------------------------------------
-- 💰 Customize chop rewards
----------------------------------------------
rewards = {
-- The number in these [brackets] are the player level
[1] = {
-- The min/max amount of "ls_auto_parts" rewarded for each part chopped
wheels = { min = 1, max = 3 },
doors = { min = 2, max = 4 },
frame = { min = 5, max = 10 },
-- The min/max amount of XP rewarded when completing a chop job
xp = { min = 25, max = 35 },
-- The percentage at which chopping parts duration is reduced for this level
speed = 0
},
[2] = {
wheels = { min = 2, max = 4 },
doors = { min = 3, max = 5 },
frame = { min = 6, max = 12 },
xp = { min = 35, max = 50 },
speed = 15
},
[3] = {
wheels = { min = 3, max = 5 },
doors = { min = 4, max = 6 },
frame = { min = 7, max = 14 },
xp = { min = 50, max = 75 },
speed = 30
},
[4] = {
wheels = { min = 4, max = 6 },
doors = { min = 5, max = 7 },
frame = { min = 8, max = 16 },
xp = { min = 75, max = 100 },
speed = 45
},
[5] = {
wheels = { min = 5, max = 7 },
doors = { min = 6, max = 8 },
frame = { min = 9, max = 18 },
xp = { min = 100, max = 150 },
speed = 60
}
},
----------------------------------------------
-- 🗺️ Assign chop zones
----------------------------------------------
zones = {
vec3(1565.1211, -2161.1277, 77.5340),
vec3(1134.0985, -793.7753, 57.5917),
vec3(-84.7814, -2225.9697, 7.8117),
vec3(-467.7876, -1678.4623, 19.0395),
vec3(1596.7920, -1709.7660, 88.1285),
vec3(833.9869, -1405.5132, 26.1511),
vec3(970.2102, -1632.1747, 30.1107),
vec3(248.3347, 380.5432, 105.5951),
vec3(-69.7967, 83.2294, 71.5020),
vec3(-1315.3870, -1257.1395, 4.5771),
vec3(-443.0953, -2282.7065, 7.6081),
vec3(-1597.3135, -1008.8568, 7.6894),
-- You can add or remove locations as you wish
},
----------------------------------------------
-- 🔨 Customize chop shop items
----------------------------------------------
items = {
-- The main item rewarded when a vehicle part is chopped
auto_parts = 'ls_auto_parts',
-- The torch item used to remove doors/frame from vehicle
torch = {
-- The item spawn name
item = 'ls_torch',
-- Do you want to require the player have a torch to chop?
require = true,
-- Should this item be removed on each use?
remove = false,
-- Percentage chance this item breaks on failed skillcheck
-- Set chance to 0 to disable break chance
break_chance = 20
},
lug_wrench = {
-- The item spawn name
item = 'ls_lug_wrench',
-- Do you want to require the player have a lug wrench to chop?
require = true,
-- Should this item be removed on each use?
remove = false,
-- Percentage chance this item breaks on failed skillcheck
-- Set chance to 0 to disable break chance
break_chance = 20
},
vehicle_finder = {
-- The item spawn name
item = 'ls_vehicle_finder',
-- Should this item be removed on each use?
remove = false,
-- How far the detector searches for vehicles
-- 424 is the maximum "focus zone" with FiveM OneSync
-- Anything higher than this will not work
radius = 424,
-- How long (in seconds) the vehicle finder lasts
duration = 60,
-- Customize blip related settings
blips = {
-- The blip sprite ID
sprite = 225,
-- The blip color ID
color = 1,
-- The blip scale
scale = 0.8,
-- The blip name
name = 'Discovered Vehicle',
}
}
},
----------------------------------------------
-- 🛒 Customize shops
----------------------------------------------
shops = {
-- ⚠️ The shops below are added to the main chop shop menu when enabled!
-- The swap shop is a shop where players can exchange their ls_auto_parts
-- For whatever items you wish, such as materials, illegal items, etc
swap = {
-- Optionally disable this shop if you wish
enable = true,
-- This shop specifically only accepts an item as payment
-- You cannot use traditional methods of cash/bank/etc
account = 'ls_auto_parts',
-- Items available for swapping in this shop
items = {
-- item: item spawn name
-- price: price of item in ls_auto_parts
-- quantity: amount of item given for price
-- icon: icon for item
-- level: optional level requirement to buy item
-- metadata: optional metadata for item
[1] = { item = 'plastic', price = 1, quantity = 1, icon = 'recycle' },
[2] = { item = 'aluminium', price = 1, quantity = 1, icon = 'recycle' },
[3] = { item = 'copper', price = 1, quantity = 1, icon = 'recycle' },
-- Add or remove items as you wish
}
},
tool = {
-- Optionally disable this shop if you wish
enable = true,
-- Use cash or bank when purchasing here?
account = 'cash',
-- Items available for sale in this shop
items = {
-- item: item spawn name
-- price: price of item
-- icon: icon for item
-- level: optional level requirement to buy item
-- metadata: optional metadata for item
[1] = { item = 'ls_torch', price = 1250, icon = 'fire-flame-curved' },
[2] = { item = 'ls_lug_wrench', price = 750, icon = 'wrench' },
[3] = { item = 'ls_vehicle_finder', price = 4500, icon = 'satellite-dish' },
-- Add or remove items as you wish
}
}
},
}
Copy
-- Initialize global variables to store framework & inventory
Framework, Inventory = nil, nil
-- Initialize global player variables
PlayerLoaded, PlayerData = false, {}
-- Get framework
local function InitializeFramework()
if GetResourceState('es_extended') == 'started' then
ESX = exports['es_extended']:getSharedObject()
Framework = 'esx'
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
PlayerData = xPlayer
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
RegisterNetEvent('esx:onPlayerLogout', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_chopshop:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
elseif GetResourceState('qbx_core') == 'started' then
Framework = 'qbx'
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
RegisterNetEvent('qbx_core:client:playerLoggedOut', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_chopshop:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
elseif GetResourceState('qb-core') == 'started' then
QBCore = exports['qb-core']:GetCoreObject()
Framework = 'qb'
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_chopshop:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
elseif GetResourceState('ox_core') == 'started' then
Ox = require '@ox_core.lib.init'
Framework = 'ox'
AddEventHandler('ox:playerLoaded', function()
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
AddEventHandler('ox:playerLogout', function()
table.wipe(PlayerData)
PlayerLoaded = false
TriggerEvent('lation_chopshop:playerDropped')
end)
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
PlayerData = GetPlayerData()
PlayerLoaded = true
TriggerEvent('lation_chopshop:playerLoaded')
end)
else
-- Add custom framework here
end
end
-- Get inventory
local function InitializeInventory()
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'
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 job
function GetJob()
local player = GetPlayerData()
if not player then return end
if Framework == 'esx' then
return player?.job?.name
elseif Framework == 'qb' then
return player.job.name
elseif Framework == 'qbx' then
return player.job.name
elseif Framework == 'ox' then
return player.getGroupByType('job')
else
-- Add custom framework here
end
return 'unemployed'
end
-- Return data for item
--- @param item string
function GetItemData(item)
if not item then return end
if Inventory then
if Inventory == 'ox_inventory' then
return exports[Inventory]:Items(item)
elseif Inventory == 'qb-inventory' or Inventory == 'ps-inventory' then
return QBCore.Shared.Items[item]
elseif Inventory == 'qs-inventory' then
local items = exports[Inventory]:GetItemList()
if not items then return end
return items[item]
elseif Inventory == 'origen_inventory' then
local items = exports[Inventory]:GetItems()
if not items then return end
return items[item]
elseif Inventory == 'codem-inventory' then
local items = exports[Inventory]:GetItemList()
if not items then return end
return items[item]
elseif Inventory == 'core_inventory' then
-- No available client-side export to get item list
if Framework == 'qb' then
return QBCore.Shared.Items[item]
else
print('^1[ERROR]: An issue has occured, please contact support at: https://discord.gg/9EbY4nM5uu^0')
end
else
-- Add custom inventory here
end
else
if Framework == 'esx' then
-- Unlikely to need anything here but.. just in case..
print('^1[ERROR]: An error has occured with lation_chopshop - please contact support^0')
elseif Framework == 'qb' then
return QBCore.Shared.Items[item]
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')
end
end
end
-- Returns boolean if player has specified amount of 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)
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()
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()
else
-- Add custom inventory here
end
end
-- Initialize defaults
InitializeFramework()
InitializeInventory()
Copy
-- Initialize global variable(s) to store phone
Phone = nil
-- Initialize config(s)
local shared = require 'config.shared'
-- Localize export
local chopshop = exports.lation_chopshop
-- You can change the textUI script here
-- Options: 'lation_ui', 'ox_lib', 'jg-textui', 'okokTextUI', 'qbcore' & 'custom'
local textui = 'ox_lib'
-- Check if ls_bolt_minigame is installed/started
local ls_bolt_minigame = GetResourceState('ls_bolt_minigame') == 'started'
-- Get phone
local function InitializePhone()
if GetResourceState('lb-phone') == 'started' then
Phone = 'lb-phone'
elseif GetResourceState('qb-phone') == 'started' then
Phone = 'qb-phone'
elseif GetResourceState('qs-smartphone-pro') == 'started' then
Phone = 'qs-smartphone-pro'
elseif GetResourceState('qs-smartphone') == 'started' then
Phone = 'qs-smartphone'
elseif GetResourceState('gksphone') == 'started' then
Phone = 'gksphone'
elseif GetResourceState('roadphone') == 'started' then
Phone = 'roadphone'
elseif GetResourceState('npwd') == 'started' then
Phone = 'npwd'
elseif GetResourceState('yseries') == 'started' then
Phone = 'yseries'
elseif GetResourceState('okokPhone') == 'started' then
Phone = 'okokPhone'
else
-- Add custom phone here
end
end
-- Display a notification
--- @param message string
--- @param type any
function ShowNotification(message, type)
if shared.setup.notify == 'lation_ui' then
exports.lation_ui:notify({ title = 'Chop Shop', message = message, type = type or 'info', icon = 'fas fa-car' })
elseif shared.setup.notify == 'ox_lib' then
lib.notify({ description = message, type = type or 'inform', position = 'top', icon = 'fas fa-car' })
elseif shared.setup.notify == 'esx' then
ESX.ShowNotification(message)
elseif shared.setup.notify == 'qb' then
QBCore.Functions.Notify(message, type or 'primary')
elseif shared.setup.notify == 'okok' then
exports['okokNotify']:Alert('Chop Shop', message, 5000, type or 'info', false)
elseif shared.setup.notify == 'sd-notify' then
exports['sd-notify']:Notify('Chop Shop', message, type or 'primary')
elseif shared.setup.notify == 'wasabi_notify' then
exports.wasabi_notify:notify('Chop Shop', message, 5000, type or 'info', false, 'fas fa-car')
elseif shared.setup.notify == 'custom' then
-- Add custom notification export/event here
end
end
-- Display notifications from server
--- @param message string
--- @param type string
RegisterNetEvent('lation_chopshop:notify', function(message, type)
ShowNotification(message, type)
end)
-- Return true or false if player can start chopping
--- @param model string Model name
--- @param entity number Entity ID
--- @return boolean
function CanStartChopping(model, entity)
if not model or not entity then return false end
if not shared.start.allowOwned then
local plate = GetVehicleNumberPlateText(entity)
if not plate or plate == '' then return false end
local isOwned = lib.callback.await('lation_chopshop:isOwnedVehicle', false, plate)
if isOwned then
ShowNotification(locale('notify.owned-vehicle'), 'error')
return false
end
end
return true
end
-- Send email
--- @param message string
local function SendEmail(message)
if not message then return end
if Phone == 'qs-smartphone' then
TriggerServerEvent('qs-smartphone:server:sendNewMail', {
sender = 'Salvage Specialist',
subject = 'Chop Shop',
message = message
})
elseif Phone == 'roadphone' then
exports[Phone]:sendMail({
sender = 'Salvage Specialist',
subject = 'Chop Shop',
message = message
})
else
lib.alertDialog({
header = 'Chop Shop',
content = message,
centered = true,
cancel = true
})
end
end
-- Display a progress bar
--- @param data table
function ProgressBar(data)
if shared.setup.progress == 'lation_ui' then
if exports.lation_ui:progressBar({
label = data.label,
description = data.description or nil,
icon = data.icon or nil,
duration = data.duration,
useWhileDead = data.useWhileDead,
canCancel = data.canCancel,
steps = data.steps or nil,
disable = data.disable,
anim = {
dict = data.anim.dict or nil,
clip = data.anim.clip or nil,
flag = data.anim.flag or nil
},
prop = {
model = data.prop.model or nil,
bone = data.prop.bone or nil,
pos = data.prop.pos or nil,
rot = data.prop.rot or nil
}
}) then
return true
end
return false
elseif shared.setup.progress == 'ox_lib' then
-- Want to use ox_lib's progress circle instead of bar?
-- Change "progressBar" to "progressCircle" below & done!
if lib.progressBar({
label = data.label,
duration = data.duration,
position = data.position or 'bottom',
useWhileDead = data.useWhileDead,
allowSwimming = data.allowSwimming or false,
canCancel = data.canCancel,
disable = data.disable,
anim = {
dict = data.anim.dict or nil,
clip = data.anim.clip or nil,
flag = data.anim.flag or nil
},
prop = {
model = data.prop.model or nil,
bone = data.prop.bone or nil,
pos = data.prop.pos or nil,
rot = data.prop.rot or nil
}
}) then
return true
end
return false
elseif shared.setup.progress == 'qbcore' then
local p = promise.new()
QBCore.Functions.Progressbar(data.label, data.label, data.duration, data.useWhileDead, data.canCancel, {
disableMovement = data.disable.move,
disableCarMovement = data.disable.car,
disableMouse = false,
disableCombat = data.disable.combat
}, {
animDict = data.anim.dict or nil,
anim = data.anim.clip or nil,
flags = data.anim.flag or nil
}, {
model = data.prop.model or nil,
bone = data.prop.bone or nil,
coords = data.prop.pos or nil,
rotation = data.prop.rot or nil
}, {},
function()
ClearPedTasks(cache.ped)
p:resolve(true)
end,
function()
ClearPedTasks(cache.ped)
p:resolve(false)
end)
return Citizen.Await(p)
else
-- Add 'custom' progress bar here
end
end
-- Register menu
--- @param data table
function RegisterMenu(data)
if shared.setup.menu == 'lation_ui' then
exports.lation_ui:registerMenu(data)
elseif shared.setup.menu == 'ox_lib' then
lib.registerContext(data)
elseif shared.setup.menu == 'custom' then
-- Add 'custom' menu system here
end
end
-- Show menu
--- @param menuId string
function ShowMenu(menuId)
if shared.setup.menu == 'lation_ui' then
exports.lation_ui:showMenu(menuId)
elseif shared.setup.menu == 'ox_lib' then
lib.showContext(menuId)
elseif shared.setup.menu == 'custom' then
-- Add 'custom' menu system here
end
end
-- Display an alert dialog
--- @param data table
function ShowAlert(data)
if shared.setup.dialogs == 'lation_ui' then
return exports.lation_ui:alert(data)
elseif shared.setup.dialogs == 'ox_lib' then
return lib.alertDialog(data)
elseif shared.setup.dialogs == 'custom' then
-- Add your custom alert dialog here
end
end
-- Display an input dialog
--- @param data table
function ShowInput(data)
if shared.setup.dialogs == 'lation_ui' then
return exports.lation_ui:input({ title = data.title, options = data.options })
elseif shared.setup.dialogs == 'ox_lib' then
return lib.inputDialog(data.title, data.options)
elseif shared.setup.dialogss == 'custom' then
-- Add your custom input dialog here
end
end
-- Display TextUI
--- @param text string
--- @param icon string
function ShowTextUI(text, icon)
if textui == 'lation_ui' then
exports.lation_ui:showText({
description = text,
icon = icon,
iconAnimation = 'beat'
})
elseif textui == 'ox_lib' then
lib.showTextUI(text, {
position = 'left-center',
icon = icon,
iconAnimation = 'beat'
})
elseif textui == 'jg-textui' then
exports['jg-textui']:DrawText(text)
elseif textui == 'okokTextUI' then
exports['okokTextUI']:Open(text, 'lightblue ', 'left', false)
elseif textui == 'qbcore' then
exports['qb-core']:DrawText(text, 'left')
else
-- Add custom textUI here
end
end
-- Hide TextUI
--- @param label string
function HideTextUI(label)
if textui == 'lation_ui' then
local isOpen, text = exports.lation_ui:isOpen()
if isOpen and text == label then
exports.lation_ui:hideText()
end
elseif textui == 'ox_lib' then
local isOpen, text = lib.isTextUIOpen()
if isOpen and text == label then
lib.hideTextUI()
end
elseif textui == 'jg-textui' then
exports['jg-textui']:HideText()
elseif textui == 'okokTextUI' then
exports['okokTextUI']:Close()
elseif textui == 'qbcore' then
exports['qb-core']:HideText()
else
-- Add custom textUI here
end
end
-- Display skillcheck
--- @param data table .difficulty, .inputs
function ShowSkillcheck(data)
if shared.setup.minigame == 'lation_ui' then
if exports.lation_ui:skillCheck('Chopping', data.difficulty, data.inputs) then
return true
end
return false
elseif shared.setup.minigame == 'ox_lib' then
if lib.skillCheck(data.difficulty, data.inputs) then
return true
end
return false
elseif shared.setup.minigame == 'custom' then
-- Add custom minigame here
end
return false
end
-- Show Lith Studios Bolt Minigame
--- @param entity number Entity number
--- @param wheelId number Wheel index
--- @param mount boolean Mount wheel
--- @param goToWheel boolean Go to wheel
--- @param coords any
function ShowLsBoltMinigame(entity, wheelId, mount, goToWheel, coords)
if shared.setup.ls_bolt_minigame and not ls_bolt_minigame then
print('^1[ERROR]: ls_bolt_minigame resource is not installed (or started correctly)^0')
print('^1[ERROR]: You either have not installed it or the start order is incorrect^0')
print('^1[ERROR]: If installed, ensure the resource is started BEFORE lation_chopshop^0')
print('^4[INFO]: Don\'t have ls_bolt_minigame yet? Get it here for free: https://lith.store/package/6174416^0')
return false
end
return exports['ls_bolt_minigame']:BoltMinigame(entity, wheelId, mount, goToWheel, coords)
end
-- Add entity target
--- @param entity number Entity number
--- @param data table Options table
function AddTargetEntity(entity, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:addLocalEntity(entity, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:AddTargetEntity(entity, {options = data, distance = 1.75})
elseif shared.setup.interact == 'interact' then
exports.interact:AddLocalEntityInteraction({
entity = entity,
interactDst = 1.75,
offset = vec3(0.0, 0.0, 1.0),
options = data
})
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system was detected - please visit config/shared "setup" section^0')
end
end
-- Add target to model
--- @param model string Model
--- @param data table Options table
function AddTargetModel(model, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:addModel(model, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:AddTargetModel(model, {options = data, distance = 1.75})
elseif shared.setup.interact == 'interact' then
exports.interact:AddModelInteraction({
model = model,
offset = vec3(0.0, 0.0, 0.0),
id = model,
interactDst = 1.75,
options = data
})
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system was detected - please visit config/shared "setup" section^0')
end
end
-- Add target to entity bone
--- @param entity number
--- @param bone string
--- @param data table
function AddTargetBone(entity, bone, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:addLocalEntity(entity, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:AddTargetBone(bone, {options = data, distance = 1.75})
elseif shared.setup.interact == 'interact' then
exports.interact:AddLocalEntityInteraction({
entity = entity,
interactDst = 1.75,
bone = bone,
options = data
})
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system was detected - please visit config/shared "setup" section^0')
end
end
-- Remove target from entity
--- @param entity any|number
--- @param data table|string
function RemoveTargetEntity(entity, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:removeLocalEntity(entity, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:RemoveTargetEntity(entity, data)
elseif shared.setup.interact == 'interact' then
exports.interact:RemoveLocalEntityInteraction(entity, data)
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system was detected - please visit config/shared "setup" section^0')
end
end
-- Remove target model
--- @param model string Model
function RemoveTargetModel(model)
if shared.setup.interact == 'ox_target' then
exports.ox_target:removeModel(model, nil)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:RemoveTargetModel(model, nil)
elseif shared.setup.interact == 'interact' then
exports.interact:RemoveModelInteraction(model, model)
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system was detected - please visit config/shared "setup" section^0')
end
end
-- Remove target from entity bone
--- @param entity any|number
--- @param bone string
--- @param data table|string
function RemoveTargetBone(entity, bone, data)
if shared.setup.interact == 'ox_target' then
exports.ox_target:removeLocalEntity(entity, data)
elseif shared.setup.interact == 'qb-target' then
exports['qb-target']:RemoveTargetBone(bone, data)
elseif shared.setup.interact == 'interact' then
exports.interact:RemoveLocalEntityInteraction(entity, data)
elseif shared.setup.interact == 'custom' then
-- Add support for a custom target system here
else
print('^1[ERROR]: No interaction system was detected - please visit config/shared "setup" section^0')
end
end
-- Empty function triggered when "Start Chopping" is selected
--- @param entity number Entity ID
--- @param model string Model name
function StartedChopping(entity, model)
-- print(('Chopping has started on entity/model: %s/%s'):format(entity, model))
end
-- Assign waypoint to random zone upon vehicle entry
--- @param value number
--- @param _ any
lib.onCache('vehicle', function(value, _)
if not value then return end
local model = modelHashes[GetEntityModel(value)]
if not model then return end
local assignedModel = chopshop:getAssignedModel()
if not assignedModel then return end
if model ~= assignedModel then return end
local zone = shared.zones[math.random(#shared.zones)]
if not zone then return end
SetNewWaypoint(zone.x, zone.y)
TriggerServerEvent('lation_chopshop:foundModel')
end)
-- Function to spawn NPCs
--- @param model string
--- @param position vector4
function SpawnPed(model, position)
lib.requestModel(model)
local ped = CreatePed(0, model, position.x, position.y, position.z - 1.0, position.w, false, true)
FreezeEntityPosition(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
SetEntityInvincible(ped, true)
return ped
end
-- Print debug message
--- @param message string
function Debug(message)
if not shared.setup.debug then return end
print(('^2[DEBUG]:^0 %s'):format(message))
end
-- Register net event(s)
RegisterNetEvent('lation_chopshop:sendEmail', SendEmail)
-- Initialize default(s)
InitializePhone()
