├── fxmanifest.lua ├── README.md ├── server.lua ├── config.lua ├── handling.meta └── client.lua /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version "cerulean" 2 | game "gta5" 3 | lua54 'yes' 4 | 5 | author "Cadburry" 6 | description "Postal Op Job which uses qb-target & polyzone" 7 | version "1.2" 8 | 9 | shared_scripts { 10 | 'config.lua', 11 | } 12 | server_script 'server.lua' 13 | client_script 'client.lua' 14 | 15 | files { 16 | 'handling.meta', 17 | } 18 | 19 | data_file 'HANDLING_FILE' 'handling.meta' 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Postal Op Delivery Job 2 | This is a job where you sign in near the postal op and put in a deposit for a truck which will be used to deliver packages to store, banks, etc 3 | 4 | # Dependencies 5 | - qb-core 6 | - qb-target 7 | - PolyZone 8 | 9 | # How to install? 10 | - Download latest version from releases 11 | - Put in resources and ensure in server.cfg 12 | - Thats all! 13 | 14 | # Support 15 | Join Discord: https://discord.gg/qxGPARNwNP 16 | -------------------------------------------------------------------------------- /server.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | -- FUNCTIONS 4 | 5 | function Notify(src, msg) 6 | TriggerClientEvent("QBCore:Notify", src, msg) 7 | end 8 | 9 | -- JOB CASH 10 | 11 | RegisterNetEvent('cad-delivery:cash', function(currentJobPay, value) 12 | local _source = source 13 | local Player = QBCore.Functions.GetPlayer(_source) 14 | if not Player then return end 15 | if value == "job" then 16 | Player.Functions.AddMoney("bank", currentJobPay, "postal-job") 17 | Notify(_source, "You recieved payslip of $" .. currentJobPay) 18 | elseif value == "add" then 19 | Player.Functions.AddMoney("cash", currentJobPay, "postal-deposit-return") 20 | Notify(_source, "Your $" .. currentJobPay .. " deposit was returned.") 21 | elseif value == "remove" and Player.PlayerData.money.cash >= currentJobPay then 22 | Player.Functions.RemoveMoney("cash", currentJobPay, "postal-deposit-pay") 23 | Notify(_source, "Your $" .. currentJobPay .. " was taken as deposit.") 24 | end 25 | end) 26 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.blip = { 4 | label = 'Postal OP', 5 | coords = vector3(-425.44, -2787.76, 6.0), 6 | sprite = 478, 7 | scale = 0.6, 8 | color = 10 9 | } 10 | 11 | Config.pedModel = 's_m_m_postal_01' 12 | Config.pedCoords = vector4(-425.44, -2787.76, 5.0, 326.65) 13 | 14 | Config.vehicleModel = `boxville2` 15 | 16 | Config.fuel = 'LegacyFuel' -- 'LegacyFuel' or 'ps-fuel' or 'lj-fuel', etc 17 | Config.target = 'qb' -- 'qb' or 'ox' 18 | 19 | Config.vehicleSpawnLocations = { 20 | vector4(-446.24, -2789.72, 5.96, 46.04), 21 | vector4(-451.24, -2793.8, 5.96, 47.51), 22 | vector4(-455.8, -2798.48, 5.96, 44.91), 23 | } 24 | 25 | Config.deliveryLocations = { 26 | vector3(441.12, -981.12, 30.68), 27 | vector3(306.72, -594.88, 43.28), 28 | vector3(-37.2, -1110.36, 26.44), 29 | vector3(-267.08, -955.64, 31.24), 30 | vector3(-1200.36, -891.12, 14.0), 31 | vector3(-705.72, -906.68, 19.2), 32 | vector3(26.04, -1339.24, 29.48), 33 | vector3(-41.52, -1752.52, 29.44), 34 | vector3(-534.64, -165.96, 38.32), 35 | vector3(-633.08, 233.88, 81.88), 36 | vector3(246.92, 222.2, 106.28), 37 | vector3(375.64, 333.72, 103.56), 38 | vector3(314.48, -278.6, 54.16), 39 | vector3(149.44, -1040.0, 29.36), 40 | vector3(-1081.96, -248.24, 37.76), 41 | vector3(-350.76, -48.84, 49.04), 42 | vector3(-1484.2, -380.24, 40.16), 43 | vector3(2550.24, 382.0, 108.64), 44 | vector3(-1816.56, -1193.48, 14.32), 45 | vector3(-2966.64, 388.0, 15.04), 46 | } 47 | -------------------------------------------------------------------------------- /handling.meta: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | benson 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 224048 52 | 0 53 | 0 54 | TRUCK 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | -- LOCALS 4 | local currentJob = {} 5 | local onJob = false 6 | local onDelivery = false 7 | local goPostalVehicle = nil 8 | local currentJobPay = 0 9 | local totalpayamount = 0 10 | local MaxDelivery = 0 11 | local PackageObject = nil 12 | local missionblip = nil 13 | local isDeliverySignedIn = false 14 | 15 | -- CONFIG 16 | local locations = Config.deliveryLocations 17 | local vehicleSpawnLocations = Config.vehicleSpawnLocations 18 | 19 | function Notify(msg) 20 | QBCore.Functions.Notify(msg) 21 | end 22 | 23 | function Givekeys(plate) 24 | TriggerEvent("vehiclekeys:client:SetOwner", plate) 25 | end 26 | 27 | -- FUNCTIONS 28 | 29 | function DeliveryBlipToggle() 30 | if onJob then 31 | local bCfg = Config.blip 32 | PostalBlip = AddBlipForCoord(bCfg.coords.x, bCfg.coords.y, bCfg.coords.z) 33 | SetBlipSprite(PostalBlip, bCfg.sprite) 34 | SetBlipScale(PostalBlip, bCfg.scale) 35 | SetBlipDisplay(PostalBlip, 4) 36 | SetBlipColour(PostalBlip, bCfg.color) 37 | SetBlipAsShortRange(PostalBlip, true) 38 | BeginTextCommandSetBlipName("STRING") 39 | AddTextComponentString(bCfg.label) 40 | EndTextCommandSetBlipName(PostalBlip) 41 | else 42 | if PostalBlip and DoesBlipExist(PostalBlip) then 43 | RemoveBlip(PostalBlip) 44 | end 45 | end 46 | end 47 | 48 | function NewDeliveryShift() 49 | if MaxDelivery >= 0 then 50 | local vehcoords = GetEntityCoords(goPostalVehicle) 51 | local jobLocation = locations[math.random(1, #locations)] 52 | SetDeliveryJobBlip(jobLocation[1], jobLocation[2], jobLocation[3]) 53 | currentJob = jobLocation 54 | currentJobPay = CalculateTravelDistanceBetweenPoints(vehcoords.x, vehcoords.y, vehcoords.z, currentJob[1], currentJob[2], currentJob[3]) / 2 / 4 55 | if currentJobPay > 60 then 56 | currentJobPay = math.random(1200, 1500) 57 | end 58 | Notify("Go to next delivery point.") 59 | else 60 | onJob = false 61 | RemoveJobBlip() 62 | SetNewWaypoint(-425.44, -2787.76) 63 | Notify("You finished deliveries, please return to warehouse.") 64 | end 65 | end 66 | 67 | function SpawnGoPostal(x, y, z, w) 68 | local vehicleHash = Config.vehicleModel 69 | RequestModel(vehicleHash) 70 | while not HasModelLoaded(vehicleHash) do 71 | Wait(0) 72 | end 73 | goPostalVehicle = CreateVehicle(vehicleHash, x, y, z, w, true, false) 74 | local id = NetworkGetNetworkIdFromEntity(goPostalVehicle) 75 | SetNetworkIdCanMigrate(id, true) 76 | SetNetworkIdExistsOnAllMachines(id, true) 77 | SetVehicleDirtLevel(goPostalVehicle, 0) 78 | SetVehicleHasBeenOwnedByPlayer(goPostalVehicle, true) 79 | SetEntityAsMissionEntity(goPostalVehicle, true, true) 80 | SetVehicleEngineOn(goPostalVehicle, true, true, true) 81 | SetVehicleColours(goPostalVehicle, 131, 74) 82 | exports[Config.fuel]:SetFuel(goPostalVehicle, 100.0) 83 | local plate = GetVehicleNumberPlateText(goPostalVehicle) 84 | Givekeys(plate) 85 | end 86 | 87 | function GetParkingPosition(spots) 88 | for id, v in pairs(spots) do 89 | if GetClosestVehicle(v.x, v.y, v.z, 3.0, 0, 70) == 0 then 90 | return true, v 91 | end 92 | end 93 | Notify("Parking Spots Full, Please Wait") 94 | end 95 | 96 | function SetDeliveryJobBlip(x, y, z) 97 | if missionblip and DoesBlipExist(missionblip) then RemoveBlip(missionblip) end 98 | missionblip = AddBlipForCoord(x, y, z) 99 | SetBlipSprite(missionblip, 164) 100 | SetBlipColour(missionblip, 53) 101 | SetNewWaypoint(x, y) 102 | BeginTextCommandSetBlipName("STRING") 103 | AddTextComponentString("Destination") 104 | EndTextCommandSetBlipName(missionblip) 105 | end 106 | 107 | function RemoveJobBlip() 108 | if DoesBlipExist(missionblip) then RemoveBlip(missionblip) end 109 | end 110 | 111 | function LoadAnim(animDict) 112 | RequestAnimDict(animDict) 113 | while not HasAnimDictLoaded(animDict) do 114 | Wait(10) 115 | end 116 | end 117 | 118 | function LoadModel(model) 119 | RequestModel(model) 120 | while not HasModelLoaded(model) do 121 | Wait(10) 122 | end 123 | end 124 | 125 | -- JOB WORK 126 | 127 | CreateThread(function() 128 | local pedModel = GetHashKey(Config.pedModel) 129 | RequestModel(pedModel) 130 | while not HasModelLoaded(pedModel) do 131 | Wait(1) 132 | end 133 | local ped = CreatePed(4, pedModel, Config.pedCoords.x, Config.pedCoords.y, Config.pedCoords.z, Config.pedCoords.w, false, false) 134 | SetBlockingOfNonTemporaryEvents(ped, true) 135 | SetPedDiesWhenInjured(ped, false) 136 | SetEntityHeading(ped, Config.pedCoords.w) 137 | SetPedCanPlayAmbientAnims(ped, true) 138 | SetPedCanRagdollFromPlayerImpact(ped, false) 139 | SetEntityInvincible(ped, true) 140 | FreezeEntityPosition(ped, true) 141 | if Config.target == 'ox' then 142 | exports.ox_target:addModel('s_m_m_postal_01', { 143 | { 144 | label = "Sign In", 145 | distance = 1.5, 146 | event = "cad-postalop:startwork", 147 | icon = "fas fa-sign-in-alt", 148 | canInteract = function(entity, data) 149 | return not isDeliverySignedIn 150 | end, 151 | }, 152 | { 153 | label = "Recieve Payment & End Work", 154 | distance = 1.5, 155 | event = "cad-postalop:finishwork", 156 | icon = "fas fa-money-check-alt", 157 | canInteract = function(entity, data) 158 | return isDeliverySignedIn 159 | end, 160 | }, 161 | }) 162 | exports.ox_target:addGlobalVehicle({ 163 | { 164 | label = "Take Package", 165 | event = "cad-postalop:takepackage", 166 | icon = "fas fa-box", 167 | distance = 2.5, 168 | canInteract = function(entity, data) 169 | return onJob and (GetEntityModel(entity) == Config.vehicleModel) 170 | end, 171 | } 172 | }) 173 | elseif Config.target == 'qb' then 174 | exports['qb-target']:AddTargetModel('s_m_m_postal_01', { 175 | options = { 176 | { 177 | type = "client", 178 | event = "cad-postalop:startwork", 179 | icon = "fas fa-sign-in-alt", 180 | label = "Sign In", 181 | canInteract = function(entity, data) 182 | return not isDeliverySignedIn 183 | end, 184 | }, 185 | { 186 | type = "client", 187 | event = "cad-postalop:finishwork", 188 | icon = "fas fa-money-check-alt", 189 | label = "Recieve Payment & End Work", 190 | canInteract = function(entity, data) 191 | return isDeliverySignedIn 192 | end, 193 | }, 194 | }, 195 | distance = 1.5 196 | }) 197 | exports['qb-target']:AddGlobalVehicle({ 198 | options = { 199 | { 200 | type = "client", 201 | event = "cad-postalop:takepackage", 202 | icon = "fas fa-box", 203 | label = "Take Package", 204 | canInteract = function(entity, data) 205 | return onJob and (GetEntityModel(entity) == Config.vehicleModel) 206 | end, 207 | } 208 | }, 209 | distance = 2.5, 210 | }) 211 | end 212 | end) 213 | 214 | function DeliveryCheck() 215 | CreateThread(function() 216 | while isDeliverySignedIn do 217 | local inRange = false 218 | local coords = GetEntityCoords(PlayerPedId()) 219 | local distance = #(vec3(coords.x, coords.y, coords.z)- vec3(currentJob[1], currentJob[2], currentJob[3]) ) 220 | if distance < 50 and onJob and onDelivery then 221 | inRange = true 222 | DrawMarker(2, currentJob[1], currentJob[2], currentJob[3], 0, 0, 0, 0, 0, 0, 0.3, 0.2, -0.2, 100, 100, 155, 255, true, true, 0, false, nil, nil, false) 223 | if distance < 1.5 and onJob and onDelivery then 224 | LoadAnim("creatures@rottweiler@tricks@") 225 | TaskPlayAnim(PlayerPedId(), "creatures@rottweiler@tricks@", "petting_franklin", 8.0, 8.0, -1, 50, 0, false, false, false) 226 | FreezeEntityPosition(PlayerPedId(), true) 227 | Wait(5000) 228 | DeleteObject(PackageObject) 229 | FreezeEntityPosition(PlayerPedId(), false) 230 | ClearPedTasksImmediately(PlayerPedId()) 231 | PackageObject = nil 232 | onDelivery = false 233 | totalpayamount = totalpayamount + currentJobPay 234 | MaxDelivery = MaxDelivery - 1 235 | NewDeliveryShift() 236 | end 237 | end 238 | if not inRange then Wait(800) end 239 | Wait(4) 240 | end 241 | end) 242 | end 243 | 244 | RegisterNetEvent('cad-postalop:startwork', function() 245 | if not isDeliverySignedIn and not onJob then 246 | if not DoesEntityExist(goPostalVehicle) then 247 | if QBCore.Functions.GetPlayerData().money.cash >= 1000 then 248 | local freespot, v = GetParkingPosition(vehicleSpawnLocations) 249 | if freespot then SpawnGoPostal(v.x, v.y, v.z, v.w) end 250 | MaxDelivery = math.random(2, 8) 251 | TriggerServerEvent('cad-delivery:cash', 1000, "remove") 252 | NewDeliveryShift() 253 | onJob = true 254 | isDeliverySignedIn = true 255 | DeliveryCheck() 256 | DeliveryBlipToggle() 257 | else 258 | Notify("No money for deposit") 259 | end 260 | end 261 | else 262 | Notify("Already doing a work, finish that first") 263 | end 264 | end) 265 | 266 | RegisterNetEvent('cad-postalop:takepackage', function() 267 | if not onDelivery and onJob and not IsPedInAnyVehicle(PlayerPedId()) and GetDistanceBetweenCoords(GetEntityCoords(PlayerPedId()), currentJob[1], currentJob[2], currentJob[3], true) < 40 then 268 | LoadModel("hei_prop_heist_box") 269 | local pos = GetEntityCoords(PlayerPedId(), false) 270 | PackageObject = CreateObject(GetHashKey("hei_prop_heist_box"), pos.x, pos.y, pos.z, true, true, true) 271 | AttachEntityToEntity(PackageObject, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), 28422), 0.0, -0.03, 0.0, 5.0, 0.0, 0.0, true, true, false, true, 0, true) 272 | LoadAnim("anim@heists@box_carry@") 273 | TaskPlayAnim(PlayerPedId(), "anim@heists@box_carry@", "idle", 8.0, 8.0, -1, 50, 0, false, false, false) 274 | onDelivery = true 275 | end 276 | end) 277 | 278 | RegisterNetEvent('cad-postalop:finishwork', function() 279 | if isDeliverySignedIn then 280 | if not onJob then 281 | if goPostalVehicle and DoesEntityExist(goPostalVehicle) then 282 | DeleteVehicle(goPostalVehicle) 283 | RemoveJobBlip() 284 | if IsVehicleDamaged(goPostalVehicle) then 285 | TriggerServerEvent('cad-delivery:cash', 200, "add") 286 | else 287 | TriggerServerEvent('cad-delivery:cash', 1000, "add") 288 | end 289 | isDeliverySignedIn = false 290 | onJob = false 291 | TriggerServerEvent('cad-delivery:cash', totalpayamount, "job") 292 | Wait(500) 293 | totalpayamount = 0 294 | else 295 | isDeliverySignedIn = false 296 | onJob = false 297 | Notify("You wont get anything, cause you lost the delivery vehicle.") 298 | end 299 | DeliveryBlipToggle() 300 | else 301 | Notify("You have to complete your work to recieve payment.") 302 | end 303 | else 304 | Notify("Sign in & work first to recieve payment.") 305 | end 306 | end) --------------------------------------------------------------------------------