⚙️Config File
The script is plug and play , however if you want to customize the script to your liking feel free to explore config files.
Config = Config or {}
Menu = {}
--[[
===============================
⚙️ Developer Debug Settings
===============================
]]
Config.RefreshTime = 2000 -- Interval in ms to refresh zone checks (0 disables)
Config.debugPoly = false -- Enables drawing of polyzone outlines (used for debugging zone shapes)
Config.debug = false -- Enables printing debug info to console
--[[
===============================
⚙️ Core Settings
===============================
]]
Config.Framework =
"auto" -- "qb", "esx", "auto","standalone" If you dont want to use any provided framework functions, Checkout sv_editable.lua
Config.AllowOnlyOwnedVehicles = false -- Only allow stancing for vehicles the player owns (logic handled in sv_editable.lua)
Config.Notify =
"ox_lib" -- Notification system to use (supports: codem, esx, qb, okok, wasabi, t-notify, r_notify, pNotify, mythic, ox_lib)
Config.StanceRenderDistance = 250.0 -- Max distance (in meters) at which stance changes are applied to vehicles
--[[
===============================
📍 Location & Blip Settings
===============================
]]
Config.GeneralBlip = {
Sprite = 545, -- Blip icon (https://docs.fivem.net/docs/game-references/blips/)
Display = 4, -- Blip display mode
Scale = 0.6, -- Size of the blip
Colour = 70, -- Blip color (https://docs.fivem.net/docs/game-references/blips/#blip-colors)
Name = "Tuff Stance Location" -- Name that appears on the map
}
Config.PreviewExitKey = {
controlId = 200, -- https://docs.fivem.net/docs/game-references/controls/
displayName = "ESC" -- The Button name shown in the UI of the stancer.
}
-- Locations where stancer is available
Config.Stance_Locations = {
["1"] = {
enable = true, -- Enables this location
coords = vector4(-204.23, -1301.38, 30.67, 355.52), -- Coordinates and heading for the interaction
interactDistance = 5.0, -- Max distance to trigger interaction
ItemRequired = false, -- Whether specific item(s) are required
ItemChecks = { toolkit = true, stancekit = true }, -- If ItemRequired is true, list items that can be accepted (set any to true)
ItemRemoval = false, -- Remove one of the required items
IgnoreCarLiftSection = false, -- Set true to allow stancing anywhere within the zone, not just in the car lift poly section
DynamicPricings = { -- Dynamic Pricing for each option
WHEEL_TYPE = 500,
WHEEL_NUMBER = 500,
ALL_WHEELS_WIDTH = 100,
ALL_WHEELS_SIZE = 100,
ALL_WHEELS_OFFSET = 100,
ALL_WHEELS_CAMBER = 100,
SUSPENSION_HEIGHT = 100,
WHEEL_FRONT_LEFT_OFFSET = 100,
WHEEL_FRONT_RIGHT_OFFSET = 100,
WHEEL_REAR_LEFT_OFFSET = 100,
WHEEL_REAR_RIGHT_OFFSET = 100,
WHEEL_FRONT_LEFT_CAMBER = 100,
WHEEL_FRONT_RIGHT_CAMBER = 100,
WHEEL_REAR_LEFT_CAMBER = 100,
WHEEL_REAR_RIGHT_CAMBER = 100,
},
Options = { -- Disable/Enable any options for this location
WHEEL_TYPE = true,
WHEEL_NUMBER = true,
ALL_WHEELS_WIDTH = true,
ALL_WHEELS_SIZE = true,
ALL_WHEELS_OFFSET = true,
ALL_WHEELS_CAMBER = true,
SUSPENSION_HEIGHT = true,
WHEEL_FRONT_LEFT_OFFSET = true,
WHEEL_FRONT_RIGHT_OFFSET = true,
WHEEL_REAR_LEFT_OFFSET = true,
WHEEL_REAR_RIGHT_OFFSET = true,
WHEEL_FRONT_LEFT_CAMBER = true,
WHEEL_FRONT_RIGHT_CAMBER = true,
WHEEL_REAR_LEFT_CAMBER = true,
WHEEL_REAR_RIGHT_CAMBER = true,
},
JobLock = {
JobRequired = true, -- Whether a job check is required to access stancing
jobs = {
mechanic = { 1, 2, 3 }, -- Mechanic job allowed with grade 1, 2 or 3
police = true -- Police job allowed, all grades
}
},
Invoice = {
AllowInvoice = false,
InvoiceSocietyName = 'mechanic'
},
blip = true, -- Whether to display a blip for this location
blipDetails = {} -- Datas that are in this will be callbacked to the global blip details (Config.Generalblip)
-- Example custom blipDetails:
-- blipDetails = {
-- Sprite = 446,
-- Scale = 0.5,
-- Name = "Custom Stancer Shop"
-- }
},
["2"] = {
enable = true, -- Enables this location
coords = vector4(-204.23, -13010.38, 30.67, 355.52), -- Coordinates and heading for the interaction
interactDistance = 5.0, -- Max distance to trigger interaction
ItemRequired = false, -- Whether specific item(s) are required
ItemChecks = { toolkit = true, stancekit = true }, -- If ItemRequired is true, list items that can be accepted (set any to true)
ItemRemoval = false, -- Remove one of the required items
IgnoreCarLiftSection = false, -- Set true to allow stancing anywhere within the zone, not just in the car lift poly section
DynamicPricings = { -- Dynamic Pricing for each option
WHEEL_TYPE = 500,
WHEEL_NUMBER = 500,
ALL_WHEELS_WIDTH = 100,
ALL_WHEELS_SIZE = 100,
ALL_WHEELS_OFFSET = 100,
ALL_WHEELS_CAMBER = 100,
SUSPENSION_HEIGHT = 100,
WHEEL_FRONT_LEFT_OFFSET = 100,
WHEEL_FRONT_RIGHT_OFFSET = 100,
WHEEL_REAR_LEFT_OFFSET = 100,
WHEEL_REAR_RIGHT_OFFSET = 100,
WHEEL_FRONT_LEFT_CAMBER = 100,
WHEEL_FRONT_RIGHT_CAMBER = 100,
WHEEL_REAR_LEFT_CAMBER = 100,
WHEEL_REAR_RIGHT_CAMBER = 100,
},
Options = { -- Disable/Enable any for this location options
WHEEL_TYPE = true,
WHEEL_NUMBER = true,
ALL_WHEELS_WIDTH = true,
ALL_WHEELS_SIZE = true,
ALL_WHEELS_OFFSET = true,
ALL_WHEELS_CAMBER = true,
SUSPENSION_HEIGHT = true,
WHEEL_FRONT_LEFT_OFFSET = true,
WHEEL_FRONT_RIGHT_OFFSET = true,
WHEEL_REAR_LEFT_OFFSET = true,
WHEEL_REAR_RIGHT_OFFSET = true,
WHEEL_FRONT_LEFT_CAMBER = true,
WHEEL_FRONT_RIGHT_CAMBER = true,
WHEEL_REAR_LEFT_CAMBER = true,
WHEEL_REAR_RIGHT_CAMBER = true,
},
JobLock = {
JobRequired = false, -- Whether a job check is required to access stancing
jobs = {
mechanic = { 1, 2, 3 }, -- Mechanic job allowed with grade 1, 2 or 3
police = true -- Police job allowed, all grades
}
},
Invoice = {
AllowInvoice = false,
InvoiceSocietyName = 'mechanic'
},
blip = true, -- Whether to display a blip for this location
blipDetails = {} -- Use empty table to fallback to Config.GeneralBlip
-- Example custom blipDetails:
-- blipDetails = {
-- Sprite = 446,
-- Display = 4,
-- Scale = 0.5,
-- Colour = 5,
-- Name = "Custom Stancer Shop"
-- }
},
}
--[[
===============================
🔐 Admin Settings
===============================
]]
Config.Administration = {
StanceCommand = "adminstancer", -- Command to force-open stancer UI as admin
RemoveStancerCommand = "adminrestancer", -- Command to remove stance from target vehicle
RemovePrice = 0 -- Price to charge for removing stance (0 = free)
}
--[[
===================
TEXTUI Settings
===================
]]
-- Determines how the interaction UI is triggered.
-- Options:
-- "buttonPress" - Press a key (e.g. "E") to interact.
-- "RadialMenu" - Uses a radial menu to interact.
Config.Interact = "buttonPress"
-- If Config.Interact is set to "RadialMenu", specify which radial menu system to use.
-- Options:
-- "qb-radialmenu" - Default QB-core radial menu.
-- "ox_lib" - ox_lib radial menu support.
-- "custom" - Use your own custom implementation (handled in cl_editable.lua).
Config.RadialMenu = "ox_lib"
-- Which text UI system to use for showing interaction hints.
-- Options:
-- "ox_lib" - Uses ox_lib's built-in text UI.
-- "cd_drawtextui" - Uses cd_drawtextui system.
-- "jg-textui" - Uses jg-textui system.
-- "lab-HintUI" - Uses lab-HintUI system.
-- "okokTextUI" - Uses okokTextUI system.
Config.DrawTextScript = 'ox_lib'
-- Configuration for displaying the interaction text UI.
Config.DrawTextUI = {
enable = true, -- Enable or disable the text UI system.
hotkey = "e", -- Key to press for interaction if using "buttonPress".
text = "Press %s to open Stance Menu", -- Text to show. "%s" will be replaced by the hotkey automatically.
custom = function(text) -- Function to show the text UI using the selected DrawTextScript.
if Config.DrawTextScript == 'ox_lib' and lib then
lib.showTextUI(text, {
position = "top-center",
icon = 'hand',
style = {
borderRadius = 0,
backgroundColor = '#48BB78',
color = 'white'
}
})
elseif Config.DrawTextScript == 'cd_drawtextui' then
TriggerEvent('cd_drawtextui:ShowUI', 'show', text)
elseif Config.DrawTextScript == 'jg-textui' then
exports['jg-textui']:DrawText(text)
elseif Config.DrawTextScript == 'lab-HintUI' then
exports['lab-HintUI']:Show(text, "Wheel Stancer")
elseif Config.DrawTextScript == 'okokTextUI' then
exports['okokTextUI']:Open(text, 'darkblue')
end
end,
customhide = function() -- Function to hide the text UI depending on which DrawTextScript is used.
if Config.DrawTextScript == 'ox_lib' and lib then
lib.hideTextUI()
elseif Config.DrawTextScript == 'cd_drawtextui' then
TriggerEvent('cd_drawtextui:HideUI')
elseif Config.DrawTextScript == 'jg-textui' then
exports['jg-textui']:HideText()
elseif Config.DrawTextScript == 'lab-HintUI' then
exports['lab-HintUI']:Hide()
elseif Config.DrawTextScript == 'okokTextUI' then
exports['okokTextUI']:Close()
end
end,
}
-- Dynamically change the text shown in DrawTextUI based on the interaction method. [#DONT TOUCH!]
if Config.DrawTextUI.enable and Config.Interact == "RadialMenu" then
Config.DrawTextUI.text = "Open Radial Menu To Stance the vehicle."
elseif Config.DrawTextUI.enable and Config.Interact == "buttonPress" then
-- If using buttonPress, inject the hotkey into the text string.
Config.DrawTextUI.text = string.format(Config.DrawTextUI.text, string.upper(Config.DrawTextUI.hotkey))
end
-- Configuration for drawing a marker on the ground at stance locations.
Config.DrawMarker = {
enabled = true, -- Enable or disable marker drawing.
Scale = 1.0,
MarkerType = 20, -- Marker type. Reference: https://docs.fivem.net/docs/game-references/markers/
-- Marker color values. Reference RGBA values from: https://rgbacolorpicker.com/
red = 255, -- Red component (0–255)
green = 255, -- Green component (0–255)
blue = 252, -- Blue component (0–255)
alpha = 100, -- Transparency (0–255)
MarkerUpAndDown = true, -- If true, marker has vertical pulsing animation.
MarkerRotate = true, -- If true, marker rotates in place.
}
--[[
===============================
🧾 Inventory Settings
===============================
]]
Config.Inventory = {
Enable_Inventory = false, -- Enable inventory-based item check
Inventory_Name = "qb-inventory" -- Inventory resource name (e.g., qb-inventory, ox_inventory, etc.)
}
--[[
==============================
REMOVE STANCE COMMAND SETTINGS
==============================
]]
-- Configuration for the command that removes a stance from a vehicle.
-- The Ui Will Automatically Put the Plate on the Input If the Player is within a Vehicle
-- Default usage: /dv_stance
Config.Remove_Stance_Command = {
Enable = true, -- Enable or disable the /dv_stance command entirely.
ResetWheelTypeAndWheelNumber = true, -- You can set this to false to prevent dv_stance from resseting the wheels if they were changed by stancer itself
Command_name = "dv_stance", -- The actual command players will use to remove a vehicle's stance.
IgnoreStanceLocations = true, -- If true, the command can be used anywhere. If false, it only works within defined stance locations.
RemovePrice = 100, -- Price to remove the stance. Set to 0 for free.
Requires_Jobs = true, -- If true, only players with specific jobs and grades can use the command.
Jobs = {
["mechanic"] = { 1, 2, 3 }, -- Only grades 1, 2, and 3 from 'mechanic' job can use the command.
["police"] = true, -- All police grades can use the command.
},
ItemRequired = false, -- If true, using the command requires a specific item in inventory.
ItemChecks = {
["toolkit"] = true, -- Players with a "toolkit" can use the command (if ItemRequired = true).
["stancekit"] = true, -- Players with a "stancekit" can also use the command.
},
ItemRemoval = false, -- Remove One of the required items
}
--[[
=======================
🚗 Car lift Settings
=======================
]]
Config.Carlift = {
Enable = true, -- Enable/disable car lift system
Locations = { -- Car lift locations with heading
['Bennys1'] = {
x = -225.5,
y = -1295.31,
z = 31.34,
heading = 260.48,
displayName = 'Bennys 1' -- Shown in wheel storage UI as the name.
}
},
WheelStorages = { -- Wheel storage near lift
['BennysStorage1'] = {
x = -196.34,
y = -1319.07,
z = 31.09,
LiftDistance = 50.0 -- Vehicles on lifts within this distance will be shown as presets for the wheel storage UI
}
},
Requires_Jobs = true, -- Restrict the lift usage to jobs
Jobs = {
["mechanic"] = { 1, 2, 3 }, -- Specific grades allowed
["police"] = true -- All grades allowed
},
Interaction = {
type = "DrawText", -- "ox", "DrawText", "qb"
TargetResourceName = 'ox_target' -- 'ox_target' , 'qb-target'
},
MiniGame = {
OnRemoval = false, -- Enable minigame on wheel removal
OnInstall = false, -- Enable minigame on wheel install
BoltMovements = 0.0008 -- Movement speed of bolts, only increase by small amounts! (example : 0.0015)
},
UseProgressbar = true, -- Use OX progress bar for actions
ProgressBarDetails = {
type = 'circle', -- "circle" or "box"
duration = 3000, -- Time in ms
label = 'Adjusting',
position = 'bottom', -- "middle" or "bottom"
useWhileDead = false,
canCancel = true,
anim = {
dict = 'amb@prop_human_movie_bulb@idle_a',
clip = 'idle_b'
},
disable = {
move = true,
car = true,
combat = true,
mouse = false,
sprint = true
}
}
}
-- If a addon vehicle has a Long model name, Use this to make sure stancer knows the actual display name.
Config.AddonDisplayNames = { -- [`Vehicle Name`] = 'Vehicle Name'
[`rrdominators197`] = 'rrdominators197'
}
--[[
=======================
Set Limit to Menu Settings
=======================
]]
Config.Limits = { -- the key values beside global can be either vehicle class number, or vehicle model itself for more info on vehicle class : https://docs.fivem.net/natives/?_0x29439776AAA00A62
Suspension = {
Global = {
max = 0.1,
min = -1.3
},
[`sandking`] = { -- define based on vehicle model ,if you are adding new vehicle models, make sure the name is between `MoDEL`
max = 0.05,
min = -2.0
},
[2] = { -- define based on vehicle class : 2 = SUVs
max = 0.7,
min = -3.0,
}
},
WheelSize = {
Global = {
max = 1.0,
min = 0.5
},
[9] = { -- OffRoads can have bigger wheels
min = 0.5,
max = 1.4
},
[`monster`] = {
min = 0.5,
max = 2.5
}
},
WheelWidth = {
Global = {
max = 1.4,
min = 0.1
},
[`monster`] = {
min = 0.5,
max = 3.5
}
},
-- Min and maximums of rightsides and leftsides should be exact opposite of each other. otherwise [All Wheels] will break.
WheelxRotationRight = { -- X rotoation for wheels on the right side of the vehicle
Global = {
max = 1.0,
min = 0.0
},
},
WheelxRotationLeft = { -- X rotoation for wheels on the left side of the vehicle
Global = {
max = 0.0,
min = -1.0
},
},
WheelyRotationRight = { -- Y rotoation for wheels on the right side of the vehicle
Global = {
max = 0.15,
min = -1.0
},
},
WheelyRotationLeft = { -- Y rotoation for wheels on the left side of the vehicle
Global = {
max = 1.0,
min = -0.15
},
}
}
-------------------------------
------- BLACKLIST Vehicles!
-------------------------------
Config.BlacklistedVehicles = { -- Blacklist Vehicle by Name.
-- Add vehicle spawn name to Blacklist them from using the Stance Menu.
[`bmx`] = true,
}
Config.BlackListedVehicleClass = { -- Blacklist by Vehicle Class, uncomment (remove --) any class to blacklist it from doing stances!
--[1] = true, -- Sedans
--[0] = true, -- Compacts
--[2] = true, -- SUVs
--[3] = true, -- Coupes
--[5] = true, -- Sports Classics
--[4] = true, -- Muscle
--[6] = true, -- Sports
--[7] = true, -- Super
[8] = true, -- Motorcycles
--[9] = true, -- Off-road
[10] = true, -- Industrial
[11] = true, -- Utility
--[12] = true, -- Vans
[13] = true, -- Cycles
[14] = true, -- Boats
[15] = true, -- Helicopters
[16] = true, -- Planes
--[17] = true, -- Service
--[18] = true, -- Emergency
--[19] = true, -- Military
--[20] = true, -- Commercial
[21] = true, -- Trains
[22] = true, -- Open Wheel
}
--[[
=======================
Webhooks Settings
=======================
]]
Config.EnableWebhooks = false -- true/false
Config.WebhooksOptions = {
SendOptions = {
OnRemoval = true,
RemoveColor = 'red',
OnAppliance = true,
ApplianceColor = 'green'
},
TagEveryone = false,
IncludeAdminJobs = false,
Details = {
TargetPlate = true,
PlayerName = true,
PlayerJob = true, -- Player Job
PlayerId = true, -- Player Server ID
PlayerIdentifier = true, -- Player Identifier
PlayerMoney = false
}
}
-- Note: To add WebhookURL you need to open sv_webhook.lua file add it there !
Config.Webhooks = {
HeaderImageURL = 'YOUR_IMAGE_LINK', -- If you Want Image Header For The Logs insert The URL
LogHeader = 'WheelStancer By Tuff', -- What Will be the Header Of Your logs
WebhookTitle = 'Wheel Stancer Log:'
}
--[[
=======================
Framework Settings
=======================
]]
-- ### IF YOU KNOW WHAT YOU ARE DOING YOU CAN CHANGE THIS ###
Config.PlayerSpawnEvents = {
'esx:playerLoaded',
'playerSpawned',
'QBCore:Client:OnPlayerLoaded'
}
if Config.Framework == "auto" then
if GetResourceState("es_extended") == "started" then
Config.Framework = "esx"
elseif GetResourceState("qb-core") == "started" or GetResourceState("qbx_core") == "started" then
Config.Framework = "qb"
else
Config.Framework = 'standalone'
end
end
Config.FrameworkSharedObject = nil
Config.FrameworkSharedObjects = {
['qb'] = function()
if Config.FrameworkSharedObject == nil then
Config.FrameworkSharedObject = exports['qb-core']:GetCoreObject()
end
return Config.FrameworkSharedObject
end,
['esx'] = function()
if Config.FrameworkSharedObject == nil then
Config.FrameworkSharedObject = exports['es_extended']:getSharedObject()
end
return Config.FrameworkSharedObject
end,
['old_esx'] = function()
if Config.FrameworkSharedObject == nil then
local ESX = nil
while ESX == nil do
TriggerEvent('esx:getSharedObject', function(obj)
ESX = obj
end)
Citizen.Wait(30)
end
Config.FrameworkSharedObject = ESX
end
return Config.FrameworkSharedObject
end
}
--[[
=======================
Invoice/Billing Settings
=======================
]]
--Supported Scripts: loaf_billing, okokBilling, codem, qs-billing, esx
Config.Invoice_Script = ""
function Config.SendInvoice(price, target, source, society_name)
if not society_name then return end -- Dont Send invoices if the society_name was invalid
if Config.Invoice_Script == "okokBilling" then
local from = {
label = society_name,
identifier = society_name,
}
TriggerEvent('bill:createBill', target, price, society_name, from, source)
elseif Config.Invoice_Script == "loaf_billing" then
exports['loaf_billing']:CreateBill(source, function() end, target, 30, 0, price, society_name,
"Stance Billing", society_name)
elseif Config.Invoice_Script == "codem" then
exports['codem-billing']:createBilling(source, target, price, society_name, society_name)
elseif Config.Invoice_Script == "esx" then
TriggerEvent('esx_billing:sendBill', target, society_name, society_name, price)
elseif Config.Invoice_Script == "cs_billing" then
exports['cs_billing']:createBill({
playerID = target, --Player's ID to whom you want to send an invoice
society = society_name, --Invoice Society Name
society_name = society_name, --Invoice Society Label
amount = price, --Invoice Amount,
senderID = source
})
elseif Config.Invoice_Script == "qs-billing" then
exports['qs-billing']:ServerCreateInvoice(source, society_name, "Stance Billing", price, true, false,
false, nil, society_name)
elseif Config.Invoice_Script == 'RxBilling' then
-- sendTo: PlayerId or Job Name
-- isJobInvoice: should be true if invoice is sent by job from the player (fromPlayerId)
exports['RxBilling']:SendInvoice(source, target, price, 'Stance Billing', true)
elseif Config.Invoice_Script == "custom" then
-- put your export here !!!
end
end
function Config:CreateRadialMenu(id, title, icon, callback)
-- Custom Code
end
-- If The Radial menu requires a event to trigger, Trigger This : tuff-wheelstancer:radialmenu
function Config.RemoveRadialMenu(id)
-- Custom Code
end
-- Target Scripts For Car lift!
if Config.Carlift.Interaction.type ~= 'DrawText' then
local targetResource = Config.Carlift.Interaction.TargetResourceName
local type = Config.Carlift.Interaction.type
local Target
if exports[targetResource] and GetResourceState(targetResource) == 'started' then
Target = exports[targetResource]
elseif type == 'ox' and GetResourceState('ox_target') == 'started' then
Target = exports['ox_target']
elseif type == 'qb' and GetResourceState("qb-target") == 'started' then
Target = exports['qb-target']
else
Config.Carlift.Interaction.type = 'DrawText' -- revert back to drawtext since Target is not available
error('[MAJOR ERROR] No Target Resource found. Please review config.lua')
end
RemoveTargetZone = function(zoneName)
if type == 'ox' then
Target:removeZone(zoneName)
elseif type == 'qb' then
Target:RemoveZone(zoneName)
end
end
AddEntityTarget = function(entity, label, name, onSelect)
if type == 'ox' then
local createdTarget = Target:addLocalEntity(entity, {
label = label,
name = name,
onSelect = onSelect,
icon = 'fas fa-magnifying-glass',
canInteract = function(entity, distance, data)
return not IsPedInAnyVehicle(PlayerPedId(), false)
end,
})
return createdTarget
elseif type == 'qb' then
Target:AddEntityZone(name, entity, {
name = name,
debugPoly = Config.debugPoly,
}, {
options = {
{
icon = 'fas fa-magnifying-glass',
label = label,
action = onSelect,
canInteract = function(entity, distance, data)
return not IsPedInAnyVehicle(PlayerPedId(), false)
end,
}
},
distance = 1.5,
})
return name
end
end
AddSphereZone = function(coords, label, name, radius, onSelect)
if type == 'ox' then
local createdTarget = Target:addSphereZone({
coords = coords,
radius = radius,
debug = Config.debugPoly,
options = {
label = label,
name = name,
onSelect = onSelect,
distance = 1.8,
icon = "fa-solid fa-car-side",
canInteract = function(entity, distance, data)
return not IsPedInAnyVehicle(PlayerPedId(), false)
end,
}
})
return createdTarget
elseif type == 'qb' then
Target:AddCircleZone(name, coords, radius, {
name = name,
debugPoly = Config.debugPoly,
useZ = true
}, {
options = {
{
icon = "fa-solid fa-car-side",
label = label,
action = onSelect,
canInteract = function(entity, distance, data)
return not IsPedInAnyVehicle(PlayerPedId(), false)
end,
}
},
distance = 1.8
})
return name
end
end
end
RetrieveRealPlate = function(plate, vehicle) -- if you have a fake plate script you can implement it here to retrieve the real plate of the vehicle
if GetResourceState('brazzers-fakeplates') == 'started' and exports['brazzers-fakeplates'] then
local hasFakePlate = exports['brazzers-fakeplates']:getPlateFromFakePlate(plate)
if hasFakePlate then return hasFakePlate end
end
return plate
end
Notify = function(title, msgKey, cooldown, ntype)
if not cooldown then cooldown = 3000 end
if not ntype then ntype = 'success' end
if ntype == "Success" then
ntype = "success"
end
local languageKey = Language or "en"
local translatedMessage = Translations[languageKey][msgKey] or Translations["en"][msgKey]
if not translatedMessage or translatedMessage == '' then
translatedMessage = msgKey
end
if Config.Notify == "codem" then
TriggerEvent('codem-notification:Create', translatedMessage, "bminfo", title, cooldown)
elseif Config.Notify == "esx" then
ESX.ShowNotification(translatedMessage)
elseif Config.Notify == "qb" then
QBCore.Functions.Notify({ text = translatedMessage, caption = title }, ntype, cooldown)
elseif Config.Notify == "okok" then
exports['okokNotify']:Alert(title, translatedMessage, cooldown, ntype)
elseif Config.Notify == 'wasabi' then
exports.wasabi_notify:notify(title, translatedMessage, cooldown, ntype)
elseif Config.Notify == 't-notify' then
exports['t-notify']:Alert({ style = ntype, message = translatedMessage, duration = cooldown, })
elseif Config.Notify == 'r_notify' then
exports.r_notify:notify({
title = title,
content = translatedMessage,
type = ntype,
icon = "fas fa-check",
duration =
cooldown,
position = 'top-right',
sound = false
})
elseif Config.Notify == 'pNotify' then
exports['pNotify']:SendNotification({
text = translatedMessage,
type = ntype,
timeout = cooldown,
layout =
'centerRight'
})
elseif Config.Notify == 'mythic' then
exports['mythic_notify']:SendAlert('inform', translatedMessage, cooldown)
elseif Config.Notify == "ox_lib" or lib then
lib.notify({
title = title,
description = translatedMessage,
type = ntype
})
end
end
-- Custom 3d Text function
function Draw3DText(x, y, z, str, r, g, b, a, font, scaleSize, enableProportional, enableCenter, enableOutline,
enableShadow, sDist, sR, sG, sB, sA, boxR, boxG, boxB, boxA, padding)
local onScreen = World3dToScreen2d(x, y, z)
if not onScreen then return end
SetDrawOrigin(x, y, z, 0)
local scale = 0.25
local fontId = font or 0
local pad = padding or 0.003 -- horizontal padding around the text box
-- Text setup
SetTextScale(scale, scale)
SetTextFont(fontId)
SetTextColour(r or 255, g or 255, b or 255, a or 255)
if enableProportional then SetTextProportional(true) end
if enableCenter then SetTextCentre(true) end
if enableOutline then SetTextOutline() end
if enableShadow then
SetTextDropshadow(sDist or 1, sR or 0, sG or 0, sB or 0, sA or 255)
end
-- EXPERIMENTAL BOX
BeginTextCommandGetWidth("STRING")
AddTextComponentString(str)
local textWidth = GetTextScreenWidth(true)
DrawRect(
0.0, 0.0 + 0.0125, --
textWidth + pad * 2, 0.03,
boxR or 27, boxG or 27, boxB or 27, boxA or 120
)
SetTextEntry("STRING")
AddTextComponentString(str)
-- Draw the text over the box
DrawText(0.0, 0.0)
ClearDrawOrigin()
end
-- External Methods to open the stancer menu!
AddEventHandler('tuff:openStanceMenu',function()
TryOpenStanceMenu(false--[[ Should Allow Opening Anywhere ?! ]],nil --[[ If not opening Anywhere, What is the stance location id, can see it in the Config.Stance_Location ]])
end)Config.HasItem = function(src, item, count)
local _item = nil
if Config.Framework == 'standalone' then return true end -- Skip Item checks for standalone framework
if Config.Inventory.Enable_Inventory then
if Config.Inventory.Inventory_Name == "ox_inventory" then
_item = exports.ox_inventory:GetItem(src, item)
if _item and _item.count then return _item.count >= count end
elseif Config.Inventory.Inventory_Name == "qb-inventory" or Config.Inventory.Inventory_Name == "ps-inventory" then
local Player = QBCore.Functions.GetPlayer(src)
local foundItem = Player.Functions.GetItemByName(item)
if foundItem then
_item = foundItem.amount
return _item >= count
end
elseif Config.Inventory.Inventory_Name == "codem-inventory" then
_item = exports["codem-inventory"]:GetItemData(src, item)
if _item then
local _count = type(_item) == 'number' and _item or (_item.amount or 0)
return _count >= count
end
elseif Config.Inventory.Inventory_Name == "qs-inventory" then
_item = exports['qs-inventory']:GetItemTotalAmount(src, item)
return _item >= count
elseif Config.Inventory.Inventory_Name == "esx" then
_item = ESX.GetPlayerFromId(src).getInventoryItem(item).count
return _item >= count
elseif Config.Inventory.Inventory_Name == "custom" then
-- Add your custom inventory check here
end
return false
end
return true
end
Config.DoItemCheck = function(player, items)
for item, _ in pairs(items) do if Config.HasItem(player, item, 1) then return true end end
return false
end
Config.RemoveItemCheck = function(player,items)
for item,_ in pairs(items) do
if Config.HasItem(player,item) then
Config.RemoveItem(player,item,1)
break
end
end
end
function Config.RemoveItem(source, item, count)
if Config.Inventory.Enable_Inventory then
local hasItem = Config.HasItem(source, item, count)
if hasItem then
if Config.Inventory.Inventory_Name == "ox_inventory" then
exports.ox_inventory:RemoveItem(source, item, count)
elseif Config.Inventory.Inventory_Name == "qb-inventory" or Config.Inventory.Inventory_Name == "ps-inventory" then
local Player = QBCore.Functions.GetPlayer(source)
Player.Functions.RemoveItem(item, count)
elseif Config.Inventory.Inventory_Name == "codem-inventory" then
exports["codem-inventory"]:RemoveItem(source, item, count)
elseif Config.Inventory.Inventory_Name == "qs-inventory" then
exports['qs-inventory']:RemoveItem(source, item, count)
elseif Config.Inventory.Inventory_Name == "esx" then
ESX.GetPlayerFromId(source).removeInventoryItem(item, count)
elseif Config.Inventory.Inventory_Name == "custom" then
-- Add your custom inventory remove item here
end
end
end
end
function Config.GetPlayerMoney(source, type)
if not source then return end
if Config.Framework == 'standalone' then return 1000 * 100 end
local money = 0
if QBCore or Config.Framework == 'qb' then
if not QBCore then
QBCore = exports['qb-core']:GetCoreObject()
end
local Player = QBCore.Functions.GetPlayer(source)
money = Player.Functions.GetMoney(type) or 0
elseif ESX or Config.Framework == 'esx' then
if not ESX then
ESX = exports['es_extended']:getSharedObject()
end
local xPlayer = ESX.GetPlayerFromId(source)
if type ~= 'bank' then
money = xPlayer.getAccount('money').money or 0
else
money = xPlayer.getAccount('bank').money or 0
end
else
-- STANDALONE, Implement your money system!!!
end
return money
end
function Config.RemovePlayerMoney(source, type, amount,societyName)
if not source or not amount then return false end
if QBCore or Config.Framework == 'qb' then
if not QBCore then
QBCore = exports['qb-core']:GetCoreObject()
end
local Player = QBCore.Functions.GetPlayer(source)
Player.Functions.RemoveMoney(type, amount, 'Wheel Stancer')
elseif ESX or Config.Framework == 'esx' then
if not ESX then
ESX = exports['es_extended']:getSharedObject()
end
local xPlayer = ESX.GetPlayerFromId(source)
if type ~= 'bank' then
xPlayer.removeAccountMoney("cash", amount, "Wheel Stancer Payment.")
else
xPlayer.removeAccountMoney("bank", amount, "Wheel Stancer Payment.")
end
else
-- STANDALONE, Implement your money system!!!
end
end
function Config.GetPlayerInGameName(source)
local firstname, lastname = 'TestName', 'TestLastName'
if QBCore or Config.Framework == 'qb' then
if not QBCore then
QBCore = exports['qb-core']:GetCoreObject()
end
local Player = QBCore.Functions.GetPlayer(source)
firstname, lastname = Player.PlayerData.charinfo.firstname, Player.PlayerData.charinfo.lastname
elseif ESX or Config.Framework == 'esx' then
if not ESX then
ESX = exports['es_extended']:getSharedObject()
end
local xPlayer = ESX.GetPlayerFromId(source)
firstname, lastname = xPlayer.getName(), '#'
else
firstname, lastname = GetPlayerName(source), '#'
end
return firstname, lastname
end
function Config.GetPlayerJob(source)
local job, grade = "Tuff Stancer", 0
if QBCore then
local Player = QBCore.Functions.GetPlayer(source)
job = Player.PlayerData.job.name
grade = Player.PlayerData.job.grade.level
elseif ESX then
local xPlayer = ESX.GetPlayerFromId(source)
local jobtbl = xPlayer.getJob()
job = jobtbl.name
grade = jobtbl.grade
else
-- STANDALONE, Implement your job system!!!
end
return job, grade
end
Config.DoJobCheck = function(player, jobs)
local job, grade = Config.GetPlayerJob(player)
if Config.Framework == 'standalone' then return true end -- Skip Job Checks for standalone
if jobs[job] then
if type(jobs[job]) == "table" then
for _, gr in pairs(jobs[job]) do -- Check Grade
if gr == grade then return true end
end
else
return true
end
end
return false
end
Config.IsVehicleOwned = function(player, plateNum)
local result = false
if not plateNum then return false end
if GetResourceState('qb-core') == 'started' or GetResourceState('qbx_core') == 'started' then -- Qb/QBX ownership
local retrievedLicense = exports.oxmysql:scalar_async(
'SELECT `license` FROM `player_vehicles` WHERE `plate` = ? LIMIT 1', {
plateNum
})
if retrievedLicense then result = retrievedLicense ~= nil end
elseif GetResourceState('es_extended') == 'started' then -- ESX ownership
local retrievedOwner = exports.oxmysql:scalar_async(
'SELECT `owner` FROM `owned_vehicles` WHERE `plate` = ? LIMIT 1', {
plateNum
})
local trimmedOwner = retrievedOwner:sub(retrievedOwner:find(":") + 1)
if trimmedOwner then result = trimmedOwner ~= nil end
else
-- STANDALONE, Implement your money system!!!
end
return result
end
Config.GetPlayerIdentifier = function(player)
if QBCore then
local Player = QBCore.Functions.GetPlayer(player)
return Player.PlayerData.citizenid
elseif ESX then
local xPlayer = ESX.GetPlayerFromId(player)
return xPlayer.getIdentifier()
else -- STANDALONE, Implement your money system!!!
return GetPlayerIdentifierByType(player, 'license')
end
end
Config.IsPlayerAnAdmin = function(player)
if IsPlayerAceAllowed(player, 'stancer') or IsPlayerAceAllowed(player, 'command') then
return true
end
if QBCore and Config.Framework == 'qb' and QBCore.Functions.HasPermission(player, 'admin') then
return true
end
if ESX and Config.Framework == 'esx' and ESX.GetPlayerFromId then
local xPlayer = ESX.GetPlayerFromId(player)
return xPlayer.group == 'admin'
end
return false
end
if Config.EnableWebhooks then
local webHook = '' -- WEBHOOK URL , insert yours here
local Colors = { -- https://www.spycolor.com/
['default'] = 14423100,
['blue'] = 255,
['red'] = 16711680,
['green'] = 65280,
['white'] = 16777215,
['black'] = 0,
['orange'] = 16744192,
['yellow'] = 16776960,
['pink'] = 16761035,
["lightgreen"] = 65309,
-- ADD MORE HERE
}
AddEventHandler('tuff-wheelstancer:webhook', function(title, color, message)
local defaultImageURL =
"https://www.freepnglogos.com/uploads/discord-logo-png/discord-logo-logodownload-download-logotipos-1.png"
local headerImageURL = Config.Webhooks.HeaderImageURL
-- Check if HeaderImageURL is not defined, empty, or contains uppercase letters, numbers, special characters, or underscores
if not headerImageURL or headerImageURL == "" or string.match(headerImageURL, "[%u%d%W_]") then
headerImageURL = defaultImageURL
end
local embedData = {
{
['title'] = Config.Webhooks.WebhookTitle .. title,
['color'] = Colors[color] or Colors['default'],
['footer'] = { ['text'] = os.date('%c'), },
['description'] = message,
['author'] = { ['name'] = Config.Webhooks.LogHeader, ['icon_url'] = headerImageURL, },
}
}
PerformHttpRequest(webHook, function() end, 'POST',
json.encode({ username = Config.Webhooks.LogHeader, embeds = embedData }),
{ ['Content-Type'] = 'application/json' })
Citizen.Wait(100)
if Config.WebhooksOptions.TagEveryone then
PerformHttpRequest(webHook, function() end, 'POST',
json.encode({ username = Config.Webhooks.LogHeader, content = '@everyone' }),
{ ['Content-Type'] = 'application/json' })
end
end)
end
Last updated