Config file

--[[
KLB‑Scrapyard — Configuration
Clean, consistent, and fully commented (English)

Notes
- Difficulty tiers are UNIFIED across the file: Easy / Medium / Hard
- Boolean flags force behavior. Numbers between 0–1 act as probability (e.g., 0.65 = 65%).
- Time values are seconds unless otherwise specified.
- Distances are meters.
]]

Config = {}

-- =========================
-- Core / General
-- =========================
Config.Locale = 'en'
Config.Debug  = false            -- true = extra console logs

-- Optional integrations
Config.UseTarget   = true        -- Enable qb-target (or compatible) interactions
Config.UseDatabase = true        -- Enable MySQL persistence for contracts, reputation, etc.

-- Inventory compatibility (auto-detects ox_inventory, ps-inventory, qb-inventory)
Config.Inventory = {
    Type = 'auto'               -- Options: 'auto', 'qb', 'ps', 'ox'
}

-- Dispatch / Police alerts integration
Config.Dispatch = {
    Type = 'auto',              -- 'auto', 'legacy', 'ps-dispatch', 'qb-dispatch', 'cd_dispatch', 'lb-tablet', or an array of these values
    Jobs = nil,                 -- Optional override for jobs that receive alerts (defaults to Config.AlertJobs)
    Title = 'Scrapyard Theft',  -- Title used for dispatch notifications (supports placeholders)
    Message = nil,              -- Override alert message (supports placeholders); fallback is dynamic if nil
    Description = 'Contract: {contract}\nVehicle: {vehicle}\nPlate: {plate}\nColor: {color}', -- Shown in systems that support rich descriptions
    Code = '10-66',             -- Dispatch code used by supported systems
    Priority = 2,               -- 1 = high priority, 3 = low
    Radius = 55.0,              -- Alert radius for systems that support it
    Blip = {
        sprite = 229,
        colour = 1,
        scale = 1.2,
        flashes = true
    },
    Legacy = {
        Enabled = true,         -- true = send legacy qb-notify/blip alerts if dispatch fails
        Always = false,         -- true = always send legacy alerts even when dispatch succeeds
        Message = 'Scrapyard job alert for {vehicle} ({plate})', -- Legacy notify text (placeholders supported)
        NotifyType = 'error'    -- qb-notify message type
    },
    PS = {
        DispatchCode = '10-66', -- Custom code for ps-dispatch
        Radius = 0,
        Event = 'vehicle_theft',
        Zone = 'Scrapyard',
        Title = nil,             -- Optional ps-dispatch title override (placeholders ok)
        Message = nil,           -- Optional ps-dispatch message override (placeholders ok)
        Description = nil,       -- Optional ps-dispatch description override (placeholders ok)
        BlipText = nil           -- Optional ps-dispatch blip label (placeholders ok)
    },
    QB = {
        Length = '7500',        -- Duration (ms) the qb-dispatch alert should stay active
        Title = nil,            -- Optional qb-dispatch title override (placeholders ok)
        Info = nil,             -- Optional qb-dispatch info field override (placeholders ok)
        Description = nil       -- Optional qb-dispatch description override (placeholders ok)
    },
    CD = {
        DispatchCode = '10-66', -- Code used for cd_dispatch alerts
        Message = nil,          -- Optional cd_dispatch message override (placeholders ok)
        Description = nil       -- Optional cd_dispatch description override (placeholders ok)
    },
    LB = {
        Icon = 'fas fa-car',    -- Icon shown in lb-tablet alerts
        Response = 'emergency', -- Response type for lb-tablet
        Department = nil,       -- Optional lb-tablet department override
        Title = nil,            -- Optional lb-tablet title override (placeholders ok)
        Message = nil,          -- Optional lb-tablet message override (placeholders ok)
        Description = nil       -- Optional lb-tablet description override (placeholders ok)
    }
}

-- =========================
-- Mission Interaction (battery & GPS hacking)
-- =========================

-- Battery removal duration (supports fixed or ranged)
Config.BatteryRemovalTime      = 50                            -- fallback if range below is disabled
Config.BatteryRemovalTimeRange = { min = 50, max = 50 }        -- set min=max for fixed time

-- Hacking duration for intact vehicles that ship with GPS trackers
Config.HackingTime = 12

-- High-tier logic
Config.RequireHackingForIntact = true  -- Intact high-tier cars require hacking first
Config.UseFlatbedForDamaged    = true  -- Damaged cars must be transported on flatbed

