Compare commits

..

5 Commits

Author SHA1 Message Date
Jim Shield
26b1f36029 Version Bump
`2.1.04` - `2.1.05`
2025-10-02 12:24:27 +01:00
Jim Shield
331dc94e2f Revert Crafting
I realised in trying to increase readability and optimize, I broke crafting recipes that supported different multiple recipes in the same list

eg. Mining has 3 recipes in the smelter for `goldingot`

This reverts the system back to the previous recipe style but keeps all the fixes/features from the latest updates
2025-10-02 12:20:58 +01:00
Jim Shield
d1dab48acc Fix singleprogress multiplying crafting recipes
Adds a `cloneTable()` function to make a "deep copy" of the crafting recipes when calculating the multiple amounts to be taken when multicrafting
2025-10-02 00:14:13 +01:00
Jim Shield
7e595df143 Remove gta progressbar print from testing 2025-10-01 17:04:59 +01:00
Jim Shield
d791a2e09f quick fix converting old crafting style 2025-10-01 11:42:47 +01:00
5 changed files with 115 additions and 97 deletions

View File

@@ -1,6 +1,6 @@
name "Jim_Bridge" name "Jim_Bridge"
author "Jimathy" author "Jimathy"
version "2.1.04" version "2.1.05"
description "Framework Bridge By Jimathy" description "Framework Bridge By Jimathy"
fx_version "cerulean" fx_version "cerulean"
rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'

View File

