26 Commits

Author SHA1 Message Date
Jim Shield
62db9f6900 Version Bump
`2.0.06` - `2.0.07`
2025-10-01 21:44:20 +01:00
Jim Shield
6317dee667 Stop double calculation of hunger and thirst
Missed this, it's doing the player stats + new stats and then in jim_bridge doing this again and doubling it all up
2025-10-01 21:43:14 +01:00
Jim Shield
15bb54c206 Add support for jim_bridge setStress function
Allows multiframework stress events
2025-10-01 01:20:33 +01:00
Jim Shield
94023857ff Release Notifier Push 2025-09-24 18:18:14 +01:00
Jim Shield
daa70a8844 Version Bump
`2.0.05` - `2.0.06`
2025-09-15 16:31:34 +01:00
Jim Shield
25e8d99839 Rework code to server side
In an attempt to tackle exploiters, I reworked a lot of the code to server side

This will be tied to the new `jim_bridge` release but should function as normal now
2025-09-13 02:54:36 +01:00
Jim Shield
2c2a486a41 Update notify-discord.yml 2025-09-04 16:59:41 +01:00
Jim Shield
16e957116e Create release.yml
Automate release zips and make them generate the folder name correctly

This helps users download a correctly named script so they dont end up with a script with a version number in the title
2025-09-01 10:22:10 +01:00
Jim Shield
b4d65ff043 Update notify-discord.yml 2025-07-27 18:48:05 +01:00
Jim Shield
9d866c88ee Update README.md 2025-07-25 00:41:11 +01:00
Jim Shield
018dcca6cc Update README.md 2025-07-24 22:51:21 +01:00
Jim Shield
e27b840a5e Try to fix props now spawning correctly
Not sure what is going on, they are server synced
but Its possible its not loading the correctly because of the spawn location

I've changed it to make the props at the players location in hopes this fixes the issue
2025-07-08 15:02:28 +01:00
Jim Shield
e794d9a446 Version Bump
`2.0.04` - `2.0.05`
2025-06-22 16:37:46 +01:00
Jim Shield
541263b156 Fixes related to progressbars 2025-06-22 16:13:18 +01:00
Jim Shield
60c42b2506 Fix hunger being wrong on esx when consuming 2025-06-20 21:54:14 +01:00
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
11 changed files with 473 additions and 247 deletions

12
.gitattributes vendored Normal file
View File

@@ -0,0 +1,12 @@
# VCS / CI noise
.gitattributes export-ignore
.gitignore export-ignore
.github/** export-ignore
.gitmodules export-ignore
# Dev tooling/config
.vscode/** export-ignore
*.code-workspace export-ignore
.editorconfig export-ignore
.eslintrc* export-ignore
.prettier* export-ignore

View File

@@ -3,15 +3,16 @@ name: Discord Commit Notifier
on:
push:
branches:
- '*' # triggers on all branches
- '*'
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Discord Commits
uses: Sniddl/discord-commits@v1.6
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
template: "avatar-with-link"
include-extras: true
- name: Send commit payload to Discord Bot
env:
URL: http://${{ secrets.DISCORDBOT }}:3000/github-commits
run: |
curl -sS -X POST "$URL" \
-H 'Content-Type: application/json' \
--data-binary "@$GITHUB_EVENT_PATH"

71
.github/workflows/notify-release.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
name: Discord Release Notifier
on:
# Manual or UI-published releases
release:
types: [published]
# Releases created by your tag-driven workflow
workflow_run:
workflows: ["Package & Release"] # must match the 'name:' in release.yml
types: [completed]
permissions:
contents: read
jobs:
# 1) Handle manual / UI / non-workflow-created releases
notify-from-release:
if: github.event_name == 'release' && github.event.action == 'published'
runs-on: ubuntu-latest
steps:
- name: Send release payload to Discord Bot
env:
URL: http://${{ secrets.DISCORDBOT }}:3000/github-releases
run: |
curl -sS -X POST "$URL" \
-H 'Content-Type: application/json' \
--data-binary "@$GITHUB_EVENT_PATH"
# 2) Handle releases created by your 'Package & Release' workflow
# (which wont trigger `on: release` when using GITHUB_TOKEN)
notify-from-workflow-run:
if: >
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- name: Fetch release JSON by tag
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
OWNER="${GITHUB_REPOSITORY%/*}"
REPO="${GITHUB_REPOSITORY#*/}"
TAG="${{ github.event.workflow_run.head_branch }}"
# head_branch is the tag name for a tag push workflow_run
curl -fsSL -H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$OWNER/$REPO/releases/tags/$TAG" \
> release.json
- name: Wrap as 'release' event payload & notify bot
env:
URL: http://${{ secrets.DISCORDBOT }}:3000/github-releases
run: |
set -euo pipefail
jq -n --slurpfile rel release.json \
--arg repo "$GITHUB_REPOSITORY" \
--arg repo_url "https://github.com/$GITHUB_REPOSITORY" \
--arg action "published" '
{ action: $action,
repository: { full_name: $repo, html_url: $repo_url },
release: $rel[0],
sender: { login: "github-actions[bot]" } }' > payload.json
curl -sS -X POST "$URL" \
-H 'Content-Type: application/json' \
--data-binary "@payload.json"

