From dcfe4f62366ddad7c3a74a841db358d244783630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20N=27gadi?= Date: Sat, 26 Aug 2017 18:36:49 +0200 Subject: [PATCH] Add NPC jobs --- client/main.lua | 253 +++++++++++++++++++++++++++++++++++++++++------- config.lua | 82 +++++++++++++++- server/main.lua | 21 ++++ 3 files changed, 318 insertions(+), 38 deletions(-) diff --git a/client/main.lua b/client/main.lua index 527178c1..2fa62033 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,14 +1,34 @@ -ESX = nil -local PlayerData = {} -local GUI = {} -GUI.Time = 0 -local HasAlreadyEnteredMarker = false -local LastZone = nil -local CurrentAction = nil -local CurrentActionMsg = '' -local CurrentActionData = {} -local OnJob = false -local TargetCoords = nil +local Keys = { + ["ESC"] = 322, ["F1"] = 288, ["F2"] = 289, ["F3"] = 170, ["F5"] = 166, ["F6"] = 167, ["F7"] = 168, ["F8"] = 169, ["F9"] = 56, ["F10"] = 57, + ["~"] = 243, ["1"] = 157, ["2"] = 158, ["3"] = 160, ["4"] = 164, ["5"] = 165, ["6"] = 159, ["7"] = 161, ["8"] = 162, ["9"] = 163, ["-"] = 84, ["="] = 83, ["BACKSPACE"] = 177, + ["TAB"] = 37, ["Q"] = 44, ["W"] = 32, ["E"] = 38, ["R"] = 45, ["T"] = 245, ["Y"] = 246, ["U"] = 303, ["P"] = 199, ["["] = 39, ["]"] = 40, ["ENTER"] = 18, + ["CAPS"] = 137, ["A"] = 34, ["S"] = 8, ["D"] = 9, ["F"] = 23, ["G"] = 47, ["H"] = 74, ["K"] = 311, ["L"] = 182, + ["LEFTSHIFT"] = 21, ["Z"] = 20, ["X"] = 73, ["C"] = 26, ["V"] = 0, ["B"] = 29, ["N"] = 249, ["M"] = 244, [","] = 82, ["."] = 81, + ["LEFTCTRL"] = 36, ["LEFTALT"] = 19, ["SPACE"] = 22, ["RIGHTCTRL"] = 70, + ["HOME"] = 213, ["PAGEUP"] = 10, ["PAGEDOWN"] = 11, ["DELETE"] = 178, + ["LEFT"] = 174, ["RIGHT"] = 175, ["TOP"] = 27, ["DOWN"] = 173, + ["NENTER"] = 201, ["N4"] = 108, ["N5"] = 60, ["N6"] = 107, ["N+"] = 96, ["N-"] = 97, ["N7"] = 117, ["N8"] = 61, ["N9"] = 118 +} + +ESX = nil +local PlayerData = {} +local GUI = {} +GUI.Time = 0 +local HasAlreadyEnteredMarker = false +local LastZone = nil +local CurrentAction = nil +local CurrentActionMsg = '' +local CurrentActionData = {} +local OnJob = false +local TargetCoords = nil +local CurrentlyTowedVehicle = nil +local Blips = {} +local NPCOnJob = false +local NPCTargetTowable = nil +local NPCTargetTowableZone = nil +local NPCHasSpawnedTowable = false +local NPCLastCancel = GetGameTimer() - 5 * 60000 +local NPCHasBeenNextToTowable = false Citizen.CreateThread(function() while ESX == nil do @@ -17,6 +37,60 @@ Citizen.CreateThread(function() end end) +function SelectRandomTowable() + + local index = GetRandomIntInRange(1, #Config.Towables) + + for k,v in pairs(Config.Zones) do + if v.Pos.x == Config.Towables[index].x and v.Pos.y == Config.Towables[index].y and v.Pos.z == Config.Towables[index].z then + return k + end + end + +end + +function StartNPCJob() + + NPCOnJob = true + + NPCTargetTowableZone = SelectRandomTowable() + local zone = Config.Zones[NPCTargetTowableZone] + + Blips['NPCTargetTowableZone'] = AddBlipForCoord(zone.Pos.x, zone.Pos.y, zone.Pos.z) + SetBlipRoute(Blips['NPCTargetTowableZone'], true) + + ESX.ShowNotification('~y~Conduisez~s~ jusqu\'à l\'endroit indiqué') +end + +function StopNPCJob(cancel) + + if Blips['NPCTargetTowableZone'] ~= nil then + RemoveBlip(Blips['NPCTargetTowableZone']) + Blips['NPCTargetTowableZone'] = nil + end + + if Blips['NPCDelivery'] ~= nil then + RemoveBlip(Blips['NPCDelivery']) + Blips['NPCDelivery'] = nil + end + + + Config.Zones.VehicleDelivery.Type = -1 + + NPCOnJob = false + NPCTargetTowable = nil + NPCTargetTowableZone = nil + NPCHasSpawnedTowable = false + NPCHasBeenNextToTowable = false + + if cancel then + ESX.ShowNotification('Mission ~r~annulée~s~') + else + TriggerServerEvent('esx_mecanojob:onNPCJobCompleted') + end + +end + function OpenMecanoActionsMenu() local elements = { @@ -344,7 +418,7 @@ function OpenMobileMecanoActionsMenu() SetVehicleDoorsLocked(vehicle, 1) SetVehicleDoorsLockedForAllPlayers(vehicle, false) ClearPedTasksImmediately(playerPed) - TriggerEvent('esx:showNotification', 'Véhicule ~g~déverouillé') + ESX.ShowNotification('Véhicule ~g~déverouillé') end) end @@ -376,7 +450,7 @@ function OpenMobileMecanoActionsMenu() SetVehicleUndriveable(vehicle, false) SetVehicleEngineOn(vehicle, true, true) ClearPedTasksImmediately(playerPed) - TriggerEvent('esx:showNotification', 'Véhicule ~g~réparé') + ESX.ShowNotification('Véhicule ~g~réparé') end) end end @@ -403,7 +477,7 @@ function OpenMobileMecanoActionsMenu() Citizen.Wait(10000) SetVehicleDirtLevel(vehicle, 0) ClearPedTasksImmediately(playerPed) - TriggerEvent('esx:showNotification', 'Véhicule ~g~néttoyé') + ESX.ShowNotification('Véhicule ~g~néttoyé') end) end end @@ -420,11 +494,11 @@ function OpenMobileMecanoActionsMenu() local vehicle = GetVehiclePedIsIn( ped, false ) if ( GetPedInVehicleSeat( vehicle, -1 ) == ped ) then - TriggerEvent('esx:showNotification', 'Vehicule ~r~mis en fourrière') + ESX.ShowNotification('Vehicule ~r~mis en fourrière') SetEntityAsMissionEntity( vehicle, true, true ) deleteCar( vehicle ) else - TriggerEvent('esx:showNotification', 'Vous devez être assis du ~r~côté conducteur!') + ESX.ShowNotification('Vous devez être assis du ~r~côté conducteur!') end else local playerPos = GetEntityCoords( ped, 1 ) @@ -432,11 +506,11 @@ function OpenMobileMecanoActionsMenu() local vehicle = GetVehicleInDirection( playerPos, inFrontOfPlayer ) if ( DoesEntityExist( vehicle ) ) then - TriggerEvent('esx:showNotification', 'Vehicule ~r~mis en fourrière') + ESX.ShowNotification('Vehicule ~r~mis en fourrière') SetEntityAsMissionEntity( vehicle, true, true ) deleteCar( vehicle ) else - TriggerEvent('esx:showNotification', 'Vous devez être ~r~près d\'un véhicule~s~ pour le mettre en fourrière') + ESX.ShowNotification('Vous devez être ~r~près d\'un véhicule~s~ pour le mettre en fourrière') end end end @@ -456,28 +530,65 @@ function OpenMobileMecanoActionsMenu() local coordB = GetOffsetFromEntityInWorldCoords(playerped, 0.0, 5.0, 0.0) local targetVehicle = getVehicleInDirection(coordA, coordB) - if currentlyTowedVehicle == nil then + if CurrentlyTowedVehicle == nil then if targetVehicle ~= 0 then if not IsPedInAnyVehicle(playerped, true) then if vehicle ~= targetVehicle then AttachEntityToEntity(targetVehicle, vehicle, 20, -0.5, -5.0, 1.0, 0.0, 0.0, 0.0, false, false, false, false, 20, true) - currentlyTowedVehicle = targetVehicle - TriggerEvent('esx:showNotification', 'Vehicule ~b~attaché~s~ avec succès!') + CurrentlyTowedVehicle = targetVehicle + ESX.ShowNotification('Vehicule ~b~attaché~s~ avec succès!') + + if NPCOnJob then + + if NPCTargetTowable == targetVehicle then + ESX.ShowNotification('Veuillez déposer le véhicule à la concession') + + Config.Zones.VehicleDelivery.Type = 1 + + if Blips['NPCTargetTowableZone'] ~= nil then + RemoveBlip(Blips['NPCTargetTowableZone']) + Blips['NPCTargetTowableZone'] = nil + end + + Blips['NPCDelivery'] = AddBlipForCoord(Config.Zones.VehicleDelivery.Pos.x, Config.Zones.VehicleDelivery.Pos.y, Config.Zones.VehicleDelivery.Pos.z) + + SetBlipRoute(Blips['NPCDelivery'], true) + + end + + end + else - TriggerEvent('esx:showNotification', '~r~Impossible~s~ d\'attacher votre propre dépanneuse') + ESX.ShowNotification('~r~Impossible~s~ d\'attacher votre propre dépanneuse') end end else - TriggerEvent('esx:showNotification', 'Il n\'y a ~r~pas de véhicule~s~ à attacher') + ESX.ShowNotification('Il n\'y a ~r~pas de véhicule~s~ à attacher') end else - AttachEntityToEntity(currentlyTowedVehicle, vehicle, 20, -0.5, -12.0, 1.0, 0.0, 0.0, 0.0, false, false, false, false, 20, true) - DetachEntity(currentlyTowedVehicle, true, true) - currentlyTowedVehicle = nil - TriggerEvent('esx:showNotification', 'Vehicule ~b~détattaché~s~ avec succès!') + + AttachEntityToEntity(CurrentlyTowedVehicle, vehicle, 20, -0.5, -12.0, 1.0, 0.0, 0.0, 0.0, false, false, false, false, 20, true) + DetachEntity(CurrentlyTowedVehicle, true, true) + + if NPCOnJob then + + if CurrentlyTowedVehicle == NPCTargetTowable then + ESX.Game.DeleteVehicle(NPCTargetTowable) + TriggerServerEvent('esx_mecanojob:onNPCJobMissionCompleted') + StopNPCJob() + + else + ESX.ShowNotification('Ce n\'est pas le bon véhicule') + end + + end + + CurrentlyTowedVehicle = nil + + ESX.ShowNotification('Vehicule ~b~détattaché~s~ avec succès!') end else - TriggerEvent('esx:showNotification', '~r~Impossible! ~s~Vous devez avoir un ~b~Flatbed ~s~pour ça') + ESX.ShowNotification('~r~Impossible! ~s~Vous devez avoir un ~b~Flatbed ~s~pour ça') end end @@ -562,9 +673,9 @@ AddEventHandler('esx_mecanojob:onHijack', function() SetVehicleDoorsLocked(vehicle, 1) SetVehicleDoorsLockedForAllPlayers(vehicle, false) ClearPedTasksImmediately(playerPed) - TriggerEvent('esx:showNotification', '~g~Véhicule déverouillé') + ESX.ShowNotification('~g~Véhicule déverouillé') else - TriggerEvent('esx:showNotification', '~r~Crochetage raté') + ESX.ShowNotification('~r~Crochetage raté') ClearPedTasksImmediately(playerPed) end end) @@ -595,7 +706,7 @@ AddEventHandler('esx_mecanojob:onCarokit', function() SetVehicleFixed(vehicle) SetVehicleDeformationFixed(vehicle) ClearPedTasksImmediately(playerPed) - TriggerEvent('esx:showNotification', '~g~Carosserie réparée') + ESX.ShowNotification('~g~Carosserie réparée') end) end end @@ -624,7 +735,7 @@ AddEventHandler('esx_mecanojob:onFixkit', function() SetVehicleDeformationFixed(vehicle) SetVehicleUndriveable(vehicle, false) ClearPedTasksImmediately(playerPed) - TriggerEvent('esx:showNotification', '~g~Véhicule réparé') + ESX.ShowNotification('~g~Véhicule réparé') end) end end @@ -656,21 +767,29 @@ AddEventHandler('esx:setJob', function(job) end) AddEventHandler('esx_mecanojob:hasEnteredMarker', function(zone) + + if zone == NPCJobTargetTowable then + + end + if zone == 'MecanoActions' then CurrentAction = 'mecano_actions_menu' CurrentActionMsg = 'Appuyez sur ~INPUT_CONTEXT~ pour accéder au menu.' CurrentActionData = {} end + if zone == 'Garage' then CurrentAction = 'mecano_harvest_menu' CurrentActionMsg = 'Appuyez sur ~INPUT_CONTEXT~ pour accéder au menu de récolte.' CurrentActionData = {} end + if zone == 'Craft' then CurrentAction = 'mecano_craft_menu' CurrentActionMsg = 'Appuyez sur ~INPUT_CONTEXT~ pour accéder au menu établi.' CurrentActionData = {} end + if zone == 'VehicleDeleter' then local playerPed = GetPlayerPed(-1) if IsPedInAnyVehicle(playerPed, false) then @@ -679,6 +798,7 @@ AddEventHandler('esx_mecanojob:hasEnteredMarker', function(zone) CurrentActionData = {} end end + end) AddEventHandler('esx_mecanojob:hasExitedMarker', function(zone) @@ -729,6 +849,46 @@ AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) end) +-- Pop NPC mission vehicle when inside area +Citizen.CreateThread(function() + while true do + + Wait(0) + + if NPCTargetTowableZone ~= nil and not NPCHasSpawnedTowable then + + local coords = GetEntityCoords(GetPlayerPed(-1)) + local zone = Config.Zones[NPCTargetTowableZone] + + if GetDistanceBetweenCoords(coords, zone.Pos.x, zone.Pos.y, zone.Pos.z, true) < Config.NPCSpawnDistance then + + local model = Config.Vehicles[GetRandomIntInRange(1, #Config.Vehicles)] + + ESX.Game.SpawnVehicle(model, zone.Pos, 0, function(vehicle) + NPCTargetTowable = vehicle + end) + + NPCHasSpawnedTowable = true + + end + + end + + if NPCTargetTowableZone ~= nil and NPCHasSpawnedTowable and not NPCHasBeenNextToTowable then + + local coords = GetEntityCoords(GetPlayerPed(-1)) + local zone = Config.Zones[NPCTargetTowableZone] + + if(GetDistanceBetweenCoords(coords, zone.Pos.x, zone.Pos.y, zone.Pos.z, true) < Config.NPCNextToDistance) then + ESX.ShowNotification('Veuillez ~y~remorquer~s~ le véhicule') + NPCHasBeenNextToTowable = true + end + + end + + end +end) + -- Create Blips Citizen.CreateThread(function() local blip = AddBlipForCoord(Config.Zones.MecanoActions.Pos.x, Config.Zones.MecanoActions.Pos.y, Config.Zones.MecanoActions.Pos.z) @@ -823,6 +983,7 @@ end) Citizen.CreateThread(function() while true do Citizen.Wait(0) + if CurrentAction ~= nil then SetTextComponentFormat('STRING') AddTextComponentString(CurrentActionMsg) @@ -857,8 +1018,34 @@ Citizen.CreateThread(function() end end - if IsControlJustReleased(0, 167) and PlayerData.job ~= nil and PlayerData.job.name == 'mecano' then + if IsControlJustReleased(0, Keys['F6']) and PlayerData.job ~= nil and PlayerData.job.name == 'mecano' then OpenMobileMecanoActionsMenu() end + + if IsControlJustReleased(0, Keys['DELETE']) and PlayerData.job ~= nil and PlayerData.job.name == 'mecano' then + + if NPCOnJob then + + if GetGameTimer() - NPCLastCancel > 5 * 60000 then + StopNPCJob(true) + -- NPCLastCancel = GetGameTimer() + else + ESX.ShowNotification('Vous devez ~r~attendre~s~ 5 minutes') + end + + else + + local playerPed = GetPlayerPed(-1) + + if IsPedInAnyVehicle(playerPed, false) and IsVehicleModel(GetVehiclePedIsIn(playerPed, false), GetHashKey("flatbed")) then + StartNPCJob() + else + ESX.ShowNotification('Vous devez être en flatbed pour commencer la mission') + end + + end + + end + end end) \ No newline at end of file diff --git a/config.lua b/config.lua index ffb65d2b..96d25931 100644 --- a/config.lua +++ b/config.lua @@ -2,11 +2,14 @@ Config = {} Config.DrawDistance = 100.0 Config.MaxInService = -1 Config.EnablePlayerManagement = true +Config.NPCSpawnDistance = 500.0 +Config.NPCNextToDistance = 25.0 +Config.NPCJobEarnings = {min = 15, max = 40} Config.Zones = { MecanoActions = { - Pos = {x = -205.90548706055, y = -1327.7248535156, z = 29.89040184021}, + Pos = {x = -347.291, y = -133.370, z = 38.009}, Size = {x = 1.5, y = 1.5, z = 1.0}, Color = {r = 204, g = 204, b = 0}, Type = 1 @@ -20,23 +23,92 @@ Config.Zones = { }, Craft = { - Pos = {x = -196.51593017578, y = -1318.4403076172, z = 30.089345932007}, + Pos = {x = -323.140, y = -129.882, z = 37.999}, Size = {x = 1.5, y = 1.5, z = 1.0}, Color = {r = 204, g = 204, b = 0}, Type = 1 }, VehicleSpawnPoint = { - Pos = {x = -157.7899017334, y = -1305.8979492188, z = 30.348382949829}, + Pos = {x = -366.354, y = -110.766, z = 37.696}, Size = {x = 1.5, y = 1.5, z = 1.0}, Type = -1 }, VehicleDeleter = { - Pos = {x = -182.78112792969, y = -1330.5368652344, z = 30.194892883301}, + Pos = {x = -386.899, y = -105.675, z = 37.683}, Size = {x = 3.0, y = 3.0, z = 1.0}, Color = {r = 204, g = 204, b = 0}, Type = 1 - } + }, + + VehicleDelivery = { + Pos = {x = -382.925, y = -133.748, z = 37.685}, + Size = {x = 20.0, y = 20.0, z = 3.0}, + Color = {r = 204, g = 204, b = 0}, + Type = -1 + }, } + +Config.Towables = { + { ['x'] = -2480.8720703125, ['y'] = -211.96409606934, ['z'] = 17.397672653198 }, + { ['x'] = -2723.392578125, ['y'] = 13.207388877869, ['z'] = 15.12806892395 }, + { ['x'] = -3169.6235351563, ['y'] = 976.18127441406, ['z'] = 15.038360595703 }, + { ['x'] = -3139.7568359375, ['y'] = 1078.7182617188, ['z'] = 20.189767837524 }, + { ['x'] = -1656.9357910156, ['y'] = -246.16479492188, ['z'] = 54.510955810547 }, + { ['x'] = -1586.6560058594, ['y'] = -647.56115722656, ['z'] = 29.441320419312 }, + { ['x'] = -1036.1470947266, ['y'] = -491.05856323242, ['z'] = 36.214912414551 }, + { ['x'] = -1029.1884765625, ['y'] = -475.53167724609, ['z'] = 36.416831970215 }, + { ['x'] = 75.212287902832, ['y'] = 164.8522644043, ['z'] = 104.69123077393 }, + { ['x'] = -534.60491943359, ['y'] = -756.71801757813, ['z'] = 31.599143981934 }, + { ['x'] = 487.24212646484, ['y'] = -30.827201843262, ['z'] = 88.856712341309 }, + { ['x'] = -772.20111083984, ['y'] = -1281.8114013672, ['z'] = 4.5642876625061 }, + { ['x'] = -663.84173583984, ['y'] = -1206.9936523438, ['z'] = 10.171216011047 }, + { ['x'] = 719.12451171875, ['y'] = -767.77545166016, ['z'] = 24.892364501953 }, + { ['x'] = -970.95465087891, ['y'] = -2410.4453125, ['z'] = 13.344270706177 }, + { ['x'] = -1067.5234375, ['y'] = -2571.4064941406, ['z'] = 13.211874008179 }, + { ['x'] = -619.23968505859, ['y'] = -2207.2927246094, ['z'] = 5.5659561157227 }, + { ['x'] = 1192.0831298828, ['y'] = -1336.9086914063, ['z'] = 35.106426239014 }, + { ['x'] = -432.81033325195, ['y'] = -2166.0505371094, ['z'] = 9.8885231018066 }, + { ['x'] = -451.82403564453, ['y'] = -2269.34765625, ['z'] = 7.1719741821289 }, + { ['x'] = 939.26702880859, ['y'] = -2197.5390625, ['z'] = 30.546691894531 }, + { ['x'] = -556.11486816406, ['y'] = -1794.7312011719, ['z'] = 22.043060302734 }, + { ['x'] = 591.73504638672, ['y'] = -2628.2197265625, ['z'] = 5.5735430717468 }, + { ['x'] = 1654.515625, ['y'] = -2535.8325195313, ['z'] = 74.491394042969 }, + { ['x'] = 1642.6146240234, ['y'] = -2413.3159179688, ['z'] = 93.139915466309 }, + { ['x'] = 1371.3223876953, ['y'] = -2549.525390625, ['z'] = 47.575256347656 }, + { ['x'] = 383.83779907227, ['y'] = -1652.8695068359, ['z'] = 37.278503417969 }, + { ['x'] = 27.219129562378, ['y'] = -1030.8818359375, ['z'] = 29.414621353149 }, + { ['x'] = 229.26435852051, ['y'] = -365.91101074219, ['z'] = 43.750762939453 }, + { ['x'] = -85.809432983398, ['y'] = -51.665500640869, ['z'] = 61.10591506958 }, + { ['x'] = -4.5967531204224, ['y'] = -670.27124023438, ['z'] = 31.85863494873 }, + { ['x'] = -111.89884185791, ['y'] = 91.96940612793, ['z'] = 71.080169677734 }, + { ['x'] = -314.26129150391, ['y'] = -698.23309326172, ['z'] = 32.545776367188 }, + { ['x'] = -366.90979003906, ['y'] = 115.53963470459, ['z'] = 65.575706481934 }, + { ['x'] = -592.06726074219, ['y'] = 138.20733642578, ['z'] = 60.074813842773 }, + { ['x'] = -1613.8572998047, ['y'] = 18.759860992432, ['z'] = 61.799819946289 }, + { ['x'] = -1709.7995605469, ['y'] = 55.105819702148, ['z'] = 65.706237792969 }, + { ['x'] = -521.88830566406, ['y'] = -266.7805480957, ['z'] = 34.940990447998 }, + { ['x'] = -451.08666992188, ['y'] = -333.52026367188, ['z'] = 34.021533966064 }, + { ['x'] = 322.36480712891, ['y'] = -1900.4990234375, ['z'] = 25.773607254028 }, +} + +for i=1, #Config.Towables, 1 do + + Config.Zones['Towable' .. i] = { + Pos = Config.Towables[i], + Size = {x = 1.5, y = 1.5, z = 1.0}, + Color = {r = 204, g = 204, b = 0}, + Type = -1 + } + +end + +Config.Vehicles = { + 'adder', + 'asea', + 'asterope', + 'banshee', + 'buffalo' +} diff --git a/server/main.lua b/server/main.lua index 3edee613..0e2ffb1d 100644 --- a/server/main.lua +++ b/server/main.lua @@ -233,6 +233,27 @@ AddEventHandler('esx_mecanojob:stopCraft3', function() PlayersCrafting3[_source] = false end) +---------------------------- NPC Job Earnings ------------------------------------------------------ + +RegisterServerEvent('esx_mecanojob:onNPCJobMissionCompleted') +AddEventHandler('esx_mecanojob:onNPCJobMissionCompleted', function() + + local _source = source + local xPlayer = ESX.GetPlayerFromId(_source) + local total = math.random(Config.NPCJobEarnings.min, Config.NPCJobEarnings.max); + + if xPlayer.job.grade >= 3 then + total = total * 2 + end + + TriggerEvent('esx_addonaccount:getSharedAccount', 'society_mecano', function(account) + account.addMoney(total) + end) + + TriggerClientEvent("esx:showNotification", _source, "Votre société a ~g~gagné~s~ ~g~$".. total) + +end) + ---------------------------- register usable item -------------------------------------------------- ESX.RegisterUsableItem('blowpipe', function(source)