21 Commits

Author SHA1 Message Date
Jim Shield
ce137d9089 Version Bump
`2.0.08` - `2.0.09`
2025-10-12 00:10:28 +01:00
Jim Shield
d7a8a20491 Make GenerateRandomValues() return 0
Its possible if no health, thirst or stress was set, it may sent back an empty table, which would then break the finish consume function in the server
2025-10-10 01:57:01 +01:00
Jim Shield
38d0b04e71 Fix pack and return items not being given 2025-10-06 12:56:14 +01:00
Jim Shield
762240b500 Version Bump
`2.0.07` - `2.0.08`
2025-10-06 02:29:07 +01:00
Jim Shield
0a0f60468a Rework calculations and fix alcohol/drug effects
This fixes calculation errors and implements support for jim_bridge built in synchronized alcohol/drug effects

Separates "drug control" `canOD` variable too
2025-10-06 02:28:03 +01:00
Jim Shield
9b189d89f5 fix needStats calculcation
My mad on this one, got `needTypes` and `needStats` confused, so made it just one table and included stress in it
2025-10-04 01:12:19 +01:00
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
12 changed files with 425 additions and 252 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: on:
push: push:
branches: branches:
- '*' # triggers on all branches - '*'
jobs: jobs:
notify: notify:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Discord Commits - name: Send commit payload to Discord Bot
uses: Sniddl/discord-commits@v1.6 env:
with: URL: http://${{ secrets.DISCORDBOT }}:3000/github-commits
webhook: ${{ secrets.DISCORD_WEBHOOK }} run: |
template: "avatar-with-link" curl -sS -X POST "$URL" \
include-extras: true -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 }}

144
README.md
View File

