local Config = lib.require('config') local QBCore = exports['qb-core']:GetCoreObject() local pizzaBlips = {} local bossPoints = {} local isHired, holdingPizza, pizzaDelivered, activeOrder = false, false, false, false local pizzaProp, pizzaBoss, pizzaCar, currZone, JobBlip, currentDelivery local oxtarget = GetResourceState('ox_target') == 'started' -- Check if player has required job local function hasRequiredJob() local Player = QBCore.Functions.GetPlayerData() if not Player or not Player.job then return false end local PlayerJob = Player.job.name local PlayerGrade = Player.job.grade.level for _, allowedJob in ipairs(Config.AllowedJobs) do if PlayerJob == allowedJob then if Config.RequiredJobGrade and PlayerGrade >= Config.RequiredJobGrade then return true elseif not Config.RequiredJobGrade then return true end end end return false end -- Handle pizza box prop and animation local function doEmote(bool) if bool then local model = `prop_coolbox_01` lib.requestModel(model) local coords = GetEntityCoords(cache.ped) pizzaProp = CreateObject(model, coords.x, coords.y, coords.z, true, true, true) 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) lib.requestAnimDict('anim@heists@box_carry@') TaskPlayAnim(cache.ped, 'anim@heists@box_carry@', 'idle', 5.0, 5.0, -1, 51, 0, 0, 0, 0) SetModelAsNoLongerNeeded(model) CreateThread(function() while DoesEntityExist(pizzaProp) do if not IsEntityPlayingAnim(cache.ped, 'anim@heists@box_carry@', 'idle', 3) then TaskPlayAnim(cache.ped, 'anim@heists@box_carry@', 'idle', 5.0, 5.0, -1, 51, 0, 0, 0, 0) end Wait(1000) end RemoveAnimDict('anim@heists@box_carry@') end) else if DoesEntityExist(pizzaProp) then DetachEntity(cache.ped, true, false) DeleteEntity(pizzaProp) pizzaProp = nil ClearPedTasksImmediately(cache.ped) end end holdingPizza = bool end -- Reset job state local function resetJob() if oxtarget then if currZone then exports.ox_target:removeZone(currZone) end else if currZone then exports['qb-target']:RemoveZone(currZone) end end currZone = nil if JobBlip then RemoveBlip(JobBlip) end isHired = false holdingPizza = false pizzaDelivered = false activeOrder = false doEmote(false) end -- Cleanup function local function cleanupBossPeds() for _, boss in pairs(bossPoints) do if DoesEntityExist(boss) then if oxtarget then exports.ox_target:removeLocalEntity(boss, {'Liefern starten', 'Liefern beenden'}) else exports['qb-target']:RemoveTargetEntity(boss) end DeleteEntity(boss) end end bossPoints = {} end -- Handle taking pizza from vehicle local function TakePizza() if IsPedInAnyVehicle(cache.ped, false) or IsEntityDead(cache.ped) or holdingPizza then return end local pos = GetEntityCoords(cache.ped) if #(pos - vec3(currentDelivery.x, currentDelivery.y, currentDelivery.z)) >= 30.0 then return QBCore.Functions.Notify('Park dichter am Kundenhaus!', 'error') end doEmote(true) end -- Handle pizza delivery local function deliverPizza() if not hasRequiredJob() then return QBCore.Functions.Notify('Du hast den Job nicht mehr!', 'error') end if holdingPizza and isHired and not pizzaDelivered then lib.requestAnimDict('timetable@jimmy@doorknock@') TaskPlayAnim(cache.ped, 'timetable@jimmy@doorknock@', 'knockdoor_idle', 3.0, 1.0, -1, 49, 0, true, true, true) RemoveAnimDict('timetable@jimmy@doorknock@') pizzaDelivered = true if lib.progressCircle({ duration = 7000, position = 'bottom', label = 'Ausliefern', useWhileDead = false, canCancel = false, disable = { move = true, car = true, mouse = false, combat = true, }, }) then local success, data = lib.callback.await('randol_pizzajob:server:Payment', false) if not success then return end if JobBlip then RemoveBlip(JobBlip) end if oxtarget then if currZone then exports.ox_target:removeZone(currZone) end else if currZone then exports['qb-target']:RemoveZone(currZone) end end currZone = nil activeOrder = false pizzaDelivered = false doEmote(false) if data then NextDelivery(data) end end else QBCore.Functions.Notify('Hol die Lieferung aus dem Auto... Idiot!', 'error') end end -- Setup next delivery function NextDelivery(data) if activeOrder or not hasRequiredJob() then return end currentDelivery = data.current if JobBlip then RemoveBlip(JobBlip) end JobBlip = AddBlipForCoord(currentDelivery.x, currentDelivery.y, currentDelivery.z) SetBlipSprite(JobBlip, 1) SetBlipDisplay(JobBlip, 4) SetBlipScale(JobBlip, 0.8) SetBlipFlashes(JobBlip, true) SetBlipAsShortRange(JobBlip, false) SetBlipColour(JobBlip, 2) SetBlipRoute(JobBlip, true) SetBlipRouteColour(JobBlip, 2) BeginTextCommandSetBlipName('STRING') AddTextComponentSubstringPlayerName('Nächster Kunde') EndTextCommandSetBlipName(JobBlip) if oxtarget then if currZone then exports.ox_target:removeZone(currZone) end currZone = exports.ox_target:addSphereZone({ coords = vec3(currentDelivery.x, currentDelivery.y, currentDelivery.z), radius = 1.3, debug = false, options = { { icon = 'fa-solid fa-pizza-slice', label = 'Ausliefern', onSelect = deliverPizza, distance = 1.5, }, } }) else if currZone then exports['qb-target']:RemoveZone(currZone) end local zoneName = 'deliverZone'..math.random(1,1000) exports['qb-target']:AddBoxZone(zoneName, vector3(currentDelivery.x, currentDelivery.y, currentDelivery.z), 1.5, 1.5, { name = zoneName, heading = 0, debugPoly = false, minZ = currentDelivery.z - 1, maxZ = currentDelivery.z + 2, }, { options = { { type = "client", icon = 'fas fa-pizza-slice', label = 'Ausliefern', action = function() deliverPizza() end } }, distance = 2.0 } ) currZone = zoneName end activeOrder = true QBCore.Functions.Notify('Du hast eine neue Lieferung', 'success') end -- Handle finishing work local function finishWork() if not hasRequiredJob() then return QBCore.Functions.Notify('Du hast den Job nicht mehr!', 'error') end local pos = GetEntityCoords(cache.ped) if not isHired then return end if oxtarget then exports.ox_target:removeEntity(NetworkGetNetworkIdFromEntity(pizzaCar), {'Take Pizza', 'Return Pizza'}) else exports['qb-target']:RemoveTargetEntity(pizzaCar) end local success = lib.callback.await('randol_pizzajob:server:clockOut', false) if success then resetJob() QBCore.Functions.Notify('Liefern beenden.', 'success') pizzaCar = nil end end -- Handle vehicle spawn local function PullOutVehicle(netid, data) if not hasRequiredJob() then return QBCore.Functions.Notify('Du hast den Job nicht mehr!', 'error') end pizzaCar = lib.waitFor(function() if NetworkDoesEntityExistWithNetworkId(netid) then return NetToVeh(netid) end end, 'Could not load entity in time.', 1000) if pizzaCar == 0 then return QBCore.Functions.Notify('Fehler beim Fahrzeug ausparken.', 'error') end SetVehicleNumberPlateText(pizzaCar, 'NORDI'..tostring(math.random(1000, 9999))) SetVehicleColours(pizzaCar, 111, 111) SetVehicleDirtLevel(pizzaCar, 1) TriggerEvent('vehiclekeys:client:SetOwner', QBCore.Functions.GetPlate(pizzaCar)) SetVehicleEngineOn(pizzaCar, true, true) isHired = true Wait(500) if Config.FuelScript.enable then exports[Config.FuelScript.script]:SetFuel(pizzaCar, 100.0) else Entity(pizzaCar).state.fuel = 100 end if oxtarget then exports.ox_target:addEntity(netid, { { icon = 'fa-solid fa-pizza-slice', label = 'Lieferung nehmen', onSelect = TakePizza, canInteract = function() return isHired and activeOrder and not holdingPizza and hasRequiredJob() end, distance = 2.5 }, { icon = 'fa-solid fa-pizza-slice', label = 'Lieferung zurücklegen', onSelect = function() doEmote(false) end, canInteract = function() return isHired and activeOrder and holdingPizza and hasRequiredJob() end, distance = 2.5 }, }) else exports['qb-target']:AddTargetEntity(pizzaCar, { options = { { type = "client", icon = 'fas fa-pizza-slice', label = 'Lieferung nehmen', action = function() TakePizza() end }, { type = "client", icon = 'fas fa-pizza-slice', label = 'Lieferung zurücklegen', action = function() doEmote(false) end } }, distance = 2.0 }) end NextDelivery(data) end -- Create boss peds at all locations local function spawnBossPeds() cleanupBossPeds() for _, location in pairs(Config.BossLocations) do lib.requestModel(Config.BossModel) local boss = CreatePed(0, Config.BossModel, location.coords.x, location.coords.y, location.coords.z, location.coords.w, false, false) SetEntityAsMissionEntity(boss) SetPedFleeAttributes(boss, 0, 0) SetBlockingOfNonTemporaryEvents(boss, true) SetEntityInvincible(boss, true) FreezeEntityPosition(boss, true) -- Phone animation local phoneModel = `prop_npc_phone_02` lib.requestModel(phoneModel) local phoneProps = CreateObject(phoneModel, 0.0, 0.0, 0.0, true, true, true) AttachEntityToEntity(phoneProps, boss, GetPedBoneIndex(boss, 28422), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, true, false, true, 1, true) lib.requestAnimDict('cellphone@') TaskPlayAnim(boss, 'cellphone@', 'cellphone_call_listen_base', 8.0, 1.0, -1, 49, 0, false, false, false) RemoveAnimDict('cellphone@') SetModelAsNoLongerNeeded(phoneModel) if oxtarget then exports.ox_target:addLocalEntity(boss, { { icon = 'fa-solid fa-pizza-slice', label = 'Liefern starten', onSelect = function() if not hasRequiredJob() then return QBCore.Functions.Notify('Geh weg, du arbeitest nicht hier!', 'error') end local netid, data = lib.callback.await('randol_pizzajob:server:spawnVehicle', false, location.vehicleSpawn) if netid and data then PullOutVehicle(netid, data) end end, canInteract = function() return not isHired and hasRequiredJob() end, distance = 1.5, }, { icon = 'fa-solid fa-pizza-slice', label = 'Liefern beenden', onSelect = finishWork, canInteract = function() return isHired and hasRequiredJob() end, distance = 1.5, }, }) else exports['qb-target']:AddTargetEntity(boss, { options = { { type = "client", icon = 'fas fa-pizza-slice', label = 'Liefern starten', action = function() if not hasRequiredJob() then return QBCore.Functions.Notify('Geh weg, du arbeitest nicht hier!', 'error') end local netid, data = lib.callback.await('randol_pizzajob:server:spawnVehicle', false, location.vehicleSpawn) if netid and data then PullOutVehicle(netid, data) end end }, { type = "client", icon = 'fas fa-pizza-slice', label = 'Liefern beenden', action = function() finishWork() end } }, distance = 2.0 }) end table.insert(bossPoints, boss) end end -- Create points for all locations local function createJobPoints() for _, location in pairs(Config.BossLocations) do lib.points.new({ coords = vector3(location.coords.x, location.coords.y, location.coords.z), distance = 50, onEnter = spawnBossPeds, onExit = cleanupBossPeds, }) end end -- Create blips for all locations local function toggleBlips() for _, blip in pairs(pizzaBlips) do if DoesBlipExist(blip) then RemoveBlip(blip) end end pizzaBlips = {} if not hasRequiredJob() then return end for _, location in pairs(Config.BossLocations) do local blip = AddBlipForCoord(location.coords.x, location.coords.y, location.coords.z) SetBlipSprite(blip, 545) SetBlipAsShortRange(blip, true) SetBlipScale(blip, 0.6) SetBlipColour(blip, 2) BeginTextCommandSetBlipName('STRING') AddTextComponentString(location.blipName or 'Liefern') EndTextCommandSetBlipName(blip) table.insert(pizzaBlips, blip) end end -- Events für QB-Target RegisterNetEvent('pizzajob:client:startWork', function() if not hasRequiredJob() then return QBCore.Functions.Notify('Geh weg, du arbeitest nicht hier!', 'error') end local netid, data = lib.callback.await('randol_pizzajob:server:spawnVehicle', false, Config.BossLocations[1].vehicleSpawn) if netid and data then PullOutVehicle(netid, data) end end) RegisterNetEvent('pizzajob:client:finishWork', function() finishWork() end) RegisterNetEvent('pizzajob:client:takePizza', function() TakePizza() end) RegisterNetEvent('pizzajob:client:returnPizza', function() doEmote(false) end) RegisterNetEvent('pizzajob:client:deliverPizza', function() deliverPizza() end) -- Register core events RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() toggleBlips() createJobPoints() end) RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) toggleBlips() end) -- Initial setup CreateThread(function() Wait(1000) toggleBlips() createJobPoints() end) -- Resource handlers AddEventHandler('onResourceStart', function(resource) if GetCurrentResourceName() ~= resource then return end Wait(1000) toggleBlips() createJobPoints() end) AddEventHandler('onResourceStop', function(resourceName) if GetCurrentResourceName() ~= resourceName then return end cleanupBossPeds() resetJob() end)