Compare commits

...

53 Commits

Author SHA1 Message Date
Jim Shield
f09e34b1b5 Version Bump
`2.0.21` - `2.0.22``

- Support for qs-interface
- Fix polyzone minZ + maxZ handling
- Fix ox_target zone location to match qb-target
- Fix "Waiting for script start" loop bug
- Add condition for fading in entities
- Fix typo breaking scripts on gta skillbar fail
- Fix typo for gksphone invoice event
- Fix latio_ui TextUI placement
- Remove registerShop debug message
- Fix typo in print when removing codem stash item
- Fix function typo from origen inv documenation
2025-07-27 20:00:42 +01:00
Jim Shield
1146639d6f Update notify-discord.yml 2025-07-27 18:50:08 +01:00
Jim Shield
a9ac2e8897 Fix typo for orgien_inv getInventory 2025-07-24 21:49:37 +01:00
Jim Shield
1a1960f5a8 Fix entity blips looking for wrong preview script 2025-07-24 21:48:39 +01:00
Jim Shield
996b135973 Improve ox_target zone creation locations 2025-07-24 19:53:30 +01:00
Jim Shield
9337238832 fix typo in debugprint for codem stash staving 2025-07-23 19:53:07 +01:00
Jim Shield
ee049a953c Make ox_lib polyzone conversion code more accurate 2025-07-23 16:46:10 +01:00
Jim Shield
adfe3e8e08 Remove registerShop debug message
People keep thinking this is a massive error, its literally just stating that there isn't a script found to register a shop. not all inventories have these
2025-07-22 16:00:27 +01:00
Jim Shield
71c9c99db7 fix location typo for lation_ui drawtext 2025-07-22 13:04:46 +01:00
Jim Shield
dd66083f45 fix typo for gks sendPhoneInvoice function
when creating a `phone invoice` the function was checking if `gks-phone` was started, but I believe the script folder name is default `gksphone`
2025-07-22 11:44:32 +01:00
Jim Shield
9ddd6422bd fix typo breaking gta skillcheck if failed 2025-07-22 01:25:14 +01:00
Jim Shield
7d24c274cd Fix ox_lib polyzone maxZ and minZ math 2025-07-20 23:20:43 +01:00
Jim Shield
3911824fc6 Add resourceName sendback for onResource functions 2025-07-17 12:47:36 +01:00
Jim Shield
9729d2fef1 Add condition for stopping entity fade in 2025-07-17 12:40:43 +01:00
Jim Shield
3cb4005889 Merge pull request #51 from oosayeroo/main 2025-07-17 12:39:48 +01:00
Jim Shield
bf759ebaf8 Fix inventory config check to check "starting" scripts 2025-07-17 12:38:31 +01:00
Jim Shield
3d0eb3df65 fix PolyZone support for minZ maxZ 2025-07-17 12:37:41 +01:00
oosayeroo
fe09d0f773 qs notify + progressbar + makeVeh edit 2025-07-16 08:18:55 +01:00
Jim Shield
880d2b0233 Version Bump
`2.0.20` - `2.0.21`
2025-07-14 11:29:29 +01:00
Jim Shield
5865d2eeb2 increase authToken timeout from 5 sec to 1 min 2025-07-13 17:41:06 +01:00
Jim Shield
528735abd1 fix anim flag on lation_ui progressbars 2025-07-12 22:24:28 +01:00
Jim Shield
d4df995c71 fix lation_ui progressbars returning true when cancelled 2025-07-12 13:05:10 +01:00
Jim Shield
3b13fa663e Merge pull request #49 from derTweaky/patch-1 2025-07-12 11:31:16 +01:00
derTweaky
e973c64ffd Update makePed.lua
Typo fix
2025-07-12 12:27:31 +02:00
Jim Shield
6ecf10c084 fix inventory config loading if vector4 is present 2025-07-11 21:16:45 +01:00
Jim Shield
79e765fbb0 Stop crafting trying to check blank stash names
Skimmed through the stash checking and editing functions and added more "security" for handling nil or blank stash names
2025-07-11 13:33:44 +01:00
Jim Shield
d4a08d5e2b Rewrite waitForSharedLoad() to fix issues 2025-07-10 22:41:28 +01:00
Jim Shield
eeb3f40018 fix break when trying to fade ents too far away 2025-07-10 22:16:26 +01:00
Jim Shield
e6b4c1d13c Add fade in animations for entity spawning 2025-07-09 15:10:53 +01:00
Jim Shield
6ecbc3392a make breaktool() smarter and add useToolDegrade() 2025-07-09 15:09:41 +01:00
Jim Shield
7f10c394de make cache timeout optional 2025-07-09 15:09:14 +01:00
Jim Shield
fae099fd93 fix progressbar looking for missing function 2025-07-08 11:20:52 +01:00
Jim Shield
64d43b54e9 fix getPlayer() checking users not logged in yet 2025-07-08 00:48:39 +01:00
Jim Shield
d4abdbc93f fix typo stopping mysql cache loading on esx 2025-07-07 17:56:23 +01:00
Jim Shield
b595453efe Rename metahandler functions
Realised I named the player and item metadata functions too closely and made it confusing, this separates them better
2025-07-07 17:47:04 +01:00
Jim Shield
d255034c02 Increase timer again on cache timeout
Also moved around the cache timers to be a bit more accurate (timers start when the cache creation is triggered)
2025-07-07 02:11:36 +01:00
Jim Shield
d6fd2cc28c Fix toggleDuty() for ESX 2025-07-07 00:09:34 +01:00
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
34 changed files with 716 additions and 359 deletions

View File

@@ -8,10 +8,10 @@ on:
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 run: |
with: curl -X POST http://${{ secrets.DISCORDBOT }}:3000/github-commits \
webhook: ${{ secrets.DISCORD_WEBHOOK }} -H "Content-Type: application/json" \
template: "avatar-with-link" -d '${{ toJson(github.event) }}'
include-extras: true

View File

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

View File

@@ -21,12 +21,12 @@ This module provides access and control over player metadata, which is useful fo
print("Player stress level:", stress) print("Player stress level:", stress)
``` ```
- **SetMetadata(player, key, value)** - **setPlayerMetadata(player, key, value)**
⚠️ Server side only ⚠️ Server side only
- Updates or assigns a value to a specific metadata key for a player. - Updates or assigns a value to a specific metadata key for a player.
- The function updates the player's metadata using the active core export. - The function updates the player's metadata using the active core export.
- **Example:** - **Example:**
```lua ```lua
SetMetadata(player, "stress", 0) setPlayerMetadata(player, "stress", 0)
``` ```

View File

@@ -41,15 +41,27 @@ local cache = {
Jobs = {}, Jobs = {},
Gangs = {}, 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).."s)"
end
startTimer("Cache") startTimer("Items") startTimer("Vehicles") startTimer("Jobs") startTimer("InvWeight") startTimer("InvSlots")
-- Helper function to check if resource exists in server (instead of if it is already started) -- Helper function to check if resource exists in server (instead of if it is already started)
local function checkExists(resourceName) local function checkExists(resourceName)
return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped") return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped")
end end
-- Ensure oxmysql resource is loaded -- Ensure oxmysql resource is loaded
local fileLoader = assert(load(LoadResourceFile("oxmysql", ('lib/MySQL.lua')), ('@@oxmysql/lib/MySQL.lua'))) if checkExists(Exports.OXCoreExport) or checkExists(Exports.ESXExport) then
fileLoader() local fileLoader = assert(load(LoadResourceFile("oxmysql", ('lib/MySQL.lua')), ('@@oxmysql/lib/MySQL.lua')))
fileLoader()
end
if checkExists(Exports.OXCoreExport) then if checkExists(Exports.OXCoreExport) then
-- Detected OX_Core in server, wait for it to be started if needed -- Detected OX_Core in server, wait for it to be started if needed
@@ -155,6 +167,7 @@ elseif checkExists(Exports.RSGExport) then
itemResource = Exports.RSGExport itemResource = Exports.RSGExport
cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
end end
endTimer("Items")
--------------------- ---------------------
--- Load Vehicles --- --- Load Vehicles ---
@@ -197,6 +210,7 @@ elseif checkExists(Exports.RSGExport) then
cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
end end
endTimer("Vehicles")
--------------------- ---------------------
----- Load Jobs ----- ----- Load Jobs -----
@@ -267,6 +281,7 @@ elseif checkExists(Exports.RSGExport) then
cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs
end end
endTimer("Jobs")
-- Fallback if nil or empty -- Fallback if nil or empty
if cache.Items == nil or not next(cache.Items) then if cache.Items == nil or not next(cache.Items) then
@@ -305,12 +320,19 @@ local function getInventoryConfig(resource, data)
if not content then return nil, "Failed to load file" end if not content then return nil, "Failed to load file" end
local env = { local env = {
GetConvar = GetConvar, vector3 = vector3, Citizen = Citizen, GetConvar = GetConvar,
GetResourceState = GetResourceState, exports = exports, vector3 = vector3,
DependencyCheck = DependencyCheck or function() return nil end, vector4 = vector4,
Citizen = Citizen,
GetResourceState = GetResourceState,
exports = exports,
DependencyCheck = function() return nil end,
} }
local fn, err = load(content, '@'..data.file, 't', env) local fn, err = load(content, '@'..data.file, 't', env)
--local success, err = xpcall(fn, function(e)
-- print(debug.traceback("Error while executing qs-inventory config:\n" .. tostring(e), 2))
--end)
if not fn then return nil, "Failed to compile config: " .. err end if not fn then return nil, "Failed to compile config: " .. err end
if not pcall(fn) then return nil, "Error executing config file" end if not pcall(fn) then return nil, "Error executing config file" end
@@ -327,14 +349,18 @@ local function getInventoryConfig(resource, data)
end end
end end
-- Inventory table -- Inventory table
local invWeightTable = { local invWeightTable = {
[Exports.OXInv] = { convars = { [Exports.OXInv] = { convars = {
weight = { key = "inventory:weight", default = 30000 }, weight = { key = "inventory:weight", default = 30000 },
slots = { key = "inventory:slots", default = 40 } 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.JPRInv] = { file = "configs/main_config.lua", path = { "MaxInventoryWeight" }, slotPath = { "MaxInventorySlots" } },
[Exports.PSInv] = { file = "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" } }, [Exports.QSInv] = { file = "config/config.lua", path = { "InventoryWeight", "weight" }, slotPath = { "InventoryWeight", "slots" } },
@@ -348,9 +374,25 @@ local invWeightTable = {
local invResource = "" local invResource = ""
for script, data in pairs(invWeightTable) do for script, data in pairs(invWeightTable) do
if checkExists(script) then 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
while GetResourceState(script) ~= "started" and GetResourceState(script) ~= "stopped" do
Wait(100)
print("Waiting for script start")
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 if not lookup then
print(("^1ERROR^7: ^1Config loader failed from ^5%s^7: ^1%s^7"):format(script, err or "unknown")) print(("^1ERROR^7: ^1Config loader failed from ^5%s^7: ^1%s^7"):format(script, err or "unknown"))
break break
@@ -366,14 +408,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")) print(("%s^7: ^1Failed to get ^7Inventory%s ^1from ^5%s^7: ^1%s^7"):format(warnType, label, script, perr or "unknown"))
end end
resolve("Weight", data.path or { "MaxWeight" }) resolve("Weight", used.path or { "MaxWeight" })
resolve("Slots", data.slotPath or { "MaxSlots" }) resolve("Slots", used.slotPath or { "MaxSlots" })
invResource = script:gsub("-", "^7-^4"):gsub("_", "^7_^4") invResource = script:gsub("-", "^7-^4"):gsub("_", "^7_^4")
break break
end end
::skip::
end end
endTimer("InvWeight")
endTimer("InvSlots")
CreateThread(function() CreateThread(function()
local counts = { local counts = {
@@ -383,15 +427,18 @@ CreateThread(function()
if type(v) ~= "number" then for count in pairs(v) do counts[k] += 1 end end if type(v) ~= "number" then for count in pairs(v) do counts[k] += 1 end end
end end
if cache.InventoryWeight then 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"])
end end
if cache.InventorySlots then 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"])
end 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"..itemResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Items).."^2 Items ^7"..timers["Items"])
print("^6FrameworkCache^7: ^4"..vehResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Vehicles).."^2 Vehicles^7") print("^6FrameworkCache^7: ^4"..vehResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Vehicles).."^2 Vehicles ^7"..timers["Vehicles"])
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.Jobs).."^2 Jobs ^7"..timers["Jobs"])
print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Gangs).."^2 Gangs^7") print("^6FrameworkCache^7: ^4"..jobResource:gsub("-", "^7-^4"):gsub("_", "^7_^4").."^2 Loaded ^3"..tostring(counts.Gangs).."^2 Gangs ^7"..timers["Jobs"])
endTimer("Cache")
print("^6FrameworkCache^7: ^2Cache Ready ^7"..timers["Cache"])
cacheReady = true
end) end)
RegisterNetEvent("jim_bridge:requestCache", function() RegisterNetEvent("jim_bridge:requestCache", function()
@@ -400,15 +447,16 @@ RegisterNetEvent("jim_bridge:requestCache", function()
end) end)
exports("GetSharedData", function() exports("GetSharedData", function()
-- Wait for data to be ready before returning it -- Wait indefinitely (with periodic checks) until cacheReady is true
local timeout = GetGameTimer() + 5000 local timeout = GetGameTimer() + 20000 -- 20 second failsafe timeout
while ( while not cacheReady and GetGameTimer() < timeout do
not cache or Wait(100)
(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)
end 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 return cache
end) end)

View File

@@ -1,6 +1,6 @@
name "Jim_Bridge" name "Jim_Bridge"
author "Jimathy" author "Jimathy"
version "2.0.19" version "2.0.22"
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

@@ -53,13 +53,13 @@ if isServer() then
if authCooldown[src] then if authCooldown[src] then
debugPrint("^1Auth^7: ^3Cooldown active^7 for Player ^1"..src.."^7, ignoring additional auth request") debugPrint("^1Auth^7: ^3Cooldown active^7 for Player ^1"..src.."^7, ignoring additional auth request")
return "" return AuthEvent
end end
debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent) debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent)
authCooldown[src] = true authCooldown[src] = true
SetTimeout(5000, function() -- 5 second cooldown SetTimeout(60000, function() -- 1 minute cooldown
authCooldown[src] = nil authCooldown[src] = nil
end) end)

View File

@@ -117,8 +117,7 @@ function onResourceStart(func, thisScript)
debugPrint("^6Bridge^7: ^2Shared Load Detected^7.") debugPrint("^6Bridge^7: ^2Shared Load Detected^7.")
hasPrinted = true hasPrinted = true
end end
if isStarted(ESXExport) then Wait(10000) end func(resourceName)
func()
end end
end end
end) end)
@@ -137,7 +136,7 @@ function onResourceStop(func, thisScript)
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^7()") debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^7()")
AddEventHandler('onResourceStop', function(resourceName) AddEventHandler('onResourceStop', function(resourceName)
if getScript() == resourceName and (thisScript or true) then if getScript() == resourceName and (thisScript or true) then
func() func(resourceName)
end end
end) end)
end end
@@ -194,14 +193,13 @@ end
local messageShown = false local messageShown = false
function waitForSharedLoad() function waitForSharedLoad()
local timeout = 100000 -- 10 seconds in milliseconds local timeout = GetGameTimer() + 900000 -- 15 minutes max wait (had to up this from 2 minutes because of slow servers)
local startTime = GetGameTimer()
local loaded = true
local loop = 0 local loop = 0
while ((not Jobs or not next(Jobs)) or while ((not Jobs or not next(Jobs)) or
(not Items or not next(Items)) or (not Items or not next(Items)) or
(not Vehicles or not next(Vehicles)) (not Vehicles or not next(Vehicles))
) and ((GetGameTimer() - startTime) < timeout) do ) and (timeout and GetGameTimer() < timeout) do
if loop >= 3 and not messageShown then if loop >= 3 and not messageShown then
if (not Jobs or not next(Jobs)) then if (not Jobs or not next(Jobs)) then
print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded") print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded")
@@ -214,19 +212,16 @@ function waitForSharedLoad()
end end
messageShown = true messageShown = true
end end
--print((GetGameTimer() - startTime) < timeout)
Wait(1000) Wait(1000)
if Jobs and Items and Vehicles then
--print("^6Bridge^7: ^2Jobs, Items, and Vehicles Loaded^7.")
loaded = true
break
end
loop += 1 loop += 1
end end
if not loaded then
if Jobs and Items and Vehicles then
debugPrint("^6Bridge^7: ^2Jobs, Items, and Vehicles Loaded^7.")
return true
else
print("^4Error^7: ^1Timeout reached while waiting for shared load^7.") print("^4Error^7: ^1Timeout reached while waiting for shared load^7.")
return false return false
else
return true
end end
end end

View File

@@ -47,48 +47,7 @@
--- }) --- })
--- ``` --- ```
function openMenu(Menu, data) function openMenu(Menu, data)
if Config.System.Menu == "jim" then if Config.System.Menu == "ox" 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
local index = nil local index = nil
if data.onBack and not data.onSelected then if data.onBack and not data.onSelected then
table.insert(Menu, 1, { table.insert(Menu, 1, {
@@ -286,11 +245,57 @@ function openMenu(Menu, data)
function(data, menu) function(data, menu)
menu.close() menu.close()
end) 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
end end
--- A line break constant used for menu header formatting. --- 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. --- 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. --- @return boolean boolean True if using ox or gta menus, otherwise false.

View File

@@ -34,10 +34,10 @@ end
if IsDuplicityVersion() then if IsDuplicityVersion() then
local cache = nil local cache = nil
local timeout = GetGameTimer() + 5000 -- 5 seconds max wait local timeout = GetGameTimer() + 900000 -- 15 minutes max wait (had to up this from 2 minutes because of slow servers)
-- Wait until jim_bridge is started and export is available -- Wait until jim_bridge is started and export is available
while not cache and GetGameTimer() < timeout do while not cache and (timeout and GetGameTimer() < timeout) do
if GetResourceState("jim_bridge"):find("start") then if GetResourceState("jim_bridge"):find("start") then
local success, result = pcall(function() local success, result = pcall(function()
return exports["jim_bridge"]:GetSharedData() return exports["jim_bridge"]:GetSharedData()
@@ -49,7 +49,7 @@ if IsDuplicityVersion() then
Wait(100) Wait(100)
end end
if not cache then if timeout and not cache then
print("^1ERROR^7: ^2jim_bridge export not available after timeout^7.") print("^1ERROR^7: ^2jim_bridge export not available after timeout^7.")
return return
end end

View File

@@ -89,6 +89,14 @@ function craftingMenu(data)
-- Check if the player can carry the required items (server callback). -- Check if the player can carry the required items (server callback).
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable) local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
local usingStash = data.stashName ~= nil and data.stashName ~= ""
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. -- Process each recipe to create menu entries.
for i = 1, #Recipes do for i = 1, #Recipes do
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end 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 ""), { 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", type = "slider",
label = "How many to craft? "..br.."Max: "..carryMax, label = "How many to craft? "..br.."Max: "..carryMax,
required = true, required = true,
@@ -311,6 +319,7 @@ end
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" }, --- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
--- }) --- })
--- ``` --- ```
function makeItem(data) function makeItem(data)
if CraftLock then return end if CraftLock then return end
CraftLock = true CraftLock = true
@@ -330,117 +339,144 @@ function makeItem(data)
local crafted, crafting = true, true local crafted, crafting = true, true
local cam = createTempCam(PlayerPedId(), data.coords) local cam = createTempCam(PlayerPedId(), data.coords)
startTempCam(cam) 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 i = 1, craftAmount do
for k, v in pairs(data.craft) do for k, v in pairs(data.craft) do
if not excludeKeys[k] then if not excludeKeys[k] and type(v) == "table" then
if type(v) == "table" then for l, b in pairs(v) do
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 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")
crafted, crafting = false, false
stopTempCam() stopTempCam()
ClearPedTasks(PlayerPedId()) ClearPedTasks(PlayerPedId())
if canReturn then craftingMenu(data) end if canReturn then craftingMenu(data) end
CraftLock = false CraftLock = false
return return
end end
if crafting and progressBar({
if crafted then label = "Using "..b.." "..Items[l].label,
local craftProp = nil time = 1000,
if prop then cancel = true,
craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true }) dict = 'pickup_object',
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) anim = "putdown_low",
end flag = 49,
if data.sound then icon = l,
local s = data.sound }) then
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0) TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', Items[l], "use", b)
end else
if isInventoryOpen() then crafted, crafting = false, false
--print("^1Error^7: ^2Inventory is open, you tried to break things") break
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
end end
Wait(200)
end end
end end
end end
Wait(500)
end 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:setPlayerMetadata", "craftedItems", data.craftable.craftedItems)
end
if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give * craftAmount
triggerCallback(getScript()..":server:setPlayerMetadata", "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:setPlayerMetadata", "craftedItems", data.craftable.craftedItems)
end
if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give
triggerCallback(getScript()..":server:setPlayerMetadata", "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() stopTempCam()
CraftLock = false CraftLock = false
if canReturn then craftingMenu(data) end if canReturn then craftingMenu(data) end
ClearPedTasks(PlayerPedId()) ClearPedTasks(PlayerPedId())
end end
------------------------------------------------------------- -------------------------------------------------------------
-- Server Event Handler: Crafted Item -- Server Event Handler: Crafted Item
------------------------------------------------------------- -------------------------------------------------------------

View File

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

View File

@@ -138,6 +138,98 @@ function createInput(title, opts)
) )
return dialog 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 elseif Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(opts), WarMenu.CreateMenu(tostring(opts),
title, title,

View File

@@ -111,7 +111,7 @@ function getPlayerInv(src)
if src then if src then
grabInv = exports[OrigenInv]:getInventory(src) grabInv = exports[OrigenInv]:getInventory(src)
else else
grabInv = exports[OrigenInv]:getInventory() grabInv = exports[OrigenInv]:GetInventory()
end end
elseif isStarted(CoreInv) then elseif isStarted(CoreInv) then
@@ -232,6 +232,7 @@ function invImg(item)
elseif isStarted(PSInv) then elseif isStarted(PSInv) then
imgLink = "nui://"..PSInv.."/html/images/"..(Items[item].image or "") imgLink = "nui://"..PSInv.."/html/images/"..(Items[item].image or "")
elseif isStarted(TgiannInv) then elseif isStarted(TgiannInv) then
imgLink = "nui://inventory_images/images/"..(Items[item].image or "") imgLink = "nui://inventory_images/images/"..(Items[item].image or "")

View File

@@ -275,7 +275,6 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
else else
local amountToAdd = amount or 1 local amountToAdd = amount or 1
if isStarted(OXInv) then invName = OXInv if isStarted(OXInv) then invName = OXInv
jsonPrint(info)
exports[OXInv]:AddItem(src, item, amountToAdd, info, slot) exports[OXInv]:AddItem(src, item, amountToAdd, info, slot)
elseif isStarted(QSInv) then invName = QSInv elseif isStarted(QSInv) then invName = QSInv
@@ -377,130 +376,73 @@ end
--- ```lua --- ```lua
--- breakTool({ item = "drill", damage = 10 }) --- breakTool({ item = "drill", damage = 10 })
--- ``` --- ```
function breakTool(data) -- WIP function breakTool(data)
local durability, slot = getDurability(data.item) local metadata, slot = getItemMetadata(data.item)
if not durability then durability = 100 end metadata = metadata or {}
durability -= data.damage if not metadata.durability then metadata.durability = 100 end
if durability <= 0 then metadata.durability -= data.damage
removeItem(data.item, 1) if metadata.durability <= 0 then
removeItem(data.item, 1, nil, slot)
local breakId = GetSoundId() local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0) PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
else else
TriggerServerEvent(getScript()..":server:setMetaData", { item = data.item, slot = slot, metadata = { durability = durability } }) TriggerServerEvent(getScript()..":server:setItemMetaData", { item = data.item, slot = slot, metadata = metadata })
end end
end end
--- Retrieves the durability and slot number of an item in a player's inventory.
--- Reduces the uses of a tool by a specified damage amount.
--- ---
--- Searches through the player's inventory for the specified item. --- If uses reaches zero or below, the tool is removed and a break sound is played.
--- ---
--- @param item string The item name. --- @param data table Contains:
--- @return number|nil number The durability, or nil if not found. --- - item (string): The tool's name.
--- @return number|nil number The slot number, or nil if not found. --- - damage (number): The damage % to apply.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- local durability, slot = getDurability("drill") --- useToolDegrade({ item = "drill", maxUse = 10 })
--- if durability then
--- print("Durability:", durability)
--- print("Slot:", slot)
--- end
--- ``` --- ```
function getDurability(item) function useToolDegrade(data) -- WIP
local metadata, slot = getItemMetadata(data.item)
metadata = metadata or {}
if not metadata["Uses Left"] then metadata["Uses Left"] = data.maxUse end
metadata["Uses Left"] -= 1
if metadata["Uses Left"] <= 0 then
removeItem(data.item, 1, nil, slot)
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
else
TriggerServerEvent(getScript()..":server:setItemMetaData", { item = data.item, slot = slot, metadata = metadata })
end
end
function getItemMetadata(item, slot, src)
local lowestSlot = 100 local lowestSlot = 100
local durability = nil local chosenSlot = slot
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then local metadata = {}
local itemcheck = Core.Functions.GetPlayerData().items
local itemcheck = getPlayerInv(src)
if chosenSlot then
for k, v in pairs(itemcheck) do
if v.name == item and v.slot == chosenSlot then
lowestSlot = v.slot
metadata = itemcheck[k].info or itemcheck[k].metadata or {}
debugPrint("^6Bridge^7: ^2Found metadata for item ^3"..item.." ^2in slot ^3"..lowestSlot.."^7")
end
end
else
for k, v in pairs(itemcheck) do for k, v in pairs(itemcheck) do
if v.name == item then if v.name == item then
if v.slot <= lowestSlot then if v.slot <= lowestSlot then
lowestSlot = v.slot lowestSlot = v.slot
durability = itemcheck[k].info and itemcheck[k].info.durability or nil metadata = itemcheck[k].info or itemcheck[k].metadata or {}
end end
end end
end end
end end
return metadata, lowestSlot
if isStarted(OXInv) then
local itemcheck = exports[OXInv]:Search('slots', item)
for k, v in pairs(itemcheck) do
if v.slot <= lowestSlot then
debugPrint(v.slot, itemcheck[k].metadata.durability)
lowestSlot = v.slot
durability = v.metadata and v.metadata.durability or nil
end
end
end
if isStarted(QSInv) then
local itemcheck = exports[QSInv]:getUserInventory()
for _, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
durability = v.info and v.info.durability or nil
end
end
end
if isStarted(CoreInv) then
local itemcheck = exports[CoreInv]:getInventory()
for _, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
durability = v.metadata and v.metadata.durability or nil
end
end
end
if isStarted(CodeMInv) then
local itemcheck = exports[CodeMInv]:GetClientPlayerInventory()
for _, v in pairs(itemcheck) do
if v.name == item and tonumber(v.slot) <= lowestSlot then
lowestSlot = tonumber(v.slot)
durability = v.info and v.info.durability or nil
end
end
end
if isStarted(OrigenInv) then
local itemcheck = exports[OrigenInv]:getPlayerInventory()
for _, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
durability = v.metadata and v.metadata.durability or nil
end
end
end
if isStarted(TgiannInv) then
local itemcheck = exports[TgiannInv]:GetPlayerItems()
for _, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
durability = v.metadata and v.metadata.durability or nil
end
end
end
-- For ESX default inventory (es_extended)
if ESX and isStarted(ESXExport) then
local xPlayer = ESX.GetPlayerData() or {}
if xPlayer.inventory then
for _, v in ipairs(xPlayer.inventory) do
if v.name == item then
-- Optionally use a slot field if available; otherwise, use the index
if v.slot and v.slot <= lowestSlot then
lowestSlot = v.slot
end
if v.metadata and v.metadata.durability then
durability = v.metadata.durability
end
end
end
end
end
return durability, lowestSlot
end end
--- Server event handler to set metadata for an item. --- Server event handler to set metadata for an item.
@@ -514,9 +456,9 @@ end
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } }) --- TriggerServerEvent("script:server:setItemMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } })
--- ``` --- ```
RegisterNetEvent(getScript()..":server:setMetaData", function(data, src) RegisterNetEvent(getScript()..":server:setItemMetaData", function(data, src)
local src = src or source local src = src or source
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
--debugPrint(src, data.item, 1, data.slot) --debugPrint(src, data.item, 1, data.slot)
@@ -655,7 +597,7 @@ function canCarry(itemTable, src)
elseif isStarted(TgiannInv) then elseif isStarted(TgiannInv) then
for k, v in pairs(itemTable) do for k, v in pairs(itemTable) do
resultTable[k] = exports[TgiannInv]:CanCarryItem(source, k, v) resultTable[k] = exports[TgiannInv]:CanCarryItem(src, k, v)
end end
elseif isStarted(JPRInv) then elseif isStarted(JPRInv) then
@@ -892,9 +834,28 @@ end
--- if doesItemExist(item) then print("item exists") end --- if doesItemExist(item) then print("item exists") end
--- ``` --- ```
function doesItemExist(item) 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 if Items[item] ~= nil then
return true return Items[item]
end end
return false 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 end

View File

@@ -104,16 +104,31 @@ function toggleDuty()
TriggerServerEvent("QBCore:ToggleDuty") TriggerServerEvent("QBCore:ToggleDuty")
Wait(100) Wait(100)
onDuty = getPlayer().onDuty onDuty = getPlayer().onDuty
elseif isStarted(RSGExport) then elseif isStarted(RSGExport) then
TriggerServerEvent("RSGCore:ToggleDuty") TriggerServerEvent("RSGCore:ToggleDuty")
Wait(100) Wait(100)
onDuty = getPlayer().onDuty onDuty = getPlayer().onDuty
else
elseif isStarted(ESXExport) then
local tempJob = ESX.GetPlayerData().job
tempJob.onDuty = not onDuty
ESX.SetPlayerData("job", tempJob)
onDuty = getPlayer().onDuty
if onDuty then if onDuty then
triggerNotify(nil, "Now on duty", "success") triggerNotify(nil, "Now on duty", "success")
else else
triggerNotify(nil, "Now off duty", "success") triggerNotify(nil, "Now off duty", "success")
end end
else
onDuty = not onDuty
if onDuty then
triggerNotify(nil, "Now on duty", "success")
else
triggerNotify(nil, "Now off duty", "success")
end
end end
end end

View File

@@ -187,7 +187,7 @@ end
--- ``` --- ```
function playAnim(animDict, animName, duration, flag, ped, speed) function playAnim(animDict, animName, duration, flag, ped, speed)
loadAnimDict(animDict) loadAnimDict(animDict)
debugPrint("^6Bridge^7: ^3playAnim^7() ^2Triggered^7: ", animDict, animName) debugPrint("^6Bridge^7: ^3playAnim^7() ^2Triggered^7: ", animDict, animName, flag)
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) 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 end
@@ -238,4 +238,25 @@ function playGameSound(audioBank, soundSet, soundRef, coords, synced, range)
PlaySoundFromEntity(soundId, soundRef, coords, soundSet, synced, 1.0) PlaySoundFromEntity(soundId, soundRef, coords, soundSet, synced, 1.0)
end end
ReleaseScriptAudioBank(audioBank) ReleaseScriptAudioBank(audioBank)
end
-- Experimental, add fade in for spawned entities
function fadeInEnt(ent, duration)
if not DoesEntityExist(ent) then return end
duration = duration or 500 -- in ms
local fadeSteps = 20
local stepTime = duration / fadeSteps
SetEntityAlpha(ent, 0, false)
SetEntityVisible(ent, true, false)
SetEntityLocallyInvisible(ent) -- sometimes helps prevent "pop-in" in early frames
for i = 1, fadeSteps do
Wait(stepTime)
local newAlpha = math.floor((i / fadeSteps) * 255)
SetEntityAlpha(ent, newAlpha, false)
end
-- Restore full visibility
ResetEntityAlpha(ent)
end end

View File

@@ -133,16 +133,19 @@ function makeEntityBlip(data)
AddTextComponentString(tostring(data.name)) AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip) EndTextCommandSetBlipName(blip)
-- Handle preview image if certain resources are running -- Handle preview image if certain resources are running
if isStarted("fs_smallresources") or isStarted("blip-info") or isStarted("blipinfo") then if isStarted("jim-blipcontroller") then
if data.preview then if data.preview then
local txname = string.gsub(tostring(data.name..'preview'), "[ ()~]", "") local txname = string.gsub(tostring(data.name..'preview'..string.gsub(data.coords.z, "%.", "")), "[ ()~]", "")
if data.preview:find("http") or data.preview:find("nui") then if data.preview:find("http") or data.preview:find("nui") then
createDui(txname, data.preview, vec2(512, 256), scriptTxd) createDui(txname, data.preview, vec2(512, 256), scriptTxd)
else else
CreateRuntimeTextureFromImage(scriptTxd, txname, data.preview) CreateRuntimeTextureFromImage(scriptTxd, txname, data.preview)
end end
exports["fs_smallresources"]:SetBlipInfoImage(blip, getScript()..'previewTxd', txname) exports["jim-blipcontroller"]:ShowBlipInfo(blip, {
exports["fs_smallresources"]:SetBlipInfoTitle(blip, data.name, false) title = data.name,
dict = getScript()..'scriptTxd',
tex = txname,
})
end end
end end
end end

View File

@@ -55,14 +55,14 @@ end
-- ```lua -- ```lua
-- local ped = makePed(pedData, pedCoords, true, false, nil, {'animDict', 'animName'}, true) -- local ped = makePed(pedData, pedCoords, true, false, nil, {'animDict', 'animName'}, true)
-- ``` -- ```
function makePed(data, coords, freeze, collision, scenario, anim, synced) function makePed(data, coords, freeze, collision, scenario, anim, synced, fade)
local ped = nil local ped = nil
local model = nil local model = nil
if type(data) == "table" then if type(data) == "table" then
model = data.model model = data.model
loadModel(data.model) loadModel(data.model)
ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.03, coords.w, synced and synced or false, false) ped = CreatePed(0, model, coords.x, coords.y, coords.z - 1.03, coords.w, synced and synced or false, false)
SetEntityAlpha(ped, 0, false)
-- Inheritance -- Inheritance
SetPedHeadBlendData(ped, data.custom.faceFather, data.custom.faceMother, data.custom.raceShape, data.custom.skinFather, data.custom.skinMother, data.custom.raceSkin, data.custom.faceMix or 0, data.custom.skinMix or 0, data.custom.raceMix or 0, false) SetPedHeadBlendData(ped, data.custom.faceFather, data.custom.faceMother, data.custom.raceShape, data.custom.skinFather, data.custom.skinMother, data.custom.raceSkin, data.custom.faceMix or 0, data.custom.skinMix or 0, data.custom.raceMix or 0, false)
@@ -116,6 +116,7 @@ function makePed(data, coords, freeze, collision, scenario, anim, synced)
loadModel(model) loadModel(model)
if gameName == "rdr3" then if gameName == "rdr3" then
ped = CreatePed(model, coords.x, coords.y, coords.z - 1.03, coords.w, synced or false, false) ped = CreatePed(model, coords.x, coords.y, coords.z - 1.03, coords.w, synced or false, false)
SetEntityAlpha(ped, 0, false)
SetEntityVisible(ped, 1) -- SetEntityVisible SetEntityVisible(ped, 1) -- SetEntityVisible
SetEntityAlpha(ped, 255, false) -- SetEntityAlpha SetEntityAlpha(ped, 255, false) -- SetEntityAlpha
SetRandomOutfitVariation(ped, true) -- Invisible without SetRandomOutfitVariation(ped, true) -- Invisible without
@@ -141,6 +142,11 @@ function makePed(data, coords, freeze, collision, scenario, anim, synced)
end end
unloadModel(model) unloadModel(model)
Peds[keyGen()..keyGen()] = ped Peds[keyGen()..keyGen()] = ped
if fade ~= false then
CreateThread(function()
fadeInEnt(ped)
end)
end
return ped return ped
end end
@@ -243,4 +249,4 @@ onResourceStop(function()
for k in pairs(Peds) do for k in pairs(Peds) do
DeletePed(Peds[k]) DeletePed(Peds[k])
end end
end, true) end, true)

View File

@@ -20,15 +20,21 @@ local Props = {}
--- } --- }
--- local prop = makeProp(propData, true, false) --- local prop = makeProp(propData, true, false)
--- ``` --- ```
function makeProp(data, freeze, synced) function makeProp(data, freeze, synced, fade)
loadModel(data.prop) loadModel(data.prop)
local prop = CreateObject(data.prop, data.coords.x, data.coords.y, data.coords.z-1.03, synced and synced or false, synced and synced or false, false) local prop = CreateObject(data.prop, data.coords.x, data.coords.y, data.coords.z-1.03, synced and synced or false, synced and synced or false, false)
SetEntityAlpha(veh, 0, false)
SetEntityHeading(prop, (data.coords.w or 0) + 180.0) SetEntityHeading(prop, (data.coords.w or 0) + 180.0)
FreezeEntityPosition(prop, freeze or false) FreezeEntityPosition(prop, freeze or false)
debugPrint("^6Bridge^7: ^1Prop ^2Created^7: '^6"..prop.."^7' | ^2Hash^7: ^7'^6"..data.prop.."^7' | ^2Coord^7: "..formatCoord(data.coords)) debugPrint("^6Bridge^7: ^1Prop ^2Created^7: '^6"..prop.."^7' | ^2Hash^7: ^7'^6"..data.prop.."^7' | ^2Coord^7: "..formatCoord(data.coords))
SetModelAsNoLongerNeeded(data.prop) SetModelAsNoLongerNeeded(data.prop)
Props[keyGen()..keyGen()] = prop Props[keyGen()..keyGen()] = prop
if fade ~= false then
CreateThread(function()
fadeInEnt(prop)
end)
end
return prop return prop
end end

View File

@@ -13,23 +13,29 @@ local Vehicles = {}
--- ```lua --- ```lua
--- local vehicle = makeVeh('adder', vector4(123.4, 567.8, 90.1, 180.0)) --- local vehicle = makeVeh('adder', vector4(123.4, 567.8, 90.1, 180.0))
--- ``` --- ```
function makeVeh(model, coords) function makeVeh(model, coords, synced, fade)
loadModel(model) loadModel(model)
local veh = CreateVehicle(model, coords.x, coords.y, coords.z, coords.w, true, false) local veh = CreateVehicle(model, coords.x, coords.y, coords.z, coords.w, synced ~= false, false)
SetVehicleHasBeenOwnedByPlayer(veh, true) SetVehicleHasBeenOwnedByPlayer(veh, true)
if gameName ~= "rdr3" then if gameName ~= "rdr3" then
SetNetworkIdCanMigrate(NetworkGetNetworkIdFromEntity(veh), true) SetEntityAlpha(veh, 0, false)
SetNetworkIdCanMigrate(NetworkGetNetworkIdFromEntity(veh), true)
Wait(100) Wait(100)
SetVehicleNeedsToBeHotwired(veh, false) SetVehicleNeedsToBeHotwired(veh, false)
SetVehRadioStation(veh, 'OFF') SetVehRadioStation(veh, 'OFF')
SetVehicleFuelLevel(veh, 100.0) SetVehicleFuelLevel(veh, 100.0)
SetVehicleModKit(veh, 0) SetVehicleModKit(veh, 0)
end end
SetVehicleOnGroundProperly(veh) SetVehicleOnGroundProperly(veh)
debugPrint("^6Bridge^7: ^1Veh ^2Created^7: '^6"..veh.."^7' | ^2Hash^7: ^7'^6"..model.."^7' | ^2Coord^7: "..formatCoord(coords)) debugPrint("^6Bridge^7: ^1Veh ^2Created^7: '^6"..veh.."^7' | ^2Hash^7: ^7'^6"..model.."^7' | ^2Coord^7: "..formatCoord(coords))
unloadModel(model) unloadModel(model)
Vehicles[#Vehicles + 1] = veh Vehicles[#Vehicles + 1] = veh
if fade ~= false then
CreateThread(function()
fadeInEnt(veh)
end)
end
return veh return veh
end end
@@ -52,7 +58,7 @@ function makeDistVehicle(data, radius, onEnter, onExit)
coords = vec3(data.coords.x, data.coords.y, data.coords.z), coords = vec3(data.coords.x, data.coords.y, data.coords.z),
radius = radius, radius = radius,
onEnter = function() onEnter = function()
vehicle = makeVeh(data.model, data.coords) vehicle = makeVeh(data.model, data.coords, false)
if onEnter then if onEnter then
debugPrint("^6Bridge^7: ^4makeDistVehicle ^3onEnter^7() ^2running^7") debugPrint("^6Bridge^7: ^4makeDistVehicle ^3onEnter^7() ^2running^7")
onEnter(vehicle) onEnter(vehicle)

View File

@@ -119,6 +119,27 @@ function progressBar(data)
result = false result = false
end, data.icon) end, data.icon)
elseif Config.System.ProgressBar == "qs" then -- documentation left intact based on qs interface
local qs_success = exports['qs-interface']:ProgressBar({
duration = debugMode and 1000 or data.time, -- Duration of the progress bar (in milliseconds)
label = data.label, -- Text that will appear on the progress bar
position = 'bottom', -- Position of the progress bar on the screen (e.g., 'top', 'bottom', 'center')
useWhileDead = data.dead or false, -- Whether the progress bar shows when the player is dead (true/false)
canCancel = data.cancel or true, -- Whether the player can cancel the progress (true/false)
disable = data.disableMovement or false, -- Whether to disable player controls during the progress (true/false)
anim = { -- Animation to be played while the progress is happening
dict = data.dict,
clip = data.anim,
flag = data.flag or 32 -- Optional animation flags
},
prop = nil -- Optional prop to show alongside the progress bar (can be nil)
})
if qs_success then
result = true
else
result = false
end
elseif Config.System.ProgressBar == "esx" then elseif Config.System.ProgressBar == "esx" then
ESX.Progressbar(data.label, debugMode and 1000 or data.time, { ESX.Progressbar(data.label, debugMode and 1000 or data.time, {
FreezePlayer = true, FreezePlayer = true,
@@ -135,6 +156,35 @@ function progressBar(data)
end 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,
flag = data.flag
},
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 = false
end
elseif Config.System.ProgressBar == "red" then elseif Config.System.ProgressBar == "red" then
-- Currently only uses jim-redui if you choose this option -- Currently only uses jim-redui if you choose this option
if exports["jim_bridge"]:redProgressBar({ if exports["jim_bridge"]:redProgressBar({
@@ -204,6 +254,8 @@ function stopProgressBar()
exports[OXLibExport]:cancelProgress() exports[OXLibExport]:cancelProgress()
elseif Config.System.ProgressBar == "qb" then elseif Config.System.ProgressBar == "qb" then
TriggerEvent("progressbar:client:cancel") 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 elseif Config.System.ProgressBar == "gta" or Config.System.ProgressBar == "red" then
exports["jim_bridge"]:stopProgressBar() exports["jim_bridge"]:stopProgressBar()
end end

View File

@@ -58,9 +58,9 @@ end
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- local myMeta = GetMetadata(player, "myKey") --- local myMeta = getPlayerMetadata(player, "myKey")
--- ``` --- ```
function GetMetadata(player, key) function getPlayerMetadata(player, key)
if not player then if not player then
debugPrint("^6Bridge^7: ^3GetMetadata^7() calling server: "..key) debugPrint("^6Bridge^7: ^3GetMetadata^7() calling server: "..key)
return triggerCallback(getScript()..":server:GetMetadata", key) return triggerCallback(getScript()..":server:GetMetadata", key)
@@ -98,11 +98,11 @@ createCallback(getScript()..":server:GetMetadata", function(source, key)
if type(key) == "table" then if type(key) == "table" then
local Metadata = {} local Metadata = {}
for _, k in ipairs(key) do for _, k in ipairs(key) do
Metadata[k] = GetMetadata(player, k) Metadata[k] = getPlayerMetadata(player, k)
end end
return Metadata return Metadata
elseif type(key) == "string" then elseif type(key) == "string" then
return GetMetadata(player, key) return getPlayerMetadata(player, key)
end end
end) end)
@@ -120,9 +120,9 @@ end)
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- SetMetadata(player, "myKey", "newValue") --- setPlayerMetadata(player, "myKey", "newValue")
--- ``` --- ```
function SetMetadata(player, key, value) function setPlayerMetadata(player, key, value)
debugPrint("^6Bridge^7: ^3SetMetadata^7() setting metadata for key: "..key) debugPrint("^6Bridge^7: ^3SetMetadata^7() setting metadata for key: "..key)
if isStarted(QBExport) or isStarted(QBXExport) then if isStarted(QBExport) or isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^3SetMetadata^7() using QBExport/QBXExport") debugPrint("^6Bridge^7: ^3SetMetadata^7() using QBExport/QBXExport")
@@ -144,14 +144,14 @@ function SetMetadata(player, key, value)
end end
-- Register a server callback for setting metadata. -- Register a server callback for setting metadata.
createCallback(getScript()..":server:SetMetadata", function(source, key, value) createCallback(getScript()..":server:setPlayerMetadata", function(source, key, value)
debugPrint("SetMetadata callback triggered for source:", source, "key:", key, "value:", value) debugPrint("SetMetadata callback triggered for source:", source, "key:", key, "value:", value)
local player = GetPlayer(source) local player = GetPlayer(source)
--[[if not player then --[[if not player then
print("Error setting metadata: player not found for source "..tostring(source)) print("Error setting metadata: player not found for source "..tostring(source))
return false return false
end]] end]]
SetMetadata(player, key, value) setPlayerMetadata(player, key, value)
print("Metadata set successfully.", key) print("Metadata set successfully.", key)
return true return true
end) end)

View File

@@ -44,6 +44,12 @@ function triggerNotify(title, message, type, src)
else else
TriggerClientEvent("QBCore:Notify", src, message, type) TriggerClientEvent("QBCore:Notify", src, message, type)
end end
elseif Config.System.Notify == "qs" then
if not src then
exports['qs-interface']:AddNotify(message, title, 6000)
else
TriggerClientEvent('interface:notification', src, message, title, 6000)
end
elseif Config.System.Notify == "ox" then elseif Config.System.Notify == "ox" then
if not src then if not src then
TriggerEvent('ox_lib:notify', { title = title, description = message, type = type or "success" }) TriggerEvent('ox_lib:notify', { title = title, description = message, type = type or "success" })
@@ -62,6 +68,20 @@ function triggerNotify(title, message, type, src)
else else
TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message) TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message)
end 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 elseif Config.System.Notify == "red" then
if isStarted("jim-redui") then if isStarted("jim-redui") then

View File

@@ -171,7 +171,7 @@ function sendPhoneInvoice(data)
end end
}, },
{ {
name = "gks-phone", name = "gksphone",
send = function(mailData) send = function(mailData)
-- Defensive check for required fields -- Defensive check for required fields
local required = { "billedCitizenid", "amount", "job", "name", "billerCitizenid", "label" } local required = { "billedCitizenid", "amount", "job", "name", "billerCitizenid", "label" }
@@ -197,7 +197,6 @@ function sendPhoneInvoice(data)
} }
} }
-- Check each phone system in order and use the first active one. -- Check each phone system in order and use the first active one.
for _, phone in ipairs(phoneSystems) do for _, phone in ipairs(phoneSystems) do
if isStarted(phone.name) then if isStarted(phone.name) then

View File

@@ -412,7 +412,7 @@ function hasJob(job, source, grade)
elseif isStarted(QBExport) and not isStarted(QBXExport) then elseif isStarted(QBExport) and not isStarted(QBXExport) then
if Core.Functions.GetPlayer then if Core.Functions.GetPlayer then
local player = Core.Functions.GetPlayer(src) local player = Core.Functions.GetPlayer(src)
if not player then print("Player not found for src: "..src) end if not player then goto skip end
local jobinfo = player.PlayerData.job local jobinfo = player.PlayerData.job
if jobinfo.name == job then if jobinfo.name == job then
hasJobFlag = true hasJobFlag = true
@@ -437,11 +437,10 @@ function hasJob(job, source, grade)
if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end
end end
end end
elseif isStarted(RSGExport) then elseif isStarted(RSGExport) then
if Core.Functions.GetPlayer then if Core.Functions.GetPlayer then
local player = Core.Functions.GetPlayer(src) local player = Core.Functions.GetPlayer(src)
if not player then print("Player not found for src: "..src) end if not player then goto skip end
local jobinfo = player.PlayerData.job local jobinfo = player.PlayerData.job
if jobinfo.name == job then if jobinfo.name == job then
hasJobFlag = true hasJobFlag = true
@@ -531,6 +530,7 @@ function hasJob(job, source, grade)
print("^4ERROR^7: ^2No Core detected for hasJob() ^7- ^2Check ^3starter^1.^2lua^7") print("^4ERROR^7: ^2No Core detected for hasJob() ^7- ^2Check ^3starter^1.^2lua^7")
end end
end end
::skip::
return hasJobFlag, duty return hasJobFlag, duty
end end

View File

@@ -44,19 +44,34 @@ function createPoly(data)
local Location = nil local Location = nil
if isStarted(OXLibExport) then if isStarted(OXLibExport) then
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4"..OXLibExport.."^7': "..data.name) debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4"..OXLibExport.."^7': "..data.name)
-- Convert 2D points to 3D with a fixed Z coordinate (e.g., 12.0)
data.minZ = data.minZ or 12.0
data.maxZ = data.maxZ or 1000.0
data.thickness = ((data.maxZ / 2) - (data.minZ / 2)) * 2
local mid = data.maxZ - ((data.maxZ / 2) - (data.minZ / 2))
for i = 1, #data.points do for i = 1, #data.points do
data.points[i] = vec3(data.points[i].x, data.points[i].y, 12.0) data.points[i] = vec3(data.points[i].x, data.points[i].y, mid)
end end
data.thickness = 1000 -- Set a default thickness value
Location = lib.zones.poly(data) Location = lib.zones.poly(data)
elseif isStarted("PolyZone") then elseif isStarted("PolyZone") then
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4PolyZone^7': "..data.name) debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4PolyZone^7': "..data.name)
Location = PolyZone:Create(data.points, { name = data.name, debugPoly = data.debug }) Location = PolyZone:Create(data.points, {
name = data.name,
minZ = data.minZ or nil,
maxZ = data.maxZ or nil,
debugPoly = data.debug
})
Location:onPlayerInOut(function(isPointInside) Location:onPlayerInOut(function(isPointInside)
if isPointInside then data.onEnter() else data.onExit() end if isPointInside then
data.onEnter()
else
data.onExit()
end
end) end)
else else
print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7") print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7")
end end

View File

@@ -261,6 +261,6 @@ function registerShop(name, label, items, society)
if shopResource ~= "" then if shopResource ~= "" then
debugPrint("^6Bridge^7: ^2Registering ^5"..shopResource.." ^3Store^7:", name, "^4Label^7: "..label) debugPrint("^6Bridge^7: ^2Registering ^5"..shopResource.." ^3Store^7:", name, "^4Label^7: "..label)
else else
debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register shop^7:", name) -- debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register shop^7:", name)
end end
end end

View File

@@ -32,6 +32,25 @@ function skillCheck(data)
elseif Config.System.skillCheck == "gta" then elseif Config.System.skillCheck == "gta" then
exports.jim_bridge:skillCheck() 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 else
result = true result = true
end end

View File

@@ -15,6 +15,9 @@ local stash
-- If running on the server, create a callback to retrieve stash items. -- If running on the server, create a callback to retrieve stash items.
if isServer() then if isServer() then
createCallback(getScript()..':server:GetStashItems', function(source, stashName) createCallback(getScript()..':server:GetStashItems', function(source, stashName)
if stashName == nil or stashName == "" then
return {}
end
stash = getStash(stashName) stash = getStash(stashName)
return stash return stash
end) end)
@@ -35,9 +38,9 @@ local stashCache = {}
--- local cached = GetStashTimeout("playerStash") --- local cached = GetStashTimeout("playerStash")
--- ``` --- ```
function GetStashTimeout(stashName, stop) function GetStashTimeout(stashName, stop)
if stop then if stop or (stashName == nil or stashName == "") then
stashCache = {} stashCache = {}
return return false
end end
-- Retrieve cache for this stash, or initialize if not present. -- Retrieve cache for this stash, or initialize if not present.
@@ -85,7 +88,7 @@ end
--- local found, stashName = checkStashItem({"playerStash", "storageStash"}, { iron = 2, wood = 5 }) --- local found, stashName = checkStashItem({"playerStash", "storageStash"}, { iron = 2, wood = 5 })
--- ``` --- ```
function checkStashItem(stashes, itemTable) function checkStashItem(stashes, itemTable)
if not stashes then if not stashes or stashes == "" then
return hasItem(itemTable), nil return hasItem(itemTable), nil
end end
@@ -317,7 +320,7 @@ end
--- ``` --- ```
function getStash(stashName) function getStash(stashName)
local stashResource = "" local stashResource = ""
if type(stashName) ~= "string" then if stashName == "" or type(stashName) ~= "string" then
print("^6Bridge^7: ^2Stash name was not a string ^3"..stashName.."^7(^3"..type(stashName).."^7)") print("^6Bridge^7: ^2Stash name was not a string ^3"..stashName.."^7(^3"..type(stashName).."^7)")
return {} return {}
end end
@@ -420,6 +423,10 @@ end
--- stashRemoveItem(currentItems, "playerStash", { iron = 2, wood = 5 }) --- stashRemoveItem(currentItems, "playerStash", { iron = 2, wood = 5 })
--- ``` --- ```
function stashRemoveItem(stashItems, stashName, items) function stashRemoveItem(stashItems, stashName, items)
if stashName == "" or stashName == nil then
print("^1ERROR^7: ^1stashRemoveItem triggered but stashName was empty^7")
return
end
if type(stashName) ~= "table" then if type(stashName) ~= "table" then
stashName = { stashName } stashName = { stashName }
end end
@@ -462,7 +469,7 @@ function stashRemoveItem(stashItems, stashName, items)
end end
end end
exports[CodeMInv]:UpdateStash(stashName[1], stashItems) exports[CodeMInv]:UpdateStash(stashName[1], stashItems)
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3CodeM^2 stash ^7'^6"..stashName.."^7'") debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3CodeM^2 stash ^7'^6"..stashName[1].."^7'")
elseif isStarted(OrigenInv) then elseif isStarted(OrigenInv) then
for k, v in pairs(items) do for k, v in pairs(items) do

View File

@@ -158,19 +158,25 @@ function createBoxTarget(data, opts, dist)
icon = opts[i].icon, icon = opts[i].icon,
label = opts[i].label, label = opts[i].label,
items = opts[i].item or nil, items = opts[i].item or nil,
groups = opts[i].job or opts[i].gang, groups = opts[i].groups or opts[i].job or opts[i].gang,
onSelect = opts[i].onSelect or opts[i].action, onSelect = opts[i].onSelect or opts[i].action,
distance = dist, distance = dist,
canInteract = opts[i].canInteract or nil, canInteract = opts[i].canInteract or nil,
} }
end end
if not data[5].useZ then
local z = data[2].z + math.abs(data[5].maxZ - data[5].minZ) / 2 data[5].maxZ = data[5].maxZ or (data[2].z + 0.80)
data[2] = vec3(data[2].x, data[2].y, z) data[5].minZ = data[5].minZ or data[2].z - 1.05
end local thickness = ((data[5].maxZ / 2) - (data[5].minZ / 2)) * 2
local mid = data[5].maxZ - ((data[5].maxZ / 2) - (data[5].minZ / 2))
--if not data[5].useZ then
-- local z = data[2].z + math.abs(data[5].maxZ - data[5].minZ) / 2
data[2] = vec3(data[2].x, data[2].y, mid) -- force the coord to middle of the minZ and maxZ
--end
local target = exports[OXTargetExport]:addBoxZone({ local target = exports[OXTargetExport]:addBoxZone({
coords = data[2], coords = data[2],
size = vec3(data[4], data[3], (data[5].useZ or not data[5].maxZ) and data[2].z or math.abs(data[5].maxZ - data[5].minZ)), size = vec3(data[4], data[3], thickness), -- size uses the math to determine how high it needs to be
rotation = data[5].heading, rotation = data[5].heading,
debug = data[5].debugPoly, debug = data[5].debugPoly,
options = options options = options

View File

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

View File

@@ -6,6 +6,25 @@ local function loadTextureDict(dict)
end end
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)
RemoveAnimDict(animDict)
end
function redProgressBar(data) function redProgressBar(data)
local ped = PlayerPedId() local ped = PlayerPedId()

View File

@@ -53,7 +53,7 @@ function gtaSkillCheck()
DrawRect(x - width / 2 + (highlightStart + highlightSize / 2) * width, y, highlightSize * width, height+0.001, 228, 52, 52, 255) DrawRect(x - width / 2 + (highlightStart + highlightSize / 2) * width, y, highlightSize * width, height+0.001, 228, 52, 52, 255)
DrawRect(x - width / 2 + cursorPos * width, y, 0.002, height + 0.01, 255, 255, 255, 255) DrawRect(x - width / 2 + cursorPos * width, y, 0.002, height + 0.01, 255, 255, 255, 255)
drawSuccessText(x, y, "Failed", 228, 52, 52) drawGTASuccessText(x, y, "Failed", 228, 52, 52)
end end
return false return false
end end

View File

@@ -1,8 +1,15 @@
2.0.19 2.0.22
- SetMetadata function now handles full metadata, not just durability - Support for qs-interface
- Completely rework framework caching, it was lagging behind and breaking - Fix polyzone minZ + maxZ handling
- Add new hasFreeInvnentorySlots() function - Fix ox_target zone location to match qb-target
- Add inv config file grabber, checks inventory configs for inventory weight/slots - Fix "Waiting for script start" loop bug
- Add condition for fading in entities
- Fix typo breaking scripts on gta skillbar fail
- Fix typo for gksphone invoice event
- Fix latio_ui TextUI placement
- Remove registerShop debug message
- Fix typo in print when removing codem stash item
- Fix function typo from origen inv documenation
https://github.com/jimathy/jim_bridge https://github.com/jimathy/jim_bridge