[ESX] & [QBCORE] Vineyard Job Script

Showcase
VINEYARD JOB SCRIPT

Purchase
[QBCore & ESX] - Vineyard job script

Features

  • Realistic Winemaking Cycle: Simulates the entire process of winemaking, starting with gathering grapes from vineyard rows.
  • Interactive Grape Processing: Players can crush grapes individually or as a team with 1-2 friends for faster processing. Synced effects, animations, and custom grape props enhance realism and immersion.
  • Detailed Wine Production: After crushing, players can blend different types of crushed grapes to create premium wine varieties. Ferment the crushed grapes in barrels with realistic time multipliers, ensuring an authentic experience.
  • Value Addition through Blending: Blending grapes into exclusive wine types increases their value, offering opportunities to maximize profits.
  • Flexible Selling Options: Provides two selling methods—quick sales at the vineyard for efficient but lower earnings, or customer deliveries for higher profits based on distance and order size.
  • Built-in Job Instructions: Includes comprehensive instructions for the vineyard job, guiding players and enhancing the role-play experience.
  • Advanced Delivery System: Features a detailed delivery process with dynamic pricing and bonuses for longer distances, adding depth to the job.
  • Dynamic Pricing: Wine prices change dynamically based on market demand, making the gameplay more engaging and rewarding.
  • Protection Against Exploits: Built-in safeguards ensure fair gameplay and prevent script exploitation.
  • Compatibility: Fully compatible with popular targets like qb-target, ox_target, qtarget, as well as context menus like qb-menu, ox_lib context menu, nh-context, and input menus like qb-input, ox_lib input menu, nh-keyboard.
  • Customizable: Every aspect of the script, including grape gathering rates, processing times, and product prices, can be customized through a config file, allowing for adaptability to various RP scenarios.

INFO

  • This script contains custom map edits. Using any other MLO or map for the vineyard may result in conflicts with the provided map edits included in the script.

Optimization
Full optimized.
0.00 ms

Code is accessible No
Subscription-based No
Lines (approximately) : ~4200
Requirements d3MBA-lib - Support most popular context menus (ox_lib, qb-menu, nh-context) - (ox_inputmenu, qb-input, nh-keyboard) (ox_target, qtarget, qb-target)
Support Yes

Other Resources

Config

Config.DebugPolyZone = false -- [true/false] - If you want to see polyzone borders, set it to 'true' (Config.DebugPolyZone = true) 

Config.KickReason = "[d3MBA-vineyardjob] | Protection!" -- Kick reason if player try to cheat.

-- Config.JobRequired = "JOB_NAME" -- if you want to disable it just set it to 'false' (Config.JobRequired = false) 
Config.JobRequired = false -- Job name e.g. Config.JobRequired = "vineyardjob", make sure you have that job in the your framework(qbcore, esx, qbox...)

-- Particles synchronization   
Config.SyncParticlesWithAllPlayers = { -- If is false particles will see only the player who triggered it. 
    CrushingGrapes = true, -- [true/false] - If you want to sync particles with all players, set it to 'true'
    WashingDirtyBottle = true, -- [true/false] - If you want to sync particles with all players, set it to 'true'
    BlendingGrapes = true, -- [true/false] - If you want to sync particles with all players, set it to 'true'
    FillBottle = true, -- [true/false] - If you want to sync particles with all players, set it to 'true'
}

Config.Blip = { -- Main blip configuration
    -- https://docs.fivem.net/docs/game-references/blips/
    Pos = vector3(-1903.82, 2035.739, 140.73),
    Use = true, 
    Sprite = 93,
    Scale = 1.0,
    Color = 27,
    Label = "Vineyard", 
}