88
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Package & Release
on:
push:
tags:
- "v*"
- "*.*.*"
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Derive vars
id: vars
shell: bash
run: |
echo "name=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT # e.g. jim_bridge
echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT # strip leading v
- name: Build zip with folder prefix
shell: bash
run: |
NAME='${{ steps.vars.outputs.name }}'
VER='${{ steps.vars.outputs.version }}'
git archive --format=zip --prefix="${NAME}/" -o "${NAME}-${VER}.zip" "$GITHUB_REF_NAME"
ls -lh "${NAME}-${VER}.zip"
# --- Patch notes generation (commit titles + links) -------------------
- name: Find previous tag (SemVer aware)
id: prev
shell: bash
run: |
CUR="${GITHUB_REF_NAME}"
# Sort tags by version desc, drop the current, take the next newest
PREV=$(git tag --sort=-v:refname | grep -v -x "$CUR" | head -n 1 || true)
echo "prev=$PREV" >> $GITHUB_OUTPUT
echo "Previous tag: ${PREV:-<none>}"
- name: Generate RELEASE_NOTES.md from commits
shell: bash
run: |
REPO="${{ github.repository }}"
CUR="${GITHUB_REF_NAME}"
PREV='${{ steps.prev.outputs.prev }}'
if [ -n "$PREV" ]; then
RANGE="$PREV..$CUR"
HEADER="## Changes in $CUR"
else
# First release: include all commits
ROOT=$(git rev-list --max-parents=0 HEAD | tail -n 1)
RANGE="$ROOT..$CUR"
HEADER="## Changes"
fi
{
echo "$HEADER"
echo
# Oldest → newest, subject only, skip merge commits
# Escape '[' and ']' so markdown doesn't break on rare titles
git log --reverse --no-merges --pretty=format:'- ['%h'] - %s' "$RANGE" \
| perl -pe 's/([\[\]])/\\$1/g'
} > RELEASE_NOTES.md
echo "--- RELEASE_NOTES.md ---"
cat RELEASE_NOTES.md
echo "------------------------"
- name: Create / Update GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
files: |
*.zip
body_path: RELEASE_NOTES.md # attach our generated notes
# If you wanted GitHub's auto notes instead, set:
# generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

129
README.md
View File

