11 Commits

Author SHA1 Message Date
Jim Shield
2a4de6fdc1 Version Bump
`2.0.03` - `2.0.04`
2025-06-16 11:19:07 +01:00
Jim Shield
4b015e5efa Revamp hunger and thirst events
Realised I made a mistake, and the server function wasn't working fully on ESX

moved it to use the jim_bridge `setNeed` trigger and made the function smarter
2025-06-14 22:29:40 +01:00
Jim Shield
518c5204ae Version Bump
`2.0.02` - `2.0.03`
2025-05-22 14:25:48 +01:00
Jim Shield
12642b7e7b Temp fix consumables not being usable randomly
I believe, the imported consumables from other scripts were fighting with each other in the `createUsableItem` event

In theory, when syncing them (at least at the moment) the restaurant would try to make them usable, and also this script would try. Possibly cancelling each other out?

This removes the `createUsableItem` event from the sync add item event
2025-05-22 14:23:12 +01:00
Jim Shield
c2843eaee2 Try to fix hunger/thirst for essx 2025-05-22 14:12:27 +01:00
Jim Shield
b92e26129f Convert to globalstatebag syncing for optimization
When consumables are added from outside the script, it uses global statebags to ensure data is received successfully. This syncs live and for players who have just connected

This removes the callbacks in the script to help optimization
2025-05-22 14:10:30 +01:00
Jim Shield
129b65deda Update version.txt 2025-05-19 17:10:56 +01:00
Jim Shield
d83d0bbfc4 check and store variable
If `jim-consumables:Consume` was triggered by an item with no information it would error and possibly break the script

This a check to instantly `return` when no consumable information is found
2025-05-17 21:03:29 +01:00
Jim Shield
2b6d3f2825 Update README.md 2025-05-14 15:56:13 +01:00
Jim Shield
cab1ddbe52 Update README.md 2025-05-14 15:54:58 +01:00
Jim Shield
868f322c8c Re-add dynamic string changer
Removed this by accident when people were getting notification issues

Restored it to display the correct message when consuming
2025-05-14 15:54:53 +01:00
5 changed files with 182 additions and 112 deletions

View File

