mirror of
https://github.com/jimathy/jim_bridge.git
synced 2026-08-19 14:56:02 +01:00
Merge branch 'main' of https://github.com/oosayeroo/jim_bridge
This commit is contained in:
12
.gitattributes
vendored
Normal file
12
.gitattributes
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# VCS / CI noise
|
||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
||||||
|
.github/** export-ignore
|
||||||
|
.gitmodules export-ignore
|
||||||
|
|
||||||
|
# Dev tooling/config
|
||||||
|
.vscode/** export-ignore
|
||||||
|
*.code-workspace export-ignore
|
||||||
|
.editorconfig export-ignore
|
||||||
|
.eslintrc* export-ignore
|
||||||
|
.prettier* export-ignore
|
||||||
88
.github/workflows/release.yml
vendored
Normal file
88
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
name: Package & Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
- "*.*.*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout (full history)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Derive vars
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "name=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT # e.g. jim_bridge
|
||||||
|
echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT # strip leading v
|
||||||
|
|
||||||
|
- name: Build zip with folder prefix
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
NAME='${{ steps.vars.outputs.name }}'
|
||||||
|
VER='${{ steps.vars.outputs.version }}'
|
||||||
|
git archive --format=zip --prefix="${NAME}/" -o "${NAME}-${VER}.zip" "$GITHUB_REF_NAME"
|
||||||
|
ls -lh "${NAME}-${VER}.zip"
|
||||||
|
|
||||||
|
# --- Patch notes generation (commit titles + links) -------------------
|
||||||
|
- name: Find previous tag (SemVer aware)
|
||||||
|
id: prev
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
CUR="${GITHUB_REF_NAME}"
|
||||||
|
# Sort tags by version desc, drop the current, take the next newest
|
||||||
|
PREV=$(git tag --sort=-v:refname | grep -v -x "$CUR" | head -n 1 || true)
|
||||||
|
echo "prev=$PREV" >> $GITHUB_OUTPUT
|
||||||
|
echo "Previous tag: ${PREV:-<none>}"
|
||||||
|
|
||||||
|
- name: Generate RELEASE_NOTES.md from commits
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
REPO="${{ github.repository }}"
|
||||||
|
CUR="${GITHUB_REF_NAME}"
|
||||||
|
PREV='${{ steps.prev.outputs.prev }}'
|
||||||
|
|
||||||
|
if [ -n "$PREV" ]; then
|
||||||
|
RANGE="$PREV..$CUR"
|
||||||
|
HEADER="## Changes in $CUR"
|
||||||
|
else
|
||||||
|
# First release: include all commits
|
||||||
|
ROOT=$(git rev-list --max-parents=0 HEAD | tail -n 1)
|
||||||
|
RANGE="$ROOT..$CUR"
|
||||||
|
HEADER="## Changes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "$HEADER"
|
||||||
|
echo
|
||||||
|
# Oldest → newest, subject only, skip merge commits
|
||||||
|
# Escape '[' and ']' so markdown doesn't break on rare titles
|
||||||
|
git log --reverse --no-merges --pretty=format:'- ['%h'] - %s' "$RANGE" \
|
||||||
|
| perl -pe 's/([\[\]])/\\$1/g'
|
||||||
|
} > RELEASE_NOTES.md
|
||||||
|
|
||||||
|
echo "--- RELEASE_NOTES.md ---"
|
||||||
|
cat RELEASE_NOTES.md
|
||||||
|
echo "------------------------"
|
||||||
|
|
||||||
|
- name: Create / Update GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: ${{ github.ref_name }}
|
||||||
|
files: |
|
||||||
|
*.zip
|
||||||
|
body_path: RELEASE_NOTES.md # attach our generated notes
|
||||||
|
# If you wanted GitHub's auto notes instead, set:
|
||||||
|
# generate_release_notes: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -1,67 +1,78 @@
|
|||||||
local function parseVersion(version)
|
local function readBoolMeta(key, default)
|
||||||
local parts = {}
|
local val = GetResourceMetadata(GetCurrentResourceName(), key, 0)
|
||||||
for num in version:gmatch("%d+") do
|
if not val then return default end
|
||||||
table.insert(parts, tonumber(num))
|
val = tostring(val):lower()
|
||||||
end
|
return (val == 'true' or val == '1' or val == 'yes' or val == 'on')
|
||||||
return parts
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function compareVersions(current, newest)
|
local SUPPRESS_UPDATES = readBoolMeta('suppress_updates', false)
|
||||||
local currentParts = parseVersion(current)
|
|
||||||
local newestParts = parseVersion(newest)
|
if not SUPPRESS_UPDATES then
|
||||||
for i = 1, math.max(#currentParts, #newestParts) do
|
local function parseVersion(version)
|
||||||
local c = currentParts[i] or 0
|
local parts = {}
|
||||||
local n = newestParts[i] or 0
|
for num in version:gmatch("%d+") do
|
||||||
if c < n then return -1
|
table.insert(parts, tonumber(num))
|
||||||
elseif c > n then return 1 end
|
end
|
||||||
|
return parts
|
||||||
end
|
end
|
||||||
return 0 -- equal
|
|
||||||
end
|
|
||||||
|
|
||||||
function CheckBridgeVersion()
|
local function compareVersions(current, newest)
|
||||||
if IsDuplicityVersion() then
|
local currentParts = parseVersion(current)
|
||||||
CreateThread(function()
|
local newestParts = parseVersion(newest)
|
||||||
Wait(4000)
|
for i = 1, math.max(#currentParts, #newestParts) do
|
||||||
local currentVersionRaw = GetResourceMetadata("jim_bridge", 'version')
|
local c = currentParts[i] or 0
|
||||||
--PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/test.txt', function(err, body, headers)
|
local n = newestParts[i] or 0
|
||||||
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/jim_bridge/master/version.txt', function(err, body, headers)
|
if c < n then return -1
|
||||||
if not body then
|
elseif c > n then return 1 end
|
||||||
print("^1Unable to run version check for ^7'^3jim_bridge^7' (^3"..currentVersionRaw.."^7)")
|
end
|
||||||
return
|
return 0 -- equal
|
||||||
end
|
end
|
||||||
|
|
||||||
local lines = {}
|
function CheckBridgeVersion()
|
||||||
for line in body:gmatch("[^\r\n]+") do
|
if IsDuplicityVersion() then
|
||||||
table.insert(lines, line)
|
CreateThread(function()
|
||||||
end
|
Wait(4000)
|
||||||
|
local currentVersionRaw = GetResourceMetadata("jim_bridge", 'version')
|
||||||
local newestVersionRaw = lines[1] or "0.0.0"
|
--PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/test.txt', function(err, body, headers)
|
||||||
local changelog = {}
|
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/jim_bridge/master/version.txt', function(err, body, headers)
|
||||||
for i = 2, #lines do
|
if not body then
|
||||||
table.insert(changelog, lines[i])
|
print("^1Unable to run version check for ^7'^3jim_bridge^7' (^3"..currentVersionRaw.."^7)")
|
||||||
end
|
return
|
||||||
|
|
||||||
local compareResult = compareVersions(currentVersionRaw, newestVersionRaw)
|
|
||||||
|
|
||||||
if compareResult == 0 then
|
|
||||||
print("^7'^3jim_bridge^7' - ^2You are running the latest version^7. ^7(^3"..currentVersionRaw.."^7)")
|
|
||||||
elseif compareResult < 0 then
|
|
||||||
print("^1----------------------------------------------------------------------^7")
|
|
||||||
print("^7'^3jim_bridge^7' - ^1You are running an outdated version^7! ^7(^3"..currentVersionRaw.."^7 → ^3"..newestVersionRaw.."^7)")
|
|
||||||
for _, line in ipairs(changelog) do
|
|
||||||
print((line:find("http") and "^7" or "^5")..line)
|
|
||||||
end
|
end
|
||||||
print("^1----------------------------------------------------------------------^7")
|
|
||||||
SetTimeout(3600000, function()
|
local lines = {}
|
||||||
CheckBridgeVersion()
|
for line in body:gmatch("[^\r\n]+") do
|
||||||
end)
|
table.insert(lines, line)
|
||||||
else
|
end
|
||||||
print("^7'^3jim_bridge^7' - ^5You are running a newer version ^7(^3"..currentVersionRaw.."^7 ← ^3"..newestVersionRaw.."^7)")
|
|
||||||
end
|
local newestVersionRaw = lines[1] or "0.0.0"
|
||||||
|
local changelog = {}
|
||||||
|
for i = 2, #lines do
|
||||||
|
table.insert(changelog, lines[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
local compareResult = compareVersions(currentVersionRaw, newestVersionRaw)
|
||||||
|
|
||||||
|
if compareResult == 0 then
|
||||||
|
print("^7'^3jim_bridge^7' - ^2You are running the latest version^7. ^7(^3"..currentVersionRaw.."^7)")
|
||||||
|
elseif compareResult < 0 then
|
||||||
|
print("^1----------------------------------------------------------------------^7")
|
||||||
|
print("^7'^3jim_bridge^7' - ^1You are running an outdated version^7! ^7(^3"..currentVersionRaw.."^7 → ^3"..newestVersionRaw.."^7)")
|
||||||
|
for _, line in ipairs(changelog) do
|
||||||
|
print((line:find("http") and "^7" or "^5")..line)
|
||||||
|
end
|
||||||
|
print("^1----------------------------------------------------------------------^7")
|
||||||
|
SetTimeout(3600000, function()
|
||||||
|
CheckBridgeVersion()
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
print("^7'^3jim_bridge^7' - ^5You are running a newer version ^7(^3"..currentVersionRaw.."^7 ← ^3"..newestVersionRaw.."^7)")
|
||||||
|
end
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
CheckBridgeVersion()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
CheckBridgeVersion()
|
|
||||||
@@ -31,17 +31,18 @@ local Exports = {
|
|||||||
|
|
||||||
-- REDM
|
-- REDM
|
||||||
RSGExport = "rsg-core",
|
RSGExport = "rsg-core",
|
||||||
RSGInv = "rsg-inventory"
|
RSGInv = "rsg-inventory",
|
||||||
|
|
||||||
|
VorpExport = "vorp_core",
|
||||||
|
VorpInv = "vorp_inventory",
|
||||||
|
VorpMenu = "vorp_menu",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Prevent reloading if cache is already initialized
|
-- Prevent reloading if cache is already initialized
|
||||||
local cache = {
|
local cache = { Items = {}, Vehicles = {}, Jobs = {}, Gangs = {}, }
|
||||||
Items = {},
|
|
||||||
Vehicles = {},
|
|
||||||
Jobs = {},
|
|
||||||
Gangs = {},
|
|
||||||
}
|
|
||||||
local cacheReady = false
|
local cacheReady = false
|
||||||
|
|
||||||
|
-- Timer info, for debugging more then anything
|
||||||
local timers = {}
|
local timers = {}
|
||||||
local function startTimer(label)
|
local function startTimer(label)
|
||||||
timers[label] = GetGameTimer()
|
timers[label] = GetGameTimer()
|
||||||
@@ -52,26 +53,57 @@ local function endTimer(label)
|
|||||||
timers[label] = "("..(timers[label] / 1000).."s)"
|
timers[label] = "("..(timers[label] / 1000).."s)"
|
||||||
end
|
end
|
||||||
startTimer("Cache") startTimer("Items") startTimer("Vehicles") startTimer("Jobs") startTimer("InvWeight") startTimer("InvSlots")
|
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 functions --
|
||||||
local function checkExists(resourceName)
|
local function checkExists(resourceName)
|
||||||
return GetResourceState(resourceName):find("start") or GetResourceState(resourceName):find("stopped")
|
local state = GetResourceState(resourceName)
|
||||||
|
return state and (state:find("start") or state:find("stopped"))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ensure oxmysql resource is loaded
|
local function waitStarted(resourceName)
|
||||||
if checkExists(Exports.OXCoreExport) or checkExists(Exports.ESXExport) then
|
while GetResourceState(resourceName) ~= "started" do Wait(100) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function waitStartedOrStopped(resourceName)
|
||||||
|
local state = GetResourceState(resourceName)
|
||||||
|
while state ~= "started" and state ~= "stopped" do
|
||||||
|
Wait(100)
|
||||||
|
state = GetResourceState(resourceName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dupLowercaseWeapons(items)
|
||||||
|
for k, v in pairs(items or {}) do
|
||||||
|
if type(k) == "string" then
|
||||||
|
if k:find("WEAPON") then
|
||||||
|
items[k:lower()] = v
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Forceload libs --
|
||||||
|
|
||||||
|
-- Ensure oxmysql resource is loaded for jim_bridge internally
|
||||||
|
-- For some core's it needs to get data from the database
|
||||||
|
if checkExists(Exports.OXCoreExport) or checkExists(Exports.ESXExport) or checkExists(Exports.VorpExport) then
|
||||||
local fileLoader = assert(load(LoadResourceFile("oxmysql", ('lib/MySQL.lua')), ('@@oxmysql/lib/MySQL.lua')))
|
local fileLoader = assert(load(LoadResourceFile("oxmysql", ('lib/MySQL.lua')), ('@@oxmysql/lib/MySQL.lua')))
|
||||||
fileLoader()
|
fileLoader()
|
||||||
end
|
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
|
||||||
while GetResourceState(Exports.OXCoreExport) ~= "started" do Wait(100) end
|
waitStartedOrStopped(Exports.OXCoreExport)
|
||||||
local fileLoader = assert(load(LoadResourceFile(Exports.OXCoreExport, ('lib/init.lua')), ('@@'..Exports.OXCoreExport..'/lib/init.lua')))
|
local fileLoader = assert(load(LoadResourceFile(Exports.OXCoreExport, ('lib/init.lua')), ('@@'..Exports.OXCoreExport..'/lib/init.lua')))
|
||||||
fileLoader()
|
fileLoader()
|
||||||
end
|
end
|
||||||
if checkExists(Exports.ESXExport) then
|
if checkExists(Exports.ESXExport) then
|
||||||
-- Detected ESX in server, wait for it to be started if needed
|
-- Detected ESX in server, wait for it to be started if needed
|
||||||
while GetResourceState(Exports.ESXExport) ~= "started" do Wait(100) end
|
waitStarted(Exports.ESXExport)
|
||||||
local fileLoader = assert(load(LoadResourceFile(Exports.ESXExport, ('/imports.lua')), ('@@'..Exports.ESXExport..'/imports.lua')))
|
local fileLoader = assert(load(LoadResourceFile(Exports.ESXExport, ('/imports.lua')), ('@@'..Exports.ESXExport..'/imports.lua')))
|
||||||
fileLoader()
|
fileLoader()
|
||||||
end
|
end
|
||||||
@@ -89,225 +121,274 @@ end
|
|||||||
---------------------
|
---------------------
|
||||||
---- Load Items -----
|
---- Load Items -----
|
||||||
---------------------
|
---------------------
|
||||||
-- Items initialization based on detected inventory system
|
|
||||||
if checkExists(Exports.OXInv) then
|
|
||||||
-- Wait for OX Inventory to start if it's not already started
|
|
||||||
while GetResourceState(Exports.OXInv) ~= "started" do Wait(100) end
|
|
||||||
itemResource = Exports.OXInv
|
|
||||||
|
|
||||||
local success, result = pcall(function()
|
local itemFunc = {
|
||||||
return exports[Exports.OXInv]:Items()
|
{ script = Exports.OXInv,
|
||||||
end)
|
cacheItem = function()
|
||||||
if success and result then
|
local success, result = pcall(function()
|
||||||
cache.Items = result
|
return exports[Exports.OXInv]:Items()
|
||||||
end
|
end)
|
||||||
|
if success and result then
|
||||||
-- Get Weapon info and duplicate them if they are uppercase
|
cache.Items = result
|
||||||
-- (duplicate incase anything checks for the uppercase version)
|
|
||||||
for k, v in pairs(cache.Items) do
|
|
||||||
if type(k) == "string" then
|
|
||||||
if k:find("WEAPON") then
|
|
||||||
cache.Items[k:lower()] = cache.Items[k]
|
|
||||||
end
|
end
|
||||||
else
|
end,
|
||||||
print("^1ERROR^7: ^1Possible table inside a table, check your items.lua^7?")
|
},
|
||||||
print("^1Possible Issue found^7:")
|
{ script = Exports.TgiannInv,
|
||||||
print(json.encode(cache.Items[k], {indent = true}))
|
cacheItem = function()
|
||||||
end
|
local success, result = pcall(function()
|
||||||
end
|
return exports[Exports.TgiannInv]:Items()
|
||||||
|
end)
|
||||||
elseif checkExists(Exports.TgiannInv) then
|
if success and result then
|
||||||
-- Wait for OX Inventory to start if it's not already started
|
cache.Items = result
|
||||||
while GetResourceState(Exports.TgiannInv) ~= "started" do Wait(100) end
|
|
||||||
itemResource = Exports.TgiannInv
|
|
||||||
|
|
||||||
local success, result = pcall(function()
|
|
||||||
return exports[Exports.TgiannInv]:Items()
|
|
||||||
end)
|
|
||||||
if success and result then
|
|
||||||
cache.Items = result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get Weapon info and duplicate them if they are uppercase
|
|
||||||
-- (duplicate incase anything checks for the uppercase version)
|
|
||||||
for k, v in pairs(cache.Items) do
|
|
||||||
if type(k) == "string" then
|
|
||||||
if k:find("WEAPON") then
|
|
||||||
cache.Items[k:lower()] = cache.Items[k]
|
|
||||||
end
|
end
|
||||||
else
|
end,
|
||||||
print("^1ERROR^7: ^1Possible table inside a table, check your items.lua^7?")
|
},
|
||||||
print("^1Possible Issue found^7:")
|
{ script = Exports.QBXExport,
|
||||||
print(json.encode(cache.Items[k], {indent = true}))
|
cacheItem = function()
|
||||||
end
|
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
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.QBExport,
|
||||||
|
cacheItem = function()
|
||||||
|
cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.ESXExport,
|
||||||
|
cacheItem = function()
|
||||||
|
if GetResourceState(Exports.QSInv):find("start") then
|
||||||
|
cache.Items = exports[Exports.QSInv]:GetItemList()
|
||||||
|
else
|
||||||
|
cache.Items = ESX.GetItems()
|
||||||
|
while not next(cache.Items) do
|
||||||
|
cache.Items = ESX.GetItems()
|
||||||
|
Wait(1000)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.RSGExport,
|
||||||
|
cacheItem = function()
|
||||||
|
cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.VorpInv,
|
||||||
|
cacheItem = function()
|
||||||
|
local dbItems = MySQL.query.await('SELECT * FROM `items`')
|
||||||
|
local tempItems = {}
|
||||||
|
for i = 1, #dbItems do
|
||||||
|
local v = dbItems[i]
|
||||||
|
tempItems[v.item] = {
|
||||||
|
name = v.item,
|
||||||
|
label = v.label,
|
||||||
|
weight = v.weight,
|
||||||
|
info = v.metadata,
|
||||||
|
usable = v.usable,
|
||||||
|
type = v.type,
|
||||||
|
description = v.desc,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
cache.Items = tempItems
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, #itemFunc do
|
||||||
|
local data = itemFunc[i]
|
||||||
|
if checkExists(data.script) then
|
||||||
|
waitStarted(data.script) -- Wait for detected script to start fully
|
||||||
|
data.cacheItem() -- run tablized function for core/inv
|
||||||
|
dupLowercaseWeapons(cache.Items) -- make usable weapon item names for jim_bridge
|
||||||
|
itemResource = data.script -- Grab script name to announce later
|
||||||
|
endTimer("Items") -- end timer
|
||||||
|
break -- break loop so it doesn't keep checking
|
||||||
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
|
|
||||||
|
|
||||||
elseif checkExists(Exports.QBExport) then
|
|
||||||
while GetResourceState(Exports.QBExport) ~= "started" do Wait(100) end
|
|
||||||
itemResource = Exports.QBExport
|
|
||||||
cache.Items = exports[Exports.QBExport]:GetCoreObject().Shared.Items
|
|
||||||
|
|
||||||
elseif checkExists(Exports.ESXExport) then
|
|
||||||
itemResource = Exports.ESXExport
|
|
||||||
if GetResourceState(Exports.QSInv):find("start") then
|
|
||||||
cache.Items = exports[Exports.QSInv]:GetItemList()
|
|
||||||
else
|
|
||||||
cache.Items = ESX.GetItems()
|
|
||||||
while not next(cache.Items) do
|
|
||||||
cache.Items = ESX.GetItems()
|
|
||||||
Wait(1000)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif checkExists(Exports.RSGExport) then
|
|
||||||
while GetResourceState(Exports.RSGExport) ~= "started" do Wait(100) end
|
|
||||||
itemResource = Exports.RSGExport
|
|
||||||
cache.Items = exports[Exports.RSGExport]:GetCoreObject().Shared.Items
|
|
||||||
end
|
end
|
||||||
endTimer("Items")
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
--- Load Vehicles ---
|
--- Load Vehicles ---
|
||||||
---------------------
|
---------------------
|
||||||
-- Vehicle loading depending on framework
|
---
|
||||||
if checkExists(Exports.QBXExport) then
|
local vehicleFunc = {
|
||||||
vehResource = Exports.QBXExport
|
|
||||||
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
|
||||||
|
|
||||||
elseif checkExists(Exports.QBExport)then
|
{ script = Exports.QBXExport,
|
||||||
vehResource = Exports.QBExport
|
cacheVehicle = function()
|
||||||
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.QBExport,
|
||||||
|
cacheVehicle = function()
|
||||||
|
cache.Vehicles = exports[Exports.QBExport]:GetCoreObject().Shared.Vehicles
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.OXCoreExport,
|
||||||
|
cacheVehicle = function()
|
||||||
|
cache.Vehicles = {}
|
||||||
|
for k, v in pairs(Ox.GetVehicleData()) do
|
||||||
|
cache.Vehicles[k] = {
|
||||||
|
model = k, hash = joaat(k),
|
||||||
|
price = v.price,
|
||||||
|
name = v.name,
|
||||||
|
brand = v.make
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.ESXExport,
|
||||||
|
cacheVehicle = function()
|
||||||
|
while not MySQL do Wait(100) end
|
||||||
|
for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do
|
||||||
|
cache.Vehicles[v.model] = {
|
||||||
|
model = v.model,
|
||||||
|
hash = joaat(v.model),
|
||||||
|
price = v.price,
|
||||||
|
name = v.name,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.RSGExport,
|
||||||
|
cacheVehicle = function()
|
||||||
|
cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.VorpExport,
|
||||||
|
cacheVehicle = function()
|
||||||
|
cache.Vehicles = { ["unkown"] = {} }
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
elseif checkExists(Exports.OXCoreExport) then
|
for i = 1, #vehicleFunc do
|
||||||
vehResource = Exports.OXCoreExport
|
local data = vehicleFunc[i]
|
||||||
cache.Vehicles = {}
|
if checkExists(data.script) then
|
||||||
for k, v in pairs(Ox.GetVehicleData()) do
|
waitStarted(data.script) -- Wait for detected script to start fully
|
||||||
cache.Vehicles[k] = {
|
data.cacheVehicle() -- run tablized function for core
|
||||||
model = k, hash = joaat(k),
|
vehResource = data.script -- Grab script name to announce later
|
||||||
price = v.price,
|
endTimer("Vehicles") -- end timer
|
||||||
name = v.name,
|
break -- break loop so it doesn't keep checking
|
||||||
brand = v.make
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif checkExists(Exports.ESXExport) then
|
|
||||||
vehResource = Exports.ESXExport
|
|
||||||
while not MySQL do Wait(1000) end
|
|
||||||
for _, v in pairs(MySQL.query.await('SELECT model, price, name FROM vehicles')) do
|
|
||||||
cache.Vehicles[v.model] = {
|
|
||||||
model = v.model,
|
|
||||||
hash = joaat(v.model),
|
|
||||||
price = v.price,
|
|
||||||
name = v.name,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif checkExists(Exports.RSGExport) then
|
|
||||||
vehResource = Exports.RSGExport
|
|
||||||
cache.Vehicles = exports[Exports.RSGExport]:GetCoreObject().Shared.Vehicles
|
|
||||||
|
|
||||||
end
|
end
|
||||||
endTimer("Vehicles")
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
----- Load Jobs -----
|
----- Load Jobs -----
|
||||||
---------------------
|
---------------------
|
||||||
-- Jobs loading based on framework
|
|
||||||
if checkExists(Exports.QBXExport) then
|
|
||||||
jobResource = Exports.QBXExport
|
|
||||||
cache.Jobs, cache.Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
|
|
||||||
|
|
||||||
elseif checkExists(Exports.QBExport) then
|
local jobFunc = {
|
||||||
jobResource = Exports.QBExport
|
|
||||||
cache.Jobs, cache.Gangs = exports[Exports.QBExport]:GetCoreObject().Shared.Jobs, exports[Exports.QBExport]:GetCoreObject().Shared.Gangs
|
|
||||||
|
|
||||||
elseif checkExists(Exports.OXCoreExport) then
|
{ script = Exports.QBXExport,
|
||||||
jobResource = Exports.OXCoreExport
|
cacheJob = function()
|
||||||
while not MySQL do Wait(1000) end
|
cache.Jobs, cache.Gangs = exports[Exports.QBXExport]:GetJobs(), exports[Exports.QBXExport]:GetGangs()
|
||||||
local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`')
|
end,
|
||||||
local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`')
|
},
|
||||||
local gradeMap = {}
|
{ script = Exports.QBExport,
|
||||||
for _, grade in pairs(tempGrades) do
|
cacheJob = function()
|
||||||
gradeMap[grade.group] = gradeMap[grade.group] or {}
|
Core = exports[Exports.QBExport]:GetCoreObject()
|
||||||
gradeMap[grade.group][grade.grade] = { name = grade.label }
|
cache.Jobs, cache.Gangs = Core.Shared.Jobs, Core.Shared.Gangs
|
||||||
end
|
end,
|
||||||
for _, job in pairs(tempJobs) do
|
},
|
||||||
cache.Jobs[job.name] = {
|
{ script = Exports.OXCoreExport,
|
||||||
label = job.label,
|
cacheJob = function()
|
||||||
grades = gradeMap[job.name] or {}
|
while not MySQL do Wait(100) end
|
||||||
}
|
local tempJobs = MySQL.query.await('SELECT * FROM `ox_groups`')
|
||||||
end
|
local tempGrades = MySQL.query.await('SELECT * FROM `ox_group_grades`')
|
||||||
cache.Gangs = cache.Jobs
|
local gradeMap = {}
|
||||||
|
for _, grade in pairs(tempGrades) do
|
||||||
elseif checkExists(Exports.ESXExport) then
|
gradeMap[grade.group] = gradeMap[grade.group] or {}
|
||||||
jobResource = Exports.ESXExport
|
gradeMap[grade.group][grade.grade] = { name = grade.label }
|
||||||
ESX = exports[Exports.ESXExport]:getSharedObject()
|
|
||||||
cache.Jobs = ESX.GetJobs()
|
|
||||||
while not next(cache.Jobs) do
|
|
||||||
Wait(100)
|
|
||||||
cache.Jobs = ESX.GetJobs()
|
|
||||||
end
|
|
||||||
for Role, Grades in pairs(cache.Jobs) do
|
|
||||||
-- Check for if user has added grades
|
|
||||||
if Grades.grades == nil or not next(Grades.grades) then
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
for grade, info in pairs(Grades.grades) do
|
|
||||||
if info.label and info.label:find("[Bb]oss") then
|
|
||||||
cache.Jobs[Role].grades[grade].isBoss = true
|
|
||||||
goto continue
|
|
||||||
end
|
end
|
||||||
end
|
for _, job in pairs(tempJobs) do
|
||||||
local highestGrade = nil
|
cache.Jobs[job.name] = {
|
||||||
for k in pairs(Grades.grades) do
|
label = job.label,
|
||||||
local num = tonumber(k)
|
grades = gradeMap[job.name] or {}
|
||||||
if num and (not highestGrade or num > highestGrade) then
|
}
|
||||||
highestGrade = num
|
|
||||||
end
|
end
|
||||||
end
|
cache.Gangs = cache.Jobs
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.ESXExport,
|
||||||
|
cacheJob = function()
|
||||||
|
ESX = ESX or exports[Exports.ESXExport]:getSharedObject()
|
||||||
|
cache.Jobs = ESX.GetJobs()
|
||||||
|
while not next(cache.Jobs) do
|
||||||
|
Wait(100)
|
||||||
|
cache.Jobs = ESX.GetJobs()
|
||||||
|
end
|
||||||
|
for Role, Grades in pairs(cache.Jobs) do
|
||||||
|
-- Check for if user has added grades
|
||||||
|
if Grades.grades == nil or not next(Grades.grades) then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
for grade, info in pairs(Grades.grades) do
|
||||||
|
if info.label and info.label:find("[Bb]oss") then
|
||||||
|
cache.Jobs[Role].grades[grade].isBoss = true
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local highestGrade = nil
|
||||||
|
for k in pairs(Grades.grades) do
|
||||||
|
local num = tonumber(k)
|
||||||
|
if num and (not highestGrade or num > highestGrade) then
|
||||||
|
highestGrade = num
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if highestGrade then
|
if highestGrade then
|
||||||
cache.Jobs[Role].grades[tostring(highestGrade)].isBoss = true
|
cache.Jobs[Role].grades[tostring(highestGrade)].isBoss = true
|
||||||
end
|
end
|
||||||
::continue::
|
::continue::
|
||||||
|
end
|
||||||
|
cache.Gangs = cache.Jobs
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.RSGExport,
|
||||||
|
cacheJob = function()
|
||||||
|
Core = exports[Exports.RSGExport]:GetCoreObject()
|
||||||
|
cache.Jobs, cache.Gangs = Core.Shared.Jobs, Core.Shared.Gangs
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{ script = Exports.VorpExport,
|
||||||
|
cacheJob = function()
|
||||||
|
cache.Jobs = { ["unkown"] = {} }
|
||||||
|
cache.Gangs = cache.Jobs
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, #jobFunc do
|
||||||
|
local data = jobFunc[i]
|
||||||
|
if checkExists(data.script) then
|
||||||
|
waitStarted(data.script) -- Wait for detected script to start fully
|
||||||
|
data.cacheJob() -- run tablized function for core
|
||||||
|
jobResource = data.script -- Grab script name to announce later
|
||||||
|
endTimer("Jobs") -- end timer
|
||||||
|
break -- break loop so it doesn't keep checking
|
||||||
end
|
end
|
||||||
cache.Gangs = cache.Jobs
|
|
||||||
|
|
||||||
elseif checkExists(Exports.RSGExport) then
|
|
||||||
jobResource = Exports.RSGExport
|
|
||||||
cache.Jobs, cache.Gangs = exports[Exports.RSGExport]:GetCoreObject().Shared.Jobs, exports[Exports.RSGExport]:GetCoreObject().Shared.Gangs
|
|
||||||
|
|
||||||
end
|
end
|
||||||
endTimer("Jobs")
|
|
||||||
|
|
||||||
-- Fallback if nil or empty
|
-- Fallback if nil or empty
|
||||||
if cache.Items == nil or not next(cache.Items) then
|
if cache.Items == nil or not next(cache.Items) then
|
||||||
@@ -402,10 +483,8 @@ for script, data in pairs(invWeightTable) do
|
|||||||
if checkExists(script) then
|
if checkExists(script) then
|
||||||
if script == Exports.QBInv and GetResourceState(Exports.JPRInv):find("start") then goto skip end
|
if script == Exports.QBInv and GetResourceState(Exports.JPRInv):find("start") then goto skip end
|
||||||
|
|
||||||
while GetResourceState(script) ~= "started" and GetResourceState(script) ~= "stopped" do
|
waitStartedOrStopped(script)
|
||||||
Wait(100)
|
|
||||||
print("Waiting for script start")
|
|
||||||
end
|
|
||||||
local attempts = data.fallback or { data }
|
local attempts = data.fallback or { data }
|
||||||
local lookup, used, err
|
local lookup, used, err
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aw
|
|||||||
games { 'gta5', 'rdr3' }
|
games { 'gta5', 'rdr3' }
|
||||||
lua54 'yes'
|
lua54 'yes'
|
||||||
|
|
||||||
|
|
||||||
files {
|
files {
|
||||||
'starter.lua',
|
'starter.lua',
|
||||||
'shared/*.lua',
|
'shared/*.lua',
|
||||||
@@ -25,3 +24,5 @@ client_scripts {
|
|||||||
'clientFrameworkCache.lua',
|
'clientFrameworkCache.lua',
|
||||||
'ui_modules/*.lua',
|
'ui_modules/*.lua',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suppress_updates 'false' -- set to 'true' to disable update pings
|
||||||
@@ -12,7 +12,7 @@ if isServer() then
|
|||||||
local src = source
|
local src = source
|
||||||
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
|
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
|
||||||
--debugPrint(GetInvokingResource())
|
--debugPrint(GetInvokingResource())
|
||||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= Exports.QBExport and GetInvokingResource() ~= Exports.VorpExport 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
|
||||||
@@ -45,8 +45,9 @@ if isServer() then
|
|||||||
|
|
||||||
createCallback(getScript()..":callback:GetAuthEvent", function(source)
|
createCallback(getScript()..":callback:GetAuthEvent", function(source)
|
||||||
local src = source
|
local src = source
|
||||||
|
--debugPrint(GetInvokingResource())
|
||||||
|
|
||||||
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= "qb-core" then
|
if GetInvokingResource() and GetInvokingResource() ~= getScript() and GetInvokingResource() ~= Exports.QBExport and GetInvokingResource() ~= Exports.VorpExport then
|
||||||
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital callback was called from an external resource^7")
|
debugPrint("^1Error^7: ^1Possible exploit^7, ^1vital callback was called from an external resource^7")
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ function onPlayerLoaded(func, onStart)
|
|||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
onPlayerFramework = RSGExport
|
onPlayerFramework = RSGExport
|
||||||
RegisterNetEvent('RSGCore:Client:OnPlayerLoaded', tempFunc)
|
RegisterNetEvent('RSGCore:Client:OnPlayerLoaded', tempFunc)
|
||||||
|
elseif isStarted(VorpExport) then
|
||||||
|
onPlayerFramework = VorpExport
|
||||||
|
RegisterNetEvent("vorp_core:Client:OnPlayerSpawned", tempFunc)
|
||||||
end
|
end
|
||||||
|
|
||||||
if onPlayerFramework ~= "" then
|
if onPlayerFramework ~= "" then
|
||||||
@@ -174,6 +177,12 @@ function waitForLogin()
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elseif isStarted(VorpExport) then
|
||||||
|
-- For other frameworks, use LocalPlayer.state.isLoggedIn.
|
||||||
|
while not LocalPlayer.state.IsInSession and (GetGameTimer() - startTime) < timeout do
|
||||||
|
Wait(100)
|
||||||
|
end
|
||||||
|
loggedIn = LocalPlayer.state.IsInSession
|
||||||
else
|
else
|
||||||
-- For other frameworks, use LocalPlayer.state.isLoggedIn.
|
-- For other frameworks, use LocalPlayer.state.isLoggedIn.
|
||||||
while not LocalPlayer.state.isLoggedIn and (GetGameTimer() - startTime) < timeout do
|
while not LocalPlayer.state.isLoggedIn and (GetGameTimer() - startTime) < timeout do
|
||||||
|
|||||||
@@ -1,100 +1,111 @@
|
|||||||
function parseVersion(version)
|
local function readBoolMeta(key, default)
|
||||||
local parts = {}
|
local val = GetResourceMetadata("jim_bridge", key, 0)
|
||||||
for num in version:gmatch("%d+") do
|
if not val then return default end
|
||||||
table.insert(parts, tonumber(num))
|
val = tostring(val):lower()
|
||||||
|
return (val == 'true' or val == '1' or val == 'yes' or val == 'on')
|
||||||
|
end
|
||||||
|
|
||||||
|
local SUPPRESS_UPDATES = readBoolMeta('suppress_updates', false)
|
||||||
|
|
||||||
|
if not SUPPRESS_UPDATES then
|
||||||
|
function parseVersion(version)
|
||||||
|
local parts = {}
|
||||||
|
for num in version:gmatch("%d+") do
|
||||||
|
table.insert(parts, tonumber(num))
|
||||||
|
end
|
||||||
|
return parts
|
||||||
end
|
end
|
||||||
return parts
|
|
||||||
end
|
|
||||||
|
|
||||||
function compareVersions(current, newest)
|
function compareVersions(current, newest)
|
||||||
local currentParts = parseVersion(current)
|
local currentParts = parseVersion(current)
|
||||||
local newestParts = parseVersion(newest)
|
local newestParts = parseVersion(newest)
|
||||||
for i = 1, math.max(#currentParts, #newestParts) do
|
for i = 1, math.max(#currentParts, #newestParts) do
|
||||||
local c = currentParts[i] or 0
|
local c = currentParts[i] or 0
|
||||||
local n = newestParts[i] or 0
|
local n = newestParts[i] or 0
|
||||||
if c < n then return -1
|
if c < n then return -1
|
||||||
elseif c > n then return 1 end
|
elseif c > n then return 1 end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
end
|
end
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function capitalize(str)
|
function capitalize(str)
|
||||||
return (str:gsub("^%l", string.upper):gsub("%-%l", function(letter) return "-" .. string.upper(letter:sub(2)) end))
|
return (str:gsub("^%l", string.upper):gsub("%-%l", function(letter) return "-" .. string.upper(letter:sub(2)) end))
|
||||||
end
|
end
|
||||||
|
|
||||||
local scriptName = ("^2"..capitalize(getScript()):gsub("%-", "^7-^2"):gsub("%_", "^7_^2")) or ""
|
local scriptName = ("^2"..capitalize(getScript()):gsub("%-", "^7-^2"):gsub("%_", "^7_^2")) or ""
|
||||||
local scriptVersion = ("^5"..GetResourceMetadata(getScript(), 'version', nil):gsub("%.", "^7.^5")) or ""
|
local scriptVersion = ("^5"..GetResourceMetadata(getScript(), 'version', nil):gsub("%.", "^7.^5")) or ""
|
||||||
local scriptDescription = ("^2"..GetResourceMetadata(getScript(), 'description', nil)) or ""
|
local scriptDescription = ("^2"..GetResourceMetadata(getScript(), 'description', nil)) or ""
|
||||||
local scriptAuthor = ("^2by ^4"..GetResourceMetadata(getScript(), 'author', nil):gsub("%and", "^7and^4")) or ""
|
local scriptAuthor = ("^2by ^4"..GetResourceMetadata(getScript(), 'author', nil):gsub("%and", "^7and^4")) or ""
|
||||||
|
|
||||||
print(scriptName.." ^7v"..scriptVersion.."^7 - "..scriptDescription.." "..scriptAuthor.."^7")
|
print(scriptName.." ^7v"..scriptVersion.."^7 - "..scriptDescription.." "..scriptAuthor.."^7")
|
||||||
|
|
||||||
function CheckVersion()
|
function CheckVersion()
|
||||||
if isServer() and GetResourceMetadata(getScript(), 'author', nil) == "Jimathy" then
|
if isServer() and GetResourceMetadata(getScript(), 'author', nil) == "Jimathy" then
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
Wait(4000)
|
Wait(4000)
|
||||||
local script = getScript()
|
local script = getScript()
|
||||||
local currentVersionRaw = GetResourceMetadata(script, 'version')
|
local currentVersionRaw = GetResourceMetadata(script, 'version')
|
||||||
|
|
||||||
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/'..script..'.txt', function(err, newestVersionRaw, headers)
|
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/'..script..'.txt', function(err, newestVersionRaw, headers)
|
||||||
if not newestVersionRaw then
|
if not newestVersionRaw then
|
||||||
-- fallback
|
-- fallback
|
||||||
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/'..script..'/master/version.txt', function(err, fallbackVersionRaw, headers)
|
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/'..script..'/master/version.txt', function(err, fallbackVersionRaw, headers)
|
||||||
if not fallbackVersionRaw then
|
if not fallbackVersionRaw then
|
||||||
print("^1Currently unable to run a version check for ^7'^3"..script.."^7' (^3"..currentVersionRaw.."^7)")
|
print("^1Currently unable to run a version check for ^7'^3"..script.."^7' (^3"..currentVersionRaw.."^7)")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
for line in fallbackVersionRaw:gmatch("[^\r\n]+") do table.insert(lines, line) end
|
||||||
|
local fallbackVersion = (lines[1] or "0.0.0"):gsub("v", "")
|
||||||
|
local changelog = {}
|
||||||
|
for i = 2, #lines do table.insert(changelog, lines[i]) end
|
||||||
|
|
||||||
|
local compareResult = compareVersions(currentVersionRaw, fallbackVersion)
|
||||||
|
if compareResult == 0 then
|
||||||
|
print("^7'^3"..script.."^7' - ^2You are running the latest version^7. (^3"..currentVersionRaw.."^7)")
|
||||||
|
elseif compareResult < 0 then
|
||||||
|
print("^1----------------------------------------------------------------------^7")
|
||||||
|
print("^7'^3"..script.."^7' - ^1You are currently running an outdated version^7! (^3"..currentVersionRaw.."^7 → ^3"..fallbackVersion.."^7)")
|
||||||
|
if #changelog > 0 then
|
||||||
|
for _, line in ipairs(changelog) do
|
||||||
|
print((line:find("http") and "^7" or "^5")..line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print("^1----------------------------------------------------------------------^7")
|
||||||
|
SetTimeout(1200000, function() CheckVersion() end)
|
||||||
|
else
|
||||||
|
print("^7'^3"..script.."^7' - ^5You are running a newer version ^7(^3"..currentVersionRaw.."^7 ← ^3"..fallbackVersion.."^7)")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for line in fallbackVersionRaw:gmatch("[^\r\n]+") do table.insert(lines, line) end
|
for line in newestVersionRaw:gmatch("[^\r\n]+") do table.insert(lines, line) end
|
||||||
local fallbackVersion = (lines[1] or "0.0.0"):gsub("v", "")
|
local newestVersion = (lines[1] or "0.0.0"):gsub("v", "")
|
||||||
local changelog = {}
|
local changelog = {}
|
||||||
for i = 2, #lines do table.insert(changelog, lines[i]) end
|
for i = 2, #lines do table.insert(changelog, lines[i]) end
|
||||||
|
|
||||||
local compareResult = compareVersions(currentVersionRaw, fallbackVersion)
|
local compareResult = compareVersions(currentVersionRaw, newestVersion)
|
||||||
if compareResult == 0 then
|
if compareResult == 0 then
|
||||||
print("^7'^3"..script.."^7' - ^2You are running the latest version^7. (^3"..currentVersionRaw.."^7)")
|
print("^7'^3"..script.."^7' - ^2You are running the latest version^7. (^3"..currentVersionRaw.."^7)")
|
||||||
elseif compareResult < 0 then
|
elseif compareResult < 0 then
|
||||||
print("^1----------------------------------------------------------------------^7")
|
print("^1----------------------------------------------------------------------^7")
|
||||||
print("^7'^3"..script.."^7' - ^1You are currently running an outdated version^7! (^3"..currentVersionRaw.."^7 → ^3"..fallbackVersion.."^7)")
|
print("^7'^3"..script.."^7' - ^1You are currently running an outdated version^7! (^3"..currentVersionRaw.."^7 → ^3"..newestVersion.."^7)")
|
||||||
if #changelog > 0 then
|
if #changelog > 0 then
|
||||||
for _, line in ipairs(changelog) do
|
for _, line in ipairs(changelog) do
|
||||||
print((line:find("http") and "^7" or "^5")..line)
|
print((line:find("http") and "^7" or "^5")..line)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print("^1----------------------------------------------------------------------^7")
|
print("^1----------------------------------------------------------------------^7")
|
||||||
SetTimeout(1200000, function() CheckVersion() end)
|
SetTimeout(3600000, function() CheckVersion() end)
|
||||||
else
|
else
|
||||||
print("^7'^3"..script.."^7' - ^5You are running a newer version ^7(^3"..currentVersionRaw.."^7 ← ^3"..fallbackVersion.."^7)")
|
print("^7'^3"..script.."^7' - ^5You are running a newer version ^7(^3"..currentVersionRaw.."^7 ← ^3"..newestVersion.."^7)")
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
else
|
|
||||||
local lines = {}
|
|
||||||
for line in newestVersionRaw:gmatch("[^\r\n]+") do table.insert(lines, line) end
|
|
||||||
local newestVersion = (lines[1] or "0.0.0"):gsub("v", "")
|
|
||||||
local changelog = {}
|
|
||||||
for i = 2, #lines do table.insert(changelog, lines[i]) end
|
|
||||||
|
|
||||||
local compareResult = compareVersions(currentVersionRaw, newestVersion)
|
|
||||||
if compareResult == 0 then
|
|
||||||
print("^7'^3"..script.."^7' - ^2You are running the latest version^7. (^3"..currentVersionRaw.."^7)")
|
|
||||||
elseif compareResult < 0 then
|
|
||||||
print("^1----------------------------------------------------------------------^7")
|
|
||||||
print("^7'^3"..script.."^7' - ^1You are currently running an outdated version^7! (^3"..currentVersionRaw.."^7 → ^3"..newestVersion.."^7)")
|
|
||||||
if #changelog > 0 then
|
|
||||||
for _, line in ipairs(changelog) do
|
|
||||||
print((line:find("http") and "^7" or "^5")..line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print("^1----------------------------------------------------------------------^7")
|
|
||||||
SetTimeout(3600000, function() CheckVersion() end)
|
|
||||||
else
|
|
||||||
print("^7'^3"..script.."^7' - ^5You are running a newer version ^7(^3"..currentVersionRaw.."^7 ← ^3"..newestVersion.."^7)")
|
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
CheckVersion()
|
CheckVersion()
|
||||||
|
end
|
||||||
@@ -37,6 +37,10 @@ function createCallback(callbackName, funct)
|
|||||||
debugPrint("^6Bridge^7: ^2Registering ^4"..QBExport.." ^3Callback^7:", callbackName)
|
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(VorpExport) then
|
||||||
|
debugPrint("^6Bridge^7: ^2Registering ^4"..VorpExport.." ^3Callback^7:", callbackName)
|
||||||
|
Core = Core or exports.vorp_core:GetCore()
|
||||||
|
Core.Callback.Register(callbackName, adaptedFunction)
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
debugPrint("^6Bridge^7: ^2Registering ^4"..ESXExport.." ^3Callback^7:", callbackName)
|
debugPrint("^6Bridge^7: ^2Registering ^4"..ESXExport.." ^3Callback^7:", callbackName)
|
||||||
ESX.RegisterServerCallback(callbackName, adaptedFunction)
|
ESX.RegisterServerCallback(callbackName, adaptedFunction)
|
||||||
@@ -76,6 +80,13 @@ function triggerCallback(callbackName, ...)
|
|||||||
end, ...)
|
end, ...)
|
||||||
result = Citizen.Await(p)
|
result = Citizen.Await(p)
|
||||||
Wait(10)
|
Wait(10)
|
||||||
|
elseif isStarted(VorpExport) then
|
||||||
|
local p = promise.new()
|
||||||
|
Core.Callback.TriggerAwait(callbackName, function(cbResult)
|
||||||
|
p:resolve(cbResult)
|
||||||
|
end, ...)
|
||||||
|
result = Citizen.Await(p)
|
||||||
|
Wait(10)
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
local p = promise.new()
|
local p = promise.new()
|
||||||
ESX.TriggerServerCallback(callbackName, function(cbResult)
|
ESX.TriggerServerCallback(callbackName, function(cbResult)
|
||||||
|
|||||||
@@ -10,6 +10,263 @@
|
|||||||
• esx (using ESX.UI.Menu)
|
• esx (using ESX.UI.Menu)
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
local contextFunc = {
|
||||||
|
["ox"] =
|
||||||
|
function(Menu, data)
|
||||||
|
local index = nil
|
||||||
|
if data.onBack and not data.onSelected then
|
||||||
|
table.insert(Menu, 1, {
|
||||||
|
icon = "fas fa-circle-arrow-left",
|
||||||
|
title = "Return",
|
||||||
|
onSelect = data.onBack,
|
||||||
|
label = "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
|
||||||
|
local menuID = 'Menu'
|
||||||
|
(data.onSelected and lib.registerMenu or lib.registerContext)({
|
||||||
|
id = menuID,
|
||||||
|
title = data.header..br..br..(data.headertxt and data.headertxt or ""),
|
||||||
|
position = 'top-right',
|
||||||
|
options = Menu,
|
||||||
|
canClose = data.canClose and data.canClose or nil,
|
||||||
|
onClose = (data.onBack and data.onBack) or (data.onExit and data.onExit) or nil,
|
||||||
|
onExit = data.onExit and data.onExit or nil,
|
||||||
|
onSelected = data.onSelected and (function(selected) index = selected end) or nil,
|
||||||
|
}, data.onSelected and (function(x, y, args)
|
||||||
|
if Menu[x].refresh then
|
||||||
|
if Menu[x].onSelect then
|
||||||
|
Menu[x].onSelect()
|
||||||
|
end
|
||||||
|
lib.showMenu(menuID, index)
|
||||||
|
else
|
||||||
|
if Menu[x].onSelect then
|
||||||
|
Menu[x].onSelect()
|
||||||
|
else
|
||||||
|
lib.showMenu(menuID, index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end) or nil)
|
||||||
|
if data.onSelected then
|
||||||
|
lib.showMenu(menuID, 1)
|
||||||
|
else
|
||||||
|
lib.showContext(menuID)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
["qb"] =
|
||||||
|
function(Menu, data)
|
||||||
|
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
|
||||||
|
exports[QBMenuExport]:openMenu(Menu)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["gta"] =
|
||||||
|
function(Menu, data)
|
||||||
|
WarMenu.CreateMenu(tostring(Menu), data.header, " ", {
|
||||||
|
titleColor = { 222, 255, 255 },
|
||||||
|
maxOptionCountOnScreen = 15,
|
||||||
|
width = 0.25,
|
||||||
|
x = 0.7,
|
||||||
|
})
|
||||||
|
if WarMenu.IsAnyMenuOpened() then return end
|
||||||
|
WarMenu.OpenMenu(tostring(Menu))
|
||||||
|
CreateThread(function()
|
||||||
|
local close = true
|
||||||
|
while true do
|
||||||
|
if WarMenu.Begin(tostring(Menu)) then
|
||||||
|
if data.onBack then
|
||||||
|
if WarMenu.SpriteButton("Return", 'commonmenu', "arrowleft", 127, 127, 127) then
|
||||||
|
WarMenu.CloseMenu()
|
||||||
|
Wait(10)
|
||||||
|
data.onBack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k in pairs(Menu) do
|
||||||
|
local pressed = WarMenu.Button(Menu[k].header)
|
||||||
|
if not Menu[k].header then
|
||||||
|
Menu[k].header = Menu[k].txt
|
||||||
|
Menu[k].txt = nil
|
||||||
|
end
|
||||||
|
if Menu[k].txt and Menu[k].txt ~= "" and WarMenu.IsItemHovered() then
|
||||||
|
if Menu[k].disabled or Menu[k].isMenuHeader then
|
||||||
|
WarMenu.ToolTip("~r~"..Menu[k].txt, 0.18, true)
|
||||||
|
else
|
||||||
|
WarMenu.ToolTip(
|
||||||
|
(Menu[k].blip and "~BLIP_".."8".."~ " or "")..
|
||||||
|
Menu[k].txt:gsub("%:", ":~g~"):gsub("%\n", "\n~s~"), 0.18,
|
||||||
|
true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if pressed and not Menu[k].isMenuHeader then
|
||||||
|
WarMenu.CloseMenu()
|
||||||
|
close = false
|
||||||
|
Wait(10)
|
||||||
|
Menu[k].onSelect()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
WarMenu.End()
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not WarMenu.IsAnyMenuOpened() and close then
|
||||||
|
stopTempCam(cam)
|
||||||
|
if data.onExit then data.onExit() end
|
||||||
|
end
|
||||||
|
Wait(0)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["esx"] =
|
||||||
|
function(Menu, data)
|
||||||
|
for k in pairs(Menu) do
|
||||||
|
Menu[k].label = Menu[k].header
|
||||||
|
Menu[k].name = "button"..k
|
||||||
|
end
|
||||||
|
if data.canClose then
|
||||||
|
table.insert(Menu, 1, {
|
||||||
|
icon = "fas fa-circle-xmark",
|
||||||
|
label = "Close",
|
||||||
|
name = "close",
|
||||||
|
onSelect = data.onExit,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
if data.onBack then
|
||||||
|
table.insert(Menu, 1, {
|
||||||
|
icon = "fas fa-circle-arrow-left",
|
||||||
|
label = "Return",
|
||||||
|
name = "return",
|
||||||
|
onSelect = data.onBack,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
ESX.UI.Menu.Open("default", getScript(), "Example_Menu", {
|
||||||
|
title = data.header,
|
||||||
|
align = 'top-right',
|
||||||
|
elements = Menu,
|
||||||
|
},
|
||||||
|
function(menuData, menu)
|
||||||
|
for k in pairs(Menu) do
|
||||||
|
if menuData.current.name == Menu[k].name then
|
||||||
|
menu.close()
|
||||||
|
Wait(10)
|
||||||
|
Menu[k].onSelect()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
function(data, menu)
|
||||||
|
menu.close()
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["lation"] =
|
||||||
|
function(Menu, data)
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Opens a menu using the configured menu system.
|
--- Opens a menu using the configured menu system.
|
||||||
---
|
---
|
||||||
--- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`.
|
--- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`.
|
||||||
@@ -47,255 +304,22 @@
|
|||||||
--- })
|
--- })
|
||||||
--- ```
|
--- ```
|
||||||
function openMenu(Menu, data)
|
function openMenu(Menu, data)
|
||||||
if Config.System.Menu == "ox" then
|
contextFunc[Config.System.Menu](Menu, data)
|
||||||
local index = nil
|
|
||||||
if data.onBack and not data.onSelected then
|
|
||||||
table.insert(Menu, 1, {
|
|
||||||
icon = "fas fa-circle-arrow-left",
|
|
||||||
title = "Return",
|
|
||||||
onSelect = data.onBack,
|
|
||||||
label = "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
|
|
||||||
local menuID = 'Menu'
|
|
||||||
(data.onSelected and lib.registerMenu or lib.registerContext)({
|
|
||||||
id = menuID,
|
|
||||||
title = data.header..br..br..(data.headertxt and data.headertxt or ""),
|
|
||||||
position = 'top-right',
|
|
||||||
options = Menu,
|
|
||||||
canClose = data.canClose and data.canClose or nil,
|
|
||||||
onClose = (data.onBack and data.onBack) or (data.onExit and data.onExit) or nil,
|
|
||||||
onExit = data.onExit and data.onExit or nil,
|
|
||||||
onSelected = data.onSelected and (function(selected) index = selected end) or nil,
|
|
||||||
}, data.onSelected and (function(x, y, args)
|
|
||||||
if Menu[x].refresh then
|
|
||||||
if Menu[x].onSelect then
|
|
||||||
Menu[x].onSelect()
|
|
||||||
end
|
|
||||||
lib.showMenu(menuID, index)
|
|
||||||
else
|
|
||||||
if Menu[x].onSelect then
|
|
||||||
Menu[x].onSelect()
|
|
||||||
else
|
|
||||||
lib.showMenu(menuID, index)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end) or nil)
|
|
||||||
if data.onSelected then
|
|
||||||
lib.showMenu(menuID, 1)
|
|
||||||
else
|
|
||||||
lib.showContext(menuID)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif Config.System.Menu == "qb" 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
|
|
||||||
exports[QBMenuExport]:openMenu(Menu)
|
|
||||||
|
|
||||||
elseif Config.System.Menu == "gta" then
|
|
||||||
WarMenu.CreateMenu(tostring(Menu), data.header, " ", {
|
|
||||||
titleColor = { 222, 255, 255 },
|
|
||||||
maxOptionCountOnScreen = 15,
|
|
||||||
width = 0.25,
|
|
||||||
x = 0.7,
|
|
||||||
})
|
|
||||||
if WarMenu.IsAnyMenuOpened() then return end
|
|
||||||
WarMenu.OpenMenu(tostring(Menu))
|
|
||||||
CreateThread(function()
|
|
||||||
local close = true
|
|
||||||
while true do
|
|
||||||
if WarMenu.Begin(tostring(Menu)) then
|
|
||||||
if data.onBack then
|
|
||||||
if WarMenu.SpriteButton("Return", 'commonmenu', "arrowleft", 127, 127, 127) then
|
|
||||||
WarMenu.CloseMenu()
|
|
||||||
Wait(10)
|
|
||||||
data.onBack()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for k in pairs(Menu) do
|
|
||||||
local pressed = WarMenu.Button(Menu[k].header)
|
|
||||||
if not Menu[k].header then
|
|
||||||
Menu[k].header = Menu[k].txt
|
|
||||||
Menu[k].txt = nil
|
|
||||||
end
|
|
||||||
if Menu[k].txt and Menu[k].txt ~= "" and WarMenu.IsItemHovered() then
|
|
||||||
if Menu[k].disabled or Menu[k].isMenuHeader then
|
|
||||||
WarMenu.ToolTip("~r~"..Menu[k].txt, 0.18, true)
|
|
||||||
else
|
|
||||||
WarMenu.ToolTip(
|
|
||||||
(Menu[k].blip and "~BLIP_".."8".."~ " or "")..
|
|
||||||
Menu[k].txt:gsub("%:", ":~g~"):gsub("%\n", "\n~s~"), 0.18,
|
|
||||||
true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if pressed and not Menu[k].isMenuHeader then
|
|
||||||
WarMenu.CloseMenu()
|
|
||||||
close = false
|
|
||||||
Wait(10)
|
|
||||||
Menu[k].onSelect()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
WarMenu.End()
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not WarMenu.IsAnyMenuOpened() and close then
|
|
||||||
stopTempCam(cam)
|
|
||||||
if data.onExit then data.onExit() end
|
|
||||||
end
|
|
||||||
Wait(0)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
elseif Config.System.Menu == "esx" then
|
|
||||||
for k in pairs(Menu) do
|
|
||||||
Menu[k].label = Menu[k].header
|
|
||||||
Menu[k].name = "button"..k
|
|
||||||
end
|
|
||||||
if data.canClose then
|
|
||||||
table.insert(Menu, 1, {
|
|
||||||
icon = "fas fa-circle-xmark",
|
|
||||||
label = "Close",
|
|
||||||
name = "close",
|
|
||||||
onSelect = data.onExit,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
if data.onBack then
|
|
||||||
table.insert(Menu, 1, {
|
|
||||||
icon = "fas fa-circle-arrow-left",
|
|
||||||
label = "Return",
|
|
||||||
name = "return",
|
|
||||||
onSelect = data.onBack,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
ESX.UI.Menu.Open("default", getScript(), "Example_Menu", {
|
|
||||||
title = data.header,
|
|
||||||
align = 'top-right',
|
|
||||||
elements = Menu,
|
|
||||||
},
|
|
||||||
function(menuData, menu)
|
|
||||||
for k in pairs(Menu) do
|
|
||||||
if menuData.current.name == Menu[k].name then
|
|
||||||
menu.close()
|
|
||||||
Wait(10)
|
|
||||||
Menu[k].onSelect()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
function(data, menu)
|
|
||||||
menu.close()
|
|
||||||
end)
|
|
||||||
|
|
||||||
elseif Config.System.Menu == "lation" then
|
|
||||||
if data.onBack then
|
|
||||||
table.insert(Menu, 1, {
|
|
||||||
icon = "fas fa-circle-arrow-left",
|
|
||||||
onSelect = data.onBack,
|
|
||||||
header = "Return",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
for k in pairs(Menu) do
|
|
||||||
if data.onSelected and Menu[k].arrow then
|
|
||||||
Menu[k].icon = "fas fa-angle-right"
|
|
||||||
end
|
|
||||||
-- If no title, use header or txt as title/label.
|
|
||||||
if not Menu[k].title then
|
|
||||||
if Menu[k].header ~= nil and Menu[k].header ~= "" then
|
|
||||||
Menu[k].title = Menu[k].header
|
|
||||||
Menu[k].label = Menu[k].header
|
|
||||||
if Menu[k].txt then Menu[k].description = Menu[k].txt else Menu[k].description = "" end
|
|
||||||
else
|
|
||||||
Menu[k].title = Menu[k].txt
|
|
||||||
Menu[k].label = Menu[k].txt
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Copy parameters from 'params' if available.
|
|
||||||
if Menu[k].params then
|
|
||||||
Menu[k].event = Menu[k].params.event
|
|
||||||
Menu[k].args = Menu[k].params.args or {}
|
|
||||||
end
|
|
||||||
if Menu[k].isMenuHeader then
|
|
||||||
Menu[k].readOnly = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
exports.lation_ui:registerMenu({
|
|
||||||
id = 'menu',
|
|
||||||
title = data.header,
|
|
||||||
onExit = data.onExit and data.onExit or nil,
|
|
||||||
subtitle = (data.headertxt and data.headertxt or ""),
|
|
||||||
options = Menu,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Show menu
|
|
||||||
exports.lation_ui:showMenu('menu')
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Line break conversion table, basically, to automate the linebreak in functions `br`
|
||||||
|
-- This is declared and automatically sets it for what menu you are using (depends if one is uding MD formatting or HTML formatting)
|
||||||
|
local lineBreakConversion = {
|
||||||
|
["ox"] = "\n",
|
||||||
|
["gta"] = "\n",
|
||||||
|
["lation"] = "\n",
|
||||||
|
|
||||||
|
["qb"] = "<br>",
|
||||||
|
["esx"] = "<br>",
|
||||||
|
}
|
||||||
|
|
||||||
--- 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" or Config.System.Menu == "lation") and "\n" or "<br>"
|
br = lineBreakConversion[Config.System.Menu]
|
||||||
|
|
||||||
--- 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.
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv, TgiannInv, JPRInv =
|
|||||||
Exports.JPRInv or ""
|
Exports.JPRInv or ""
|
||||||
|
|
||||||
RSGExport, RSGInv = Exports.RSGExport or "", Exports.RSGInv or ""
|
RSGExport, RSGInv = Exports.RSGExport or "", Exports.RSGInv or ""
|
||||||
|
VorpExport, VorpInv = Exports.VorpExport or "", Exports.VorpInv or ""
|
||||||
|
|
||||||
|
|
||||||
QBMenuExport = Exports.QBMenuExport or ""
|
QBMenuExport = Exports.QBMenuExport or ""
|
||||||
QBTargetExport, OXTargetExport = Exports.QBTargetExport or "", Exports.OXTargetExport or ""
|
QBTargetExport, OXTargetExport = Exports.QBTargetExport or "", Exports.OXTargetExport or ""
|
||||||
|
|
||||||
@@ -29,6 +32,8 @@ if isStarted(QBXExport) or isStarted(QBExport) then
|
|||||||
Core = Core or exports[QBExport]:GetCoreObject()
|
Core = Core or exports[QBExport]:GetCoreObject()
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
Core = Core or exports[RSGExport]:GetCoreObject()
|
Core = Core or exports[RSGExport]:GetCoreObject()
|
||||||
|
elseif isStarted(VorpExport) then
|
||||||
|
Core = Core or exports[VorpExport]:GetCore()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -115,12 +115,12 @@ function craftingMenu(data)
|
|||||||
local metaTable = {}
|
local metaTable = {}
|
||||||
-- Build ingredient details.
|
-- Build ingredient details.
|
||||||
for l, b in pairs(Recipes[i][tostring(k)]) do
|
for l, b in pairs(Recipes[i][tostring(k)]) do
|
||||||
local label = Items[l] and Items[l].label or "error - "..l
|
local label = getItemLabel(l)
|
||||||
local hasItem = checkStashItem(data.stashName, { [l] = b })
|
local hasItem = checkStashItem(data.stashName, { [l] = b })
|
||||||
local missingMark = not hasItem and " ❌" or " "
|
local missingMark = not hasItem and " ❌" or " "
|
||||||
settext = settext..(settext ~= "" and br or "").."[ x"..b.." ] - "..label..missingMark
|
settext = settext..(settext ~= "" and br or "").."[ x"..b.." ] - "..label..missingMark
|
||||||
|
|
||||||
metaTable[Items[l] and Items[l].label or "error - "..l] = b
|
metaTable[label] = b
|
||||||
itemTable[l] = b
|
itemTable[l] = b
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ function craftingMenu(data)
|
|||||||
Wait(10)
|
Wait(10)
|
||||||
end
|
end
|
||||||
disable = not checkStashItem(data.stashName, itemTable)
|
disable = not checkStashItem(data.stashName, itemTable)
|
||||||
setheader = ((metadata and metadata.label) or (Items[tostring(k)] and Items[tostring(k)].label) or "error - "..tostring(k))
|
setheader = ((metadata and metadata.label) or getItemLabel(k))
|
||||||
..(Recipes[i]["amount"] > 1 and " x"..Recipes[i]["amount"] or "")
|
..(Recipes[i]["amount"] > 1 and " x"..Recipes[i]["amount"] or "")
|
||||||
|
|
||||||
local statusEmoji = disable and " " or not canCarryTable[k] and " 📦" or " ✔️"
|
local statusEmoji = disable and " " or not canCarryTable[k] and " 📦" or " ✔️"
|
||||||
@@ -359,7 +359,7 @@ function makeItem(data)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
if crafting and progressBar({
|
if crafting and progressBar({
|
||||||
label = "Using "..b.." "..Items[l].label,
|
label = "Using "..b.." "..getItemLabel(l),
|
||||||
time = 1000,
|
time = 1000,
|
||||||
cancel = true,
|
cancel = true,
|
||||||
dict = 'pickup_object',
|
dict = 'pickup_object',
|
||||||
@@ -398,7 +398,7 @@ function makeItem(data)
|
|||||||
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0)
|
PlaySoundFromEntity(s.soundId, s.audioName, PlayerPedId(), s.audioRef, true, 0)
|
||||||
end
|
end
|
||||||
if crafting and progressBar({
|
if crafting and progressBar({
|
||||||
label = bartext..((metadata and metadata.label) or Items[data.item].label).." x"..craftAmount,
|
label = bartext..((metadata and metadata.label) or getItemLabel(data.item)).." x"..craftAmount,
|
||||||
time = totalBartime,
|
time = totalBartime,
|
||||||
cancel = true,
|
cancel = true,
|
||||||
dict = animDict,
|
dict = animDict,
|
||||||
@@ -437,7 +437,7 @@ function makeItem(data)
|
|||||||
-- Run the original loop for multiple progress bars
|
-- Run the original loop for multiple progress bars
|
||||||
for i = 1, craftAmount do
|
for i = 1, craftAmount do
|
||||||
if crafting and progressBar({
|
if crafting and progressBar({
|
||||||
label = bartext..((metadata and metadata.label) or Items[data.item].label),
|
label = bartext..((metadata and metadata.label) or getItemLabel(data.item)),
|
||||||
time = bartime,
|
time = bartime,
|
||||||
cancel = true,
|
cancel = true,
|
||||||
dict = animDict,
|
dict = animDict,
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ function GetPrintTime()
|
|||||||
local hour, min, sec = os.date('%H'), os.date('%M'), os.date('%S')
|
local hour, min, sec = os.date('%H'), os.date('%M'), os.date('%S')
|
||||||
return "^7("..string.format("%02d", hour)..":"..string.format("%02d", min)..":"..string.format("%02d", sec)..")"
|
return "^7("..string.format("%02d", hour)..":"..string.format("%02d", min)..":"..string.format("%02d", sec)..")"
|
||||||
else
|
else
|
||||||
|
if gameName == "rdr3" then return "" end
|
||||||
local _, _, _, hour, min, sec = GetLocalTime()
|
local _, _, _, hour, min, sec = GetLocalTime()
|
||||||
return "^7("..string.format("%02d", hour)..":"..string.format("%02d", min)..":"..string.format("%02d", sec)..")"
|
return "^7("..string.format("%02d", hour)..":"..string.format("%02d", min)..":"..string.format("%02d", sec)..")"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
exports[QSInv]:RemoveItemIntoStash(stashName, k, v)
|
exports[QSInv]:RemoveItemIntoStash(stashName[1], k, v)
|
||||||
debugPrint("^6Bridge^7: ^2Removing ^3"..QSInv.." ^2Stash item^7:", k, v)
|
debugPrint("^6Bridge^7: ^2Removing ^3"..QSInv.." ^2Stash item^7:", k, v)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -291,7 +291,7 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
exports[CoreInv]:removeItemExact(stashName, k, v)
|
exports[CoreInv]:removeItemExact(stashName[1], k, v)
|
||||||
debugPrint("^6Bridge^7: ^2Removing ^3"..CoreInv.." ^2Stash item^7:", k, v)
|
debugPrint("^6Bridge^7: ^2Removing ^3"..CoreInv.." ^2Stash item^7:", k, v)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -381,7 +381,7 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
exports[OrigenInv]:RemoveFromStash(stashName, k, v)
|
exports[OrigenInv]:RemoveFromStash(stashName[1], k, v)
|
||||||
debugPrint("^6Bridge^7: ^2Removing ^3"..OrigenInv.." ^2Stash item^7:", k, v)
|
debugPrint("^6Bridge^7: ^2Removing ^3"..OrigenInv.." ^2Stash item^7:", k, v)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -494,7 +494,7 @@ local InvFunc = {
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
exports[CodeMInv]:UpdateStash(stashName, stashItems)
|
exports[CodeMInv]:UpdateStash(stashName[1], stashItems)
|
||||||
end,
|
end,
|
||||||
registerStash =
|
registerStash =
|
||||||
function(name, label, slots, weight, owner, coords)
|
function(name, label, slots, weight, owner, coords)
|
||||||
@@ -588,8 +588,8 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
local itemData = exports[TgiannInv]:GetItemByNameFromSecondaryInventory("stash", stashName, k)
|
local itemData = exports[TgiannInv]:GetItemByNameFromSecondaryInventory("stash", stashName[1], k)
|
||||||
exports[TgiannInv]:RemoveItemFromSecondaryInventory("stash", stashName, k, v, itemData.slot, nil)
|
exports[TgiannInv]:RemoveItemFromSecondaryInventory("stash", stashName[1], k, v, itemData.slot, nil)
|
||||||
debugPrint("^6Bridge^7: ^2Removing ^3"..TgiannInv.." ^2Stash item^7:", k, v)
|
debugPrint("^6Bridge^7: ^2Removing ^3"..TgiannInv.." ^2Stash item^7:", k, v)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -743,7 +743,7 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
if not stashItems or not next(stashItems) then
|
if not stashItems or not next(stashItems) then
|
||||||
stashItems = getStash(stashName)
|
stashItems = getStash(stashName[1])
|
||||||
end
|
end
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
for l in pairs(stashItems) do
|
for l in pairs(stashItems) do
|
||||||
@@ -757,9 +757,9 @@ local InvFunc = {
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3JPR^2 stash '^6"..stashName.."^7'")
|
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3JPR^2 stash '^6"..stashName[1].."^7'")
|
||||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||||
['stash'] = stashName,
|
['stash'] = stashName[1],
|
||||||
['items'] = json.encode(stashItems)
|
['items'] = json.encode(stashItems)
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
@@ -924,12 +924,12 @@ local InvFunc = {
|
|||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
if checkExportExists(QBInv, "RemoveItem") then
|
if checkExportExists(QBInv, "RemoveItem") then
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
exports[QBInv]:RemoveItem(stashName, k, v, false, 'crafting')
|
exports[QBInv]:RemoveItem(stashName[1], k, v, false, 'crafting')
|
||||||
debugPrint("^6Bridge^7: ^2Removing ^3"..QBInv.." ^2Stash item^7:", k, v)
|
debugPrint("^6Bridge^7: ^2Removing ^3"..QBInv.." ^2Stash item^7:", k, v)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if not stashItems or not next(stashItems) then
|
if not stashItems or not next(stashItems) then
|
||||||
stashItems = getStash(stashName)
|
stashItems = getStash(stashName[1])
|
||||||
end
|
end
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
for l in pairs(stashItems) do
|
for l in pairs(stashItems) do
|
||||||
@@ -943,9 +943,9 @@ local InvFunc = {
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash '^6"..stashName.."^7'")
|
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash '^6"..stashName[1].."^7'")
|
||||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||||
['stash'] = stashName,
|
['stash'] = stashName[1],
|
||||||
['items'] = json.encode(stashItems)
|
['items'] = json.encode(stashItems)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@@ -1105,7 +1105,7 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
if not stashItems or not next(stashItems) then
|
if not stashItems or not next(stashItems) then
|
||||||
stashItems = getStash(stashName)
|
stashItems = getStash(stashName[1])
|
||||||
end
|
end
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
for l in pairs(stashItems) do
|
for l in pairs(stashItems) do
|
||||||
@@ -1119,9 +1119,9 @@ local InvFunc = {
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3PS^2 stash ^7'^6"..stashName.."^7'")
|
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3PS^2 stash ^7'^6"..stashName[1].."^7'")
|
||||||
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
|
||||||
['stash'] = stashName,
|
['stash'] = stashName[1],
|
||||||
['items'] = json.encode(stashItems)
|
['items'] = json.encode(stashItems)
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
@@ -1244,7 +1244,7 @@ local InvFunc = {
|
|||||||
stashRemoveItem =
|
stashRemoveItem =
|
||||||
function(stashItems, stashName, items)
|
function(stashItems, stashName, items)
|
||||||
for k, v in pairs(items) do
|
for k, v in pairs(items) do
|
||||||
exports[RSGInv]:RemoveItem(stashName, k, v, false, 'crafting')
|
exports[RSGInv]:RemoveItem(stashName[1], k, v, false, 'crafting')
|
||||||
debugPrint("^6Bridge^7: ^2Removing ^3"..RSGInv.." ^2Stash item^7:", k, v)
|
debugPrint("^6Bridge^7: ^2Removing ^3"..RSGInv.." ^2Stash item^7:", k, v)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -1585,7 +1585,7 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
|||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1))
|
debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..getItemLabel(item).."("..item..") x"..(amount or 1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -1610,7 +1610,7 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
|
|||||||
ESX.GetPlayerFromId(src).addInventoryItem(item, amountToAdd)
|
ESX.GetPlayerFromId(src).addInventoryItem(item, amountToAdd)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1))
|
debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..getItemLabel(item).."("..item..") x"..(amount or 1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@@ -1951,6 +1951,7 @@ end
|
|||||||
--- if doesItemExist(item) then print("item exists") end
|
--- if doesItemExist(item) then print("item exists") end
|
||||||
--- ```
|
--- ```
|
||||||
function doesItemExist(item)
|
function doesItemExist(item)
|
||||||
|
local item = type(item) == "string" and item or tostring(item)
|
||||||
if not item or item == "" then
|
if not item or item == "" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -1965,16 +1966,18 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function getItemLabel(item)
|
function getItemLabel(item)
|
||||||
|
local item = type(item) == "string" and item or tostring(item)
|
||||||
|
|
||||||
if not item or item == "" then
|
if not item or item == "" then
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
if not Items or not next(Items) then
|
if not Items or not next(Items) then
|
||||||
return item.." (Missing)"
|
return item.." (Missing item error)"
|
||||||
end
|
end
|
||||||
if Items[item] ~= nil then
|
if Items[item] ~= nil then
|
||||||
return Items[item].label
|
return Items[item].label
|
||||||
end
|
end
|
||||||
return item.." (Missing)"
|
return item.." (Missing item error)"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -2280,7 +2283,7 @@ function stashRemoveItem(stashItems, stashName, items)
|
|||||||
for i = 1, #InvFunc do
|
for i = 1, #InvFunc do
|
||||||
local inv = InvFunc[i]
|
local inv = InvFunc[i]
|
||||||
if isStarted(inv.invName) then
|
if isStarted(inv.invName) then
|
||||||
inv.stashRemoveItem(stashItems, stashName[1], items)
|
inv.stashRemoveItem(stashItems, stashName, items)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2476,7 +2479,7 @@ function sellMenu(data)
|
|||||||
Menu[#Menu + 1] = {
|
Menu[#Menu + 1] = {
|
||||||
isMenuHeader = not hasTable[k].hasItem,
|
isMenuHeader = not hasTable[k].hasItem,
|
||||||
icon = invImg(k),
|
icon = invImg(k),
|
||||||
header = Items[k].label..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
|
header = getItemLabel(k)..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
|
||||||
txt = (Loc and Loc[Config.Lan]) and Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"]
|
txt = (Loc and Loc[Config.Lan]) and Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"]
|
||||||
or "Sell ALL at $"..v.." each",
|
or "Sell ALL at $"..v.." each",
|
||||||
onSelect = function()
|
onSelect = function()
|
||||||
@@ -2534,7 +2537,7 @@ end
|
|||||||
--- ```
|
--- ```
|
||||||
function sellAnim(data, token)
|
function sellAnim(data, token)
|
||||||
if not hasItem(data.item, 1) then
|
if not hasItem(data.item, 1) then
|
||||||
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error")
|
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..getItemLabel(data.item), "error")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2596,7 +2599,7 @@ RegisterNetEvent(getScript()..":Sellitems", function(data, token)
|
|||||||
print((hasTable[data.item].count * data.price), data.price)
|
print((hasTable[data.item].count * data.price), data.price)
|
||||||
fundPlayer((hasTable[data.item].count * data.price), "cash", src)
|
fundPlayer((hasTable[data.item].count * data.price), "cash", src)
|
||||||
else
|
else
|
||||||
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error", src)
|
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..getItemLabel(data.item), "error", src)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ function makePed(data, coords, freeze, collision, scenario, anim, synced, fade)
|
|||||||
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)
|
||||||
|
|
||||||
@@ -145,7 +144,8 @@ function makePed(data, coords, freeze, collision, scenario, anim, synced, fade)
|
|||||||
end
|
end
|
||||||
unloadModel(model)
|
unloadModel(model)
|
||||||
Peds[keyGen()..keyGen()] = ped
|
Peds[keyGen()..keyGen()] = ped
|
||||||
if fade ~= false then
|
if fade ~= false and gameName ~= "rdr3" then
|
||||||
|
SetEntityAlpha(ped, 0, false)
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
fadeInEnt(ped)
|
fadeInEnt(ped)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ local distProps = {}
|
|||||||
function makeProp(data, freeze, synced, fade)
|
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
|
if fade ~= false and gameName ~= "rdr3" then
|
||||||
|
SetEntityAlpha(prop, 0, false)
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
fadeInEnt(prop)
|
fadeInEnt(prop)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function makeVeh(model, coords, synced, fade)
|
|||||||
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
|
if fade ~= false and gameName ~= "rdr3" then
|
||||||
SetEntityAlpha(veh, 0, false)
|
SetEntityAlpha(veh, 0, false)
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
fadeInEnt(veh)
|
fadeInEnt(veh)
|
||||||
|
|||||||
@@ -4,220 +4,236 @@ local storedPID = nil
|
|||||||
|
|
||||||
local progressFunc = {
|
local progressFunc = {
|
||||||
ox = {
|
ox = {
|
||||||
start = function(data)
|
start =
|
||||||
local options = {
|
function(data)
|
||||||
duration = debugMode and 1000 or data.time,
|
local options = {
|
||||||
label = data.label,
|
duration = debugMode and 1000 or data.time,
|
||||||
position = data.position or "bottom",
|
label = data.label,
|
||||||
useWhileDead = data.dead or false,
|
position = data.position or "bottom",
|
||||||
canCancel = data.cancel or true,
|
useWhileDead = data.dead or false,
|
||||||
anim = {
|
canCancel = data.cancel or true,
|
||||||
dict = data.dict,
|
anim = {
|
||||||
clip = data.anim,
|
dict = data.dict,
|
||||||
flag = (data.flag == 8 and 32 or data.flag) or nil,
|
clip = data.anim,
|
||||||
scenario = data.task
|
flag = (data.flag == 8 and 32 or data.flag) or nil,
|
||||||
},
|
scenario = data.task
|
||||||
disable = {
|
},
|
||||||
combat = data.combat or true,
|
disable = {
|
||||||
move = data.disableMovement or false,
|
combat = data.combat or true,
|
||||||
car = data.disableMovement or false,
|
move = data.disableMovement or false,
|
||||||
mouse = data.mouse or false
|
car = data.disableMovement or false,
|
||||||
},
|
mouse = data.mouse or false
|
||||||
}
|
},
|
||||||
if data.prop and data.prop.model then
|
|
||||||
options.prop = {
|
|
||||||
model = data.prop.model,
|
|
||||||
pos = data.prop.pos or vec3(0, 0, 0),
|
|
||||||
rot = data.prop.rot or vec3(0, 0, 0),
|
|
||||||
bone = data.prop.bone or 0
|
|
||||||
}
|
}
|
||||||
end
|
if data.prop and data.prop.model then
|
||||||
if data.propTwo and data.propTwo.model then
|
options.prop = {
|
||||||
options.propTwo = {
|
model = data.prop.model,
|
||||||
model = data.propTwo.model,
|
pos = data.prop.pos or vec3(0, 0, 0),
|
||||||
pos = data.propTwo.pos or vec3(0, 0, 0),
|
rot = data.prop.rot or vec3(0, 0, 0),
|
||||||
rot = data.propTwo.rot or vec3(0, 0, 0),
|
bone = data.prop.bone or 0
|
||||||
bone = data.propTwo.bone or 0
|
}
|
||||||
}
|
|
||||||
end
|
|
||||||
if data.progressType == "circle" then
|
|
||||||
if exports[OXLibExport]:progressCircle(options) then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
end
|
if data.propTwo and data.propTwo.model then
|
||||||
if not data.progressType or data.progressType == "bar" then
|
options.propTwo = {
|
||||||
if exports[OXLibExport]:progressBar(options) then
|
model = data.propTwo.model,
|
||||||
return true
|
pos = data.propTwo.pos or vec3(0, 0, 0),
|
||||||
else
|
rot = data.propTwo.rot or vec3(0, 0, 0),
|
||||||
return false
|
bone = data.propTwo.bone or 0
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
if data.progressType == "circle" then
|
||||||
end,
|
if exports[OXLibExport]:progressCircle(options) then
|
||||||
stop = function()
|
return true
|
||||||
exports[OXLibExport]:cancelProgress()
|
else
|
||||||
end,
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not data.progressType or data.progressType == "bar" then
|
||||||
|
if exports[OXLibExport]:progressBar(options) then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
print("^1progressBar was not successful")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
stop =
|
||||||
|
function()
|
||||||
|
exports[OXLibExport]:cancelProgress()
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
qb = {
|
qb = {
|
||||||
start = function(data)
|
start =
|
||||||
Core.Functions.Progressbar("progbar",
|
function(data)
|
||||||
data.label,
|
local p = promise.new()
|
||||||
debugMode and 1000 or data.time,
|
Core.Functions.Progressbar("progbar",
|
||||||
data.dead or false,
|
data.label,
|
||||||
data.cancel or true,
|
debugMode and 1000 or data.time,
|
||||||
{
|
data.dead or false,
|
||||||
disableMovement = data.disableMovement or false,
|
data.cancel or true,
|
||||||
disableCarMovement = data.disableMovement or false,
|
{
|
||||||
disableMouse = data.disableMouse or false,
|
disableMovement = data.disableMovement or false,
|
||||||
disableCombat = data.disableCombat or true,
|
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,
|
animDict = data.dict,
|
||||||
task = data.task
|
anim = data.anim,
|
||||||
},
|
flags = data.flag or 32,
|
||||||
{}, {},
|
task = data.task
|
||||||
function()
|
},
|
||||||
return true
|
{}, {},
|
||||||
end, function()
|
function() p:resolve(true) end,
|
||||||
return false
|
function() p:resolve(false) end,
|
||||||
end, data.icon)
|
data.icon)
|
||||||
|
return Citizen.Await(p)
|
||||||
|
end,
|
||||||
|
stop =
|
||||||
|
function()
|
||||||
|
TriggerEvent("progressbar:client:cancel")
|
||||||
end,
|
end,
|
||||||
stop = function()
|
|
||||||
TriggerEvent("progressbar:client:cancel")
|
|
||||||
end,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
qs = {
|
qs = {
|
||||||
start = function(data)
|
start =
|
||||||
if exports['qs-interface']:ProgressBar({
|
function(data)
|
||||||
duration = debugMode and 1000 or data.time,
|
if exports['qs-interface']:ProgressBar({
|
||||||
label = data.label,
|
duration = debugMode and 1000 or data.time,
|
||||||
position = 'bottom',
|
label = data.label,
|
||||||
useWhileDead = data.dead or false,
|
position = 'bottom',
|
||||||
canCancel = data.cancel or true,
|
useWhileDead = data.dead or false,
|
||||||
disable = data.disableMovement or false,
|
canCancel = data.cancel or true,
|
||||||
anim = {
|
disable = data.disableMovement or false,
|
||||||
dict = data.dict,
|
anim = {
|
||||||
clip = data.anim,
|
dict = data.dict,
|
||||||
flag = data.flag or 32
|
clip = data.anim,
|
||||||
},
|
flag = data.flag or 32
|
||||||
prop = nil
|
},
|
||||||
}) then
|
prop = nil
|
||||||
return true
|
}) then
|
||||||
else
|
return true
|
||||||
return false
|
else
|
||||||
end
|
return false
|
||||||
end,
|
end
|
||||||
stop = function()
|
end,
|
||||||
--??
|
stop =
|
||||||
TriggerEvent("progressbar:client:cancel")
|
function()
|
||||||
end,
|
--??
|
||||||
|
TriggerEvent("progressbar:client:cancel")
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
esx = {
|
esx = {
|
||||||
start = function(data)
|
start =
|
||||||
ESX.Progressbar(data.label, debugMode and 1000 or data.time, {
|
function(data)
|
||||||
FreezePlayer = true,
|
local p = promise.new()
|
||||||
animation = {
|
ESX.Progressbar(data.label, debugMode and 1000 or data.time, {
|
||||||
type = data.anim,
|
FreezePlayer = true,
|
||||||
dict = data.dict,
|
animation = {
|
||||||
scenario = data.task,
|
type = data.anim,
|
||||||
},
|
dict = data.dict,
|
||||||
onFinish = function()
|
scenario = data.task,
|
||||||
return true
|
},
|
||||||
end,
|
onFinish = function()
|
||||||
onCancel = function()
|
p:resolve(true)
|
||||||
return false
|
end,
|
||||||
end
|
onCancel = function()
|
||||||
})
|
p:resolve(false)
|
||||||
end,
|
return false
|
||||||
stop = function()
|
end
|
||||||
ESX.CancelProgressbar()
|
})
|
||||||
end,
|
return Citizen.Await(p)
|
||||||
|
end,
|
||||||
|
stop =
|
||||||
|
function()
|
||||||
|
ESX.CancelProgressbar()
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
lation = {
|
lation = {
|
||||||
start = function(data)
|
start =
|
||||||
if exports.lation_ui:progressBar({
|
function(data)
|
||||||
label = data.label,
|
if exports.lation_ui:progressBar({
|
||||||
description = nil,
|
label = data.label,
|
||||||
duration = debugMode and 1000 or data.time,
|
description = nil,
|
||||||
icon = data.icon,
|
duration = debugMode and 1000 or data.time,
|
||||||
useWhileDead = data.dead or false,
|
icon = data.icon,
|
||||||
disable = {
|
useWhileDead = data.dead or false,
|
||||||
combat = data.combat or true,
|
disable = {
|
||||||
move = data.disableMovement or false,
|
combat = data.combat or true,
|
||||||
car = data.disableMovement or false,
|
move = data.disableMovement or false,
|
||||||
},
|
car = data.disableMovement or false,
|
||||||
anim = {
|
},
|
||||||
dict = data.dict,
|
anim = {
|
||||||
clip = data.anim,
|
dict = data.dict,
|
||||||
flag = data.flag
|
clip = data.anim,
|
||||||
},
|
flag = data.flag
|
||||||
prop = {
|
},
|
||||||
model = data.prop and data.prop.model,
|
prop = {
|
||||||
pos = data.prop and (data.prop.pos or vec3(0, 0, 0)),
|
model = data.prop and data.prop.model,
|
||||||
rot = data.prop and (data.prop.rot or vec3(0, 0, 0)),
|
pos = data.prop and (data.prop.pos or vec3(0, 0, 0)),
|
||||||
bone = data.prop and (data.prop.bone or 0)
|
rot = data.prop and (data.prop.rot or vec3(0, 0, 0)),
|
||||||
}
|
bone = data.prop and (data.prop.bone or 0)
|
||||||
}) then
|
}
|
||||||
return true
|
}) then
|
||||||
else
|
return true
|
||||||
return false
|
else
|
||||||
end
|
return false
|
||||||
end,
|
end
|
||||||
stop = function()
|
end,
|
||||||
exports.lation_ui:cancelProgress()
|
stop =
|
||||||
end,
|
function()
|
||||||
|
exports.lation_ui:cancelProgress()
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
red = {
|
red = {
|
||||||
start = function(data)
|
start =
|
||||||
if exports.jim_bridge:redProgressBar({
|
function(data)
|
||||||
label = data.label,
|
if exports.jim_bridge:redProgressBar({
|
||||||
time = debugMode and 1000 or data.time,
|
label = data.label,
|
||||||
dict = data.dict,
|
time = debugMode and 1000 or data.time,
|
||||||
anim = data.anim,
|
dict = data.dict,
|
||||||
flag = data.flag or 32,
|
anim = data.anim,
|
||||||
task = data.task,
|
flag = data.flag or 32,
|
||||||
disableMovement = data.disableMovement or false,
|
task = data.task,
|
||||||
cancel = data.cancel or true,
|
disableMovement = data.disableMovement or false,
|
||||||
}) then
|
cancel = data.cancel or true,
|
||||||
return true
|
}) then
|
||||||
else
|
return true
|
||||||
return false
|
else
|
||||||
end
|
return false
|
||||||
end,
|
end
|
||||||
stop = function()
|
end,
|
||||||
exports.jim_bridge:stopProgressBar()
|
stop =
|
||||||
end,
|
function()
|
||||||
|
exports.jim_bridge:stopProgressBar()
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
gta = {
|
gta = {
|
||||||
start = function(data)
|
start =
|
||||||
|
function(data)
|
||||||
if exports.jim_bridge:gtaProgressBar({
|
if exports.jim_bridge:gtaProgressBar({
|
||||||
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,
|
||||||
disableMovement = data.disableMovement or false,
|
disableMovement = data.disableMovement or false,
|
||||||
cancel = data.cancel or true,
|
cancel = data.cancel or true,
|
||||||
}) then
|
}) then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
stop = function()
|
stop =
|
||||||
exports.jim_bridge:stopProgressBar()
|
function()
|
||||||
end,
|
exports.jim_bridge:stopProgressBar()
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +270,11 @@ local progressFunc = {
|
|||||||
--- cancel = true,
|
--- cancel = true,
|
||||||
--- })
|
--- })
|
||||||
--- ```
|
--- ```
|
||||||
|
|
||||||
|
local progresssBarActive = false
|
||||||
function progressBar(data)
|
function progressBar(data)
|
||||||
|
if progresssBarActive then return else progresssBarActive = true end
|
||||||
|
|
||||||
local ped = PlayerPedId()
|
local ped = PlayerPedId()
|
||||||
if data.shared then
|
if data.shared then
|
||||||
debugPrint("^6Bridge^7: ^6Sharing progressBar to player^7: ^6"..data.shared.pid.."^7")
|
debugPrint("^6Bridge^7: ^6Sharing progressBar to player^7: ^6"..data.shared.pid.."^7")
|
||||||
@@ -273,6 +293,7 @@ function progressBar(data)
|
|||||||
while result == nil do
|
while result == nil do
|
||||||
Wait(10)
|
Wait(10)
|
||||||
end
|
end
|
||||||
|
debugPrint("^5Debug^7: ^2ProgressBar result^7: ^3"..tostring(result).."^7")
|
||||||
|
|
||||||
-- Cleanup
|
-- Cleanup
|
||||||
FreezeEntityPosition(ped, false)
|
FreezeEntityPosition(ped, false)
|
||||||
@@ -293,6 +314,7 @@ function progressBar(data)
|
|||||||
TriggerServerEvent(getScript()..":clearAuthToken")
|
TriggerServerEvent(getScript()..":clearAuthToken")
|
||||||
currentToken = triggerCallback(AuthEvent)
|
currentToken = triggerCallback(AuthEvent)
|
||||||
end
|
end
|
||||||
|
progresssBarActive = false
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -301,6 +323,7 @@ end
|
|||||||
--- 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.
|
||||||
function stopProgressBar()
|
function stopProgressBar()
|
||||||
progressFunc[Config.System.ProgressBar].stop()
|
progressFunc[Config.System.ProgressBar].stop()
|
||||||
|
progresssBarActive = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- System to handle sending/sharing progress bars between players --
|
-- System to handle sending/sharing progress bars between players --
|
||||||
|
|||||||
@@ -21,24 +21,56 @@
|
|||||||
--- })
|
--- })
|
||||||
--- ```
|
--- ```
|
||||||
function registerCommand(command, options)
|
function registerCommand(command, options)
|
||||||
|
-- Name the options
|
||||||
|
local optionTable = {
|
||||||
|
helpInfo = options[1] or nil, -- help info that pops up in chat box
|
||||||
|
subText = options[2] or nil, -- sub help info for each arg
|
||||||
|
argsRequired = options[3] or nil, -- command only works if you enter args
|
||||||
|
funct = options[4] or nil, -- function that will run when command is triggered
|
||||||
|
restriction = options[5] or nil -- admin group that can use the group or `nil`
|
||||||
|
}
|
||||||
|
|
||||||
local commandResource = ""
|
local commandResource = ""
|
||||||
if isStarted(OXLibExport) then
|
if isStarted(OXLibExport) then
|
||||||
commandResource = OXLibExport
|
commandResource = OXLibExport
|
||||||
lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[3])
|
lib.addCommand(command,
|
||||||
|
{
|
||||||
|
help = optionTable.helpInfo,
|
||||||
|
params = optionTable.subText,
|
||||||
|
restricted = optionTable.restriction and "group."..optionTable.restriction or nil
|
||||||
|
},
|
||||||
|
optionTable.funct)
|
||||||
|
|
||||||
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
elseif isStarted(QBExport) and not isStarted(QBXExport) then
|
||||||
commandResource = QBExport
|
commandResource = QBExport
|
||||||
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
Core.Commands.Add(command,
|
||||||
|
optionTable.helpInfo,
|
||||||
|
optionTable.subText,
|
||||||
|
optionTable.argsRequired,
|
||||||
|
optionTable.funt,
|
||||||
|
optionTable.restriction or nil
|
||||||
|
)
|
||||||
|
|
||||||
elseif isStarted(RSGExport) then
|
elseif isStarted(RSGExport) then
|
||||||
commandResource = RSGExport
|
commandResource = RSGExport
|
||||||
Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
|
Core.Commands.Add(command,
|
||||||
|
optionTable.helpInfo,
|
||||||
|
optionTable.subText,
|
||||||
|
optionTable.argsRequired,
|
||||||
|
optionTable.funt,
|
||||||
|
optionTable.restriction or nil
|
||||||
|
)
|
||||||
|
|
||||||
elseif isStarted(ESXExport) then
|
elseif isStarted(ESXExport) then
|
||||||
commandResource = ESXExport
|
commandResource = ESXExport
|
||||||
ESX.RegisterCommand(command, options[5] or 'admin', function(xPlayer, args, showError)
|
ESX.RegisterCommand(command,
|
||||||
options[4](xPlayer.source, args, showError)
|
optionTable.restriction or 'admin',
|
||||||
end, false, { help = options[1] })
|
function(xPlayer, args, showError)
|
||||||
|
optionTable.funct(xPlayer.source, args, showError)
|
||||||
|
end,
|
||||||
|
false,
|
||||||
|
{ help = options[1] }
|
||||||
|
)
|
||||||
|
|
||||||
end
|
end
|
||||||
if commandResource ~= "" then
|
if commandResource ~= "" then
|
||||||
|
|||||||
@@ -25,7 +25,12 @@ Exports = {
|
|||||||
|
|
||||||
-- REDM
|
-- REDM
|
||||||
RSGExport = "rsg-core",
|
RSGExport = "rsg-core",
|
||||||
RSGInv = "rsg-inventory"
|
RSGInv = "rsg-inventory",
|
||||||
|
|
||||||
|
VorpExport = "vorp_core",
|
||||||
|
VorpInv = "vorp_inventory",
|
||||||
|
VorpMenu = "vorp_menu",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Required variables
|
-- Required variables
|
||||||
|
|||||||
@@ -53,27 +53,11 @@ function redProgressBar(data)
|
|||||||
Wait(0)
|
Wait(0)
|
||||||
local elapsed = GetGameTimer()
|
local elapsed = GetGameTimer()
|
||||||
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||||
|
if percentage < 0 then percentage = 0 end
|
||||||
|
if percentage > 100 then percentage = 100 end
|
||||||
|
|
||||||
-- 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
|
-- Draw your segmented progress bar
|
||||||
ShowRedProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
ShowRedProgressBar(percentage, data.label, ("%.0f%%"):format(percentage))
|
||||||
|
|
||||||
-- Controls to disable during progress
|
-- Controls to disable during progress
|
||||||
if data.disableMouse then
|
if data.disableMouse then
|
||||||
@@ -151,40 +135,48 @@ function redProgressBar(data)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function ShowRedProgressBar(currentProg, title, level)
|
function ShowRedProgressBar(percentage, title, level)
|
||||||
local loc = vec2(0.40, 0.90)
|
local loc = vec2(0.40, 0.90)
|
||||||
local size = vec2(0.3, 0.03)
|
local size = vec2(0.3, 0.03)
|
||||||
|
local tickCount = 10 -- visually split into 10 segments (9 inner lines)
|
||||||
|
local barHeight = size.y / 3.4
|
||||||
|
local barWidth = 0.21 -- tuned to your existing layout
|
||||||
|
local barLeft = (loc.x - size.x / 4) + 0.075
|
||||||
|
local barCenter = barLeft + barWidth / 2
|
||||||
|
local lineW = 0.001
|
||||||
|
local lineH = barHeight + 0.00
|
||||||
|
|
||||||
-- Draw background box
|
-- Background plate
|
||||||
DrawSprite("generic_textures", "inkroller_1a", loc.x+0.1, loc.y-0.01, 0.25, 0.07, 180.0, 0, 0, 0, 200)
|
DrawSprite("generic_textures", "inkroller_1a", loc.x + 0.1, loc.y - 0.01, 0.25, 0.07, 180.0, 0, 0, 0, 200)
|
||||||
|
|
||||||
|
-- Title (left)
|
||||||
SetTextFontForCurrentCommand(6)
|
SetTextFontForCurrentCommand(6)
|
||||||
SetTextScale(0.35, 0.35)
|
SetTextScale(0.35, 0.35)
|
||||||
SetTextColor(255, 255, 255, 255)
|
SetTextColor(255, 255, 255, 255)
|
||||||
SetTextDropshadow(1, 0, 0, 0, 200)
|
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||||
BgDisplayText(title, loc.x - size.x / 4 + 0.074, loc.y - 0.034)
|
BgDisplayText(title, loc.x - size.x / 4 + 0.074, loc.y - 0.034)
|
||||||
|
|
||||||
|
-- Percentage (right)
|
||||||
SetTextFontForCurrentCommand(1)
|
SetTextFontForCurrentCommand(1)
|
||||||
SetTextScale(0.35, 0.35)
|
SetTextScale(0.35, 0.35)
|
||||||
SetTextColor(255, 255, 255, 255)
|
SetTextColor(255, 255, 255, 255)
|
||||||
SetTextDropshadow(1, 0, 0, 0, 200)
|
SetTextDropshadow(1, 0, 0, 0, 200)
|
||||||
BgDisplayText(level, loc.x - size.x / 4 + 0.246, loc.y - 0.030)
|
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)
|
-- Track (bg)
|
||||||
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
DrawRect(barCenter, loc.y, barWidth, barHeight, 100, 100, 100, 255)
|
||||||
|
|
||||||
for i = 1, #currentProg do
|
-- Fill
|
||||||
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
local fillWidth = barWidth * (percentage / 100.0)
|
||||||
local fillPercentage = currentProg[i]
|
if fillWidth > 0.0 then
|
||||||
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
local fillCenter = barLeft + (fillWidth / 2.0)
|
||||||
|
DrawRect(fillCenter, loc.y, fillWidth, barHeight, 255, 0, 0, 200) -- red fill
|
||||||
|
end
|
||||||
|
|
||||||
-- Semi-transparent background for each segment
|
-- Tick lines (9 inner lines for 10 segments)
|
||||||
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
for i = 1, (tickCount - 1) do
|
||||||
|
local x = barLeft + (barWidth * (i / tickCount))
|
||||||
-- Filling progress for each segment
|
DrawRect(x, loc.y, lineW, lineH, 0, 0, 0, 120)
|
||||||
if progressBarWidth > 0 then
|
|
||||||
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 255, 0, 0, 200) -- Blue progress
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -213,29 +205,14 @@ function gtaProgressBar(data)
|
|||||||
local elapsed = GetGameTimer()
|
local elapsed = GetGameTimer()
|
||||||
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
local percentage = ((elapsed - (endTime - wait)) / wait) * 100
|
||||||
|
|
||||||
-- Convert to segmented progress (assuming 5 segments here)
|
if percentage < 0 then percentage = 0 end
|
||||||
local segments = 1 -- Number of segments in the bar
|
if percentage > 100 then percentage = 100 end
|
||||||
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
|
-- Draw your segmented progress bar
|
||||||
ShowGTAProgressBar(segmentProgress, data.label, ("%.0f%%"):format(percentage))
|
ShowGTAProgressBar(percentage, data.label, ("%.0f%%"):format(percentage))
|
||||||
|
|
||||||
-- Controls to disable during progress
|
-- Controls to disable during progress
|
||||||
DisablePlayerFiring(ped, true)
|
DisablePlayerFiring(PlayerId(), true)
|
||||||
DisableControlAction(0, 25, true) -- Disable aim
|
DisableControlAction(0, 25, true) -- Disable aim
|
||||||
DisableControlAction(0, 21, true) -- Disable sprint
|
DisableControlAction(0, 21, true) -- Disable sprint
|
||||||
DisableControlAction(0, 30, true) -- Disable move left/right
|
DisableControlAction(0, 30, true) -- Disable move left/right
|
||||||
@@ -246,13 +223,15 @@ function gtaProgressBar(data)
|
|||||||
inProgress = false
|
inProgress = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
result = inProgress
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Wait for completion or cancel
|
|
||||||
while GetGameTimer() < endTime and inProgress do
|
|
||||||
Wait(100)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Wait for completion or cancel
|
||||||
|
while result == nil do Wait(10) end
|
||||||
|
inProgress = false
|
||||||
|
|
||||||
|
print(tostring(result))
|
||||||
-- Cleanup animations/tasks
|
-- Cleanup animations/tasks
|
||||||
if data.dict then
|
if data.dict then
|
||||||
stopAnim(data.dict, data.anim, ped)
|
stopAnim(data.dict, data.anim, ped)
|
||||||
@@ -261,10 +240,6 @@ function gtaProgressBar(data)
|
|||||||
ClearPedTasks(ped)
|
ClearPedTasks(ped)
|
||||||
end
|
end
|
||||||
|
|
||||||
result = inProgress
|
|
||||||
inProgress = false
|
|
||||||
|
|
||||||
while result == nil do Wait(10) end
|
|
||||||
|
|
||||||
-- Cleanup
|
-- Cleanup
|
||||||
FreezeEntityPosition(ped, false)
|
FreezeEntityPosition(ped, false)
|
||||||
@@ -276,14 +251,22 @@ function gtaProgressBar(data)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function ShowGTAProgressBar(currentProg, title, level)
|
function ShowGTAProgressBar(percentage, title, level)
|
||||||
local loc = vec2(0.37, 0.90)
|
local loc = vec2(0.37, 0.90)
|
||||||
local size = vec2(0.3, 0.03)
|
local size = vec2(0.3, 0.03)
|
||||||
|
local tickCount = 10
|
||||||
|
local barHeight = size.y / 3.4
|
||||||
|
local barWidth = 0.19
|
||||||
|
local barLeft = (loc.x - size.x / 4) + 0.075
|
||||||
|
local barCenter = barLeft + barWidth / 2
|
||||||
|
local lineW = 0.001
|
||||||
|
local lineH = barHeight + 0.00
|
||||||
|
|
||||||
-- Draw background box
|
-- Background plates
|
||||||
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.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)
|
DrawSprite("timerbars", "all_black_bg", loc.x + 0.170, loc.y - 0.01, 0.15, 0.07, 180.0, 255, 255, 255, 255)
|
||||||
|
|
||||||
|
-- Title (left)
|
||||||
SetTextFont(0)
|
SetTextFont(0)
|
||||||
SetTextProportional(1)
|
SetTextProportional(1)
|
||||||
SetTextScale(0.35, 0.35)
|
SetTextScale(0.35, 0.35)
|
||||||
@@ -293,34 +276,35 @@ function ShowGTAProgressBar(currentProg, title, level)
|
|||||||
SetTextOutline()
|
SetTextOutline()
|
||||||
SetTextEntry("STRING")
|
SetTextEntry("STRING")
|
||||||
AddTextComponentString(title)
|
AddTextComponentString(title)
|
||||||
DrawText(loc.x - size.x / 4 + 0.074, loc.y - 0.034) -- Adjust text position
|
DrawText(loc.x - size.x / 4 + 0.074, loc.y - 0.034)
|
||||||
|
|
||||||
|
-- Percentage (right)
|
||||||
SetTextFont(0)
|
SetTextFont(0)
|
||||||
SetTextProportional(1)
|
SetTextProportional(1)
|
||||||
SetTextScale(0.35, 0.25)
|
SetTextScale(0.35, 0.25)
|
||||||
SetTextColour(255, 255, 255, 255)
|
SetTextColour(255, 255, 255, 255)
|
||||||
SetTextEntry("STRING")
|
SetTextEntry("STRING")
|
||||||
AddTextComponentString(level)
|
AddTextComponentString(level)
|
||||||
DrawText(loc.x - size.x / 4 + 0.246, loc.y - 0.030) -- Right-aligned additional text
|
DrawText(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)
|
-- Track (bg)
|
||||||
local gap = segmentWidth / #currentProg -- Smaller gap between segments
|
DrawRect(barCenter, loc.y, barWidth, barHeight, 100, 100, 100, 255)
|
||||||
|
|
||||||
for i = 1, #currentProg do
|
-- Fill
|
||||||
local segmentX = (loc.x - size.x / 4 ) + 0.075 + (i - 1) * (segmentWidth + gap)
|
local fillWidth = barWidth * (percentage / 100.0)
|
||||||
local fillPercentage = currentProg[i]
|
if fillWidth > 0.0 then
|
||||||
local progressBarWidth = segmentWidth * (fillPercentage / 100)
|
local fillCenter = barLeft + (fillWidth / 2.0)
|
||||||
|
DrawRect(fillCenter, loc.y, fillWidth, barHeight, 93, 182, 229, 255) -- GTA blue
|
||||||
|
end
|
||||||
|
|
||||||
-- Semi-transparent background for each segment
|
-- Tick lines
|
||||||
DrawRect(segmentX + segmentWidth / 2, loc.y, segmentWidth, size.y / 3.4, 100, 100, 100, 255)
|
for i = 1, (tickCount - 1) do
|
||||||
|
local x = barLeft + (barWidth * (i / tickCount))
|
||||||
-- Filling progress for each segment
|
DrawRect(x, loc.y, lineW, lineH, 0, 0, 0, 120)
|
||||||
if progressBarWidth > 0 then
|
|
||||||
DrawRect(segmentX + progressBarWidth / 2, loc.y, progressBarWidth, size.y / 3.4, 93, 182, 229, 255) -- Blue progress
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function stopProgressBar() inProgress = false end
|
function stopProgressBar() inProgress = false end
|
||||||
function isProgressBar() return inProgress end
|
function isProgressBar() return inProgress end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- Global Key Table, defined once.
|
-- Global Key Table, defined once.
|
||||||
---
|
|
||||||
local KEY_TABLE = { 38, 29, 47, 23, 45, 159, 162, 163 }
|
local KEY_TABLE = { 38, 29, 47, 23, 45, 159, 162, 163 }
|
||||||
|
|
||||||
-- Mapping of key codes to human-readable key names.
|
-- Mapping of key codes to human-readable key names.
|
||||||
@@ -17,122 +16,196 @@ local Keys = {
|
|||||||
[20] = "Z", [73] = "X", [26] = "C", [0] = "V", [29] = "B", [249] = "N",
|
[20] = "Z", [73] = "X", [26] = "C", [0] = "V", [29] = "B", [249] = "N",
|
||||||
[244] = "M", [82] = ",", [81] = "."
|
[244] = "M", [82] = ",", [81] = "."
|
||||||
}
|
}
|
||||||
-- Tables for storing created targets.
|
|
||||||
local TextTargets = {} -- For fallback DrawText3D targets.
|
|
||||||
local targetEntities = {} -- For entity targets.
|
|
||||||
|
|
||||||
function createEntityTarget(entity, opts, dist)
|
|
||||||
startTargetLoop()
|
|
||||||
targetEntities[#targetEntities + 1] = entity
|
|
||||||
local entityCoords = GetEntityCoords(entity)
|
|
||||||
|
|
||||||
local existingTarget = nil
|
-- ===== Ownership + indexes =====
|
||||||
for _, target in pairs(TextTargets) do
|
-- TextTargets: key -> target data (coords/entity/models/options/etc.)
|
||||||
if #(target.coords - entityCoords) < 0.01 then
|
local TextTargets = {}
|
||||||
existingTarget = target
|
-- targetEntities kept for parity (not strictly required)
|
||||||
break
|
local targetEntities = {}
|
||||||
end
|
|
||||||
|
-- registry: key -> owner, owner -> set(keys)
|
||||||
|
local TargetRegistry = { byKey = {}, byResource = {} }
|
||||||
|
|
||||||
|
local function getOwnerResource()
|
||||||
|
return GetInvokingResource() or GetCurrentResourceName() or "unknown"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function registerTarget(owner, key)
|
||||||
|
-- move key across owners if replacing
|
||||||
|
local prev = TargetRegistry.byKey[key]
|
||||||
|
if prev and TargetRegistry.byResource[prev] then
|
||||||
|
TargetRegistry.byResource[prev][key] = nil
|
||||||
end
|
end
|
||||||
|
TargetRegistry.byKey[key] = owner
|
||||||
|
TargetRegistry.byResource[owner] = TargetRegistry.byResource[owner] or {}
|
||||||
|
TargetRegistry.byResource[owner][key] = true
|
||||||
|
end
|
||||||
|
|
||||||
if existingTarget then
|
local function removeTargetKey(key, reason)
|
||||||
for i = 1, #opts do
|
local owner = TargetRegistry.byKey[key]
|
||||||
local key = KEY_TABLE[#existingTarget.options + i]
|
if TextTargets[key] then
|
||||||
opts[i].key = key
|
TextTargets[key] = nil
|
||||||
existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~" .. Keys[key] .. "~b~] ~w~" .. opts[i].label
|
-- print("^6Bridge^7:^5 Target^7: ^2Removed target '%s'%s", key, reason and (" ("..reason..")") or "")
|
||||||
existingTarget.options[#existingTarget.options + 1] = opts[i]
|
end
|
||||||
|
if owner then
|
||||||
|
if TargetRegistry.byResource[owner] then
|
||||||
|
TargetRegistry.byResource[owner][key] = nil
|
||||||
end
|
end
|
||||||
updateCachedText(existingTarget)
|
TargetRegistry.byKey[key] = nil
|
||||||
else
|
|
||||||
local tempText = {}
|
|
||||||
for i = 1, #opts do
|
|
||||||
opts[i].key = KEY_TABLE[i]
|
|
||||||
tempText[#tempText + 1] = " ~b~[~w~" .. Keys[opts[i].key] .. "~b~] ~w~" .. opts[i].label
|
|
||||||
end
|
|
||||||
TextTargets[entity] = {
|
|
||||||
coords = vec3(entityCoords.x, entityCoords.y, entityCoords.z),
|
|
||||||
buttontext = tempText,
|
|
||||||
options = opts,
|
|
||||||
dist = dist,
|
|
||||||
text = table.concat(tempText, "\n")
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function createZoneTarget(data, opts, dist)
|
AddEventHandler("onResourceStop", function(res)
|
||||||
startTargetLoop()
|
local owned = TargetRegistry.byResource[res]
|
||||||
local existingTarget = nil
|
if not owned then return end
|
||||||
for _, target in pairs(TextTargets) do
|
local cnt = 0
|
||||||
if #(target.coords - data[2]) < 0.01 then
|
for key in pairs(owned) do
|
||||||
existingTarget = target
|
removeTargetKey(key, "resource stopped: "..res)
|
||||||
break
|
cnt = cnt + 1
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
TargetRegistry.byResource[res] = nil
|
||||||
|
-- print("^6Bridge^7:^5 Target^7: ^2Cleared "..cnt.." target(s) from '"..res.."'")
|
||||||
|
end)
|
||||||
|
|
||||||
if existingTarget then
|
-- ===== Helpers =====
|
||||||
for i = 1, #opts do
|
local function vecKey(v)
|
||||||
local key = KEY_TABLE[#existingTarget.options + i]
|
-- stable rounded coord string for entity dedupe when name isn't provided
|
||||||
opts[i].key = key
|
return ("%.3f,%.3f,%.3f"):format(v.x, v.y, v.z)
|
||||||
existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~" .. Keys[key] .. "~b~] ~w~" .. opts[i].label
|
|
||||||
existingTarget.options[#existingTarget.options + 1] = opts[i]
|
|
||||||
end
|
|
||||||
updateCachedText(existingTarget)
|
|
||||||
else
|
|
||||||
local tempText = {}
|
|
||||||
for i = 1, #opts do
|
|
||||||
opts[i].key = KEY_TABLE[i]
|
|
||||||
tempText[#tempText + 1] = " ~b~[~w~" .. Keys[opts[i].key] .. "~b~] ~w~" .. opts[i].label
|
|
||||||
end
|
|
||||||
TextTargets[data[1]] = {
|
|
||||||
coords = data[2],
|
|
||||||
buttontext = tempText,
|
|
||||||
options = opts,
|
|
||||||
dist = dist,
|
|
||||||
text = table.concat(tempText, "\n")
|
|
||||||
}
|
|
||||||
end
|
|
||||||
return data[1]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function createModelTarget(models, opts, dist)
|
local function bakeButtons(opts)
|
||||||
startTargetLoop()
|
|
||||||
if type(models) ~= "table" then
|
|
||||||
models = { models }
|
|
||||||
end
|
|
||||||
|
|
||||||
local tempText = {}
|
local tempText = {}
|
||||||
for i = 1, #opts do
|
for i = 1, #opts do
|
||||||
opts[i].key = KEY_TABLE[i]
|
opts[i].key = KEY_TABLE[i]
|
||||||
tempText[#tempText + 1] = " ~b~[~w~" .. Keys[opts[i].key] .. "~b~] ~w~" .. opts[i].label
|
tempText[#tempText + 1] =
|
||||||
|
(" ~b~[~w~%s~b~] ~w~%s"):format(Keys[opts[i].key] or ("K"..opts[i].key), opts[i].label or ("Option "..i))
|
||||||
end
|
end
|
||||||
|
return tempText
|
||||||
|
end
|
||||||
|
|
||||||
local keyStr = ""
|
-- Update cached text blob
|
||||||
for i, m in ipairs(models) do
|
local function updateCachedText(target)
|
||||||
keyStr = keyStr .. tostring(m) .. (i < #models and "_" or "")
|
target.text = table.concat(target.buttontext, "\n")
|
||||||
end
|
end
|
||||||
local targetKey = "model_" .. keyStr
|
|
||||||
|
|
||||||
TextTargets[targetKey] = {
|
-- ===== Public API: Create targets =====
|
||||||
models = models,
|
-- ENTITY: createEntityTarget(entity, opts, dist, nameOpt?)
|
||||||
buttontext = tempText,
|
function createEntityTarget(entity, opts, dist, name)
|
||||||
options = opts,
|
startTargetLoop()
|
||||||
dist = dist,
|
|
||||||
coords = vec3(0, 0, 0),
|
if not entity or entity == 0 then return end
|
||||||
text = table.concat(tempText, "\n")
|
targetEntities[#targetEntities + 1] = entity
|
||||||
|
|
||||||
|
local owner = getOwnerResource()
|
||||||
|
local coords = GetEntityCoords(entity)
|
||||||
|
local key = tostring(name) or ("entity@" .. vecKey(coords))
|
||||||
|
|
||||||
|
-- Always overwrite on same key
|
||||||
|
local buttontext = bakeButtons(opts)
|
||||||
|
TextTargets[key] = {
|
||||||
|
_key = key,
|
||||||
|
_type = "entity",
|
||||||
|
_owner = owner,
|
||||||
|
entity = entity,
|
||||||
|
coords = vec3(coords.x, coords.y, coords.z),
|
||||||
|
buttontext = buttontext,
|
||||||
|
options = opts,
|
||||||
|
dist = dist,
|
||||||
}
|
}
|
||||||
|
updateCachedText(TextTargets[key])
|
||||||
return targetKey
|
registerTarget(owner, key)
|
||||||
|
-- print("^6Bridge^7:^5 Target^7: ^2Added/Updated ENTITY target '"..key.."' by '"..owner.."' @ "..formatCoord(coords))
|
||||||
|
return key
|
||||||
end
|
end
|
||||||
|
|
||||||
function removeEntityTarget(entity)
|
-- ZONE: createZoneTarget(data, opts, dist)
|
||||||
TextTargets[entity] = nil
|
-- Expect data[1] = id/name, data[2] = vec3 coords (as in your original)
|
||||||
|
function createZoneTarget(data, opts, dist)
|
||||||
|
startTargetLoop()
|
||||||
|
|
||||||
|
local owner = getOwnerResource()
|
||||||
|
local zname = tostring(data[1] or ("zone@"..vecKey(data[2] or vec3(0,0,0))))
|
||||||
|
local coords = data[2]
|
||||||
|
|
||||||
|
local buttontext = bakeButtons(opts)
|
||||||
|
TextTargets[zname] = {
|
||||||
|
_key = zname,
|
||||||
|
_type = "zone",
|
||||||
|
_owner = owner,
|
||||||
|
coords = coords,
|
||||||
|
buttontext = buttontext,
|
||||||
|
options = opts,
|
||||||
|
dist = dist,
|
||||||
|
}
|
||||||
|
updateCachedText(TextTargets[zname])
|
||||||
|
registerTarget(owner, zname)
|
||||||
|
-- print("^6Bridge^7:^5 Target^7: ^2Added/Updated ZONE target '"..zname.."' by '"..owner.."' @ "..formatCoord(coords))
|
||||||
|
return zname
|
||||||
end
|
end
|
||||||
|
|
||||||
function removeZoneTarget(target)
|
-- MODEL: createModelTarget(models, opts, dist, nameOpt?)
|
||||||
TextTargets[target] = nil
|
function createModelTarget(models, opts, dist, name)
|
||||||
|
startTargetLoop()
|
||||||
|
|
||||||
|
local owner = getOwnerResource()
|
||||||
|
if type(models) ~= "table" then models = { models } end
|
||||||
|
|
||||||
|
local key
|
||||||
|
if name then
|
||||||
|
key = tostring(name)
|
||||||
|
else
|
||||||
|
local parts = {}
|
||||||
|
for i, m in ipairs(models) do parts[i] = tostring(m) end
|
||||||
|
key = "model_" .. table.concat(parts, "_")
|
||||||
|
end
|
||||||
|
|
||||||
|
local buttontext = bakeButtons(opts)
|
||||||
|
TextTargets[key] = {
|
||||||
|
_key = key,
|
||||||
|
_type = "model",
|
||||||
|
_owner = owner,
|
||||||
|
models = models,
|
||||||
|
buttontext = buttontext,
|
||||||
|
options = opts,
|
||||||
|
dist = dist,
|
||||||
|
coords = vec3(0, 0, 0), -- will be updated by the refresher
|
||||||
|
}
|
||||||
|
updateCachedText(TextTargets[key])
|
||||||
|
registerTarget(owner, key)
|
||||||
|
-- print("^6Bridge^7:^5 Target^7: ^2Added/Updated MODEL target '"..key.."' by '"..owner.."' (models: "..table.concat(models, ",")..")")
|
||||||
|
return key
|
||||||
end
|
end
|
||||||
|
|
||||||
function removeModelTarget(model)
|
-- ===== Public API: Remove targets =====
|
||||||
TextTargets[model] = nil
|
-- Entity removal accepts entity handle or key string.
|
||||||
|
function removeEntityTarget(entityOrKey)
|
||||||
|
local key = nil
|
||||||
|
if type(entityOrKey) == "string" then
|
||||||
|
key = entityOrKey
|
||||||
|
elseif type(entityOrKey) == "number" then
|
||||||
|
for k, t in pairs(TextTargets) do
|
||||||
|
if t._type == "entity" and t.entity == entityOrKey then key = k break end
|
||||||
|
end
|
||||||
|
if not key then
|
||||||
|
-- Fallback: try coord-key match
|
||||||
|
local c = GetEntityCoords(entityOrKey)
|
||||||
|
local guess = "entity@"..vecKey(c)
|
||||||
|
if TextTargets[guess] then key = guess end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if key then removeTargetKey(key, "removeEntityTarget") end
|
||||||
|
end
|
||||||
|
|
||||||
|
function removeZoneTarget(key)
|
||||||
|
if not key then return end
|
||||||
|
removeTargetKey(key, "removeZoneTarget")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For models, pass the returned key from createModelTarget (recommended).
|
||||||
|
function removeModelTarget(key)
|
||||||
|
if not key then return end
|
||||||
|
removeTargetKey(key, "removeModelTarget")
|
||||||
end
|
end
|
||||||
|
|
||||||
exports("createEntityTarget", createEntityTarget)
|
exports("createEntityTarget", createEntityTarget)
|
||||||
@@ -143,22 +216,20 @@ exports("removeEntityTarget", removeEntityTarget)
|
|||||||
exports("removeZoneTarget", removeZoneTarget)
|
exports("removeZoneTarget", removeZoneTarget)
|
||||||
exports("removeModelTarget", removeModelTarget)
|
exports("removeModelTarget", removeModelTarget)
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
-- Fallback: DrawText3D Targets (Experimental)
|
-- Fallback: DrawText3D Targets (Experimental)
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
local started = false
|
local started = false
|
||||||
function startTargetLoop()
|
function startTargetLoop()
|
||||||
if started then return end
|
if started then return end
|
||||||
Config = {
|
|
||||||
System = {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
started = true
|
started = true
|
||||||
|
|
||||||
|
-- lazy include (unchanged from your original)
|
||||||
|
Config = { System = {} }
|
||||||
local fileLoader = assert(load(LoadResourceFile("jim_bridge", ('starter.lua')), ('@@jim_bridge/starter.lua')))
|
local fileLoader = assert(load(LoadResourceFile("jim_bridge", ('starter.lua')), ('@@jim_bridge/starter.lua')))
|
||||||
fileLoader()
|
fileLoader()
|
||||||
-- Model Entity Refresher
|
|
||||||
|
-- Model Entity Refresher (kept)
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while true do
|
||||||
local pedCoords = GetEntityCoords(PlayerPedId())
|
local pedCoords = GetEntityCoords(PlayerPedId())
|
||||||
@@ -178,7 +249,7 @@ function startTargetLoop()
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Main Target Loop
|
-- Main Target Loop (unchanged logic, just uses new TextTargets entries)
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while true do
|
||||||
local ped = PlayerPedId()
|
local ped = PlayerPedId()
|
||||||
@@ -215,10 +286,10 @@ function startTargetLoop()
|
|||||||
for i, opt in ipairs(target.options) do
|
for i, opt in ipairs(target.options) do
|
||||||
if IsControlJustPressed(0, opt.key) and isClosest then
|
if IsControlJustPressed(0, opt.key) and isClosest then
|
||||||
if (not target.canInteract or target.canInteract()) and
|
if (not target.canInteract or target.canInteract()) and
|
||||||
(not opt.item or hasItem(opt.item)) and
|
(not opt.item or hasItem(opt.item)) and
|
||||||
(not opt.job or hasJob(opt.job, nil)) then
|
(not opt.job or hasJob(opt.job, nil)) then
|
||||||
if opt.onSelect then opt.onSelect(targetEntity) end
|
if opt.onSelect then opt.onSelect(targetEntity) end
|
||||||
if opt.action then opt.action(targetEntity) end
|
if opt.action then opt.action(targetEntity) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -228,8 +299,8 @@ function startTargetLoop()
|
|||||||
|
|
||||||
for i, opt in ipairs(target.options) do
|
for i, opt in ipairs(target.options) do
|
||||||
if (not target.canInteract or target.canInteract()) and
|
if (not target.canInteract or target.canInteract()) and
|
||||||
(not opt.item or hasItem(opt.item)) and
|
(not opt.item or hasItem(opt.item)) and
|
||||||
(not opt.job or hasJob(opt.job, nil)) then
|
(not opt.job or hasJob(opt.job, nil)) then
|
||||||
DrawText3D(vec3(target.coords.x, target.coords.y, baseZ + lineHeight * lineOffset), target.buttontext[i], isClosest)
|
DrawText3D(vec3(target.coords.x, target.coords.y, baseZ + lineHeight * lineOffset), target.buttontext[i], isClosest)
|
||||||
lineOffset = lineOffset + 1
|
lineOffset = lineOffset + 1
|
||||||
end
|
end
|
||||||
@@ -238,12 +309,11 @@ function startTargetLoop()
|
|||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
|
|
||||||
Wait(1) -- Throttled
|
Wait(1)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function DrawText3D(coord, text, highlight)
|
function DrawText3D(coord, text, highlight)
|
||||||
SetTextScale(0.30, 0.30)
|
SetTextScale(0.30, 0.30)
|
||||||
SetTextFont(0)
|
SetTextFont(0)
|
||||||
@@ -253,37 +323,28 @@ function DrawText3D(coord, text, highlight)
|
|||||||
SetTextCentre(true)
|
SetTextCentre(true)
|
||||||
|
|
||||||
local totalLength = string.len(text)
|
local totalLength = string.len(text)
|
||||||
local textMaxLength = 99 -- max 99
|
local textMaxLength = 99
|
||||||
local text = totalLength > textMaxLength and text:sub(1, totalLength - (totalLength - textMaxLength)) or text
|
local txt = totalLength > textMaxLength and text:sub(1, textMaxLength) or text
|
||||||
AddTextComponentString(highlight and text:gsub("%~w~", "~y~") or text)
|
AddTextComponentString(highlight and txt:gsub("%~w~", "~y~") or txt)
|
||||||
SetDrawOrigin(coord.x, coord.y, coord.z, 0)
|
SetDrawOrigin(coord.x, coord.y, coord.z, 0)
|
||||||
DrawText(0.0, 0.0)
|
DrawText(0.0, 0.0)
|
||||||
local count, length = GetLineCountAndMaxLength(text)
|
local count, length = GetLineCountAndMaxLength(txt)
|
||||||
|
|
||||||
local padding = 0.005
|
local padding = 0.005
|
||||||
local heightFactor = (count / 43) + padding
|
local heightFactor = (count / 43) + padding
|
||||||
local weightFactor = (length / 150) + padding
|
local weightFactor = (length / 150) + padding
|
||||||
|
|
||||||
local height = (heightFactor / 2) - padding / 1
|
local height = (heightFactor / 2) - padding / 1
|
||||||
local width = (weightFactor / 2) - padding / 1
|
local width = (weightFactor / 2) - padding / 1
|
||||||
|
|
||||||
DrawRect(0.0, height, width, heightFactor, 0, 0, 0, 150)
|
DrawRect(0.0, height, width, heightFactor, 0, 0, 0, 150)
|
||||||
ClearDrawOrigin()
|
ClearDrawOrigin()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculates the number of lines and the maximum line length from the given text.
|
|
||||||
---
|
|
||||||
--- @param text string The text to analyze.
|
|
||||||
--- @return number, number The line count and maximum line length.
|
|
||||||
---
|
|
||||||
--- @usage
|
|
||||||
--- ```lua
|
|
||||||
--- local count, maxLen = GetLineCountAndMaxLength("Hello World")
|
|
||||||
--- ```
|
|
||||||
function GetLineCountAndMaxLength(text)
|
function GetLineCountAndMaxLength(text)
|
||||||
local lineCount, maxLength = 0, 0
|
local lineCount, maxLength = 0, 0
|
||||||
for line in text:gmatch("[^\n]+") do
|
for line in text:gmatch("[^\n]+") do
|
||||||
lineCount += 1
|
lineCount = lineCount + 1
|
||||||
local lineLength = string.len(line)
|
local lineLength = string.len(line)
|
||||||
if lineLength > maxLength then
|
if lineLength > maxLength then
|
||||||
maxLength = lineLength
|
maxLength = lineLength
|
||||||
@@ -293,7 +354,6 @@ function GetLineCountAndMaxLength(text)
|
|||||||
return lineCount, maxLength
|
return lineCount, maxLength
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function RotationToDirection(rot)
|
function RotationToDirection(rot)
|
||||||
local adjust = math.pi / 180
|
local adjust = math.pi / 180
|
||||||
return vec3(
|
return vec3(
|
||||||
@@ -311,8 +371,3 @@ function normalizeVector(vec)
|
|||||||
return vec3(0, 0, 0)
|
return vec3(0, 0, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Helper to update cached text.
|
|
||||||
function updateCachedText(target)
|
|
||||||
target.text = table.concat(target.buttontext, "\n")
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user