Config.Instructions = { 
    
    SetPedInvisable = true, -- [true/false] - If you want to set ped invisable during instructions set it to 'true' (Config.Instructions.SetPedInvisable = true)
    SetRentPedBlockingOtherEvents = true, -- [true/false] - If you want to set rent ped blocking other events, set it to 'true' (Config.Instructions.SetRentPedBlockingOtherEvents = true) 

    -- Link for ped models - https://docs.fivem.net/docs/game-references/ped-models/
    Peds = {
        {Model = "s_m_m_trucker_01", Pos = vector4(-1928.84, 2059.442, 139.83, 348.26)},
        {Model = "s_m_m_cntrybar_01", Pos = vector4(-1911.59, 2074.193, 139.38, 142.39)},
        {Model = "s_m_m_cntrybar_01", Pos = vector4(-1886.01, 2074.273, 139.99, 341.52)},
        {Model = "s_m_m_cntrybar_01", Pos = vector4(-1874.30, 2069.959, 140.00, 342.05)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1869.40, 2089.166, 139.46, 329.03)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1838.85, 2162.094, 113.43, 201.28)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1860.54, 2053.807, 140.01, 183.6)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1910.19, 2092.886, 135.01, 18.15)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1909.30, 2082.602, 139.38, 46.88)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1738.41, 1973.130, 119.64, 64.1)},
        {Model = "s_m_m_migrant_01", Pos = vector4(-1904.59, 1967.885, 146.24, 310.16)},
    },
}

Config.ChangeClothes = { 
    Use = true, -- [true/false] - If you want to use change clothes, set it to 'true' (Config.ChangeClothes.Use = true)
    CheckBeforeAnyInteraction = false,  -- [true/false] - If you want to check before any interaction, if player wearing job uniform set it to 'true' (Config.ChangeClothes.CheckBeforeAnyInteraction = true)

    AddScissorsItem = true, -- [true/false] - If you want to add scissors item when player change clothes, set it to 'true' (Config.ChangeClothes.AddScissorsItem = true)

    -- This thing you should adjust to your server. Numbers for clothes you should find in your clothing shop!
    Uniforms = {
        Male = { 
            -- AJUST THIS FOR YOUR SERVER
            tshirt_1 = 15,  tshirt_2 = 0,
            torso_1 = 43,   torso_2 = 0,
            decals_1 = 0,   decals_2 = 0,
            arms = 70,
            pants_1 = 9,   pants_2 = 6,
            shoes_1 = 25,   shoes_2 = 0,
            chain_1 = 0,    chain_2 = 0,
            hat_1 = 20,     hat_2 = 0,
            ears_1 = -1,    ears_2 = 0,
            bproof_1 = 0,   bproof_2 = 0
        },  

        Female = {
            -- AJUST THIS FOR YOUR SERVER
            tshirt_1 = 23,  tshirt_2 = 0,
            torso_1 = 22,   torso_2 = 0,
            decals_1 = 0,   decals_2 = 0,
            arms = 85,
            pants_1 = 36,   pants_2 = 0,
            shoes_1 = 12,   shoes_2 = 3,
            chain_1 = 0,    chain_2 = 0,
            hat_1 = -1,     hat_2 = 0,
            ears_1 = -1,    ears_2 = 0,
            bproof_1 = 0,   bproof_2 = 0
        }
    },
}

Config.JobVehicle = {
    Ped = {
        Model = "s_m_m_cntrybar_01", -- https://docs.fivem.net/docs/game-references/ped-models/
        Pos = vector4(-1925.59, 2048.150, 139.83, 253.98),
    },

    
    Model = "kalahari", 

    JobVehicleBlip = {
        Sprite = 477,
        Color = 2,
        Scale = 0.9,
        Label = "[Vineyard] - Job vehicle",
    },

    ReturnVehilclePos = vector3(-1890.11, 2045.751, 140.87), -- return vehicle 

    ReturnVehicleMarker = { 
        Use = true, -- [true/false] - If you want to use marker, set it to 'true'
        Distance = 40.0, -- [float] - Distance to see marker 
        Type = 1, -- https://docs.fivem.net/docs/game-references/markers/
        Size = vector3(8.0, 8.0, 1.0),
        Color = {r = 255, g = 255, b = 255, a = 150},
    },

    ReturnVehicleBlip = {
        Sprite = 477,
        Color = 1,
        Scale = 0.9,
        Label = "[Vineyard] - Return vehicle",
    },
    

    SpawnPos = vector4(-1902.63, 2056.853, 140.23, 157.84), -- vehicle spawn position

    SetPedIntoVehicle = true, -- if true, ped will be set into vehicle after spawning. 
    FullFuel = true, -- if true, vehicle will be spawned with full fuel. 

    Deposit = {
        Use = true, -- [true/false] - If you want to use deposit system, set it to 'true'
        Account = "cash", -- ("cash", "bank")
        Amount = 500, -- amount of money to deposit
    },

    SpawnPointCheck = { -- It will check spawn point. To prevent spawning vehicle on vehicle. 
        Use = true,
        Radius = 3.5 -- Default radius 3.5
    }, 
}

