Installation

circle-exclamation
circle-info

This is a step-by-step tutorial for setting up our script.

3

Install optional dependencies

These resources are not required for the script.

4

Resource start

It is important that your script is started after the necessary resources.

start oxmysql
start ox_lib
start framework
start syniq_delivery
5

Editable Config

Config = {
    Debug = false, -- Enable debug mode (prints extra info to console)
    Locale = 'en', -- Language for notifications and UI ('en', 'de', 'es', 'fr')
    useTarget = false, -- Use target system instead of markers/TextUI (auto-detects ox_target, qb-target, qtarget)

    JobRequired = true, -- Require specific job to access delivery jobs
    JobInfo = {
        name = 'delivery', -- Job name required to access delivery jobs 
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- PACKAGES CONFIG
    -- Settings for delivery packages
    -- ═══════════════════════════════════════════════════════════════════════════
    Packages = {
        prop = 'prop_cs_cardbox_01', -- Package prop model that player carries
        loadTime = 2500, -- Time in ms to pick up a package from pickup zone
        deliverTime = 2000, -- Time in ms to deliver a package to NPC
        payPerPackage = 25, -- Base payment per package delivered ($)
        
        -- Number of packages per difficulty (random between min-max)
        counts = {
            easy = {min = 1, max = 5},
            medium = {min = 6, max = 10},
            hard = {min = 11, max = 20},
            vip = {min = 21, max = 35}
        },
        
        -- Number of delivery locations per difficulty (random between min-max)
        locations = {
            easy = {min = 2, max = 3},
            medium = {min = 4, max = 5},
            hard = {min = 6, max = 8},
            vip = {min = 9, max = 15}
        }
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- VEHICLE RETURN CONFIG
    -- Settings for returning delivery vehicles to depot
    -- ═══════════════════════════════════════════════════════════════════════════
    VehicleReturn = {
        enabled = true, -- Require players to return vehicles after delivery
        damageThreshold = 0.8, -- Health percentage below which damage fee applies (0.8 = 80%)
        baseFee = 500, -- Base damage fee if vehicle is damaged ($)
        feePerDamagePercent = 10, -- Additional fee per percent of damage ($)
        returnRadius = 3.0, -- Distance from return point to allow vehicle return (meters)
        blip = {sprite = 477, color = 1, scale = 0.8, name = 'Vehicle Return'} -- Map blip settings
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- ANIMATIONS CONFIG
    -- Animation dictionaries and names for various actions
    -- ═══════════════════════════════════════════════════════════════════════════
    Animations = {
        carry = {dict = 'anim@heists@box_carry@', anim = 'idle', flag = 51}, -- Animation while carrying package
        pickup = {dict = 'random@domestic', anim = 'pickup_low', duration = 2000}, -- Animation when picking up package from ground
        putdown = {dict = 'anim@heists@box_carry@', anim = 'put_down_box_front', duration = 1500}, -- Animation when putting package in vehicle
        give = {dict = 'mp_common', anim = 'givetake1_a', duration = 1500}, -- Animation when giving package to NPC
        receive = {dict = 'mp_common', anim = 'givetake1_b', duration = 1500} -- NPC receiving animation
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- NPC CONFIG
    -- Settings for delivery NPCs that receive packages
    -- ═══════════════════════════════════════════════════════════════════════════
    NPC = {
        models = { -- Random NPC models used at delivery locations
            'a_m_m_business_01', 'a_m_m_indian_01', 'a_m_m_eastsa_02',
            'a_f_y_business_01', 'a_f_y_hipster_01', 'a_m_y_hipster_01',
            's_m_m_warehouse_01', 'a_m_m_soucent_01', 'a_f_m_eastsa_01'
        },
        despawnDelay = 3000, -- Time in ms before NPC despawns after delivery complete
        spawnDistance = 40.0 -- Distance from delivery point at which NPC spawns (meters)
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- JOB LOCATIONS
    -- Coordinates for depot, vehicle spawn, and package pickup
    -- ═══════════════════════════════════════════════════════════════════════════
    JobLocations = {
        Depot = { -- Main depot where players start jobs
            coords = vector3(-424.2207, -2789.7605, 6.5297),
            blip = {sprite = 478, color = 5, scale = 0.8, name = 'Delivery Depot', onlyForJob = false}, -- Map blip settings
            marker = {type = 1, size = vector3(1.5, 1.5, 1.0), color = {r = 66, g = 135, b = 245, a = 150}} -- Ground marker settings
        },
        VehicleSpawn = { -- Where delivery vehicles spawn
            coords = vector3(-450.4776, -2794.8721, 5.5884),
            heading = 43.9381 -- Vehicle spawn direction (degrees)
        },
        VehicleReturn = { -- Where players return vehicles after job
            coords = vector3(-450.4776, -2794.8721, 5.5884),
            marker = {type = 36, size = vector3(1.5, 1.5, 1.0), color = {r = 255, g = 50, b = 50, a = 150}}
        },
        PackagePickup = { -- Where players pick up packages before delivery
            coords = vector3(-449.3051, -2805.6663, 7.2959),
            marker = {type = 1, size = vector3(2.0, 2.0, 0.5), color = {r = 0, g = 255, b = 100, a = 150}}
        }
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- VEHICLES CONFIG
    -- Available delivery vehicles with stats and unlock requirements
    -- ═══════════════════════════════════════════════════════════════════════════
    Vehicles = {
        speedo = {
            model = 'speedo', -- GTA vehicle spawn name
            label = 'Speedo', -- Display name in menu
            icon = 'fa-van-shuttle', -- FontAwesome icon for UI
            description = 'Starter delivery van', -- Description shown in menu
            basePayMultiplier = 1.0, -- Payment multiplier (1.0 = 100%)
            speedBonus = 1.0, -- Speed rating (affects time bonus calculation)
            maxPackages = 5, -- Maximum packages this vehicle can carry
            unlockLevel = 1, -- Required player level to use
            sortOrder = 1, -- Order in vehicle selection menu
            offsetMarker = vector3(0.0, 0.0, -.98) -- Marker offset when parking vehicles
        },
        boxville = {
            model = 'youga', label = 'Youga', icon = 'fa-van-shuttle',
            description = 'Medium capacity van',
            basePayMultiplier = 1.2, speedBonus = 0.9, maxPackages = 10,
            unlockLevel = 2, sortOrder = 2,
            offsetMarker = vector3(0.0, 0.0, -.4)
        },
        rumpo = {
            model = 'rumpo', label = 'Rumpo', icon = 'fa-van-shuttle',
            description = 'Fast delivery van',
            basePayMultiplier = 1.3, speedBonus = 1.1, maxPackages = 20,
            unlockLevel = 5, sortOrder = 3,
            offsetMarker = vector3(0.0, 0.0, -.4)
        },
        mule = {
            model = 'boxville2', label = 'Boxville', icon = 'fa-truck',
            description = 'Large delivery truck',
            basePayMultiplier = 1.5, speedBonus = 0.8, maxPackages = 35,
            unlockLevel = 10, sortOrder = 4,
            offsetMarker = vector3(0.0, 0.0, -.98)
        },
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- DELIVERY POINTS
    -- All possible delivery locations grouped by area
    -- Areas: 'downtown', 'south', 'east', 'west', 'vinewood', 'county',
    -- ═══════════════════════════════════════════════════════════════════════════
    DeliveryPoints = {
        -- Downtown area (easy + medium + hard)
        {name = 'Legion Square', coords = vector4(146.079, -1058.863, 30.186, 158.527), area = 'downtown'},
        {name = 'Pill Box Hospital', coords = vector4(363.338, -711.979, 29.286, 69.285), area = 'downtown'},
        {name = 'Mission Row PD', coords = vector4(391.990, -916.229, 29.776, 269.730), area = 'downtown'},
        {name = 'Alta Street', coords = vector4(64.831, -254.602, 48.188, 73.582), area = 'downtown'},
        {name = 'Textile City', coords = vector4(465.794, -735.723, 27.364, 94.413), area = 'downtown'},
        {name = 'Little Seoul', coords = vector4(-599.346, -1014.915, 22.320, 94.416), area = 'downtown'},
        {name = 'Strawberry Ave', coords = vector4(170.282, -1336.812, 29.294, 274.828), area = 'downtown'},
        {name = 'Pillbox Hill', coords = vector4(109.546, -637.068, 44.244, 71.236), area = 'downtown'},
        -- South area (easy + medium + hard)
        {name = 'Davis Avenue', coords = vector4(144.045, -1656.479, 29.331, 224.277), area = 'south'},
        {name = 'Grove Street', coords = vector4(-33.411, -1847.319, 26.194, 316.136), area = 'south'},
        {name = 'Forum Drive', coords = vector4(-68.637, -1526.377, 34.232, 316.139), area = 'south'},
        {name = 'Carson Avenue', coords = vector4(289.026, -1792.492, 28.085, 316.141), area = 'south'},
        {name = 'Jamestown St', coords = vector4(414.488, -1856.189, 27.323, 248.963), area = 'south'},
        {name = 'Brouge Ave', coords = vector4(280.533, -1693.791, 29.266, 47.242), area = 'south'},
        -- East area (medium + hard)
        {name = 'La Mesa', coords = vector4(967.672, -1868.346, 31.293, 164.071), area = 'east'},
        {name = 'Mirror Park', coords = vector4(1052.860, -470.789, 63.899, 261.804), area = 'east'},
        {name = 'East Vinewood', coords = vector4(920.664, -238.729, 70.155, 141.169), area = 'east'},
        {name = 'El Burro Heights', coords = vector4(1258.298, -1760.014, 49.260, 21.906), area = 'east'},
        {name = 'Nikola Pl', coords = vector4(1301.221, -573.285, 71.732, 341.826), area = 'east'},
        {name = 'West Mirror Drive', coords = vector4(997.295, -729.228, 57.816, 311.708), area = 'east'},
        -- West area (medium + hard)
        {name = 'Vespucci', coords = vector4(-741.845, -981.528, 17.057, 21.909), area = 'west'},
        {name = 'Del Perro', coords = vector4(-1329.337, -938.335, 12.350, 21.913), area = 'west'},
        {name = 'Pacific Bluffs', coords = vector4(-1919.172, -555.874, 11.761, 146.599), area = 'west'},
        {name = 'Vespucci Beach', coords = vector4(-1300.161, -1233.251, 4.486, 287.639), area = 'west'},
        {name = 'Cortes St', coords = vector4(-1350.137, -1161.485, 4.507, 90.788), area = 'west'},
        {name = 'Magellan Ave', coords = vector4(-1150.644, -1473.933, 4.380, 125.439), area = 'west'},
        -- Vinewood area (hard + vip)
        {name = 'Vinewood Hills', coords = vector4(-678.681, 511.421, 113.526, 196.112), area = 'vinewood'},
        {name = 'Eclipse Towers', coords = vector4(-769.712, 299.446, 85.696, 178.487), area = 'vinewood'},
        {name = 'Rockford Hills', coords = vector4(-848.892, 103.825, 53.004, 87.441), area = 'vinewood'},
        {name = 'Burton', coords = vector4(-437.419, -67.358, 43.004, 241.881), area = 'vinewood'},
        {name = 'Kimble Hill Dr', coords = vector4(-232.659, 588.454, 190.536, 355.189), area = 'vinewood'},
        {name = 'St. Mo Milton Drive', coords = vector4(-1022.598, 588.308, 103.233, 1.627), area = 'vinewood'},
        -- County area (hard only - long distance)
        {name = 'Sandy Shores', coords = vector4(1881.399, 3810.771, 32.779, 304.496), area = 'county'},
        {name = 'Grapeseed', coords = vector4(1658.561, 4839.306, 42.035, 279.472), area = 'county'},
        {name = 'Paleto Bay', coords = vector4(-266.545, 6249.542, 31.476, 50.115), area = 'county'},
        {name = 'Harmony', coords = vector4(980.211, 2667.751, 40.061, 1.370), area = 'county'},
        {name = 'Cholla Springs Ave', coords = vector4(1809.107, 3907.363, 33.744, 193.180), area = 'county'},
        {name = 'Paleto Blvd', coords = vector4(-347.303, 6225.094, 31.884, 225.953), area = 'county'},
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- DIFFICULTY CONFIG
    -- Settings for each difficulty level
    -- ═══════════════════════════════════════════════════════════════════════════
    Difficulty = {
        easy = {
            areas = {'south'}, -- Which delivery areas are used
            timePerPackage = 120, -- Seconds allowed per package
            payMultiplier = 1.0 -- Payment multiplier (1.0 = 100%)
        },
        medium = {
            areas = {'downtown', 'south', 'west'},
            timePerPackage = 50,
            payMultiplier = 1.3 -- 30% more pay
        },
        hard = {
            areas = {'east', 'west', 'vinewood'},
            timePerPackage = 45,
            payMultiplier = 1.6 -- 60% more pay
        },
        vip = {
            areas = {'county'},
            timePerPackage = 35, -- Less time but higher pay
            payMultiplier = 2.5, -- 150% more pay
            bonusPerPackage = 50 -- Extra bonus per package ($)
        }
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- MULTIPLIERS CONFIG
    -- Bonus multipliers based on performance
    -- ═══════════════════════════════════════════════════════════════════════════
    Multipliers = {
        -- Speed bonus based on time remaining (threshold = % of time left)
        Speed = {
            excellent = {threshold = 0.5, multiplier = 1.5, label = 'mult_excellent'}, -- 50%+ time left = 1.5x
            good = {threshold = 0.3, multiplier = 1.25, label = 'mult_good'}, -- 30%+ time left = 1.25x
            normal = {threshold = 0.1, multiplier = 1.0, label = 'mult_normal'}, -- 10%+ time left = 1.0x
            slow = {threshold = 0, multiplier = 0.75, label = 'mult_slow'} -- Less than 10% = 0.75x
        },
        -- Condition bonus based on vehicle damage
        Condition = {
            perfect = {maxDamage = 0.05, multiplier = 1.3, label = 'mult_perfect'}, -- <5% damage = 1.3x
            good = {maxDamage = 0.15, multiplier = 1.15, label = 'mult_good'}, -- <15% damage = 1.15x
            acceptable = {maxDamage = 0.30, multiplier = 1.0, label = 'mult_acceptable'}, -- <30% damage = 1.0x
            damaged = {maxDamage = 0.50, multiplier = 0.8, label = 'mult_damaged'} -- <50% damage = 0.8x
        },
        -- Combo system for consecutive successful deliveries
        Combo = {
            enabled = true, -- Enable combo system
            maxCombo = 10, -- Maximum combo count
            multiplierPerCombo = 0.05, -- 5% bonus per combo level (10 combo = 50% bonus)
            resetOnFail = true, -- Reset combo on failed delivery
            bonusAtMax = 500 -- Bonus cash when reaching max combo ($)
        },
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- LEVELING CONFIG
    -- XP and level progression system
    -- ═══════════════════════════════════════════════════════════════════════════
    Leveling = {
        enabled = true, -- Enable leveling system
        xpPerPackage = 10, -- XP earned per package delivered
        xpBonusVIP = 5, -- Extra XP for VIP deliveries
        xpBonusCombo = 5, -- Extra XP per combo level
        levels = {
            -- level: Player level number
            -- xpRequired: Total XP needed to reach this level
            -- title: Localization key for level title
            -- payBonus: Payment multiplier at this level
            {level = 1, xpRequired = 0, title = 'level_1', payBonus = 1.0},
            {level = 2, xpRequired = 200, title = 'level_2', payBonus = 1.05},
            {level = 3, xpRequired = 500, title = 'level_3', payBonus = 1.10},
            {level = 4, xpRequired = 1000, title = 'level_4', payBonus = 1.15},
            {level = 5, xpRequired = 2000, title = 'level_5', payBonus = 1.20},
            {level = 6, xpRequired = 3500, title = 'level_6', payBonus = 1.25},
            {level = 7, xpRequired = 5500, title = 'level_7', payBonus = 1.30},
            {level = 8, xpRequired = 8000, title = 'level_8', payBonus = 1.35},
            {level = 9, xpRequired = 12000, title = 'level_9', payBonus = 1.40},
            {level = 10, xpRequired = 20000, title = 'level_10', payBonus = 1.50} -- Max level = 50% pay bonus
        }
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- MISC CONFIG
    -- ═══════════════════════════════════════════════════════════════════════════
    Leaderboard = {
        enabled = true, -- Enable leaderboard system
        maxPlayers = 10 -- Number of players shown on leaderboard
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- CO-OP CONFIG
    -- Settings for cooperative deliveries with multiple players
    -- ═══════════════════════════════════════════════════════════════════════════
    CoOp = {
        enabled = true, -- Enable co-op deliveries
        maxPlayers = 2, -- Maximum players in a co-op team
        inviteRadius = 10.0, -- Distance in meters to invite nearby players
        inviteTimeout = 30, -- Seconds before invite expires
        sharedPayment = true, -- If true, all players get full payment. If false, payment is split
        bonusPerPlayer = 0.1, -- 10% bonus per additional player (0.1 = 10%)
        leaderCanKick = true -- Leader can kick players from team
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- TUTORIAL CONFIG
    -- In-game tutorial with markers and guide text
    -- ═══════════════════════════════════════════════════════════════════════════
    Tutorial = {
        enabled = true, -- Enable tutorial for new players
        markerType = 20, -- Marker type for tutorial highlights
        markerColor = {r = 66, g = 135, b = 245, a = 200}, -- Blue marker color
        markerSize = vector3(1.5, 1.5, 1.5), -- Marker size
        blipSprite = 162, -- Tutorial blip sprite
        blipColor = 3, -- Blue blip
    },

    Misc = {
        penaltyForFail = 100, -- Money deducted when delivery fails ($)
        deliveryBlip = { -- GPS blip for current delivery location
            sprite = 501, -- Blip icon
            color = 5, -- Blip color (yellow)
            scale = 0.8, -- Blip size
            route = true, -- Show GPS route
            routeColor = 5 -- Route line color
        }
    },

    -- ═══════════════════════════════════════════════════════════════════════════
    -- VEHICLE KEYS CONFIG
    -- Integration with vehicle key systems
    -- ═══════════════════════════════════════════════════════════════════════════
    Keys = {
        enable = true, -- Enable vehicle key integration
        GiveKey = function(vehicle, plate) -- Function to give keys to player (edit for your key system)
            -- Default: QBCore vehicle keys
            TriggerEvent("vehiclekeys:client:SetOwner", plate)
            
            -- For other systems, replace with:
            -- ESX: TriggerEvent('esx_vehiclelock:setVehicleOwnedStatus', plate, true)
            -- jaksam: exports['jaksam-vehicles']:GiveKeys(plate)
            -- qs-vehiclekeys: TriggerEvent('qs-vehiclekeys:client:GiveKeys', plate)
        end
    },

    Fuel = {
        enable = true, -- Enable fuel consumption for delivery vehicles
        AddFuel = function(vehicle, fuelLevel) -- Function to add fuel to vehicle (edit for your fuel system)
            -- Example for LegacyFuel system
            exports['LegacyFuel']:SetFuel(vehicle, fuelLevel)
            --SetVehicleFuelLevel(vehicle, fuelLevel)
        end,
    },

    Uniform = {
        enable = true, -- Enable uniform for delivery job
        
        -- Uniform outfits per gender (male/female)
        outfits = {
            male = {
                ['tshirt_1'] = 15,  ['tshirt_2'] = 0,
                ['torso_1'] = 241,  ['torso_2'] = 0,
                ['arms'] = 0,
                ['pants_1'] = 97,  ['pants_2'] = 1,
                ['shoes_1'] = 25,   ['shoes_2'] = 0,
            },
            female = {
                ['tshirt_1'] = 15,  ['tshirt_2'] = 0,
                ['torso_1'] = 249,  ['torso_2'] = 0,
                ['arms'] = 0,
                ['pants_1'] = 101,  ['pants_2'] = 1,
                ['shoes_1'] = 27,   ['shoes_2'] = 0,
            }
        },

        SetUniform = function(outfit)
            if GetResourceState('es_extended') == 'started' then
                TriggerEvent('skinchanger:getSkin', function(skin)
                    for k, v in pairs(outfit) do
                        skin[k] = v
                    end
                    TriggerEvent('skinchanger:loadSkin', skin)
                end)
            elseif GetResourceState('qb-core') == 'started' or GetResourceState('qbx_core') == 'started' then
                TriggerEvent('qb-clothing:client:loadOutfit', {
                    outfitData = {
                        ['t-shirt'] = {item = outfit['tshirt_1'], texture = outfit['tshirt_2']},
                        ['torso2'] = {item = outfit['torso_1'], texture = outfit['torso_2']},
                        ['arms'] = {item = outfit['arms'], texture = 0},
                        ['pants'] = {item = outfit['pants_1'], texture = outfit['pants_2']},
                        ['shoes'] = {item = outfit['shoes_1'], texture = outfit['shoes_2']},
                    }
                })
            elseif GetResourceState('ox_core') == 'started' then
                exports['illenium-appearance']:setPlayerAppearance(outfit)
            end
        end,
        
        SaveClothes = function()
            if GetResourceState('es_extended') == 'started' then
                local skin = nil
                TriggerEvent('skinchanger:getSkin', function(s) skin = s end)
                Wait(100)

                return skin
            elseif GetResourceState('qb-core') == 'started' or GetResourceState('qbx_core') == 'started' then
                local clothes = {}
                for i = 0, 11 do
                    clothes[i] = {
                        drawable = GetPedDrawableVariation(cache.ped, i),
                        texture = GetPedTextureVariation(cache.ped, i)
                    }
                end

                return clothes
            elseif GetResourceState('ox_core') == 'started' then
                local clothes = exports['illenium-appearance']:getPedAppearance(cache.ped) or {}
                return clothes
            end
        end,

        RestoreClothes = function(savedClothes)
            if not savedClothes then return end
            
            if GetResourceState('es_extended') == 'started' then
                TriggerEvent('skinchanger:loadSkin', savedClothes)
            elseif GetResourceState('qb-core') == 'started' or GetResourceState('qbx_core') == 'started' then
                for i = 0, 11 do
                    if savedClothes[i] then
                        SetPedComponentVariation(cache.ped, i, savedClothes[i].drawable, savedClothes[i].texture, 0)
                    end
                end
            elseif GetResourceState('ox_core') == 'started' then
                exports['illenium-appearance']:setPlayerAppearance(savedClothes)
            end
        end,
    }
}

Supported Frameworks


If your framework is not listed below, please contact us on our discord server for help.

Last updated