@@ -65,7 +65,7 @@ local excludeKeys = {
--- ``` --- ```
function craftingMenu(data) function craftingMenu(data)
if CraftLock then return end if CraftLock then return end
local data = cloneTable(data)
-- Job or gang check; exit if not authorized. -- Job or gang check; exit if not authorized.
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
@@ -80,48 +80,48 @@ function craftingMenu(data)
data.stashName = data.stashTable or data.stashName data.stashName = data.stashTable or data.stashName
-- Wrapper to convert old style crafting recipes to be handled by the menu properly -- Wrapper to convert old style crafting recipes to be handled by the menu properly
if data.craftable.Recipes[1] then -- assume old style crafting table --if data.craftable.Recipes[1] then -- assume old style crafting table
local compatTable = {} -- local compatTable = {}
local id = 0 -- local id = 0
for k, v in ipairs(data.craftable.Recipes) do -- for k, v in ipairs(data.craftable.Recipes) do
local Recipe = v -- local Recipe = v
for l, b in pairs(Recipe) do -- for l, b in pairs(Recipe) do
id += 1 -- if doesItemExist(l) then
if doesItemExist(l) then -- id += 1
compatTable[l] = { -- compatTable[l] = {
ingredients = b, -- ingredients = b,
id = id, -- id = id,
info = { -- info = {
amount = Recipe.amount or 1, -- amount = Recipe.amount or 1,
metadata = Recipe.metadata or nil, -- metadata = Recipe.metadata or nil,
job = Recipe.job or nil, -- job = Recipe.job or nil,
gang = Recipe.gang or nil, -- gang = Recipe.gang or nil,
hasCrafted = Recipe.hasCrafted or nil, -- hasCrafted = Recipe.hasCrafted or nil,
}, -- },
} -- }
end -- end
end -- end
end -- end
data.craftable.Recipes = compatTable -- data.craftable.Recipes = compatTable
end --end
-- Convert to array -- Convert to array
local RecipesArray = {} --local RecipesArray = {}
for k, v in pairs(data.craftable.Recipes) do --for k, v in pairs(data.craftable.Recipes) do
RecipesArray[v.id] = { [k] = v } -- RecipesArray[v.id] = { [k] = v }
end --end
local Menu = {} local Menu = {}
local Recipes = data.craftable.Recipes local Recipes = cloneTable(data.craftable.Recipes)
local craftedItems = {} local craftedItems = {}
local tempCarryTable = {} local tempCarryTable = {}
-- Build a temporary table of all required ingredients (default quantity is 1). -- Build a temporary table of all required ingredients (default quantity is 1).
for i = 1, #RecipesArray do for i = 1, #Recipes do
for k, v in pairs(RecipesArray[i]) do for k, v in pairs(Recipes[i]) do
if doesItemExist(k) then if not excludeKeys[k] then
if not v.info.amount then v.info.amount = 1 end if not Recipes[i].amount then Recipes[i].amount = 1 end
tempCarryTable[k] = tempCarryTable[k] and (tempCarryTable[k] < v.info.amount) or v.info.amount tempCarryTable[k] = tempCarryTable[k] and (tempCarryTable[k] < Recipes[i].amount) or Recipes[i].amount
end end
end end
end end
@@ -136,27 +136,29 @@ function craftingMenu(data)
header = "Material Source: "..(usingStash and "Job Stash" or "Player Inventory"), header = "Material Source: "..(usingStash and "Job Stash" or "Player Inventory"),
disabled = true, disabled = true,
} }
for i = 1, #Recipes do
for i = 1, #RecipesArray do local menuId = #Menu+1
local item = "" local item = ""
for k in pairs(RecipesArray[i]) do local Recipe = {}
for k, v in pairs(Recipes[i]) do
if not excludeKeys[k] then if not excludeKeys[k] then
item = k item = k
Recipe = Recipes[i]
Recipe.amount = Recipe.amount or 1
break break
end end
end end
local Recipe = RecipesArray[i][item]
-- Job Check -- Job Check
local hasGroup = true local hasGroup = true
if Recipe.info.job then if Recipe.job then
for l, b in pairs(Recipe.info.job) do for l, b in pairs(Recipe.job) do
hasGroup = hasJob(l, nil, b) hasGroup = hasJob(l, nil, b)
if hasGroup then goto skipcheck end if hasGroup then goto skipcheck end
end end
end end
if Recipe.info.gang then if Recipe.gang then
for l, b in pairs(Recipe.info.gang) do for l, b in pairs(Recipe.gang) do
hasGroup = hasJob(l, nil, b) hasGroup = hasJob(l, nil, b)
if hasGroup then goto skipcheck end if hasGroup then goto skipcheck end
end end
@@ -165,12 +167,11 @@ function craftingMenu(data)
-- if has group requirement, continue -- if has group requirement, continue
if hasGroup then if hasGroup then
local setheader, settext, disable, metadata = "", "", false, (Recipe.info.metadata or Recipe.info.info or nil) local setheader, settext, disable, metadata = "", "", false, (Recipe.metadata or Recipe.info or nil)
local itemTable = {} local itemTable = {}
local metaTable = {} local metaTable = {}
-- Build ingredient details. -- Build ingredient details.
for l, b in pairs(Recipe[item]) do
for l, b in pairs(Recipe.ingredients) do
local label = getItemLabel(l) local label = getItemLabel(l)
local hasItem = checkStashItem(data.stashName, { [l] = b }) local hasItem = checkStashItem(data.stashName, { [l] = b })
local missingMark = not hasItem and "" or " " local missingMark = not hasItem and "" or " "
@@ -182,17 +183,16 @@ function craftingMenu(data)
-- Make sure "canCarryTable" exists -- Make sure "canCarryTable" exists
while not canCarryTable do Wait(10) end while not canCarryTable do Wait(10) end
disable = not checkStashItem(data.stashName, itemTable) disable = not checkStashItem(data.stashName, itemTable)
setheader = ((metadata and metadata.label) or getItemLabel(item)) setheader = ((metadata and metadata.label) or getItemLabel(item))
..(Recipe.info.amount > 1 and " x"..Recipe.info.amount or "") ..(Recipe.amount > 1 and " x"..Recipe.amount or "")
local statusEmoji = disable and " " or not canCarryTable[item] and " 📦" or " ✔️" local statusEmoji = disable and " " or not canCarryTable[item] and " 📦" or " ✔️"
local isNew = (Recipe.info.hasCrafted ~= nil and craftedItems[item] == nil) and "" or "" local isNew = (Recipe.hasCrafted ~= nil and craftedItems[item] == nil) and "" or ""
setheader = isNew .. setheader .. statusEmoji setheader = isNew .. setheader .. statusEmoji
-- Build menu option using info -- Build menu option using info
Menu[#Menu + 1] = { Menu[menuId] = {
arrow = isOx() and (not disable and canCarryTable[item]), arrow = isOx() and (not disable and canCarryTable[item]),
isMenuHeader = disable or not canCarryTable[item], isMenuHeader = disable or not canCarryTable[item],
icon = invImg((metadata and metadata.image) or item), icon = invImg((metadata and metadata.image) or item),
@@ -203,9 +203,10 @@ function craftingMenu(data)
onSelect = (not disable and canCarryTable[item]) and function() onSelect = (not disable and canCarryTable[item]) and function()
local transdata = { local transdata = {
item = item, item = item,
craft = RecipesArray[i][item], craft = Recipe,
craftable = data.craftable, craftable = data.craftable,
coords = data.coords, coords = data.coords,
amount = Recipe.amount,
stashName = data.stashName, stashName = data.stashName,
onBack = data.onBack, onBack = data.onBack,
metadata = metadata, metadata = metadata,
@@ -270,7 +271,7 @@ function multiCraft(data)
for i = 1, maxCreation do for i = 1, maxCreation do
multiItemTable[i] = {} multiItemTable[i] = {}
for l, b in pairs(data.craft.ingredients) do for l, b in pairs(data.craft[data.item]) do
multiItemTable[i][l] = (b * i) multiItemTable[i][l] = (b * i)
end end
end end
@@ -376,7 +377,10 @@ end
--- }) --- })
--- ``` --- ```
function makeItem(data) function makeItem(origData)
local data = cloneTable(origData)
local Ped = PlayerPedId() local Ped = PlayerPedId()
if CraftLock then return end if CraftLock then return end
CraftLock = true CraftLock = true
@@ -391,7 +395,7 @@ function makeItem(data)
local craftAmount = (data.amount and data.amount ~= 1) and data.amount or 1 local craftAmount = (data.amount and data.amount ~= 1) and data.amount or 1
local metadata = data.metadata or nil local metadata = data.metadata or nil
local prop = data.craftable.Anims and data.craftable.Anims.prop or nil local prop = data.craftable.Anims and data.craftable.Anims.prop or nil
jsonPrint(prop)
local canReturn = true local canReturn = true
local crafted, crafting = true, true local crafted, crafting = true, true
@@ -413,12 +417,12 @@ function makeItem(data)
if not Config.Crafting.SingleProgress then -- if SingleProgress is disabled, dont do ingredient progressbars if not Config.Crafting.SingleProgress then -- if SingleProgress is disabled, dont do ingredient progressbars
-- Run ingredient check and usage separately first -- Run ingredient check and usage separately first
for i = 1, craftAmount do for i = 1, craftAmount do
for k, v in pairs(data.craft.ingredients) do for k, v in pairs(data.craft[data.item]) do
if isInventoryOpen() then if isInventoryOpen() then
print("^1Error^7: ^2Inventory is open, you tried to break things") print("^1Error^7: ^2Inventory is open, you tried to break things")
stopCam(0) stopCam(0)
ClearPedTasks(Ped) ClearPedTasks(Ped)
if canReturn then craftingMenu(data) end if canReturn then craftingMenu(origData) end
CraftLock = false CraftLock = false
return return
end end
@@ -440,11 +444,7 @@ function makeItem(data)
end end
if not crafted then if not crafted then
stopCam(0) goto finishEarly
ClearPedTasks(Ped)
if canReturn then craftingMenu(data) end
CraftLock = false
return
end end
if crafting and progressBar({ if crafting and progressBar({
@@ -459,22 +459,19 @@ function makeItem(data)
}) then }) then
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken) TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
currentToken = nil currentToken = nil
if data.craft.info.hasCrafted ~= nil then if data.craft.hasCrafted ~= nil then
data.craftable.craftedItems[data.item] = true data.craftable.craftedItems[data.item] = true
triggerCallback(getScript()..":server:setPlayerMetadata", "craftedItems", data.craftable.craftedItems) triggerCallback(getScript()..":server:setPlayerMetadata", "craftedItems", data.craftable.craftedItems)
end end
if data.craft.info.exp ~= nil then if data.craft.exp ~= nil then
craftingLevel += data.craft.exp.give craftingLevel += data.craft.exp.give
triggerCallback(getScript()..":server:setPlayerMetadata", "craftingLevel", craftingLevel) triggerCallback(getScript()..":server:setPlayerMetadata", "craftingLevel", craftingLevel)
end end
--if data.craftable.Recipes[1].oneUse == true then if data.craftable.Recipes[1].oneUse == true then
-- removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot) removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
-- local breakId = GetSoundId() local breakId = GetSoundId()
-- PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0) PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
-- canReturn = false canReturn = false
--end
if data.requiredItemfunc then
data.requiredItemfunc()
end end
else else
break break
@@ -492,19 +489,19 @@ function makeItem(data)
icon = data.item, icon = data.item,
request = true, request = true,
}) then }) then
data.craft.info.amount = craftAmount data.craft.amount *= craftAmount
for k, v in pairs(data.craft.ingredients) do for k, v in pairs(data.craft[data.item]) do
-- multiply igredient requirement in sent crafting table for removal -- multiply igredient requirement in sent crafting table for removal
data.craft.ingredients[k] = (v * craftAmount) data.craft[data.item][k] = (v * craftAmount)
end end
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken) TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
currentToken = nil -- clear client cached token currentToken = nil -- clear client cached token
-- handle metadata and experience in a single go -- handle metadata and experience in a single go
if data.craft.info.hasCrafted ~= nil then if data.craft.hasCrafted ~= nil then
data.craftable.craftedItems[data.item] = true data.craftable.craftedItems[data.item] = true
triggerCallback(getScript()..":server:setPlayerMetadata", "craftedItems", data.craftable.craftedItems) triggerCallback(getScript()..":server:setPlayerMetadata", "craftedItems", data.craftable.craftedItems)
end end
if data.craft.info.exp ~= nil then if data.craft.exp ~= nil then
craftingLevel += data.craft["exp"].give * craftAmount craftingLevel += data.craft["exp"].give * craftAmount
triggerCallback(getScript()..":server:setPlayerMetadata", "craftingLevel", craftingLevel) triggerCallback(getScript()..":server:setPlayerMetadata", "craftingLevel", craftingLevel)
end end
@@ -514,20 +511,22 @@ function makeItem(data)
-- PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0) -- PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
-- canReturn = false -- canReturn = false
--end --end
end
end
::finishEarly::
if craftProp then destroyProp(craftProp) end
if data.sound then if data.sound then
StopSound(data.sound.soundId) StopSound(data.sound.soundId)
end end
if data.requiredItemfunc then if data.requiredItemfunc then
data.requiredItemfunc() data.requiredItemfunc()
end end
end
end
if craftProp then destroyProp(craftProp) end
--Wait(500) --Wait(500)
stopCam(0) stopCam(0)
CraftLock = false CraftLock = false
if canReturn then craftingMenu(data) end if canReturn then craftingMenu(origData) end
ClearPedTasks(Ped) ClearPedTasks(Ped)
end end
@@ -564,7 +563,7 @@ RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable,
if type(stashName) == "table" then if type(stashName) == "table" then
for _, name in pairs(stashName) do for _, name in pairs(stashName) do
stashItems = getStash(name) stashItems = getStash(name)
for k, v in pairs(craftable.ingredients) do for k, v in pairs(craftable[ItemMake]) do
for _, b in pairs(stashItems or {}) do for _, b in pairs(stashItems or {}) do
if k == b.name then if k == b.name then
itemRemove[k] = v itemRemove[k] = v
@@ -574,7 +573,7 @@ RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable,
end end
else else
stashItems = getStash(stashName) stashItems = getStash(stashName)
for k, v in pairs(craftable.ingredients) do for k, v in pairs(craftable[ItemMake]) do
for _, b in pairs(stashItems or {}) do for _, b in pairs(stashItems or {}) do
if k == b.name then if k == b.name then
itemRemove[k] = v itemRemove[k] = v
@@ -584,13 +583,13 @@ RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable,
end end
stashRemoveItem(stashItems, stashName, itemRemove) stashRemoveItem(stashItems, stashName, itemRemove)
else else
if craftable then if craftable[ItemMake] then
for k, v in pairs(craftable.ingredients or {}) do for k, v in pairs(craftable[ItemMake]) do
removeItem(tostring(k), v, src) removeItem(tostring(k), v, src)
end end
end end
end end
addItem(ItemMake, craftable.info.amount or 1, metadata, src) addItem(ItemMake, craftable.amount or 1, metadata, src)
-- Optionally, add experience here: -- Optionally, add experience here:
-- for example: -- for example:
-- if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end -- if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end