Config.WashingDirtyBottle = {
    Time = 5,

}

Config.LabelingBottle = {
    Time = 5,

}

Config.GatheringGrapes = {
    RequredItem = {
        Use = false, -- [true/false] - If you want to use required item, set it to 'true' 
        ItemName = Config.Items.Scissors, -- Item name required to gather grapes  
    }, 
    
    ResetTime = 120, -- Time to reset grapes (in MINUTES)
    
    GrapesPerUnitLength = 0.2, -- Adjust this value as needed to get the desired number of grapes PER ROW LENGTH (1.0 = 1 grape per meter)

    GatheringWithScissorsTime = 5, -- Time to gather grapes with scissors (in seconds)
    GatheringTimeWithHand = 10, -- Time to gather grapes with hands (in seconds)

    Amount = {
        Min = 1, -- [Min] - Minimum amount of grapes that player can will receive 
        Max = 3, -- [Max] - Maximum amount of grapes that player can will receive
        -- SET MIN, MAX AMOUNT IF YOU DON'T WANT TO USE RANDOM AMOUNT
    }
}

Config.CrushingGrapes = {
    Time = 20, -- Time to crush grapes (in seconds)

    GrapesAmount = 25, -- Amount needed to start crushing grapes
}

Config.TeamCrushingGrapes = {
    Time = {
        --[NUMBER OF PLAYERS] = TIME TO CRUSH GRAPES (IN SECONDS)
        [2] = 35, -- Time to crush grapes (in seconds) for 2 players 
        [3] = 25, -- Time to crush grapes (in seconds) for 3 players
    },

    GrapesAmount = {
        StartCrushingAmount = 250, -- Amount needed to start team crushing grapes  
        CrushedGrapesAmount = 5, -- Amount of crushed grapes that player will receive after crushing
    }, 
}

Config.BlendGrapes = {
    Time = 15, -- Time to blend grapes (in seconds)

    BlendingMenu = {
        -- Bordeaux Blend
        [Config.Items.BordeauxBlendGrape] = {
            [Config.Items.CrushedCabernetSauvignonGrape] = 1,
            [Config.Items.CrushedMerlotGrape] = 1,
        },
                
        -- Rhône Blend
        [Config.Items.RhoneBlendGrape] = {
            [Config.Items.CrushedGarnachaGrape] = 1,
            [Config.Items.CrushedSyrahGrape] = 1,
        },

        -- White Blend
        [Config.Items.WhiteBlendGrape] = {
            [Config.Items.CrushedChardonnayGrape] = 1,
            [Config.Items.CrushedSauvignonBlancGrape] = 1,
        },

        -- Sparkling Blend
        [Config.Items.SparklingBlendGrape] = {
            [Config.Items.CrushedPinotNoirGrape] = 1,
            [Config.Items.CrushedChardonnayGrape] = 1,
        },

    }
}

Config.BarrelFermentation = {
    Time = {
        Min = 120, -- [Min] - Minimum time to ferment wine (in seconds)
        Max = 360, -- [Max] - Maximum time to ferment wine (in seconds)
    }, 

    AmountMultiplier = { -- Amount multiplier for each level of fermentation depends on amount of crushed grpaes in the barrel, 
        -- e.g. if there is 2 boxes of crushed grapes in the barrel, 1.2x multiplier will be applied to the amount of wine produced.
        
        --[number of crushed grapes in the barrel] = time multiplier
        [1] = 1.0, 
        [2] = 1.2, 
        [3] = 1.5, 
        [4] = 1.8, 
        [5] = 2.0,
    },
    
    BarrelCapacity = 5, -- Amount of crushed grapes boxes that can be stored (for fermentation) in the barrel
    BottlesPerBox = 5, -- Amount of bottles per box of (blend/crushed grapes) 

    FillingBottleTime = 10, -- Time to fill bottle (in seconds)
}