@@ -1,5 +1,6 @@
# Jim-Consumables # Jim-Consumables
Consumables script for QBCore
## FiveM MultiFrameork Consumables script
# What is this? # What is this?
This script WAS 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`
@@ -12,140 +13,13 @@ Though if your players are cancelling all animations when consuming, it's recomm
# THIS DOESN'T USE DPEMOTES OR RPEMOTES OR SCULLYS EMOTEMENU # THIS DOESN'T USE DPEMOTES OR RPEMOTES OR SCULLYS EMOTEMENU
# Installation ---
- I always recommend starting my scripts **AFTER** `[qb]` not inside it as it can mess with any dependancies on server load # Installation and Previews:
- I have a separate folder called `[jimextras]` (that is also in the resources folder) that starts before any scripts would use it. ## [JixelPatterns GitBook Documentation](https://jixelpatterns.gitbook.io/docs)
- 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 ### If you need support I have a discord server available, it helps me keep track of issues and give better support.
ensure [jimextras] ## [JixelPatterns Discord](https://discord.gg/9pCDHmjYwd)
ensure [jim]
```
## QB-SmallResources ### 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)
- 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"
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
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"
```
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)
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

View File

@@ -38,6 +38,7 @@ AddStateBagChangeHandler("jimConsumableItems", nil, function(bagName, key, value
debugPrint("^5Statebag^7: ^2Synced ^6"..newItemCount.." ^2new Consumables^7") debugPrint("^5Statebag^7: ^2Synced ^6"..newItemCount.." ^2new Consumables^7")
end end
end) end)
AddStateBagChangeHandler("jimConsumableEmotes", nil, function(bagName, key, value, _unused) AddStateBagChangeHandler("jimConsumableEmotes", nil, function(bagName, key, value, _unused)
if type(value) == "table" then if type(value) == "table" then
local newEmoteCount = 0 local newEmoteCount = 0
@@ -62,7 +63,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
if requiredItem then if requiredItem then
if not hasItem(requiredItem, 1) then if not hasItem(requiredItem, 1) then
triggerNotify(nil, "You need a "..Items[requiredItem].label, "error") triggerNotify(nil, "You need a "..getItemLabel(requiredItem), "error")
return return
else else
-- if requiredItem == "lighter" then breakTool({ item = "lighter", damage = math.random(0, 1) }) end -- if requiredItem == "lighter" then breakTool({ item = "lighter", damage = math.random(0, 1) }) end
@@ -72,6 +73,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
debugPrint("^5Debug^7: ^3Consume^7: ^2Starting event, locking inventory and grabbing data^7..") debugPrint("^5Debug^7: ^3Consume^7: ^2Starting event, locking inventory and grabbing data^7..")
tempLockInv(true) tempLockInv(true)
local Player = PlayerPedId() local Player = PlayerPedId()
local pedCoords = GetEntityCoords(Player)
local emote = Emotes[consumable.emote] or Emotes["crisps"] local emote = Emotes[consumable.emote] or Emotes["crisps"]
if isAnimal then --- Animal ped adjustments if isAnimal then --- Animal ped adjustments
local presets = { local presets = {
@@ -127,25 +129,23 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
} }
end end
local returnItem = consumable.returnItem or nil
local animDict, anim = tostring(emote[1]), tostring(emote[2]) local animDict, anim = tostring(emote[1]), tostring(emote[2])
local model, model2, bone, bone2, drugeffect, stress 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 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 = consumable.rewards or nil
local type = consumable.type or "" local type = consumable.type or ""
local pack = consumable.pack or nil
local string = "Using " local string = "Using "
local canRun = consumable.canRun local canRun = consumable.canRun
local stats = consumable.stats local stats = consumable.stats
local time, stress, heal, armor, needStats = GenerateRandomValues({ local time, heal, armor, needStats = GenerateRandomValues({
time = consumable.time or { 5000, 6000 }, time = consumable.time or { 5000, 6000 },
stress = consumable.stress or 0,
heal = consumable.heal or 0, heal = consumable.heal or 0,
armor = consumable.armor or 0, armor = consumable.armor or 0,
hunger = consumable.stats and consumable.stats.hunger or 0, hunger = consumable.stats and consumable.stats.hunger or 0,
thirst = consumable.stats and consumable.stats.thirst or 0, thirst = consumable.stats and consumable.stats.thirst or 0,
stress = consumable.stress or 0,
}) })
if emote.AnimationOptions.Prop then if emote.AnimationOptions.Prop then
model = emote.AnimationOptions.Prop model = emote.AnimationOptions.Prop
bone = GetPedBoneIndex(Player, emote.AnimationOptions.PropBone) bone = GetPedBoneIndex(Player, emote.AnimationOptions.PropBone)
@@ -172,7 +172,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
debugPrint("^5Debug^7: ^3Consume^7: ^2Event already started^7, ^1Cancelling^7.") debugPrint("^5Debug^7: ^3Consume^7: ^2Event already started^7, ^1Cancelling^7.")
tempLockInv(false) tempLockInv(false)
if Config.UseProgbar then if Config.UseProgbar then
stopPropgressBar() stopProgressBar()
else else
triggerNotify(nil, "Stopped "..string, "error") triggerNotify(nil, "Stopped "..string, "error")
end end
@@ -208,13 +208,21 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
debugPrint("^5Debug^7: ^3Consume^7: ^2Player Movement Flag^7 - ^6"..json.encode(MovementType).." ^7") debugPrint("^5Debug^7: ^3Consume^7: ^2Player Movement Flag^7 - ^6"..json.encode(MovementType).." ^7")
if Config.Main.UseProgbar then if Config.Main.UseProgbar then
CreateThread(function() CreateThread(function()
if progressBar({ time = time, label = string..Items[itemName].label.."..", dead = false, cancel = false}) then if progressBar({
time = time,
label = string..getItemLabel(itemName).."..",
dead = false,
disableMovement = false,
disableCombat = true,
cancel = false
}) then
consuming = false consuming = false
else else
consuming = false
end end
end) end)
else else
triggerNotify(nil, string..Items[itemName].label.."..", "success") triggerNotify(nil, string..getItemLabel(itemName).."..", "success")
end end
consuming = true consuming = true
@@ -224,11 +232,11 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
if model then if model then
if IsModelValid(model) == 1 then if IsModelValid(model) == 1 then
debugPrint("^5Debug^7: ^3PropSpawn^7: ^2Spawning consumable prop^7...") 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) AttachEntityToEntity(attachProp, Player, bone, P1, P2, P3, P4, P5, P6, true, true, false, true, 1, true)
if model2 then if model2 then
if IsModelValid(model2) == 1 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) AttachEntityToEntity(attachProp2, Player, bone2, P7, P8, P9, P10, P11, P12, true, true, false, true, 1, true)
else else
print("^5Debug^7: ^3PropSpawn^7: ^2Second prop model isn't valid/found^7.") print("^5Debug^7: ^3PropSpawn^7: ^2Second prop model isn't valid/found^7.")
@@ -242,7 +250,7 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
if DoesEntityExist(attachProp2) then if DoesEntityExist(attachProp2) then
destroyProp(attachProp2) destroyProp(attachProp2)
attachProp2 = nil attachProp2 = nil
end end
else else
print("^5Debug^7: ^3PropSpawn^7: ^2Prop model isn't valid/found^7.") print("^5Debug^7: ^3PropSpawn^7: ^2Prop model isn't valid/found^7.")
end end
@@ -256,14 +264,14 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
"Type: "..(type or ""), "Type: "..(type or ""),
"Hunger: "..(needStats.hunger or 0).."%", "Hunger: "..(needStats.hunger or 0).."%",
"Thirst: "..(needStats.thirst or 0).."%", "Thirst: "..(needStats.thirst or 0).."%",
"Stress: "..(stress or 0).."%", "Stress: "..(needStats.stress or 0).."%",
"Heal: "..(heal or 0).."%", "Heal: "..(heal or 0).."%",
"Armour: "..(armor or 0).."%", "Armour: "..(armor or 0).."%",
}, nil, nil) }, nil, nil)
end end
makeInstructionalButtons({ makeInstructionalButtons({
{ keys = { 194 }, text = "Stop Consuming" }, { keys = { 194 }, text = "Stop Consuming" },
}) })
if time <= 0 then if time <= 0 then
consuming = false consuming = false
end end
@@ -273,80 +281,23 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
tempLockInv(false) tempLockInv(false)
hideText() hideText()
if Config.Main.UseProgbar then if Config.Main.UseProgbar then
stopPropgressBar() stopProgressBar()
else else
triggerNotify(nil, "Cancelled "..string, "error") triggerNotify(nil, "Cancelled "..string, "error")
end end
TriggerServerEvent(getScript()..":server:stopConsume")
end end
Wait(0) Wait(0)
time -= 12 time -= 12
end end
StopEntityAnim(Player, anim, animDict, 1.0) StopEntityAnim(Player, anim, animDict, 1.0)
unloadAnimDict(animDict) unloadAnimDict(animDict)
if not cancelled then if not cancelled then
hideText() hideText()
removeItem(itemName, 1)
if returnItem ~= nil then
debugPrint("returnItem detected")
currentToken = triggerCallback(AuthEvent)
addItem(returnItem.item, returnItem.amount)
end
-- Reward Item calculations TriggerServerEvent(getScript()..":server:finishConsume", needStats)
CreateThread(function()
if RewardItem then
debugPrint("Reward Item detected")
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
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
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 > 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]" )
if stress and stress ~= 0 then
debugPrint("^5Debug^7: ^3Consume^7: ^2Relieving ^6"..stress.." ^2stress^7.")
TriggerServerEvent('hud:server:RelieveStress', stress)
end
if heal and heal ~= 0 then if heal and heal ~= 0 then
debugPrint("^5Debug^7: ^3Consume^7: ^2Healing player by^7: ^6"..heal) debugPrint("^5Debug^7: ^3Consume^7: ^2Healing player by^7: ^6"..heal)
SetEntityHealth(Player, GetEntityHealth(Player) + heal) SetEntityHealth(Player, GetEntityHealth(Player) + heal)
@@ -356,20 +307,42 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
TriggerServerEvent('hospital:server:SetArmor', (GetPedArmour(Player) + armor)) SetPedArmour(Player, (GetPedArmour(Player) + armor)) TriggerServerEvent('hospital:server:SetArmor', (GetPedArmour(Player) + armor)) SetPedArmour(Player, (GetPedArmour(Player) + armor))
end end
if type == "alcohol" then if type == "alcohol" then
alcoholCount += 1 if checkExportExists("jim_bridge", "addAlcoholCount") then
debugPrint("^5Debug^7: ^3Consume^7: ^2Current ^4alcoholCount^7: ^6"..alcoholCount) exports.jim_bridge:addAlcoholCount(1, stats.canOD)
if alcoholCount > 1 and alcoholCount < 4 then else
TriggerEvent("evidence:client:SetStatus", "alcohol", 200) alcoholCount = alcoholCount + 1
elseif alcoholCount >= 4 then debugPrint("^5Debug^7: ^3Consume^7: ^2Current ^4alcoholCount^7: ^6"..alcoholCount)
TriggerEvent("evidence:client:SetStatus", "heavyalcohol", 200) if alcoholCount > 1 and alcoholCount <= 4 then
CreateThread(function() TriggerEvent("evidence:client:SetStatus", "alcohol", 200)
AlienEffect() elseif alcoholCount >= 4 then
end) -- Used as overdosing/too drunk effect TriggerEvent("evidence:client:SetStatus", "heavyalcohol", 200)
CreateThread(function()
AlienEffect()
end)
end
if stats.canOD and alcoholCount >= 5 then
SetEntityHealth(Player, GetEntityHealth(Player) - math.random(5, 10))
end
end end
end end
if pack then if type == "drug" then
currentToken = triggerCallback(AuthEvent) if checkExportExists("jim_bridge", "addDrugCount") then
addItem(pack.item, pack.amount) exports.jim_bridge:addDrugCount(1, stats.canOD or false)
else
drugCount = drugCount + 1
debugPrint("^5Debug^7: ^3Consume^7: ^2Current ^4drugCount^7: ^6"..drugCount)
if drugCount > 3 and drugCount < 7 then
elseif drugCount >= 7 then
CreateThread(function()
AlienEffect()
end)
end
if stats.canOD and drugCount >= 4 then
SetEntityHealth(Player, GetEntityHealth(Player) - math.random(5, 10))
end
end
end end
if stats then if stats then
if stats.screen then -- Screen effect activation if stats.screen then -- Screen effect activation
@@ -390,14 +363,8 @@ RegisterNetEvent(getScript()..':Consume', function(itemName)
end end
end end
if stats.canOD then if stats.canOD then
drugCount += 1 if drugCount >= 4 or alcoholCount >= 5 then
debugPrint("^5Debug^7: ^3Consume^7: ^2Current ^4drugCount^7: ^6"..drugCount) SetEntityHealth(Player, GetEntityHealth(Player) - math.random(5, 10))
if drugCount >= 4 then
debugPrint("^5Debug^7: ^3Consume^7: ^2Current ^4drugCount^7: ^6"..drugCount.."^7 - ^2 removing health")
SetEntityHealth(PlayerPedId(), GetEntityHealth(Player) - math.random(10, 15))
if drugCount >= 7 then
CreateThread(function() AlienEffect() end) -- If too many drugs, if not dead will make you stumble
end
end end
end end
if stats.effect then if stats.effect then

View File

@@ -10,7 +10,7 @@ Config = {
}, },
Main = { 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 -- This is intended to create a progressbar to show consuming but it was usually forces animations to end weirdly
}, },
Crafting = { Crafting = {

View File

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

View File

@@ -1,13 +1,50 @@
local Consumables = Config.Consumables local Consumables = Config.Consumables
local Emotes = Config.Emotes 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() onResourceStart(function()
for k, v in pairs(Config.Consumables) do 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
if not Items[k] then print("^1Debug^7: ^2Item check ^7- '^1"..k.."^7' ^2not found in the shared lua^7") end print("^1Debug^7: ^2Item check ^7- '^1"..k.."^7' ^2not found in the shared lua^7")
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
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 end
massMakeUseable()
--Export Import System-- --Export Import System--
GlobalState.jimConsumableItems = Consumables GlobalState.jimConsumableItems = Consumables
GlobalState.jimConsumableEmotes = Emotes GlobalState.jimConsumableEmotes = Emotes
@@ -21,6 +58,7 @@ end)
local syncScheduled = false local syncScheduled = false
function syncConsumables() function syncConsumables()
debugPrint("^5Statebag^7: ^2Sending ^6"..countTable(Consumables).." ^3Consumables ^2to all clients^7") debugPrint("^5Statebag^7: ^2Sending ^6"..countTable(Consumables).." ^3Consumables ^2to all clients^7")
massMakeUseable()
GlobalState.jimConsumableItems = Consumables GlobalState.jimConsumableItems = Consumables
syncScheduled = false syncScheduled = false
end end
@@ -32,6 +70,97 @@ function syncEmotes()
emoteSyncScheduled = false emoteSyncScheduled = false
end 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")
local item = isConsuming[src].rewardItem
addItem(item.item, item.amount, nil, src)
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
debugPrint("^5Debug^7: ^2Giving player ^3"..src.." ^2Pack items^7")
local item = isConsuming[src].packItem
addItem(item.item, item.amount, nil, src)
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) RegisterNetEvent(getScript()..':server:syncAddItem', function(itemName, data)
if not Consumables[itemName] then if not Consumables[itemName] then
Consumables[itemName] = data Consumables[itemName] = data

View File

@@ -47,7 +47,7 @@ Config.Consumables = {
-- heal = 0, -- Set amount to heal by after consuming -- heal = 0, -- Set amount to heal by after consuming
-- armor = 5, -- Amount of armor to add -- armor = 5, -- Amount of armor to add
-- type = "food", -- Type: "alcohol" / "drink" / "food" -- 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 = { -- stats = {
-- screen = "thermal", -- The screen effect to be played when after consuming the item "rampage" "turbo" "focus" "weed" "trevor" "nightvision" "thermal" -- 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 -- Example Box Item
-- ["9_box"] = { -- Name of the box item in the shared -- ["9_box"] = { -- Name of the box item in the shared
-- emote = "uncuff", -- The emote than should run when "unboxing" -- 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 -- time = 3500, -- How long it takes to use the item
-- type = "pack", -- Designate it as a "pack" to the script knows what to do -- type = "pack", -- Designate it as a "pack" to the script knows what to do
-- pack = { -- 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

@@ -15,9 +15,25 @@ function GenerateRandomValues(data)
transData[k] = v transData[k] = v
end end
end end
return transData["time"],
transData["stress"], if transData.hunger > 0 then
transData["heal"], transData.hunger = transData.hunger > 100 and 100 or transData.hunger
transData["armor"], end
{ hunger = transData["hunger"], thirst = transData["thirst"] }
if transData.thirst > 0 then
transData.thirst = transData.thirst > 100 and 100 or transData.thirst
end
if transData.stress > 0 then
transData.stress = transData.stress > 100 and 100 or transData.stress
end
return transData["time"],
transData["heal"],
transData["armor"],
{
hunger = transData["hunger"] or 0,
thirst = transData["thirst"] or 0,
stress = transData["stress"] or 0
}
end end

View File

@@ -1,5 +1,6 @@
2.0.04 2.0.09
- Fix hunger and thirst events - Fix possible issue with nil needTypes tables being sent
- Fix packitems and return items not giving any items
https://github.com/jimathy/jim-consumables https://github.com/jimathy/jim-consumables/releases/latest