├── config.lua ├── fxmanifest.lua ├── bridge ├── client │ ├── qbx.lua │ ├── esx.lua │ ├── nd.lua │ └── qb.lua └── server │ ├── qbx.lua │ ├── nd.lua │ ├── esx.lua │ └── qb.lua ├── README.md ├── sv_config.lua ├── sv_pizzajob.lua └── cl_pizzajob.lua /config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | BossModel = `u_m_y_party_01`, 3 | BossCoords = vec4(538.35, 101.8, 95.54, 164.05), -- The Blip also uses these coords. 4 | FuelScript = { 5 | enable = false, 6 | script = 'LegacyFuel', 7 | }, 8 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | 4 | author 'Randolio' 5 | description 'Reworked Pizza Job for ESX/QB.' 6 | 7 | shared_scripts { 8 | 'config.lua', 9 | '@ox_lib/init.lua', 10 | } 11 | 12 | server_scripts { 13 | 'bridge/server/**.lua', 14 | 'sv_config.lua', 15 | 'sv_pizzajob.lua' 16 | } 17 | 18 | client_scripts { 19 | 'bridge/client/**.lua', 20 | 'cl_pizzajob.lua' 21 | } 22 | 23 | lua54 'yes' 24 | -------------------------------------------------------------------------------- /bridge/client/qbx.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qbx_core') ~= 'started' then return end 2 | 3 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() 4 | OnPlayerLoaded() 5 | end) 6 | 7 | RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() 8 | OnPlayerUnload() 9 | end) 10 | 11 | function handleVehicleKeys(veh) 12 | TriggerEvent('vehiclekeys:client:SetOwner', GetVehicleNumberPlateText(veh)) 13 | end 14 | 15 | function hasPlyLoaded() 16 | return LocalPlayer.state.isLoggedIn 17 | end 18 | 19 | function DoNotification(text, nType) 20 | exports.qbx_core:Notify(text, nType) 21 | end 22 | -------------------------------------------------------------------------------- /bridge/server/qbx.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qbx_core') ~= 'started' then return end 2 | 3 | 4 | function GetPlayer(id) 5 | return exports.qbx_core:GetPlayer(id) 6 | end 7 | 8 | function DoNotification(src, text, nType) 9 | exports.qbx_core:Notify(src, text, nType) 10 | end 11 | 12 | function AddMoney(Player, moneyType, amount) 13 | Player.Functions.AddMoney(moneyType, amount, "cargo-delivery") 14 | end 15 | 16 | function handleExploit(id, reason) 17 | exports.qbx_core:ExploitBan(id, reason) 18 | end 19 | 20 | RegisterNetEvent('QBCore:Server:OnPlayerUnload', function(source) 21 | ServerOnLogout(source) 22 | end) -------------------------------------------------------------------------------- /bridge/client/esx.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | local ESX = exports['es_extended']:getSharedObject() 4 | 5 | RegisterNetEvent('esx:playerLoaded', function(xPlayer) 6 | ESX.PlayerLoaded = true 7 | OnPlayerLoaded() 8 | end) 9 | 10 | RegisterNetEvent('esx:onPlayerLogout', function() 11 | ESX.PlayerLoaded = false 12 | OnPlayerUnload() 13 | end) 14 | 15 | function handleVehicleKeys(veh) 16 | -- not sure if ESX use a keys system?? 17 | end 18 | 19 | function hasPlyLoaded() 20 | return ESX.PlayerLoaded 21 | end 22 | 23 | function DoNotification(text, nType) 24 | ESX.ShowNotification(text, nType) 25 | end 26 | -------------------------------------------------------------------------------- /bridge/client/nd.lua: -------------------------------------------------------------------------------- 1 | if not lib.checkDependency('ND_Core', '2.0.0') then return end 2 | 3 | NDCore = {} 4 | 5 | lib.load('@ND_Core.init') 6 | 7 | RegisterNetEvent('ND:characterUnloaded', function() 8 | LocalPlayer.state.isLoggedIn = false 9 | OnPlayerUnload() 10 | end) 11 | 12 | RegisterNetEvent('ND:characterLoaded', function(character) 13 | LocalPlayer.state.isLoggedIn = true 14 | OnPlayerLoaded() 15 | end) 16 | 17 | function hasPlyLoaded() 18 | return LocalPlayer.state.isLoggedIn 19 | end 20 | 21 | function DoNotification(text, nType) 22 | lib.notify({ title = "Notification", description = text, type = nType, }) 23 | end 24 | 25 | function handleVehicleKeys(veh) 26 | -- ? 27 | end -------------------------------------------------------------------------------- /bridge/client/qb.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' or GetResourceState('qbx_core') == 'started' then return end 2 | 3 | local QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() 6 | OnPlayerLoaded() 7 | end) 8 | 9 | RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() 10 | OnPlayerUnload() 11 | end) 12 | 13 | function handleVehicleKeys(veh) 14 | TriggerEvent('vehiclekeys:client:SetOwner', GetVehicleNumberPlateText(veh)) 15 | end 16 | 17 | function hasPlyLoaded() 18 | return LocalPlayer.state.isLoggedIn 19 | end 20 | 21 | function DoNotification(text, nType) 22 | QBCore.Functions.Notify(text, nType) 23 | end 24 | -------------------------------------------------------------------------------- /bridge/server/nd.lua: -------------------------------------------------------------------------------- 1 | if not lib.checkDependency('ND_Core', '2.0.0') then return end 2 | 3 | NDCore = {} 4 | 5 | lib.load('@ND_Core.init') 6 | 7 | function GetPlayer(id) 8 | return NDCore.getPlayer(id) 9 | end 10 | 11 | function DoNotification(src, text, nType) 12 | TriggerClientEvent('ox_lib:notify', src, { type = nType, description = text }) 13 | end 14 | 15 | function AddMoney(Player, moneyType, amount) 16 | Player.addMoney(moneyType, amount) 17 | end 18 | 19 | function handleExploit(id, reason) 20 | DropPlayer(id, 'You were dropped from the server.') 21 | print(('[^3WARNING^7] Player: ^5%s^7 Attempted to exploit randol_pizzajob!'):format(id)) 22 | end 23 | 24 | AddEventHandler("ND:characterUnloaded", function(src, character) 25 | ServerOnLogout(src) 26 | end) 27 | -------------------------------------------------------------------------------- /bridge/server/esx.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | local ESX = exports['es_extended']:getSharedObject() 4 | 5 | function GetPlayer(id) 6 | return ESX.GetPlayerFromId(id) 7 | end 8 | 9 | function DoNotification(src, text, nType) 10 | TriggerClientEvent('esx:showNotification', src, text, nType) 11 | end 12 | 13 | function AddMoney(xPlayer, moneyType, amount) 14 | local account = moneyType == 'cash' and 'money' or moneyType 15 | xPlayer.addAccountMoney(account, amount, "pizza-job") 16 | end 17 | 18 | function handleExploit(id, reason) 19 | DropPlayer(id, 'You were dropped from the server.') 20 | print(('[^3WARNING^7] Player: ^5%s^7 Attempted to exploit randol_pizzajob!'):format(id)) 21 | end 22 | 23 | AddEventHandler('esx:playerLogout', function(playerId) 24 | ServerOnLogout(playerId) 25 | end) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Randolio: Pizza Delivery Job 2 | 3 | **ESX/QB/ND supported with bridge.** 4 | 5 | Requirements: https://github.com/overextended/ox_lib/releases 6 | 7 | Preview: https://streamable.com/j4mgv7 8 | 9 | **Changes** - Last updated: 26/01/2024 10 | 11 | * Added support for both ESX/QB/ND frameworks. 12 | * Utilized ox lib throughout. 13 | * Configs are now split into client and server configs. (config.lua and sv_config.lua) 14 | * When starting work, you are generated a set number of locations to deliver pizzas to (10 by default) and are paid per delivery. Once completed all 10, you must return the vehicle. 15 | * No more duplicate locations. 16 | * Vehicle is now created server side and the whole script was rewritten to secure any exploits. 17 | 18 | **You have permission to use this in your server and edit for your personal needs but are not allowed to redistribute.** 19 | -------------------------------------------------------------------------------- /bridge/server/qb.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' or GetResourceState('qbx_core') == 'started' then return end 2 | 3 | local QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | function GetPlayer(id) 6 | return QBCore.Functions.GetPlayer(id) 7 | end 8 | 9 | function DoNotification(src, text, nType) 10 | TriggerClientEvent('QBCore:Notify', src, text, nType) 11 | end 12 | 13 | function AddMoney(Player, moneyType, amount) 14 | Player.Functions.AddMoney(moneyType, amount, "cargo-delivery") 15 | end 16 | 17 | function handleExploit(id, reason) 18 | MySQL.insert('INSERT INTO bans (name, license, discord, ip, reason, expire, bannedby) VALUES (?, ?, ?, ?, ?, ?, ?)', { 19 | GetPlayerName(id), 20 | QBCore.Functions.GetIdentifier(id, 'license'), 21 | QBCore.Functions.GetIdentifier(id, 'discord'), 22 | QBCore.Functions.GetIdentifier(id, 'ip'), 23 | reason, 24 | 2147483647, 25 | 'randol_pizzajob' 26 | }) 27 | DropPlayer(id, 'You were banned from the server for exploiting.') 28 | end 29 | 30 | RegisterNetEvent('QBCore:Server:OnPlayerUnload', function(source) 31 | ServerOnLogout(source) 32 | end) -------------------------------------------------------------------------------- /sv_config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | Deliveries = 10, -- 3 | Account = 'cash', 4 | Payout = {min = 105, max = 135}, 5 | Locations = { -- Random delivery houses. 6 | vec3(224.11, 513.52, 140.92), 7 | vec3(57.51, 449.71, 147.03), 8 | vec3(-297.81, 379.83, 112.1), 9 | vec3(-595.78, 393.0, 101.88), 10 | vec3(-842.68, 466.85, 87.6), 11 | vec3(-1367.36, 610.73, 133.88), 12 | vec3(944.44, -463.19, 61.55), 13 | vec3(970.42, -502.5, 62.14), 14 | vec3(1099.5, -438.65, 67.79), 15 | vec3(1229.6, -725.41, 60.96), 16 | vec3(288.05, -1094.98, 29.42), 17 | vec3(-32.35, -1446.46, 31.89), 18 | vec3(-34.29, -1847.21, 26.19), 19 | vec3(130.59, -1853.27, 25.23), 20 | vec3(192.2, -1883.3, 25.06), 21 | vec3(348.64, -1820.87, 28.89), 22 | vec3(427.28, -1842.14, 28.46), 23 | vec3(291.48, -1980.15, 21.6), 24 | vec3(279.87, -2043.67, 19.77), 25 | vec3(1297.25, -1618.04, 54.58), 26 | vec3(1381.98, -1544.75, 57.11), 27 | vec3(1245.4, -1626.85, 53.28), 28 | vec3(315.09, -128.31, 69.98), 29 | }, 30 | Vehicle = `surge`, 31 | VehicleSpawn = vec4(535.3, 95.58, 96.32, 159.15), 32 | } -------------------------------------------------------------------------------- /sv_pizzajob.lua: -------------------------------------------------------------------------------- 1 | local Server = lib.require('sv_config') 2 | local players = {} 3 | 4 | local function createPizzaVehicle(source) 5 | local veh = CreateVehicle(Server.Vehicle, Server.VehicleSpawn.x, Server.VehicleSpawn.y, Server.VehicleSpawn.z, Server.VehicleSpawn.w, true, true) 6 | local ped = GetPlayerPed(source) 7 | 8 | while not DoesEntityExist(veh) do Wait(0) end 9 | 10 | while GetVehiclePedIsIn(ped, false) ~= veh do 11 | TaskWarpPedIntoVehicle(ped, veh, -1) 12 | Wait(0) 13 | end 14 | 15 | return NetworkGetNetworkIdFromEntity(veh) 16 | end 17 | 18 | lib.callback.register('randol_pizzajob:server:spawnVehicle', function(source) 19 | if players[source] then return false end 20 | 21 | local src = source 22 | local netid = createPizzaVehicle(src) 23 | 24 | local generatedLocs = {} 25 | local addedLocs = {} 26 | 27 | while #generatedLocs < Server.Deliveries do 28 | local index = math.random(#Server.Locations) 29 | 30 | if not addedLocs[index] then 31 | local randomLoc = Server.Locations[index] 32 | generatedLocs[#generatedLocs + 1] = randomLoc 33 | addedLocs[index] = true 34 | end 35 | end 36 | 37 | 38 | local currentLocIndex = math.random(#generatedLocs) 39 | local currentLoc = generatedLocs[currentLocIndex] 40 | table.remove(generatedLocs, currentLocIndex) 41 | 42 | local payout = math.random(Server.Payout.min, Server.Payout.max) 43 | 44 | players[src] = { 45 | entity = NetworkGetEntityFromNetworkId(netid), 46 | locations = generatedLocs, 47 | payment = payout, 48 | current = currentLoc, 49 | } 50 | 51 | return netid, players[src] 52 | end) 53 | 54 | lib.callback.register('randol_pizzajob:server:clockOut', function(source) 55 | local src = source 56 | if players[src] then 57 | local ent = players[src].entity 58 | if DoesEntityExist(ent) then 59 | DeleteEntity(ent) 60 | end 61 | players[src] = nil 62 | return true 63 | end 64 | return false 65 | end) 66 | 67 | lib.callback.register('randol_pizzajob:server:Payment', function(source) 68 | local src = source 69 | local Player = GetPlayer(src) 70 | local pos = GetEntityCoords(GetPlayerPed(src)) 71 | 72 | if not players[src] or #(pos - players[src].current) > 5.0 then 73 | handleExploit(src, 'Exploiting Pizza Job.') 74 | return false 75 | end 76 | 77 | AddMoney(Player, Server.Account, players[src].payment) 78 | 79 | if #players[src].locations == 0 then 80 | DoNotification(src, ('You received $%s. No more deliveries left, return the vehicle.'):format(players[src].payment)) 81 | return true 82 | end 83 | 84 | DoNotification(src, ('You received $%s. Deliveries left: %s'):format(players[src].payment, #players[src].locations)) 85 | local index = math.random(#players[src].locations) 86 | local newLoc = players[src].locations[index] 87 | local payout = math.random(Server.Payout.min, Server.Payout.max) 88 | table.remove(players[src].locations, index) 89 | 90 | players[src].current = newLoc 91 | players[src].payment = payout 92 | 93 | return true, players[src] 94 | end) 95 | 96 | AddEventHandler("playerDropped", function() 97 | local src = source 98 | if players[src] then 99 | local ent = players[src].entity 100 | if DoesEntityExist(ent) then 101 | DeleteEntity(ent) 102 | end 103 | players[src] = nil 104 | end 105 | end) 106 | 107 | function ServerOnLogout(source) 108 | if players[source] then 109 | local ent = players[src].entity 110 | if DoesEntityExist(ent) then 111 | DeleteEntity(ent) 112 | end 113 | players[source] = nil 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /cl_pizzajob.lua: -------------------------------------------------------------------------------- 1 | local Config = lib.require('config') 2 | local isHired, holdingPizza, pizzaDelivered, activeOrder = false, false, false, false 3 | local pizzaProp, pizzaBoss, startZone, pizzaCar, currZone 4 | local oxtarget = GetResourceState('ox_target') == 'started' 5 | 6 | local pizzajobBlip = AddBlipForCoord(vec3(Config.BossCoords.x, Config.BossCoords.y, Config.BossCoords.z)) 7 | SetBlipSprite(pizzajobBlip, 267) 8 | SetBlipAsShortRange(pizzajobBlip, true) 9 | SetBlipScale(pizzajobBlip, 0.6) 10 | SetBlipColour(pizzajobBlip, 2) 11 | BeginTextCommandSetBlipName('STRING') 12 | AddTextComponentString('Pizza Job') 13 | EndTextCommandSetBlipName(pizzajobBlip) 14 | 15 | local function doEmote(bool) 16 | if bool then 17 | local model = `prop_pizza_box_02` 18 | lib.requestModel(model) 19 | local coords = GetEntityCoords(cache.ped) 20 | pizzaProp = CreateObject(model, coords.x, coords.y, coords.z, true, true, true) 21 | AttachEntityToEntity(pizzaProp, cache.ped, GetPedBoneIndex(cache.ped, 28422), 0.0100,-0.1000, -0.1590, 20.0000007, 0.0, 0.0, true, true, false, true, 0, true) 22 | lib.requestAnimDict('anim@heists@box_carry@') 23 | TaskPlayAnim(cache.ped, 'anim@heists@box_carry@', 'idle', 5.0, 5.0, -1, 51, 0, 0, 0, 0) 24 | SetModelAsNoLongerNeeded(model) 25 | CreateThread(function() 26 | while DoesEntityExist(pizzaProp) do 27 | if not IsEntityPlayingAnim(cache.ped, 'anim@heists@box_carry@', 'idle', 3) then 28 | TaskPlayAnim(cache.ped, 'anim@heists@box_carry@', 'idle', 5.0, 5.0, -1, 51, 0, 0, 0, 0) 29 | end 30 | Wait(1000) 31 | end 32 | RemoveAnimDict('anim@heists@box_carry@') 33 | end) 34 | else 35 | if DoesEntityExist(pizzaProp) then 36 | DetachEntity(cache.ped, true, false) 37 | DeleteEntity(pizzaProp) 38 | pizzaProp = nil 39 | ClearPedTasksImmediately(cache.ped) 40 | end 41 | end 42 | holdingPizza = bool 43 | end 44 | 45 | local function resetJob() 46 | if oxtarget then 47 | exports.ox_target:removeZone(currZone) 48 | else 49 | exports['qb-target']:RemoveZone(currZone) 50 | end 51 | currZone = nil 52 | RemoveBlip(JobBlip) 53 | isHired = false 54 | holdingPizza = false 55 | pizzaDelivered = false 56 | activeOrder = false 57 | if DoesEntityExist(pizzaBoss) then 58 | if oxtarget then 59 | exports.ox_target:removeLocalEntity(pizzaBoss, {'Start Work', 'Finish Work'}) 60 | else 61 | exports['qb-target']:RemoveTargetEntity(pizzaBoss, {'Start Work', 'Finish Work'}) 62 | end 63 | DeleteEntity(pizzaBoss) 64 | pizzaBoss = nil 65 | end 66 | if startZone then startZone:remove() startZone = nil end 67 | end 68 | 69 | local function TakePizza() 70 | if IsPedInAnyVehicle(cache.ped, false) or IsEntityDead(cache.ped) or holdingPizza then 71 | return 72 | end 73 | 74 | local pos = GetEntityCoords(cache.ped) 75 | 76 | if #(pos - vec3(currentDelivery.x, currentDelivery.y, currentDelivery.z)) >= 30.0 then 77 | return DoNotification('You\'re not close enough to the customer\'s house!', 'error') 78 | end 79 | 80 | doEmote(true) 81 | end 82 | 83 | local function PullOutVehicle(netid, data) 84 | pizzaCar = lib.waitFor(function() 85 | if NetworkDoesEntityExistWithNetworkId(netid) then 86 | return NetToVeh(netid) 87 | end 88 | end, 'Could not load entity in time.', 1000) 89 | 90 | if pizzaCar == 0 then 91 | return DoNotification('Error spawning the vehicle.', 'error') 92 | end 93 | 94 | SetVehicleNumberPlateText(pizzaCar, 'PIZZA'..tostring(math.random(1000, 9999))) 95 | SetVehicleColours(pizzaCar, 111, 111) 96 | SetVehicleDirtLevel(pizzaCar, 1) 97 | handleVehicleKeys(pizzaCar) 98 | SetVehicleEngineOn(pizzaCar, true, true) 99 | isHired = true 100 | NextDelivery(data) 101 | Wait(500) 102 | if Config.FuelScript.enable then 103 | exports[Config.FuelScript.script]:SetFuel(pizzaCar, 100.0) 104 | else 105 | Entity(pizzaCar).state.fuel = 100 106 | end 107 | 108 | if oxtarget then 109 | exports.ox_target:addEntity(netid, { 110 | { 111 | icon = 'fa-solid fa-pizza-slice', 112 | label = 'Take Pizza', 113 | onSelect = TakePizza, 114 | canInteract = function() 115 | return isHired and activeOrder and not holdingPizza 116 | end, 117 | distance = 2.5 118 | }, 119 | { 120 | icon = 'fa-solid fa-pizza-slice', 121 | label = 'Return Pizza', 122 | onSelect = function(entity) 123 | doEmote(false) 124 | end, 125 | canInteract = function() 126 | return isHired and activeOrder and holdingPizza 127 | end, 128 | distance = 2.5 129 | }, 130 | }) 131 | else 132 | exports['qb-target']:AddTargetEntity(pizzaCar, { 133 | options = { 134 | { 135 | icon = 'fa-solid fa-pizza-slice', 136 | label = 'Take Pizza', 137 | action = TakePizza, 138 | canInteract = function() 139 | return isHired and activeOrder and not holdingPizza 140 | end, 141 | 142 | }, 143 | { 144 | icon = 'fa-solid fa-pizza-slice', 145 | label = 'Return Pizza', 146 | action = function(entity) 147 | doEmote(false) 148 | end, 149 | canInteract = function() 150 | return isHired and activeOrder and holdingPizza 151 | end, 152 | 153 | }, 154 | }, 155 | distance = 2.5 156 | }) 157 | end 158 | end 159 | 160 | local function finishWork() 161 | local ped = cache.ped 162 | local pos = GetEntityCoords(ped) 163 | 164 | local finishspot = vec3(Config.BossCoords.x, Config.BossCoords.y, Config.BossCoords.z) 165 | if #(pos - finishspot) > 10.0 or not isHired then return end 166 | 167 | if oxtarget then 168 | exports.ox_target:removeEntity(NetworkGetNetworkIdFromEntity(pizzaCar), {'Take Pizza', 'Return Pizza'}) 169 | else 170 | exports['qb-target']:RemoveTargetEntity(pizzaCar, {'Take Pizza', 'Return Pizza'}) 171 | end 172 | 173 | local success = lib.callback.await('randol_pizzajob:server:clockOut', false) 174 | if success then 175 | RemoveBlip(JobBlip) 176 | doEmote(false) 177 | isHired, activeOrder = false, false 178 | DoNotification('You ended your shift.', 'success') 179 | pizzaCar = nil 180 | end 181 | end 182 | 183 | local function yeetPed() 184 | if DoesEntityExist(pizzaBoss) then 185 | if oxtarget then 186 | exports.ox_target:removeLocalEntity(pizzaBoss, {'Start Work', 'Finish Work'}) 187 | else 188 | exports['qb-target']:RemoveTargetEntity(pizzaBoss, {'Start Work', 'Finish Work'}) 189 | end 190 | DeleteEntity(pizzaBoss) 191 | pizzaBoss = nil 192 | end 193 | end 194 | 195 | local function spawnPed() 196 | if DoesEntityExist(pizzaBoss) then return end 197 | 198 | lib.requestModel(Config.BossModel) 199 | pizzaBoss = CreatePed(0, Config.BossModel, Config.BossCoords, false, false) 200 | SetEntityAsMissionEntity(pizzaBoss) 201 | SetPedFleeAttributes(pizzaBoss, 0, 0) 202 | SetBlockingOfNonTemporaryEvents(pizzaBoss, true) 203 | SetEntityInvincible(pizzaBoss, true) 204 | FreezeEntityPosition(pizzaBoss, true) 205 | lib.requestAnimDict('amb@world_human_leaning@female@wall@back@holding_elbow@idle_a') 206 | TaskPlayAnim(pizzaBoss, 'amb@world_human_leaning@female@wall@back@holding_elbow@idle_a', 'idle_a', 8.0, 1.0, -1, 01, 0, 0, 0, 0) 207 | RemoveAnimDict('amb@world_human_leaning@female@wall@back@holding_elbow@idle_a') 208 | SetModelAsNoLongerNeeded(Config.BossModel) 209 | 210 | if oxtarget then 211 | exports.ox_target:addLocalEntity(pizzaBoss, { 212 | { 213 | icon = 'fa-solid fa-pizza-slice', 214 | label = 'Start Work', 215 | onSelect = function() 216 | local netid, data = lib.callback.await('randol_pizzajob:server:spawnVehicle', false) 217 | if netid and data then 218 | PullOutVehicle(netid, data) 219 | end 220 | end, 221 | canInteract = function() 222 | return not isHired 223 | end, 224 | distance = 1.5, 225 | }, 226 | { 227 | icon = 'fa-solid fa-pizza-slice', 228 | label = 'Finish Work', 229 | onSelect = finishWork, 230 | canInteract = function() 231 | return isHired 232 | end, 233 | distance = 1.5, 234 | }, 235 | }) 236 | else 237 | exports['qb-target']:AddTargetEntity(pizzaBoss, { 238 | options = { 239 | { 240 | icon = 'fa-solid fa-pizza-slice', 241 | label = 'Start Work', 242 | action = function() 243 | local netid, data = lib.callback.await('randol_pizzajob:server:spawnVehicle', false) 244 | if netid and data then 245 | PullOutVehicle(netid, data) 246 | end 247 | end, 248 | canInteract = function() 249 | return not isHired 250 | end, 251 | }, 252 | { 253 | icon = 'fa-solid fa-pizza-slice', 254 | label = 'Finish Work', 255 | action = finishWork, 256 | canInteract = function() return isHired end, 257 | }, 258 | }, 259 | distance = 1.5, 260 | }) 261 | end 262 | end 263 | 264 | local function deliverPizza() 265 | if holdingPizza and isHired and not pizzaDelivered then 266 | lib.requestAnimDict('timetable@jimmy@doorknock@') 267 | TaskPlayAnim(cache.ped, 'timetable@jimmy@doorknock@', 'knockdoor_idle', 3.0, 1.0, -1, 49, 0, true, true, true) 268 | RemoveAnimDict('timetable@jimmy@doorknock@') 269 | pizzaDelivered = true 270 | if lib.progressCircle({ 271 | duration = 7000, 272 | position = 'bottom', 273 | label = 'Delivering pizza', 274 | useWhileDead = true, 275 | canCancel = false, 276 | disable = { move = true, car = true, mouse = false, combat = true, }, 277 | }) then 278 | local success, data = lib.callback.await('randol_pizzajob:server:Payment', false) 279 | if not success then return end 280 | RemoveBlip(JobBlip) 281 | if oxtarget then 282 | exports.ox_target:removeZone(currZone) 283 | else 284 | exports['qb-target']:RemoveZone(currZone) 285 | end 286 | currZone = nil 287 | activeOrder = false 288 | pizzaDelivered = false 289 | doEmote(false) 290 | if data then 291 | NextDelivery(data) 292 | end 293 | end 294 | else 295 | DoNotification('You need the pizza from the car dummy.', 'error') 296 | end 297 | end 298 | 299 | function NextDelivery(data) 300 | if activeOrder then return end 301 | currentDelivery = data.current 302 | JobBlip = AddBlipForCoord(currentDelivery.x, currentDelivery.y, currentDelivery.z) 303 | SetBlipSprite(JobBlip, 1) 304 | SetBlipDisplay(JobBlip, 4) 305 | SetBlipScale(JobBlip, 0.8) 306 | SetBlipFlashes(JobBlip, true) 307 | SetBlipAsShortRange(JobBlip, true) 308 | SetBlipColour(JobBlip, 2) 309 | SetBlipRoute(JobBlip, true) 310 | SetBlipRouteColour(JobBlip, 2) 311 | BeginTextCommandSetBlipName('STRING') 312 | AddTextComponentSubstringPlayerName('Next Customer') 313 | EndTextCommandSetBlipName(JobBlip) 314 | if oxtarget then 315 | currZone = exports.ox_target:addSphereZone({ 316 | coords = vec3(currentDelivery.x, currentDelivery.y, currentDelivery.z), 317 | radius = 1.3, 318 | debug = false, 319 | options = { 320 | { 321 | icon = 'fa-solid fa-pizza-slice', 322 | label = 'Deliver Pizza', 323 | onSelect = deliverPizza, 324 | distance = 1.5, 325 | }, 326 | 327 | } 328 | }) 329 | else 330 | exports['qb-target']:AddCircleZone('deliverZone', vec3(currentDelivery.x, currentDelivery.y, currentDelivery.z), 1.3,{ 331 | name = 'deliverZone', 332 | debugPoly = false, 333 | useZ=true, 334 | }, { options = { 335 | { 336 | icon = 'fa-solid fa-pizza-slice', 337 | label = 'Deliver Pizza', 338 | action = deliverPizza, 339 | },}, 340 | distance = 1.5 341 | }) 342 | currZone = 'deliverZone' 343 | end 344 | activeOrder = true 345 | DoNotification('You have a new delivery!', 'success') 346 | end 347 | 348 | local function startJobPoint() 349 | startZone = lib.points.new({ 350 | coords = Config.BossCoords.xyz, 351 | distance = 50, 352 | onEnter = spawnPed, 353 | onExit = yeetPed, 354 | }) 355 | end 356 | 357 | function OnPlayerLoaded() 358 | startJobPoint() 359 | end 360 | 361 | function OnPlayerUnload() 362 | resetJob() 363 | end 364 | 365 | AddEventHandler('onResourceStart', function(resource) 366 | if GetCurrentResourceName() ~= resource or not hasPlyLoaded() then return end 367 | startJobPoint() 368 | end) 369 | 370 | AddEventHandler('onResourceStop', function(resourceName) 371 | if GetCurrentResourceName() ~= resourceName then return end 372 | resetJob() 373 | end) 374 | --------------------------------------------------------------------------------