mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-17 22:16:03 +01:00
Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f09e34b1b5 | ||
|
|
1146639d6f | ||
|
|
a9ac2e8897 | ||
|
|
1a1960f5a8 | ||
|
|
996b135973 | ||
|
|
9337238832 | ||
|
|
ee049a953c | ||
|
|
adfe3e8e08 | ||
|
|
71c9c99db7 | ||
|
|
dd66083f45 | ||
|
|
9ddd6422bd | ||
|
|
7d24c274cd | ||
|
|
3911824fc6 | ||
|
|
9729d2fef1 | ||
|
|
3cb4005889 | ||
|
|
bf759ebaf8 | ||
|
|
3d0eb3df65 | ||
|
|
fe09d0f773 | ||
|
|
880d2b0233 | ||
|
|
5865d2eeb2 | ||
|
|
528735abd1 | ||
|
|
d4df995c71 | ||
|
|
3b13fa663e | ||
|
|
e973c64ffd | ||
|
|
6ecf10c084 | ||
|
|
79e765fbb0 | ||
|
|
d4a08d5e2b | ||
|
|
eeb3f40018 | ||
|
|
e6b4c1d13c | ||
|
|
6ecbc3392a | ||
|
|
7f10c394de | ||
|
|
fae099fd93 | ||
|
|
64d43b54e9 | ||
|
|
d4abdbc93f | ||
|
|
b595453efe | ||
|
|
d255034c02 | ||
|
|
d6fd2cc28c | ||
|
|
7c47de5dc0 | ||
|
|
c1ab2b3505 | ||
|
|
1665ab41de | ||
|
|
6d397b59bf | ||
|
|
9dfbcbe878 | ||
|
|
ca0970b650 | ||
|
|
02218a4902 | ||
|
|
a900044306 | ||
|
|
7382a534a0 | ||
|
|
3b76b15bb4 | ||
|
|
f2eda0fef6 | ||
|
|
61c1cce814 | ||
|
|
0f1660e3ed | ||
|
|
e5b55e49b7 | ||
|
|
1ff01526bc | ||
|
|
1ed227080b | ||
|
|
b07c089946 | ||
|
|
d603067c77 | ||
|
|
5a97b8f83a | ||
|
|
d0a09871c7 | ||
|
|
e87aa8d74d | ||
|
|
e204e6153d | ||
|
|
5c69f8dd28 | ||
|
|
f3fa309dbf | ||
|
|
9d74e7cb46 | ||
|
|
fea72bba2e | ||
|
|
b364f14260 | ||
|
|
8077ec4f46 | ||
|
|
e5277574df | ||
|
|
e0563dd4ce | ||
|
|
1987cf3df3 | ||
|
|
9f5145c3b2 | ||
|
|
5921cf8adc | ||
|
|
f154b45076 | ||
|
|
2c41272c36 | ||
|
|
57d4beafed | ||
|
|
bdb526bd1e | ||
|
|
bb9dbe248f | ||
|
|
caf2e7fbed | ||
|
|
d0b8fb16d6 | ||
|
|
2e57e66095 | ||
|
|
05fc856e86 | ||
|
|
74e6a679e2 | ||
|
|
17db5aa31a | ||
|
|
387c97de71 | ||
|
|
de45491c8c | ||
|
|
4da4cbfc15 | ||
|
|
9fbf2f2211 | ||
|
|
41b2965743 | ||
|
|
a02c11d3cc | ||
|
|
c888aa5ecf | ||
|
|
804327f3aa | ||
|
|
ba59418b6d |
12
.github/workflows/notify-discord.yml
vendored
12
.github/workflows/notify-discord.yml
vendored
@@ -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
|
|
||||||
10
README.md
10
README.md
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
32
clientFrameworkCache.lua
Normal file
32
clientFrameworkCache.lua
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
if _G.__bridge_cache_loaded then return end
|
||||||
|
_G.__bridge_cache_loaded = true
|
||||||
|
|
||||||
|
_G.BridgeCache = {
|
||||||
|
hasCache = false,
|
||||||
|
data = nil,
|
||||||
|
queue = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterNetEvent("jim_bridge:receiveCache", function(data)
|
||||||
|
if _G.BridgeCache.hasCache then return end
|
||||||
|
_G.BridgeCache.data = data
|
||||||
|
_G.BridgeCache.hasCache = true
|
||||||
|
|
||||||
|
for _, cb in ipairs(_G.BridgeCache.queue) do
|
||||||
|
cb(data)
|
||||||
|
end
|
||||||
|
_G.BridgeCache.queue = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Only send the request ONCE per client session
|
||||||
|
TriggerServerEvent("jim_bridge:requestCache")
|
||||||
|
|
||||||
|
-- Exported async function
|
||||||
|
function GetBridgeCache(callback)
|
||||||
|
if _G.BridgeCache.hasCache then
|
||||||
|
callback(_G.BridgeCache.data)
|
||||||
|
else
|
||||||
|
table.insert(_G.BridgeCache.queue, callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
exports("GetBridgeCache", GetBridgeCache)
|
||||||
@@ -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)
|
||||||
```
|
```
|
||||||
@@ -35,18 +35,33 @@ local Exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- Prevent reloading if cache is already initialized
|
-- Prevent reloading if cache is already initialized
|
||||||
if _G.__jimBridgeDataCache then return end
|
local cache = {
|
||||||
_G.__jimBridgeDataCache = {}
|
Items = {},
|
||||||
local cache = _G.__jimBridgeDataCache
|
Vehicles = {},
|
||||||
|
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).."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
|
||||||
@@ -62,8 +77,7 @@ if checkExists(Exports.ESXExport) then
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Initialize variables for caching
|
-- Initialize variables for caching
|
||||||
local Items, Vehicles, Jobs, Gangs, Core = nil, nil, nil, nil, nil
|
local itemResource, jobResource, vehResource = "N/A", "N/A", "N/A"
|
||||||
local itemResource, jobResource, vehResource = "", "", ""
|
|
||||||
|
|
||||||
-- Print just to announce it knows the exports/scripts exist in the server
|
-- Print just to announce it knows the exports/scripts exist in the server
|
||||||
for _, v in pairs(Exports) do
|
for _, v in pairs(Exports) do
|
||||||
@@ -80,38 +94,70 @@ if checkExists(Exports.OXInv) then
|
|||||||
-- Wait for OX Inventory to start if it's not already started
|
-- Wait for OX Inventory to start if it's not already started
|
||||||
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
|
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
|
||||||
itemResource = Exports.OXInv
|
itemResource = Exports.OXInv
|
||||||
Items = exports[Exports.OXInv]:Items()
|
|
||||||
|
local success, result = pcall(function()
|
||||||
|
return exports[Exports.OXInv]:Items()
|
||||||
|
end)
|
||||||
|
if success and result then
|
||||||
|
cache.Items = result
|
||||||
|
end
|
||||||
|
|
||||||
-- Get Weapon info and duplicate them if they are uppercase
|
-- Get Weapon info and duplicate them if they are uppercase
|
||||||
-- (duplicate incase anything checks for the uppercase version)
|
-- (duplicate incase anything checks for the uppercase version)
|
||||||
for k, v in pairs(Items) do
|
for k, v in pairs(cache.Items) do
|
||||||
if k:find("WEAPON") then
|
if type(k) == "string" then
|
||||||
Items[k:lower()] = Items[k]
|
if k:find("WEAPON") then
|
||||||
Items[k:lower()].image = k..".png"
|
cache.Items[k:lower()] = cache.Items[k]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("^1ERROR^7: ^1Possible table inside a table, check your items.lua^7?")
|
||||||
|
print("^1Possible Issue found^7:")
|
||||||
|
print(json.encode(Items[k], {indent = true}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif checkExists(Exports.QBXExport) then
|
||||||
|
while GetResourceState(Exports.QBXExport) ~= "started" do Wait(100) end
|
||||||
|
itemResource = Exports.QBXExport
|
||||||
|
cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
|
||||||
|
-- If this is nil, they need to update to qbx_core 1.23.0+
|
||||||
|
if not cache.Items then
|
||||||
|
-- if their inventory doesn't allow that (they refuse to update their butchered core replacement):
|
||||||
|
if GetResourceState(Exports.QSInv):find("start") then
|
||||||
|
itemResource = Exports.QSInv
|
||||||
|
cache.Items = exports[Exports.QSInv]:GetItemList()
|
||||||
|
|
||||||
|
elseif GetResourceState(Exports.OrigenInv):find("start") then
|
||||||
|
itemResource = Exports.OrigenInv
|
||||||
|
cache.Items = exports[Exports.OrigenInv]:Items()
|
||||||
|
|
||||||
|
elseif GetResourceState(Exports.CodeMInv):find("start") then
|
||||||
|
itemResource = Exports.CodeMInv
|
||||||
|
cache.Items = exports[Exports.CodeMInv]:GetItemList()
|
||||||
|
|
||||||
|
elseif GetResourceState(Exports.CoreInv):find("start") then
|
||||||
|
itemResource = Exports.CoreInv
|
||||||
|
cache.Items = exports[Exports.CoreInv]:getItemsList()
|
||||||
|
|
||||||
|
elseif GetResourceState(Exports.TgiannInv):find("start") then
|
||||||
|
itemResource = Exports.TgiannInv
|
||||||
|
cache.Items = exports[Exports.TgiannInv]:Items()
|
||||||
end
|
end
|
||||||
Items[k].image = (v.client and v.client.image) and v.client.image:gsub("nui://"..Exports.OXInv.."/web/images/", "") or k..".png"
|
|
||||||
Items[k].hunger = v.client and v.client.hunger
|
|
||||||
Items[k].thirst = v.client and v.client.thirst
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif checkExists(Exports.QBExport) then
|
elseif checkExists(Exports.QBExport) then
|
||||||
while GetResourceState(Exports.QBExport) ~= "started" do Wait(100) end
|
while GetResourceState(Exports.QBExport) ~= "started" do Wait(100) end
|
||||||
itemResource = Exports.QBExport
|
itemResource = Exports.QBExport
|
||||||
Core = exports[Exports.QBExport]:GetCoreObject()
|
cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
|
||||||
Items = Core.Shared.Items
|
|
||||||
if Items == nil then
|
|
||||||
print("^1ERROR^7: ^1Can NOT find shared ^7Items ^1table^7, ^1possible error in that file^7?")
|
|
||||||
Items = {} -- Fallback to an empty table
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif checkExists(Exports.ESXExport) then
|
elseif checkExists(Exports.ESXExport) then
|
||||||
itemResource = Exports.ESXExport
|
itemResource = Exports.ESXExport
|
||||||
if GetResourceState(Exports.QSInv):find("start") then
|
if GetResourceState(Exports.QSInv):find("start") then
|
||||||
Items = exports[Exports.QSInv]:GetItemList()
|
cache.Items = exports[Exports.QSInv]:GetItemList()
|
||||||
else
|
else
|
||||||
Items = ESX.GetItems()
|
cache.Items = ESX.GetItems()
|
||||||
while not next(Items) do
|
while not next(cache.Items) do
|
||||||
Items = ESX.GetItems()
|
cache.Items = ESX.GetItems()
|
||||||
Wait(1000)
|
Wait(1000)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -119,33 +165,27 @@ elseif checkExists(Exports.ESXExport) then
|
|||||||
elseif checkExists(Exports.RSGExport) then
|
elseif checkExists(Exports.RSGExport) then
|
||||||
while GetResourceState(Exports.RSGExport) ~= "started" do Wait(100) end
|
while GetResourceState(Exports.RSGExport) ~= "started" do Wait(100) end
|
||||||
itemResource = Exports.RSGExport
|
itemResource = Exports.RSGExport
|
||||||
Core = exports[Exports.RSGExport]:GetCoreObject()
|
cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
|
||||||
Items = Core.Shared.Items
|
|
||||||
if Items == nil then
|
|
||||||
print("^1ERROR^7: ^1Can NOT find shared ^7Items ^1table^7, ^1possible error in that file^7?")
|
|
||||||
Items = {} -- Fallback to an empty table
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
endTimer("Items")
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
--- Load Vehicles ---
|
--- Load Vehicles ---
|
||||||
---------------------
|
---------------------
|
||||||
-- Vehicle loading depending on framework
|
-- Vehicle loading depending on framework
|
||||||
if checkExists(Exports.QBXExport) or checkExists(Exports.QBExport) then
|
if checkExists(Exports.QBXExport) then
|
||||||
|
vehResource = Exports.QBXExport
|
||||||
|
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
||||||
|
|
||||||
|
elseif checkExists(Exports.QBExport)then
|
||||||
vehResource = Exports.QBExport
|
vehResource = Exports.QBExport
|
||||||
Core = Core or exports[Exports.QBExport]:GetCoreObject()
|
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
||||||
Vehicles = Core.Shared.Vehicles
|
|
||||||
if Vehicles == nil then
|
|
||||||
print("^1ERROR^7: ^1Can NOT find shared ^7Vehicles ^1table^7, ^1possible error in that file^7?")
|
|
||||||
Vehicles = {} -- Fallback to an empty table
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif checkExists(Exports.OXCoreExport) then
|
elseif checkExists(Exports.OXCoreExport) then
|
||||||
vehResource = Exports.OXCoreExport
|
vehResource = Exports.OXCoreExport
|
||||||
Vehicles = {}
|
cache.Vehicles = {}
|
||||||
for k, v in pairs(Ox.GetVehicleData()) do
|
for k, v in pairs(Ox.GetVehicleData()) do
|
||||||
Vehicles[k] = {
|
cache.Vehicles[k] = {
|
||||||
model = k, hash = GetHashKey(k),
|
model = k, hash = GetHashKey(k),
|
||||||
price = v.price,
|
price = v.price,
|
||||||
name = v.name,
|
name = v.name,
|
||||||
@@ -156,9 +196,8 @@ elseif checkExists(Exports.OXCoreExport) then
|
|||||||
elseif checkExists(Exports.ESXExport) then
|
elseif checkExists(Exports.ESXExport) then
|
||||||
vehResource = Exports.ESXExport
|
vehResource = Exports.ESXExport
|
||||||
while not MySQL do Wait(1000) end
|
while not MySQL do Wait(1000) end
|
||||||
Vehicles = {}
|
|
||||||
for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do
|
for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do
|
||||||
Vehicles[v.model] = {
|
cache.Vehicles[v.model] = {
|
||||||
model = v.model,
|
model = v.model,
|
||||||
hash = GetHashKey(v.model),
|
hash = GetHashKey(v.model),
|
||||||
price = v.price,
|
price = v.price,
|
||||||
@@ -168,14 +207,10 @@ elseif checkExists(Exports.ESXExport) then
|
|||||||
|
|
||||||
elseif checkExists(Exports.RSGExport) then
|
elseif checkExists(Exports.RSGExport) then
|
||||||
vehResource = Exports.RSGExport
|
vehResource = Exports.RSGExport
|
||||||
Core = Core or exports[Exports.RSGExport]:GetCoreObject()
|
cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
|
||||||
Vehicles = Core.Shared.Vehicles
|
|
||||||
if Vehicles == nil then
|
|
||||||
print("^1ERROR^7: ^1Can NOT find shared ^7Vehicles ^1table^7, ^1possible error in that file^7?")
|
|
||||||
Vehicles = {} -- Fallback to an empty table
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
endTimer("Vehicles")
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
----- Load Jobs -----
|
----- Load Jobs -----
|
||||||
@@ -183,16 +218,14 @@ end
|
|||||||
-- Jobs loading based on framework
|
-- Jobs loading based on framework
|
||||||
if checkExists(Exports.QBXExport) then
|
if checkExists(Exports.QBXExport) then
|
||||||
jobResource = Exports.QBXExport
|
jobResource = Exports.QBXExport
|
||||||
Core = Core or exports[Exports.QBXExport]:GetCoreObject()
|
cache.Jobs, cache.Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
|
||||||
Jobs, Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
|
|
||||||
if Jobs == nil then
|
elseif checkExists(Exports.QBExport) then
|
||||||
print("^1ERROR^7: ^1Can NOT find shared ^7Jobs ^1table^7, ^1possible error in that file^7?")
|
jobResource = Exports.QBExport
|
||||||
Jobs = {} -- Fallback to an empty table
|
cache.Jobs, cache.Gangs = exports[Exports.QBExport]:GetCoreObject().Shared.Jobs, exports[Exports.QBExport]:GetCoreObject().Shared.Gangs
|
||||||
end
|
|
||||||
|
|
||||||
elseif checkExists(Exports.OXCoreExport) then
|
elseif checkExists(Exports.OXCoreExport) then
|
||||||
jobResource = Exports.OXCoreExport
|
jobResource = Exports.OXCoreExport
|
||||||
Jobs = {}
|
|
||||||
while not MySQL do Wait(1000) end
|
while not MySQL do Wait(1000) end
|
||||||
local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`')
|
local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`')
|
||||||
local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`')
|
local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`')
|
||||||
@@ -202,34 +235,29 @@ elseif checkExists(Exports.OXCoreExport) then
|
|||||||
gradeMap[grade.group][grade.grade] = { name = grade.label }
|
gradeMap[grade.group][grade.grade] = { name = grade.label }
|
||||||
end
|
end
|
||||||
for _, job in pairs(tempJobs) do
|
for _, job in pairs(tempJobs) do
|
||||||
Jobs[job.name] = {
|
cache.Jobs[job.name] = {
|
||||||
label = job.label,
|
label = job.label,
|
||||||
grades = gradeMap[job.name] or {}
|
grades = gradeMap[job.name] or {}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
Gangs = Jobs
|
cache.Gangs = cache.Jobs
|
||||||
|
|
||||||
elseif checkExists(Exports.QBExport) then
|
|
||||||
jobResource = Exports.QBExport
|
|
||||||
Core = Core or exports[Exports.QBExport]:GetCoreObject()
|
|
||||||
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
|
|
||||||
|
|
||||||
elseif checkExists(Exports.ESXExport) then
|
elseif checkExists(Exports.ESXExport) then
|
||||||
jobResource = Exports.ESXExport
|
jobResource = Exports.ESXExport
|
||||||
ESX = exports[Exports.ESXExport]:getSharedObject()
|
ESX = exports[Exports.ESXExport]:getSharedObject()
|
||||||
Jobs = ESX.GetJobs()
|
cache.Jobs = ESX.GetJobs()
|
||||||
while not next(Jobs) do
|
while not next(cache.Jobs) do
|
||||||
Wait(100)
|
Wait(100)
|
||||||
Jobs = ESX.GetJobs()
|
cache.Jobs = ESX.GetJobs()
|
||||||
end
|
end
|
||||||
for Role, Grades in pairs(Jobs) do
|
for Role, Grades in pairs(cache.Jobs) do
|
||||||
-- Check for if user has added grades
|
-- Check for if user has added grades
|
||||||
if Grades.grades == nil or not next(Grades.grades) then
|
if Grades.grades == nil or not next(Grades.grades) then
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
for grade, info in pairs(Grades.grades) do
|
for grade, info in pairs(Grades.grades) do
|
||||||
if info.label and info.label:find("[Bb]oss") then
|
if info.label and info.label:find("[Bb]oss") then
|
||||||
Jobs[Role].grades[grade].isBoss = true
|
cache.Jobs[Role].grades[grade].isBoss = true
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -242,62 +270,193 @@ elseif checkExists(Exports.ESXExport) then
|
|||||||
end
|
end
|
||||||
|
|
||||||
if highestGrade then
|
if highestGrade then
|
||||||
Jobs[Role].grades[tostring(highestGrade)].isBoss = true
|
cache.Jobs[Role].grades[tostring(highestGrade)].isBoss = true
|
||||||
end
|
end
|
||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
Gangs = Jobs
|
cache.Gangs = cache.Jobs
|
||||||
|
|
||||||
elseif checkExists(Exports.RSGExport) then
|
elseif checkExists(Exports.RSGExport) then
|
||||||
jobResource = Exports.RSGExport
|
jobResource = Exports.RSGExport
|
||||||
Core = Core or exports[Exports.RSGExport]:GetCoreObject()
|
cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs
|
||||||
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
|
|
||||||
if Jobs == nil then
|
|
||||||
print("^1ERROR^7: ^1Can NOT find shared ^7Jobs ^1table^7, ^1possible error in that file^7?")
|
|
||||||
Jobs = {} -- Fallback to an empty table
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
endTimer("Jobs")
|
||||||
|
|
||||||
-- Save to global cache
|
-- Fallback if nil or empty
|
||||||
cache.Items = Items
|
if cache.Items == nil or not next(cache.Items) then
|
||||||
cache.Vehicles = Vehicles
|
print("^1--------------------------------------------^7")
|
||||||
cache.Jobs = Jobs
|
print("^1ERROR^7: ^1Can NOT find "..itemResource.." ^7Items ^1list^7, ^1possible error in that file or is it empty^7?")
|
||||||
cache.Gangs = Gangs
|
print("^1--------------------------------------------^7")
|
||||||
|
cache.Items = {} -- Fallback to an empty table
|
||||||
|
end
|
||||||
|
if cache.Vehicles == nil or not next(cache.Vehicles) then
|
||||||
|
print("^1--------------------------------------------^7")
|
||||||
|
print("^1ERROR^7: ^1Can NOT find "..vehResource.." ^7Vehicles ^1list^7, ^1possible error in that file or is it empty^7?")
|
||||||
|
print("^1--------------------------------------------^7")
|
||||||
|
cache.Vehicles = {} -- Fallback to an empty table
|
||||||
|
end
|
||||||
|
if cache.Jobs == nil or not next(cache.Jobs) then
|
||||||
|
print("^1--------------------------------------------^7")
|
||||||
|
print("^1ERROR^7: ^1Can NOT find "..jobResource.." ^7job ^1list^7, ^1possible error in that file or is it empty^7?")
|
||||||
|
print("^1--------------------------------------------^7")
|
||||||
|
cache.Jobs = {} -- Fallback to an empty table
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Auto Detection of Inventory Weight -- **EXPERIMENTAL**
|
||||||
|
-- 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
|
||||||
|
local function getInventoryConfig(resource, data)
|
||||||
|
if data.convars then
|
||||||
|
return function(path)
|
||||||
|
local key = path[1]
|
||||||
|
local convar = key == "MaxWeight" and data.convars.weight or key == "MaxSlots" and data.convars.slots
|
||||||
|
return convar and GetConvarInt(convar.key, convar.default) or nil, "Unsupported convar path: " .. table.concat(path, ".")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local content = LoadResourceFile(resource, data.file)
|
||||||
|
if not content then return nil, "Failed to load file" end
|
||||||
|
|
||||||
|
local env = {
|
||||||
|
GetConvar = GetConvar,
|
||||||
|
vector3 = vector3,
|
||||||
|
vector4 = vector4,
|
||||||
|
Citizen = Citizen,
|
||||||
|
GetResourceState = GetResourceState,
|
||||||
|
exports = exports,
|
||||||
|
DependencyCheck = function() return nil end,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 pcall(fn) then return nil, "Error executing config file" end
|
||||||
|
|
||||||
|
local cfg = env.Config or env.config
|
||||||
|
if not cfg then return nil, "Config table not found" end
|
||||||
|
|
||||||
|
return function(path)
|
||||||
|
local ref = cfg
|
||||||
|
for _, k in ipairs(path) do
|
||||||
|
if type(ref) ~= "table" then return nil, "Path invalid at: " .. tostring(k) end
|
||||||
|
ref = ref[k]
|
||||||
|
end
|
||||||
|
return ref
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Inventory table
|
||||||
|
local invWeightTable = {
|
||||||
|
[Exports.OXInv] = { convars = {
|
||||||
|
weight = { key = "inventory:weight", default = 30000 },
|
||||||
|
slots = { key = "inventory:slots", default = 40 }
|
||||||
|
}},
|
||||||
|
[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" } },
|
||||||
|
[Exports.TgiannInv] = { file = "configs/config.lua", path = { "slotsMaxWeights", "player", "maxWeight" }, slotPath = { "slotsMaxWeights", "player", "slots" } },
|
||||||
|
[Exports.CodeMInv] = { file = "config/config.lua", path = { "MaxWeight" }, slotPath = { "MaxSlots" } },
|
||||||
|
[Exports.RSGInv] = { file = "config/config.lua", path = { "MaxWeight" }, slotPath = { "MaxSlots" } },
|
||||||
|
--[Exports.OrigenInv] = { file = "config.lua", path = { "MaxWeight" } },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Run config detection
|
||||||
|
local invResource = ""
|
||||||
|
for script, data in pairs(invWeightTable) do
|
||||||
|
if checkExists(script) then
|
||||||
|
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
|
||||||
|
|
||||||
|
if not lookup then
|
||||||
|
print(("^1ERROR^7: ^1Config loader failed from ^5%s^7: ^1%s^7"):format(script, err or "unknown"))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local function resolve(label, path)
|
||||||
|
local val, perr = lookup(path)
|
||||||
|
if val then
|
||||||
|
cache["Inventory"..label] = val
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local warnType = label == "Weight" and "^1ERROR" or "^3WARNING"
|
||||||
|
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", 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()
|
CreateThread(function()
|
||||||
local counts = {
|
local counts = {
|
||||||
Items = 0,
|
Items = 0, Vehicles = 0, Jobs = 0, Gangs = 0,
|
||||||
Vehicles = 0,
|
|
||||||
Jobs = 0,
|
|
||||||
Gangs = 0,
|
|
||||||
}
|
}
|
||||||
for k, v in pairs(cache) do
|
for k, v in pairs(cache) do
|
||||||
for count in pairs(v) do
|
if type(v) ~= "number" then for count in pairs(v) do counts[k] += 1 end end
|
||||||
counts[k] += 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Items).."^2 Items from ^7"..itemResource)
|
if cache.InventoryWeight then
|
||||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Vehicles).."^2 Vehicles from ^7"..vehResource)
|
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventoryWeight^7: ^3"..cache.InventoryWeight.."^7 (^3"..(cache.InventoryWeight / 1000).."kg^7) ^7"..timers["InvWeight"])
|
||||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Jobs).."^2 Jobs from ^7"..jobResource)
|
end
|
||||||
print("^6FrameworkCache^7: ^2Loaded ^5"..tostring(counts.Gangs).."^2 Gangs from ^7"..jobResource)
|
if cache.InventorySlots then
|
||||||
|
print("^6FrameWorkCache^7: ^4"..invResource.."^2 InventorySlots^7: ^3"..cache.InventorySlots.." ^7"..timers["InvSlots"])
|
||||||
|
end
|
||||||
|
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"..timers["Vehicles"])
|
||||||
|
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"..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()
|
||||||
local src = source
|
local src = source
|
||||||
TriggerClientEvent("jim_bridge:receiveCache", src, _G.__jimBridgeDataCache)
|
TriggerClientEvent("jim_bridge:receiveCache", src, cache)
|
||||||
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 _G.__jimBridgeDataCache or
|
Wait(100)
|
||||||
(not _G.__jimBridgeDataCache.Items or next(_G.__jimBridgeDataCache.Items) == nil) or
|
|
||||||
(not _G.__jimBridgeDataCache.Vehicles or next(_G.__jimBridgeDataCache.Vehicles) == nil) or
|
|
||||||
(not _G.__jimBridgeDataCache.Jobs or next(_G.__jimBridgeDataCache.Jobs) == nil)
|
|
||||||
) and GetGameTimer() < timeout do
|
|
||||||
Wait(50)
|
|
||||||
end
|
end
|
||||||
return _G.__jimBridgeDataCache
|
|
||||||
|
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)
|
end)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
name "Jim_Bridge"
|
name "Jim_Bridge"
|
||||||
author "Jimathy"
|
author "Jimathy"
|
||||||
version "2.0.17"
|
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.'
|
||||||
@@ -18,9 +18,10 @@ files {
|
|||||||
-- Version checker
|
-- Version checker
|
||||||
server_scripts {
|
server_scripts {
|
||||||
'frameworkCache.lua',
|
'frameworkCache.lua',
|
||||||
'_versioncheck.lua'
|
'_versioncheck.lua',
|
||||||
}
|
}
|
||||||
|
|
||||||
client_scripts {
|
client_scripts {
|
||||||
'ui_modules/*.lua'
|
'clientFrameworkCache.lua',
|
||||||
|
'ui_modules/*.lua',
|
||||||
}
|
}
|
||||||
118
shared/_authToken.lua
Normal file
118
shared/_authToken.lua
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
-------------------------------------------------------------
|
||||||
|
-- Exploit Auth System
|
||||||
|
-------------------------------------------------------------
|
||||||
|
AuthEvent = nil
|
||||||
|
currentToken = nil
|
||||||
|
if isServer() then
|
||||||
|
|
||||||
|
local AuthEvent = getScript()..":"..keyGen()..keyGen()..keyGen()..keyGen()..":"..keyGen()..keyGen()..keyGen()..keyGen()
|
||||||
|
validTokens = validTokens or {}
|
||||||
|
|
||||||
|
createCallback(AuthEvent, function(source)
|
||||||
|
local src = source
|
||||||
|
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
|
||||||
|
--debugPrint(GetInvokingResource())
|
||||||
|
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||||
|
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
debugPrint("^1Auth^7:^2 Player Source^7: "..src.." ^2requested new token^7:", token)
|
||||||
|
validTokens[src] = token
|
||||||
|
timeOutAuth(validTokens[src], src) -- Give script 10 seconds, then clear token
|
||||||
|
return token
|
||||||
|
end)
|
||||||
|
|
||||||
|
function timeOutAuth(token, src)
|
||||||
|
local token = token
|
||||||
|
SetTimeout(10000, function()
|
||||||
|
if token == validTokens[src] then
|
||||||
|
print("^1--------------------------------------------^7")
|
||||||
|
print("^7Clearing token for player ^1"..src.."^7", token)
|
||||||
|
print("^7This shouldn't happen unless a token has been called by a player or script and it hasn't been used")
|
||||||
|
print("^1--------------------------------------------^7")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
RegisterNetEvent(getScript()..":clearAuthToken", function()
|
||||||
|
local src = source
|
||||||
|
debugPrint("^1Auth^7: ^2Manually removing token for Player Source^7:", src, validTokens[src])
|
||||||
|
validTokens[src] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
receivedEvent = {}
|
||||||
|
authCooldown = {}
|
||||||
|
|
||||||
|
createCallback(getScript()..":callback:GetAuthEvent", function(source)
|
||||||
|
local src = source
|
||||||
|
|
||||||
|
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||||
|
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital callback was called from an external resource^7")
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
if authCooldown[src] then
|
||||||
|
debugPrint("^1Auth^7: ^3Cooldown active^7 for Player ^1"..src.."^7, ignoring additional auth request")
|
||||||
|
return AuthEvent
|
||||||
|
end
|
||||||
|
|
||||||
|
debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent)
|
||||||
|
|
||||||
|
authCooldown[src] = true
|
||||||
|
SetTimeout(60000, function() -- 1 minute cooldown
|
||||||
|
authCooldown[src] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not receivedEvent[src] then
|
||||||
|
receivedEvent[src] = true
|
||||||
|
return AuthEvent
|
||||||
|
else
|
||||||
|
print("^1Auth^7: ^1Player ^7"..src.." ^1tried to request auth token more than once^7")
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent(getScript()..":clearAuthEventRequest", function()
|
||||||
|
local src = source
|
||||||
|
debugPrint("^1Auth^7: ^2Manually clearing Auth Event for Player Source^7:", src, AuthEvent)
|
||||||
|
receivedEvent[src] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Multiuse function to check if the generated client token is valid
|
||||||
|
function checkToken(src, token, genType, name)
|
||||||
|
if token == nil then
|
||||||
|
debugPrint("^1Auth^7: ^1No token recieved^7")
|
||||||
|
if genType == "stash" then
|
||||||
|
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1without an auth token^7")
|
||||||
|
elseif genType == "item" then
|
||||||
|
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1without an auth token^7")
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
debugPrint("^1Auth^7: ^2Auth token received^7, ^2checking against server cache^7..")
|
||||||
|
if token ~= validTokens[src] then
|
||||||
|
debugPrint("^1Auth^7: ^1Tokens don't match! ^7", token, validTokens[src])
|
||||||
|
if genType == "stash" then
|
||||||
|
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1with an incorrect auth token^7")
|
||||||
|
elseif genType == "item" then
|
||||||
|
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1with an incorrect auth token^7")
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
debugPrint("^1Auth^7: ^2Client and Server Auth tokens match^7!", token, validTokens[src])
|
||||||
|
validTokens[src] = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
onPlayerLoaded(function()
|
||||||
|
debugPrint("^1Auth^7: ^2Requesting ^3Auth Event^7")
|
||||||
|
AuthEvent = triggerCallback(getScript()..":callback:GetAuthEvent")
|
||||||
|
end, true)
|
||||||
|
|
||||||
|
onPlayerUnload(function()
|
||||||
|
debugPrint("^1Auth^7: ^2Clearing Auth Event^7")
|
||||||
|
TriggerServerEvent(getScript()..":clearAuthEventRequest")
|
||||||
|
end, true)
|
||||||
|
end
|
||||||
@@ -47,22 +47,21 @@ function onPlayerLoaded(func, onStart)
|
|||||||
|
|
||||||
if isStarted(QBExport) or isStarted(QBXExport) then
|
if isStarted(QBExport) or isStarted(QBXExport) then
|
||||||
onPlayerFramework = QBExport
|
onPlayerFramework = QBExport
|
||||||
AddEventHandler('QBCore:Client:OnPlayerLoaded', tempFunc)
|
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', tempFunc)
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
onPlayerFramework = ESXExport
|
onPlayerFramework = ESXExport
|
||||||
AddEventHandler('esx:playerLoaded', function()
|
RegisterNetEvent('esx:playerLoaded', function()
|
||||||
if waitForSharedLoad() then
|
if waitForSharedLoad() then
|
||||||
if isStarted(ESXExport) then Wait(1000) end
|
|
||||||
tempFunc()
|
tempFunc()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
elseif isStarted(OXCoreExport) then
|
elseif isStarted(OXCoreExport) then
|
||||||
onPlayerFramework = OXCoreExport
|
onPlayerFramework = OXCoreExport
|
||||||
AddEventHandler('ox:playerLoaded', tempFunc)
|
RegisterNetEvent('ox:playerLoaded', tempFunc)
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
onPlayerFramework = RSGExport
|
onPlayerFramework = RSGExport
|
||||||
AddEventHandler('RSGCore:Client:OnPlayerLoaded', tempFunc)
|
RegisterNetEvent('RSGCore:Client:OnPlayerLoaded', tempFunc)
|
||||||
end
|
end
|
||||||
|
|
||||||
if onPlayerFramework ~= "" then
|
if onPlayerFramework ~= "" then
|
||||||
@@ -83,12 +82,15 @@ end
|
|||||||
--- end)
|
--- end)
|
||||||
--- ```
|
--- ```
|
||||||
function onPlayerUnload(func)
|
function onPlayerUnload(func)
|
||||||
AddEventHandler('QBCore:Client:OnPlayerUnload', function() func() end)
|
debugPrint("^6Bridge^7: ^2Registering ^3onPlayerUnload^7()")
|
||||||
AddEventHandler('ox:playerLogout', function() func() end)
|
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() func() end)
|
||||||
AddEventHandler('RSGCore:Client:OnPlayerUnload', function() func() end)
|
|
||||||
|
RegisterNetEvent('ox:playerLogout', function() func() end)
|
||||||
|
|
||||||
|
RegisterNetEvent('RSGCore:Client:OnPlayerUnload', function() func() end)
|
||||||
|
|
||||||
|
RegisterNetEvent('esx:onPlayerLogout', function() func() end)
|
||||||
|
|
||||||
--AddEventHandler('esx:playerLogout', function() func() end)
|
|
||||||
-- ^ Only server side for now, need a way to send it to client if not already available
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@@ -115,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)
|
||||||
@@ -135,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
|
||||||
@@ -192,38 +193,35 @@ 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
|
|
||||||
--Wait(1000)
|
|
||||||
local count = {}
|
|
||||||
local loop = 0
|
local loop = 0
|
||||||
while ((not Jobs or not next(Jobs)) and (not Items or not next(Items)) and (not Vehicles or not next(Vehicles))) and (GetGameTimer() - startTime) < timeout do
|
|
||||||
if not messageShown then
|
while ((not Jobs or not next(Jobs)) or
|
||||||
|
(not Items or not next(Items)) or
|
||||||
|
(not Vehicles or not next(Vehicles))
|
||||||
|
) and (timeout and GetGameTimer() < timeout) do
|
||||||
|
if loop >= 3 and not messageShown then
|
||||||
if (not Jobs or not next(Jobs)) then
|
if (not Jobs or not next(Jobs)) then
|
||||||
debugPrint("^4Debug^7: ^2Waiting for Jobs to be loaded")
|
print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded")
|
||||||
end
|
end
|
||||||
if (not Items or not next(Items)) then
|
if (not Items or not next(Items)) then
|
||||||
debugPrint("^4Debug^7: ^2Waiting for Items to be loaded")
|
print("^4Debug^7: ^2Waiting for ^7Items^2 to be loaded")
|
||||||
end
|
end
|
||||||
if (not Vehicles or not next(Vehicles)) then
|
if (not Vehicles or not next(Vehicles)) then
|
||||||
debugPrint("^4Debug^7: ^2Waiting for Vehicles to be loaded")
|
print("^4Debug^7: ^2Waiting for ^7Vehicles^2 to be loaded")
|
||||||
end
|
end
|
||||||
|
messageShown = true
|
||||||
end
|
end
|
||||||
messageShown = true
|
|
||||||
--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
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
--- ```
|
--- ```
|
||||||
function createCallback(callbackName, funct)
|
function createCallback(callbackName, funct)
|
||||||
if isServer() then
|
if isServer() then
|
||||||
debugPrint("^6Bridge^7: ^3Registering callback^7:", callbackName)
|
|
||||||
if isStarted(OXLibExport) then
|
if isStarted(OXLibExport) then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^4"..OXLibExport.." ^3Callback^7:", callbackName)
|
||||||
lib.callback.register(callbackName, funct)
|
lib.callback.register(callbackName, funct)
|
||||||
else
|
else
|
||||||
local adaptedFunction = function(source, cb, ...)
|
local adaptedFunction = function(source, cb, ...)
|
||||||
@@ -34,12 +34,14 @@ function createCallback(callbackName, funct)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if isStarted(QBExport) then
|
if isStarted(QBExport) then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^4"..QBExport.." ^3Callback^7:", callbackName)
|
||||||
Core = Core or exports[QBExport]:GetCoreObject()
|
Core = Core or exports[QBExport]:GetCoreObject()
|
||||||
Core.Functions.CreateCallback(callbackName, adaptedFunction)
|
Core.Functions.CreateCallback(callbackName, adaptedFunction)
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^4"..ESXExport.." ^3Callback^7:", callbackName)
|
||||||
ESX.RegisterServerCallback(callbackName, adaptedFunction)
|
ESX.RegisterServerCallback(callbackName, adaptedFunction)
|
||||||
else
|
else
|
||||||
print("^6Bridge^7: ^1ERROR^7: ^3Can't find any script to register callback with", callbackName)
|
print("^1ERROR^7: ^1Can't find any supported framework to register callback with^7: "..callbackName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -64,7 +66,7 @@ end
|
|||||||
--- ```
|
--- ```
|
||||||
function triggerCallback(callbackName, ...)
|
function triggerCallback(callbackName, ...)
|
||||||
local result = nil
|
local result = nil
|
||||||
debugPrint("^6Bridge^7: ^3Triggering callback^7:", callbackName)
|
debugPrint("^6Bridge^7: ^2Triggering ^3Callback^7:", callbackName)
|
||||||
if isStarted(OXLibExport) then
|
if isStarted(OXLibExport) then
|
||||||
result = lib.callback.await(callbackName, false, ...)
|
result = lib.callback.await(callbackName, false, ...)
|
||||||
elseif isStarted(QBExport) then
|
elseif isStarted(QBExport) then
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Items, Vehicles, Jobs, Gangs, Core = {}, nil, nil, nil, nil
|
Items, Vehicles, Jobs, Gangs = nil, nil, nil, nil
|
||||||
|
|
||||||
-- Shared Exports Initialization
|
-- Shared Exports Initialization
|
||||||
Exports.PSInv = isStarted("lj-inventory") and "lj-inventory" or Exports.PSInv
|
Exports.PSInv = isStarted("lj-inventory") and "lj-inventory" or Exports.PSInv
|
||||||
@@ -33,71 +33,72 @@ end
|
|||||||
|
|
||||||
|
|
||||||
if IsDuplicityVersion() then
|
if IsDuplicityVersion() then
|
||||||
CreateThread(function()
|
local cache = nil
|
||||||
local cache = nil
|
local timeout = GetGameTimer() + 900000 -- 15 minutes max wait (had to up this from 2 minutes because of slow servers)
|
||||||
local timeout = GetGameTimer() + 5000 -- 5 seconds max wait
|
|
||||||
|
|
||||||
-- 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()
|
||||||
end)
|
end)
|
||||||
if success and result then
|
if success and result then
|
||||||
cache = result
|
cache = result
|
||||||
--print(json.encode(cache, {indent = true}))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
Wait(100)
|
|
||||||
end
|
end
|
||||||
|
Wait(100)
|
||||||
|
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
|
||||||
|
|
||||||
Items = cache.Items
|
Items = cache.Items
|
||||||
Vehicles = cache.Vehicles
|
Vehicles = cache.Vehicles
|
||||||
Jobs = cache.Jobs
|
Jobs = cache.Jobs
|
||||||
Gangs = cache.Gangs
|
Gangs = cache.Gangs
|
||||||
|
InventoryWeight = cache.InventoryWeight or InventoryWeight
|
||||||
|
InventorySlots = cache.InventorySlots or 40
|
||||||
|
|
||||||
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
|
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
|
||||||
end)
|
--print(countTable(Items), countTable(Vehicles), countTable(Jobs))
|
||||||
else
|
else
|
||||||
local hasCache = false
|
-- Shared bridge cache - prevent duplicate listeners
|
||||||
-- 🔹 Client Side: Request from server
|
exports["jim_bridge"]:GetBridgeCache(function(cache)
|
||||||
_G.__jimBridgeDataCache = {}
|
|
||||||
|
|
||||||
RegisterNetEvent("jim_bridge:receiveCache", function(data)
|
|
||||||
if not hasCache then
|
|
||||||
_G.__jimBridgeDataCache = data
|
|
||||||
hasCache = true
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
TriggerServerEvent("jim_bridge:requestCache")
|
|
||||||
|
|
||||||
CreateThread(function()
|
|
||||||
while not _G.__jimBridgeDataCache or not next(_G.__jimBridgeDataCache) do Wait(50) end
|
|
||||||
local cache = _G.__jimBridgeDataCache
|
|
||||||
Items = cache.Items or {}
|
Items = cache.Items or {}
|
||||||
Vehicles = cache.Vehicles or {}
|
Vehicles = cache.Vehicles or {}
|
||||||
Jobs = cache.Jobs or {}
|
Jobs = cache.Jobs or {}
|
||||||
Gangs = cache.Gangs or {}
|
Gangs = cache.Gangs or {}
|
||||||
|
InventoryWeight = cache.InventoryWeight or InventoryWeight
|
||||||
|
InventorySlots = cache.InventorySlots or 50
|
||||||
|
|
||||||
if isStarted(ESXExport) then
|
CreateThread(function()
|
||||||
for _, v in pairs(Vehicles) do
|
if isStarted(ESXExport) then
|
||||||
Vehicles[v.model] = {
|
for _, v in pairs(Vehicles) do
|
||||||
model = v.model,
|
Vehicles[v.model] = {
|
||||||
hash = v.hash,
|
model = v.model,
|
||||||
price = v.price,
|
hash = v.hash,
|
||||||
name = v.name,
|
price = v.price,
|
||||||
brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)
|
name = v.name,
|
||||||
}
|
brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
if isStarted(OXInv) then
|
||||||
|
for k, v in pairs(Items) do
|
||||||
|
local tempInfo = exports[OXInv]:Items(k)
|
||||||
|
Items[k].image = k..".png" -- Set default image
|
||||||
|
if tempInfo and tempInfo.client then -- if client info, check for hard set image
|
||||||
|
Items[k].image = (tempInfo.client.image) and tempInfo.client.image:gsub("nui://"..OXInv.."/web/images/", "") or Items[k].image
|
||||||
|
Items[k].hunger = tempInfo.client.hunger
|
||||||
|
Items[k].thirst = tempInfo.client.thirst
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
debugPrint("^6Bridge^7: ^2Shared cache successfully loaded from export^7.")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -110,7 +118,7 @@ function craftingMenu(data)
|
|||||||
local label = Items[l] and Items[l].label or "error - "..l
|
local label = Items[l] and Items[l].label or "error - "..l
|
||||||
local hasItem = checkStashItem(data.stashName, { [l] = b })
|
local hasItem = checkStashItem(data.stashName, { [l] = b })
|
||||||
local missingMark = not hasItem and " ❌" or " "
|
local missingMark = not hasItem and " ❌" or " "
|
||||||
settext = settext..(settext ~= "" and br or "").."[ x"..b.." ] - "..label..(b > 1 and " x"..b or "")..missingMark
|
settext = settext..(settext ~= "" and br or "").."[ x"..b.." ] - "..label..missingMark
|
||||||
|
|
||||||
metaTable[Items[l] and Items[l].label or "error - "..l] = b
|
metaTable[Items[l] and Items[l].label or "error - "..l] = b
|
||||||
itemTable[l] = b
|
itemTable[l] = b
|
||||||
@@ -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("hasCrafted Found, marking '"..data.item.."' as crafted for player")
|
|
||||||
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("exp found, giving exp for '"..data.item.."'")
|
|
||||||
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
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@@ -457,9 +493,9 @@ end
|
|||||||
--- @usage
|
--- @usage
|
||||||
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata, token)
|
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata, token)
|
||||||
local src = source
|
local src = source
|
||||||
debugPrint(GetInvokingResource())
|
-- debugPrint("^6Bridge^7: ^4"..getScript().."^7:^4Crafting^7:^4GetItem ^2invoked by^7: "..GetInvokingResource())
|
||||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
||||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
debugPrint("^1ERROR^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 "")
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
or other type (e.g., coyote). It uses predefined model hashes stored in the AnimalPeds table.
|
or other type (e.g., coyote). It uses predefined model hashes stored in the AnimalPeds table.
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
- isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal: Booleans to track the player's
|
- isCat, isPedDog, isBigDog, isSmallDog, isCoyote, isAnimal: Booleans to track the player's
|
||||||
current animal classification.
|
current animal classification.
|
||||||
|
|
||||||
When running client-side (not on the server), the module checks the player's Ped after they load.
|
When running client-side (not on the server), the module checks the player's Ped after they load.
|
||||||
@@ -26,13 +26,13 @@
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
-- Global animal classification flags.
|
-- Global animal classification flags.
|
||||||
isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
|
isPedCat, isPedDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
|
||||||
|
|
||||||
if not isServer() then
|
if not isServer() then
|
||||||
onPlayerLoaded(function()
|
onPlayerLoaded(function()
|
||||||
Wait(2000)
|
Wait(1000)
|
||||||
-- Reset classification flags
|
-- Reset classification flags
|
||||||
isCat, isDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
|
isPedCat, isPedDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
|
||||||
-- Check if the player's Ped is an animal.
|
-- Check if the player's Ped is an animal.
|
||||||
isPedAnimal()
|
isPedAnimal()
|
||||||
if isAnimal then
|
if isAnimal then
|
||||||
@@ -41,18 +41,18 @@ if not isServer() then
|
|||||||
|
|
||||||
-- Determine if the Ped is a cat:
|
-- Determine if the Ped is a cat:
|
||||||
-- Also treat 'ft-raccoon' as a cat unless it is 'ft-sphynx'
|
-- Also treat 'ft-raccoon' as a cat unless it is 'ft-sphynx'
|
||||||
isCat = (isCat(ped) or pedModel == `ft-raccoon`) and (pedModel ~= `ft-sphynx`)
|
isPedCat = (isCat(ped) == true or pedModel == `ft-raccoon`) and (pedModel ~= `ft-sphynx`)
|
||||||
|
|
||||||
-- Determine if the Ped is a dog and whether it's big:
|
-- Determine if the Ped is a dog and whether it's big:
|
||||||
isDog, isBigDog = isDog(ped)
|
isPedDog, isBigDog = isDog(ped)
|
||||||
isSmallDog = not isBigDog
|
isSmallDog = not isBigDog
|
||||||
if isDog and pedModel == `a_c_coyote` then isDog = false end
|
if isPedDog and pedModel == `a_c_coyote` then isPedDog = false end
|
||||||
|
|
||||||
-- Determine if the Ped is a coyote (special case):
|
-- Determine if the Ped is a coyote (special case):
|
||||||
isCoyote = (pedModel == `ft-sphynx` or pedModel == `a_c_coyote`)
|
isCoyote = (pedModel == `ft-sphynx` or pedModel == `a_c_coyote`)
|
||||||
|
|
||||||
-- Special override: if model is 'ft-capmonkey2', treat as a dog.
|
-- Special override: if model is 'ft-capmonkey2', treat as a dog.
|
||||||
if pedModel == `ft-capmonkey2` then isDog = true end
|
if pedModel == `ft-capmonkey2` then isPedDog = true end
|
||||||
end
|
end
|
||||||
end, true)
|
end, true)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ if not isServer() then
|
|||||||
for animalModelHash, _ in pairs(animalCategory) do
|
for animalModelHash, _ in pairs(animalCategory) do
|
||||||
if PedModel == animalModelHash then
|
if PedModel == animalModelHash then
|
||||||
isAnimal = true
|
isAnimal = true
|
||||||
debugPrint("^6Bridge^7: ^2Ped is Animal")
|
debugPrint("^4isAnimal^7: ^2Ped is ^4Animal")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -109,6 +109,7 @@ if not isServer() then
|
|||||||
local PedModel = GetEntityModel(ped or PlayerPedId())
|
local PedModel = GetEntityModel(ped or PlayerPedId())
|
||||||
for modelHash, _ in pairs(AnimalPeds.CatPeds) do
|
for modelHash, _ in pairs(AnimalPeds.CatPeds) do
|
||||||
if PedModel == modelHash then
|
if PedModel == modelHash then
|
||||||
|
debugPrint("^4isAnimal^7: ^2Ped is ^4Cat")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -126,8 +127,8 @@ if not isServer() then
|
|||||||
---
|
---
|
||||||
---@usage
|
---@usage
|
||||||
--- ```lua
|
--- ```lua
|
||||||
--- local isDog, isBigDog = isDog()
|
--- local isPedDog, isBigDog = isDog()
|
||||||
--- if isDog then
|
--- if isPedDog then
|
||||||
--- if isBigDog then
|
--- if isBigDog then
|
||||||
--- print("Player is a big dog!")
|
--- print("Player is a big dog!")
|
||||||
--- else
|
--- else
|
||||||
@@ -151,11 +152,13 @@ if not isServer() then
|
|||||||
local PedModel = GetEntityModel(ped or PlayerPedId())
|
local PedModel = GetEntityModel(ped or PlayerPedId())
|
||||||
for modelHash, _ in pairs(AnimalPeds.BigDogs) do
|
for modelHash, _ in pairs(AnimalPeds.BigDogs) do
|
||||||
if PedModel == modelHash then
|
if PedModel == modelHash then
|
||||||
|
debugPrint("^4isAnimal^7: ^2Ped is ^4Dog")
|
||||||
return true, true
|
return true, true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for modelHash, _ in pairs(AnimalPeds.SmallDogs) do
|
for modelHash, _ in pairs(AnimalPeds.SmallDogs) do
|
||||||
if PedModel == modelHash then
|
if PedModel == modelHash then
|
||||||
|
debugPrint("^4isAnimal^7: ^2Ped is ^4Small Dog")
|
||||||
return true, false
|
return true, false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,25 +30,34 @@ validTokens = {}
|
|||||||
--- end)
|
--- end)
|
||||||
--- ```
|
--- ```
|
||||||
function createUseableItem(item, funct)
|
function createUseableItem(item, funct)
|
||||||
if isStarted(ESXExport) then
|
if doesItemExist(item) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7es_extended^7", item)
|
local itemResource = ""
|
||||||
while not ESX do Wait(0) end
|
if isStarted(ESXExport) then
|
||||||
ESX.RegisterUsableItem(item, funct)
|
itemResource = ESXExport
|
||||||
|
while not ESX do Wait(0) end
|
||||||
|
ESX.RegisterUsableItem(item, funct)
|
||||||
|
|
||||||
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qb-core^7", item)
|
itemResource = QBExport
|
||||||
Core.Functions.CreateUseableItem(item, funct)
|
Core.Functions.CreateUseableItem(item, funct)
|
||||||
|
|
||||||
elseif isStarted(QBXExport) then
|
elseif isStarted(QBXExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qbx_core^7", item)
|
itemResource = QBXExport
|
||||||
exports[QBXExport]:CreateUseableItem(item, funct)
|
exports[QBXExport]:CreateUseableItem(item, funct)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^1rsg-core^7", item)
|
itemResource = RSGExport
|
||||||
Core.Functions.CreateUseableItem(item, funct)
|
Core.Functions.CreateUseableItem(item, funct)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if itemResource ~= "" then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^3UsableItem^2 with ^4"..itemResource.."^7:", item)
|
||||||
|
else
|
||||||
|
debugPrint("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
print("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
|
print("^1ERROR^7: ^1Tried to make item usable but it didn't exist^7: "..item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -266,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
|
||||||
@@ -368,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.
|
||||||
@@ -505,19 +456,21 @@ 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)
|
RegisterNetEvent(getScript()..":server:setItemMetaData", function(data, src)
|
||||||
local src = 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)
|
||||||
local Player = Core.Functions.GetPlayer(src)
|
local Player = Core.Functions.GetPlayer(src)
|
||||||
Player.PlayerData.items[data.slot].info = data.metadata
|
Player.PlayerData.items[data.slot].info = data.metadata
|
||||||
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability
|
if data.metadata.durability then
|
||||||
|
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability
|
||||||
|
end
|
||||||
Player.Functions.SetInventory(Player.PlayerData.items)
|
Player.Functions.SetInventory(Player.PlayerData.items)
|
||||||
|
|
||||||
elseif isStarted(OXInv) then
|
elseif isStarted(OXInv) then
|
||||||
exports[OXInv]:SetDurability(src, data.slot, data.metadata.durability)
|
exports[OXInv]:SetMetadata(src, data.slot, data.metadata)
|
||||||
|
|
||||||
elseif isStarted(QSInv) then
|
elseif isStarted(QSInv) then
|
||||||
exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata)
|
exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata)
|
||||||
@@ -644,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
|
||||||
@@ -708,8 +661,6 @@ end
|
|||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
-- Server Callback Registration
|
-- Server Callback Registration
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
AuthEvent = nil
|
|
||||||
currentToken = nil
|
|
||||||
if isServer() then
|
if isServer() then
|
||||||
createCallback(getScript()..":server:canCarry", function(source, itemTable)
|
createCallback(getScript()..":server:canCarry", function(source, itemTable)
|
||||||
local result = canCarry(itemTable, source)
|
local result = canCarry(itemTable, source)
|
||||||
@@ -735,103 +686,138 @@ if isServer() then
|
|||||||
end
|
end
|
||||||
return maxCanCarry
|
return maxCanCarry
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local AuthEvent = getScript()..":"..keyGen()..keyGen()..keyGen()..keyGen()..":"..keyGen()..keyGen()..keyGen()..keyGen()
|
function getMaxInvWeight()
|
||||||
validTokens = validTokens or {}
|
local weight = 0
|
||||||
|
if isStarted(QBInv) then
|
||||||
createCallback(AuthEvent, function(source)
|
if checkExportExists(QBInv, "GetMaxWeight") then
|
||||||
local src = source
|
return exports[QBInv]:GetMaxWeight()
|
||||||
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
|
else
|
||||||
--debugPrint(GetInvokingResource())
|
return InventoryWeight
|
||||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
end
|
||||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital function was called from an external resource^7")
|
end
|
||||||
return ""
|
if isStarted(PSInv) then
|
||||||
|
if checkExportExists(PSInv, "GetMaxWeight") then
|
||||||
|
return exports[PSInv]:GetMaxWeight()
|
||||||
|
else
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if isStarted(RSGInv) then
|
||||||
|
if checkExportExists(RSGInv, "GetMaxWeight") then
|
||||||
|
return exports[RSGInv]:GetMaxWeight()
|
||||||
|
else
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if isStarted(JPRInv) then
|
||||||
|
if checkExportExists(JPRInv, "GetMaxWeight") then
|
||||||
|
return exports[JPRInv]:GetMaxWeight()
|
||||||
|
else
|
||||||
|
return InventoryWeight
|
||||||
end
|
end
|
||||||
debugPrint("^1Auth^7:^2 Player Source^7: "..src.." ^2requested new token^7:", token)
|
|
||||||
validTokens[src] = token
|
|
||||||
timeOutAuth(validTokens[src], src) -- Give script 10 seconds, then clear token
|
|
||||||
return token
|
|
||||||
end)
|
|
||||||
|
|
||||||
function timeOutAuth(token, src)
|
|
||||||
local token = token
|
|
||||||
SetTimeout(10000, function()
|
|
||||||
if token == validTokens[src] then
|
|
||||||
print("^1--------------------------------------------^7")
|
|
||||||
print("^7Clearing token for player ^1"..src.."^7", token)
|
|
||||||
print("^7This shouldn't happen unless a token has been called by a player or script and it hasn't been used")
|
|
||||||
print("^1--------------------------------------------^7")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
RegisterNetEvent(getScript()..":clearAuthToken", function()
|
if isStarted(OXInv) then
|
||||||
local src = source
|
return exports[OXInv]:GetPlayerMaxWeight()
|
||||||
debugPrint("^1Auth^7: ^2Manually removing token for Player Source^7:", src, validTokens[src])
|
end
|
||||||
validTokens[src] = nil
|
|
||||||
end)
|
|
||||||
|
|
||||||
receivedEvent = {}
|
if isStarted(QSInv) then
|
||||||
createCallback(getScript()..":callback:GetAuthEvent", function(source)
|
return InventoryWeight
|
||||||
local src = source
|
end
|
||||||
--debugPrint(GetInvokingResource())
|
|
||||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
if isStarted(CoreInv) then
|
||||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital callback was called from an external resource^7")
|
return InventoryWeight
|
||||||
return ""
|
end
|
||||||
|
|
||||||
|
if isStarted(CodeMInv) then
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
|
||||||
|
if isStarted(OrigenInv) then
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
|
||||||
|
if isStarted(TgiannInv) then
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For ESX default inventory (es_extended)
|
||||||
|
if ESX and isStarted(ESXExport) then
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
|
||||||
|
return InventoryWeight
|
||||||
|
end
|
||||||
|
|
||||||
|
function getCurrentInvWeight()
|
||||||
|
local weight = 0
|
||||||
|
if isStarted(QBInv) or isStarted(PSInv) or isStarted(RSGInv) or isStarted(JPRInv) then
|
||||||
|
local itemcheck = Core.Functions.GetPlayerData().items
|
||||||
|
for _, v in pairs(itemcheck) do
|
||||||
|
weight += ((v.weight * v.amount) or 0)
|
||||||
end
|
end
|
||||||
debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent)
|
end
|
||||||
if not receivedEvent[src] then
|
|
||||||
receivedEvent[src] = true
|
if isStarted(OXInv) then
|
||||||
return AuthEvent
|
weight = exports[OXInv]:GetPlayerWeight()
|
||||||
else
|
end
|
||||||
print("^1Auth^7: ^1Player ^7"..src.." ^1tried to request auth token more than once^7")
|
|
||||||
return ""
|
if isStarted(QSInv) then
|
||||||
|
local itemcheck = exports[QSInv]:getUserInventory()
|
||||||
|
for _, v in pairs(itemcheck) do
|
||||||
|
weight += ((v.weight * v.amount) or 0)
|
||||||
end
|
end
|
||||||
end)
|
end
|
||||||
|
|
||||||
RegisterNetEvent(getScript()..":clearAuthEventRequest", function()
|
if isStarted(CoreInv) then
|
||||||
local src = source
|
local itemcheck = exports[CoreInv]:getInventory()
|
||||||
debugPrint("^1Auth^7: ^2Manually clearing Auth Event for Player Source^7:", src, AuthEvent)
|
for _, v in pairs(itemcheck) do
|
||||||
receivedEvent[src] = nil
|
weight += ((v.weight * v.amount) or 0)
|
||||||
end)
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Multiuse function to check if the generated client token is valid
|
if isStarted(CodeMInv) then
|
||||||
function checkToken(src, token, genType, name)
|
local itemcheck = exports[CodeMInv]:GetClientPlayerInventory()
|
||||||
if token == nil then
|
for _, v in pairs(itemcheck) do
|
||||||
debugPrint("^1Auth^7: ^1No token recieved^7")
|
weight += ((v.weight * v.amount) or 0)
|
||||||
if genType == "stash" then
|
end
|
||||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1without an auth token^7")
|
end
|
||||||
elseif genType == "item" then
|
|
||||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1without an auth token^7")
|
if isStarted(OrigenInv) then
|
||||||
end
|
local itemcheck = exports[OrigenInv]:getPlayerInventory()
|
||||||
return false
|
for _, v in pairs(itemcheck) do
|
||||||
else
|
weight += ((v.weight * v.amount) or 0)
|
||||||
debugPrint("^1Auth^7: ^2Auth token received^7, ^2checking against server cache^7..")
|
end
|
||||||
if token ~= validTokens[src] then
|
end
|
||||||
debugPrint("^1Auth^7: ^1Tokens don't match! ^7", token, validTokens[src])
|
|
||||||
if genType == "stash" then
|
if isStarted(TgiannInv) then
|
||||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1create a stash ^7"..name.." ^1with an incorrect auth token^7")
|
local itemcheck = exports[TgiannInv]:GetPlayerItems()
|
||||||
elseif genType == "item" then
|
for _, v in pairs(itemcheck) do
|
||||||
dupeWarn(src, name, "^1Auth Error^7: ^3"..src.." ^1attempted to spawn an item ^7"..name.." ^1with an incorrect auth token^7")
|
weight += ((v.weight * v.amount) or 0)
|
||||||
end
|
end
|
||||||
return false
|
end
|
||||||
else
|
|
||||||
debugPrint("^1Auth^7: ^2Client and Server Auth tokens match^7!", token, validTokens[src])
|
-- For ESX default inventory (es_extended)
|
||||||
validTokens[src] = nil
|
if ESX and isStarted(ESXExport) then
|
||||||
return true
|
local xPlayer = ESX.GetPlayerData() or {}
|
||||||
|
if xPlayer.inventory then
|
||||||
|
for _, v in pairs(xPlayer.inventory) do
|
||||||
|
weight += ((v.weight * v.amount) or 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
|
||||||
onPlayerLoaded(function()
|
|
||||||
debugPrint("^1Auth^7: ^2Requesting ^3Auth Event^7")
|
|
||||||
AuthEvent = triggerCallback(getScript()..":callback:GetAuthEvent")
|
|
||||||
end, true)
|
|
||||||
|
|
||||||
onPlayerUnload(function()
|
return weight
|
||||||
debugPrint("^1Auth^7: ^2Clearing Auth Event^7")
|
end
|
||||||
TriggerServerEvent(getScript()..":clearAuthEventRequest")
|
|
||||||
end, true)
|
function hasFreeInventorySlots(slots, src)
|
||||||
|
local inv = getPlayerInv(src)
|
||||||
|
if (countTable(inv) + slots) <= InventorySlots then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@@ -848,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
|
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
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -242,5 +257,8 @@ function openBossMenu(isGang, group)
|
|||||||
end
|
end
|
||||||
elseif isStarted("qbx_management") then
|
elseif isStarted("qbx_management") then
|
||||||
exports["qbx_management"]:OpenBossMenu(isGang and "gang" or "job")
|
exports["qbx_management"]:OpenBossMenu(isGang and "gang" or "job")
|
||||||
|
|
||||||
|
elseif isStarted("esx_society") then
|
||||||
|
TriggerEvent('esx_society:openBossMenu', group, function() end, { wash = false })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -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("Attempting to make player play anim", 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
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ end
|
|||||||
--- stopAnim('amb@world_human_hang_out_street@male_c@base', 'base', PlayerPedId())
|
--- stopAnim('amb@world_human_hang_out_street@male_c@base', 'base', PlayerPedId())
|
||||||
--- ```
|
--- ```
|
||||||
function stopAnim(animDict, animName, ped)
|
function stopAnim(animDict, animName, ped)
|
||||||
debugPrint("Stopping anim for "..(ped or PlayerPedId()))
|
debugPrint("^6Bridge^7: ^3stopAnim^7() ^2Triggered^7: ", animDict, animName)
|
||||||
StopAnimTask(ped or PlayerPedId(), animDict, animName, 0.5)
|
StopAnimTask(ped or PlayerPedId(), animDict, animName, 0.5)
|
||||||
StopAnimTask(ped or PlayerPedId(), animName, animDict, 0.5)
|
StopAnimTask(ped or PlayerPedId(), animName, animDict, 0.5)
|
||||||
unloadAnimDict(animDict)
|
unloadAnimDict(animDict)
|
||||||
@@ -239,3 +239,24 @@ function playGameSound(audioBank, soundSet, soundRef, coords, synced, range)
|
|||||||
end
|
end
|
||||||
ReleaseScriptAudioBank(audioBank)
|
ReleaseScriptAudioBank(audioBank)
|
||||||
end
|
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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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,16 +58,16 @@ 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("makeDistVehicle onEnter running")
|
debugPrint("^6Bridge^7: ^4makeDistVehicle ^3onEnter^7() ^2running^7")
|
||||||
onEnter(vehicle)
|
onEnter(vehicle)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
onExit = function()
|
onExit = function()
|
||||||
deleteVehicle(vehicle)
|
deleteVehicle(vehicle)
|
||||||
if onExit then
|
if onExit then
|
||||||
debugPrint("makeDistVehicle onExit running")
|
debugPrint("^6Bridge^7: ^4makeDistVehicle ^3onExit^7() ^2running^7")
|
||||||
onExit(vehicle)
|
onExit(vehicle)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
local inProgress = false
|
local inProgress = false
|
||||||
|
local storedPID = nil
|
||||||
|
|
||||||
--- Displays a progress bar using the configured progress bar system.
|
--- Displays a progress bar using the configured progress bar system.
|
||||||
---
|
---
|
||||||
@@ -48,7 +49,7 @@ function progressBar(data)
|
|||||||
label = data.label,
|
label = data.label,
|
||||||
position = data.position or "bottom",
|
position = data.position or "bottom",
|
||||||
useWhileDead = data.dead or false,
|
useWhileDead = data.dead or false,
|
||||||
canCancel = data.cancel and data.cancel or true,
|
canCancel = data.cancel or true,
|
||||||
anim = {
|
anim = {
|
||||||
dict = data.dict,
|
dict = data.dict,
|
||||||
clip = data.anim,
|
clip = data.anim,
|
||||||
@@ -57,8 +58,8 @@ function progressBar(data)
|
|||||||
},
|
},
|
||||||
disable = {
|
disable = {
|
||||||
combat = data.combat or true,
|
combat = data.combat or true,
|
||||||
move = data.movement or false,
|
move = data.disableMovement or false,
|
||||||
car = data.carMovement or false,
|
car = data.disableMovement or false,
|
||||||
mouse = data.mouse or false
|
mouse = data.mouse or false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -99,14 +100,46 @@ function progressBar(data)
|
|||||||
debugMode and 1000 or data.time,
|
debugMode and 1000 or data.time,
|
||||||
data.dead or false,
|
data.dead or false,
|
||||||
data.cancel or true,
|
data.cancel or true,
|
||||||
{ disableMovement = true, disableCarMovement = true, disableMouse = false, disableCombat = true },
|
{
|
||||||
{ animDict = data.dict, anim = data.anim, flags = data.flag or 32, task = data.task }, {}, {},
|
disableMovement = data.disableMovement or false,
|
||||||
|
disableCarMovement = data.disableMovement or false,
|
||||||
|
disableMouse = data.disableMouse or false,
|
||||||
|
disableCombat = data.disableCombat or true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
animDict = data.dict,
|
||||||
|
anim = data.anim,
|
||||||
|
flags = data.flag or 32,
|
||||||
|
task = data.task
|
||||||
|
},
|
||||||
|
{}, {},
|
||||||
function()
|
function()
|
||||||
result = true
|
result = true
|
||||||
end, function()
|
end, function()
|
||||||
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,
|
||||||
@@ -123,16 +156,46 @@ 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-redui"]:progressBar({
|
if exports["jim_bridge"]:redProgressBar({
|
||||||
label = data.label,
|
label = data.label,
|
||||||
time = debugMode and 1000 or data.time,
|
time = debugMode and 1000 or data.time,
|
||||||
dict = data.dict,
|
dict = data.dict,
|
||||||
anim = data.anim,
|
anim = data.anim,
|
||||||
flag = data.flag or 32,
|
flag = data.flag or 32,
|
||||||
task = data.task,
|
task = data.task,
|
||||||
cancel = true,
|
disableMovement = data.disableMovement or false,
|
||||||
|
cancel = data.cancel or true,
|
||||||
}) then
|
}) then
|
||||||
result = true
|
result = true
|
||||||
else
|
else
|
||||||
@@ -140,73 +203,21 @@ function progressBar(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
elseif Config.System.ProgressBar == "gta" then
|
elseif Config.System.ProgressBar == "gta" then
|
||||||
loadTextureDict("timerbars")
|
if exports["jim_bridge"]:gtaProgressBar({
|
||||||
if inProgress then return false end
|
label = data.label,
|
||||||
inProgress = true
|
time = debugMode and 1000 or data.time,
|
||||||
local wait = debugMode and 1000 or data.time
|
dict = data.dict,
|
||||||
local endTime = GetGameTimer() + wait
|
anim = data.anim,
|
||||||
local ped = PlayerPedId()
|
flag = data.flag or 32,
|
||||||
|
task = data.task,
|
||||||
-- Setup Animation/Task if specified
|
disableMovement = data.disableMovement or false,
|
||||||
if data.dict then
|
cancel = data.cancel or true,
|
||||||
playAnim(data.dict, data.anim, -1, data.flag or 32)
|
}) then
|
||||||
elseif data.task then
|
result = true
|
||||||
TaskStartScenarioInPlace(ped, data.task, -1, true)
|
else
|
||||||
|
result = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Progress bar rendering loop
|
|
||||||
CreateThread(function()
|
|
||||||
while GetGameTimer() < endTime and inProgress do
|
|
||||||
Wait(0)
|
|
||||||
local elapsed = GetGameTimer()
|
|
||||||
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
|
||||||
|
|
||||||
-- Convert to segmented progress (assuming 5 segments here)
|
|
||||||
local segments = 5 -- Number of segments in the bar
|
|
||||||
local segmentProgress = {}
|
|
||||||
local progressPerSegment = 100 / segments
|
|
||||||
|
|
||||||
for i = 1, segments do
|
|
||||||
local segmentStart = (i - 1) * progressPerSegment
|
|
||||||
local segmentEnd = i * progressPerSegment
|
|
||||||
if percentage >= segmentEnd then
|
|
||||||
segmentProgress[i] = 100
|
|
||||||
elseif percentage <= segmentStart then
|
|
||||||
segmentProgress[i] = 0
|
|
||||||
else
|
|
||||||
segmentProgress[i] = ((percentage - segmentStart) / progressPerSegment) * 100
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
percentage = percentage >= 100 and 100 or percentage
|
|
||||||
-- Draw your segmented progress bar
|
|
||||||
ShowGTAProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
|
||||||
|
|
||||||
-- Controls to disable during progress
|
|
||||||
DisablePlayerFiring(ped, true)
|
|
||||||
DisableControlAction(0, 25, true) -- Disable aim
|
|
||||||
DisableControlAction(0, 21, true) -- Disable sprint
|
|
||||||
DisableControlAction(0, 30, true) -- Disable move left/right
|
|
||||||
DisableControlAction(0, 31, true) -- Disable move forward/back
|
|
||||||
DisableControlAction(0, 36, true) -- Disable stealth
|
|
||||||
|
|
||||||
if data.cancel and (IsControlJustReleased(0, 202) or IsControlJustReleased(0, 177) or IsControlJustReleased(0, 73)) then
|
|
||||||
inProgress = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Wait for completion or cancel
|
|
||||||
while GetGameTimer() < endTime and inProgress do
|
|
||||||
Wait(100)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Cleanup animations/tasks
|
|
||||||
if data.dict then stopAnim(data.dict, data.anim, ped) end
|
|
||||||
ClearPedTasks(ped)
|
|
||||||
|
|
||||||
result = inProgress
|
|
||||||
inProgress = false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while result == nil do
|
while result == nil do
|
||||||
@@ -235,51 +246,6 @@ function progressBar(data)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function ShowGTAProgressBar(currentProg, title, level)
|
|
||||||
local loc = vec2(0.37, 0.90)
|
|
||||||
local size = vec2(0.3, 0.03)
|
|
||||||
|
|
||||||
-- Draw background box
|
|
||||||
DrawSprite("timerbars", "all_black_bg", loc.x +0.028, loc.y-0.01, 0.15, 0.07, 0.0, 255, 255, 255, 255)
|
|
||||||
DrawSprite("timerbars", "all_black_bg", loc.x +0.170, loc.y-0.01, 0.15, 0.07, 180.0, 255, 255, 255, 255)
|
|
||||||
|
|
||||||
SetTextFont(0)
|
|
||||||
SetTextProportional(1)
|
|
||||||
SetTextScale(0.35, 0.35)
|
|
||||||
SetTextColour(255, 255, 255, 255)
|
|
||||||
SetTextDropshadow(0, 0, 0, 0, 255)
|
|
||||||
SetTextDropShadow()
|
|
||||||
SetTextOutline()
|
|
||||||
SetTextEntry("STRING")
|
|
||||||
AddTextComponentString(title)
|
|
||||||
DrawText(loc.x - size.x / 4 + 0.074, loc.y - 0.034) -- Adjust text position
|
|
||||||
|
|
||||||
SetTextFont(0)
|
|
||||||
SetTextProportional(1)
|
|
||||||
SetTextScale(0.35, 0.25)
|
|
||||||
SetTextColour(255, 255, 255, 255)
|
|
||||||
SetTextEntry("STRING")
|
|
||||||
AddTextComponentString(level)
|
|
||||||
DrawText(loc.x - size.x / 4 + 0.246, loc.y - 0.030) -- Right-aligned additional text
|
|
||||||
|
|
||||||
local segmentWidth = (size.x + 0.05) / (#currentProg * 2) -- Divide the total width by 18 (9 segments * 2 gaps for each)
|
|
||||||
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
|
||||||
|
|
||||||
for i = 1, #currentProg do
|
|
||||||
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
|
||||||
local fillPercentage = currentProg[i]
|
|
||||||
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
|
||||||
|
|
||||||
-- Semi-transparent background for each segment
|
|
||||||
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
|
||||||
|
|
||||||
-- Filling progress for each segment
|
|
||||||
if progressBarWidth > 0 then
|
|
||||||
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 93, 182, 229, 255) -- Blue progress
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Stops the current progress bar.
|
--- Stops the current progress bar.
|
||||||
---
|
---
|
||||||
--- This function cancels the progress bar based on the configured progress bar system, handling any necessary cleanup.
|
--- This function cancels the progress bar based on the configured progress bar system, handling any necessary cleanup.
|
||||||
@@ -288,15 +254,16 @@ 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
|
||||||
inProgress = false
|
exports["jim_bridge"]:stopProgressBar()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- System to handle sending/sharing progress bars between players --
|
-- System to handle sending/sharing progress bars between players --
|
||||||
-- For example, healing someone --
|
-- For example, healing someone --
|
||||||
|
|
||||||
local storedPID = nil
|
|
||||||
|
|
||||||
--- Server event handler for starting a shared progress bar.
|
--- Server event handler for starting a shared progress bar.
|
||||||
--- This event is triggered when a player wants to start a progress bar on another player.
|
--- This event is triggered when a player wants to start a progress bar on another player.
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
--- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId)
|
--- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId)
|
||||||
--- ```
|
--- ```
|
||||||
function triggerNotify(title, message, type, src)
|
function triggerNotify(title, message, type, src)
|
||||||
|
if not Config.System or not Config.System.Notify then debugPrint("Notify triggered but not set up") return end
|
||||||
if Config.System.Notify == "okok" then
|
if Config.System.Notify == "okok" then
|
||||||
if not src then
|
if not src then
|
||||||
TriggerEvent('okokNotify:Alert', title, message, 6000, type)
|
TriggerEvent('okokNotify:Alert', title, message, 6000, type)
|
||||||
@@ -43,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" })
|
||||||
@@ -61,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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ function billPlayer(data)
|
|||||||
coords = data.coords.xyz,
|
coords = data.coords.xyz,
|
||||||
img = data.img
|
img = data.img
|
||||||
})
|
})
|
||||||
|
elseif Config.System.Billing == "okok" then
|
||||||
|
-- TriggerServerEvent("okokBilling:CreateCustomInvoice", target, price, reason, invoiceSource, society, societyName)
|
||||||
|
TriggerEvent("okokBilling:ToggleCreateInvoice")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -173,6 +176,7 @@ end
|
|||||||
--- chargePlayer(100, "cash", playerId)
|
--- chargePlayer(100, "cash", playerId)
|
||||||
--- ```
|
--- ```
|
||||||
function chargePlayer(cost, moneyType, newsrc)
|
function chargePlayer(cost, moneyType, newsrc)
|
||||||
|
local is_success = false
|
||||||
local src = newsrc or source
|
local src = newsrc or source
|
||||||
local fundResource = ""
|
local fundResource = ""
|
||||||
if cost < 0 then
|
if cost < 0 then
|
||||||
@@ -181,33 +185,35 @@ function chargePlayer(cost, moneyType, newsrc)
|
|||||||
end
|
end
|
||||||
if moneyType == "cash" then
|
if moneyType == "cash" then
|
||||||
if isStarted(OXInv) then fundResource = OXInv
|
if isStarted(OXInv) then fundResource = OXInv
|
||||||
exports[OXInv]:RemoveItem(src, "money", cost)
|
is_success = exports[OXInv]:RemoveItem(src, "money", cost)
|
||||||
|
|
||||||
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
||||||
fundResource = QBExport
|
fundResource = QBExport
|
||||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
fundResource = RSGExport
|
fundResource = RSGExport
|
||||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
|
||||||
|
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
fundResource = ESXExport
|
fundResource = ESXExport
|
||||||
ESX.GetPlayerFromId(src).removeMoney(cost, "")
|
ESX.GetPlayerFromId(src).removeMoney(cost, "")
|
||||||
|
is_success = true
|
||||||
|
|
||||||
end
|
end
|
||||||
elseif moneyType == "bank" then
|
elseif moneyType == "bank" then
|
||||||
if isStarted(QBExport) or isStarted(QBXExport) then
|
if isStarted(QBExport) or isStarted(QBXExport) then
|
||||||
fundResource = QBExport
|
fundResource = QBExport
|
||||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
fundResource = RSGExport
|
fundResource = RSGExport
|
||||||
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
is_success = Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
|
||||||
|
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
fundResource = ESXExport
|
fundResource = ESXExport
|
||||||
ESX.GetPlayerFromId(src).removeMoney(cost, "")
|
ESX.GetPlayerFromId(src).removeMoney(cost, "")
|
||||||
|
is_success = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -216,6 +222,7 @@ function chargePlayer(cost, moneyType, newsrc)
|
|||||||
else
|
else
|
||||||
debugPrint("^6Bridge^7: ^2Charging ^2Player^7: '^6"..cost.."^7'", moneyType, fundResource)
|
debugPrint("^6Bridge^7: ^2Charging ^2Player^7: '^6"..cost.."^7'", moneyType, fundResource)
|
||||||
end
|
end
|
||||||
|
return is_success
|
||||||
end
|
end
|
||||||
RegisterNetEvent(getScript()..":server:ChargePlayer", function(cost, moneyType, newsrc)
|
RegisterNetEvent(getScript()..":server:ChargePlayer", function(cost, moneyType, newsrc)
|
||||||
debugPrint(GetInvokingResource())
|
debugPrint(GetInvokingResource())
|
||||||
@@ -237,39 +244,42 @@ end)
|
|||||||
--- fundPlayer(150, "cash", playerId)
|
--- fundPlayer(150, "cash", playerId)
|
||||||
--- ```
|
--- ```
|
||||||
function fundPlayer(fund, moneyType, newsrc)
|
function fundPlayer(fund, moneyType, newsrc)
|
||||||
|
local is_success = false
|
||||||
local src = newsrc or source
|
local src = newsrc or source
|
||||||
local fundResource = ""
|
local fundResource = ""
|
||||||
|
|
||||||
if moneyType == "cash" then
|
if moneyType == "cash" then
|
||||||
if isStarted(OXInv) then
|
if isStarted(OXInv) then
|
||||||
fundResource = OXInv
|
fundResource = OXInv
|
||||||
exports[OXInv]:AddItem(src, "money", fund)
|
is_success = exports[OXInv]:AddItem(src, "money", fund)
|
||||||
|
|
||||||
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
elseif isStarted(QBExport) or isStarted(QBXExport) then
|
||||||
fundResource = QBExport
|
fundResource = QBExport
|
||||||
Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
fundResource = RSGExport
|
fundResource = RSGExport
|
||||||
Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
|
||||||
|
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
fundResource = ESXExport
|
fundResource = ESXExport
|
||||||
ESX.GetPlayerFromId(src).addMoney(fund, "")
|
ESX.GetPlayerFromId(src).addMoney(fund, "")
|
||||||
|
is_success = true
|
||||||
|
|
||||||
end
|
end
|
||||||
elseif moneyType == "bank" then
|
elseif moneyType == "bank" then
|
||||||
if isStarted(QBExport) or isStarted(QBXExport) then
|
if isStarted(QBExport) or isStarted(QBXExport) then
|
||||||
fundResource = QBExport
|
fundResource = QBExport
|
||||||
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
fundResource = RSGExport
|
fundResource = RSGExport
|
||||||
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
is_success = Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
|
||||||
|
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
fundResource = ESXExport
|
fundResource = ESXExport
|
||||||
ESX.GetPlayerFromId(src).addMoney(fund, "")
|
ESX.GetPlayerFromId(src).addMoney(fund, "")
|
||||||
|
is_success = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -278,6 +288,7 @@ function fundPlayer(fund, moneyType, newsrc)
|
|||||||
else
|
else
|
||||||
debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource)
|
debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource)
|
||||||
end
|
end
|
||||||
|
return is_success
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@@ -401,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
|
||||||
@@ -426,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
|
||||||
@@ -520,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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -175,18 +175,12 @@ function openShop(data)
|
|||||||
TriggerEvent("codem-inventory:openshop", data.shop)
|
TriggerEvent("codem-inventory:openshop", data.shop)
|
||||||
|
|
||||||
elseif isStarted(QBInv) then
|
elseif isStarted(QBInv) then
|
||||||
if QBInvNew then
|
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||||
else
|
|
||||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif isStarted(PSInv) then
|
elseif isStarted(PSInv) then
|
||||||
if QBInvNew then
|
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
||||||
else
|
|
||||||
TriggerServerEvent("inventory:server:OpenInventory", "shop", data.items.label, data.items)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif isStarted(RSGInv) then
|
elseif isStarted(RSGInv) then
|
||||||
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
TriggerServerEvent(getScript()..':server:openServerShop', data.shop)
|
||||||
@@ -195,10 +189,10 @@ function openShop(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
RegisterNetEvent(getScript()..':server:openServerShop', function(data)
|
RegisterNetEvent(getScript()..':server:openServerShop', function(data)
|
||||||
if isStarted(QBInv) then
|
if isStarted(QBInv) and checkExportExists(QBInv, "OpenShop") then
|
||||||
exports[QBInv]:OpenShop(source, data)
|
exports[QBInv]:OpenShop(source, data)
|
||||||
end
|
end
|
||||||
if isStarted(PSInv) then
|
if isStarted(PSInv) and checkExportExists(PSInv, "OpenShop") then
|
||||||
exports[PSInv]:OpenShop(source, data)
|
exports[PSInv]:OpenShop(source, data)
|
||||||
end
|
end
|
||||||
if isStarted(TgiannInv) then
|
if isStarted(TgiannInv) then
|
||||||
@@ -221,8 +215,9 @@ end)
|
|||||||
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
|
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
|
||||||
--- ```
|
--- ```
|
||||||
function registerShop(name, label, items, society)
|
function registerShop(name, label, items, society)
|
||||||
|
local shopResource = ""
|
||||||
if isStarted(OXInv) then
|
if isStarted(OXInv) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label)
|
shopResource = OXInv
|
||||||
exports[OXInv]:RegisterShop(name, {
|
exports[OXInv]:RegisterShop(name, {
|
||||||
name = label,
|
name = label,
|
||||||
inventory = items,
|
inventory = items,
|
||||||
@@ -230,11 +225,11 @@ function registerShop(name, label, items, society)
|
|||||||
})
|
})
|
||||||
|
|
||||||
elseif isStarted(TgiannInv) then
|
elseif isStarted(TgiannInv) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3Tgiann ^2Store^7:", name, label)
|
shopResource = TgiannInv
|
||||||
exports[TgiannInv]:RegisterShop(name, items)
|
exports[TgiannInv]:RegisterShop(name, items)
|
||||||
|
|
||||||
elseif isStarted(QBInv) and QBInvNew then
|
elseif isStarted(QBInv) and checkExportExists(QBInv, "CreateShop") then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label)
|
shopResource = QBInv
|
||||||
exports[QBInv]:CreateShop({
|
exports[QBInv]:CreateShop({
|
||||||
name = name,
|
name = name,
|
||||||
label = label,
|
label = label,
|
||||||
@@ -243,8 +238,8 @@ function registerShop(name, label, items, society)
|
|||||||
society = society,
|
society = society,
|
||||||
})
|
})
|
||||||
|
|
||||||
elseif isStarted(PSInv) and QBInvNew then
|
elseif isStarted(PSInv) and checkExportExists(PSInv, "CreateShop") then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3PS ^2Store^7:", name, label)
|
shopResource = PSInv
|
||||||
exports[PSInv]:CreateShop({
|
exports[PSInv]:CreateShop({
|
||||||
name = name,
|
name = name,
|
||||||
label = label,
|
label = label,
|
||||||
@@ -254,7 +249,7 @@ function registerShop(name, label, items, society)
|
|||||||
})
|
})
|
||||||
|
|
||||||
elseif isStarted(RSGInv) then
|
elseif isStarted(RSGInv) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3RSG ^2Store^7:", name, label)
|
shopResource = RSGInv
|
||||||
exports[RSGInv]:CreateShop({
|
exports[RSGInv]:CreateShop({
|
||||||
name = name,
|
name = name,
|
||||||
label = label,
|
label = label,
|
||||||
@@ -263,4 +258,9 @@ function registerShop(name, label, items, society)
|
|||||||
society = society,
|
society = society,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
if shopResource ~= "" then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^5"..shopResource.." ^3Store^7:", name, "^4Label^7: "..label)
|
||||||
|
else
|
||||||
|
-- debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register shop^7:", name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -192,6 +195,7 @@ function openStash(data)
|
|||||||
slots = data.slots or 40
|
slots = data.slots or 40
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
|
TriggerEvent("ps-inventory:client:SetCurrentStash", data.stash)
|
||||||
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
|
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
|
||||||
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, {
|
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, {
|
||||||
slots = data.slots or 50,
|
slots = data.slots or 50,
|
||||||
@@ -243,18 +247,14 @@ end)
|
|||||||
|
|
||||||
function clearStash(stashId)
|
function clearStash(stashId)
|
||||||
if isStarted(JPRInv) then
|
if isStarted(JPRInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..JPRInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..JPRInv.."^2 Stash^7", stashId)
|
||||||
if QBInvNew then
|
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||||
exports[JPRInv]:ClearStash(stashId)
|
['stash'] = stashId,
|
||||||
else
|
['items'] = json.encode({})
|
||||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
})
|
||||||
['stash'] = stashId,
|
|
||||||
['items'] = json.encode({})
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif isStarted(QBInv) then
|
elseif isStarted(QBInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..QBInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..QBInv.."^2 Stash^7", stashId)
|
||||||
if QBInvNew then
|
if QBInvNew then
|
||||||
exports[QBInv]:ClearStash(stashId)
|
exports[QBInv]:ClearStash(stashId)
|
||||||
else
|
else
|
||||||
@@ -265,43 +265,38 @@ function clearStash(stashId)
|
|||||||
end
|
end
|
||||||
|
|
||||||
elseif isStarted(OXInv) then
|
elseif isStarted(OXInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..OXInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..OXInv.."^2 Stash^7", stashId)
|
||||||
exports[OXInv]:ClearInventory(stashId)
|
exports[OXInv]:ClearInventory(stashId)
|
||||||
|
|
||||||
elseif isStarted(PSInv) then
|
elseif isStarted(PSInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..PSInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..PSInv.."^2 Stash^7", stashId)
|
||||||
if QBInvNew then
|
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||||
exports[PSInv]:ClearStash(stashId)
|
['stash'] = stashId,
|
||||||
else
|
['items'] = json.encode({})
|
||||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
})
|
||||||
['stash'] = stashId,
|
|
||||||
['items'] = json.encode({})
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
elseif isStarted(QSInv) then
|
elseif isStarted(QSInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..QSInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..QSInv.."^2 Stash^7", stashId)
|
||||||
exports[QSInv]:ClearOtherInventory('stash', stashId)
|
exports[QSInv]:ClearOtherInventory('stash', stashId)
|
||||||
|
|
||||||
elseif isStarted(CoreInv) then
|
elseif isStarted(CoreInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..CoreInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..CoreInv.."^2 Stash^7", stashId)
|
||||||
exports[CoreInv]:clearInventory("stash-"..stashId)
|
exports[CoreInv]:clearInventory("stash-"..stashId)
|
||||||
|
|
||||||
elseif isStarted(CodeMInv) then
|
elseif isStarted(CodeMInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..CodeMInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..CodeMInv.."^2 Stash^7", stashId)
|
||||||
exports[CodeMInv]:ClearInventory(stashId)
|
exports[CodeMInv]:ClearInventory(stashId)
|
||||||
|
|
||||||
elseif isStarted(OrigenInv) then
|
elseif isStarted(OrigenInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..OrigenInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..OrigenInv.."^2 Stash^7", stashId)
|
||||||
exports[OrigenInv]:ClearInventory(stashId)
|
exports[OrigenInv]:ClearInventory(stashId)
|
||||||
|
|
||||||
elseif isStarted(TgiannInv) then
|
elseif isStarted(TgiannInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..TgiannInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..TgiannInv.."^2 Stash^7", stashId)
|
||||||
exports["tgiann-inventory"]:DeleteInventory("stash", stashId)
|
exports["tgiann-inventory"]:DeleteInventory("stash", stashId)
|
||||||
|
|
||||||
elseif isStarted(RSGInv) then
|
elseif isStarted(RSGInv) then
|
||||||
debugPrint("^5Bridge^7: ^2Cleared ^3"..RSGInv.."^2 Stash^7", stashId)
|
debugPrint("^5Bridge^7: ^2Clearing ^3"..RSGInv.."^2 Stash^7", stashId)
|
||||||
exports[RSGInv]:ClearStash(stashId)
|
exports[RSGInv]:ClearStash(stashId)
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -325,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
|
||||||
@@ -428,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
|
||||||
@@ -470,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
|
||||||
@@ -644,27 +643,25 @@ end
|
|||||||
--- )
|
--- )
|
||||||
--- ```
|
--- ```
|
||||||
function registerStash(name, label, slots, weight, owner, coords)
|
function registerStash(name, label, slots, weight, owner, coords)
|
||||||
|
local stashResource = ""
|
||||||
if isStarted(OXInv) then
|
if isStarted(OXInv) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Stash^7:", name, label, owner or nil)
|
stashResource = OXInv
|
||||||
exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil)
|
exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil)
|
||||||
|
|
||||||
--elseif isStarted(QSInv) then
|
|
||||||
-- debugPrint("^6Bridge^7: ^2Registering ^3QS ^2Stash^7:", name, label)
|
|
||||||
-- exports[QSInv]:RegisterStash(name, label, slots or 50, weight or 4000000)
|
|
||||||
|
|
||||||
--elseif isStarted(CoreInv) then
|
|
||||||
-- debugPrint("^6Bridge^7: ^2Registering ^3CoreInv ^2Stash^7:", name, label)
|
|
||||||
-- exports[CoreInv]:openHolder(nil, name, 'stash', nil, nil, false, nil)
|
|
||||||
|
|
||||||
elseif isStarted(OrigenInv) then
|
elseif isStarted(OrigenInv) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3OrigenInv ^2Stash^7:", name, label)
|
stashResource = OrigenInv
|
||||||
exports["origen_inventory"]:registerStash(name, label, slots or 50, weight or 4000000)
|
exports["origen_inventory"]:registerStash(name, label, slots or 50, weight or 4000000)
|
||||||
|
|
||||||
elseif isStarted(TgiannInv) then
|
elseif isStarted(TgiannInv) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3TgiannInv ^2Stash^7:", name, label)
|
stashResource = TgiannInv
|
||||||
exports[TgiannInv]:RegisterStash(name, label, slots or 50, weight or 4000000)
|
exports[TgiannInv]:RegisterStash(name, label, slots or 50, weight or 4000000)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
if stashResource ~= "" then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^4"..stashResource.." ^3Stash^7:", name, "^4Label^7: "..label)
|
||||||
|
else
|
||||||
|
debugPrint("^1ERROR^7: ^1Couldn't find supported inventory to register stash^7:", name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if isServer() then
|
if isServer() then
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -21,24 +21,29 @@
|
|||||||
--- })
|
--- })
|
||||||
--- ```
|
--- ```
|
||||||
function registerCommand(command, options)
|
function registerCommand(command, options)
|
||||||
|
local commandResource = ""
|
||||||
if isStarted(OXLibExport) then
|
if isStarted(OXLibExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..OXLibExport, command)
|
commandResource = OXLibExport
|
||||||
lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[4])
|
lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[4])
|
||||||
|
|
||||||
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..QBExport, command)
|
commandResource = QBExport
|
||||||
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..RSGExport, command)
|
commandResource = RSGExport
|
||||||
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
||||||
|
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7ESX Legacy", command)
|
commandResource = ESXExport
|
||||||
ESX.RegisterCommand(command, options[5] or 'admin', function(xPlayer, args, showError)
|
ESX.RegisterCommand(command, options[5] or 'admin', function(xPlayer, args, showError)
|
||||||
options[4](xPlayer.source, args, showError)
|
options[4](xPlayer.source, args, showError)
|
||||||
end, false, { help = options[1] })
|
end, false, { help = options[1] })
|
||||||
|
|
||||||
end
|
end
|
||||||
|
if commandResource ~= "" then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^4"..commandResource.."^7:", "^7/"..command)
|
||||||
|
else
|
||||||
|
print("^1ERROR^7: ^1Couldn't find supported framework to register command^7:", "/"..command)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
44
starter.lua
44
starter.lua
@@ -177,7 +177,7 @@ for _, v in pairs({ -- This is a specific load order
|
|||||||
'effects.lua',
|
'effects.lua',
|
||||||
|
|
||||||
--'warmenu.lua',
|
--'warmenu.lua',
|
||||||
|
'_authToken.lua',
|
||||||
-- Do version check last
|
-- Do version check last
|
||||||
'_scriptversioncheck.lua'
|
'_scriptversioncheck.lua'
|
||||||
}) do
|
}) do
|
||||||
@@ -191,16 +191,46 @@ for _, v in pairs({ -- This is a specific load order
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Auto Detection of Old/New QBInv/PSInv versions --
|
||||||
|
|
||||||
if isStarted(QBInv) then
|
if isStarted(QBInv) then
|
||||||
if not checkExportExists(QBInv, "HasItem") then
|
if isServer() then
|
||||||
print("^6Bridge^7: ^2Can^7'^2t ^2find new QBInv export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
if not checkExportExists(QBInv, "CreateShop") then
|
||||||
QBInvNew = false
|
print("^6Bridge^7: ^2Can^7'^2t ^2find new ^4"..QBInv.."^2 export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||||
|
QBInvNew = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if checkExportExists(QBInv, "GetTrunkSize") then
|
||||||
|
print("^6Bridge^7: ^2Found old ^4"..QBInv.."^3 export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||||
|
QBInvNew = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if isStarted(PSInv) then
|
if isStarted(PSInv) then
|
||||||
if not checkExportExists(PSInv, "HasItem") then
|
if isServer() then
|
||||||
print("^6Bridge^7: ^2Can^7't ^2find new PSInv export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
if not checkExportExists(PSInv, "CreateShop") then
|
||||||
QBInvNew = false
|
print("^6Bridge^7: ^2Can^7'^2t ^2find new ^4"..PSInv.."^2 export^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||||
|
QBInvNew = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local function versionCompare(v1, v2)
|
||||||
|
local split = function(v) local t = {} for s in v:gmatch("[^.]+") do t[#t+1] = tonumber(s) end return t end
|
||||||
|
local a, b = split(v1), split(v2)
|
||||||
|
for i = 1, math.max(#a, #b) do
|
||||||
|
local ai, bi = a[i] or 0, b[i] or 0
|
||||||
|
if ai > bi then return true
|
||||||
|
elseif ai < bi then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if versionCompare(GetResourceMetadata(PSInv, "version", 0), "1.0.6") then
|
||||||
|
print("^6Bridge^7: ^4"..PSInv.."^2 Version above ^31.0.6^7, ^2forcing ^1QBInvNew ^2to ^1true^7")
|
||||||
|
QBInvNew = true
|
||||||
|
else
|
||||||
|
print("^6Bridge^7: ^4"..PSInv.."^2 Version ^1below ^31.0.6^7, ^2forcing ^1QBInvNew ^2to ^1false^7")
|
||||||
|
QBInvNew = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2,13 +2,15 @@ local notifications = {}
|
|||||||
local spacing = 60 -- vertical spacing between notifications
|
local spacing = 60 -- vertical spacing between notifications
|
||||||
local activeDrawing = false -- flag for drawing loop state
|
local activeDrawing = false -- flag for drawing loop state
|
||||||
|
|
||||||
if not HasStreamedTextureDictLoaded("timerbars") then
|
local function loadTextureDict(dict)
|
||||||
while not HasStreamedTextureDictLoaded("timerbars") do
|
if not HasStreamedTextureDictLoaded(dict) then
|
||||||
RequestStreamedTextureDict("timerbars")
|
while not HasStreamedTextureDictLoaded(dict) do RequestStreamedTextureDict(dict) Wait(5) end
|
||||||
Wait(5)
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- GTA Section ---
|
||||||
|
|
||||||
local notifTypes = {
|
local notifTypes = {
|
||||||
success = "✔️",
|
success = "✔️",
|
||||||
error = "❌",
|
error = "❌",
|
||||||
@@ -35,12 +37,13 @@ function gtaNotify(title, message, emoji, src)
|
|||||||
-- This allows it to be silent until the first notifcation is called, then the loop starts
|
-- This allows it to be silent until the first notifcation is called, then the loop starts
|
||||||
if not activeDrawing then
|
if not activeDrawing then
|
||||||
activeDrawing = true
|
activeDrawing = true
|
||||||
StartDrawingLoop()
|
StartGTADrawingLoop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Drawing loop as a separate controlled thread
|
-- Drawing loop as a separate controlled thread
|
||||||
function StartDrawingLoop()
|
function StartGTADrawingLoop()
|
||||||
|
--loadTextureDict("timerbars")
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while #notifications > 0 do
|
while #notifications > 0 do
|
||||||
local currentTime = GetGameTimer()
|
local currentTime = GetGameTimer()
|
||||||
@@ -87,14 +90,14 @@ function StartDrawingLoop()
|
|||||||
if not notif.title or notif.title == "" then
|
if not notif.title or notif.title == "" then
|
||||||
moveMessage = true
|
moveMessage = true
|
||||||
else
|
else
|
||||||
drawNotiText(8, 0.4, vec2(0.75, 0.975), notif.title, vec2(posX, posY))
|
drawGTANotiText(8, 0.4, vec2(0.75, 0.975), notif.title, vec2(posX, posY))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Message text
|
-- Message text
|
||||||
drawNotiText(4, 0.3, vec2(0.75, 0.975), notif.message, vec2(posX, posY + (moveMessage and 0.015 or 0.03)))
|
drawGTANotiText(4, 0.3, vec2(0.75, 0.975), notif.message, vec2(posX, posY + (moveMessage and 0.015 or 0.03)))
|
||||||
|
|
||||||
-- Emoji
|
-- Emoji
|
||||||
drawNotiText(0, 0.3, vec2(0.75, 0.995), notif.emoji, vec2(posX, posY + 0.015))
|
drawGTANotiText(0, 0.3, vec2(0.75, 0.995), notif.emoji, vec2(posX, posY + 0.015))
|
||||||
|
|
||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
@@ -104,7 +107,7 @@ function StartDrawingLoop()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function drawNotiText(font, scale, wrap, string, pos)
|
function drawGTANotiText(font, scale, wrap, string, pos)
|
||||||
SetTextFont(font)
|
SetTextFont(font)
|
||||||
SetTextScale(scale, scale)
|
SetTextScale(scale, scale)
|
||||||
SetTextWrap(wrap.x, wrap.y)
|
SetTextWrap(wrap.x, wrap.y)
|
||||||
@@ -116,5 +119,148 @@ function drawNotiText(font, scale, wrap, string, pos)
|
|||||||
DrawText(pos.x, pos.y)
|
DrawText(pos.x, pos.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
RegisterNetEvent("jim-bridge:Notify", gtaNotify)
|
|
||||||
exports("Notify", gtaNotify)
|
------------------
|
||||||
|
-- REDM SECTION --
|
||||||
|
------------------
|
||||||
|
---
|
||||||
|
---
|
||||||
|
local REDMnotifTypes = {
|
||||||
|
success = vec3(0, 150, 0),
|
||||||
|
error = vec3(150, 0, 0),
|
||||||
|
warning = vec3(0, 150, 150),
|
||||||
|
police = vec3(0, 0, 150),
|
||||||
|
ambulance = vec3(0, 0, 150),
|
||||||
|
}
|
||||||
|
|
||||||
|
function redNotify(title, message, style)
|
||||||
|
local notif = {
|
||||||
|
title = title and CreateVarString(10, "LITERAL_STRING", title) or nil,
|
||||||
|
message = message and CreateVarString(10, "LITERAL_STRING", message) or nil,
|
||||||
|
state = "enter",
|
||||||
|
startTime = GetGameTimer(),
|
||||||
|
style = REDMnotifTypes[style] or vec3(0, 0, 0),
|
||||||
|
progress = 0,
|
||||||
|
holdTime = 8000,
|
||||||
|
slideDuration = 100,
|
||||||
|
currentOffset = 0,
|
||||||
|
}
|
||||||
|
table.insert(notifications, notif)
|
||||||
|
|
||||||
|
-- Activate drawing loop if not already running
|
||||||
|
if not activeDrawing then
|
||||||
|
activeDrawing = true
|
||||||
|
StartREDMDrawingLoop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Drawing loop as a separate controlled thread
|
||||||
|
function StartREDMDrawingLoop()
|
||||||
|
CreateThread(function()
|
||||||
|
loadTextureDict("generic_textures")
|
||||||
|
while #notifications > 0 do
|
||||||
|
local currentTime = GetGameTimer()
|
||||||
|
|
||||||
|
-- Update notifications vertical offset
|
||||||
|
for i, notif in ipairs(notifications) do
|
||||||
|
local target = (#notifications - i) * spacing
|
||||||
|
notif.currentOffset += (target - notif.currentOffset) * 0.1
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = #notifications, 1, -1 do
|
||||||
|
local notif = notifications[i]
|
||||||
|
|
||||||
|
if notif.state == "enter" then
|
||||||
|
local elapsed = currentTime - notif.startTime
|
||||||
|
notif.progress = math.min(elapsed / notif.slideDuration, 1.0)
|
||||||
|
if notif.progress >= 1.0 then
|
||||||
|
notif.state = "hold"
|
||||||
|
notif.holdStart = currentTime
|
||||||
|
end
|
||||||
|
elseif notif.state == "hold" then
|
||||||
|
if currentTime - notif.holdStart >= notif.holdTime then
|
||||||
|
notif.state = "exit"
|
||||||
|
notif.exitStart = currentTime
|
||||||
|
end
|
||||||
|
elseif notif.state == "exit" then
|
||||||
|
local elapsed = currentTime - notif.exitStart
|
||||||
|
notif.progress = 1.0 - math.min(elapsed / notif.slideDuration, 1.0)
|
||||||
|
if notif.progress <= 0 then
|
||||||
|
table.remove(notifications, i)
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local startX, targetX = 1.0, 0.8
|
||||||
|
local posX = startX - (startX - targetX) * notif.progress
|
||||||
|
local posY = 0.05 + (notif.currentOffset / 1080)
|
||||||
|
|
||||||
|
-- Background sprite
|
||||||
|
DrawSprite("generic_textures", "inkroller_1a", posX + 0.09, posY + 0.025, 0.2, 0.053, 180.0, notif.style.x, notif.style.y, notif.style.z, 150)
|
||||||
|
|
||||||
|
-- Title text
|
||||||
|
local moveMessage = false
|
||||||
|
if not notif.title or notif.title == "" then
|
||||||
|
moveMessage = true
|
||||||
|
else
|
||||||
|
drawREDMNotiText(1, 0.4, vec2(0.75, 0.975), notif.title, vec2(posX, posY))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Message text
|
||||||
|
drawREDMNotiText(6, 0.3, vec2(0.75, 0.975), notif.message, vec2(posX, posY + (moveMessage and 0.0145 or 0.025)))
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
Wait(0)
|
||||||
|
end
|
||||||
|
activeDrawing = false -- No notifications left, pause drawing
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function drawREDMNotiText(font, scale, wrap, string, pos)
|
||||||
|
SetTextFontForCurrentCommand(font)
|
||||||
|
SetTextScale(scale, scale)
|
||||||
|
SetTextColor(255, 255, 255, 255)
|
||||||
|
SetTextWrap(wrap.x, wrap.y)
|
||||||
|
SetTextJustification(2)
|
||||||
|
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||||
|
DisplayText(string, pos.x, pos.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Exports --
|
||||||
|
-- Example:
|
||||||
|
-- TriggerEvent("jim_bridge:Notify", nil, "Short test message", "success")
|
||||||
|
--
|
||||||
|
-- exports["jim_bridge"]:Notify(nil, "Short test message", "success")
|
||||||
|
--
|
||||||
|
|
||||||
|
RegisterNetEvent("jim_bridge:RedNotify", redNotify)
|
||||||
|
RegisterNetEvent("jim_bridge:GTANotify", gtaNotify)
|
||||||
|
|
||||||
|
RegisterNetEvent("jim_bridge:Notify", function(...)
|
||||||
|
if GetCurrentGameName() == "rdr3" then
|
||||||
|
redNotify(...)
|
||||||
|
else
|
||||||
|
gtaNotify(...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function Notify(...)
|
||||||
|
if GetCurrentGameName() == "rdr3" then
|
||||||
|
redNotify(...)
|
||||||
|
else
|
||||||
|
gtaNotify(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
exports("Notify", Notify)
|
||||||
|
|
||||||
|
-- Example testing notifications (can remove after testing)
|
||||||
|
--CreateThread(function()
|
||||||
|
-- TriggerEvent("jim_bridge:Notify", nil, "Short test message", "success")
|
||||||
|
-- Wait(3000)
|
||||||
|
-- TriggerEvent("jim_bridge:Notify", "Error!", "This is a longer example message to show stacking.", "error")
|
||||||
|
-- Wait(3000)
|
||||||
|
-- TriggerEvent("jim_bridge:Notify", "Test!", "This notification has to 'type' set.", nil )
|
||||||
|
-- Wait(4000)
|
||||||
|
--end)
|
||||||
363
ui_modules/progressbar.lua
Normal file
363
ui_modules/progressbar.lua
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
local inProgress = false
|
||||||
|
|
||||||
|
local function loadTextureDict(dict)
|
||||||
|
if not HasStreamedTextureDictLoaded(dict) then
|
||||||
|
while not HasStreamedTextureDictLoaded(dict) do RequestStreamedTextureDict(dict) Wait(5) 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)
|
||||||
|
local ped = PlayerPedId()
|
||||||
|
|
||||||
|
local result = nil
|
||||||
|
|
||||||
|
loadTextureDict("generic_textures")
|
||||||
|
if inProgress then return false end
|
||||||
|
inProgress = true
|
||||||
|
local wait = debugMode and 1000 or data.time
|
||||||
|
local endTime = GetGameTimer() + wait
|
||||||
|
|
||||||
|
LocalPlayer.state:set("inv_busy", true, true)
|
||||||
|
TriggerEvent('inventory:client:busy:status', true)
|
||||||
|
TriggerEvent('canUseInventoryAndHotbar:toggle', not true)
|
||||||
|
|
||||||
|
-- Setup Animation/Task if specified
|
||||||
|
if data.dict then
|
||||||
|
playAnim(data.dict, data.anim, -1, data.flag or 32)
|
||||||
|
elseif data.task then
|
||||||
|
TaskStartScenarioInPlace(ped, data.task, -1, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Progress bar rendering loop
|
||||||
|
CreateThread(function()
|
||||||
|
while GetGameTimer() < endTime and inProgress do
|
||||||
|
Wait(0)
|
||||||
|
local elapsed = GetGameTimer()
|
||||||
|
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||||
|
|
||||||
|
-- Convert to segmented progress (assuming 5 segments here)
|
||||||
|
local segments = 1 -- Number of segments in the bar
|
||||||
|
local segmentProgress = {}
|
||||||
|
local progressPerSegment = 100 / segments
|
||||||
|
|
||||||
|
for i = 1, segments do
|
||||||
|
local segmentStart = (i - 1) * progressPerSegment
|
||||||
|
local segmentEnd = i * progressPerSegment
|
||||||
|
if percentage >= segmentEnd then
|
||||||
|
segmentProgress[i] = 100
|
||||||
|
elseif percentage <= segmentStart then
|
||||||
|
segmentProgress[i] = 0
|
||||||
|
else
|
||||||
|
segmentProgress[i] = ((percentage - segmentStart) / progressPerSegment) * 100
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
percentage = percentage >= 100 and 100 or percentage
|
||||||
|
-- Draw your segmented progress bar
|
||||||
|
ShowRedProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
||||||
|
|
||||||
|
-- Controls to disable during progress
|
||||||
|
if data.disableMouse then
|
||||||
|
DisableControlAction(0, `INPUT_LOOK_LR`, true)
|
||||||
|
DisableControlAction(0, `INPUT_LOOK_UD`, true)
|
||||||
|
DisableControlAction(0, `INPUT_VEH_MOUSE_CONTROL_OVERRIDE`, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.disableMovement then
|
||||||
|
DisableControlAction(0, `INPUT_MOVE_LR`, true)
|
||||||
|
DisableControlAction(0, `INPUT_MOVE_UD`, true)
|
||||||
|
DisableControlAction(0, `INPUT_DUCK`, true)
|
||||||
|
DisableControlAction(0, `INPUT_SPRINT`, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.disableCarMovement then
|
||||||
|
DisableControlAction(0, `INPUT_VEH_MOVE_LEFT_ONLY`, true)
|
||||||
|
DisableControlAction(0, `INPUT_VEH_MOVE_RIGHT_ONLY`, true)
|
||||||
|
DisableControlAction(0, `INPUT_VEH_ACCELERATE`, true)
|
||||||
|
DisableControlAction(0, `INPUT_VEH_BRAKE`, true)
|
||||||
|
DisableControlAction(0, `INPUT_VEH_EXIT`, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.disableCombat then
|
||||||
|
DisablePlayerFiring(PlayerId(), true)
|
||||||
|
DisableControlAction(0, `INPUT_ATTACK`, true)
|
||||||
|
DisableControlAction(0, `INPUT_AIM`, true)
|
||||||
|
DisableControlAction(0, `INPUT_OPEN_WHEEL_MENU`, true)
|
||||||
|
DisableControlAction(0, `INPUT_DETONATE`, true)
|
||||||
|
DisableControlAction(0, `INPUT_THROW_GRENADE`, true)
|
||||||
|
DisableControlAction(0, `INPUT_MELEE_ATTACK`, true)
|
||||||
|
DisableControlAction(0, `INPUT_MELEE_GRAPPLE_ATTACK`, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if data.cancel and (
|
||||||
|
IsControlJustReleased(0, `INPUT_FRONTEND_RS`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_SWITCH_SHOULDER`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_GAME_MENU_TAB_RIGHT_SECONDARY`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_FRONTEND_CANCEL`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_GAME_MENU_CANCEL`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_FRONTEND_RRIGHT`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_QUIT`) or
|
||||||
|
IsControlJustReleased(0, `INPUT_MINIGAME_QUIT`)
|
||||||
|
) then
|
||||||
|
inProgress = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Wait for completion or cancel
|
||||||
|
while GetGameTimer() < endTime and inProgress do
|
||||||
|
Wait(100)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleanup animations/tasks
|
||||||
|
if data.dict then
|
||||||
|
stopAnim(data.dict, data.anim, ped)
|
||||||
|
end
|
||||||
|
if data.task then
|
||||||
|
ClearPedTasks(ped)
|
||||||
|
end
|
||||||
|
|
||||||
|
result = inProgress
|
||||||
|
inProgress = false
|
||||||
|
|
||||||
|
while result == nil do Wait(10) end
|
||||||
|
|
||||||
|
-- Cleanup
|
||||||
|
FreezeEntityPosition(ped, false)
|
||||||
|
|
||||||
|
LocalPlayer.state:set("inv_busy", false, true)
|
||||||
|
TriggerEvent('inventory:client:busy:status', false)
|
||||||
|
TriggerEvent('canUseInventoryAndHotbar:toggle', not false)
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function ShowRedProgressBar(currentProg, title, level)
|
||||||
|
local loc = vec2(0.40, 0.90)
|
||||||
|
local size = vec2(0.3, 0.03)
|
||||||
|
|
||||||
|
-- Draw background box
|
||||||
|
DrawSprite("generic_textures", "inkroller_1a", loc.x+0.1, loc.y-0.01, 0.25, 0.07, 180.0, 0, 0, 0, 200)
|
||||||
|
|
||||||
|
SetTextFontForCurrentCommand(6)
|
||||||
|
SetTextScale(0.35, 0.35)
|
||||||
|
SetTextColor(255, 255, 255, 255)
|
||||||
|
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||||
|
BgDisplayText(title, loc.x - size.x / 4 + 0.074, loc.y - 0.034)
|
||||||
|
|
||||||
|
SetTextFontForCurrentCommand(1)
|
||||||
|
SetTextScale(0.35, 0.35)
|
||||||
|
SetTextColor(255, 255, 255, 255)
|
||||||
|
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||||
|
BgDisplayText(level, loc.x - size.x / 4 + 0.246, loc.y - 0.030)
|
||||||
|
|
||||||
|
local segmentWidth = (size.x + 0.05) / (#currentProg * 2) -- Divide the total width by 18 (9 segments * 2 gaps for each)
|
||||||
|
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
||||||
|
|
||||||
|
for i = 1, #currentProg do
|
||||||
|
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
||||||
|
local fillPercentage = currentProg[i]
|
||||||
|
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
||||||
|
|
||||||
|
-- Semi-transparent background for each segment
|
||||||
|
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
||||||
|
|
||||||
|
-- Filling progress for each segment
|
||||||
|
if progressBarWidth > 0 then
|
||||||
|
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 255, 0, 0, 200) -- Blue progress
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function gtaProgressBar(data)
|
||||||
|
local ped = PlayerPedId()
|
||||||
|
|
||||||
|
local result = nil
|
||||||
|
|
||||||
|
loadTextureDict("timerbars")
|
||||||
|
if inProgress then return false end
|
||||||
|
inProgress = true
|
||||||
|
local wait = debugMode and 1000 or data.time
|
||||||
|
local endTime = GetGameTimer() + wait
|
||||||
|
|
||||||
|
-- Setup Animation/Task if specified
|
||||||
|
if data.dict then
|
||||||
|
playAnim(data.dict, data.anim, -1, data.flag or 32)
|
||||||
|
elseif data.task then
|
||||||
|
TaskStartScenarioInPlace(ped, data.task, -1, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Progress bar rendering loop
|
||||||
|
CreateThread(function()
|
||||||
|
while GetGameTimer() < endTime and inProgress do
|
||||||
|
Wait(0)
|
||||||
|
local elapsed = GetGameTimer()
|
||||||
|
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||||
|
|
||||||
|
-- Convert to segmented progress (assuming 5 segments here)
|
||||||
|
local segments = 1 -- Number of segments in the bar
|
||||||
|
local segmentProgress = {}
|
||||||
|
local progressPerSegment = 100 / segments
|
||||||
|
|
||||||
|
for i = 1, segments do
|
||||||
|
local segmentStart = (i - 1) * progressPerSegment
|
||||||
|
local segmentEnd = i * progressPerSegment
|
||||||
|
if percentage >= segmentEnd then
|
||||||
|
segmentProgress[i] = 100
|
||||||
|
elseif percentage <= segmentStart then
|
||||||
|
segmentProgress[i] = 0
|
||||||
|
else
|
||||||
|
segmentProgress[i] = ((percentage - segmentStart) / progressPerSegment) * 100
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
percentage = percentage >= 100 and 100 or percentage
|
||||||
|
-- Draw your segmented progress bar
|
||||||
|
ShowGTAProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
||||||
|
|
||||||
|
-- Controls to disable during progress
|
||||||
|
DisablePlayerFiring(ped, true)
|
||||||
|
DisableControlAction(0, 25, true) -- Disable aim
|
||||||
|
DisableControlAction(0, 21, true) -- Disable sprint
|
||||||
|
DisableControlAction(0, 30, true) -- Disable move left/right
|
||||||
|
DisableControlAction(0, 31, true) -- Disable move forward/back
|
||||||
|
DisableControlAction(0, 36, true) -- Disable stealth
|
||||||
|
|
||||||
|
if data.cancel and (IsControlJustReleased(0, 202) or IsControlJustReleased(0, 177) or IsControlJustReleased(0, 73)) then
|
||||||
|
inProgress = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Wait for completion or cancel
|
||||||
|
while GetGameTimer() < endTime and inProgress do
|
||||||
|
Wait(100)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleanup animations/tasks
|
||||||
|
if data.dict then
|
||||||
|
stopAnim(data.dict, data.anim, ped)
|
||||||
|
end
|
||||||
|
if data.task then
|
||||||
|
ClearPedTasks(ped)
|
||||||
|
end
|
||||||
|
|
||||||
|
result = inProgress
|
||||||
|
inProgress = false
|
||||||
|
|
||||||
|
while result == nil do Wait(10) end
|
||||||
|
|
||||||
|
-- Cleanup
|
||||||
|
FreezeEntityPosition(ped, false)
|
||||||
|
|
||||||
|
LocalPlayer.state:set("inv_busy", false, true)
|
||||||
|
TriggerEvent('inventory:client:busy:status', false)
|
||||||
|
TriggerEvent('canUseInventoryAndHotbar:toggle', not false)
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function ShowGTAProgressBar(currentProg, title, level)
|
||||||
|
local loc = vec2(0.37, 0.90)
|
||||||
|
local size = vec2(0.3, 0.03)
|
||||||
|
|
||||||
|
-- Draw background box
|
||||||
|
DrawSprite("timerbars", "all_black_bg", loc.x +0.028, loc.y-0.01, 0.15, 0.07, 0.0, 255, 255, 255, 255)
|
||||||
|
DrawSprite("timerbars", "all_black_bg", loc.x +0.170, loc.y-0.01, 0.15, 0.07, 180.0, 255, 255, 255, 255)
|
||||||
|
|
||||||
|
SetTextFont(0)
|
||||||
|
SetTextProportional(1)
|
||||||
|
SetTextScale(0.35, 0.35)
|
||||||
|
SetTextColour(255, 255, 255, 255)
|
||||||
|
SetTextDropshadow(0, 0, 0, 0, 255)
|
||||||
|
SetTextDropShadow()
|
||||||
|
SetTextOutline()
|
||||||
|
SetTextEntry("STRING")
|
||||||
|
AddTextComponentString(title)
|
||||||
|
DrawText(loc.x - size.x / 4 + 0.074, loc.y - 0.034) -- Adjust text position
|
||||||
|
|
||||||
|
SetTextFont(0)
|
||||||
|
SetTextProportional(1)
|
||||||
|
SetTextScale(0.35, 0.25)
|
||||||
|
SetTextColour(255, 255, 255, 255)
|
||||||
|
SetTextEntry("STRING")
|
||||||
|
AddTextComponentString(level)
|
||||||
|
DrawText(loc.x - size.x / 4 + 0.246, loc.y - 0.030) -- Right-aligned additional text
|
||||||
|
|
||||||
|
local segmentWidth = (size.x + 0.05) / (#currentProg * 2) -- Divide the total width by 18 (9 segments * 2 gaps for each)
|
||||||
|
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
||||||
|
|
||||||
|
for i = 1, #currentProg do
|
||||||
|
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
||||||
|
local fillPercentage = currentProg[i]
|
||||||
|
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
||||||
|
|
||||||
|
-- Semi-transparent background for each segment
|
||||||
|
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
||||||
|
|
||||||
|
-- Filling progress for each segment
|
||||||
|
if progressBarWidth > 0 then
|
||||||
|
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 93, 182, 229, 255) -- Blue progress
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function stopProgressBar() inProgress = false end
|
||||||
|
function isProgressBar() return inProgress end
|
||||||
|
|
||||||
|
function progressBar(...)
|
||||||
|
if GetCurrentGameName() == "rdr3" then
|
||||||
|
return redProgressBar(...)
|
||||||
|
else
|
||||||
|
return gtaProgressBar(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
exports("progressBar", function(...)
|
||||||
|
return progressBar(...)
|
||||||
|
end)
|
||||||
|
exports("gtaProgressBar", function(...)
|
||||||
|
return gtaProgressBar(...)
|
||||||
|
end)
|
||||||
|
exports("redProgressBar", function(...)
|
||||||
|
return redProgressBar(...)
|
||||||
|
end)
|
||||||
|
|
||||||
|
exports("stopProgressBar", stopProgressBar)
|
||||||
|
exports("isProgressBar", isProgressBar)
|
||||||
|
|
||||||
|
--RegisterCommand("testprog", function()
|
||||||
|
-- print("test")
|
||||||
|
-- if progressBar({
|
||||||
|
-- label = "Processing...",
|
||||||
|
-- time = 10000,
|
||||||
|
-- disableMovement = true,
|
||||||
|
-- --dict = "amb@world_human_hang_out_street@female_hold_arm@base",
|
||||||
|
-- --anim = "base",
|
||||||
|
-- --flag = 49,
|
||||||
|
-- cancel = true,
|
||||||
|
-- }) then
|
||||||
|
-- Notify("Success!", "Short test message", "success")
|
||||||
|
-- else
|
||||||
|
-- Notify("Error!", "This is a longer example message to show stacking.", "error")
|
||||||
|
-- end
|
||||||
|
--end)
|
||||||
@@ -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
|
||||||
@@ -79,7 +79,7 @@ function gtaSkillCheck()
|
|||||||
-- Draw stationary cursor at result position
|
-- Draw stationary cursor at result position
|
||||||
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)
|
||||||
-- Display result text
|
-- Display result text
|
||||||
drawSuccessText(x, y, result and "Success" or "Failed", result and 114 or 228, result and 204 or 52, result and 144 or 52)
|
drawGTASuccessText(x, y, result and "Success" or "Failed", result and 114 or 228, result and 204 or 52, result and 144 or 52)
|
||||||
end
|
end
|
||||||
if result then
|
if result then
|
||||||
successes += 1
|
successes += 1
|
||||||
@@ -94,7 +94,7 @@ function gtaSkillCheck()
|
|||||||
return successes == barsRequired
|
return successes == barsRequired
|
||||||
end
|
end
|
||||||
|
|
||||||
function drawSuccessText(x, y, text, r, g, b)
|
function drawGTASuccessText(x, y, text, r, g, b)
|
||||||
SetTextFont(8)
|
SetTextFont(8)
|
||||||
SetTextScale(0.45, 0.45)
|
SetTextScale(0.45, 0.45)
|
||||||
SetTextColour(r, g, b, 255)
|
SetTextColour(r, g, b, 255)
|
||||||
@@ -118,4 +118,10 @@ function createScaleBars(x, y, width, height)
|
|||||||
DrawRect(x, y, width, height, 100, 100, 100, 255)
|
DrawRect(x, y, width, height, 100, 100, 100, 255)
|
||||||
end
|
end
|
||||||
|
|
||||||
exports("skillCheck", gtaSkillCheck)
|
exports("skillCheck", function(...)
|
||||||
|
if GetCurrentGameName() == "rdr3" then
|
||||||
|
--redSkillCheck(...)
|
||||||
|
else
|
||||||
|
gtaSkillCheck(...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
15
version.txt
15
version.txt
@@ -1,6 +1,15 @@
|
|||||||
2.0.17
|
2.0.22
|
||||||
|
|
||||||
- Fix progressbars getting stuck on ox_lib
|
- Support for qs-interface
|
||||||
- #BlameMcklean for this one
|
- 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
|
||||||
|
|
||||||
https://github.com/jimathy/jim_bridge
|
https://github.com/jimathy/jim_bridge
|
||||||
Reference in New Issue
Block a user