Compare commits

...

16 Commits

Author SHA1 Message Date
Jim Shield
7c47de5dc0 Version Bump
`2.0.19` - `2.0.20`
2025-07-06 19:16:01 +01:00
Jim Shield
c1ab2b3505 Support only one progressBar when crafting 2025-07-06 19:13:10 +01:00
Jim Shield
1665ab41de fix multicraft menu for lation_ui 2025-07-06 19:01:10 +01:00
Jim Shield
6d397b59bf fix onExit support for lation_ui 2025-07-05 20:52:08 +01:00
Jim Shield
9dfbcbe878 fix typo breaking tgiann CanCarryItem export check 2025-07-05 13:24:19 +01:00
Jim Shield
ca0970b650 Add fallback config support for QBInv inventory
Enhanced the inventory config loader to support both old and new config file structures for QBInv by introducing a fallback mechanism. The loader now attempts multiple config paths to improve compatibility with different versions.
2025-07-05 11:11:51 +01:00
Jim Shield
02218a4902 enhance dosItemExist() and add getItemLabel() 2025-07-05 00:33:26 +01:00
Jim Shield
a900044306 Fix hang when using jpr-inventory
The script was randomly looping the config file check, if it hit qb-inventory first it would end the check and break the script, changed it to skip instead and fixed it
2025-07-04 23:44:37 +01:00
Jim Shield
7382a534a0 I THINK jim_bridge now fully supports lation_ui 2025-07-04 18:56:19 +01:00
Jim Shield
3b76b15bb4 Add starting support for lation_ui
Added support for:
- Menus
- Draw Text
- Notifications
- Skillchecks
- ProgressBar

TODO:
- Input dialogs
2025-07-04 15:20:02 +01:00
Jim Shield
f2eda0fef6 Add indicator of ingredient items check location 2025-07-04 15:17:56 +01:00
Jim Shield
61c1cce814 Increase cache export timeout to 2 minutes 2025-07-04 15:16:53 +01:00
Jim Shield
0f1660e3ed remove debug print for ox_inv 2025-07-01 13:21:59 +01:00
Jim Shield
e5b55e49b7 Add missing functions for playing animations
Forgot to include animation functions for progressbar ui_module when porting it over
2025-07-01 13:07:36 +01:00
Jim Shield
1ff01526bc Add larger timeouts for caching 2025-06-30 23:19:23 +01:00
Jim Shield
1ed227080b Add missing "Open Wheel" to searchCar() function 2025-06-30 23:18:43 +01:00
15 changed files with 470 additions and 173 deletions

View File