Config.InstantSell = { 
    Use = true, -- [true/false] - If you want to use instant sell system, set it to 'true' 

    Ped = {
        Model = "ig_sacha", -- https://docs.fivem.net/docs/game-references/ped-models/
        Pos = vector4(-1928.20, 1779.578, 172.09, 113.48),
    }, 

    Blip = { -- https://docs.fivem.net/docs/game-references/blips/
        Use = true, -- [true/false] - If you want to use blip, set it to 'true'
        Sprite = 514,
        Color = 5,
        Scale = 0.9,
        Label = "[Vineyard] - Sell point",
    },

    DynamicPrices = {
        Use = true, -- [true/false] - If you want to use dynamic prices, set it to 'true'
        PriceMinMultiplier = 0.7, -- Min price multiplier, 70% of original price
        PriceMaxMultiplier = 1.5, -- Max price multiplier, 150% of original price
    }, 

    Account = "cash", -- ("cash", "bank")

    SellMenu = {
        {ItemName = Config.Items.GarnachaWineBottle, Price = 30},
        {ItemName = Config.Items.SauvignonBlancWineBottle, Price = 50},
        {ItemName = Config.Items.MerlotWineBottle, Price = 55},
        {ItemName = Config.Items.SyrahWineBottle, Price = 65},
        {ItemName = Config.Items.CabernetSauvignonWineBottle, Price = 70},
        {ItemName = Config.Items.ChardonnayWineBottle, Price = 80},
        {ItemName = Config.Items.PinotNoirWineBottle, Price = 85},
        -- Luxury wine bottles (blended)
        {ItemName = Config.Items.BordeauxBlendWineBottle, Price = 120},
        {ItemName = Config.Items.RhoneBlendWineBottle, Price = 150},
        {ItemName = Config.Items.WhiteBlendWineBottle, Price = 170},
        {ItemName = Config.Items.SparklingBlendWineBottle, Price = 200},
        -- You can add more items here.
    }
}