@@ -2,22 +2,15 @@
Consumables script for QBCore
# What is this?
This script is designed as a replacement/override for food and drink consumables in `qb-smallresources`
This script WAS designed as a replacement/override for food and drink consumables in `qb-smallresources`
It's main purpose was to make it so players did not stand up while sitting with my scripts due to lazy events such as ClearPedTasks in progressbar and dpemotes, this one is designed to cancel the animation you have chosen, not all animations.
It's main purpose was to make it so players did not stand up while sitting with my scripts due to lazy events such as ClearPedTasks in progressbar and dpemotes, this one is designed to cancel the animation you have chosen, not ALL animations.
It's recommended to set `Config.UseProgBar` to `false` to get this effect.
Users requested I readd the progressbar but it ended up being a config option to enable it
# THIS DOESN'T USE DPEMOTES OR RPEMOTES
# YOU **NEED** TO PUT THE EMOTES IN THE BOTTOM OF THE CONFIG.LUA
Though if your players are cancelling all animations when consuming, it's recommended to set `Config.UseProgBar` to `false` to get this effect.
## v1.5 Update Information
I've added a export system that allows scripts to easily add new foods and drinks to be usable and then sync them between players
This should work but may have issues.
But because of the export system being used, Jim-Consumables **NEEDS** to start before any scripts that use it.
This will be built into my scripts that use consumables to make it more plug and play
# THIS DOESN'T USE DPEMOTES OR RPEMOTES OR SCULLYS EMOTEMENU
# Installation
@@ -51,25 +44,35 @@ ensure [jim]
## New Items
To add an item, you only need to add a new item table in the Config.Consumables like this:
```lua
["heartstopper"] = {
emote = "burger", -- Select an emote from below, it has to be in here
time = math.random(5000, 6000), -- Amount of time it takes to consume the item
stress = math.random(1, 2), -- Amount of stress relief, can be 0
heal = 0, -- Set amount to heal by after consuming
armor = 5, -- Amount of armor to add
type = "food", -- Type: "alcohol" / "drink" / "food"
returnItem = { -- Item that will be given when the item is used
item = "plastic", -- eg. Plastic bottles can give "plastic"
amount = 1,
},
stats = {
screen = "rampage", -- The screen effect to be played when after consuming the item
effect = "heal", -- The status effect given by the item, "heal" / "stamina"
time = 10000, -- How long the effect should last (if not added it will default to 10000)
amount = 2, -- How much the value is changed by per second
hunger = math.random(10,20), -- The hunger/thirst stats of the item, if not found in the items.lua
thirst = math.random(10,20), -- The hunger/thirst stats of the item, if not found in the items.lua
},
heartstopper = {
emote = "burger", -- Select an emote from below, it has to be in here
time = math.random(5000, 6000), -- Amount of time it takes to consume the item
stress = math.random(1,2), -- Amount of stress relief, can be 0
heal = 0, -- Set amount to heal by after consuming
armor = 5, -- Amount of armor to add
type = "food", -- Type: "alcohol" / "drink" / "food"
canRun = true, -- If true player can run while using the item, not to it will cancel the event
stats = {
screen = "thermal", -- The screen effect to be played when after consuming the item "rampage" "turbo" "focus" "weed" "trevor" "nightvision" "thermal"
effect = "heal", -- The status effect given by the item, "heal" / "stamina"
-- This supports ps-buffs effects "armor" "stress" "swimming" "hacking" "intelligence" "luck" "strength"
time = 10000, -- How long the effect should last (if not added it will default to 10000)
amount = 6, -- How much the value is changed by per second
hunger = math.random(10, 20), -- The hunger/thirst stats of the item, if not found in the items.lua
thirst = math.random(10, 20), -- The hunger/thirst stats of the item, if not found in the items.lua
canOD = true, -- This creates an OD Effect, killing the user if they have too much
},
--Reward Items Variables
-- These can be the only thing in a consumable table and the item will still work
amounttogive = 3, -- Used for "RewardItems", tells the script how many to give
rewards = {
[1] = {
item = "plastic", -- prize item name
max = 10, -- max amount to give (this is put into math.random(1, max) )
rarity = 1, -- the rarity system, 1 being rarest, 4 being most common
},
},
},
```
Consuming an item can also manually activate screen effects
@@ -86,6 +89,28 @@ The example above uses `rampage` as this is what the effect is named after, you
"thermal"
```
You can also now create "Box" items, when setting type to "pack", it will act as if you user is opening a box
Examples:
- Ammo boxes that give clips
- Packets of cigarretes
- Crates of beer
How to set up these:
```lua
-- Example Box Item
["9_box"] = { -- Name of the box item in the shared items
emote = "uncuff", -- The emote than should run when "unboxing"
canRun = true, -- If true player can run while using the item, not to it will cancel the event
time = 3500, -- How long it takes to use the item
type = "pack", -- Designate it as a "pack" to the script knows what to do
pack = {
item = "9_ammo", -- The item to give from the box when complete
amount = 20 -- How many of the item to give
},
},
```
## PS-Buffs Support
This scripts can be expanded with ps-buffs (https://github.com/Project-Sloth/ps-buffs)

View File

@@ -1,42 +1,64 @@
local alcoholCount, drugCount, consuming, cancelled = 0, 0, false, false
Consumables, Emotes = {}, {}
local function syncConsumables()
Consumables = triggerCallback(getScript()..":server:syncConsumables")
Emotes = triggerCallback(getScript()..":server:syncEmotes")
debugPrint("^5Debug^7: ^2Retrieved ^6"..countTable(Consumables).." ^2Items and ^6"..countTable(Emotes).." ^2Emotes^7")
end
RegisterNetEvent(getScript()..":client:syncConsumables", function(NewConsumables)
if debugMode then
for k, v in pairs(NewConsumables) do
if not Consumables[k] then
print("^5Debug^7: ^2New Item Info added^7: ^6"..k.."^7")
end
end
end
Consumables = NewConsumables
end)
RegisterNetEvent(getScript()..":client:syncEmotes", function(NewEmotes)
if debugMode then
for k, v in pairs(NewEmotes) do
if not Emotes[k] then
print("^5Debug^7: ^2New Emote Info added^7: ^6"..k.."^7")
end
end
end
Emotes = NewEmotes
end)
onPlayerLoaded(function()
syncConsumables()
-- Wait until statebag exists, then trigger local handler
CreateThread(function()
while not GlobalState.jimConsumableItems do Wait(100) end
local newConsumables = GlobalState.jimConsumableItems
for k in pairsByKeys(newConsumables) do
if not Consumables[k] then
debugPrint("^5Statebag^7: ^2New ^3Consumable ^2Info synced^7: ^6"..k.."^7")
end
end
while not GlobalState.jimConsumableEmotes do Wait(100) end
local newEmotes = GlobalState.jimConsumableEmotes
for k in pairsByKeys(newEmotes) do
if not Emotes[k] then
debugPrint("^5Statebag^7: ^2New ^3Emote ^2Info synced^7: ^6"..k.."^7")
end
end
Consumables = newConsumables
Emotes = newEmotes
debugPrint("^5Statebag^7: ^2Synced ^6"..countTable(Consumables).." ^2Items and ^6"..countTable(Emotes).." ^2Emotes^7")
end)
end, true)
-- Handlers to recieve global statebag data from the server
AddStateBagChangeHandler("jimConsumableItems", nil, function(bagName, key, value, _unused)
if type(value) == "table" then
local newItemCount = 0
for k in pairsByKeys(value) do
if not Consumables[k] then
newItemCount += 1
debugPrint("^5Statebag^7: ^2New ^3Consumable ^2Info synced^7: ^6"..k.."^7")
end
end
Consumables = value
debugPrint("^5Statebag^7: ^2Synced ^6"..newItemCount.." ^2new Consumables^7")
end
end)
AddStateBagChangeHandler("jimConsumableEmotes", nil, function(bagName, key, value, _unused)
if type(value) == "table" then
local newEmoteCount = 0
for k in pairsByKeys(value) do
if not Emotes[k] then
newEmoteCount += 1
debugPrint("^5Statebag^7: ^2New ^3Emote ^2Info synced^7: ^6"..k.."^7")
end
end
Emotes = value
debugPrint("^5Statebag^7: ^2Synced ^6"..newEmoteCount.." ^2new Emotes^7")
end
end)
RegisterNetEvent(getScript()..':Consume', function(itemName)
if not Consumables[itemName] then return end
local consumable = Consumables[itemName]
if not hasItem(itemName, 1) then
print("^5Debug^7: ^1Error^7: ^2Item not found in inventory^7, ^2stopping^7..")
end
local requiredItem = Consumables[itemName].requiredItem or nil
local requiredItem = consumable and consumable.requiredItem or nil
if requiredItem then
if not hasItem(requiredItem, 1) then
@@ -50,7 +72,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
debugPrint("^5Debug^7: ^3Consume^7: ^2Starting event, locking inventory and grabbing data^7..")
tempLockInv(true)
local Player = PlayerPedId()
local emote = Emotes[Consumables[itemName].emote] or Emotes["crisps"]
local emote = Emotes[consumable.emote] or Emotes["crisps"]
if isAnimal then --- Animal ped adjustments
local presets = {
["default"] = {
@@ -98,31 +120,31 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
Prop = "v_res_tt_bowl",
PropBone = 64081,
PropPlacement = propPlacement,
SecondProp = Emotes[Consumables[itemName].emote].AnimationOptions.Prop or nil,
SecondPropBone = Emotes[Consumables[itemName].emote].AnimationOptions.Prop and 64081 or nil,
SecondPropPlacement = Emotes[Consumables[itemName].emote].AnimationOptions.Prop and propPlacement2 or nil,
SecondProp = Emotes[consumable.emote].AnimationOptions.Prop or nil,
SecondPropBone = Emotes[consumable.emote].AnimationOptions.Prop and 64081 or nil,
SecondPropPlacement = Emotes[consumable.emote].AnimationOptions.Prop and propPlacement2 or nil,
},
}
end
local returnItem = Consumables[itemName].returnItem or nil
local returnItem = consumable.returnItem or nil
local animDict, anim = tostring(emote[1]), tostring(emote[2])
local model, model2, bone, bone2, drugeffect, stress
local P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12 = table.unpack({0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}) -- Default placement coord cariable
local RewardItem = Consumables[itemName].rewards or nil
local type = Consumables[itemName].type or ""
local pack = Consumables[itemName].pack or nil
local RewardItem = consumable.rewards or nil
local type = consumable.type or ""
local pack = consumable.pack or nil
local string = "Using "
local canRun = Consumables[itemName].canRun
local stats = Consumables[itemName].stats
local canRun = consumable.canRun
local stats = consumable.stats
local time, stress, heal, armor, needStats = GenerateRandomValues({
time = Consumables[itemName].time or { 5000, 6000 },
stress = Consumables[itemName].stress or 0,
heal = Consumables[itemName].heal or 0,
armor = Consumables[itemName].armor or 0,
hunger = Consumables[itemName].stats and Consumables[itemName].stats.hunger or 0,
thirst = Consumables[itemName].stats and Consumables[itemName].stats.thirst or 0,
time = consumable.time or { 5000, 6000 },
stress = consumable.stress or 0,
heal = consumable.heal or 0,
armor = consumable.armor or 0,
hunger = consumable.stats and consumable.stats.hunger or 0,
thirst = consumable.stats and consumable.stats.thirst or 0,
})
if emote.AnimationOptions.Prop then
model = emote.AnimationOptions.Prop
@@ -133,10 +155,20 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
bone2 = GetPedBoneIndex(Player, emote.AnimationOptions.SecondPropBone)
P7, P8, P9, P10, P11, P12 = table.unpack(emote.AnimationOptions.SecondPropPlacement)
end
end
end
if consuming then
cancelled = true
if type == "drink" or type == "alcohol" then
string = "Drinking "
elseif type == "food" then
string = "Eating "
elseif type == "smoke" then
string = "Smoking "
elseif type == "pack" then
string = "Opening "
end
if consuming then
cancelled = true
debugPrint("^5Debug^7: ^3Consume^7: ^2Event already started^7, ^1Cancelling^7.")
tempLockInv(false)
if Config.UseProgbar then
@@ -264,7 +296,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
CreateThread(function()
if RewardItem then
debugPrint("Reward Item detected")
for i = 1, Consumables[itemName].amounttogive do
for i = 1, consumable.amounttogive do
local rarity = math.random(1, 4) -- rarity calculation
while true do
local item = math.random(1, countTable(RewardItem)) -- random item in the list to pick
@@ -281,11 +313,33 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
end
end)
if needStats then
if needStats.hunger then
TriggerServerEvent(getScript()..":server:addNeed", Core.Functions.GetPlayerData().metadata["hunger"] + needStats.hunger, "hunger")
jsonPrint(needStats)
if needStats.hunger > 0 then
local hunger = 0
if Core and Core.Functions then
hunger = (Core.Functions.GetPlayerData().metadata["hunger"] or 0) + needStats.hunger
hunger = hunger > 100 and 100 or hunger
elseif ESX and ESX.GetPlayerData then
hunger = (needStats.thirst / 100) * 1000000
hunger = hunger > 1000000 and 1000000 or hunger
end
TriggerServerEvent(getScript()..":server:setNeed", "hunger", hunger)
end
if needStats.thirst then
TriggerServerEvent(getScript()..":server:addNeed", Core.Functions.GetPlayerData().metadata["thirst"] + needStats.thirst, "thirst")
if needStats.thirst > 0 then
local thirst = 0
if Core and Core.Functions then
thirst = (Core.Functions.GetPlayerData().metadata["thirst"] or 0) + needStats.thirst
thirst = thirst > 100 and 100 or thirst
elseif ESX and ESX.GetPlayerData then
thirst = (needStats.thirst / 100) * 1000000
thirst = thirst > 1000000 and 1000000 or thirst
end
TriggerServerEvent(getScript()..":server:setNeed", "thirst", thirst)
end
end
debugPrint("^5Debug^7: ^2Hunger^7: [^6"..(needStats.hunger or 0).."^7] ^2Thrist^7: [^6"..(needStats.thirst or 0).."^7]" )

View File

@@ -1,6 +1,6 @@
name "Jim-Consumables"
author "Jimathy"
version "2.0.02"
version "2.0.04"
description "Consumables Script"
fx_version "cerulean"
game "gta5"

View File

@@ -9,43 +9,37 @@ onResourceStart(function()
end
--Export Import System--
createCallback(getScript()..':server:syncConsumables', function(source) return Consumables end)
createCallback(getScript()..':server:syncEmotes', function(source) return Emotes end)
GlobalState.jimConsumableItems = Consumables
GlobalState.jimConsumableEmotes = Emotes
end, true)
RegisterNetEvent(getScript()..':server:addNeed', function(amount, type)
local Player = Core.Functions.GetPlayer(source) if not Player then return end
if type == "thirst" then
Player.Functions.SetMetaData('thirst', amount)
TriggerClientEvent('hud:client:UpdateNeeds', source, Player.PlayerData.metadata.hunger, amount)
elseif type == "hunger" then
Player.Functions.SetMetaData('hunger', amount)
TriggerClientEvent('hud:client:UpdateNeeds', source, amount, Player.PlayerData.metadata.thirst)
end
onResourceStop(function()
GlobalState.jimConsumableItems = Consumables
GlobalState.jimConsumableEmotes = Emotes
end)
local syncScheduled = false
function syncConsumables()
debugPrint("^5Debug^7: ^2Sending ^6"..countTable(Consumables).." ^3Consumables ^2to all clients^7")
TriggerClientEvent(getScript()..":client:syncConsumables", -1, Consumables)
debugPrint("^5Statebag^7: ^2Sending ^6"..countTable(Consumables).." ^3Consumables ^2to all clients^7")
GlobalState.jimConsumableItems = Consumables
syncScheduled = false
end
local emoteSyncScheduled = false
function syncEmotes()
debugPrint("^5Debug^7: ^2Sending ^6"..countTable(Emotes).." ^3Emotes to all clients^7")
TriggerClientEvent(getScript()..":client:syncEmotes", -1, Emotes)
debugPrint("^5Statebag^7: ^2Sending ^6"..countTable(Emotes).." ^3Emotes to all clients^7")
GlobalState.jimConsumableEmotes = Emotes
emoteSyncScheduled = false
end
RegisterNetEvent(getScript()..':server:syncAddItem', function(itemName, data)
if not Consumables[itemName] then
Consumables[itemName] = data
createUseableItem(itemName, function(source, item) TriggerClientEvent(getScript()..':Consume', source, itemName) end)
--createUseableItem(itemName, function(source, item) TriggerClientEvent(getScript()..':Consume', source, itemName) end)
debugPrint("^5Debug^7: "..GetInvokingResource().." ^2is sending new ^3Item^7: '"..itemName.."'")
if not syncScheduled then
syncScheduled = true
Citizen.SetTimeout(5000, syncConsumables)
SetTimeout(5000, syncConsumables)
end
else
debugPrint("^1Debug^7: "..GetInvokingResource().." ^2is sending ^1duplicate ^3Item^7: '"..itemName.."'")
@@ -58,20 +52,13 @@ RegisterNetEvent(getScript()..':server:syncAddEmote', function(emoteName, data)
debugPrint("^5Debug^7: "..GetInvokingResource().." ^2is sending new ^3Emote^7: '"..emoteName.."'")
if not emoteSyncScheduled then
emoteSyncScheduled = true
Citizen.SetTimeout(5000, syncEmotes)
SetTimeout(5000, syncEmotes)
end
else
debugPrint("^1Debug^7: "..GetInvokingResource().." ^2is sending ^1duplicate ^3Emote^7: '"..emoteName.."'")
end
end)
RegisterNetEvent(getScript()..":server:syncConsumables", function()
TriggerClientEvent(getScript()..':client:syncConsumables', -1, Consumables)
end)
RegisterNetEvent(getScript()..":server:syncEmotes", function()
TriggerClientEvent(getScript()..':client:syncEmotes', -1, Emotes)
end)
--[[
Core.Commands.Add('consumableCreator', "Create consumables (admin only)", {}, false, function(source)
if source > 0 then return TriggerClientEvent(getScript()..":client:consumableCreator", source) end

View File

@@ -1 +1,5 @@
2.0.02
2.0.04
- Fix hunger and thirst events
https://github.com/jimathy/jim-consumables