-- Items (rewards / requirements)
Config.BatteryRewardItem   = 'car_battery'
Config.BatteryToolItem     = 'tool_kit'
Config.HackingToolItem     = 'hacking_device'
Config.ConsumeHackingTool  = true      -- true = remove one hacking_device on successful hack

-- Police GPS blip lingers after battery removal
Config.BatteryRemovalAlertLinger = 30   -- seconds

-- Cooldown after canceling a contract
Config.CancelCooldownMinutes = 5

-- Progressive hacking stages (e.g., repeated attempts)
Config.HackStages = {
    { cooldown = 60,  suppression = 30, durationMultiplier = 1.00 },
    { cooldown = 180, suppression = 30, durationMultiplier = 1.35 },
    { cooldown = 0,   suppression = 0,  durationMultiplier = 1.60 }
}

-- Auto-mark a vehicle on map when player gets close
Config.AutoMarkRadius = 35.0

-- How often police GPS updates (ms) for tracked vehicles
Config.PoliceGpsUpdateInterval = 15000

-- Reward multipliers based on vehicle state when delivered
Config.RewardMultiplier = {
    Intact  = 2.0,
    Damaged = 1.0
}

-- High-tier vehicles (names or hashes)
Config.HighTierList = {
    'banshee','adder','turismo','entity2','infernus','italigto','zentorno'
}

-- Treat these GTA classes as high tier (auto-include custom cars)
Config.HighTierClasses = { 'super', 'sports', 'sportsclassics' }

-- Legacy alias (do not edit). Maintains backward compatibility.
Config.HighEndVehicles = Config.HighEndVehicles or Config.HighTierList

-- Mission type overrides (probabilities or booleans)
-- Use  true/false to force, or 0.0–1.0 for chance.
Config.MissionTypes = {
    Easy   = { requiresFlatbed = false,  requiresHacking = false },
    Medium = { requiresFlatbed = 0.15,   requiresHacking = 0.25  },
    Hard   = { requiresFlatbed = 0.65,   requiresHacking = 0.65  }
}

Config.ProgressTime = 5000

-- Target distance for wheel interactions
Config.TargetDistance = 1.5

-- Item name required
Config.ItemName = 'rubber_tire'

-- Vehicle wheel bones and tire indexes
Config.WheelBones = {
    { bone = 'wheel_lf', tire = 0, label = 'Front Left Tire'  },
    { bone = 'wheel_rf', tire = 1, label = 'Front Right Tire' },
    { bone = 'wheel_rr', tire = 4, label = 'Rear Right Tire'  },
    { bone = 'wheel_lr', tire = 5, label = 'Rear Left Tire'   },
}



-- =========================
-- Job Start (NPC) & Workshop
-- =========================
Config.StartLocation = {
    coords   = vector3(-540.16, -1637.78, 19.89),
    heading  = 160.0,
    ped      = 's_m_m_lathandy_01',
    scenario = 'WORLD_HUMAN_CLIPBOARD'
}

Config.Workshop = {
    pos    = vector3(-543.97, -1628.35, 18.21),
    radius = 5.0,
    label  = 'Dismantle Bay'
}

Config.FlatbedSpawn = {
    coords  = vector3(-567.61, -1647.88, 19.23),
    heading = 237.34
}

