mirror of
https://github.com/jimathy/jim-consumables.git
synced 2026-08-17 06:26:03 +01:00
Compare commits
26 Commits
v2.0.02
...
62db9f6900
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62db9f6900 | ||
|
|
6317dee667 | ||
|
|
15bb54c206 | ||
|
|
94023857ff | ||
|
|
daa70a8844 | ||
|
|
25e8d99839 | ||
|
|
2c2a486a41 | ||
|
|
16e957116e | ||
|
|
b4d65ff043 | ||
|
|
9d866c88ee | ||
|
|
018dcca6cc | ||
|
|
e27b840a5e | ||
|
|
e794d9a446 | ||
|
|
541263b156 | ||
|
|
60c42b2506 | ||
|
|
2a4de6fdc1 | ||
|
|
4b015e5efa | ||
|
|
518c5204ae | ||
|
|
12642b7e7b | ||
|
|
c2843eaee2 | ||
|
|
b92e26129f | ||
|
|
129b65deda | ||
|
|
d83d0bbfc4 | ||
|
|
2b6d3f2825 | ||
|
|
cab1ddbe52 | ||
|
|
868f322c8c |
12
.gitattributes
vendored
Normal file
12
.gitattributes
vendored
Normal 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
|
||||
15
.github/workflows/notify-discord.yml
vendored
15
.github/workflows/notify-discord.yml
vendored
@@ -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
71
.github/workflows/notify-release.yml
vendored
Normal 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 won’t 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
88
.github/workflows/release.yml
vendored
Normal 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
129
README.md
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
-- }
|
||||
-- },
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user