@@ -1,126 +1,25 @@
# Jim-Consumables
Consumables script for QBCore
## FiveM MultiFrameork Consumables script
# 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.
# THIS DOESN'T USE DPEMOTES OR RPEMOTES OR SCULLYS EMOTEMENU
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
# Installation and Previews:
## [JixelPatterns GitBook Documentation](https://jixelpatterns.gitbook.io/docs)
# Installation
### If you need support I have a discord server available, it helps me keep track of issues and give better support.
## [JixelPatterns Discord](https://discord.gg/9pCDHmjYwd)
- I always recommend starting my scripts **AFTER** `[qb]` not inside it as it can mess with any dependancies on server load
- I have a separate folder called `[jimextras]` (that is also in the resources folder) that starts before any scripts would use it.
- This ensure's it has everything it requires before trying to load
- Example of my load order:
```CSS
# QBCore & Extra stuff
ensure qb-core
ensure [qb]
ensure [standalone]
ensure [voice]
ensure [defaultmaps]
ensure [vehicles]
# Extra Jim Stuff
ensure [jimextras]
ensure [jim]
```
## QB-SmallResources
- It should already take control of default qbcore food, drink and drugs.
- If it fails to do this and attempts to use qb-smallresources still you will need to:
- `[qb]` > `qb-smallresources` > `server` > `consumables.lua`
- Remove or comment out the `CreateUseableItem` events for *alcohol*, *eat*, *drink* and *drug* in this file
- Not all of them! (such as armour related items)
- This ***should*** stop `qb-smallresources` taking control and confusing `jim-consumables`
## 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
},
},
```
Consuming an item can also manually activate screen effects
The example above uses `rampage` as this is what the effect is named after, you can use it for any item you think best
```lua
--The current list of screen effects are:
"turbo"
"focus"
"rampage"
"weed"
"trevor"
"nightvision"
"thermal"
```
## PS-Buffs Support
This scripts can be expanded with ps-buffs (https://github.com/Project-Sloth/ps-buffs)
If this script is enabled it will automatically try to use their system to apply buffs:
```lua
--The extra buffs that can be set include:
"heal" -- Health recovery buff
"stamina" -- Stamina recovery buff
"swimming" -- Swimming speed buff
"stress" -- Stress recovery buff
"armor" -- Armour recovery buff
"hacking" -- Hacking effect
"intelligence" -- Intelligence effect
"luck" -- Luck effect
"strength" -- Strength effect
```
This script supports dpemotes style emotes, so if you have some that you want to be triggered when eating or drinking drop it in the Config.Emotes section.
## Add consumables from external scripts
A small example of a server-sided snippet for this imports the item and the emote, then syncs it to players before and after they connect.
```lua
local foodTable = {
["shotfries"] = { emote = "bsfries", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "food", stats = { hunger = math.random(55,65), }},
}
local emoteTable = {
["bsfries"] = {"mp_player_inteat@burger", "mp_player_int_eat_burger_fp", "Fries", AnimationOptions = { Prop = "prop_food_bs_chips", PropBone = 18905, PropPlacement = {0.09, -0.06, 0.05, 300.0, 150.0}, EmoteMoving = true, EmoteLoop = true, }},
}
for k, v in pairs(foodTable) do TriggerEvent("jim-consumables:server:syncAddItem", k, v) end
for k, v in pairs(emoteTable) do TriggerEvent("jim-consumables:server:syncAddEmote", k, v) end
```
This will grab the `shotfries` item info and add it to the `Config.Consumables` while the servers running and the same with the built-in emote system
### If you think I did a good job here, consider donating as it keeps by lights on and my cat round:
## [JixelPatterns Kofi](https://ko-fi.com/jixelpatterns)

View File

@@ -1,42 +1,65 @@
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 +73,8 @@ 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 pedCoords = GetEntityCoords(Player)
local emote = Emotes[consumable.emote] or Emotes["crisps"]
if isAnimal then --- Animal ped adjustments
local presets = {
["default"] = {
@@ -98,31 +122,28 @@ 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 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 type = consumable.type or ""
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,14 +154,24 @@ 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
stopPropgressBar()
stopProgressBar()
else
triggerNotify(nil, "Stopped "..string, "error")
end
@@ -176,9 +207,17 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
debugPrint("^5Debug^7: ^3Consume^7: ^2Player Movement Flag^7 - ^6"..json.encode(MovementType).." ^7")
if Config.Main.UseProgbar then
CreateThread(function()
if progressBar({ time = time, label = string..Items[itemName].label.."..", dead = false, cancel = false}) then
if progressBar({
time = time,
label = string..Items[itemName].label.."..",
dead = false,
disableMovement = false,
disableCombat = true,
cancel = false
}) then
consuming = false
else
consuming = false
end
end)
else
@@ -192,11 +231,11 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
if model then
if IsModelValid(model) == 1 then
debugPrint("^5Debug^7: ^3PropSpawn^7: ^2Spawning consumable prop^7...")
attachProp = makeProp({ prop = model, coords = vector4(0.0,0.0,0.0,0.0)}, 1, 1)
attachProp = makeProp({ prop = model, coords = vec4(pedCoords.x, pedCoords.y, pedCoords.z, 0.0)}, 1, 1, true)
AttachEntityToEntity(attachProp, Player, bone, P1, P2, P3, P4, P5, P6, true, true, false, true, 1, true)
if model2 then
if IsModelValid(model2) == 1 then
attachProp2 = makeProp({ prop = model2, coords = vector4(0.0,0.0,0.0,0.0)}, 1, 1)
attachProp2 = makeProp({ prop = model2, coords = vec4(pedCoords.x, pedCoords.y, pedCoords.z, 0.0)}, 1, 1)
AttachEntityToEntity(attachProp2, Player, bone2, P7, P8, P9, P10, P11, P12, true, true, false, true, 1, true)
else
print("^5Debug^7: ^3PropSpawn^7: ^2Second prop model isn't valid/found^7.")
@@ -210,7 +249,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
if DoesEntityExist(attachProp2) then
destroyProp(attachProp2)
attachProp2 = nil
end
end
else
print("^5Debug^7: ^3PropSpawn^7: ^2Prop model isn't valid/found^7.")
end
@@ -231,7 +270,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
end
makeInstructionalButtons({
{ keys = { 194 }, text = "Stop Consuming" },
})
})
if time <= 0 then
consuming = false
end
@@ -241,57 +280,38 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
tempLockInv(false)
hideText()
if Config.Main.UseProgbar then
stopPropgressBar()
stopProgressBar()
else
triggerNotify(nil, "Cancelled "..string, "error")
end
TriggerServerEvent(getScript()..":server:stopConsume")
end
Wait(0)
time -= 12
end
StopEntityAnim(Player, anim, animDict, 1.0)
unloadAnimDict(animDict)
if not cancelled then
hideText()
removeItem(itemName, 1)
if returnItem ~= nil then
debugPrint("returnItem detected")
currentToken = triggerCallback(AuthEvent)
addItem(returnItem.item, returnItem.amount)
end
local needTypes = { }
-- Reward Item calculations
CreateThread(function()
if RewardItem then
debugPrint("Reward Item detected")
for i = 1, Consumables[itemName].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
if RewardItem[item].rarity >= rarity then
currentToken = triggerCallback(AuthEvent)
addItem(RewardItem[item].item, math.random(1, RewardItem[item].max or 1))
debugPrint("^5Debug^7: ^2Given reward prize^7: '^6"..RewardItem[item].item.."^7'")
break
end
Wait(100)
end
Wait(1500)
end
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
needTypes.hunger = needTypes.hunger > 100 and 100 or needTypes.hunger
end
if needStats.thirst then
TriggerServerEvent(getScript()..":server:addNeed", Core.Functions.GetPlayerData().metadata["thirst"] + needStats.thirst, "thirst")
if needStats.thirst > 0 then
needTypes.thirst = needTypes.thirst > 100 and 100 or needTypes.thirst
end
end
debugPrint("^5Debug^7: ^2Hunger^7: [^6"..(needStats.hunger or 0).."^7] ^2Thrist^7: [^6"..(needStats.thirst or 0).."^7]" )
TriggerServerEvent(getScript()..":server:finishConsume", needTypes)
if stress and stress ~= 0 then
debugPrint("^5Debug^7: ^3Consume^7: ^2Relieving ^6"..stress.." ^2stress^7.")
TriggerServerEvent('hud:server:RelieveStress', stress)
needTypes.stress = stress
end
if heal and heal ~= 0 then
debugPrint("^5Debug^7: ^3Consume^7: ^2Healing player by^7: ^6"..heal)
@@ -313,10 +333,6 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
end) -- Used as overdosing/too drunk effect
end
end
if pack then
currentToken = triggerCallback(AuthEvent)
addItem(pack.item, pack.amount)
end
if stats then
if stats.screen then -- Screen effect activation
debugPrint(stats.screen)

View File

@@ -10,7 +10,7 @@ Config = {
},
Main = {
UseProgbar = false, -- Disabled this, progress bars stop all anims when complete.
UseProgbar = false, -- Recommended to disable this, progress bars stop ALL anims when complete.
-- This is intended to create a progressbar to show consuming but it was usually forces animations to end weirdly
},
Crafting = {

View File

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

View File

@@ -1,51 +1,178 @@
local Consumables = Config.Consumables
local Emotes = Config.Emotes
local isConsuming = {}
local function massMakeUseable()
for k, v in pairs(Config.Consumables) do
createUseableItem(k, function(source, item)
local src = source
if not isConsuming[src] and hasItem(item.name, 1, src) then
registerConsume(src, item.name)
TriggerClientEvent(getScript()..':Consume', src, item.name)
end
end)
end
end
function registerConsume(src, item)
-- Start building server sided item return table
local consumable = Consumables[item]
isConsuming[src] = {
item = item,
rewardItem = checkReward(consumable) or nil,
returnItem = checkReturnItem(consumable) or nil,
packItem = checkPackItem(consumable) or nil
}
debugPrint("^5Debug^7: ^2Player ^3"..src.." ^2started consuming ^7'^2"..item.."^7'")
end
RegisterNetEvent(getScript()..":RegisterConsuming", function(item)
local src = source
registerConsume(src, item)
end)
onResourceStart(function()
for k, v in pairs(Config.Consumables) do
createUseableItem(k, function(source, item) TriggerClientEvent(getScript()..':Consume', source, item.name) end)
if not Items[k] then print("^1Debug^7: ^2Item check ^7- '^1"..k.."^7' ^2not found in the shared lua^7") end
if not Config.Emotes[v.emote] then print("^1Debug^7: ^2Emote check ^7- '^1"..k.."^7' ^2requested emote ^7'^6"..v.emote.."^7' - ^2not found in config^7.^2lua^7") end
if not Items[k] then
print("^1Debug^7: ^2Item check ^7- '^1"..k.."^7' ^2not found in the shared lua^7")
end
if not Config.Emotes[v.emote] then
print("^1Debug^7: ^2Emote check ^7- '^1"..k.."^7' ^2requested emote ^7'^6"..v.emote.."^7' - ^2not found in config^7.^2lua^7")
end
end
--Export Import System--
createCallback(getScript()..':server:syncConsumables', function(source) return Consumables end)
createCallback(getScript()..':server:syncEmotes', function(source) return Emotes end)
massMakeUseable()
--Export Import System--
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")
massMakeUseable()
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
-- Return item
function checkPackItem(consumable)
local result = nil
if consumable.pack then
result = { item = consumable.pack.item, amount = consumable.pack.amount }
end
return result
end
-- Return item
function checkReturnItem(consumable)
local result = nil
if consumable.returnItem then
result = { item = consumable.returnItem.item, amount = consumable.returnItem.amount }
end
return result
end
-- Reward items
function checkReward(consumable)
local result = nil
local rewardItem = consumable.rewards or nil
if rewardItem then
debugPrint("Reward Item detected")
for i = 1, consumable.amounttogive or 1 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
if rewardItem[item].rarity >= rarity then
result = result or {}
result[#result+1] = { item = rewardItem[item].item, amount = math.random(1, rewardItem[item].max or 1) }
debugPrint("^5Debug^7: ^2Cachced reward prize^7: '^6"..rewardItem[item].item.."^7'")
break
end
Wait(10)
break
end
end
end
return result
end
RegisterNetEvent(getScript()..":server:finishConsume", function(needTypes)
local src = source
if isConsuming[src] ~= nil then
removeItem(isConsuming[src].item, 1, src)
if isConsuming[src].rewardItem then
debugPrint("^5Debug^7: ^2Giving player ^3"..src.." ^2reward items^7")
for i = 1, #isConsuming[src].rewardItem do
local item = isConsuming[src].rewardItem[i]
addItem(item.item, item.amount, nil, src)
end
end
if isConsuming[src].returnItem then
debugPrint("^5Debug^7: ^2Giving player ^3"..src.." ^2return item^7")
for i = 1, #isConsuming[src].returnItem do
local item = isConsuming[src].returnItem[i]
addItem(item.item, item.amount, nil, src)
end
end
if isConsuming[src].packItem then
for i = 1, #isConsuming[src].packItem do
debugPrint("^5Debug^7: ^2Giving player ^3"..src.." ^2Pack items^7")
local item = isConsuming[src].packItem[i]
addItem(item.item, item.amount, nil, src)
end
end
-- Needs
if needTypes.thirst then
debugPrint("^5Debug^7: ^2Thrist^7: [^6"..(needTypes.thirst or 0).."^7]")
setThirst(src, needTypes.thirst)
end
if needTypes.hunger then
debugPrint("^5Debug^7: ^2Hunger^7: [^6"..(needTypes.hunger or 0).."^7]")
setHunger(src, needTypes.hunger)
end
if needTypes.stress then
debugPrint("^5Debug^7: ^2Stress^7: [^6"..(needTypes.stress or 0).."^7]")
setStress(src, needTypes.stress)
end
isConsuming[src] = nil
else
return
end
end)
RegisterNetEvent(getScript()..":server:stopConsume", function()
local src = source
debugPrint("^5Player stopped consuming early^7", src)
isConsuming[src] = nil
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 +185,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

@@ -47,7 +47,7 @@ Config.Consumables = {
-- 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
-- canRun = true, -- If true player can run while using the item, if not 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"
@@ -74,7 +74,7 @@ Config.Consumables = {
-- Example Box Item
-- ["9_box"] = { -- Name of the box item in the shared
-- 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
-- canRun = true, -- If true player can run while using the item, if not 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 = {
@@ -83,4 +83,18 @@ Config.Consumables = {
-- },
-- },
-- Example Cigar Item wiht requirements
-- ["cigar"] = { -- Name of the item in your shared items
-- emote = "smokecigar", -- The emote than should run when "smoking"
-- canRun = true, -- If true player can run while using the item, if not it will cancel the event
-- time = math.random(5000, 6000), -- How long it takes to use the item
-- type = "smoke", -- Designate it as a "smoke" to the script knows what to do
-- requiredItem = "lighter", -- Set a required item making it unsable if they don't have it
-- stats = {
-- effect = "stress",
-- time = 5000,
-- amount = math.random(10, 15),
-- canOD = false
-- }
-- },
}

View File

@@ -1 +1,6 @@
2.0.02
2.0.07
- Add support for multi-framework stress events (fixes ESX stress)
- Fix double calculation of hunger and thirst
https://github.com/jimathy/jim-consumables/releases/latest