-- =========================
-- Dynamic Spawn Points
-- =========================
local dynamicSpawnGroups = {
    {
        name = 'Vinewood Hills',
        spawns = {
            { id = 'vinewood_hills_jump',     coords = vec4(-1419.32, 537.44, 121.36, 118.73) },
            { id = 'vinewood_hills_neighbor', coords = vec4(-1483.06, 529.00, 118.27, 209.21) },
            { id = 'vinewood_hills_drive',    coords = vec4(-1347.71, 610.62, 133.90, 113.49) },
            { id = 'vinewood_hills_corner',   coords = vec4(-1352.40, 493.45, 103.89, 103.85) },
            { id = 'vinewood_hills_overlook', coords = vec4(-1321.23, 449.97, 99.65, 176.53) }
        }
    },
    {
        name = 'Vinewood Hills 2',
        spawns = {
            { id = 'vinewood_hills2_front',    coords = vec4(-1115.72, 770.31, 163.22, 183.42) },
            { id = 'vinewood_hills2_garage',   coords = vec4(-1120.13, 788.90, 163.06, 17.79) },
            { id = 'vinewood_hills2_curve',    coords = vec4(-1030.10, 695.91, 161.39, 135.22) },
            { id = 'vinewood_hills2_outlook',  coords = vec4(-976.49, 777.42, 174.28, 40.84) },
            { id = 'vinewood_hills2_drive',    coords = vec4(-1051.90, 769.36, 167.55, 117.58) },
            { id = 'vinewood_hills2_overlook', coords = vec4(-957.98, 800.36, 177.68, 320.00) }
        }
    },
    {
        name = 'Vinewood Hills 3',
        spawns = {
            { id = 'vinewood_hills3_front',  coords = vec4(-274.53, 602.02, 181.75, 176.00) },
            { id = 'vinewood_hills3_upper',  coords = vec4(-243.42, 611.54, 187.19, 345.62) },
            { id = 'vinewood_hills3_court',  coords = vec4(-223.84, 594.01, 190.43, 178.04) },
            { id = 'vinewood_hills3_corner', coords = vec4(-199.17, 612.78, 195.75, 335.05) },
            { id = 'vinewood_hills3_view',   coords = vec4(-144.31, 597.13, 203.82, 182.53) }
        }
    },
    {
        name = 'Mirror Park Classic',
        spawns = {
            { id = 'mirror_park_classic_1', coords = vec4(1057.48, -489.57, 63.51, 85.92) },
            { id = 'mirror_park_classic_2', coords = vec4(1063.33, -445.77, 65.84, 70.39) },
            { id = 'mirror_park_classic_3', coords = vec4(1090.36, -471.40, 65.86, 259.00) },
            { id = 'mirror_park_classic_4', coords = vec4(1006.69, -520.01, 60.71, 32.04) },
            { id = 'mirror_park_classic_5', coords = vec4(1084.86, -496.25, 64.26, 80.68) },
            { id = 'mirror_park_classic_6', coords = vec4(982.36, -572.38, 59.26, 208.22) }
        }
    },
    {
        name = 'Mirror Park Sedans',
        spawns = {
            { id = 'mirror_park_sedan_1',  coords = vec4(958.93, -553.46, 59.22, 32.04) },
            { id = 'mirror_park_sedan_2',  coords = vec4(955.32, -599.88, 59.38, 207.39) },
            { id = 'mirror_park_sedan_3',  coords = vec4(927.84, -569.47, 57.95, 31.76) },
            { id = 'mirror_park_sedan_4',  coords = vec4(914.63, -624.66, 58.06, 137.45) },
            { id = 'mirror_park_sedan_5',  coords = vec4(925.27, -630.38, 57.87, 134.36) },
            { id = 'mirror_park_sedan_6',  coords = vec4(958.51, -661.93, 58.02, 119.86) },
            { id = 'mirror_park_sedan_7',  coords = vec4(974.04, -620.49, 58.84, 305.53) },
            { id = 'mirror_park_sedan_8',  coords = vec4(973.10, -685.50, 57.65, 123.41) },
            { id = 'mirror_park_sedan_9',  coords = vec4(982.21, -708.36, 57.57, 133.17) },
            { id = 'mirror_park_sedan_10', coords = vec4(857.37, -563.96, 57.47, 105.07) },
            { id = 'mirror_park_sedan_11', coords = vec4(853.34, -542.89, 57.33, 71.30) },
            { id = 'mirror_park_sedan_12', coords = vec4(861.50, -524.31, 57.30, 53.26) },
            { id = 'mirror_park_sedan_13', coords = vec4(1024.08, -419.85, 65.75, 43.89) }
        }
    },
    {
        name = 'Grove Street',
        spawns = {
            { id = 'grove_street_1', coords = vec4(90.78, -1966.52, 20.75, 136.56) },
            { id = 'grove_street_2', coords = vec4(42.35, -1919.16, 21.65, 139.39) },
            { id = 'grove_street_3', coords = vec4(53.05, -1878.36, 22.25, 326.24) },
            { id = 'grove_street_4', coords = vec4(15.47, -1882.77, 23.12, 132.94) },
            { id = 'grove_street_5', coords = vec4(2.51, -1875.49, 23.70, 136.29) },
            { id = 'grove_street_6', coords = vec4(125.20, -1858.78, 24.77, 309.03) },
            { id = 'grove_street_7', coords = vec4(121.87, -1890.30, 23.55, 158.15) },
            { id = 'grove_street_8', coords = vec4(137.80, -1893.43, 23.37, 158.77) },
            { id = 'grove_street_9', coords = vec4(160.09, -1872.83, 23.98, 327.32) }
        }
    }
}

