> For the complete documentation index, see [llms.txt](https://tuff-scripts.gitbook.io/tuff-scripts/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tuff-scripts.gitbook.io/tuff-scripts/scripts/tuff-wheelstancer-v2/config-file.md).

# 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.

{% tabs %}
{% tab title="Config.lua" %}

```lua
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               = 0,
            WHEEL_NUMBER             = 0,
            ALL_WHEELS_WIDTH         = 0,
            ALL_WHEELS_SIZE          = 0,
            ALL_WHEELS_OFFSET        = 0,
            ALL_WHEELS_CAMBER        = 0,
            SUSPENSION_HEIGHT        = 0,
            WHEEL_FRONT_LEFT_OFFSET  = 0,
            WHEEL_FRONT_RIGHT_OFFSET = 1,
            WHEEL_REAR_LEFT_OFFSET   = 1,
            WHEEL_REAR_RIGHT_OFFSET  = 1,
            WHEEL_FRONT_LEFT_CAMBER  = 1,
            WHEEL_FRONT_RIGHT_CAMBER = 1,
            WHEEL_REAR_LEFT_CAMBER   = 1,
            WHEEL_REAR_RIGHT_CAMBER  = 1,
        },
        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
}

```

{% endtab %}

{% tab title="Invoice.lua" %}

```lua
--[[
    =======================
    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

```

{% endtab %}

{% tab title="cl\_editable.lua" %}

```lua
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)
```

{% endtab %}

{% tab title="sv\_editable.lua" %}

```lua
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

```

{% endtab %}

{% tab title="sv\_webhook.lua" %}

```lua
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

```

{% endtab %}
{% endtabs %}