@@ -53,11 +53,11 @@ setr jim_DisableDebug true
setr jim_DisableEventDebug true
# Force the default setting for what framework scripts should be used
setr jim_menuScript qb # qb, ox, gta, jim
setr jim_notifyScript gta # qb, ox, gta, esx, okok, red
setr jim_drawTextScript qb # qb, ox, gta, esx
setr jim_progressBarScript qb # qb, ox, gta, esx
setr jim_skillCheckScript qb # qb, ox, gta
setr jim_menuScript qb # qb, ox, gta, lation
setr jim_notifyScript gta # qb, ox, gta, esx, okok, red, lation
setr jim_drawTextScript qb # qb, ox, gta, esx, lation
setr jim_progressBarScript qb # qb, ox, gta, esx, lation
setr jim_skillCheckScript qb # qb, ox, gta, lation
setr jim_dontUseTarget false # Set to true to disable target systems and use draw text 3d
```

View File

@@ -41,7 +41,17 @@ local cache = {
Jobs = {},
Gangs = {},
}
local cacheReady = false
local timers = {}
local function startTimer(label)
timers[label] = GetGameTimer()
end
local function endTimer(label)
timers[label] = GetGameTimer() - (timers[label] or GetGameTimer())
timers[label] = timers[label] / 1000
end
startTimer("Cache")
-- Helper function to check if resource exists in server (instead of if it is already started)
local function checkExists(resourceName)
return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped")
@@ -78,6 +88,7 @@ end
---- Load Items -----
---------------------
-- Items initialization based on detected inventory system
startTimer("Items")
if checkExists(Exports.OXInv) then
-- Wait for OX Inventory to start if it's not already started
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
@@ -155,10 +166,12 @@ elseif checkExists(Exports.RSGExport) then
itemResource = Exports.RSGExport
cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
end
endTimer("Items")
---------------------
--- Load Vehicles ---
---------------------
startTimer("Vehicles")
-- Vehicle loading depending on framework
if checkExists(Exports.QBXExport) then
vehResource = Exports.QBXExport
@@ -197,10 +210,12 @@ elseif checkExists(Exports.RSGExport) then
cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
end
endTimer("Vehicles")
---------------------
----- Load Jobs -----
---------------------
startTimer("Jobs")
-- Jobs loading based on framework
if checkExists(Exports.QBXExport) then
jobResource = Exports.QBXExport
@@ -267,6 +282,7 @@ elseif checkExists(Exports.RSGExport) then
cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs
end
endTimer("Jobs")
-- Fallback if nil or empty
if cache.Items == nil or not next(cache.Items) then
@@ -292,6 +308,8 @@ end
-- Forcefully load the the specified config file from inventory scripts
-- This allows to get information required for certain functions that need to detect how much space is left in a players inventory
-- This is born from too many tickets of me needing to explain that they need to change "InventoryWeight" to match their inv setting
startTimer("InvWeight")
startTimer("InvSlots")
local function getInventoryConfig(resource, data)
if data.convars then
return function(path)
@@ -327,14 +345,18 @@ local function getInventoryConfig(resource, data)
end
end
-- Inventory table
local invWeightTable = {
[Exports.OXInv] = { convars = {
weight = { key = "inventory:weight", default = 30000 },
slots = { key = "inventory:slots", default = 40 }
}},
[Exports.QBInv] = { file = "config/config.lua", path = { "MaxWeight" }, slotPath = { "MaxSlots" } },
[Exports.QBInv] = {
fallback = {
{ file = "config.lua", path = { "MaxInventoryWeight" }, slotPath = { "MaxInventorySlots" } }, -- old version
{ file = "config/config.lua", path = { "MaxWeight" }, slotPath = { "MaxSlots" } }, -- new version
}
},
[Exports.JPRInv] = { file = "configs/main_config.lua", path = { "MaxInventoryWeight" }, slotPath = { "MaxInventorySlots" } },
[Exports.PSInv] = { file = "config.lua", path = { "MaxInventoryWeight" }, slotPath = { "MaxInventorySlots" } },
[Exports.QSInv] = { file = "config/config.lua", path = { "InventoryWeight", "weight" }, slotPath = { "InventoryWeight", "slots" } },
@@ -348,9 +370,21 @@ local invWeightTable = {
local invResource = ""
for script, data in pairs(invWeightTable) do
if checkExists(script) then
if script == Exports.QBInv and GetResourceState(Exports.JPRInv):find("start") then return end
if script == Exports.QBInv and GetResourceState(Exports.JPRInv):find("start") then goto skip end
local attempts = data.fallback or { data }
local lookup, used, err
for _, option in ipairs(attempts) do
local try, e = getInventoryConfig(script, option)
if try then
lookup = try
used = option
break
end
err = e
end
local lookup, err = getInventoryConfig(script, data)
if not lookup then
print(("^1ERROR^7: ^1Config loader failed from ^5%s^7: ^1%s^7"):format(script, err or "unknown"))
break
@@ -366,14 +400,16 @@ for script, data in pairs(invWeightTable) do
print(("%s^7: ^1Failed to get ^7Inventory%s ^1from ^5%s^7: ^1%s^7"):format(warnType, label, script, perr or "unknown"))
end
resolve("Weight", data.path or { "MaxWeight" })
resolve("Slots", data.slotPath or { "MaxSlots" })
resolve("Weight", used.path or { "MaxWeight" })
resolve("Slots", used.slotPath or { "MaxSlots" })
invResource = script:gsub("-", "^7-^4"):gsub("_", "^7_^4")
break
end
::skip::
end
endTimer("InvWeight")
endTimer("InvSlots")
CreateThread(function()
local counts = {
@@ -383,15 +419,18 @@ CreateThread(function()
if type(v) ~= "number" then for count in pairs(v) do counts[k] += 1 end end
end
if cache.InventoryWeight then
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventoryWeight^7: ^3"..cache.InventoryWeight.."^7 (^3"..(cache.InventoryWeight / 1000).."kg^7)")
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventoryWeight^7: ^3"..cache.InventoryWeight.."^7 (^3"..(cache.InventoryWeight / 1000).."kg^7) ^7("..timers["InvWeight"].."s)")
end
if cache.InventorySlots then
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventorySlots^7: ^3"..cache.InventorySlots.."^7")
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventorySlots^7: ^3"..cache.InventorySlots.." ^7("..timers["InvSlots"].."s)")
end
print("^6FrameworkCache^7: ^4"..itemResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Items).."^2 Items^7")
print("^6FrameworkCache^7: ^4"..vehResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Vehicles).."^2 Vehicles^7")
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Jobs).."^2 Jobs^7")
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Gangs).."^2 Gangs^7")
print("^6FrameworkCache^7: ^4"..itemResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Items).."^2 Items ^7("..timers["Items"].."s)")
print("^6FrameworkCache^7: ^4"..vehResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Vehicles).."^2 Vehicles ^7("..timers["Vehicles"].."s)")
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Jobs).."^2 Jobs ^7("..timers["Jobs"].."s)")
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Gangs).."^2 Gangs ^7("..timers["Jobs"].."s)")
endTimer("Cache")
print("^6FrameworkCache^7: ^2Cache Ready ^7("..timers["Cache"].."s)")
cacheReady = true
end)
RegisterNetEvent("jim_bridge:requestCache", function()
@@ -400,15 +439,16 @@ RegisterNetEvent("jim_bridge:requestCache", function()
end)
exports("GetSharedData", function()
-- Wait for data to be ready before returning it
local timeout = GetGameTimer() + 5000
while (
not cache or
(not cache.Items or next(cache.Items) == nil) or
(not cache.Vehicles or next(cache.Vehicles) == nil) or
(not cache.Jobs or next(cache.Jobs) == nil)
) and GetGameTimer() < timeout do
Wait(50)
-- Wait indefinitely (with periodic checks) until cacheReady is true
local timeout = GetGameTimer() + 20000 -- 20 second failsafe timeout
while not cacheReady and GetGameTimer() < timeout do
Wait(100)
end
if not cacheReady then
print("^1ERROR^7: jim_bridge cache timed out waiting for data.")
return nil -- Signal clearly if cache never became ready
end
return cache
end)

View File

@@ -1,6 +1,6 @@
name "Jim_Bridge"
author "Jimathy"
version "2.0.19"
version "2.0.20"
description "Framework Bridge By Jimathy"
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.'

View File

@@ -47,48 +47,7 @@
--- })
--- ```
function openMenu(Menu, data)
if Config.System.Menu == "jim" then
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
header = " ",
txt = "Return",
params = {
isAction = true,
event = data.onBack,
},
})
elseif data.canClose then
table.insert(Menu, 1, {
icon = "fas fa-circle-xmark",
header = " ",
txt = "Close",
params = {
isAction = true,
event = data.onExit and data.onExit or (function() exports[QBMenuExport]:closeMenu() end),
},
})
end
if data.header ~= nil then
local tempMenu = {}
for k, v in pairs(Menu) do tempMenu[k + 1] = v end
tempMenu[1] = { header = data.header, txt = data.headertxt or "", isMenuHeader = true }
Menu = tempMenu
end
for k in pairs(Menu) do
if not Menu[k].params or not Menu[k].params.event then
Menu[k].params = {
isAction = true,
event = Menu[k].onSelect or function() end,
}
end
if not Menu[k].header then Menu[k].header = " " end
if Menu[k].arrow then Menu[k].icon = "fas fa-angle-right" end
Menu[k].isMenuHeader = Menu[k].isMenuHeader or Menu[k].disable
end
TriggerEvent("jim_bridge:client:openMenu", Menu)
elseif Config.System.Menu == "ox" then
if Config.System.Menu == "ox" then
local index = nil
if data.onBack and not data.onSelected then
table.insert(Menu, 1, {
@@ -286,11 +245,57 @@ function openMenu(Menu, data)
function(data, menu)
menu.close()
end)
elseif Config.System.Menu == "lation" then
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
onSelect = data.onBack,
header = "Return",
})
end
for k in pairs(Menu) do
if data.onSelected and Menu[k].arrow then
Menu[k].icon = "fas fa-angle-right"
end
-- If no title, use header or txt as title/label.
if not Menu[k].title then
if Menu[k].header ~= nil and Menu[k].header ~= "" then
Menu[k].title = Menu[k].header
Menu[k].label = Menu[k].header
if Menu[k].txt then Menu[k].description = Menu[k].txt else Menu[k].description = "" end
else
Menu[k].title = Menu[k].txt
Menu[k].label = Menu[k].txt
end
end
-- Copy parameters from 'params' if available.
if Menu[k].params then
Menu[k].event = Menu[k].params.event
Menu[k].args = Menu[k].params.args or {}
end
if Menu[k].isMenuHeader then
Menu[k].readOnly = true
end
end
exports.lation_ui:registerMenu({
id = 'menu',
title = data.header,
onExit = data.onExit and data.onExit or nil,
subtitle = (data.headertxt and data.headertxt or ""),
options = Menu,
})
-- Show menu
exports.lation_ui:showMenu('menu')
end
end
--- A line break constant used for menu header formatting.
br = (Config.System.Menu == "ox" or Config.System.Menu == "gta") and "\n" or "<br>"
br = (Config.System.Menu == "ox" or Config.System.Menu == "gta" or Config.System.Menu == "lation") and "\n" or "<br>"
--- Checks if the current menu system is 'ox' or 'gta' for formatting purposes.
--- @return boolean boolean True if using ox or gta menus, otherwise false.