View File

@@ -651,6 +651,29 @@ function lookEnt(entity)
end end
end end
-- Function to clone tables, to use when referencing tables that need to be hard set
function cloneTable(obj, opts, seen)
if type(obj) ~= "table" then return obj end
seen = seen or {}
if seen[obj] then return seen[obj] end
local copy = {}
seen[obj] = copy
-- Copy entries
for k, v in pairs(obj) do
local k2 = (opts and opts.copy_keys) and cloneTable(k, opts, seen) or k
copy[k2] = cloneTable(v, opts, seen)
end
-- Preserve metatable unless told not to
if not (opts and opts.strip_meta) then
local mt = getmetatable(obj)
if mt ~= nil then setmetatable(copy, mt) end
end
return copy
end
------------------------------------------------------------- -------------------------------------------------------------
-- Material and Prop Functions -- Material and Prop Functions

View File

@@ -231,7 +231,6 @@ function gtaProgressBar(data)
while result == nil do Wait(10) end while result == nil do Wait(10) end
inProgress = false inProgress = false
print(tostring(result))
-- Cleanup animations/tasks -- Cleanup animations/tasks
if data.dict then if data.dict then
stopAnim(data.dict, data.anim, ped) stopAnim(data.dict, data.anim, ped)
@@ -240,7 +239,6 @@ function gtaProgressBar(data)
ClearPedTasks(ped) ClearPedTasks(ped)
end end
-- Cleanup -- Cleanup
FreezeEntityPosition(ped, false) FreezeEntityPosition(ped, false)

View File

@@ -1,9 +1,7 @@
2.1.04 2.1.05
- Crafting fixes (prop/sound/remove ingredients) - Fix "true/false" print from built-in gta progressbars
- "SingleProgress" is now one progressbar - Revert Crafting system to previous style (I was too quick to do this)
- Add ropeControl.lua - to help create and edit custom ropes/wires - Fix singleprogress multiplying crafting recipes
- Support for new crafting recipe style
- create setStress() multiframework function
https://github.com/jimathy/jim_bridge/releases/latest https://github.com/jimathy/jim_bridge/releases/latest