├── README.md ├── _INSTALL ├── Images │ ├── baseball.png │ ├── basketball.png │ ├── football.png │ └── soccer.png └── Items │ ├── esx_limit.sql │ ├── esx_weight.sql │ ├── ox_inventory.lua │ └── qbcore.lua ├── bridge ├── esx │ ├── client.lua │ └── server.lua ├── qb │ ├── client.lua │ └── server.lua └── standalone │ ├── client.lua │ └── server.lua ├── config.lua ├── core ├── client.lua └── shared.lua ├── fxmanifest.lua ├── locales ├── locale.lua └── translations │ ├── de.lua │ └── en.lua └── modules └── throwables ├── client.lua └── server.lua /README.md: -------------------------------------------------------------------------------- 1 | # pickle_throwables 2 | A multi-framework and standalone throwing script, great for football, soccer and other sports. 3 | -------------------------------------------------------------------------------- /_INSTALL/Images/baseball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PickleModifications/pickle_throwables/e0e576401bae6d5d4561edfb3c86784cc9925c96/_INSTALL/Images/baseball.png -------------------------------------------------------------------------------- /_INSTALL/Images/basketball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PickleModifications/pickle_throwables/e0e576401bae6d5d4561edfb3c86784cc9925c96/_INSTALL/Images/basketball.png -------------------------------------------------------------------------------- /_INSTALL/Images/football.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PickleModifications/pickle_throwables/e0e576401bae6d5d4561edfb3c86784cc9925c96/_INSTALL/Images/football.png -------------------------------------------------------------------------------- /_INSTALL/Images/soccer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PickleModifications/pickle_throwables/e0e576401bae6d5d4561edfb3c86784cc9925c96/_INSTALL/Images/soccer.png -------------------------------------------------------------------------------- /_INSTALL/Items/esx_limit.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `items` (`name`, `label`, `limit`) VALUES 2 | ('football', 'Football', 100), 3 | ('basketball', 'Basketball', 100), 4 | ('baseball', 'Baseball', 100), 5 | ('soccer', 'Soccer Ball', 100), 6 | ; -------------------------------------------------------------------------------- /_INSTALL/Items/esx_weight.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `items` (`name`, `label`, `weight`) VALUES 2 | ('football', 'Football', 100), 3 | ('basketball', 'Basketball', 100), 4 | ('baseball', 'Baseball', 100), 5 | ('soccer', 'Soccer Ball', 100), 6 | ; -------------------------------------------------------------------------------- /_INSTALL/Items/ox_inventory.lua: -------------------------------------------------------------------------------- 1 | ["football"] = { 2 | label = 'Football', 3 | weight = 1, 4 | stack = true, 5 | description = "" 6 | }, 7 | ["basketball"] = { 8 | label = 'Basketball', 9 | weight = 1, 10 | stack = true, 11 | description = "" 12 | }, 13 | ["baseball"] = { 14 | label = 'Baseball', 15 | weight = 1, 16 | stack = true, 17 | description = "" 18 | }, 19 | ["soccer"] = { 20 | label = 'Soccer Ball', 21 | weight = 1, 22 | stack = true, 23 | description = "" 24 | }, -------------------------------------------------------------------------------- /_INSTALL/Items/qbcore.lua: -------------------------------------------------------------------------------- 1 | ["football"] = {["name"] = "football", ["label"] = "Football", ["weight"] = 1, ["type"] = "item", ["image"] = "football.png", ["unique"] = true, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = ""}, 2 | ["basketball"] = {["name"] = "basketball", ["label"] = "Basketball", ["weight"] = 1, ["type"] = "item", ["image"] = "basketball.png", ["unique"] = true, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = ""}, 3 | ["baseball"] = {["name"] = "baseball", ["label"] = "Baseball", ["weight"] = 1, ["type"] = "item", ["image"] = "baseball.png", ["unique"] = true, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = ""}, 4 | ["soccer"] = {["name"] = "soccer", ["label"] = "Soccer Ball", ["weight"] = 1, ["type"] = "item", ["image"] = "soccer.png", ["unique"] = true, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = ""}, -------------------------------------------------------------------------------- /bridge/esx/client.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | ESX = exports.es_extended:getSharedObject() 4 | 5 | function ShowNotification(text) 6 | ESX.ShowNotification(text) 7 | end 8 | 9 | function ShowHelpNotification(text) 10 | ESX.ShowHelpNotification(text) 11 | end 12 | 13 | function ServerCallback(name, cb, ...) 14 | ESX.TriggerServerCallback(name, cb, ...) 15 | end 16 | 17 | function GetPlayersInArea(coords, maxDistance) 18 | return ESX.Game.GetPlayersInArea(coords, maxDistance) 19 | end 20 | 21 | function CanAccessGroup(data) 22 | if not data then return true end 23 | local pdata = ESX.GetPlayerData() 24 | for k,v in pairs(data) do 25 | if (pdata.job.name == k and pdata.job.grade >= v) then return true end 26 | end 27 | return false 28 | end 29 | 30 | RegisterNetEvent(GetCurrentResourceName()..":showNotification", function(text) 31 | ShowNotification(text) 32 | end) -------------------------------------------------------------------------------- /bridge/esx/server.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') ~= 'started' then return end 2 | 3 | ESX = exports.es_extended:getSharedObject() 4 | 5 | function RegisterCallback(name, cb) 6 | ESX.RegisterServerCallback(name, cb) 7 | end 8 | 9 | function RegisterUsableItem(...) 10 | ESX.RegisterUsableItem(...) 11 | end 12 | 13 | function ShowNotification(target, text) 14 | TriggerClientEvent(GetCurrentResourceName()..":showNotification", target, text) 15 | end 16 | 17 | function Search(source, name) 18 | local xPlayer = ESX.GetPlayerFromId(source) 19 | if (name == "money") then 20 | return xPlayer.getMoney() 21 | elseif (name == "bank") then 22 | return xPlayer.getAccount('bank').money 23 | else 24 | local item = xPlayer.getInventoryItem(name) 25 | if item ~= nil then 26 | return item.count 27 | else 28 | return 0 29 | end 30 | end 31 | end 32 | 33 | function AddItem(source, name, amount) 34 | local xPlayer = ESX.GetPlayerFromId(source) 35 | if (name == "money") then 36 | return xPlayer.addMoney(amount) 37 | elseif (name == "bank") then 38 | return xPlayer.addAccountMoney('bank', amount) 39 | else 40 | return xPlayer.addInventoryItem(name, amount) 41 | end 42 | end 43 | 44 | function RemoveItem(source, name, amount) 45 | local xPlayer = ESX.GetPlayerFromId(source) 46 | if (name == "money") then 47 | return xPlayer.removeMoney(amount) 48 | elseif (name == "bank") then 49 | return xPlayer.removeAccountMoney('bank', amount) 50 | else 51 | return xPlayer.removeInventoryItem(name, amount) 52 | end 53 | end 54 | 55 | function CanAccessGroup(source, data) 56 | if not data then return true end 57 | local pdata = ESX.GetPlayerFromId(source) 58 | for k,v in pairs(data) do 59 | if (pdata.job.name == k and pdata.job.grade >= v) then return true end 60 | end 61 | return false 62 | end 63 | 64 | function GetIdentifier(source) 65 | local xPlayer = ESX.GetPlayerFromId(source) 66 | return xPlayer.identifier 67 | end -------------------------------------------------------------------------------- /bridge/qb/client.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' then return end 2 | 3 | QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | function ServerCallback(name, cb, ...) 6 | QBCore.Functions.TriggerCallback(name, cb, ...) 7 | end 8 | 9 | function ShowNotification(text) 10 | QBCore.Functions.Notify(text) 11 | end 12 | 13 | function ShowHelpNotification(text) 14 | AddTextEntry('qbHelpNotification', text) 15 | BeginTextCommandDisplayHelp('qbHelpNotification') 16 | EndTextCommandDisplayHelp(0, false, false, -1) 17 | end 18 | 19 | function GetPlayersInArea(coords, maxDistance) 20 | return QBCore.Functions.GetPlayersFromCoords(coords, maxDistance) 21 | end 22 | 23 | function CanAccessGroup(data) 24 | if not data then return true end 25 | local pdata = QBCore.Functions.GetPlayerData() 26 | for k,v in pairs(data) do 27 | if (pdata.job.name == k and pdata.job.grade.level >= v) then return true end 28 | end 29 | return false 30 | end 31 | 32 | RegisterNetEvent(GetCurrentResourceName()..":showNotification", function(text) 33 | ShowNotification(text) 34 | end) -------------------------------------------------------------------------------- /bridge/qb/server.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('qb-core') ~= 'started' then return end 2 | 3 | QBCore = exports['qb-core']:GetCoreObject() 4 | 5 | function RegisterCallback(name, cb) 6 | QBCore.Functions.CreateCallback(name, cb) 7 | end 8 | 9 | function RegisterUsableItem(...) 10 | QBCore.Functions.CreateUseableItem(...) 11 | end 12 | 13 | function ShowNotification(target, text) 14 | TriggerClientEvent(GetCurrentResourceName()..":showNotification", target, text) 15 | end 16 | 17 | function Search(source, name) 18 | local xPlayer = QBCore.Functions.GetPlayer(source) 19 | if (name == "money") then 20 | return xPlayer.PlayerData.money['cash'] 21 | elseif (name == "bank") then 22 | return xPlayer.PlayerData.money['cash'] -- If anyone knows how to get bank balance for QBCore, let me know. 23 | else 24 | local item = xPlayer.Functions.GetItemByName(name) 25 | if item ~= nil then 26 | return item.amount 27 | else 28 | return 0 29 | end 30 | end 31 | end 32 | 33 | function AddItem(source, name, amount) 34 | local xPlayer = QBCore.Functions.GetPlayer(source) 35 | if (name == "money") then 36 | return xPlayer.Functions.AddMoney("cash", amount) 37 | elseif (name == "bank") then 38 | return xPlayer.Functions.AddMoney("cash", amount) -- If anyone knows how to add to bank balance for QBCore, let me know. 39 | else 40 | return xPlayer.Functions.AddItem(name, amount) 41 | end 42 | end 43 | 44 | function RemoveItem(source, name, amount) 45 | local xPlayer = QBCore.Functions.GetPlayer(source) 46 | if (name == "money") then 47 | return xPlayer.Functions.RemoveMoney("cash", amount) 48 | elseif (name == "bank") then 49 | return xPlayer.Functions.RemoveMoney("cash", amount) -- If anyone knows how to remove from bank balance for QBCore, let me know. 50 | else 51 | return xPlayer.Functions.RemoveItem(name, amount) 52 | end 53 | end 54 | 55 | function CanAccessGroup(source, data) 56 | if not data then return true end 57 | local pdata = QBCore.Functions.GetPlayer(source).PlayerData 58 | for k,v in pairs(data) do 59 | if (pdata.job.name == k and pdata.job.grade.level >= v) then return true end 60 | end 61 | return false 62 | end 63 | 64 | function GetIdentifier(source) 65 | local xPlayer = QBCore.Functions.GetPlayer(source).PlayerData 66 | return xPlayer.citizenid 67 | end -------------------------------------------------------------------------------- /bridge/standalone/client.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') == 'started' then return end 2 | if GetResourceState('qb-core') == 'started' then return end 3 | 4 | print("You are not using a supported framework, it will be required to make edits to the bridge files.") -------------------------------------------------------------------------------- /bridge/standalone/server.lua: -------------------------------------------------------------------------------- 1 | if GetResourceState('es_extended') == 'started' then return end 2 | if GetResourceState('qb-core') == 'started' then return end 3 | 4 | print("You are not using a supported framework, it will be required to make edits to the bridge files.") -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | Config.Debug = true 4 | 5 | Config.Language = "en" 6 | 7 | Config.RenderDistance = 20.0 8 | Config.CatchRadius = 2.5 9 | 10 | Config.CommandSpawning = false -- Set this to true if you want to be able to get throwables without using items. 11 | 12 | Config.CommandSpawnCheck = function() 13 | return true 14 | end 15 | 16 | Config.Throwables = { 17 | ["football"] = { 18 | item = "football", 19 | entityType = "object", -- "object", "vehicle", "ped" 20 | model = `p_ld_am_ball_01`, 21 | maxThrowingPower = 200 22 | }, 23 | ["basketball"] = { 24 | item = "basketball", 25 | entityType = "object", -- "object", "vehicle", "ped" 26 | model = `prop_bskball_01`, 27 | maxThrowingPower = 200 28 | }, 29 | ["baseball"] = { 30 | item = "baseball", 31 | entityType = "object", -- "object", "vehicle", "ped" 32 | model = `w_am_baseball`, 33 | maxThrowingPower = 200 34 | }, 35 | ["soccer"] = { 36 | item = "soccer", 37 | entityType = "object", -- "object", "vehicle", "ped" 38 | model = `p_ld_soc_ball_01`, 39 | maxThrowingPower = 200 40 | }, 41 | } -------------------------------------------------------------------------------- /core/client.lua: -------------------------------------------------------------------------------- 1 | function ModelRequest(modelHash) 2 | if not IsModelInCdimage(modelHash) then return end 3 | RequestModel(modelHash) 4 | local loaded 5 | for i=1, 100 do 6 | if HasModelLoaded(modelHash) then 7 | loaded = true 8 | break 9 | end 10 | Wait(100) 11 | end 12 | return loaded 13 | end 14 | 15 | function CreateVeh(modelHash, ...) 16 | if not ModelRequest(modelHash) then 17 | print("Couldn't load model: " .. modelHash) 18 | return 19 | end 20 | local veh = CreateVehicle(modelHash, ...) 21 | SetModelAsNoLongerNeeded(modelHash) 22 | return veh 23 | end 24 | 25 | function CreateNPC(modelHash, ...) 26 | if not ModelRequest(modelHash) then 27 | print("Couldn't load model: " .. modelHash) 28 | return 29 | end 30 | local ped = CreatePed(26, modelHash, ...) 31 | SetModelAsNoLongerNeeded(modelHash) 32 | return ped 33 | end 34 | 35 | function CreateProp(modelHash, ...) 36 | if not ModelRequest(modelHash) then 37 | print("Couldn't load model: " .. modelHash) 38 | return 39 | end 40 | local obj = CreateObject(modelHash, ...) 41 | SetModelAsNoLongerNeeded(modelHash) 42 | return obj 43 | end 44 | 45 | function PlayAnim(ped, dict, ...) 46 | RequestAnimDict(dict) 47 | while not HasAnimDictLoaded(dict) do Wait(0) end 48 | TaskPlayAnim(ped, dict, ...) 49 | end 50 | 51 | local interactTick = 0 52 | local interactThread = false 53 | local interactText = nil 54 | 55 | function ShowInteractText(text) 56 | interactTick = GetGameTimer() 57 | lib.showTextUI(text) 58 | if interactThread then return end 59 | interactThread = true 60 | CreateThread(function() 61 | while interactThread do 62 | if GetGameTimer() - interactTick > 20 then 63 | interactThread = false 64 | break 65 | end 66 | Citizen.Wait(150) 67 | end 68 | lib.hideTextUI() 69 | end) 70 | end -------------------------------------------------------------------------------- /core/shared.lua: -------------------------------------------------------------------------------- 1 | function v3(coords) return vec3(coords.x, coords.y, coords.z), coords.w end 2 | 3 | function GetRandomInt(min, max, exclude) 4 | for i=1, 1000 do 5 | local int = math.random(min, max) 6 | if exclude == nil or exclude ~= int then 7 | return int 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | lua54 'yes' 3 | game 'gta5' 4 | 5 | name 'pickle_throwables' 6 | version '1.0.1' 7 | description 'A multi-framework and standalone throwing script, great for football, soccer and other sports.' 8 | author 'Pickle Mods' 9 | 10 | shared_scripts { 11 | '@ox_lib/init.lua', 12 | 'config.lua', 13 | 'core/shared.lua', 14 | "locales/locale.lua", 15 | "locales/translations/*.lua", 16 | 'modules/**/shared.lua', 17 | } 18 | 19 | server_scripts { 20 | 'bridge/**/server.lua', 21 | 'modules/**/server.lua', 22 | } 23 | 24 | client_scripts { 25 | 'core/client.lua', 26 | 'bridge/**/client.lua', 27 | 'modules/**/client.lua', 28 | } 29 | -------------------------------------------------------------------------------- /locales/locale.lua: -------------------------------------------------------------------------------- 1 | Language = {} 2 | 3 | function _L(name, ...) 4 | if name then 5 | local str = Language[Config.Language][name] 6 | if str then 7 | return string.format(str, ...) 8 | else 9 | return "ERR_TRANSLATE_"..(name).."_404" 10 | end 11 | else 12 | return "ERR_TRANSLATE_404" 13 | end 14 | end -------------------------------------------------------------------------------- /locales/translations/de.lua: -------------------------------------------------------------------------------- 1 | Language["de"] = { 2 | throwable_interact = "[E] - Aufnehmen \n [G] - Treten \n Power: %s", 3 | throwable_list = "[E] - Werfen \n [G] - Ablegen \n [H] - Übergeben \n Power: %s", 4 | throwable_list_alt = "[E] - Werfen \n [G] - Ablegen \n [H] - Inventar \n Power: %s", 5 | } -------------------------------------------------------------------------------- /locales/translations/en.lua: -------------------------------------------------------------------------------- 1 | Language["en"] = { 2 | throwable_interact = "[E] - Take Object \n [G] - Kick \n Power: %s", 3 | throwable_list = "[E] - Throw \n [G] - Drop \n [H] - Handoff \n Power: %s", 4 | throwable_list_alt = "[E] - Throw \n [G] - Drop \n [H] - Inventory \n Power: %s", 5 | } -------------------------------------------------------------------------------- /modules/throwables/client.lua: -------------------------------------------------------------------------------- 1 | local ThrowingPower = 1 2 | local Throwables = {} 3 | local canInteract = true 4 | local attemptingCatch = false 5 | local holdingBall = nil 6 | 7 | function GetClosestPlayer(coords, radius) 8 | local closest 9 | local coords = coords or GetEntityCoords(PlayerPedId()) 10 | local radius = radius or 2.0 11 | for _, player in ipairs(GetActivePlayers()) do 12 | local ped = GetPlayerPed(player) 13 | if PlayerPedId() ~= ped then 14 | local pedCoords = GetEntityCoords(ped) 15 | local distance = #(coords - pedCoords) 16 | if distance < radius and (not closest or closest.distance > distance) then 17 | closest = {player = player, distance = distance} 18 | end 19 | end 20 | end 21 | return closest?.player, closest?.distance 22 | end 23 | 24 | function GetDirectionFromRotation(rotation) 25 | local dm = (math.pi / 180) 26 | return vector3(-math.sin(dm * rotation.z) * math.abs(math.cos(dm * rotation.x)), math.cos(dm * rotation.z) * math.abs(math.cos(dm * rotation.x)), math.sin(dm * rotation.x)) 27 | end 28 | 29 | function PerformPhysics(throwType, entity, action) 30 | local cfg = Config.Throwables[throwType] 31 | local power = (ThrowingPower / 10) * cfg.maxThrowingPower 32 | FreezeEntityPosition(entity, false) 33 | local rot = GetGameplayCamRot(2) 34 | local dir = GetDirectionFromRotation(rot) 35 | SetEntityHeading(entity, rot.z + 90.0) 36 | if not action or action == "throw" then 37 | SetEntityVelocity(entity, dir.x * power, dir.y * power, dir.z * power) 38 | else 39 | SetEntityVelocity(entity, dir.x * power, dir.y * power, (dir.z * 1.75) * power) 40 | end 41 | end 42 | 43 | function CreateThrowable(throwType, attach) 44 | local cfg = Config.Throwables[throwType] 45 | local ped = PlayerPedId() 46 | local model = cfg.model 47 | local heading = GetEntityHeading(ped) 48 | local coords = GetOffsetFromEntityInWorldCoords(ped, 0.0, 1.0, 0.5) 49 | local prop 50 | if cfg.entityType == "object" then 51 | prop = CreateProp(model, coords.x, coords.y, coords.z, true, true, true) 52 | elseif cfg.entityType == "vehicle" then 53 | prop = CreateVeh(model, coords.x, coords.y, coords.z, true, true, true) 54 | elseif cfg.entityType == "ped" then 55 | prop = CreateNPC(model, coords.x, coords.y, coords.z, true, true, true) 56 | end 57 | if not prop then return end 58 | if attach then 59 | local off, rot = vector3(0.05, 0.0, -0.085), vector3(90.0, 90.0, 0.0) 60 | AttachEntityToEntity(prop, ped, GetPedBoneIndex(ped, 28422), off.x, off.y, off.z, rot.x, rot.y, rot.z, false, false, false, true, 2, true) 61 | else 62 | local coords = GetOffsetFromEntityInWorldCoords(ped, 0.0, 1.0, -0.9) 63 | SetEntityCoords(prop, coords.x, coords.y, coords.z) 64 | end 65 | return prop 66 | end 67 | 68 | function HoldThrowable(throwType) 69 | local ped = PlayerPedId() 70 | if holdingBall then return end 71 | local prop = CreateThrowable(throwType, true) 72 | holdingBall = prop 73 | CreateThread(function() 74 | while holdingBall do 75 | local player, dist = GetClosestPlayer() 76 | if player then 77 | ShowInteractText(_L("throwable_list", ThrowingPower .. "/" .. 10)) 78 | else 79 | ShowInteractText(_L("throwable_list_alt", ThrowingPower .. "/" .. 10)) 80 | end 81 | if IsControlJustPressed(1, 51) then 82 | CreateThread(function() 83 | PlayAnim(ped, "melee@thrown@streamed_core", "plyr_takedown_front", -8.0, 8.0, -1, 49) 84 | Wait(600) 85 | ClearPedTasks(ped) 86 | end) 87 | Wait(550) 88 | DetachEntity(prop, false, true) 89 | SetEntityCollision(prop, true, true) 90 | SetEntityRecordsCollisions(prop, true) 91 | TriggerServerEvent("pickle_throwables:throwObject", {throwType = throwType, net_id = ObjToNet(prop)}) 92 | local coords = GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.0, 1.0) 93 | SetEntityCoords(prop, coords.x, coords.y, coords.z) 94 | SetEntityHeading(prop, GetEntityHeading(ped) + 90.0) 95 | PerformPhysics(throwType, prop) 96 | holdingBall = nil 97 | elseif IsControlJustPressed(1, 47) then 98 | PlayAnim(ped, "pickup_object", "pickup_low", -8.0, 8.0, -1, 49, 1.0) 99 | Wait(800) 100 | DetachEntity(prop, true, true) 101 | SetEntityCollision(prop, true, true) 102 | SetEntityRecordsCollisions(prop, true) 103 | ActivatePhysics(prop) 104 | TriggerServerEvent("pickle_throwables:throwObject", {throwType = throwType, net_id = ObjToNet(prop)}) 105 | Wait(800) 106 | ClearPedTasks(ped) 107 | holdingBall = nil 108 | elseif IsControlJustPressed(1, 74) then 109 | if player then 110 | ServerCallback("pickle_throwables:giveObject", function(result) 111 | if not result then return end 112 | DeleteEntity(prop) 113 | holdingBall = nil 114 | PlayAnim(PlayerPedId(), "mp_common", "givetake1_b", -8.0, 8.0, -1, 49, 1.0) 115 | Wait(1600) 116 | ClearPedTasks(ped) 117 | end, GetPlayerServerId(player)) 118 | else 119 | ServerCallback("pickle_throwables:storeObject", function(result) 120 | if not result then return end 121 | PlayAnim(PlayerPedId(), "pickup_object", "putdown_low", -8.0, 8.0, -1, 49, 1.0) 122 | Wait(1600) 123 | ClearPedTasks(ped) 124 | DeleteEntity(prop) 125 | holdingBall = nil 126 | end) 127 | end 128 | end 129 | PowerControls() 130 | Wait(0) 131 | end 132 | end) 133 | end 134 | 135 | function CatchObject(index, cb) 136 | if attemptingCatch then return end 137 | attemptingCatch = true 138 | local data = Throwables[index] 139 | local entity = NetToObj(data.net_id) 140 | SetEntityCollision(entity, false, false) 141 | DeleteEntity(entity) 142 | ServerCallback("pickle_throwables:catchObject", cb, index) 143 | Wait(100) 144 | attemptingCatch = false 145 | end 146 | 147 | function PowerControls() 148 | if IsControlJustPressed(1, 181) then 149 | ThrowingPower = (ThrowingPower + 1 > 10 and 10 or ThrowingPower + 1) 150 | elseif IsControlJustPressed(1, 180) then 151 | ThrowingPower = (ThrowingPower - 1 < 1 and 1 or ThrowingPower - 1) 152 | end 153 | end 154 | 155 | function deepcopy(orig) 156 | local orig_type = type(orig) 157 | local copy 158 | if orig_type == 'table' then 159 | copy = {} 160 | for orig_key, orig_value in next, orig, nil do 161 | copy[deepcopy(orig_key)] = deepcopy(orig_value) 162 | end 163 | setmetatable(copy, deepcopy(getmetatable(orig))) 164 | else -- number, string, boolean, etc 165 | copy = orig 166 | end 167 | return copy 168 | end 169 | 170 | CreateThread(function() 171 | while true do 172 | local wait = 1000 173 | local ped = PlayerPedId() 174 | local throwables = deepcopy(Throwables) 175 | for k,v in pairs(throwables) do 176 | if NetworkDoesNetworkIdExist(v.net_id) then 177 | local entity = NetToObj(v.net_id) 178 | local dist = #(GetEntityCoords(ped) - GetEntityCoords(entity)) 179 | if dist < Config.RenderDistance then 180 | wait = 0 181 | if not holdingBall and canInteract and dist < Config.CatchRadius and not ShowInteractText(_L("throwable_interact", ThrowingPower .. "/" .. 10)) then 182 | if IsControlJustPressed(1, 51) then 183 | CatchObject(k, function(result) 184 | if not result then return end 185 | HoldThrowable(v.throwType) 186 | end) 187 | elseif IsControlJustPressed(1, 47) then 188 | CatchObject(k, function(result) 189 | if not result then return end 190 | canInteract = false 191 | local prop = CreateThrowable(v.throwType, false) 192 | TriggerServerEvent("pickle_throwables:throwObject", {throwType = v.throwType, net_id = ObjToNet(prop)}) 193 | --FreezeEntityPosition(ped, true) 194 | --PlayAnim(ped, "melee@unarmed@streamed_core", "ground_attack_0", -8.0, 8.0, -1, 33, 1.0) 195 | --Wait(1000) 196 | PerformPhysics(v.throwType, prop, "kick") 197 | --Wait(600) 198 | --ClearPedTasks(ped) 199 | --FreezeEntityPosition(ped, false) 200 | canInteract = true 201 | end) 202 | end 203 | PowerControls() 204 | end 205 | end 206 | end 207 | end 208 | Wait(wait) 209 | end 210 | end) 211 | 212 | RegisterNetEvent("pickle_throwables:giveObject", function(data) 213 | HoldThrowable(data.throwType) 214 | end) 215 | 216 | RegisterNetEvent("pickle_throwables:setObjectData", function(throwID, data) 217 | Throwables[throwID] = data 218 | end) 219 | 220 | AddEventHandler("onResourceStop", function(name) 221 | if (GetCurrentResourceName() ~= name) then return end 222 | for k,v in pairs(Throwables) do 223 | DeleteEntity(NetToObj(v.net_id)) 224 | end 225 | end) 226 | -------------------------------------------------------------------------------- /modules/throwables/server.lua: -------------------------------------------------------------------------------- 1 | local Throwables = {} 2 | local Carrying = {} 3 | 4 | function GiveObject(source, data, timeout) 5 | Carrying[source] = data 6 | if timeout then 7 | SetTimeout(1600, function() 8 | TriggerClientEvent("pickle_throwables:giveObject", source, data) 9 | end) 10 | else 11 | TriggerClientEvent("pickle_throwables:giveObject", source, data) 12 | end 13 | end 14 | 15 | RegisterNetEvent("pickle_throwables:throwObject", function(data) 16 | local source = source 17 | if not Carrying[source] then return end 18 | Carrying[source] = nil 19 | local throwID = nil 20 | repeat 21 | throwID = os.time() .. "_" .. math.random(1000, 9999) 22 | until not Throwables[throwID] 23 | Throwables[throwID] = data 24 | TriggerClientEvent("pickle_throwables:setObjectData", -1, throwID, data) 25 | end) 26 | 27 | RegisterCallback("pickle_throwables:catchObject", function(source, cb, throwID) 28 | if Carrying[source] then return cb(false) end 29 | if not Throwables[throwID] then return cb(false) end 30 | local entity = NetworkGetEntityFromNetworkId(Throwables[throwID].net_id) 31 | Carrying[source] = {throwType = Throwables[throwID].throwType} 32 | DeleteEntity(entity) 33 | Throwables[throwID] = nil 34 | TriggerClientEvent("pickle_throwables:setObjectData", -1, throwID, nil) 35 | cb(true) 36 | end) 37 | 38 | RegisterCallback("pickle_throwables:storeObject", function(source, cb) 39 | if not Carrying[source] then return cb(false) end 40 | local data = Carrying[source] 41 | local cfg = Config.Throwables[data.throwType] 42 | Carrying[source] = nil 43 | if cfg.item and not Config.CommandSpawning then 44 | AddItem(source, cfg.item, 1) 45 | end 46 | cb(true) 47 | end) 48 | 49 | RegisterCallback("pickle_throwables:giveObject", function(source, cb, target) 50 | if not Carrying[source] or Carrying[target] then return cb(false) end 51 | local data = Carrying[source] 52 | GiveObject(target, {throwType = data.throwType}, true) 53 | Carrying[source] = nil 54 | cb(true) 55 | end) 56 | 57 | if Config.CommandSpawning then 58 | RegisterCommand("spawnthrowable", function(source, args, raw) 59 | if not args[1] or not Config.Throwables[args[1]] then return end 60 | if not Config.CommandSpawnCheck(source, args[1]) then return end 61 | GiveObject(source, {throwType = args[1]}) 62 | end) 63 | else 64 | for k,v in pairs(Config.Throwables) do 65 | if v.item then 66 | RegisterUsableItem(v.item, function(source) 67 | if Carrying[source] then return end 68 | RemoveItem(source, v.item, 1) 69 | GiveObject(source, {throwType = k}) 70 | end) 71 | end 72 | end 73 | end --------------------------------------------------------------------------------