Config.Delivery = {
    AutoDeleteOrder = 5, -- Time to auto delete order if is it unpackaged or not delivered (IN DAYS)

    PedModels = {
        "s_m_m_migrant_01", -- https://docs.fivem.net/docs/game-references/ped-models/
    },

    Account = "cash", -- ("cash", "bank")

    WaitingTime = { -- Time to find order (in seconds)  
        Min = 30, -- [Min] - Minimum time to find order (delivery point) (in seconds)
        Max = 60, -- [Max] - Maximum time to find order (delivery point) (in seconds)
    },

    Blip = {
        Use = true, -- [true/false] - If you want to use blip, set it to 'true'
        Sprite = 514,
        Color = 47,
        Scale = 1.0,
        Label = "[Vineyard job] - Delivery point",
    },

    DeliveryPoints = { -- Delivery points
        vector4(-1823.27, 786.6229, 138.21, 218.45),
        vector4(-3037.64, 588.1326, 7.8140, 289.99),
        vector4(-3239.41, 1002.942, 12.485, 295.05),
        vector4(1732.510, 6410.205, 35.000, 142.04),
        vector4(1697.607, 4928.052, 42.230, 43.75),
        vector4(542.9543, 2673.037, 42.159, 323.56),
        vector4(2681.972, 3279.717, 55.240, 240.87),
        vector4(-1681.29, -290.961, 51.883, 239.38),
        vector4(-324.328, 2818.092, 59.449, 58.08),
        vector4(456.1636, -2059.39, 23.912, 277.31),
        vector4(-54.4112, -1756.01, 29.439, 168.13),
        vector4(53.14377, -1478.75, 29.287, 183.77),
        vector4(-1225.69, -901.540, 12.320, 47.41),
        vector4(-1492.06, -383.027, 40.169, 139.77),
        vector4(-2974.12, 389.6747, 15.031, 84.01),
        vector4(1165.268, 2703.067, 38.180, 182.81),
        vector4(1392.693, 3598.891, 35.025, 199.73),
        -- You can add more delivery points here.
    }, 

    NumberOfItems = { -- Number of items per order 
        Small = {
            Min = 2, -- [Min] - Minimum number of items per order
            Max = 4, -- [Max] - Maximum number of items per order
        },
        Large = { -- NUMBER IS LOWER THAN SMALL BECAUSE OF PRICE OF AMOUNT NEEDED FOR EACH ITEM
            Min = 4, -- [Min] - Minimum number of items per order
            Max = 6, -- [Max] - Maximum number of items per order
        },
    },

    DynamicPrices = {
        Use = true, -- [true/false] - If you want to use dynamic prices, set it to 'true'
        PriceMinMultiplier = 0.8, -- Min price multiplier, 80% of original price
        PriceMaxMultiplier = 1.7, -- Max price multiplier, 170% of original price
    }, 

    DistanceMultiplier = {
        Use = true, -- [true/false] - If you want to use distance multiplier, set it to 'true'
        PriceMultiplier = 0.1, -- Adittional earn for each meter - (distance between company and delivery point) default 0.1$ per meter
    },

    PackagingTime = 15, -- Time to pack items (in seconds) 

    DeliveryItems = { -- Items that can be delivered
        Small = { 
            {ItemName = Config.Items.GarnachaWineBottle, Price = 55, Amount = {Min = 6, Max = 18}},
            {ItemName = Config.Items.SauvignonBlancWineBottle, Price = 70, Amount = {Min = 1, Max = 2}},
            {ItemName = Config.Items.MerlotWineBottle, Price = 85, Amount = {Min = 1, Max = 4}},
            {ItemName = Config.Items.SyrahWineBottle, Price = 100, Amount = {Min = 1, Max = 3}},
            {ItemName = Config.Items.CabernetSauvignonWineBottle, Price = 120, Amount = {Min = 2, Max = 3}},
            {ItemName = Config.Items.ChardonnayWineBottle, Price = 135, Amount = {Min = 1, Max = 6}},
            {ItemName = Config.Items.PinotNoirWineBottle, Price = 140, Amount = {Min = 1, Max = 2}},
            -- Blended wine bottles
            {ItemName = Config.Items.BordeauxBlendWineBottle, Price = 160, Amount = {Min = 1, Max = 1}},
            {ItemName = Config.Items.RhoneBlendWineBottle, Price = 165, Amount = {Min = 1, Max = 1}},
            {ItemName = Config.Items.WhiteBlendWineBottle, Price = 175, Amount = {Min = 1, Max = 1}},
            {ItemName = Config.Items.SparklingBlendWineBottle, Price = 185, Amount = {Min = 1, Max = 1}},
        },
        Large = { -- LARGE ORDER
            {ItemName = Config.Items.GarnachaWineBottle, Price = 55, Amount = {Min = 6, Max = 18}},
            {ItemName = Config.Items.SauvignonBlancWineBottle, Price = 70, Amount = {Min = 1, Max = 2}},
            {ItemName = Config.Items.MerlotWineBottle, Price = 85, Amount = {Min = 1, Max = 3}},
            {ItemName = Config.Items.SyrahWineBottle, Price = 100, Amount = {Min = 1, Max = 3}},
            {ItemName = Config.Items.CabernetSauvignonWineBottle, Price = 120, Amount = {Min = 1, Max = 3}},
            {ItemName = Config.Items.ChardonnayWineBottle, Price = 135, Amount = {Min = 1, Max = 3}},
            {ItemName = Config.Items.PinotNoirWineBottle, Price = 140, Amount = {Min = 1, Max = 3}},
            -- Blended wine bottles
            {ItemName = Config.Items.BordeauxBlendWineBottle, Price = 160, Amount = {Min = 1, Max = 2}},
            {ItemName = Config.Items.RhoneBlendWineBottle, Price = 165, Amount = {Min = 1, Max = 2}},
            {ItemName = Config.Items.WhiteBlendWineBottle, Price = 175, Amount = {Min = 1, Max = 2}},
            {ItemName = Config.Items.SparklingBlendWineBottle, Price = 185, Amount = {Min = 1, Max = 2}},
        },
    }
}