View File

@@ -34,7 +34,7 @@ end
if IsDuplicityVersion() then
local cache = nil
local timeout = GetGameTimer() + 5000 -- 5 seconds max wait
local timeout = GetGameTimer() + 120000 -- 2 minutes max wait (had to up this from 5 seconds because of slow servers)
-- Wait until jim_bridge is started and export is available
while not cache and GetGameTimer() < timeout do

View File

@@ -89,6 +89,14 @@ function craftingMenu(data)
-- Check if the player can carry the required items (server callback).
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
local usingStash = data.stashName ~= nil
Menu[#Menu+1] = {
icon = usingStash and "fas fa-boxes-stacked" or "fas fa-person",
header = "Material Source: "..(usingStash and "Job Stash" or "Player Inventory"),
disabled = true,
}
-- Process each recipe to create menu entries.
for i = 1, #Recipes do
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
@@ -238,7 +246,7 @@ function multiCraft(data)
})
local dialog = createInput(data.craftable.Header..(Config.System.Menu == "qb" and ": "..br.."How many to craft? "..br.."Max: "..carryMax or ""), {
((Config.System.Menu == "ox") and {
((Config.System.Menu == "ox" or Config.System.Menu == "lation") and {
type = "slider",
label = "How many to craft? "..br.."Max: "..carryMax,
required = true,
@@ -311,6 +319,7 @@ end
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
--- })
--- ```
function makeItem(data)
if CraftLock then return end
CraftLock = true
@@ -330,117 +339,144 @@ function makeItem(data)
local crafted, crafting = true, true
local cam = createTempCam(PlayerPedId(), data.coords)
startTempCam(cam)
-- Calculate total bartime if SingleProgress is enabled
local totalBartime = bartime * craftAmount
-- Run ingredient check and usage separately first
for i = 1, craftAmount do
for k, v in pairs(data.craft) do
if not excludeKeys[k] then
if type(v) == "table" then
for l, b in pairs(v) do
if isInventoryOpen() then
print("^1Error^7: ^2Inventory is open, you tried to break things")
crafted, crafting = false, false
stopTempCam()
ClearPedTasks(PlayerPedId())
if canReturn then craftingMenu(data) end
CraftLock = false
return
end
if crafting and progressBar({
label = "Using "..b.." "..Items[l].label,
time = 1000,
cancel = true,
dict = 'pickup_object',
anim = "putdown_low",
flag = 49,
icon = l,
}) then
TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', Items[l], "use", b)
else
crafted, crafting = false, false
break
end
Wait(200)
end
if not excludeKeys[k] and type(v) == "table" then
for l, b in pairs(v) do
if isInventoryOpen() then
print("^1Error^7: ^2Inventory is open, you tried to break things")
crafted, crafting = false, false
stopTempCam()
ClearPedTasks(PlayerPedId())
if canReturn then craftingMenu(data) end
CraftLock = false
return
end
if crafted then
local craftProp = nil
if prop then
craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true })
AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), prop.bone), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true, true, false, true, 1, true)
end
if data.sound then
local s = data.sound
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0)
end
if isInventoryOpen() then
--print("^1Error^7: ^2Inventory is open, you tried to break things")
crafted, crafting = false, false
crafted, crafting, CraftLock = false, false, false
return
end
if crafting and progressBar({
label = bartext..((metadata and metadata.label) or Items[data.item].label),
time = bartime,
cancel = true,
dict = animDict,
anim = anim,
flag = 49,
icon = data.item,
request = true,
}) then
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
currentToken = nil -- clear client cached token
CreateThread(function()
if data.craft["hasCrafted"] ~= nil then
debugPrint("^5Bridge^7: ^3hasCrafted ^2Found^7, ^2marking ^7'^4"..data.item.."^7' ^2as crafted for player^7")
data.craftable.craftedItems[data.item] = true
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
end
Wait(100)
if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give
jsonPrint(data.craft["exp"])
debugPrint("^6Bridge^7: ^3EXP ^2found^7, ^2giving exp for ^7'^4"..data.item.."^7'")
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
end
end)
if data.craftable.Recipes[1].oneUse == true then
removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
canReturn = false
end
if data.sound then
StopSound(data.sound.soundId)
end
if data.requiredItemfunc then
data.requiredItemfunc()
end
else
crafting = false
break
end
if craftProp then destroyProp(craftProp) end
if crafting and progressBar({
label = "Using "..b.." "..Items[l].label,
time = 1000,
cancel = true,
dict = 'pickup_object',
anim = "putdown_low",
flag = 49,
icon = l,
}) then
TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', Items[l], "use", b)
else
crafted, crafting = false, false
break
end
Wait(200)
end
end
end
Wait(500)
end
if not crafted then
stopTempCam()
ClearPedTasks(PlayerPedId())
if canReturn then craftingMenu(data) end
CraftLock = false
return
end
-- Handle SingleProgress option
if Config.Crafting.SingleProgress then
local craftProp = nil
if prop then
craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true })
AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), prop.bone), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true, true, false, true, 1, true)
end
if data.sound then
local s = data.sound
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0)
end
if crafting and progressBar({
label = bartext..((metadata and metadata.label) or Items[data.item].label).." x"..craftAmount,
time = totalBartime,
cancel = true,
dict = animDict,
anim = anim,
flag = 49,
icon = data.item,
request = true,
}) then
data.craft.amount = craftAmount
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
currentToken = nil -- clear client cached token
-- handle metadata and experience in a single go
if data.craft["hasCrafted"] ~= nil then
data.craftable.craftedItems[data.item] = true
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
end
if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give * craftAmount
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
end
if data.craftable.Recipes[1].oneUse == true then
removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
canReturn = false
end
if data.sound then
StopSound(data.sound.soundId)
end
if data.requiredItemfunc then
data.requiredItemfunc()
end
end
if craftProp then destroyProp(craftProp) end
else
-- Run the original loop for multiple progress bars
for i = 1, craftAmount do
if crafting and progressBar({
label = bartext..((metadata and metadata.label) or Items[data.item].label),
time = bartime,
cancel = true,
dict = animDict,
anim = anim,
flag = 49,
icon = data.item,
request = true,
}) then
TriggerServerEvent(getScript()..":Crafting:GetItem", data.item, data.craft, data.stashName, metadata, currentToken)
currentToken = nil
if data.craft["hasCrafted"] ~= nil then
data.craftable.craftedItems[data.item] = true
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
end
if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
end
if data.craftable.Recipes[1].oneUse == true then
removeItem("craftrecipe", 1, nil, data.craftable.Recipes[1].slot)
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
canReturn = false
end
if data.requiredItemfunc then
data.requiredItemfunc()
end
else
break
end
end
end
Wait(500)
stopTempCam()
CraftLock = false
if canReturn then craftingMenu(data) end
ClearPedTasks(PlayerPedId())
end
-------------------------------------------------------------
-- Server Event Handler: Crafted Item
-------------------------------------------------------------

View File

@@ -35,11 +35,26 @@ function drawText(image, input, style, oxStyleTable)
elseif Config.System.drawText == "ox" then
-- Append newline spacing to each input line.
local inputnum = countTable(input)
for k, v in pairs(input) do
input[k] = v.." \n"
input[k] = v..(inputnum ~= k and " \n" or "")
end
lib.showTextUI(table.concat(input), { icon = (image and radarTable[image] or image) or nil, position = 'left-center', style = oxStyleTable })
elseif Config.System.drawText == "lation" then
local inputnum = countTable(input)
for k, v in pairs(input) do
input[k] = v..(inputnum ~= k and " \n" or "")
end
exports.lation_ui:showText({
--title = " ",
description = table.concat(input),
keybind = nil,
icon = (image and radarTable[image] or image) or nil,
iconColor = '#3B82F6',
position = 'center-left'
})
elseif Config.System.drawText == "gta" then
-- Concatenate input lines and apply GTA style formatting.
for i = 1, #input do
@@ -88,6 +103,8 @@ function hideText()
exports[QBExport]:HideText()
elseif Config.System.drawText == "ox" then
lib.hideTextUI()
elseif Config.System.drawText == "lation" then
exports.lation_ui:hideText()
elseif Config.System.drawText == "gta" then
ClearAllHelpMessages()
elseif Config.System.drawText == "esx" then

View File

@@ -138,6 +138,98 @@ function createInput(title, opts)
)
return dialog
elseif Config.System.Menu == "lation" then
for i in pairs(opts) do
currentNum += 1
if opts[i] == nil then currentNum -= 1 goto skip end
if opts[i].type == "radio" then
for k in pairs(opts[i].options) do
opts[i].options[k].label = opts[i].options[k].text
end
options[currentNum] = {
type = "select",
required = opts[i].isRequired,
label = opts[i].label or opts[i].text,
name = opts[i].name,
default = opts[i].default or opts[i].options[1].value,
options = opts[i].options,
}
end
if opts[i].type == "number" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label or opts[i].text,
description = opts[i].txt and " - "..opts[i].txt or "",
required = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
}
end
if opts[i].type == "text" then
options[currentNum] = {
type = "input",
label = opts[i].label or opts[i].text,
description = (opts[i].txt and " - "..opts[i].txt or ""),
placeholder = opts[i].default,
required = opts[i].isRequired,
}
end
if opts[i].type == "select" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label or opts[i].text,
description = opts[i].txt and " - "..opts[i].txt or "",
required = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
if opts[i].type == "checkbox" then
for k in pairs(opts[i].options) do
if options[currentNum] then currentNum += 1 end
options[currentNum] = {
type = opts[i].type,
label = opts[i].options[k].text..(opts[i].txt and " - "..opts[i].txt or ""),
name = opts[i].options[k].value,
}
end
end
if opts[i].type == "color" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
required = opts[i].isRequired,
format = opts[i].format,
default = opts[i].default,
}
end
if opts[i].type == "slider" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
required = opts[i].required,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
::skip::
end
local dialog = exports.lation_ui:input({
title = title,
submitText = "Accept",
options = options
})
return dialog
elseif Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(opts),
title,

View File

@@ -275,7 +275,6 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
else
local amountToAdd = amount or 1
if isStarted(OXInv) then invName = OXInv
jsonPrint(info)
exports[OXInv]:AddItem(src, item, amountToAdd, info, slot)
elseif isStarted(QSInv) then invName = QSInv
@@ -655,7 +654,7 @@ function canCarry(itemTable, src)
elseif isStarted(TgiannInv) then
for k, v in pairs(itemTable) do
resultTable[k] = exports[TgiannInv]:CanCarryItem(source, k, v)
resultTable[k] = exports[TgiannInv]:CanCarryItem(src, k, v)
end
elseif isStarted(JPRInv) then
@@ -892,9 +891,28 @@ end
--- if doesItemExist(item) then print("item exists") end
--- ```
function doesItemExist(item)
if not item or item == "" then return false end
if not item or item == "" then
return false
end
if not Items or not next(Items) then
return item.." (Missing)"
end
if Items[item] ~= nil then
return true
return Items[item]
end
return false
end
function getItemLabel(item)
if not item or item == "" then
return ""
end
if not Items or not next(Items) then
return item.." (Missing)"
end
if Items[item] ~= nil then
return Items[item].label
end
return item.." (Missing)"
end

View File

@@ -135,6 +135,34 @@ function progressBar(data)
end
})
elseif Config.System.ProgressBar == "lation" then
if exports.lation_ui:progressBar({
label = data.label,
description = nil,
duration = debugMode and 1000 or data.time,
icon = data.icon,
useWhileDead = data.dead or false,
disable = {
combat = data.combat or true,
move = data.disableMovement or false,
car = data.disableMovement or false,
},
anim = {
dict = data.dict,
clip = data.anim,
},
prop = {
model = data.prop and data.prop.model,
pos = data.prop and (data.prop.pos or vec3(0, 0, 0)),
rot = data.prop and (data.prop.rot or vec3(0, 0, 0)),
bone = data.prop and (data.prop.bone or 0)
}
}) then
result = true
else
result = true
end
elseif Config.System.ProgressBar == "red" then
-- Currently only uses jim-redui if you choose this option
if exports["jim_bridge"]:redProgressBar({
@@ -204,6 +232,8 @@ function stopProgressBar()
exports[OXLibExport]:cancelProgress()
elseif Config.System.ProgressBar == "qb" then
TriggerEvent("progressbar:client:cancel")
elseif Config.System.ProgressBar == "lation" then
exports.lation_ui:cancelProgress()
elseif Config.System.ProgressBar == "gta" or Config.System.ProgressBar == "red" then
exports["jim_bridge"]:stopProgressBar()
end

View File

@@ -62,6 +62,20 @@ function triggerNotify(title, message, type, src)
else
TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message)
end
elseif Config.System.Notify == "lation" then
if not src then
exports.lation_ui:notify({
title = title,
message = message,
type = type or "success",
})
else
TriggerClientEvent("lation_ui:notify", src, {
title = title,
message = message,
type = type or "success",
})
end
elseif Config.System.Notify == "red" then
if isStarted("jim-redui") then

View File

@@ -32,6 +32,25 @@ function skillCheck(data)
elseif Config.System.skillCheck == "gta" then
exports.jim_bridge:skillCheck()
elseif Config.System.skillCheck == "lation" then
local Skillbar = exports.lation_ui:skillCheck("",
{
"easy",
"easy",
"easy"
},
{
"1",
"2",
"3",
"4"
})
if Skillbar then
result = true
else
result = false
end
else
result = true
end

View File

@@ -53,6 +53,7 @@ function searchCar(vehicle)
"Military", --20
"Commercial", --21
"Trains", --22
"Open Wheel", --23
}
if Vehicles then
for k, v in pairs(Vehicles) do

View File

@@ -6,6 +6,25 @@ local function loadTextureDict(dict)
end
end
local function loadAnimDict(animDict)
if not DoesAnimDictExist(animDict) then
print("^6Bridge^7: ^1ERROR^7: ^2Anim Dictionary^7 - '^6"..animDict.."^7' ^2does not exist in server") return
else
while not HasAnimDictLoaded(animDict) do RequestAnimDict(animDict) Wait(5) end
end
end
local function playAnim(animDict, animName, duration, flag, ped, speed)
loadAnimDict(animDict)
TaskPlayAnim(ped and ped or PlayerPedId(), animDict, animName, speed or 8.0, speed or -8.0, duration or 30000, flag or 50, 1, false, false, false)
end
local function stopAnim(animDict, animName, ped)
StopAnimTask(ped or PlayerPedId(), animDict, animName, 0.5)
StopAnimTask(ped or PlayerPedId(), animName, animDict, 0.5)
unloadAnimDict(animDict)
end
function redProgressBar(data)
local ped = PlayerPedId()

View File

@@ -1,8 +1,14 @@
2.0.19
2.0.20
- SetMetadata function now handles full metadata, not just durability
- Completely rework framework caching, it was lagging behind and breaking
- Add new hasFreeInvnentorySlots() function
- Add inv config file grabber, checks inventory configs for inventory weight/slots
- Add Full support for lation_ui
- Add "Open Wheel" to searchCar()
- Increase timeout for cache timer from 5 seconds to 2 minutes
- Fix GTA progressbar erroring on playAnim()
- Add Igredient location indicator when crafting
- Fix JPRInv checks breaking early when caching framework info
- Enhance doesItemExist() and add getItemLabel() function
- Add Support for old qb-inventory config loading
- Fix typo in cancarry when using Tgiann Inv
- Add support for single progressBar when crafting
https://github.com/jimathy/jim_bridge