Config.DynamicSpawnGroups = dynamicSpawnGroups
Config.DynamicSpawns      = {}
for _, group in ipairs(dynamicSpawnGroups) do
    for _, spawn in ipairs(group.spawns) do
        Config.DynamicSpawns[#Config.DynamicSpawns + 1] = spawn
    end
end
-- Config.DynamicSpawns now contains a flat list for internal use.

-- Spawn blacklist regions / distances
Config.SpawnBlacklist = {
    minPlayerDistance = 75.0,
    minPoliceDistance = 110.0,
    zones = {
        { coords = vector3(440.84,  -981.14, 30.69),  radius = 150.0 }, -- Mission Row PD
        { coords = vector3(1853.0,  3689.0,  34.27),  radius = 120.0 }, -- Sandy PD
        { coords = vector3(-449.56, 6015.95, 32.29),  radius = 120.0 }  -- Paleto PD
    }
}

-- =========================
-- Tools & Contracts
-- =========================
Config.Tools = {
    wrench = { item = 'klb_grinder', breakChance = 0.02, price = 250, stock = 15, uses = 5 },
    cutter = { item = 'klb_cutter',  breakChance = 0.05, price = 375, stock = 10, uses = 5 },
    torch  = { item = 'klb_torch',   breakChance = 0.03, price = 425, stock = 10, uses = 5 }
}

Config.ToolDefaultUses = 5

Config.ToolShop = {
    label        = 'Scrapyard Tools',
    currency     = 'cash',
    defaultPrice = 300,
    defaultStock = 10,
    restock = {
        intervalMinutes = 30, -- How often to top up the limited tool stock (set to 0 to disable automatic restocks)
        amount = 'full'       -- Use 'full' to refill to the maximum, or a number to add that many items each cycle
    },
    hackingDevice = {
        enabled = true,               -- Allow purchasing the hacking device from the tool shop
        item    = Config.HackingToolItem,
        label   = 'Hacking Device',
        price   = 750,
        stock   = 5
    }
}

-- Vehicle categories used for contract generation
Config.VehicleCategories = {
    {
        name = 'Compacts',               -- Common street finds
        previewLabel = 'Compact street cars',
        minLevel = 1,
        chance   = 50,
        vehicles = {
            'blista','asbo','futo','dominator','sultan','bestiagts','elegy','flashgt','gb200','sultan2','rhapsody','kanjo','brioso','club','issi','panto','brioso2','dominator7','buffalo','calico','blista2'
        }
    },
    {
        name = 'Sports Classic',         -- Classic collector cars
        previewLabel = 'Vintage performance cars',
        minLevel = 2,
        chance   = 25,
        vehicles = {
            'mamba','jb700','zion3','z190','infernus2','michelli','stingergt','coquette','cypher','furoregt','growler','jester','ninef','remus'
        }
    },
    {
        name = 'Sports',                  -- Modern performance cars
        previewLabel = 'Modern sports cars',
        minLevel = 5,
        chance   = 15,
        vehicles = {
            'sultan2','sugoi','sentinel3','jugular','jester3','italigto','growler','elegy','comet2','comet3','coquette4','italirsx','pfister811'
        }
    },
    {
        name = 'Super',                   -- Ultra rare super cars
        previewLabel = 'High-end supercars',
        minLevel = 7,
        chance   = 5,
        vehicles = {
            'entity2','ignus','sheava','turismo3','zentorno','sultanrs','thrax','infernus','krieger','tempesta','torero2','tyrant','zorrusso'
        }
    }
}

-- How many contract options to roll per difficulty when presenting to player
Config.ContractsPerDifficulty = 3

-- Category weights by difficulty when picking the vehicle pool
Config.ContractCategoryWeights = {
    Easy = {
        Compacts = 60,
        ['Sports Classic'] = 32,
        Sports = 8
    },
    Medium = {
        Compacts = 20,
        ['Sports Classic'] = 35,
        Sports = 45
    },
    Hard = {
        ['Sports Classic'] = 20,
        Sports = 45,
        Super  = 35
    }
}

-- Extra payout by vehicle category
Config.VehicleCategoryPayoutBonus = {
    default = 1.0,
    Compacts = 1.0,
    ['Sports Classic'] = 1.15,
    Sports = 1.35,
    Super  = 2.0
}

-- Random variance on total payout
Config.ContractPayoutVariance = {
    default = { min = 0.95, max = 1.15 },
    Easy    = { min = 0.95, max = 1.15 },
    Medium  = { min = 1.05, max = 1.25 },
    Hard    = { min = 1.15, max = 1.35 }
}

-- Fallback vehicle list if a pool is empty or invalid
Config.VehicleFallback = { 'baller2' }

-- Supported flatbed models for tow checks
Config.FlatbedModels = { 'flatbed','flatbed2','flatbed3','flatbedm2' }

-- If string (e.g., 'KLB'), plates get that prefix; false disables
Config.DefaultPlatePrefix = false

-- Long‑range contract bonuses
Config.FarDistanceBonus = {
    threshold        = 1800.0,  -- distance before contract is considered long‑range
    minimumLootRolls = 2,       -- ensure at least this many item rolls
    payoutMultiplier = 1.35,    -- extra payout multiplier
    lifetimeMinutes  = 15       -- highlighted/limited flag duration
}

-- Fuel settings for spawned vehicles
Config.VehicleFuel = {
    minPercent    = 5,          -- minimum starting fuel %
    maxPercent    = 15,         -- maximum starting fuel %
    stateBagKey   = 'fuel',     -- false to disable setting state bag key
    exportResource = nil,       -- e.g., 'LegacyFuel'
    exportFunction = 'SetFuel'  -- function on the export to call
}

-- Contract area sizes by difficulty
Config.ContractAreaRadius = {
    default = 120.0,
    Easy    = 110.0,
    Medium  = 135.0,
    Hard    = 160.0
}

-- Contract tier definitions
Config.ContractTypes = {
    Easy = {
        label    = 'Easy Contract',
        minDist  = 150.0,
        maxDist  = 2600.0,
        base     = 150,
        itemRolls= 1,
        risk     = 0.06
    },
    Medium = {
        label    = 'Standard Contract',
        minDist  = 400.0,
        maxDist  = 2600.0,
        base     = 300,
        itemRolls= 3,
        risk     = 0.12
    },
    Hard = {
        label     = 'Heavy Tow Contract',
        minDist   = 900.0,
        maxDist   = 3200.0,
        base      = 750,
        itemRolls = 6,
        risk      = 0.20,
        requiresTow = true
    }
}

-- Item reward table (min/max pieces, base value, rarity)
Config.ItemTable = {
    metalscrap          = { min = 2, max = 6, value = 15,  rarity = 1.0  },
    aluminum            = { min = 1, max = 4, value = 22,  rarity = 0.7  },
    copper              = { min = 1, max = 3, value = 35,  rarity = 0.45 },
    glass               = { min = 1, max = 4, value = 10,  rarity = 0.9  },
    car_battery         = { min = 1, max = 1, value = 120, rarity = 0.15 },
    catalytic_converter = { min = 1, max = 1, value = 250, rarity = 0.08 },
    rubber_tire         = { min = 1, max = 2, value = 30,  rarity = 0.4  }
}

-- Reputation / Leveling
Config.Reputation = {
    perContract = { Easy = 1, Medium = 2, Hard = 3 },
    thresholds  = {
        { rep = 0,   bonus = 0.00 }, -- Level 1
        { rep = 10,  bonus = 0.02 }, -- Level 2
        { rep = 25,  bonus = 0.04 }, -- Level 3
        { rep = 45,  bonus = 0.06 }, -- Level 4
        { rep = 70,  bonus = 0.08 }, -- Level 5
        { rep = 100, bonus = 0.10 }, -- Level 6
        { rep = 140, bonus = 0.12 }  -- Level 7
    },
    minRep = { Easy = 0, Medium = 10, Hard = 35 },
    maxRep = 250
}

-- Economy multipliers
Config.Economy = {
    cashMultiplier      = 1.0,
    distanceMultiplier  = 0.55,
    itemValueMultiplier = 1.0,
    rareMultiplier      = 1.2
}

-- Jobs that receive alerts (police GPS, supercar pings, etc.)
Config.AlertJobs = { 'police' }

-- =========================
-- Timers & Cooldowns
-- =========================
Config.CooldownMinutes       = 3     -- time before another job can be started
Config.VehicleTimeoutMinutes = 12    -- contract vehicle expires after
Config.StripDuration         = 5500  -- ms per part removal

-- Distance measurement origin for payouts (start or workshop)
Config.DistanceMeasureFrom = 'start' -- 'start' or 'workshop'

-- =========================
-- Animations & Emotes
-- =========================
Config.Animations = {
    strip = {
        disableMovement = true,
        sound = { name = 'NAV_UP_DOWN', set = 'HUD_FRONTEND_DEFAULT_SOUNDSET' },
        options = {
            { dict = 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@', anim = 'machinic_loop_mechandplayer', flag = 49 },
            { dict = 'mini@repair',                               anim = 'fixing_a_player',             flag = 49 },
            { dict = 'amb@world_human_vehicle_mechanic@male@base',anim = 'base',                        flag = 49 }
        }
    },
    towAttach = {
        disableMovement = true,
        sound = { name = 'SELECT', set = 'HUD_FRONTEND_DEFAULT_SOUNDSET' },
        options = {
            { dict = 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@', anim = 'machinic_loop_mechandplayer', flag = 49 },
            { dict = 'amb@world_human_hammering@male@base',      anim = 'base',                        flag = 49 }
        }
    },
    towDetach = {
        disableMovement = true,
        sound = { name = 'BACK', set = 'HUD_FRONTEND_DEFAULT_SOUNDSET' },
        options = {
            { dict = 'mini@repair',                               anim = 'fixing_a_player',             flag = 49 },
            { dict = 'amb@world_human_vehicle_mechanic@male@idle_a',anim = 'idle_a',                    flag = 49 }
        }
    }
}

-- =========================
-- Direct Mechanic Integration
-- =========================
Config.AllowDirectMechanicUse = true  -- Allow scrapyard parts to be used directly
Config.MechanicIntegration = {
    useMechanicMenu = false,          -- If true, klb-mechanic should handle the UI/menu
    resource = 'klb-mechanic'
}

-- =========================
-- Timed Contracts (rotating jobs)
-- =========================
Config.TimedJobs = {
    minActive        = 3,   -- minimum active timed contracts in rotation
    maxActive        = 4,   -- maximum active timed contracts in rotation
    rotationMinutes  = 15,  -- how often the pool regenerates
    deadlines = {           -- deadline (minutes) per difficulty
        Easy = 14,
        Medium = 12,
        Hard = 10
    },
    previewItems        = 3,   -- number of reward previews sent to client
    supercarChance      = 35,  -- % chance to spawn a supercar when allowed
    guardAggroDistance  = 30.0,
    guardDespawnDistance= 300.0,
    batteryTool         = 'tool_kit',   -- item required to remove a battery on-site
    batteryItem         = 'car_battery',-- item required to INSTALL a battery on-site
    towProofTimeout     = 60,           -- seconds a flatbed check stays valid
    -- requiresFlatbed  = { Hard = true },  -- optional per-difficulty override
    hardFlatbedChance   = 75            -- % chance Hard contracts require flatbed
}

-- Chance that supercars spawn without a battery (forces on-site install)
Config.SpawnWithoutBatteryChance = 25   -- % (supercars only)

-- How far the contract area preview blip can be offset
Config.BlipOffsetRange = { min = 35.0, max = 75.0 }

-- Guards used in timed jobs
Config.Guards = {
    spawnRadius = 8.0,
    pistol  = { model = 's_m_m_highsec_02', weapon = 'WEAPON_PISTOL',  accuracy = 55 },
    machete = { model = 'g_m_y_mexgang_01', weapon = 'WEAPON_MACHETE', accuracy = 40 }
}

-- Timed job clusters / centers
Config.ScrapJobCenters = {
    {
        id = 'terminal_docks',
        label = 'Terminal Docks',
        center = vector3(1209.83, -3238.42, 5.90),
        radius = 140.0,
        difficulties = { 'Easy', 'Medium' },
        needsTools  = { 'klb_wrench' },
        allowSupercars = false,
        vehiclePool = {
            regular = { 'blista','asbo','futo','dominator','sultan','bestiagts','elegy','flashgt','gb200','sultan2','rhapsody','kanjo','brioso','club','issi','panto','brioso2','dominator7','buffalo','calico','blista2' }
        }
    },
    {
        id = 'del_perro',
        label = 'Del Perro Garages',
        center = vector3(-1616.24, -849.55, 9.0),
        radius = 125.0,
        difficulties = { 'Medium', 'Hard' },
        needsTools = {
            Medium = { 'klb_wrench', 'klb_cutter' },
            Hard   = { 'klb_wrench', 'klb_cutter', 'tool_kit', 'car_battery' }
        },
        allowSupercars = true,
        -- requiresFlatbed = { Hard = false }, -- optional per-location override
        -- flatbedChance  = { Hard = 50 },
        vehiclePool = {
            regular = { 'buffalo2','sentinel2','penumbra','comet2','sugoi','bestiagts','elegy','flashgt','gb200','sultan2' },
            super   = { 'turismo3','thrax','ignus','entity2','comet6' }
        }
    },
    {
        id = 'vinewood_hills_special',
        label = 'Vinewood Hills Estates',
        center = vector3(-867.51, 458.66, 88.28),
        radius = 160.0,
        difficulties = { 'Medium' },
        needsTools  = { 'klb_wrench', 'klb_torch' },
        allowSupercars = true,
        vehiclePool = {
            regular = { 'zion3','drafter','paragon','locust','jugular','bestiagts','elegy','flashgt','gb200','sultan2' },
            super   = { 'sheava','zentorno','sultanrs','entity2','comet6' }
        }
    },
    {
        id = 'mirror_park_row',
        label = 'Mirror Park Row Houses',
        center = vector3(1104.62, -431.72, 66.0),
        radius = 115.0,
        difficulties = { 'Easy' },
        needsTools  = { 'klb_wrench' },
        allowSupercars = false,
        vehiclePool = {
            regular = { 'kanjo','rhapsody','brioso','issi3','futo2','comet6','elegy','flashgt','gb200','sultan2' }
        }
    }
}

-- Fallback for timed jobs
Config.TimedJobVehicleFallback = { 'baller2','tailgater2','schafter2' }

-- Supercar GPS alerting
Config.SupercarGps = {
    alertJobs  = { 'police' },
    pingInterval = 45,      -- seconds between GPS pings
    blipSprite  = 326,
    blipColour  = 1,
    blipScale   = 0.85,
    blipLabel   = 'Supercar GPS',
    blipRadius  = 20.0,
    blipAlpha   = 120,
    coords = nil             -- optional override (vector3 or list)
}

-- Timed job reward bands
Config.RewardTables = {
    Easy = {
        cash  = { min = 250, max = 420 },
        items = {
            { item = 'metalscrap',       min = 2, max = 5, chance = 100 },
            { item = 'aluminum',         min = 1, max = 3, chance = 75  },
            { item = 'copper',           min = 1, max = 2, chance = 65  },
            { item = 'rubber_tire',      min = 1, max = 2, chance = 60  },
            { item = 'mechanic_skirts',  min = 1, max = 1, chance = 25  },
            { item = 'mechanic_bumpers', min = 1, max = 1, chance = 20  },
            { item = 'mechanic_spoilers',min = 1, max = 1, chance = 15  }
        }
    },
    Medium = {
        cash  = { min = 420, max = 650 },
        items = {
            { item = 'metalscrap',        min = 3, max = 6, chance = 100 },
            { item = 'aluminum',          min = 2, max = 4, chance = 85  },
            { item = 'copper',            min = 1, max = 3, chance = 75  },
            { item = 'car_battery',       min = 1, max = 1, chance = 30  },
            { item = 'mechanic_spoilers', min = 1, max = 2, chance = 30  },
            { item = 'mechanic_exhaust',  min = 1, max = 1, chance = 35  },
            { item = 'mechanic_roof',     min = 1, max = 1, chance = 25  },
            { item = 'mechanic_hood',     min = 1, max = 1, chance = 25  }
        }
    },
    Hard = {
        cash  = { min = 950, max = 1350 },
        items = {
            { item = 'metalscrap',          min = 4, max = 8, chance = 100 },
            { item = 'aluminum',            min = 2, max = 5, chance = 90  },
            { item = 'copper',              min = 2, max = 4, chance = 85  },
            { item = 'car_battery',         min = 1, max = 1, chance = 45  },
            { item = 'mechanic_rollcage',   min = 1, max = 1, chance = 35  },
            { item = 'mechanic_seats',      min = 1, max = 1, chance = 35  },
            { item = 'mechanic_spoilers',   min = 1, max = 2, chance = 45  },
            { item = 'mechanic_bumpers',    min = 1, max = 2, chance = 40  },
            { item = 'mechanic_exhaust',    min = 1, max = 1, chance = 40  }
        }
    }
}
```lua

Last updated