function DrawText3D(coords, text, scale2)
    local camCoords = GetGameplayCamCoord()
    local dist = #(coords - camCoords)
    
    -- Experimental math to scale the text down
    local scale = 200 / (GetGameplayCamFov() * dist)

    -- Format the text
    SetTextScale(0.0, scale2 * scale)
    SetTextFont(1)
    SetTextDropshadow(0, 0, 0, 0, 255)
    SetTextEdge(2, 0, 0, 0, 150)
    SetTextOutline()
    SetTextDropShadow()
    SetTextCentre(true)

    -- Diplay the text
    BeginTextCommandDisplayText("STRING")
    AddTextComponentSubstringPlayerName(text)
    SetDrawOrigin(coords, 0)
    EndTextCommandDisplayText(0.0, 0.0)
    ClearDrawOrigin()
end



Items

Config.Items = {
    Scissors = "vineyard_scissors",
    Crate = "vineyard_crate",
    DirtyBottle = "vineyard_dirty_bottle",
    Bottle = "vineyard_bottle",
    LabeledBottle = "vineyard_labeled_bottle",
    
    -- Gather grapes
    GarnachaGrape = "vineyard_garnacha_grape",
    SauvignonBlancGrape = "vineyard_sauvignon_blanc_grape",
    MerlotGrape = "vineyard_merlot_grape",
    SyrahGrape = "vineyard_syrah_grape",
    CabernetSauvignonGrape = "vineyard_cabernet_sauvignon_grape",
    ChardonnayGrape = "vineyard_chardonnay_grape",
    PinotNoirGrape = "vineyard_pinot_noir_grape",

    -- Crushed grapes 
    CrushedGarnachaGrape = "vineyard_crushed_garnacha_grape",
    CrushedSauvignonBlancGrape = "vineyard_crushed_sauvignon_blanc_grape",
    CrushedMerlotGrape = "vineyard_crushed_merlot_grape",
    CrushedSyrahGrape = "vineyard_crushed_syrah_grape",
    CrushedCabernetSauvignonGrape = "vineyard_crushed_cabernet_sauvignon_grape",
    CrushedChardonnayGrape = "vineyard_crushed_chardonnay_grape",
    CrushedPinotNoirGrape = "vineyard_crushed_pinot_noir_grape",

    -- Blend grapes
    BordeauxBlendGrape = "vineyard_bordeaux_blend_grape",
    RhoneBlendGrape = "vineyard_rhone_blend_grape",
    WhiteBlendGrape = "vineyard_white_blend_grape",
    SparklingBlendGrape = "vineyard_sparkling_blend_grape",
    
    -- Wine bottles
    GarnachaWineBottle = "vineyard_garnacha_wine_bottle",
    SauvignonBlancWineBottle = "vineyard_sauvignon_blanc_wine_bottle",
    MerlotWineBottle = "vineyard_merlot_wine_bottle",
    SyrahWineBottle = "vineyard_syrah_wine_bottle",
    CabernetSauvignonWineBottle = "vineyard_cabernet_sauvignon_wine_bottle",
    ChardonnayWineBottle = "vineyard_chardonnay_wine_bottle",
    PinotNoirWineBottle = "vineyard_pinot_noir_wine_bottle",

    -- Blended wine bottles
    BordeauxBlendWineBottle = "vineyard_bordeaux_blend_wine_bottle",
    RhoneBlendWineBottle = "vineyard_rhone_blend_wine_bottle",
    WhiteBlendWineBottle = "vineyard_white_blend_wine_bottle",
    SparklingBlendWineBottle = "vineyard_sparkling_blend_wine_bottle",

    -- Order
    Order = "vineyard_order",
}