diff --git a/shared/_loaders.lua b/shared/_loaders.lua
index 78f87b4..07c617a 100644
--- a/shared/_loaders.lua
+++ b/shared/_loaders.lua
@@ -1,81 +1,101 @@
---- Executes a function when the player character is loaded into the game.
----
---- This function sets up event listeners for player loading based on the game framework detected (e.g., QB, ESX, OX).
----
---- If `onStart` is `true`, it will also attempt to execute the function on resource start after ensuring the player is logged in. (Helpful for debugging)
+--[[
+ Player & Resource Event Utility Functions
+ -------------------------------------------
+ This module provides functions to:
+ • Execute code when the player character is loaded or unloaded.
+ • Execute code on resource start and stop.
+ • Wait for the player to be logged in before proceeding.
+]]
+
+-------------------------------------------------------------
+-- Player Loaded and Unloaded Events
+-------------------------------------------------------------
+
+--- Executes a function when the player character is loaded.
+--- If onStart is true, the function will also run on resource start (after ensuring the player is logged in).
---
--- @param func function The function to execute when the player is loaded.
---- @param onStart boolean (optional) If `true`, the function will also execute on resource start. Default is `false`.
----
+--- @param onStart boolean (optional) If true, also execute on resource start. Default is false.
--- @usage
--- ```lua
--- onPlayerLoaded(function()
---- -- Your code here
+--- print("Player logged in")
+--- -- Your initialization code here.
--- end, true)
--- ```
function onPlayerLoaded(func, onStart)
- local onPlayerName = ""
+ local onPlayerFramework = ""
local loaded = false
+
if onStart then
onResourceStart(function()
- if not LocalPlayer.state.isLoggedIn then
- Wait(3000)
- if not LocalPlayer.state.isLoggedIn then -- If the player is not logged in after waiting, skip execution
- return
- end
- end
- loaded = true -- Mark as already loaded
- debugPrint("^6Bridge^7: ^2Loading ^3onResourceStart^7() ^2through ^3onPlayerLoaded^7()")
+ if not waitForLogin() then return end
+
+ loaded = true
+ debugPrint("^6Bridge^7: ^3onResourceStart^7()^2 executed through ^3onPlayerLoaded^7()")
Wait(2000)
func()
end, true)
end
+
if not loaded then
local tempFunc = function()
- debugPrint("^6Bridge^7: ^2Executing ^3onPlayerLoaded^7()")
+ debugPrint("^6Bridge^7: ^2Executing onPlayerLoaded")
func()
end
- if isStarted(QBExport) or isStarted(QBXExport) then onPlayerName = QBExport
+
+ if isStarted(QBExport) or isStarted(QBXExport) then
+ onPlayerFramework = QBExport
AddEventHandler('QBCore:Client:OnPlayerLoaded', tempFunc)
- elseif isStarted(ESXExport) then onPlayerName = ESXExport
+ elseif isStarted(ESXExport) then
+ onPlayerFramework = ESXExport
AddEventHandler('esx:playerLoaded', tempFunc)
- elseif isStarted(OXCoreExport) then onPlayerName = OXCoreExport
+ elseif isStarted(OXCoreExport) then
+ onPlayerFramework = OXCoreExport
AddEventHandler('ox:playerLoaded', tempFunc)
end
- if onPlayerName ~= "" then
- debugPrint("^6Bridge^7: ^2Registering ^3onPlayerLoaded^2 with ^7"..onPlayerName)
+
+ if onPlayerFramework ~= "" then
+ debugPrint("^6Bridge^7: ^2Registering ^3onPlayerLoaded^7()^2 with ^3" .. onPlayerFramework.."^7")
else
- print("^4ERROR^7: ^2No Core detected for onPlayerLoaded ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: No supported core detected for onPlayerLoaded - Check exports.lua")
end
end
end
---trying to add unload functions for when players switch ped
+--- Executes a function when the player character is unloaded.
+--- @param func function The function to execute when the player unloads.
+--- @usage
+--- ```lua
+--- onPlayerUnload(function()
+--- print("Player has logged out of their character")
+--- -- Your cleanup code here.
+--- end)
+--- ```
function onPlayerUnload(func)
- AddEventHandler('QBCore:Client:OnPlayerUnload', function()
- func()
- end)
- AddEventHandler('ox:playerLogout', function()
- func()
- end)
+ AddEventHandler('QBCore:Client:OnPlayerUnload', function() func() end)
+ AddEventHandler('ox:playerLogout', function() func() end)
+
+ --AddEventHandler('esx:playerLogout', function() func() end)
+ -- ^ Only server side for now, need a way to send it to client if not already available
end
+-------------------------------------------------------------
+-- Resource Start and Stop Events
+-------------------------------------------------------------
--- Executes a function when the resource starts.
----
---- This function wraps the `onResourceStart` event, allowing you to execute code when the resource starts.
----
---- @param func function The function to execute on resource start.
---- @param thisScript boolean (optional) If `true`, only runs the function when this resource starts. Default is `true`.
----
+--- @param func function The function to execute.
+--- @param thisScript boolean (optional) If true, only runs when this resource starts (default true).
--- @usage
--- ```lua
--- onResourceStart(function()
---- -- Your code here
+--- print("Script ensured")
+--- -- Initialization code on resource start.
--- end, true)
--- ```
function onResourceStart(func, thisScript)
- debugPrint("^6Bridge^7: ^2Registering ^3onResourceStart^2")
+ debugPrint("^6Bridge^7: Registering ^3onResourceStart^7()")
AddEventHandler('onResourceStart', function(resourceName)
if getScript() == resourceName and (thisScript or true) then
func()
@@ -84,20 +104,16 @@ function onResourceStart(func, thisScript)
end
--- Executes a function when the resource stops.
----
---- This function wraps the `onResourceStop` event, allowing you to execute code when the resource stops.
----
---- @param func function The function to execute on resource stop.
---- @param thisScript boolean (optional) If `true`, only runs the function when this resource stops. Default is `true`.
----
+--- @param func function The function to execute.
+--- @param thisScript boolean (optional) If true, only runs when this resource stops (default true).
--- @usage
--- ```lua
--- onResourceStop(function()
---- -- Cleanup code here
+--- -- Cleanup code here.
--- end, true)
--- ```
function onResourceStop(func, thisScript)
- debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^2")
+ debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^7()")
AddEventHandler('onResourceStop', function(resourceName)
if getScript() == resourceName and (thisScript or true) then
func()
@@ -105,17 +121,41 @@ function onResourceStop(func, thisScript)
end)
end
---- Waits until the player is logged in before continuing execution.
----
---- This function blocks execution until `LocalPlayer.state.isLoggedIn` is `true`.
----
----@usage
---- ```lua
+-------------------------------------------------------------
+-- Wait for Login
+-------------------------------------------------------------
+
+--- Blocks execution until the player is logged in.
+--- @usage
--- waitForLogin()
---- ```
function waitForLogin()
- while not LocalPlayer.state.isLoggedIn do
- debugPrint("Waiting")
- Wait(100)
+ local timeout = 10000 -- 10 seconds in milliseconds
+ local startTime = GetGameTimer()
+ local loggedIn = false
+
+ if isStarted(ESXExport) then
+ debugPrint("^6Bridge^7: ^3ESX waitForLogin^7() ^2running^7")
+ while (GetGameTimer() - startTime) < timeout do
+ local playerData = ESX.GetPlayerData()
+ if playerData and playerData.job then
+ loggedIn = true
+ break
+ end
+ Wait(100)
+ end
+ else
+ -- For other frameworks, use LocalPlayer.state.isLoggedIn.
+ while not LocalPlayer.state.isLoggedIn and (GetGameTimer() - startTime) < timeout do
+ Wait(100)
+ end
+ loggedIn = LocalPlayer.state.isLoggedIn
end
-end
\ No newline at end of file
+
+ if not loggedIn then
+ print("^4Error^7: ^2Timeout reached while waiting for player login^7.")
+ return false
+ else
+ debugPrint("^6Bridge^7: ^2Player Login Detected^7.")
+ return true
+ end
+end
diff --git a/shared/versioncheck.lua b/shared/_versioncheck.lua
similarity index 100%
rename from shared/versioncheck.lua
rename to shared/_versioncheck.lua
diff --git a/shared/contextmenus.lua b/shared/contextmenus.lua
index d6a35cc..4bc0778 100644
--- a/shared/contextmenus.lua
+++ b/shared/contextmenus.lua
@@ -1,25 +1,37 @@
+--[[
+ Menu Opening Module
+ ---------------------
+ This module provides a unified function to open menus using the configured menu system.
+ Supported systems include:
+ • jim-nui (kinda)
+ • ox (or ox_context)
+ • qb (using QBMenuExport)
+ • gta (using WarMenu)
+ • esx (using ESX.UI.Menu)
+]]
+
--- 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`.
---
---@param Menu table A table containing the menu options to display.
--- Each menu item can include:
---- - **header** (`string`): The text to display for the menu item.
---- - **txt** (`string`, optional): Additional text or description.
---- - **icon** (`string`, optional): Icon to display with the menu item.
---- - **onSelect** (`function`, optional): Function to execute when the menu item is selected.
---- - **arrow** (`boolean`, optional): Whether to display an arrow next to the item (for certain menus).
---- - **params** (`table`, optional): Additional parameters, such as events and arguments.
---- - **isMenuHeader** (`boolean`, optional): Marks the item as a header.
---- - **disabled** (`boolean`, optional): Disables the menu item if `true`.
+--- - header (`string`): The text to display for the menu item.
+--- - txt (`string`, optional): Additional text or description.
+--- - icon (`string`, optional): Icon to display with the menu item.
+--- - onSelect (`function`, optional): Function to execute when the menu item is selected.
+--- - arrow (`boolean`, optional): Whether to display an arrow next to the item (for certain menus).
+--- - params (`table`, optional): Additional parameters, such as events and arguments.
+--- - isMenuHeader (`boolean`, optional): Marks the item as a header.
+--- - disabled (`boolean`, optional): Disables the menu item if `true`.
---
---@param data table A table containing configuration data for the menu.
---- - **header** (`string`): The header/title of the menu.
---- - **headertxt** (`string`, optional): Additional header text.
---- - **onBack** (`function`, optional): Function to call when the "Return" option is selected.
---- - **onExit** (`function`, optional): Function to call when the menu is exited.
---- - **onSelected** (`function`, optional): Function to call when a menu item is selected (for certain menu systems).
---- - **canClose** (`boolean`, optional): Whether the menu can be closed by the user.
+--- - header (`string`): The header/title of the menu.
+--- - headertxt (`string`, optional): Additional header text.
+--- - onBack (`function`, optional): Function to call when the "Return" option is selected.
+--- - onExit (`function`, optional): Function to call when the menu is exited.
+--- - onSelected (`function`, optional): Function to call when a menu item is selected (for certain menu systems).
+--- - canClose (`boolean`, optional): Whether the menu can be closed by the user.
---
---@usage
--- ```lua
@@ -36,6 +48,7 @@
--- ```
function openMenu(Menu, data)
if Config.System.Menu == "jim" then
+ -- Insert "Return" option if onBack is defined.
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
@@ -65,6 +78,7 @@ function openMenu(Menu, data)
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
@@ -75,6 +89,7 @@ function openMenu(Menu, data)
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 {}
@@ -143,17 +158,10 @@ function openMenu(Menu, data)
end
for k in pairs(Menu) do
if not Menu[k].params or not Menu[k].params.event then
- if Menu[k].onSelect then
- Menu[k].params = {
- isAction = true,
- event = Menu[k].onSelect,
- }
- else
- Menu[k].params = {
- isAction = true,
- event = function() end,
- }
- end
+ 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
@@ -162,15 +170,12 @@ function openMenu(Menu, data)
exports[QBMenuExport]:openMenu(Menu)
elseif Config.System.Menu == "gta" then
- WarMenu.CreateMenu(tostring(Menu),
- data.header,
- data.headertxt or " ",
- {
- titleColor = { 222, 255, 255 },
- maxOptionCountOnScreen = 15,
- width = 0.25,
- x = 0.7,
- })
+ WarMenu.CreateMenu(tostring(Menu), data.header, data.headertxt or " ", {
+ titleColor = { 222, 255, 255 },
+ maxOptionCountOnScreen = 15,
+ width = 0.25,
+ x = 0.7,
+ })
if WarMenu.IsAnyMenuOpened() then return end
WarMenu.OpenMenu(tostring(Menu))
CreateThread(function()
@@ -239,7 +244,6 @@ function openMenu(Menu, data)
onSelect = data.onBack,
})
end
-
ESX.UI.Menu.Open("default", getScript(), "Example_Menu", {
title = data.header,
align = 'top-right',
@@ -260,15 +264,11 @@ function openMenu(Menu, data)
end
end
---- A line break constant used for formatting menu headers.
+--- A line break constant used for menu header formatting.
br = (Config.System.Menu == "ox" or Config.System.Menu == "gta") and "\n" or "
"
---- Checks if the menu system is classified as 'ox' or 'gta'.
----
---- This function is used to decide how to make line breaks in menu headers.
----
---- @return boolean Returns `true` if the menu system is 'ox' or 'gta'; otherwise, `false`.
----
+--- 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.
--- @usage
--- ```lua
--- if isOx() then
@@ -280,7 +280,7 @@ function isOx() return (Config.System.Menu == "ox" or Config.System.Menu == "gta
--- Checks if any WarMenu menu is currently open.
---
---- @return boolean Returns `true` if a WarMenu menu is open; otherwise, `false`.
+--- @return boolean boolean Returns `true` if a WarMenu menu is open; otherwise, `false`.
---
--- @usage
--- ```lua
diff --git a/shared/coreloader.lua b/shared/coreloader.lua
index 174e5bd..331c02a 100644
--- a/shared/coreloader.lua
+++ b/shared/coreloader.lua
@@ -1,32 +1,63 @@
--- Create empty Variables --
+--[[
+ Resource Initialization Module
+ --------------------------------
+ This module initializes and loads shared data (Items, Vehicles, Jobs, Gangs) from the
+ various frameworks/inventory systems (OX, QB, ESX, etc.). It also corrects export names,
+ caches framework exports into simple variables, and prints debug information if enabled.
+]]
+
+-------------------------------------------------------------
+-- Global Variable Initialization
+-------------------------------------------------------------
Items, Vehicles, Jobs, Gangs, Core, ESX = {}, nil, nil, nil, nil, nil
--- Correct QB inventory export (if needed) from 'qb-inventory' to 'ps-inventory' or 'lj-inventory' --
+-------------------------------------------------------------
+-- Correct QB Inventory Export
+-------------------------------------------------------------
+-- Ensure that the QB inventory export is corrected from 'qb-inventory' to 'ps-inventory' or 'lj-inventory' if needed.
Exports.QBInv = (isStarted("ps-inventory") and "ps-inventory") or (isStarted("lj-inventory") and "lj-inventory") or Exports.QBInv
--- Create simple variables based on the corresponding framework exports --
-OXLibExport, QBXExport, QBExport, ESXExport, OXCoreExport = Exports.OXLibExport or "", Exports.QBXExport or "", Exports.QBExport or "", Exports.ESXExport or "", Exports.OXCoreExport or ""
+-------------------------------------------------------------
+-- Framework Exports and Inventory Identifiers
+-------------------------------------------------------------
+OXLibExport, QBXExport, QBExport, ESXExport, OXCoreExport =
+ Exports.OXLibExport or "",
+ Exports.QBXExport or "",
+ Exports.QBExport or "",
+ Exports.ESXExport or "",
+ Exports.OXCoreExport or ""
--- Create simple variables based on the corresponding inventory names --
-OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv = Exports.OXInv or "", Exports.QBInv or "", Exports.PSInv or "", Exports.QSInv or "", Exports.CoreInv or "", Exports.CodeMInv or "", Exports.OrigenInv or ""
+OXInv, QBInv, PSInv, QSInv, CoreInv, CodeMInv, OrigenInv =
+ Exports.OXInv or "",
+ Exports.QBInv or "",
+ Exports.PSInv or "",
+ Exports.QSInv or "",
+ Exports.CoreInv or "",
+ Exports.CodeMInv or "",
+ Exports.OrigenInv or ""
--- QB-Menu export name grabbed from exports.lua --
QBMenuExport = Exports.QBMenuExport or ""
-
--- Target exports based on what is loaded --
QBTargetExport, OXTargetExport = Exports.QBTargetExport or "", Exports.OXTargetExport or ""
--- If Debug mode is on in the loading script, print the list of found exports --
--- Some may "lie", 'ox_target' attempts to use 'qb-target' exports and this print will say its loaded (which is technically true) --
+-------------------------------------------------------------
+-- Debug: Print Found Exports
+-------------------------------------------------------------
+-- Print a list of all exports that are currently started (if debugMode is enabled).
for _, v in pairs(Exports) do
- if isStarted(v) then debugPrint("^6Bridge^7: '^3"..v.."^7' ^2export found ^7") end
+ if isStarted(v) then
+ debugPrint("^6Bridge^7: '^3"..v.."^7' export found")
+ end
end
+-------------------------------------------------------------
+-- Resource Variables for Items, Jobs, and Vehicles
+-------------------------------------------------------------
local itemResource, jobResource, vehResource = "", "", ""
--- Load item lists --
--- Complies the items from ox_inventory, qb-core or esx into 'Items' and loads them in a layout similar to qb-core's Shared items.lua --
--- For example this makes it so instead of QBCore.Shared.Items[item], you can load 'Item[item]' in the script --
+-------------------------------------------------------------
+-- Loading Items
+-------------------------------------------------------------
+-- Load and compile shared items from the detected inventory system.
if isStarted(OXInv) then
itemResource = OXInv
Items = exports[OXInv]:Items()
@@ -54,14 +85,13 @@ elseif isStarted(QBExport) then
elseif isStarted(ESXExport) then
itemResource = ESXExport
ESX = exports[ESXExport]:getSharedObject()
- --Items = ESX and ESX.Items or nil
while ESX == nil do
print("Waiting for ESX")
Wait(0)
end
if isServer() then
Items = ESX.GetItems()
- debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7" .. itemResource)
+ debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7"..itemResource)
end
CreateThread(function()
while not ESX do Wait(0) end
@@ -72,25 +102,23 @@ elseif isStarted(ESXExport) then
end
if not isServer() then
Items = triggerCallback(getScript()..":getItems")
- debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7" .. itemResource)
+ debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7"..itemResource)
end
end)
-
-
end
--- If it fails to load items, then it will print the error below --
--- If it loads them and debug is on, print how many items and where from --
+
if not isStarted(ESXExport) then
if not Items then
- print("^4ERROR^7: ^2No Core Items detected ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Core Items detected ^7- ^2Check ^3starter^1.^2lua^7")
else
- debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7" .. itemResource)
+ debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Items).." ^3Items^2 from ^7"..itemResource)
end
end
--- Load Vehicles --
--- Complies the vehicles from the detected frameworks into a table in the style of qb-cores shared vehicles.lua --
--- For example, instead of using QBCore.Shared.Vehicles[vehicle] you can load 'Vehicles[vehicle]' in the script --
+-------------------------------------------------------------
+-- Loading Vehicles
+-------------------------------------------------------------
+-- Compile vehicles from the detected frameworks into a unified table.
if isStarted(QBXExport) or isStarted(QBExport) then
Core = Core or exports[QBExport]:GetCoreObject()
Vehicles = Core and Core.Shared.Vehicles
@@ -101,15 +129,15 @@ if isStarted(QBXExport) or isStarted(QBExport) then
end)
end
vehResource = QBExport
+
elseif isStarted(OXCoreExport) then
Vehicles = {}
for k, v in pairs(Ox.GetVehicleData()) do
Vehicles[k] = { model = k, price = v.price, name = v.name, brand = v.make }
end
vehResource = OXCoreExport
+
elseif isStarted(ESXExport) then
- -- print("^6Bridge^7: ^2Loading ^3Vehicles^2 from ^7"..ESXExport)
- -- print("^6Bridge^7: ^2Loading ^6"..countTable(Vehicles).." ^3Vehicles^2 from ^7"..ESXExport)
CreateThread(function()
if isServer() then
createCallback(getScript()..":getVehiclesPrices", function(source)
@@ -122,43 +150,56 @@ elseif isStarted(ESXExport) then
local TempVehicles = triggerCallback(getScript()..":getVehiclesPrices")
for _, v in pairs(TempVehicles) do
Vehicles = Vehicles or {}
- Vehicles[v.model] = { model = v.model, price = v.price, name = v.name, brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper) }
+ Vehicles[v.model] = {
+ model = v.model,
+ price = v.price,
+ name = v.name,
+ brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)
+ }
end
end
end)
end
+
if vehResource == nil then
- print("^4ERROR^7: ^2No Vehicle info detected ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Vehicle info detected ^7- ^2Check ^3starter^1.^2lua^7")
else
debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Vehicles).." ^3Vehicles^2 from ^7"..vehResource)
end
--- Load Jobs --
--- Attempts to load the details of jobs and gangs and compile into tables --
--- For example, instead of using QBCore.Shared.Jobs[job] you can load 'Jobs[job]' in the script --
-if isStarted(QBXExport) then jobResource = QBXExport
+-------------------------------------------------------------
+-- Loading Jobs and Gangs
+-------------------------------------------------------------
+-- Compile jobs and gangs from the detected framework.
+if isStarted(QBXExport) then
+ jobResource = QBXExport
Core = Core or exports[QBExport]:GetCoreObject()
Jobs, Gangs = exports[QBXExport]:GetJobs(), exports[QBXExport]:GetGangs()
-elseif isStarted(OXCoreExport) then jobResource = OXExport
+elseif isStarted(OXCoreExport) then
+ jobResource = OXExport
CreateThread(function()
if isServer() then
createCallback(getScript()..":getOxGroups", function(source)
- Jobs = MySQL.query.await('SELECT * FROM `ox_groups`') return Jobs
+ Jobs = MySQL.query.await('SELECT * FROM `ox_groups`')
+ return Jobs
end)
else
local TempJobs = triggerCallback(getScript()..":getOxGroups")
Jobs = TempJobs or {}
for k, v in pairs(TempJobs) do
local grades = {}
- for i = 1, #v.grades do grades[i] = { name = v.grades[i], isboss = (i == #v.grades)} end
+ for i = 1, #v.grades do
+ grades[i] = { name = v.grades[i], isboss = (i == #v.grades) }
+ end
Jobs[v.name] = { label = v.label, grades = grades }
end
Gangs = Jobs
end
end)
-elseif isStarted(QBExport) then jobResource = QBExport
+elseif isStarted(QBExport) then
+ jobResource = QBExport
Core = Core or exports[QBExport]:GetCoreObject()
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
if isStarted(QBExport) and not isStarted(QBXExport) then
@@ -169,12 +210,11 @@ elseif isStarted(QBExport) then jobResource = QBExport
end
elseif isStarted(ESXExport) then
- --print("^6Bridge^7: ^2Loading ^3Jobs^7/^3Gangs^2 from ^7"..ESXExport)
ESX = exports[ESXExport]:getSharedObject()
if isServer() then
Jobs = ESX.GetJobs()
for k, v in pairs(Jobs) do
- local count = countTable(Jobs[k].grades)-1
+ local count = countTable(Jobs[k].grades) - 1
Jobs[k].grades[tostring(count)].isBoss = true
end
Gangs = Jobs
@@ -192,6 +232,8 @@ elseif isStarted(ESXExport) then
end
end)
end
+
if not isStarted(ESXExport) and Jobs then
- debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Jobs).." ^3Jobs^2 from ^7"..jobResource, "^6Bridge^7: ^2Loading ^6"..countTable(Gangs).." ^3Gangs^2 from ^7"..jobResource)
-end
\ No newline at end of file
+ debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Jobs).." ^3Jobs^2 from ^7"..jobResource)
+ debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Gangs).." ^3Gangs^2 from ^7"..jobResource)
+end
diff --git a/shared/crafting.lua b/shared/crafting.lua
index b96a1fa..a9cb3d8 100644
--- a/shared/crafting.lua
+++ b/shared/crafting.lua
@@ -1,24 +1,34 @@
-local CraftLock = false
+--[[
+ Crafting, Selling, and Shop Module
+ -----------------------------------
+ This module provides functions for opening crafting menus, handling multi-crafting,
+ performing the crafting process (with animations and progress bars), selling items,
+ and opening shop interfaces. It integrates with various inventory and menu systems,
+ and uses server callbacks to check item carry capacity.
+]]
---- Opens a crafting menu based on the provided data.
+-------------------------------------------------------------
+-- Global Variables
+-------------------------------------------------------------
+CraftLock = false
+
+-------------------------------------------------------------
+-- Crafting Menu
+-------------------------------------------------------------
+
+--- Opens the crafting menu based on provided data.
+--- Checks job restrictions, builds the recipe menu, and opens the menu.
---
---- This function checks job requirements, prepares the menu options, and opens the crafting menu.
---- It handles item availability, crafting recipes, and displays appropriate icons and labels.
+--- @param data table Crafting menu configuration containing:
+--- - craftable (`table`) Table with Header, Recipes, Anims, and (optionally) craftedItems.
+--- - coords (`vector3`) The coordinates where the crafting menu is being opened.
+--- - stashTable|stashName (`string\table`) Name(s) of the stash for checking item availability.
+--- - job|gang (`string`) Job or gang requirements.
+--- - onBack (optional): Function to call when returning.
---
----@param data table A table containing crafting menu data.
---- - **craftable** (`table`): The crafting options and settings.
---- - **Header** (`string`): The header/title of the crafting menu.
---- - **Recipes** (`table`): A list of crafting recipes.
---- - **coords** (`vector3`): The coordinates where the crafting menu is being opened.
---- - **stashTable** (`string` or `table`, optional): The stash name(s) to check for item availability.
---- - **stashName** (`string` or `table`, optional): Alias for `stashTable`.
---- - **job** (`string` or `table`, optional): Job(s) required to access the crafting menu.
---- - **gang** (`string` or `table`, optional): Gang(s) required to access the crafting menu.
---- - **onBack** (`function`, optional): Function to call when returning from the menu.
----
----@usage
+--- @usage
--- ```lua
---- craftingMenu({
+---craftingMenu({
--- craftable = {
--- Header = "Weapon Crafting",
--- Recipes = {
@@ -34,158 +44,103 @@ local CraftLock = false
--- },
--- },
--- coords = vector3(100.0, 200.0, 300.0),
---- stashTable = "crafting_stash",
---- job = "mechanic", -- Optional
---- onBack = function() print("Returning to previous menu") end,
---- })
---- ```
+---stashTable = "crafting_stash",
+--- job = "mechanic",
+--- onBack = function() print("Returning to previous menu") end,
+---})
function craftingMenu(data)
- -- Prevent opening the menu if crafting is locked.
if CraftLock then return end
- -- If a job or gang restriction exists and the player doesn't pass the job check, exit early.
+ -- Job or gang check; exit if not authorized.
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
- -- Display a temporary "thinking" notification/menu depending on the configured system.
+ -- Display a temporary "thinking" notification.
if Config.System.Menu == "jim" then
triggerNotify(nil, "Thinking", "info")
else
openMenu({ { header = "Thinking...", icon = "fas fa-hourglass-end", isMenuHeader = true } }, { header = "Crafting Menu" } )
end
- -- Normalize stash name: if stashTable is provided, assign it to stashName.
+ -- Normalize stash name.
data.stashName = data.stashTable or data.stashName
- -- Initialize an empty menu table and a flag for job verification.
- local Menu, hasjob = {}, false
- -- Get the list of recipes from the provided data.
+ local Menu = {}
local Recipes = data.craftable.Recipes
-
local craftedItems = {}
-
- -- Create a temporary table to collect required item amounts for each recipe.
local tempCarryTable = {}
+
+ -- Build a table of all required ingredients (default quantity is 1).
for i = 1, #Recipes do
- -- Iterate over each key in the current recipe.
for k in pairs(Recipes[i]) do
if k == "hasCrafted" and not data.craftable.craftedItems then
craftedItems = GetMetadata(nil, "craftedItems") or {}
data.craftable.craftedItems = craftedItems
end
- -- Ignore meta keys: "amount", "metadata", "job", and "gang".
if k ~= "amount" and k ~= "metadata" and k ~= "job" and k ~= "gang" then
- -- Record the required amount for this ingredient (default to 1 if not specified).
tempCarryTable[k] = Recipes[i].amount or 1
end
end
end
- -- Trigger a server callback to check if the player can carry the required items.
+ -- Check if the player can carry the required items (server callback).
local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
-
- -- Process each recipe to build the menu entries.
+ -- Process each recipe to create menu entries.
for i = 1, #Recipes do
- -- Ensure the recipe has an "amount" field; default to 1 if missing.
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
-
- -- Loop through each key-value pair in the recipe.
- for k, v in pairs(Recipes[i]) do
- -- Skip meta keys that are not ingredients.
+ for k, _ in pairs(Recipes[i]) do
local excludeKeys = {
- amount = true,
- metadata = true,
- description = true,
- info = true,
- job = true,
- gang = true,
- oneUse = true,
- slot = true,
- blueprintRef = true,
- craftingLevel = true,
- craftedItems = true,
- hasCrafted = true,
- exp = true,
+ amount = true, metadata = true, description = true, info = true,
+ job = true, gang = true, oneUse = true, slot = true,
+ blueprintRef = true, craftingLevel = true, craftedItems = true,
+ hasCrafted = true, exp = true, anim = true, time = true,
}
-
if not excludeKeys[k] then
-
- -- Check job requirements if specified for the recipe.
+ local hasjob = true
if Recipes[i].job then
for l, b in pairs(Recipes[i].job) do
- -- hasJob returns true if the player meets the job criteria.
hasjob = hasJob(l, nil, b)
- if hasjob == true then break end
+ if hasjob then break end
end
- else
- hasjob = true
end
-
- -- Initialize variables for menu display text, disable flag, and any metadata.
- local setheader, settext, disable, metadata = "", "", false, (Recipes[i]["metadata"] or Recipes[i]["info"] or nil)
-
if hasjob then
- -- Build tables for ingredient details.
+ local setheader, settext, disable, metadata = "", "", false, (Recipes[i]["metadata"] or Recipes[i]["info"] or nil)
local itemTable = {}
local metaTable = {}
-
- -- Iterate over the ingredients for the current key.
+ -- Build ingredient details.
for l, b in pairs(Recipes[i][tostring(k)]) do
- -- Append item label and quantity to the settext string.
- -- Use a line break (br) if settext is not empty.
settext = settext..(settext ~= "" and br or "")..(Items[l] and Items[l].label or "error - "..l)..(b > 1 and " x"..b or "")
- -- Populate the metaTable with item labels and their amounts.
metaTable[Items[l] and Items[l].label or "error - "..l] = b
- -- Build a simple table of items required.
itemTable[l] = b
- Wait(0) -- Yield to avoid freezing the game.
+ Wait(0)
end
- -- Wait until the server callback (canCarryTable) has returned.
while not canCarryTable do Wait(0) end
-
- -- Determine if the recipe should be disabled by checking if the player has the required items.
disable = not checkHasItem(data.stashName, itemTable)
+ setheader = ((metadata and metadata.label) or (Items[tostring(k)] and Items[tostring(k)].label) or "error - "..tostring(k))
+ ..(Recipes[i]["amount"] > 1 and " x"..Recipes[i]["amount"] or "")
- -- Construct the header text for this menu item using metadata or default item label.
- setheader = ((metadata and metadata.label) or (Items[tostring(k)] and Items[tostring(k)].label) or "error - " .. tostring(k))
- .. (Recipes[i]["amount"] > 1 and " x" .. Recipes[i]["amount"] or "")
-
- -- Append an emoji to indicate carry status:
- -- If not disabled and the player cannot carry the item, append 📦,
- -- otherwise append ✔️ if they can carry it.
- -- if jim-crafting and its a blueprint item that has/hasnt been crafting prefix with ✨ to represent its a new item
if not disable then
if not canCarryTable[k] then
- setheader = setheader .. " 📦"
+ setheader = setheader.." 📦"
else
- setheader = setheader .. " ✔️"
+ setheader = setheader.." ✔️"
end
elseif not canCarryTable[k] then
- setheader = setheader .. " 📦"
+ setheader = setheader.." 📦"
end
- if Recipes[i]["hasCrafted"] ~= nil then
- if craftedItems[k] == nil then
- setheader = "✨ "..setheader
- end
+ if Recipes[i]["hasCrafted"] ~= nil and craftedItems[k] == nil then
+ setheader = "✨ "..setheader
end
- -- Add the constructed menu item into the Menu table.
+
Menu[#Menu + 1] = {
- -- Show an arrow if the item is enabled and can be carried.
arrow = not disable and canCarryTable[k],
- -- Disable the menu item based on the state of QBMenuExport and carry-check.
isMenuHeader = disable or not canCarryTable[k],
- -- Set icon and image for the menu item (using metadata image if available).
icon = invImg((metadata and metadata.image) or tostring(k)),
image = invImg((metadata and metadata.image) or tostring(k)),
- -- Final header text, appending ❌ if disabled or cannot be carried.
header = setheader..((disable or not canCarryTable[k]) and " ❌" or ""),
- -- Set description text if QBMenuExport is started.
txt = (isStarted(QBMenuExport) or disable) and settext or nil,
- -- Attach the metadata table containing ingredient details.
metadata = metaTable,
- -- Define the onSelect function to trigger crafting actions if the item is selectable.
- onSelect = ((not disable and canCarryTable[k]) and (function()
- -- Build transaction data with details needed for crafting.
+ onSelect = (not disable and canCarryTable[k]) and function()
local transdata = {
item = k,
craft = data.craftable.Recipes[i],
@@ -193,23 +148,21 @@ function craftingMenu(data)
coords = data.coords,
stashName = data.stashName,
onBack = data.onBack,
- metadata = metadata
+ metadata = metadata,
}
- -- Call multiCraft or makeItem based on configuration.
if Config.Crafting.MultiCraft then
multiCraft(transdata)
else
makeItem(transdata)
end
- end) or nil),
+ end or nil,
}
end
end
- Wait(0) -- Yield within the loop to maintain responsiveness.
+ Wait(0)
end
end
- -- Open the final crafting menu with the built Menu table and provided header/onBack configuration.
openMenu(Menu, {
header = data.craftable.Header,
headertxt = data.craftable.Headertxt,
@@ -217,32 +170,33 @@ function craftingMenu(data)
canClose = true,
onExit = function() end,
})
-
- -- Trigger an action (likely camera or player focus) to look at the specified coordinates.
lookEnt(data.coords)
end
+-------------------------------------------------------------
+-- Multi-Craft Menu
+-------------------------------------------------------------
--- Opens a menu for selecting the quantity to craft.
---
---- This function presents the player with options to craft multiple quantities of an item, based on `Config.Crafting.MultiCraftAmounts`.
+--- Presents the player with multiple crafting quantities based on Config.Crafting.MultiCraftAmounts.
---
----@param data table A table containing crafting data.
---- - **item** (`string`): The item to craft.
---- - **craft** (`table`): The crafting recipe for the item.
---- - **craftable** (`table`): The crafting options and settings.
---- - **coords** (`vector3`): The coordinates where the crafting is taking place.
---- - **stashName** (`string` or `table`, optional): The stash name(s) to check for item availability.
---- - **onBack** (`function`, optional): Function to call when returning from the menu.
---- - **metadata** (`table`, optional): Metadata for the crafted item.
+--- @param data table Crafting configuration containing:
+--- - item `string`) The item to craft.
+--- - craft (`table`) The crafting recipe.
+--- - craftable (`table`) Crafting options.
+--- - coords (`vector3`) where crafting occurs.
+--- - stashName (`string`) The stash name(s) for item availability.
+--- - onBack (`function`) Callback when returning.
+--- - metadata (`table`) (optional): Metadata for the crafted item.
---
----@usage
+--- @usage
--- ```lua
--- multiCraft({
--- item = "weapon_pistol",
--- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 },
--- craftable = craftingOptions,
---- coords = vector3(100.0, 200.0, 300.0),
+--- coords = vector3(100,200,300),
--- stashName = "crafting_stash",
--- onBack = function() craftingMenu(data) end,
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
@@ -250,28 +204,31 @@ end
--- ```
function multiCraft(data)
local Menu = {}
- local success = Config.Crafting.MultiCraftAmounts
+ local amounts = Config.Crafting.MultiCraftAmounts
local metadata = data.metadata or nil
- Menu[#Menu+1] = {
+
+ -- Header for the multi-craft menu.
+ Menu[#Menu + 1] = {
isMenuHeader = true,
icon = invImg(metadata and metadata.image or data.item),
header = metadata and metadata.label or Items[data.item].label,
}
- for k in pairsByKeys(success) do
+
+ for k in pairsByKeys(amounts) do
local settext = ""
local itemTable = {}
for l, b in pairs(data.craft[data.item]) do
itemTable[l] = (b * k)
- settext = settext..(settext ~= "" and br or "")..Items[l].label..(b*k > 1 and "- x"..b*k or "")
+ settext = settext..(settext ~= "" and br or "")..Items[l].label..(b * k > 1 and " x"..b * k or "")
Wait(0)
end
local disable, stashname = checkHasItem(data.stashName, itemTable)
Menu[#Menu + 1] = {
isMenuHeader = not disable,
arrow = disable,
- header = "Craft - x"..k * data.craft.amount,
+ header = "Craft - x"..(k * data.craft.amount),
txt = settext,
- onSelect = function ()
+ onSelect = function()
makeItem({
item = data.item,
craft = data.craft,
@@ -281,37 +238,41 @@ function multiCraft(data)
stashName = stashname,
stashTable = data.stashName,
onBack = data.onBack,
- metadata = data.metadata
+ metadata = data.metadata,
})
end,
}
end
- openMenu(Menu, { header = data.craftable.Header, onBack = function() craftingMenu(data) end, })
+
+ openMenu(Menu, { header = data.craftable.Header, onBack = function() craftingMenu(data) end })
end
+-------------------------------------------------------------
+-- Crafting Process
+-------------------------------------------------------------
+
--- Initiates the crafting process for a specified item.
---
---- This function handles the crafting animation, progress bar, item removal, and item creation.
+--- Plays crafting animations, shows progress bars, removes ingredients, and triggers item creation.
---
----@param data table A table containing crafting data.
---- - **item** (`string`): The item to craft.
---- - **craft** (`table`): The crafting recipe for the item.
---- - **craftable** (`table`): The crafting options and settings.
---- - **amount** (`number`, optional): The quantity to craft. Default is `1`.
---- - **coords** (`vector3`): The coordinates where the crafting is taking place.
---- - **stashName** (`string` or `table`, optional): The stash name(s) to remove items from.
---- - **stashTable** (`string` or `table`, optional): Alias for `stashName`.
---- - **onBack** (`function`, optional): Function to call when returning from the menu.
---- - **metadata** (`table`, optional): Metadata for the crafted item.
+--- @param data table Crafting configuration containing:
+--- - item `string`) The item to craft.
+--- - craft (`table`) The crafting recipe.
+--- - craftable (`table`) Crafting options.
+--- - amount (`number`) (optional): Quantity to craft (default 1).
+--- - coords (`vector3`) where crafting occurs.
+--- - stashName (`string`) The stash name(s) for item availability.
+--- - onBack (`function`) Callback when returning.
+--- - metadata (`table`) (optional): Metadata for the crafted item.
---
----@usage
+--- @usage
--- ```lua
--- makeItem({
--- item = "weapon_pistol",
--- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 },
--- craftable = craftingOptions,
--- amount = 2,
---- coords = vector3(100.0, 200.0, 300.0),
+--- coords = vector3(100,200,300),
--- stashName = "crafting_stash",
--- onBack = function() craftingMenu(data) end,
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
@@ -320,40 +281,31 @@ end
function makeItem(data)
if CraftLock then return end
CraftLock = true
- if data.stashTable then data.stashName = data.stashTable end
- local bartime = data.craftable.progressBar and data.craftable.progressBar.time or 5000
- local bartext = (data.craftable.progressBar and data.craftable.progressBar.label) or (Loc[Config.Lan].progressbar and Loc[Config.Lan].progressbar["progress_make"]) or "Making "
- local animDict = data.craftable.Anims and data.craftable.Anims.animDict or "amb@prop_human_parking_meter@male@idle_a"
- local anim = data.craftable.Anims and data.craftable.Anims.anim or "idle_a"
- local amount = data.amount and (data.amount ~= 1) and data.amount or 1
+ data.stashName = data.stashTable or data.stashName
+
+ local bartime = (data.craftable.progressBar and data.craftable.progressBar.time) or 5000
+ local bartext = (data.craftable.progressBar and data.craftable.progressBar.label)
+ or (Loc[Config.Lan].progressbar and Loc[Config.Lan].progressbar["progress_make"])
+ or "Making "
+ local animDict = (data.craftable.Anims and data.craftable.Anims.animDict) or "amb@prop_human_parking_meter@male@idle_a"
+ local anim = (data.craftable.Anims and data.craftable.Anims.anim) or "idle_a"
+ local craftAmount = (data.amount and data.amount ~= 1) and data.amount or 1
local metadata = data.metadata or nil
local prop = data.craftable.Anims and data.craftable.Anims.prop or nil
-
local canReturn = true
local crafted, crafting = true, true
local cam = createTempCam(PlayerPedId(), data.coords)
startTempCam(cam)
- for i = 1, amount do
- countTable(data.craft)
+ for i = 1, craftAmount do
for k, v in pairs(data.craft) do
local excludeKeys = {
- amount = true,
- info = true,
- metadata = true,
- description = true,
- job = true,
- gang = true,
- oneUse = true,
- slot = true,
- blueprintRef = true,
- craftingLevel = true,
- craftedItems = true,
- hasCrafted = true,
- exp = true,
+ amount = true, info = true, metadata = true, description = true,
+ job = true, gang = true, oneUse = true, slot = true,
+ blueprintRef = true, craftingLevel = true, craftedItems = true,
+ hasCrafted = true, exp = true, anim = true, time = true,
}
-
if not excludeKeys[k] then
if type(v) == "table" then
for l, b in pairs(v) do
@@ -366,7 +318,7 @@ function makeItem(data)
flag = 48,
icon = l,
}) then
- TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", Items[l], "use", b) -- Show item box for each item
+ TriggerEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', Items[l], "use", b)
else
crafted, crafting = false, false
break
@@ -376,9 +328,8 @@ function makeItem(data)
if crafted then
local craftProp = nil
if prop then
- local model, pos, rot, bone = prop.model, prop.pos, prop.rot, prop.bone
- craftProp = makeProp({ prop = model, coords = vec4(0, 0, 0, 0), true, true })
- AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), bone), pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, true, true, false, true, 1, true)
+ craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true })
+ AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), prop.bone), prop.pos.x, prop.pos.y, prop.pos.z, prop.rot.x, prop.rot.y, prop.rot.z, true, true, false, true, 1, true)
end
if crafting and progressBar({
label = bartext..((metadata and metadata.label) or Items[data.item].label),
@@ -393,16 +344,14 @@ function makeItem(data)
CreateThread(function()
if data.craft["hasCrafted"] ~= nil then
debugPrint("hasCrafted Found, marking '"..data.item.."' as crafted for player")
-
data.craftable.craftedItems[data.item] = true
- triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems )
+ triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
end
Wait(100)
if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give
-
jsonPrint(data.craft["exp"])
- debugPrint("exp Found, giving exp for '"..data.item.."'")
+ debugPrint("exp found, giving exp for '"..data.item.."'")
triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
end
end)
@@ -411,7 +360,6 @@ function makeItem(data)
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
canReturn = false
- -- If recipe is removed it doesn't try to open menu again, it was causing blank menus for some reason
end
else
crafting = false
@@ -431,16 +379,22 @@ function makeItem(data)
ClearPedTasks(PlayerPedId())
end
+-------------------------------------------------------------
+-- Server Event Handler: Crafted Item
+-------------------------------------------------------------
+
--- Server event handler for giving the crafted item to the player.
---
---- This event is triggered when the crafting process is completed successfully.
+--- Removes required ingredients from the player's inventory or stash,
+--- then adds the crafted item to their inventory.
---
--- @param ItemMake string The item being crafted.
--- @param craftable table The crafting recipe and details.
---- @param stashName string|table The stash name(s) to remove items from.
+--- @param stashName string|table The stash name(s) to remove ingredients from.
--- @param metadata table (optional) Metadata for the crafted item.
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata)
- local src, amount, stashItems = source, craftable and craftable.amount or 1, nil
+ local src = source
+ local hasItems, hasTable = hasItem(ItemMake, 1, src)
if stashName then
local itemRemove = {}
if type(stashName) == "table" then
@@ -468,22 +422,22 @@ RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable,
end
end
end
- addItem(ItemMake, amount, metadata, src)
- --if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end
+ addItem(ItemMake, craftable.amount or 1, metadata, src)
+ -- Optionally, add experience here:
+ -- if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end
end)
---- Opens a selling menu based on the provided data.
+-------------------------------------------------------------
+-- Selling Menu and Animation
+-------------------------------------------------------------
+
+--- Opens a selling menu with available items and prices.
---
---- This function checks available items to sell, prepares the menu options, and opens the selling menu.
----
----@param data table A table containing selling menu data.
---- - **sellTable** (`table`): The selling options and settings.
---- - **Items** (`table`): A list of items that can be sold with their prices.
---- - **Header** (`string`, optional): The header/title of the selling menu.
---- - **ped** (`number`, optional): The ped entity involved in the selling interaction.
---- - **onBack** (`function`, optional): Function to call when returning from the menu.
----
----@usage
+--- @param data table Contains selling menu data:
+--- - sellTable (`table`) Table with Header and Items (item names and prices).
+--- - ped (optional) (`number`) Ped entity involved.
+--- - onBack (optional) (`function`) Callback for returning.
+--- @usage
--- ```lua
--- sellMenu({
--- sellTable = {
@@ -505,10 +459,10 @@ function sellMenu(data)
for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end
local _, hasTable = hasItem(itemList)
for k, v in pairsByKeys(data.sellTable.Items) do
- Menu[#Menu +1] = {
+ Menu[#Menu + 1] = {
isMenuHeader = not hasTable[k].hasItem,
icon = invImg(k),
- header = Items[k].label.. (hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
+ header = Items[k].label..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
txt = Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"],
onSelect = function()
sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end })
@@ -518,7 +472,7 @@ function sellMenu(data)
else
for k, v in pairsByKeys(data.sellTable) do
if type(v) == "table" then
- Menu[#Menu +1] = {
+ Menu[#Menu + 1] = {
arrow = true,
header = k,
txt = "Amount of items: "..countTable(v.Items),
@@ -531,19 +485,24 @@ function sellMenu(data)
end
end
end
- openMenu(Menu, { header = data.sellTable.Header or "Amount of items: "..countTable(data.sellTable.Items), headertxt = data.sellTable.Header and "Amount of items: "..countTable(data.sellTable.Items) or "", canClose = true, onBack = data.onBack })
+ openMenu(Menu, {
+ header = data.sellTable.Header or "Amount of items: "..countTable(data.sellTable.Items),
+ headertxt = data.sellTable.Header and "Amount of items: "..countTable(data.sellTable.Items) or "",
+ canClose = true,
+ onBack = data.onBack,
+ })
end
---- Handles the selling animation and item transaction.
+--- Plays the selling animation and processes the sale transaction.
---
---- This function plays the selling animation, removes the item from the player's inventory, and gives the player money.
----
----@param data table A table containing selling data.
---- - **item** (`string`): The item to sell.
---- - **price** (`number`): The price per item.
---- - **ped** (`number`, optional): The ped entity involved in the selling interaction.
---- - **onBack** (`function`, optional): Function to call when returning from the menu.
+--- Checks if the player has the item, plays animations, triggers the server event for selling,
+--- and then calls the onBack callback if provided.
---
+--- @param data table Contains:
+--- `- item: The item to sell.
+--- `- price: Price per item.
+--- `- ped (optional): Ped entity involved.
+--- `- onBack (optional): Callback to call on completion.
---@usage
--- ```lua
--- sellAnim({
@@ -558,16 +517,20 @@ function sellAnim(data)
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error")
return
end
- for k, v in pairs(GetGamePool('CObject')) do
- for _, model in pairs({`p_cs_clipboard`}) do
- if GetEntityModel(v) == model then
- if IsEntityAttachedToEntity(data.ped, v) then
- DeleteObject(v) DetachEntity(v, 0, 0) SetEntityAsMissionEntity(v, true, true)
- Wait(100) DeleteEntity(v)
- end
+
+ -- Remove any attached clipboard objects.
+ for _, obj in pairs(GetGamePool('CObject')) do
+ for _, model in pairs({ `p_cs_clipboard` }) do
+ if GetEntityModel(obj) == model and IsEntityAttachedToEntity(data.ped, obj) then
+ DeleteObject(obj)
+ DetachEntity(obj, 0, 0)
+ SetEntityAsMissionEntity(obj, true, true)
+ Wait(100)
+ DeleteEntity(obj)
end
end
end
+
TriggerServerEvent(getScript().."Sellitems", data)
lookEnt(data.ped)
local dict = "mp_common"
@@ -579,11 +542,8 @@ function sellAnim(data)
if data.onBack then data.onBack() end
end
---- Server event handler for processing the item sale.
----
---- This event removes the sold item from the player's inventory and adds money to their account.
----
----@param data table The data containing item and price information.
+--- Server event handler for processing item sales.
+--- Removes sold items from inventory and funds the player based on the sale.
RegisterNetEvent(getScript().."Sellitems", function(data)
local src = source
local hasItems, hasTable = hasItem(data.item, 1, src)
@@ -595,17 +555,18 @@ RegisterNetEvent(getScript().."Sellitems", function(data)
end
end)
+-------------------------------------------------------------
+-- Shop Interface
+-------------------------------------------------------------
+
--- Opens a shop interface for the player.
---
---- This function checks job requirements and opens the shop using the appropriate inventory system.
----
----@param data table A table containing shop data.
---- - **shop** (`string`): The shop identifier.
---- - **items** (`table`): The items available in the shop.
---- - **coords** (`vector3`): The coordinates where the shop interaction is happening.
---- - **job** (`string` or `table`, optional): Job(s) required to access the shop.
---- - **gang** (`string` or `table`, optional): Gang(s) required to access the shop.
----
+--- Checks job/gang restrictions, then uses the active inventory system to open the shop.
+--- @param data table Contains:
+--- - shop (`string`) The shop identifier.
+--- - items (`table`) The items available in the shop.
+--- - coords (`vector3`) where the shop is located.
+--- - job/gang (optional) (`string`) Job or gang requirements.
---@usage
--- ```lua
--- openShop({
@@ -617,30 +578,34 @@ end)
--- ```
function openShop(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
+
if isStarted(OXInv) then
exports[OXInv]:openInventory('shop', { type = data.shop })
+
elseif isStarted(QBInv) then
if QBInvNew then
- TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop) -- i hate qb-inv
+ TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop)
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
+
+ --elseif isStarted(OrigenInv) then -- Needs testing, not sure if i did this right
+ -- exports[OrigenInv]:openInventory('shop', data.shop, data.items)
+
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
lookEnt(data.coords)
end
---- Server event handler for opening a new QB inventory shop.
----
---- This event is triggered when using the new QB inventory system.
----
----@param data table The shop data to open.
+--- Server event handler for opening a shop using the new QB inventory system.
RegisterNetEvent(getScript()..':server:OpenShopNewQB', function(data)
exports[QBInv]:OpenShop(source, data)
end)
---- Server-side callback registration for checking if the player can carry items.
+-------------------------------------------------------------
+-- Server Callback Registration
+-------------------------------------------------------------
if isServer() then
createCallback(getScript()..':server:canCarry', function(source, itemTable) local result = canCarry(itemTable, source) return result end)
end
\ No newline at end of file
diff --git a/shared/drawText.lua b/shared/drawText.lua
index a72420e..66b908e 100644
--- a/shared/drawText.lua
+++ b/shared/drawText.lua
@@ -1,53 +1,80 @@
-local radarTable = {}
+--[[
+ Text Drawing Module
+ ---------------------
+ This module provides functions to display and hide text on screen using
+ various frameworks: QB, OX, GTA, and ESX.
+]]
+
+local radarTable = {} -- Table to store image references for drawing text
--- Displays text on the screen using the configured draw text system.
---
---- This function handles displaying text with optional images or icons using different frameworks like 'qb', 'ox', 'gta', and 'esx'.
+--- Depending on Config.System.drawText, this function will use different methods to
+--- display text along with optional images/icons.
---
----@param image string|nil An optional image or icon to display with the text. Can be a URL, path, or a reference to an icon.
----@param input table A table of strings, each representing a line of text to display.
----@param style string|nil An optional style code for default GTA popups (e.g., '~g~' for green text).
----@param oxStyleTable table|nil An optional table specifying style parameters for the 'ox' draw text system.
+--- @param image string|nil Optional image/icon identifier to display with the text.
+--- @param input table An array of strings; each string is a line of text to display.
+--- @param style string|nil Optional style code for default GTA popups (e.g., "~g~" for green).
+--- @param oxStyleTable table|nil Optional table specifying style parameters for the OX text UI.
---
---@usage
--- ```lua
--- drawText("img_link", { "Test line 1", "Test Line 2" }, "~g~")
--- ```
-function drawText(image, input, style, oxStyleTable) local text = ""
- if Config.System.drawText == "qb" then
- for i = 1, #input do
- text = text..input[i]..""..(input[i+1] ~= nil and "
" or "") end
- local text = text:gsub("%:", ":")
- if image then
- text = '
'..text
- end
- exports[QBExport]:DrawText(text, 'left')
+function drawText(image, input, style, oxStyleTable)
+ local text = ""
- elseif Config.System.drawText == "ox" then
- for k, v in pairs(input) do
- input[k] = v.." \n"
- end
- lib.showTextUI(table.concat(input), { icon = (image and radarTable[image] or image) or nil, position = 'left-center', style = oxStyleTable})
-
- elseif Config.System.drawText == "gta" then
- for i = 1, #input do if input[i] ~= "" then text = text..input[i].."\n~s~" end end
- if image then text = "~BLIP_"..image.."~ "..text end
-
- DisplayHelpMsg(text:gsub("%:", ":~"..(style or "g").."~"))
- elseif Config.System.drawText == "esx" then
+ if Config.System.drawText == "qb" then
+ -- Concatenate lines for QB system with HTML line breaks.
for i = 1, #input do
- text = text..input[i]..""..(input[i+1] ~= nil and "
" or "") end
- local text = text:gsub("%:", ":")
- if image then
- text = '
'..text
- end
- ESX.TextUI(text, nil)
- end
+ text = text..input[i]..""..(input[i + 1] and "
" or "")
+ end
+ text = text:gsub("%:", ":")
+ if image then
+ text = '
'..text
+ end
+ exports[QBExport]:DrawText(text, 'left')
+
+ elseif Config.System.drawText == "ox" then
+ -- Append newline spacing to each input line.
+ for k, v in pairs(input) do
+ input[k] = v.." \n"
+ end
+ lib.showTextUI(table.concat(input), { icon = (image and radarTable[image] or image) or nil, position = 'left-center', style = oxStyleTable })
+
+ elseif Config.System.drawText == "gta" then
+ -- Concatenate input lines and apply GTA style formatting.
+ for i = 1, #input do
+ if input[i] ~= "" then
+ text = text..input[i].."\n~s~"
+ end
+ end
+ if image then
+ text = "~BLIP_"..image.."~ "..text
+ end
+ DisplayHelpMsg(text:gsub("%:", ":~"..(style or "g").."~"))
+
+ elseif Config.System.drawText == "esx" then
+ -- ESX-based text UI uses similar HTML formatting as QB.
+ for i = 1, #input do
+ text = text..input[i]..""..(input[i + 1] and "
" or "")
+ end
+ text = text:gsub("%:", ":")
+ if image then
+ text = '
'..text
+ end
+ ESX.TextUI(text, nil)
+ end
end
---- Hides any text currently being displayed on the screen.
+--- Hides any text currently displayed on the screen.
---
---- This function clears the text displayed by the `drawText` function, using the appropriate method based on the configured draw text system.
+--- Clears the text using the appropriate method for the configured draw text system.
+---
+--- @usage
+--- ```lua
+--- hideText()
+--- ```
function hideText()
if Config.System.drawText == "qb" then
exports[QBExport]:HideText()
diff --git a/shared/duifunctions.lua b/shared/duifunctions.lua
index 178c189..ff5d777 100644
--- a/shared/duifunctions.lua
+++ b/shared/duifunctions.lua
@@ -1,122 +1,165 @@
--- DUI STUFF -- * Experimental * --
+--[[
+ DUI Module (Experimental)
+ --------------------------
+ This module handles the creation, modification, and removal of custom DUI (Display UI)
+ elements using runtime textures. It supports both client and server functionality to update DUI
+ images dynamically.
+]]
+-- Create a runtime texture dictionary on the client if not running on the server.
scriptTxd = not isServer() and CreateRuntimeTxd(getScript()..'scriptTxd') or nil
customDUIList = {}
--- DUI CLIENT
-function createDui(name, http, size, txd)
- --print(name, http, size, txd)
- if not customDUIList[name] then
- local newTxt = CreateDui(http, math.floor(size.x), math.floor(size.y))
- while not GetDuiHandle(newTxt) do Wait(0) end
- CreateRuntimeTextureFromDuiHandle(txd, name, GetDuiHandle(newTxt))
- customDUIList[name] = newTxt
- SetDuiUrl(customDUIList[name], http)
- else
- SetDuiUrl(customDUIList[name], http)
- end
-end
+-------------------------------------------------------------
+-- DUI Client Functions
+-------------------------------------------------------------
-function DuiSelect(data)
- local image = ""
- for k, v in pairs(duiList[data.name]) do
- if v.tex.texn == data.texn then
- if duiList[data.name][k] then
- image = "- Current Image -
"..
- "
"..
- "Size: ["..math.floor(data.size.x)..", "..math.floor(data.size.y).."]
"
- end
- end
- end
- local dialog = exports['qb-input']:ShowInput({
- header = image..Loc[Config.Lan].menu["dui_new"],
- submitText = Loc[Config.Lan].menu["dui_change"],
- inputs = { { type = 'text', isRequired = true, name = 'url', text = Loc[Config.Lan].menu["dui_url"] } } })
- if dialog then
- if not dialog.url then return end
- data.url = dialog.url
- --Scan the link to see if it has an image extention otherwise, stop here.
- local searchList = { "png", "jpg", "jpeg", "gif", "webp", "bmp" }
- --Scan the link for certain terms that will flag it and refuse to show it
- local banList = { "porn" } -- I dunno, let me know what links people manage to find
- local searchFound = false
- for k, v in pairs(searchList) do
- if string.find(tostring(data.url), tostring(v))then
- searchFound = true
- end
- end
- for k, v in pairs(banList) do
- if string.find(tostring(data.url), tostring(v)) then
- searchFound = false print("BANNED WORD: "..v)
- end
- end
- if searchFound then
- TriggerServerEvent(getScript()..":Server:ChangeDUI", data)
- end
+--- Creates or updates a DUI element.
+---
+--- @param name string The unique name for the DUI element.
+--- @param http string The URL to load into the DUI.
+--- @param size table A table with .x and .y fields specifying the DUI dimensions.
+--- @param txd table The runtime texture dictionary where the DUI texture will be created.
+--- @usage
+--- ```lua
+--- createDui("logo", "https://example.com/logo.png", { x = 512, y = 256 }, scriptTxd)
+--- ```
+function createDui(name, http, size, txd)
+ if not customDUIList[name] then
+ local newDui = CreateDui(http, math.floor(size.x), math.floor(size.y))
+ while not GetDuiHandle(newDui) do Wait(0) end
+ CreateRuntimeTextureFromDuiHandle(txd, name, GetDuiHandle(newDui))
+ customDUIList[name] = newDui
+ SetDuiUrl(customDUIList[name], http)
+ else
+ SetDuiUrl(customDUIList[name], http)
end
end
+--- Opens a DUI selection input allowing the user to change the DUI image URL.
+---
+--- @param data table A table containing DUI data:
+--- - name: The key name in the DUI list.
+--- - texn: The texture name.
+--- - texd: The texture dictionary.
+--- - size: A table with .x and .y dimensions.
+---
+--- @usage
+--- ```lua
+--- DuiSelect({ name = "logo", texn = "logoTex", texd = "someTxd", size = { x = 512, y = 256 } })
+--- ```
+function DuiSelect(data)
+ local imagePreview = ""
+ for k, v in pairs(duiList[data.name]) do
+ if v.tex.texn == data.texn and duiList[data.name][k] then
+ imagePreview = "- Current Image -
" ..
+ "
" ..
+ "Size: ["..math.floor(data.size.x)..", "..math.floor(data.size.y).."]
"
+ end
+ end
+ local dialog = exports['qb-input']:ShowInput({
+ header = imagePreview..Loc[Config.Lan].menu["dui_new"],
+ submitText = Loc[Config.Lan].menu["dui_change"],
+ inputs = { { type = 'text', isRequired = true, name = 'url', text = Loc[Config.Lan].menu["dui_url"] } }
+ })
+ if dialog and dialog.url then
+ data.url = dialog.url
+ -- Scan URL for valid image extension and banned words.
+ local searchList = { "png", "jpg", "jpeg", "gif", "webp", "bmp" }
+ local banList = { "porn" }
+ local searchFound = false
+ for _, ext in pairs(searchList) do
+ if string.find(tostring(data.url), ext) then
+ searchFound = true
+ break
+ end
+ end
+ for _, banned in pairs(banList) do
+ if string.find(tostring(data.url), banned) then
+ searchFound = false
+ print("BANNED WORD: "..banned)
+ break
+ end
+ end
+ if searchFound then
+ TriggerServerEvent(getScript()..":Server:ChangeDUI", data)
+ end
+ end
+end
+
+--- Client event handler to update DUI elements.
RegisterNetEvent(getScript()..":Client:ChangeDUI", function(data)
- debugPrint("^6Bridge^7: ^2Recieving new DUI ^7- ^6"..data.url.."^7")
+ debugPrint("^6Bridge^7: ^2Receiving new DUI ^7- ^6"..data.url.."^7")
if tostring(data.url) ~= "-" then
- createDui(data.texn, tostring(data.url), data.size, scriptTxd)
- AddReplaceTexture(tostring(data.texd), tostring(data.texn), getScript()..'scriptTxd', tostring(data.texn))
+ createDui(data.texn, tostring(data.url), data.size, scriptTxd)
+ AddReplaceTexture(tostring(data.texd), tostring(data.texn), getScript().."scriptTxd", tostring(data.texn))
end
end)
+--- Client event handler to clear DUI elements.
RegisterNetEvent(getScript()..":Client:ClearDUI", function(data)
if customDUIList[tostring(data.texn)] then
RemoveReplaceTexture(tostring(data.texd), tostring(data.texn))
if IsDuiAvailable(customDUIList[tostring(data.texn)]) then
- SetDuiUrl(customDUIList[data.name], nil)
- end
+ SetDuiUrl(customDUIList[data.name], nil)
+ end
end
end)
--- DUI SERVER
+-------------------------------------------------------------
+-- DUI Server Functions
+-------------------------------------------------------------
+
+--- Server event handler to change DUI settings.
+--- If no URL is provided, resets to the preset value.
RegisterNetEvent(getScript()..":Server:ChangeDUI", function(data)
- -- if no url given, "reset" it back to preset
if not data.url then
- for k, v in pairs(duiList[data.name]) do
- if v.tex.texn == data.texn then
- debugPrint("^6Bridge^7: ^2Preset^7: ^6"..tostring(duiList[data.name][k].preset).."^7")
- data.url = duiList[data.name][k].preset
- end
- end
+ for k, v in pairs(duiList[data.name]) do
+ if v.tex.texn == data.texn then
+ debugPrint("^6Bridge^7: ^2Preset: ^6"..tostring(duiList[data.name][k].preset).."^7")
+ data.url = duiList[data.name][k].preset
+ end
+ end
+ end
+ for k, v in pairs(duiList[data.name]) do
+ if v.tex.texn == data.texn then
+ duiList[data.name][k].url = data.url
+ end
end
- -- if it has a url, update server DUI list and send to players
- for k, v in pairs(duiList[data.name]) do
- if v.tex.texn == data.texn then
- duiList[data.name][k].url = data.url
- end
- end
debugPrint("^6Bridge^7: ^3DUI^2 Sending new DUI to all players^7 - ^6"..data.url.."^7")
- TriggerClientEvent(getScript()..":Client:ChangeDUI", -1, data)
+ TriggerClientEvent(getScript()..":Client:ChangeDUI", -1, data)
end)
+--- Server event handler to clear DUI settings.
RegisterNetEvent(getScript()..":Server:ClearDUI", function(data)
if data.url == "-" then
- for k, v in pairs(duiList[data.name]) do
- if v.tex.texn == data.texn then
- duiList[data.name][k].url = "-"
- end
- end
- end
- -- Clear the DUI from loading
+ for k, v in pairs(duiList[data.name]) do
+ if v.tex.texn == data.texn then
+ duiList[data.name][k].url = "-"
+ end
+ end
+ end
TriggerClientEvent(getScript()..":Client:ClearDUI", -1, data)
- --duiList[tostring(data.tex)].url = ""
end)
-AddEventHandler('onResourceStop', function(r) if r ~= getScript() then return end
+-------------------------------------------------------------
+-- Resource Cleanup
+-------------------------------------------------------------
+
+onResourceStop(function()
for k, v in pairs(duiList or {}) do
- for i = 1, #v do
- RemoveReplaceTexture(tostring(v[i].tex.texd), tostring(v[i].tex.texn))
- end
+ for i = 1, #v do
+ RemoveReplaceTexture(tostring(v[i].tex.texd), tostring(v[i].tex.texn))
+ end
end
-end)
+end, true)
+
+-------------------------------------------------------------
+-- DUI List Callback (Server)
+-------------------------------------------------------------
if isServer() then
createCallback(getScript()..":Server:duiList", function(source)
- return duiList
+ return duiList
end)
end
\ No newline at end of file
diff --git a/shared/helpers.lua b/shared/helpers.lua
index 067d1f2..41c30e9 100644
--- a/shared/helpers.lua
+++ b/shared/helpers.lua
@@ -1,15 +1,19 @@
---- Utility Functions for Resource Management and Debugging
----
---- This script provides a set of utility functions for managing resources, debugging, and handling various common tasks within the game environment.
---- It includes functions for checking resource states, generating unique keys, formatting numbers and coordinates, handling JSON data, and more.
+--[[
+ Utility Functions for Resource Management and Debugging
+ ----------------------------------------------------------
+ This script provides a set of utility functions for managing resources,
+ debugging, and handling common tasks in the game environment.
+ It includes functions to check resource states, generate unique keys,
+ format numbers and coordinates, handle JSON data, perform raycasts, and more.
+]]
---[[ Resource and Environment Checks ]]--
+-------------------------------------------------------------
+-- Resource and Environment Checks
+-------------------------------------------------------------
--- Checks if a specific resource is started.
----
----@param script string The name of the resource to check.
----@return boolean `true` if the resource state contains "start", otherwise `false`.
----
+--- @param script string The name of the resource.
+--- @return boolean boolean True if the resource state contains "start", false otherwise.
---@usage
--- ```lua
--- if isStarted("myResource") then
@@ -22,12 +26,8 @@ end
local scriptName = nil
---- Retrieves the current resource name.
----
---- Caches the resource name after the first call for efficiency.
----
---- @return string scriptName The name of the current resource.
----
+--- Retrieves the current resource name, caching it for efficiency.
+--- @return string string The current resource name.
--- @usage
--- ```lua
--- local currentScript = getScript()
@@ -38,12 +38,8 @@ function getScript()
return scriptName
end
---- Determines if the current execution context is the server.
----
---- Very helpful for shared files complaining about client functions running on server or vice versa
----
---- @return boolean Returns `true` if running on the server, otherwise `false`.
----
+--- Determines if the current context is the server.
+--- @return boolean boolean True if running on the server, false otherwise.
---@usage
--- ```lua
--- if isServer() then
@@ -56,14 +52,13 @@ function isServer()
return IsDuplicityVersion()
end
---[[ Debugging Functions ]]--
+-------------------------------------------------------------
+-- Debugging and JSON Utilities
+-------------------------------------------------------------
---- Prints debug messages if debugging mode is enabled.
----
---- Concatenates all arguments and prints them along with debug information.
----
---- @param ... any Multiple arguments to be concatenated and printed.
----
+--- Prints debug messages if debugMode is enabled.
+--- Concatenates all arguments and prints them with debug info.
+--- @param ... any One or more values to print.
--- @usage
--- ```lua
--- debugPrint("Player has joined:", playerName)
@@ -71,15 +66,13 @@ end
function debugPrint(...)
if debugMode then
local args = {...}
- local output = table.concat(args, " ") -- Concatenate all arguments with a space
+ local output = table.concat(args, " ")
print(output, getDebugInfo(debug.getinfo(2, "nSl")))
end
end
--- Prints event-related debug messages if event debugging is enabled.
----
---- @param ... any Multiple arguments to be printed.
----
+--- @param ... any One or more values to print.
--- @usage
--- ```lua
--- eventPrint("Event triggered:", eventName)
@@ -90,24 +83,22 @@ function eventPrint(...)
end
end
--- Function to recursively colorize the JSON data
+--- Returns the keys of a table in sorted order.
+--- @param tbl table The table to sort keys for.
+--- @return table table A sorted array of keys.
function getSortedKeys(tbl)
local keys = {}
for k in pairs(tbl) do keys[#keys + 1] = k end
table.sort(keys, function(a, b)
local numA, numB = tonumber(a), tonumber(b)
- if numA and numB then return numA < numB
- else return tostring(a) < tostring(b) end
+ if numA and numB then return numA < numB else return tostring(a) < tostring(b) end
end)
return keys
end
--- Recursively colorizes a table for debug printing.
----
--- @param tbl table The table to colorize.
---- @return table colourizedTable The colorized table.
----
---- @usage
+--- @return table table A new table with colorized keys and values.
--- ```lua
--- local colorizedData = colorizeTable(myTable)
--- jsonPrint(colorizedData)
@@ -116,18 +107,19 @@ function colorizeTable(tbl)
local newData, sortedKeys = {}, getSortedKeys(tbl)
for _, k in ipairs(sortedKeys) do
local v = tbl[k]
- newData["^6"..tostring(k).."^7"] = ((type(v) == "table") and colorizeTable(v)) or (type(v):find("vector") and formatCoord(v)) or "^2"..tostring(v).."^7"
+ newData["^6"..tostring(k).."^7"] =
+ (type(v) == "table" and colorizeTable(v))
+ or (tostring(type(v)):find("vector") and formatCoord(v))
+ or "^2"..tostring(v).."^7"
end
return newData
end
--- Encodes a table into an ordered JSON string with indentation.
----
--- @param data table The table to encode.
---- @param indent string The string used for indentation (e.g., " ").
---- @param level number The current indentation level.
+--- @param indent string The indentation string (e.g., " ").
+--- @param level number The current level of indentation.
--- @return string The formatted JSON string.
----
--- @usage
--- ```lua
--- local jsonString = encodeOrderedJSON(myTable, " ", 0)
@@ -143,10 +135,8 @@ function encodeOrderedJSON(data, indent, level)
return table.concat(jsonParts)
end
---- Prints a table as a colorized and ordered JSON string if debugging mode is enabled.
----
+--- Prints a table as a colorized and ordered JSON string if debugMode is enabled.
--- @param data table The table to print.
----
--- @usage
--- ```lua
--- jsonPrint(myTable)
@@ -158,9 +148,7 @@ function jsonPrint(data)
end
--- Retrieves the current time formatted for debug prints.
----
--- @return string string The formatted time string, e.g., "^7(14:23:45)".
----
--- @usage
--- ```lua
--- local currentTime = GetPrintTime()
@@ -177,9 +165,7 @@ function GetPrintTime()
end
--- Generates a unique 3-character alphanumeric key.
----
---- @return string GeneratedString A randomly generated 3-character string.
----
+--- @return string string The generated key.
--- @usage
--- ```lua
--- local uniqueKey = keyGen()
@@ -187,20 +173,28 @@ end
--- ```
function keyGen()
local charset = {
- "q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m",
- "Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M",
+ "q","w","e","r","t","y","u","i","o","p",
+ "a","s","d","f","g","h","j","k","l",
+ "z","x","c","v","b","n","m",
+ "Q","W","E","R","T","Y","U","I","O","P",
+ "A","S","D","F","G","H","J","K","L",
+ "Z","X","C","V","B","N","M",
"1","2","3","4","5","6","7","8","9","0"
}
local GeneratedID = ""
- for i = 1, 3 do GeneratedID = GeneratedID..charset[math.random(1, #charset)] end
+ for i = 1, 3 do
+ GeneratedID = GeneratedID..charset[math.random(1, #charset)]
+ end
return GeneratedID
end
+-------------------------------------------------------------
+-- Formatting and Vector Math Functions
+-------------------------------------------------------------
+
--- Formats a number with commas as thousand separators.
----
--- @param amount number The number to format.
---- @return string commaValue The formatted number string with commas.
----
+--- @return string string The formatted number.
--- @usage
--- ```lua
--- local formattedNumber = cv(1000000) -- "1,000,000"
@@ -208,15 +202,17 @@ end
--- ``
function cv(amount)
local formatted = tostring(amount or "0")
- while true do formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') if (k==0) then break end Wait(0) end
+ while true do
+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
+ if k == 0 then break end
+ Wait(0)
+ end
return formatted
end
--- Formats a coordinate vector for debug printing.
----
---- @param coord table A vector3 or vector4 table with `x`, `y`, `z`, and optional `w` components.
---- @return string The formatted coordinate string with color codes.
----
+--- @param coord table A vector3 or vector4 with x, y, z (and optional w).
+--- @return string string The formatted coordinate string.
--- @usage
--- ```lua
--- local formattedCoord = formatCoord(vector3(100.0, 200.0, 300.0))
@@ -233,47 +229,42 @@ function formatCoord(coord)
return "^5"..vecType.."^7("..components[1]..components[2]..components[3]..components[4].."^7)"
end
---- Calculates the center point of a list of zones (coordinates).
----
---- @param table table A table of vector3 coordinates.
+--- Calculates the center point of a list of coordinates.
+--- @param tbl table An array of vector3 coordinates.
--- @return vector3 vector3 The center coordinate.
----
--- @usage
--- ```lua
--- local center = getCenterOfZones({vector3(100, 200, 300), vector3(110, 210, 310)})
--- print("Center of Zones:", center)
--- ```
-function getCenterOfZones(table)
+function getCenterOfZones(tbl)
local totalX, totalY, totalZ = 0, 0, 0
-
- for _, coord in ipairs(table) do
+ for _, coord in ipairs(tbl) do
totalX = totalX + coord.x
totalY = totalY + coord.y
totalZ = totalZ + coord.z
end
-
- local count = #table
+ local count = #tbl
return vector3(totalX / count, totalY / count, totalZ / count)
end
--- Counts the number of keys in a table.
----
---- @param table table The table to count keys in.
---- @return number number The number of keys in the table.
----
+--- @param tbl table The table to count.
+--- @return number number The key count.
--- @usage
--- ```lua
--- local count = countTable(myTable)
--- print("Number of keys:", count)
--- ```
-function countTable(table) local i = 0 for keys in pairs(table) do i += 1 end return i end
+function countTable(tbl)
+ local i = 0
+ for _ in pairs(tbl) do i = i + 1 end
+ return i
+end
-
---- Returns an iterator that iterates over a table's keys in sorted order.
----
+--- Returns an iterator over a table's keys in sorted order.
--- @param t table The table to iterate over.
---- @return function function An iterator function.
----
+--- @return function An iterator function for sorted keys.
--- @usage
--- ```lua
--- for k, v in pairsByKeys(myTable) do
@@ -281,7 +272,6 @@ function countTable(table) local i = 0 for keys in pairs(table) do i += 1 end re
--- end
--- ```
function pairsByKeys(t)
- local t = t
if not t then
print("^1Error^7: ^3Nil ^2table recieved for ^3pairsByKeys^7(), ^2setting to ^7{} ^2to prevent break^7")
t = {}
@@ -289,24 +279,20 @@ function pairsByKeys(t)
local a = {} for n in pairs(t) do a[#a+1] = n end table.sort(a) local i = 0 local iter = function() i += 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter
end
---- Creates a new table with consecutive numerical indices sorted by the `id` field.
----
---- @param originalTable table The original table with entries containing an `id` field.
---- @return table The new table with sorted entries and consecutive `id` values.
----
+--- Creates a new table with consecutive numerical indices sorted by the 'id' field.
+--- @param originalTable table The table containing entries with an 'id' field.
+--- @return table table A sorted table with consecutive indices.
--- @usage
+--- ```lua
--- local sortedTable = createConsecutiveTable(originalTable)
--- for i, entry in ipairs(sortedTable) do
--- print(i, entry)
--- end
+--- ```
function createConsecutiveTable(originalTable)
local sortedEntries = {}
- for _, entry in pairs(originalTable) do
- table.insert(sortedEntries, entry)
- end
- table.sort(sortedEntries, function(a, b)
- return a.id < b.id
- end)
+ for _, entry in pairs(originalTable) do table.insert(sortedEntries, entry) end
+ table.sort(sortedEntries, function(a, b) return a.id < b.id end)
local newTable = {}
for newIndex, entry in ipairs(sortedEntries) do
entry.id = newIndex
@@ -315,112 +301,9 @@ function createConsecutiveTable(originalTable)
return newTable
end
---[[ Drawing Functions ]]--
-
---- Draws 3D text at specified coordinates.
----
---- @param coord table A vector3 table with `x`, `y`, and `z` coordinates.
---- @param text string The text to display.
---- @param highlight boolean (optional) Whether to highlight certain parts of the text.
----
---- @usage
---- ```lua
---- CreateThread(function()
---- while true do
---- DrawText3D(vector3(100, 200, 300), "Hello World", true)
---- Wait(0)
---- end
---- end)
---- ```
-function DrawText3D(coord, text, highlight)
- SetTextScale(0.30, 0.30)
- SetTextFont(0)
- SetTextProportional(1)
- SetTextColour(255, 255, 255, 215)
- SetTextEntry("STRING")
- SetTextCentre(true)
- local totalLength = string.len(text)
- local textMaxLength = textMaxLength or 99 -- max 99
- local text = totalLength > textMaxLength and text:sub(1, totalLength - (totalLength - textMaxLength)) or text
- AddTextComponentString(highlight and text:gsub("%~w~", "~y~") or text)
- SetDrawOrigin(coord.x, coord.y, coord.z, 0)
- DrawText(0.0, 0.0)
- local count, length = GetLineCountAndMaxLength(text)
-
- local padding = 0.005
- local heightFactor = (count / 43) + padding
- local weightFactor = (length / 150) + padding
-
- local height = (heightFactor / 2) - padding / 1
- local width = (weightFactor / 2) - padding / 1
-
- DrawRect(0.0, height, width, heightFactor, 0, 0, 0, 150)
- ClearDrawOrigin()
-end
-
--- readd missing function for drawtext
-function GetLineCountAndMaxLength(text)
- local lineCount = 0
- local maxLength = 0
- for line in text:gmatch("[^\n]+") do
- lineCount = lineCount + 1
- local lineLength = string.len(line)
- if lineLength > maxLength then
- maxLength = lineLength
- end
- end
- -- If there are no newline characters (or text is empty), treat it as a single line.
- if lineCount == 0 then
- lineCount = 1
- end
- return lineCount, maxLength
-end
-
---- Displays a help message on the screen.
----
---- @param text string The text to display as a help message.
----
---- @usage
---- DisplayHelpMsg("Press E to interact")
-function DisplayHelpMsg(text)
- BeginTextCommandDisplayHelp("STRING")
- AddTextComponentScaleform(text)
- EndTextCommandDisplayHelp(0, true, false, -1)
-end
-
---- Displays a "Saving/Loading" spinner with a custom message.
----
---- @param text string The message to display alongside the spinner.
----
---- @usage
---- ```lua
---- displaySpinner("Saving data...")
---- ```
-function displaySpinner(text)
- BeginTextCommandBusyspinnerOn('STRING')
- AddTextComponentSubstringPlayerName(text)
- EndTextCommandBusyspinnerOn(4)
-end
-
---- Stops the "Saving/Loading" spinner.
----
---- This function is client-side only.
----
---- @usage
---- ```lua
---- stopSpinner()
---- ```
-function stopSpinner()
- if not isServer() then
- BusyspinnerOff()
- end
-end
-
--- Concatenates a table of strings into a single string separated by newlines.
----
---- @param tbl table A table containing string elements.
---- @return string string The concatenated string with newline separators.
----
+--- @param tbl table The table containing strings.
+--- @return string string The concatenated string.
--- @usage
--- ```lua
--- local combinedText = concatenateText({"Line 1", "Line 2", "Line 3"})
@@ -429,74 +312,69 @@ end
function concatenateText(tbl)
local result = ""
for i = 1, #tbl do
- result = result..tbl[i]
- if i < #tbl then
- result = result.."\n" -- Add newline only if it's not the last element
- end
+ result = result..tbl[i]..(i < #tbl and "\n" or "")
end
return result
end
---- Converts rotation to a direction vector.
----
---- @param rot vector3 A vector3 containing rotation values
---- @return vector3 vector3 A vector3 representing the direction.
----
+--- Converts a rotation (degrees) to a direction vector.
+--- @param rot vector3 A vector3 with rotation values.
+--- @return vector3 vector3 The forward direction vector.
--- @usage
--- ```lua
--- local direction = RotationToDirection({ z = 90 })
--- print(direction)
--- ```
function RotationToDirection(rot)
- local adjust = (math.pi / 180)
- return vec3(-math.sin(adjust * rot.z) * math.abs(math.cos(adjust * rot.x)), math.cos(adjust * rot.z) * math.abs(math.cos(adjust * rot.x)), math.sin(adjust * rot.x))
+ local adjust = math.pi / 180
+ return vec3(
+ -math.sin(adjust * rot.z) * math.abs(math.cos(adjust * rot.x)),
+ math.cos(adjust * rot.z) * math.abs(math.cos(adjust * rot.x)),
+ math.sin(adjust * rot.x)
+ )
end
---- Creates a simple text-based progress bar.
----
---- @param percentage number The completion percentage (0-100).
---- @return string string A string representing the progress bar, e.g., "█████░░░░░".
----
+--- Creates a basic progress bar string.
+--- @param percentage number Completion percentage (0-100).
+--- @return string string The progress bar (e.g., "█████░░░░░").
--- @usage
--- ```lua
--- local bar = basicBar(50) -- "█████░░░░░"
--- print(bar)
--- ```
function basicBar(percentage)
- local percentage = math.ceil(percentage)
- local totalBlocks = 10
- local filledBlocks = math.floor((percentage / 100) * totalBlocks)
- local emptyBlocks = totalBlocks - filledBlocks
-
- local bar = string.rep("█", filledBlocks)..string.rep("░", emptyBlocks)
- return bar
+ local perc = math.ceil(percentage)
+ local total = 10
+ local filled = math.floor((perc / 100) * total)
+ local empty = total - filled
+ return string.rep("█", filled)..string.rep("░", empty)
end
--- Normalizes a 3D vector.
----
---- @param vec vector3 A vector3 table with `x`, `y`, and `z` components.
---- @return vector3 vector3 The normalized vector3.
----
+--- @param vec vector3 A vector3 table.
+--- @return vector3 vector3 A normalized vector.
--- @usage
--- ```lua
--- local normalizedVec = normalizeVector(vector3(1, 2, 3))
--- print(normalizedVec)
--- ```
function normalizeVector(vec)
- local length = math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z)
- if length ~= 0 then
- return vec3(vec.x / length, vec.y / length, vec.z / length)
+ local len = math.sqrt(vec.x^2 + vec.y^2 + vec.z^2)
+ if len ~= 0 then
+ return vec3(vec.x / len, vec.y / len, vec.z / len)
else
return vec3(0, 0, 0)
end
end
---- Draws a line between two coordinates for debugging purposes.
----
---- @param startCoords vector3 A vector3 table representing the start point.
---- @param endCoords vector3 A vector3 table representing the end point.
---- @param col vector4 A table with `x`, `y`, `z`, `w` representing the color and opacity.
----
+-------------------------------------------------------------
+-- Drawing and Raycasting Functions
+-------------------------------------------------------------
+
+--- Draws a line between two coordinates (for debugging).
+--- @param startCoords vector3 The starting coordinate.
+--- @param endCoords vector3 The ending coordinate.
+--- @param col vector4 A vector4 specifying color and opacity.
--- @usage
--- ```lua
--- drawLine(vector3(100, 200, 300), vector3(150, 250, 350), vector4(255, 0, 0, 255))
@@ -504,21 +382,19 @@ end
function drawLine(startCoords, endCoords, col)
if debugMode then
CreateThread(function()
- local showCount = 1000
- while showCount >= 0 do
+ local count = 1000
+ while count >= 0 do
DrawLine(startCoords.x, startCoords.y, startCoords.z, endCoords.x, endCoords.y, endCoords.z, col.x, col.y, col.z, col.w)
- showCount -= 10
+ count -= 10
Wait(0)
end
end)
end
end
---- Draws a sphere at specified coordinates for debugging purposes.
----
---- @param coords vector3 A vector3 table representing the center of the sphere.
---- @param col vector4 A table with `x`, `y`, `z`, `w` representing the color and opacity.
----
+--- Draws a sphere at the specified coordinates (for debugging).
+--- @param coords vector3 The center of the sphere.
+--- @param col vector4 A vector4 specifying color and opacity.
--- @usage
--- ```lua
--- drawSphere(vector3(100, 200, 300), vector4(0, 255, 0, 255))
@@ -526,24 +402,22 @@ end
function drawSphere(coords, col)
if debugMode then
CreateThread(function()
- local showCount = 1000
- while showCount >= 0 do
+ local count = 1000
+ while count >= 0 do
DrawSphere(coords.x, coords.y, coords.z, 0.5, col.x, col.y, col.z, col.w)
- showCount -= 1
+ count -= 1
Wait(10)
end
end)
end
end
---- Performs a raycast between two coordinates and returns the result.
----
---- @param startCoords table A vector3 table representing the start point.
---- @param endCoords table A vector3 table representing the end point.
---- @param entity number|nil The entity to ignore during the raycast.
---- @param flags number|nil Raycast flags to customize the raycast behavior. Defaults to `4294967295`.
---- @return multiple multiple Returns multiple values from `GetShapeTestResultIncludingMaterial`.
----
+--- Performs a raycast between two coordinates and returns the results.
+--- @param startCoords vector3 The starting coordinate.
+--- @param endCoords vector3 The ending coordinate.
+--- @param entity number|nil An entity to ignore.
+--- @param flags number|nil Optional raycast flags (default: 4294967295).
+--- @return multiple Multiple values returned by GetShapeTestResultIncludingMaterial.
--- @usage
--- ```lua
--- local hit, hitPos, material = PerformRaycast(startVec, endVec, playerPed, 1)
@@ -553,47 +427,41 @@ end
--- end
--- ```
function PerformRaycast(startCoords, endCoords, entity, flags)
- drawLine(startCoords, endCoords, vec4(0, 0, 255, 255))
- local val1, val2, val3, val4, val5, val6 = GetShapeTestResult(StartExpensiveSynchronousShapeTestLosProbe(startCoords.x, startCoords.y, startCoords.z, endCoords.x, endCoords.y, endCoords.z, flags or 4294967295, entity, 0))
- if val2 then
- --drawSphere(val3, vec4(255, 0, 255, 0.5))
- end
+ drawLine(startCoords, endCoords, vec4(0,0,255,255))
+ local val1, val2, val3, val4, val5, val6 = GetShapeTestResult(
+ StartExpensiveSynchronousShapeTestLosProbe(
+ startCoords.x, startCoords.y, startCoords.z,
+ endCoords.x, endCoords.y, endCoords.z,
+ flags or 4294967295, entity, 0
+ )
+ )
return val1, val2, val3, val4, val5, val6
end
---- Adjusts the Z-coordinate of a position to align with the ground.
----
---- @param coords vector4 A vector3 or vector4 table with `x`, `y`, `z`, and optional `w` components.
---- @return vector3|vector4 vector adjusted coordinate with the Z value set to the ground level.
----
+--- Adjusts the Z-coordinate of a position to the ground level.
+--- @param coords vector4 A vector3 or vector4 with x, y, z (and optional w).
+--- @return vector3|vector4 vector The coordinates adjusted for ground level.
--- @usage
--- ```lua
--- local groundCoords = adjustForGround(playerCoords)
--- print("Ground Position:", groundCoords)
--- ```
function adjustForGround(coords)
- local coords = coords
local foundGround, zPos = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z + 1.0)
-
if foundGround then
if coords.w then
- coords = vec4(coords.x, coords.y, zPos, coords.w)
+ return vec4(coords.x, coords.y, zPos, coords.w)
else
- coords = vec3(coords.x, coords.y, zPos)
+ return vec3(coords.x, coords.y, zPos)
end
- --debugPrint("^6Bridge^7: Adjusting for ground pos ", coords.z, zPos)
-
- return coords
else
return coords
end
end
---- Ensures that a network vehicle exists by verifying its network ID.
----
---- @param vehNetID number The network ID of the vehicle.
---- @return number number The vehicle entity if it exists, otherwise `0`.
----
+--- Ensures a network vehicle exists from its network ID.
+--- @param vehNetID number The network ID.
+--- @return number number The vehicle entity, or 0 if not found.
--- @usage
--- ```lua
--- local vehicle = ensureNetToVeh(netID)
@@ -619,16 +487,16 @@ function ensureNetToVeh(vehNetID)
return vehicle
end
---- Ensures that a network entity exists by verifying its network ID.
----
---- @param entNetID number The network ID of the entity.
---- @return number The entity if it exists, otherwise `0`.
----
+--- Ensures a network entity exists from its network ID.
+--- @param entNetID number The network ID.
+--- @return number number The entity, or 0 if not found.
--- @usage
+--- ```lua
--- local entity = ensureNetToEnt(netID)
--- if entity ~= 0 then
--- print("Entity exists:", entity)
--- end
+--- ```
function ensureNetToEnt(entNetID)
debugPrint("^6Bridge^7: ^3ensureNetToEnt^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..entNetID.."^7)")
local timeout = 100
@@ -647,7 +515,9 @@ function ensureNetToEnt(entNetID)
return entity
end
---[[ Material Definitions ]]--
+-------------------------------------------------------------
+-- Material and Prop Functions
+-------------------------------------------------------------
--- A table mapping material names to their corresponding hash values.
---
@@ -869,22 +739,17 @@ local materials = {
temp_30 = 13626292
}
---- Retrieves the ground material at a specified position.
----
---- This function performs a raycast downwards from the given coordinates to determine the material type of the ground.
----
---- @param coords vector3 The coordinates from which to perform the raycast.
---- @return number|nil number The material hash if found; otherwise, `nil`.
---- @return string string The name of the material.
----
+--- Retrieves the ground material at a given position.
+--- @param coords vector3 The coordinate to test.
+--- @return number|nil number The material hash if hit, nil otherwise.
+--- @return string string The material name.
--- @usage
--- ```lua
---- local materialHash, materialName = GetGroundMaterialAtPosition(vector3(100, 200, 300))
---- print("Ground material:", materialName)
+--- local matHash, matName = GetGroundMaterialAtPosition(vector3(100,200,300))
+--- print("Material:", matName)
--- ```
function GetGroundMaterialAtPosition(coords)
local endX, endY, endZ = coords.x, coords.y, coords.z - 1.0
-
local rayHandle = StartShapeTestCapsule(coords.x, coords.y, coords.z, endX, endY, endZ, 1.0, 1, playerPed, 7)
local _, hit, _, _, materialHash, _ = GetShapeTestResultIncludingMaterial(rayHandle)
local materialName = "Unknown"
@@ -894,14 +759,10 @@ function GetGroundMaterialAtPosition(coords)
break
end
end
- if hit then return materialHash, materialName
- else return nil, materialName end
+ if hit then return materialHash, materialName else return nil, materialName end
end
---- Retrieves the dimensions of a prop/model.
----
---- This function loads the specified model and returns its width, depth, and height based on its bounding box.
----
+--- Retrieves the dimensions (width, depth, height) of a prop/model.
--- @param model string The name or hash of the model.
--- @return number number The width of the prop.
--- @return number number The depth of the prop.
diff --git a/shared/input.lua b/shared/input.lua
index dd3145b..6c18958 100644
--- a/shared/input.lua
+++ b/shared/input.lua
@@ -1,25 +1,28 @@
--- INPUT --
--- Multiscript input script function to create simple input text boxes --
+--[[
+ Input Dialog Module
+ ---------------------
+ This module provides a function to create a simple input dialog compatible with
+ multiple menu systems (OX, QB, GTA/WarMenu, and ESX). It supports various input
+ types such as radio buttons, numbers, text, and select dropdowns.
---- Creates a simple input dialog compatible with multiple menu systems.
+]]
+
+--- Creates a simple input dialog using the configured menu system.
---
---- This function generates input dialogs for different frameworks (OX, QB, GTA) based on the configuration.
---- It supports various input types such as radio buttons, numbers, text, and select dropdowns.
+--- @param title string The title or header of the input dialog.
+--- @param opts table A table of input option definitions. Each option should include:
+--- - type (string): The input type ("radio", "number", "text", "select").
+--- - label (string, optional): A label for the input (used in radio/select for OX).
+--- - text (string, optional): The text prompt for the input.
+--- - name (string): The identifier for the input.
+--- - isRequired (boolean, optional): Whether input is mandatory.
+--- - default (any, optional): The default value.
+--- - options (table, optional): A table of choices for "radio" and "select" types.
+--- - min (number, optional): Minimum value (for "number" and "select").
+--- - max (number, optional): Maximum value.
+--- - txt (string, optional): Additional description.
---
----@param title string The title/header of the input dialog.
----@param opts table A table containing input options. Each option should have a `type` and other relevant fields based on the type.
---- - **type** (`string`): The type of input. Supported types: "radio", "number", "text", "select".
---- - **label** (`string`, optional): The label for the input (used for "radio" and "select" types in OX).
---- - **text** (`string`, optional): The text prompt for the input.
---- - **name** (`string`): The identifier name for the input.
---- - **isRequired** (`boolean`, optional): Whether the input is required.
---- - **default** (`any`, optional): The default value for the input.
---- - **options** (`table`, optional): A table of options for "radio" and "select" types.
---- - **min** (`number`, optional): The minimum value (used for "select" type).
---- - **max** (`number`, optional): The maximum value (used for "number" and "select" types).
---- - **txt** (`string`, optional): Additional text or description for the input.
----
----@return table|nil table Returns the user's input as a table if the dialog is submitted, otherwise returns `nil`.
+--- @return table|nil table Returns the user's input as a table if submitted, otherwise nil.
---
---@usage
--- ```lua
diff --git a/shared/inventories.lua b/shared/inventories.lua
new file mode 100644
index 0000000..e094f1c
--- /dev/null
+++ b/shared/inventories.lua
@@ -0,0 +1,133 @@
+-------------------------------------------------------------
+-- Item Availability & Inventory Retrieval
+-------------------------------------------------------------
+---
+--- Locks or unlocks the player's inventory.
+--- Freezes/unfreezes the player's position, sets inventory busy state, and toggles hotbar usage.
+---
+--- @param toggle boolean True to lock inventory; false to unlock.
+---
+--- @usage
+--- ```lua
+--- lockInv(true) -- Lock inventory.
+--- lockInv(false) -- Unlock inventory.
+--- ```
+function lockInv(toggle)
+ FreezeEntityPosition(PlayerPedId(), toggle)
+ LocalPlayer.state:set("inv_busy", toggle, true)
+ TriggerEvent('inventory:client:busy:status', toggle)
+ TriggerEvent('canUseInventoryAndHotbar:toggle', not toggle)
+end
+--- Checks if a player has the specified items in their inventory.
+---
+--- Verifies whether the required quantities are present. Returns a boolean and a table of details.
+---
+--- @param items string|table A single item name or table with required amounts.
+--- @param amount number The required quantity (default 1).
+--- @param src number|nil Player source ID (defaults to caller).
+--- @return boolean boolean True if all items are available; otherwise, false.
+--- @return table|nil table Table detailing counts for each item.
+---
+---@usage
+--- ```lua
+--- local hasAll, details = hasItem({"health_potion", "mana_potion"}, 2, playerId)
+--- if hasAll then
+--- -- Proceed with action
+--- else
+--- -- Inform the player about missing items
+--- end
+--- ```
+function hasItem(items, amount, src)
+ local amount = amount and amount or 1
+ local grabInv, foundInv = getPlayerInv(src)
+ if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end
+
+ if grabInv then
+ local hasTable = {}
+ for item, amt in pairs(items) do
+ if not Items[item] then print("^4ERROR^7: ^2Script can't find ingredient item in Shared Items - ^1"..item.."^7") end
+
+ local count = 0
+ for _, itemData in pairs(grabInv) do
+ if itemData and itemData.name == item then
+ count += (itemData.count or itemData.amount or 1)
+ end
+ end
+ foundInv = foundInv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6")
+ local foundMessage = "^6Bridge^7: ^3hasItem^7[^6"..foundInv.."^7]: "..tostring(item).." ^3"..count.."^7/^3"..amt
+ if count >= amt then foundMessage = foundMessage.." ^5FOUND^7" else foundMessage = foundMessage .." ^1NOT FOUND^7" end
+ debugPrint(foundMessage)
+ hasTable[item] = { hasItem = count >= amt, count = count }
+ end
+ for k, v in pairs(hasTable) do if not v.hasItem then return false, hasTable end end
+ return true, hasTable
+ end
+end
+
+--- Retrieves a player's inventory based on the active inventory system.
+---
+--- @param src number|nil The player source ID (if nil, retrieves current player's inventory).
+--- @return table|nil table The inventory items.
+--- @return string|nil string The name of the inventory system.
+---
+---@usage
+--- ```lua
+--- local inventory, system = getPlayerInv(playerId)
+--- if inventory then
+--- -- Process inventory
+--- end
+--- ```
+function getPlayerInv(src)
+ local grabInv = nil
+ local foundInv = ""
+
+ if isStarted(OXInv) then
+ foundInv = OXInv
+ if src then grabInv = exports[OXInv]:GetInventoryItems(src)
+ else grabInv = exports[OXInv]:GetPlayerItems() end
+
+ elseif isStarted(QSInv) then
+ foundInv = QSInv
+ if src then grabInv = exports[QSInv]:GetInventory(src)
+ else grabInv = exports[QSInv]:getUserInventory() end
+
+ elseif isStarted(OrigenInv) then
+ foundInv = OrigenInv
+ if src then grabInv = exports[OrigenInv]:getInventory(src)
+ else grabInv = exports[OrigenInv]:getInventory() end
+
+ elseif isStarted(CoreInv) then
+ foundInv = CoreInv
+ if src then grabInv = exports[CoreInv]:getInventory(src)
+ else grabInv = exports[CoreInv]:getInventory() end
+
+ elseif isStarted(CodeMInv) then
+ foundInv = CodeMInv
+ if src then grabInv = exports[CodeMInv]:GetInventory(src)
+ else grabInv = exports[CodeMInv]:GetClientPlayerInventory() end
+
+ elseif isStarted(QBInv) then
+ foundInv = QBInv
+ if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
+ else grabInv = Core.Functions.GetPlayerData().items end
+
+ elseif isStarted(PSInv) then
+ foundInv = PSInv
+ if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
+ else grabInv = Core.Functions.GetPlayerData().items end
+
+ elseif ESX and isStarted(ESXExport) then
+ foundInv = ESX
+ if src then
+ local xPlayer = ESX.GetPlayerFromId(src)
+ grabInv = xPlayer.inventory
+ else
+ local xPlayer = ESX.GetPlayerData() -- Client side, if available
+ grabInv = xPlayer.inventory
+ end
+
+ else
+ print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3starter^1.^2lua^7")
+ end
+ return grabInv, foundInv
+end
\ No newline at end of file
diff --git a/shared/isAnimal.lua b/shared/isAnimal.lua
index 418cb38..f99c118 100644
--- a/shared/isAnimal.lua
+++ b/shared/isAnimal.lua
@@ -1,36 +1,76 @@
+--[[
+ Animal Detection Module
+ -------------------------
+ This module determines whether a Ped is an animal and categorizes it as a cat, dog,
+ or other type (e.g., coyote). It uses predefined model hashes stored in the AnimalPeds table.
+
+ Global Flags:
+ - isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal: Booleans to track the player's
+ current animal classification.
+
+ When running client-side (not on the server), the module checks the player's Ped after they load.
+
+ Usage Examples:
+ -- Check if the player's Ped is an animal:
+ local animalStatus = isPedAnimal()
+
+ -- Check if a given Ped is a cat:
+ if isCat(somePed) then print("This is a cat!") end
+
+ -- Determine if a Ped is a dog and whether it's big or small:
+ local isDogFlag, isBig = isDog(somePed)
+
+ -- Retrieve a flat list of all animal model hashes:
+ local allAnimalModels = getAnimalModels()
+
+ File Separation Suggestion:
+ For scalability, consider separating this module into two files:
+ • AnimalDetection.lua (for functions and callbacks)
+ • AnimalPedsData.lua (for the AnimalPeds table)
+]]
+
+-- Global animal classification flags.
isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
if not isServer() then
onPlayerLoaded(function()
Wait(2000)
+ -- Reset classification flags
isCat, isDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
+ -- Check if the player's Ped is an animal.
isPedAnimal()
if isAnimal then
local ped = PlayerPedId()
local pedModel = GetEntityModel(ped)
+ -- Determine if the Ped is a cat:
+ -- Also treat 'ft-raccoon' as a cat unless it is 'ft-sphynx'
isCat = (isCat(ped) or pedModel == `ft-raccoon`) and (pedModel ~= `ft-sphynx`)
+ -- Determine if the Ped is a dog and whether it's big:
isDog, isBigDog = isDog(ped)
isSmallDog = not isBigDog
if isDog and pedModel == `a_c_coyote` then isDog = false end
+ -- Determine if the Ped is a coyote (special case):
isCoyote = (pedModel == `ft-sphynx` or pedModel == `a_c_coyote`)
+ -- Special override: if model is 'ft-capmonkey2', treat as a dog.
if pedModel == `ft-capmonkey2` then isDog = true end
end
end, true)
- --- Determines if a given Ped is classified as an animal.
+ -------------------------------------------------------------
+ -- Animal Classification Functions
+ -------------------------------------------------------------
+
+ --- Determines whether a given Ped is classified as an animal.
---
- --- This function checks whether the specified Ped (or the player's Ped if none is provided)
- --- is listed within the predefined `AnimalPeds` tables. It iterates through all animal types
- --- to verify if the Ped's model hash matches any known animal models.
+ --- Checks if the Ped's model hash appears in any of the animal categories defined in AnimalPeds.
---
- ---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`).
- ---
- ---@return boolean `true` if the Ped is an animal, otherwise `false`.
+ --- @param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped.
+ --- @return boolean boolean True if the Ped is an animal, otherwise false.
---
--- @usage
--- ```lua
@@ -39,31 +79,24 @@ if not isServer() then
--- ```
function isPedAnimal(ped)
local PedModel = GetEntityModel(ped or PlayerPedId())
-
- for _, animalTypeTable in pairs(AnimalPeds) do
- for animalModelHash, _ in pairs(animalTypeTable) do
+ for _, animalCategory in pairs(AnimalPeds) do
+ for animalModelHash, _ in pairs(animalCategory) do
if PedModel == animalModelHash then
isAnimal = true
- break
+ debugPrint("^6Bridge^7: ^2Ped is Animal")
+ return true
end
end
- if isAnimal then
- debugPrint("^6Bridge^7: ^2Ped is Animal^1")
- break
- end
end
-
- return isAnimal
+ return false
end
- --- Checks if a given Ped is classified specifically as a cat.
+ --- Checks if a given Ped is classified as a cat.
---
- --- This function verifies whether the specified Ped (or the player's Ped if none is provided)
- --- matches any of the model hashes listed under `AnimalPeds.CatPeds`. It returns `true` if a match is found.
+ --- Iterates through the CatPeds table and returns true if the Ped's model matches.
---
- ---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`).
- ---
- ---@return boolean `true` if the Ped is a cat, otherwise `false`.
+ --- @param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped.
+ --- @return boolean True if the Ped is a cat, otherwise false.
---
---@usage
--- ```lua
@@ -78,24 +111,20 @@ if not isServer() then
--- ```
function isCat(ped)
local PedModel = GetEntityModel(ped or PlayerPedId())
- for k, v in pairs(AnimalPeds.CatPeds) do
- if PedModel == k then
+ for modelHash, _ in pairs(AnimalPeds.CatPeds) do
+ if PedModel == modelHash then
return true
end
end
return false
end
- --- Determines if a given Ped is classified as a dog and identifies its size category.
+ --- Determines if a given Ped is a dog and identifies its size category.
---
- --- This function checks whether the specified Ped (or the player's Ped if none is provided)
- --- matches any model hashes listed under `AnimalPeds.BigDogs` or `AnimalPeds.SmallDogs`. It returns
- --- two values: the first indicates if the Ped is a dog, and the second specifies whether it's a
- --- large dog (`true`) or a small dog (`false`). If the Ped is not a dog, the second return value is `nil`.
+ --- Checks the BigDogs and SmallDogs tables to see if the Ped's model matches any dog model.
---
- ---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`).
- ---
- ---@return boolean, boolean|nil Returns `true` and `true` if the Ped is a big dog,
+ --- @param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped.
+ ---@return boolean, boolean|nil boolean Returns `true` and `true` if the Ped is a big dog,
--- `true` and `false` if it's a small dog,
--- or `false` and `nil` if it's not a dog.
---
@@ -124,27 +153,24 @@ if not isServer() then
--- ```
function isDog(ped)
local PedModel = GetEntityModel(ped or PlayerPedId())
- for k, v in pairs(AnimalPeds.BigDogs) do
- if PedModel == k then
+ for modelHash, _ in pairs(AnimalPeds.BigDogs) do
+ if PedModel == modelHash then
return true, true
end
end
-
- for k, v in pairs(AnimalPeds.SmallDogs) do
- if PedModel == k then
+ for modelHash, _ in pairs(AnimalPeds.SmallDogs) do
+ if PedModel == modelHash then
return true, false
end
end
return false, nil
end
- --- Retrieves a list of all animal model hashes.
+ --- Compiles and returns a flat table of all animal model hashes.
---
- --- This function compiles and returns a flat table containing all model hashes
- --- from the various animal categories defined within the `AnimalPeds` table.
- --- It's useful for iterating over or performing bulk operations on all animal models.
+ --- Iterates through every category in AnimalPeds and collects all model hashes.
---
- ---@return table table A table containing all animal model hashes.
+ --- @return table table A table containing all animal model hashes.
---
---@usage
--- ```lua
@@ -154,289 +180,83 @@ if not isServer() then
--- end
--- ```
function getAnimalModels()
- local animalTable = {}
- for k in pairs(AnimalPeds) do
- for v in pairs(AnimalPeds[k]) do
- animalTable[#animalTable+1] = v
+ local animalModels = {}
+ for _, animalCategory in pairs(AnimalPeds) do
+ for modelHash, _ in pairs(animalCategory) do
+ table.insert(animalModels, modelHash)
end
end
- return animalTable
+ return animalModels
end
end
-
+-------------------------------------------------------------
+-- Animal Models Data
+-------------------------------------------------------------
+-- Define the animal models and their associated animations.
AnimalPeds = {
BigDogs = {
- -- Big Dogs
- [`a_c_chop`] = {
- deathAnim = "dead_right", deathDict = "creatures@chop@move",
- exitAnim = "getup_r", exitDict = "creatures@chop@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_k9`] = {
- deathAnim = "dead_right", deathDict = "creatures@chop@move",
- exitAnim = "getup_r", exitDict = "creatures@chop@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_husky`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_retriever`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_shepherd`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_rottweiler`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-aushep`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`golden_r`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-dobermanv2`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`doberman`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-gs`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`k9_husky`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-bloodhound`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`bernard`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-pterrier`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-labrador`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`dane`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft_malinois`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`abdog`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`dalmatian`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_dalmatian`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-boxer`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`ft-bs`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`chowchow`] = {
- deathAnim = "dead_right", deathDict = "creatures@rottweiler@move",
- exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup",
- sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car"
- },
- [`a_c_coyote`] = {
- deathAnim = "dead_right", deathDict = "creatures@coyote@move",
- exitAnim = "getup_r", exitDict = "creatures@coyote@getup",
- sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
- },
- [`a_c_coyote_02`] = {
- deathAnim = "dead_right", deathDict = "creatures@coyote@move",
- exitAnim = "getup_r", exitDict = "creatures@coyote@getup",
- sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
- },
+ [`a_c_chop`] = { deathAnim = "dead_right", deathDict = "creatures@chop@move", exitAnim = "getup_r", exitDict = "creatures@chop@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_k9`] = { deathAnim = "dead_right", deathDict = "creatures@chop@move", exitAnim = "getup_r", exitDict = "creatures@chop@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_husky`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_retriever`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_shepherd`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_rottweiler`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-aushep`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`golden_r`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-dobermanv2`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`doberman`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-gs`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`k9_husky`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-bloodhound`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`bernard`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-pterrier`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-labrador`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`dane`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft_malinois`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`abdog`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`dalmatian`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_dalmatian`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-boxer`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`ft-bs`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`chowchow`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
+ [`a_c_coyote`] = { deathAnim = "dead_right", deathDict = "creatures@coyote@move", exitAnim = "getup_r", exitDict = "creatures@coyote@getup", sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base" },
+ [`a_c_coyote_02`] = { deathAnim = "dead_right", deathDict = "creatures@coyote@move", exitAnim = "getup_r", exitDict = "creatures@coyote@getup", sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base" },
},
SmallDogs = {
- -- Small Dogs
- [`a_c_poodle`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
- [`ft-chihuahua`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
- [`a_c_pug`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
- [`a_c_pug_02`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
- [`a_c_westy`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
- [`ft-pretriever`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
- [`ft-shepk9`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
+ [`a_c_poodle`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
+ [`ft-chihuahua`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
+ [`a_c_pug`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
+ [`a_c_pug_02`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
+ [`a_c_westy`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
+ [`ft-pretriever`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
+ [`ft-shepk9`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
},
CatPeds = {
- -- Cat
- [`bshorthair`] = {
- deathAnim = "dead_right", deathDict = "creatures@cat@move",
- exitAnim = "getup_r", exitDict = "creatures@cat@getup",
- sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base"
- },
- [`a_c_cat_01`] = {
- deathAnim = "dead_right", deathDict = "creatures@cat@move",
- exitAnim = "getup_r", exitDict = "creatures@cat@getup",
- sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base"
- },
- [`ft-sphynx`] = {
- deathAnim = "dead_right", deathDict = "creatures@coyote@move",
- exitAnim = "getup_r", exitDict = "creatures@coyote@getup",
- sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
- },
+ [`bshorthair`] = { deathAnim = "dead_right", deathDict = "creatures@cat@move", exitAnim = "getup_r", exitDict = "creatures@cat@getup", sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base" },
+ [`a_c_cat_01`] = { deathAnim = "dead_right", deathDict = "creatures@cat@move", exitAnim = "getup_r", exitDict = "creatures@cat@getup", sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base" },
+ [`ft-sphynx`] = { deathAnim = "dead_right", deathDict = "creatures@coyote@move", exitAnim = "getup_r", exitDict = "creatures@coyote@getup", sitAnim = "base", sitDict = "creatures@coyote@amb@world_coyote_howl@base" },
},
OtherPeds = {
- -- Other Animals
- [`ft-raccoon`] = {
- deathAnim = "dead_right", deathDict = "creatures@cat@move",
- exitAnim = "getup_r", exitDict = "creatures@cat@getup",
- sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base"
- },
- [`a_c_hen`] = {
- deathAnim = "dead_right", deathDict = "creatures@hen@move",
- exitAnim = "getup_r", exitDict = "creatures@hen@getup"
- },
- [`a_c_rabbit_01`] = {
- deathAnim = "dead_right", deathDict = "creatures@rabbit@move",
- exitAnim = "getup_r", exitDict = "creatures@rabbit@getup",
- sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
- },
- [`a_c_rabbit_02`] = {
- deathAnim = "dead_right", deathDict = "creatures@rabbit@move",
- exitAnim = "getup_r", exitDict = "creatures@rabbit@getup",
- sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base"
- },
- [`a_c_rat`] = {
- deathAnim = "dead_right", deathDict = "creatures@rat@move",
- exitAnim = "getup_r", exitDict = "creatures@rat@getup"
- },
- [`a_c_deer`] = {
- deathAnim = "dead_right", deathDict = "creatures@deer@move",
- exitAnim = "getup_r", exitDict = "creatures@deer@getup"
- },
- [`a_c_boar`] = {
- deathAnim = "dead_right", deathDict = "creatures@boar@move",
- exitAnim = "getup_r", exitDict = "creatures@boar@getup"
- },
- [`a_c_boar_02`] = {
- deathAnim = "dead_right", deathDict = "creatures@boar@move",
- exitAnim = "getup_r", exitDict = "creatures@boar@getup"
- },
- [`a_c_chicken`] = {
- deathAnim = "dead_right", deathDict = "creatures@chicken@move",
- exitAnim = "getup_r", exitDict = "creatures@chicken@getup"
- },
- [`a_c_pig`] = {
- deathAnim = "dead_right", deathDict = "creatures@pig@move",
- exitAnim = "getup_r", exitDict = "creatures@pig@getup"
- },
- [`a_c_sharkhammer`] = {
- deathAnim = "dead_right", deathDict = "creatures@sharkhammer@move",
- exitAnim = "getup_r", exitDict = "creatures@sharkhammer@getup"
- },
- [`a_c_sharktiger`] = {
- deathAnim = "dead_right", deathDict = "creatures@sharktiger@move",
- exitAnim = "getup_r", exitDict = "creatures@sharktiger@getup"
- },
- [`a_c_crow`] = {
- deathAnim = "dead_down", deathDict = "creatures@crow@move",
- exitAnim = "nill", exitDict = "creatures@pug@move", -- no get up anim
- },
- [`a_c_pigeon`] = {
- deathAnim = "dead_down", deathDict = "creatures@pigeon@move",
- exitAnim = "nill", exitDict = "creatures@pug@move", -- no get up anim
- },
+ [`ft-raccoon`] = { deathAnim = "dead_right", deathDict = "creatures@cat@move", exitAnim = "getup_r", exitDict = "creatures@cat@getup", sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@base" },
+ [`a_c_hen`] = { deathAnim = "dead_right", deathDict = "creatures@hen@move", exitAnim = "getup_r", exitDict = "creatures@hen@getup" },
+ [`a_c_rabbit_01`] = { deathAnim = "dead_right", deathDict = "creatures@rabbit@move", exitAnim = "getup_r", exitDict = "creatures@rabbit@getup", sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base" },
+ [`a_c_rabbit_02`] = { deathAnim = "dead_right", deathDict = "creatures@rabbit@move", exitAnim = "getup_r", exitDict = "creatures@rabbit@getup", sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base" },
+ [`a_c_rat`] = { deathAnim = "dead_right", deathDict = "creatures@rat@move", exitAnim = "getup_r", exitDict = "creatures@rat@getup" },
+ [`a_c_deer`] = { deathAnim = "dead_right", deathDict = "creatures@deer@move", exitAnim = "getup_r", exitDict = "creatures@deer@getup" },
+ [`a_c_boar`] = { deathAnim = "dead_right", deathDict = "creatures@boar@move", exitAnim = "getup_r", exitDict = "creatures@boar@getup" },
+ [`a_c_boar_02`] = { deathAnim = "dead_right", deathDict = "creatures@boar@move", exitAnim = "getup_r", exitDict = "creatures@boar@getup" },
+ [`a_c_chicken`] = { deathAnim = "dead_right", deathDict = "creatures@chicken@move", exitAnim = "getup_r", exitDict = "creatures@chicken@getup" },
+ [`a_c_pig`] = { deathAnim = "dead_right", deathDict = "creatures@pig@move", exitAnim = "getup_r", exitDict = "creatures@pig@getup" },
+ [`a_c_sharkhammer`] = { deathAnim = "dead_right", deathDict = "creatures@sharkhammer@move", exitAnim = "getup_r", exitDict = "creatures@sharkhammer@getup" },
+ [`a_c_sharktiger`] = { deathAnim = "dead_right", deathDict = "creatures@sharktiger@move", exitAnim = "getup_r", exitDict = "creatures@sharktiger@getup" },
+ [`a_c_crow`] = { deathAnim = "dead_down", deathDict = "creatures@crow@move", exitAnim = "nill", exitDict = "creatures@pug@move" },
+ [`a_c_pigeon`] = { deathAnim = "dead_down", deathDict = "creatures@pigeon@move", exitAnim = "nill", exitDict = "creatures@pug@move" },
},
Monekys = {
- [`ft-chimpanzee`] = {
- deathAnim = "dead", deathDict = "dead_a",
- exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
- },
- [`a_c_chimp`] = {
- deathAnim = "dead", deathDict = "dead_a",
- exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
- },
- [`a_c_chimp_02`] = {
- deathAnim = "dead", deathDict = "dead_a",
- exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
- },
- [`a_c_rhesus`] = {
- deathAnim = "dead", deathDict = "dead_a",
- exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0"
- },
- [`ft-capmonkey2`] = {
- deathAnim = "dead_right", deathDict = "creatures@pug@move",
- exitAnim = "getup_r", exitDict = "creatures@pug@getup",
- sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base"
- },
+ [`ft-chimpanzee`] = { deathAnim = "dead", deathDict = "dead_a", exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0" },
+ [`a_c_chimp`] = { deathAnim = "dead", deathDict = "dead_a", exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0" },
+ [`a_c_chimp_02`] = { deathAnim = "dead", deathDict = "dead_a", exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0" },
+ [`a_c_rhesus`] = { deathAnim = "dead", deathDict = "dead_a", exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0" },
+ [`ft-capmonkey2`] = { deathAnim = "dead_right", deathDict = "creatures@pug@move", exitAnim = "getup_r", exitDict = "creatures@pug@getup", sitAnim = "base", sitDict = "creatures@pug@amb@world_dog_sitting@base" },
}
}
\ No newline at end of file
diff --git a/shared/itemcontrol.lua b/shared/itemcontrol.lua
index d388422..7281f1c 100644
--- a/shared/itemcontrol.lua
+++ b/shared/itemcontrol.lua
@@ -1,12 +1,28 @@
--- Function to register items as usable for ESX, QBX, and QBcore --
+--[[
+ Usable Items & Inventory Utilities Module
+ -------------------------------------------
+ This module provides functions for:
+ • Registering items as usable across different inventory systems (ESX, QBcore, QBX).
+ • Retrieving an item's image as a NUI link.
+ • Adding and removing items from a player's inventory.
+ • Toggling items in inventory (with server event for exploit protection).
+ • Checking for item duplication exploits.
+ • Handling tool durability mechanics.
+ • Checking item availability and retrieving inventory.
+ • Granting random rewards from a reward pool.
+ • Checking if a player can carry specific items based on weight.
+]]
+
+-------------------------------------------------------------
+-- Registering Usable Items
+-------------------------------------------------------------
+
+--- Registers an item as usable for ESX, QBcore, or QBX.
---
---- This function registers an item as usable across different inventory systems such as ESX, QBcore, and QBX.
---- It checks which inventory system is active and registers the usable item accordingly.
+--- @param item string The name of the item.
+--- @param funct function The function to execute when the item is used.
---
----@param item string The name of the item to be registered as usable.
----@param funct function The function to execute when the item is used.
----
----@usage
+--- @usage
--- ```lua
--- createUseableItem("health_potion", function(source)
--- -- Code to consume the health potion
@@ -23,23 +39,24 @@ function createUseableItem(item, funct)
elseif isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qbx_core", item)
exports[QBXExport]:CreateUseableItem(item, funct)
+ else
+ print("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
end
end
--- Simple function to grab the item's image from inventories and retrieve it as a nui:// link --
+-------------------------------------------------------------
+-- Item Image Retrieval
+-------------------------------------------------------------
+
+--- Retrieves the NUI link for an item's image from the active inventory system.
---
---- This function retrieves the image URL of an item from various inventory systems and formats it as a `nui://` link.
---- It supports multiple inventory systems such as OXInv, QSInv, CoreInv, OrigenInv, QBInv, and CodeMInv.
+--- @param item string The item name.
+--- @return string string A `nui://` link to the item's image, or an empty string if not found.
---
----@param item string The name of the item whose image is to be retrieved.
----@return string link The `nui://` link to the item's image. Returns an empty string if the inventory system is not detected or the item doesn't exist.
----
----@usage
+--- @usage
--- ```lua
--- local imageLink = invImg("health_potion")
---- if imageLink ~= "" then
---- print(imageLink)
---- end
+--- if imageLink ~= "" then print(imageLink) end
--- ```
function invImg(item)
local imgLink = ""
@@ -50,33 +67,39 @@ function invImg(item)
imgLink = "nui://"..QSInv.."/html/images/"..(Items[item].image or "")
elseif isStarted(CoreInv) then
imgLink = "nui://"..CoreInv.."/html/img/"..(Items[item].image or "")
+ elseif isStarted(CodeMInv) then
+ imgLink = "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "")
elseif isStarted(OrigenInv) then
imgLink = "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "")
elseif isStarted(QBInv) then
imgLink = "nui://"..QBInv.."/html/images/"..(Items[item].image or "")
- elseif isStarted(CodeMInv) then
- imgLink = "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "")
else
- print("^4ERROR^7: ^2No Inventory detected for invImg ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Inventory detected for invImg - Check exports.lua")
end
end
return imgLink
end
+-------------------------------------------------------------
+-- Adding and Removing Items
+-------------------------------------------------------------
+
--- Adds an item to a player's inventory.
---
---- This function triggers a server event to add a specified amount of an item to the player's inventory.
+--- Triggers a server event (or local event) to add the specified item.
---
----@param item string The name of the item to add.
----@param amount number The quantity of the item to add.
----@param info table|nil Additional information or metadata for the item.
+--- @param item string The item name.
+--- @param amount number The quantity to add.
+--- @param info table|nil Additional metadata for the item.
+--- @param src number|nil Optional player source; if nil, defaults to the caller.
---
----@usage
+--- @usage
--- ```lua
--- addItem("health_potion", 2, { quality = "high" })
--- ```
function addItem(item, amount, info, src)
if not Items[item] then print("^6Bridge^7: ^1Error^7 - ^2Tried to give ^7'^3"..item.."^7'^2 but it doesn't exist") return end
+
if src then
TriggerEvent(getScript()..":server:toggleItem", true, item, amount, src, info)
else
@@ -86,17 +109,23 @@ end
--- Removes an item from a player's inventory.
---
---- This function triggers a server event to remove a specified amount of an item from the player's inventory.
+--- Triggers a server event (or local event) to remove the specified item.
---
----@param item string The name of the item to remove.
----@param amount number The quantity of the item to remove.
+--- @param item string The item name.
+--- @param amount number The quantity to remove.
+--- @param src number|nil Optional player source.
+--- @param slot number|nil Optional inventory slot.
---
----@usage
+--- @usage
--- ```lua
--- removeItem("health_potion", 1)
--- ```
function removeItem(item, amount, src, slot)
- if not Items[item] then print("^6Bridge^7: ^1Error^7 - ^2Tried to remove ^7'^3"..item.."^7'^2 but it doesn't exist") return end
+ if not Items[item] then
+ print("^6Bridge^7: ^1Error^7 - ^2Tried to remove ^7'^3"..item.."^7'^2 but it doesn't exist")
+ return
+ end
+
if src then
TriggerEvent(getScript()..":server:toggleItem", false, item, amount, src, nil, slot)
else
@@ -104,161 +133,188 @@ function removeItem(item, amount, src, slot)
end
end
---- Server event handler to toggle items in a player's inventory.
+-------------------------------------------------------------
+-- Toggle Items (Server Event)
+-------------------------------------------------------------
+
+--- Server event handler to toggle (add or remove) an item from a player's inventory.
---
---- This event handles adding or removing items based on the parameters received.
---- It supports multiple inventory systems and includes exploit protection to prevent duplication.
+--- This function validates the item, then calls the appropriate export functions based on the active inventory system.
+--- It also includes exploit protection via the dupeWarn function.
---
----@param give boolean Indicates whether to add (`true`) or remove (`false`) the item.
----@param item string The name of the item to toggle.
----@param amount number The quantity of the item to toggle.
----@param newsrc number|nil The source ID of the player. If `nil`, it defaults to the event source.
----@param info table|nil Additional information or metadata for the item.
+--- @param give boolean True to add the item, false to remove.
+--- @param item string The item name.
+--- @param amount number The quantity.
+--- @param newsrc number|nil The player source; defaults to event source.
+--- @param info table|nil Additional metadata.
+--- @param slot number|nil Optional inventory slot.
---
----@usage
+--- @usage
--- ```lua
---- TriggerServerEvent("script:server:toggleItem", true, "health_potion", 1)
+--- TriggerServerEvent(getScript()..":server:toggleItem", true, "health_potion", 1)
--- ```
RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount, newsrc, info, slot)
- if not Items[item] then print("^6Bridge^7: ^1Error^7 - ^2Tried to "..(tostring(give) == "true" and "add" or "remove").." ^7'^3"..item.."^7'^2 but it doesn't exist") return end
+ if not Items[item] then
+ print("^6Bridge^7: ^1Error^7 - ^2Tried to "..(tostring(give) == "true" and "add" or "remove").." '^3"..item.."^7' but it doesn't exist")
+ return
+ end
+
local src = newsrc or source
- local addremove = (tostring(give) == "true" and "addItem" or "removeItem")
- debugPrint("^6Bridge^7: ^3toggleItem ^2triggered^7: ^6"..addremove.."^7 - '"..tostring(item).."' x"..(tostring(amount) or "1"))
- local remamount = (amount and amount or 1)
+ local action = (tostring(give) == "true" and "addItem" or "removeItem")
+ local remamount = amount or 1
if item == nil then return end
+
+ -- Grab the current inventory (you can expand usage of 'inv' if needed)
+ local invName = ""
if give == 0 or give == false then
- if hasItem(item, amount and amount or 1, src) then -- Check if the player has the item
- if isStarted(OXInv) then
- local success = exports[OXInv]:RemoveItem(src, item, (amount and amount or 1), nil)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OXInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
- elseif isStarted(QSInv) then
- local success = exports[QSInv]:RemoveItem(src, item, amount)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
+ if not hasItem(item, amount or 1, src) then
+ dupeWarn(src, item, amount)
- elseif isStarted(CoreInv) then
- if isStarted(QBExport) then
- Core.Functions.GetPlayer(src).Functions.RemoveItem(item, amount, nil)
- elseif isStarted(ESXExport) then
- ESX.GetPlayerFromId(src).removeInventoryItem(item, count)
- end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CoreInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
+ else
+ if isStarted(OXInv) then invName = OXInv
+ exports[OXInv]:RemoveItem(src, item, remamount, nil)
- elseif isStarted(OrigenInv) then
- local success = exports[OrigenInv]:RemoveItem(src, item, amount)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OrigenInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
+ elseif isStarted(QSInv) then invName = QSInv
+ exports[QSInv]:RemoveItem(src, item, remamount)
- elseif isStarted(CodeMInv) then
- local success = exports[CodeMInv]:RemoveItem(src, item, amount)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CodeMInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
+ elseif isStarted(CoreInv) then invName = CoreInv
+ exports[CoreInv]:removeItem(src, item, remamount)
- elseif isStarted(QBInv) then
+ elseif isStarted(OrigenInv) then invName = OrigenInv
+ exports[OrigenInv]:removeItem(src, item, remamount)
+
+ elseif isStarted(CodeMInv) then invName = CodeMInv
+ exports[CodeMInv]:RemoveItem(src, item, remamount)
+
+ elseif isStarted(QBInv) then invName = QBInv
while remamount > 0 do
if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then
remamount -= 1
else
- print("^1Error removing "..data.item.." Amount left to remove: "..remamount.."^7")
+ print("^1Error removing "..item.." Amount left: "..remamount)
break
end
end
if Config.Crafting.showItemBox then
- TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', src, Items[item], "remove", (amount and amount or 1))
+ TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "remove", amount or 1)
end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QBInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
- elseif isStarted(PSInv) then
+ elseif isStarted(PSInv) then invName = PSInv
while remamount > 0 do
- if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1) then
+ if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then
remamount -= 1
else
- print("^1Error removing "..data.item.." Amount left to remove: "..remamount.."^7")
+ print("^1Error removing "..item.." Amount left: "..remamount)
break
end
end
if Config.Crafting.showItemBox then
- TriggerClientEvent('inventory:client:ItemBox', src, Items[item], "remove", (amount and amount or 1))
+ TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "remove", amount or 1)
end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..PSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
- else
- print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
end
- else
- dupeWarn(src, item, amount) -- Trigger exploit protection
+ -----
+ -- Fallback for if no inventory found:
+ -----
+ if invName == "" then
+ if isStarted(QBExport) or isStarted(QBXExport) then -- if qbcore or qbxcore, just use core functions
+ invName = isStarted(QBXExport) and QBXExport or isStarted(QBExport) and QBExport
+ Core.Functions.GetPlayer(src).Functions.RemoveItem(item, remamount, slot)
+
+ elseif ESX and isStarted(ESXExport) then -- if esx then use core functions
+ invName = ESX
+ ESX.GetPlayerFromId(src).removeInventoryItem(item, remamount)
+
+ end
+ end
+ -- Final check for if inventory was found
+ if invName == "" then
+ print("^4ERROR^7: No Inventory detected - Check starter.lua")
+ else
+ debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1))
+ end
end
else
- local amount = amount and amount or 1
- if isStarted(OXInv) then
- local success = exports[OXInv]:AddItem(src, item, amount or 1, info)
- if not Items[item] or not Items[item].label then
- print("^1Error^7: "..addremove.." ["..OXInv.."] Player("..src..") "..Items[item]?.label.."("..item..") x"..(amount or 1))
+ local amountToAdd = amount or 1
+ if isStarted(OXInv) then invName = OXInv
+ exports[OXInv]:AddItem(src, item, amountToAdd, info, slot)
+
+ elseif isStarted(QSInv) then invName = QSInv
+ exports[QSInv]:AddItem(src, item, amountToAdd, slot, info)
+
+ elseif isStarted(CoreInv) then invName = CoreInv
+ exports[CoreInv]:addItem(src, item, amountToAdd, info)
+
+ elseif isStarted(CodeMInv) then invName = CodeMInv
+ exports[CodeMInv]:AddItem(src, item, amountToAdd, slot, info)
+
+ elseif isStarted(OrigenInv) then invName = OrigenInv
+ exports[OrigenInv]:addItem(src, item, amountToAdd, info, slot)
+
+ elseif isStarted(QBInv) then invName = QBInv
+ if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then
+ TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "").."inventory:client:ItemBox", src, Items[item], "add", amountToAdd)
end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OXInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
- elseif isStarted(QSInv) then
- local success = exports[QSInv]:AddItem(src, item, amount)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
-
- elseif isStarted(CoreInv) then
- if isStarted(QBExport) or isStarted(QBXExport) then
- Core.Functions.GetPlayer(src).Functions.AddItem(item, amount, nil, nil)
- elseif isStarted(ESXExport) then
- ESX.GetPlayerFromId(src).addInventoryItem(item, amount)
- end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CoreInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
-
- elseif isStarted(CodeMInv) then
- local success = exports[CodeMInv]:AddItem(src, item, amount)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..CodeMInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
- elseif isStarted(OrigenInv) then
- local success = exports[OrigenInv]:AddItem(src, item, amount)
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..OrigenInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
-
- elseif isStarted(QBInv) then
- if Core.Functions.GetPlayer(src).Functions.AddItem(item, amount or 1, nil, info) then
- TriggerClientEvent((isStarted(QBInv) and QBInvNew and "qb-" or "")..'inventory:client:ItemBox', src, Items[item], "add", amount and amount or 1)
- end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..QBInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
-
- elseif isStarted(PSInv) then
- if Core.Functions.GetPlayer(src).Functions.AddItem(item, amount or 1, nil, info) then
+ elseif isStarted(PSInv) then invName = PSInv
+ if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then
if Config.Crafting.showItemBox then
- TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "add", amount and amount or 1)
+ TriggerClientEvent("inventory:client:ItemBox", src, Items[item], "add", amountToAdd)
end
end
- debugPrint("^6Bridge^7: ^3"..addremove.."^7[^6"..PSInv.."^7] ^2Player^7("..src..") ^6"..Items[item].label.."^7("..item..") x^5"..(amount and amount or "1").."^7")
+ end
+
+ if invName == "" then
+ if isStarted(QBExport) or isStarted(QBXExport) then -- if qbcore or qbxcore, just use core functions
+ invName = isStarted(QBXExport) and QBXExport or isStarted(QBExport) and QBExport
+ Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info)
+
+ elseif ESX and isStarted(ESXExport) then -- if esx then use core functions
+ invName = ESX
+ ESX.GetPlayerFromId(src).addInventoryItem(item, amountToAdd)
+ end
+ end
+
+ -- Final check for if inventory was found
+ if invName == "" then
+ print("^4ERROR^7: No Inventory detected - Check starter.lua")
else
- print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
+ debugPrint("^6Bridge^7: ^3"..action.."^7["..invName.."] Player("..src..") "..Items[item].label.."("..item..") x"..(amount or 1))
end
end
end)
---- Protects against item duplication exploits by warning and potentially kicking the player.
+-------------------------------------------------------------
+-- Exploit Protection
+-------------------------------------------------------------
+
+--- Warns and kicks a player if they try to remove an item they don't have.
---
---- This function is called when an attempt is made to remove an item that the player does not possess.
---- It logs the incident and kicks the player if `debugMode` is not enabled.
----
---- @param src number The source ID of the player attempting the exploit.
---- @param item string The name of the item being exploited.
+--- @param src number The player's source ID.
+--- @param item string The item name.
---
--- @usage
--- ```lua
--- dupeWarn(playerId, "health_potion")
--- ```
-function dupeWarn(src, item)
+function dupeWarn(src, item, amount)
local name = getPlayer(src).name
- print("^5DupeWarn^7: "..name.." (^1"..tostring(src).."^7) ^2Tried to remove item ^7'^3"..item.."^7'^2 but it wasn't there^7")
+ print("^5DupeWarn^7: "..name.." (^1"..tostring(src).."^7) ^2Tried to remove item '^3"..item.."^7' but it wasn't there")
if not debugMode then
DropPlayer(src, name.."("..tostring(src)..") Kicked for suspected duplicating items: "..item)
end
print("^5DupeWarn^7: "..name.."(^1"..tostring(src).."^7) ^2Dropped from server - exploit protection detected an item not being found in players inventory^7")
end
---- Breaks a tool by reducing its durability or removing it if durability reaches zero.
+-------------------------------------------------------------
+-- Tool Durability & Metadata
+-------------------------------------------------------------
+
+--- Reduces the durability of a tool by a specified damage amount.
---
---- This function handles the durability mechanics for tools. If a tool's durability drops to zero or below,
---- it removes the tool from the player's inventory and plays a breaking sound.
+--- If durability reaches zero or below, the tool is removed and a break sound is played.
---
---- @param data table A table containing data about the tool being used.
---- - **item** (`string`): The name of the tool item.
---- - **damage** (`number`): The amount of durability damage to apply.
+--- @param data table Contains:
+--- - item (string): The tool's name.
+--- - damage (number): The damage % to apply.
---
--- @usage
--- ```lua
@@ -277,19 +333,20 @@ function breakTool(data) -- WIP
end
end
---- Retrieves the durability and slot of an item in a player's inventory.
+--- Retrieves the durability and slot number of an item in a player's inventory.
---
---- This function searches the player's inventory for the specified item and returns its durability and slot number.
+--- Searches through the player's inventory for the specified item.
---
---- @param item string The name of the item to check.
---- @return number|nil The durability of the item. Returns `nil` if not found.
---- @return number|nil The slot number of the item. Returns `nil` if not found.
+--- @param item string The item name.
+--- @return number|nil number The durability, or nil if not found.
+--- @return number|nil number The slot number, or nil if not found.
---
--- @usage
--- ```lua
--- local durability, slot = getDurability("drill")
--- if durability then
--- print("Durability:", durability)
+--- print("Slot:", slot)
--- end
--- ```
function getDurability(item)
@@ -297,7 +354,7 @@ function getDurability(item)
local durability = nil
if isStarted(QBInv) or isStarted(PSInv) then
local itemcheck = Core.Functions.GetPlayerData().items
- for k, v in pairs(itemcheck) do
+ for _, v in pairs(itemcheck) do
if v.name == item then
if v.slot <= lowestSlot then
lowestSlot = v.slot
@@ -309,47 +366,86 @@ function getDurability(item)
if isStarted(OXInv) then
local itemcheck = exports[OXInv]:Search('slots', item)
- for k, v in pairs(itemcheck) do
+ for _, v in pairs(itemcheck) do
if v.slot <= lowestSlot then
debugPrint(v.slot, itemcheck[k].metadata.durability)
lowestSlot = v.slot
- durability = itemcheck[k].metadata.durability
+ durability = v.metadata.durability
end
end
end
if isStarted(QSInv) then
local itemcheck = exports[QSInv]:getUserInventory()
- for k, v in pairs(itemcheck) do
+ for _, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
- durability = itemcheck[k].metadata.durability
+ durability = v.metadata.durability
+ end
+ end
+ end
+
+ if isStarted(CoreInv) then
+ local itemcheck = exports[CoreInv]:getInventory()
+ for _, v in pairs(itemcheck) do
+ if v.name == item and v.slot <= lowestSlot then
+ lowestSlot = v.slot
+ durability = v.metadata.durability
+ end
+ end
+ end
+
+ if isStarted(CodeMInv) then
+ local itemcheck = exports[CodeMInv]:GetClientPlayerInventory()
+ for _, v in pairs(itemcheck) do
+ if v.name == item and v.slot <= lowestSlot then
+ lowestSlot = v.slot
+ durability = v.metadata.durability
end
end
end
if isStarted(OrigenInv) then
local itemcheck = exports[OrigenInv]:getPlayerInventory()
- for k, v in pairs(itemcheck) do
+ for _, v in pairs(itemcheck) do
if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot
- durability = itemcheck[k].metadata.durability
+ durability = v.metadata.durability
end
end
end
+
+ -- For ESX default inventory (es_extended)
+ if ESX and isStarted(ESXExport) then
+ local xPlayer = ESX.GetPlayerData() or {}
+ if xPlayer.inventory then
+ for _, v in ipairs(xPlayer.inventory) do
+ if v.name == item then
+ -- Optionally use a slot field if available; otherwise, use the index
+ if v.slot and v.slot <= lowestSlot then
+ lowestSlot = v.slot
+ end
+ if v.metadata and v.metadata.durability then
+ durability = v.metadata.durability
+ end
+ end
+ end
+ end
+ end
+
return durability, lowestSlot
end
---- Server event handler to set metadata for an item in a player's inventory.
+--- Server event handler to set metadata for an item.
---
---- This event updates the metadata (e.g., durability) of an item in the player's inventory.
+--- Updates item metadata (e.g. durability) for the player's inventory based on slot.
---
----@param data table A table containing metadata information.
---- - **item** (`string`): The name of the item.
---- - **slot** (`number`): The slot number of the item in the inventory.
---- - **metadata** (`table`): The metadata to set for the item.
+--- @param data table Contains:
+--- - item (string): The item name.
+--- - slot (number): The inventory slot.
+--- - metadata (table): The metadata to set.
---
----@usage
+--- @usage
--- ```lua
--- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } })
--- ```
@@ -361,171 +457,61 @@ RegisterNetEvent(getScript()..":server:setMetaData", function(data)
Player.PlayerData.items[data.slot].info = data.metadata
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability
Player.Functions.SetInventory(Player.PlayerData.items)
- end
- if isStarted(OXInv) then
- exports[OXInv]:SetMetadata(source, data.slot, data.metadata)
- end
+ elseif isStarted(OXInv) then
+ exports[OXInv]:SetDurability(src, data.slot, data.metadata.durability)
- if isStarted(QSInv) then
- exports[QSInv]:SetItemMetadata(source, data.slot, data.metadata)
- end
+ elseif isStarted(QSInv) then
+ exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata)
- if isStarted(OrigenInv) then
- local item = exports[OrigenInv]:GetItemBySlot(source, data.slot)
- if item then
- exports[OrigenInv]:SetItemData(source, item.name, "durability", data.metadata.durability)
- end
+ elseif isStarted(CoreInv) then
+ exports[CoreInv]:setMetadata(src, data.slot, data.metadata)
+
+ elseif isStarted(CodeMInv) then
+ exports[CodeMInv]:SetItemMetadata(src, data.slot, data.metadata)
+
+ elseif isStarted(OrigenInv) then
+ exports[OrigenInv]:setMetadata(src, data.slot, data.metadata)
end
end)
---- Checks if a player has the specified items in their inventory.
+-------------------------------------------------------------
+-- Random Reward
+-------------------------------------------------------------
+
+--- Grants a random reward from a predefined reward pool if the player is eligible.
---
---- This function verifies whether a player possesses the required quantity of specified items.
---- It supports multiple inventory systems and provides detailed feedback on item availability.
+--- Checks if the item qualifies for a reward, removes the item, then calculates a random reward based on rarity.
---
----@param items string|table A single item name or a table of item names with their required amounts.
----@param amount number The quantity required for each item. Defaults to `1` if not specified.
----@param src number|nil The source ID of the player. If `nil`, it defaults to the caller.
----@return boolean Returns `true` if the player has all the required items in the specified amounts.
----@return table|nil Returns a table detailing which items are present or missing if not all items are found.
----
----@usage
---- ```lua
---- local hasAll, details = hasItem({"health_potion", "mana_potion"}, 2, playerId)
---- if hasAll then
---- -- Proceed with action
---- else
---- -- Inform the player about missing items
---- end
---- ```
-function hasItem(items, amount, src)
- local amount = amount and amount or 1
- local grabInv, foundInv = getPlayerInv(src)
- if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end
-
- if grabInv then
- local hasTable = {}
- for item, amt in pairs(items) do
- if not Items[item] then print("^4ERROR^7: ^2Script can't find ingredient item in Shared Items - ^1"..item.."^7") end
- local count = 0
- for _, itemData in pairs(grabInv) do
- if itemData and (itemData.name == item) then count += (itemData.count or itemData.amount or 1) end
- end
- foundInv = foundInv:gsub("%-", "^7-^6"):gsub("%_", "^7_^6")
- local foundMessage = "^6Bridge^7: ^3hasItem^7[^6"..foundInv.."^7]: "..tostring(item).." ^3"..count.."^7/^3"..amt
- if count >= amt then foundMessage = foundMessage.." ^5FOUND^7" else foundMessage = foundMessage .." ^1NOT FOUND^7" end
- debugPrint(foundMessage)
- hasTable[item] = { hasItem = count >= amt, count = count }
- end
- for k, v in pairs(hasTable) do if not v.hasItem then return false, hasTable end end
- return true, hasTable
- end
-end
-
---- Retrieves a player's inventory from the active inventory system.
----
---- This function fetches the player's inventory based on the active inventory system.
---- It supports multiple systems including OXInv, QSInv, OrigenInv, CoreInv, CodeMInv, QBInv, and PSInv.
----
----@param src number|nil The source ID of the player. If `nil`, it fetches the current player's inventory.
----@return table|nil The inventory items of the player.
----@return string|nil The name of the inventory system being used.
----
----@usage
---- ```lua
---- local inventory, system = getPlayerInv(playerId)
---- if inventory then
---- -- Process inventory
---- end
---- ```
-function getPlayerInv(src)
- local grabInv = nil
- local foundInv = ""
-
- if isStarted(OXInv) then
- foundInv = OXInv
- if src then grabInv = exports[OXInv]:GetInventoryItems(src)
- else grabInv = exports[OXInv]:GetPlayerItems() end
-
- elseif isStarted(QSInv) then
- foundInv = QSInv
- if src then grabInv = exports[QSInv]:GetInventory(src)
- else grabInv = exports[QSInv]:getUserInventory() end
-
- elseif isStarted(OrigenInv) then
- foundInv = OrigenInv
- if src then grabInv = exports[OrigenInv]:GetInventory(src)
- else grabInv = exports[OrigenInv]:getPlayerInventory() end
-
- elseif isStarted(CoreInv) then
- foundInv = CoreInv
- if src then
- if isStarted(QBExport) or isStarted(QBXExport) then
- grabInv = Core.Functions.GetPlayer(src).PlayerData.items
- elseif isStarted(ESXExport) then
- local Player = ESX.GetPlayerFromId(src)
- grabInv = Player.getInventory(false)
- end
- else
- local p = promise.new()
- Core.Functions.TriggerCallback('core_inventory:server:getInventory', function(cb) p:resolve(cb) end)
- local result = Citizen.Await(p)
- if type(result) == "string" then result = json.decode(result) end
- grabInv = result
- end
-
- elseif isStarted(CodeMInv) then
- foundInv = CodeMInv
- if src then grabInv = exports[CodeMInv]:GetInventory(src)
- else grabInv = exports[CodeMInv]:GetClientPlayerInventory() end
-
- elseif isStarted(QBInv) then
- foundInv = QBInv
- if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
- else grabInv = Core.Functions.GetPlayerData().items end
-
- elseif isStarted(PSInv) then
- foundInv = PSInv
- if src then grabInv = Core.Functions.GetPlayer(src).PlayerData.items
- else grabInv = Core.Functions.GetPlayerData().items end
-
- else
- print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
- end
- return grabInv, foundInv
-end
-
---- Generates a random reward from a predefined reward pool.
----
---- This function is intended for job scripts where players receive random rewards upon completing certain tasks.
---- It ensures that the player has the required item before attempting to grant a reward.
----
----@param itemName string The name of the item to check for eligibility to receive a reward.
+--- @param itemName string The item name to check.
---
---@usage
--- ```lua
--- getRandomReward("gold_ring")
--- ```
-function getRandomReward(itemName) -- Intended for job scripts
+function getRandomReward(itemName)
if Config.Rewards.RewardPool then
local reward = false
- if type(Config.Rewards.RewardItem) == "string" then Config.Rewards.RewardItem = { Config.Rewards.RewardItem } end
+ if type(Config.Rewards.RewardItem) == "string" then
+ Config.Rewards.RewardItem = { Config.Rewards.RewardItem }
+ end
for k, v in pairs(Config.Rewards.RewardItem) do
- if v == itemName then reward = true break end
+ if v == itemName then
+ reward = true
+ break
+ end
end
if reward then
removeItem(itemName, 1)
local totalRarity = 0
- for i=1, #Config.Rewards.RewardPool do
+ for i = 1, #Config.Rewards.RewardPool do
totalRarity += Config.Rewards.RewardPool[i].rarity
end
- debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Total Rarity ^7'^6"..totalRarity.."^7'")
-
+ debugPrint("^6Bridge^7: ^3getRandomReward^7: Total Rarity '"..totalRarity.."'")
local randomNum = math.random(1, totalRarity)
- debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Random Number ^7'^6"..randomNum.."^7'")
+ debugPrint("^6Bridge^7: ^3getRandomReward^7: Random Number '"..randomNum.."'")
local currentRarity = 0
- for i=1, #Config.Rewards.RewardPool do
+ for i = 1, #Config.Rewards.RewardPool do
currentRarity += Config.Rewards.RewardPool[i].rarity
if randomNum <= currentRarity then
debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Selected toy ^7'^6"..Config.Rewards.RewardPool[i].item.."^7'")
@@ -537,24 +523,25 @@ function getRandomReward(itemName) -- Intended for job scripts
end
end
---- Checks if a player can carry specific items in their inventory.
+-------------------------------------------------------------
+-- Carry Capacity Check
+-------------------------------------------------------------
+
+--- Checks if a player can carry the specified items based on weight.
---
---- This function determines whether a player has enough capacity to carry the specified items.
---- It considers the weight of each item and the player's current inventory weight.
+--- Calculates the current total weight in the player's inventory and determines whether adding the new items would exceed capacity.
---
----@param itemTable table A table where keys are item names and values are the quantities to check.
----@param src number The source ID of the player.
----@return table A table where keys are item names and values are booleans indicating if the player can carry the specified quantity.
+--- @param itemTable table A table where keys are item names and values are required quantities.
+--- @param src number The player's source ID.
+--- @return table A table mapping each item to a boolean indicating if it can be carried.
---
----@usage
---- ```lua
---- local canCarry = canCarry({ ["health_potion"] = 2, ["mana_potion"] = 3 }, playerId)
---- if canCarry["health_potion"] and canCarry["mana_potion"] then
---- -- Proceed with adding items
+--- @usage
+--- local carryCheck = canCarry({ ["health_potion"] = 2, ["mana_potion"] = 3 }, playerId)
+--- if carryCheck["health_potion"] and carryCheck["mana_potion"] then
+--- -- Player can carry items.
--- else
---- -- Inform the player they can't carry all items
+--- -- Notify player.
--- end
---- ```
function canCarry(itemTable, src)
local resultTable = {}
if src then
@@ -565,16 +552,28 @@ function canCarry(itemTable, src)
elseif isStarted(QSInv) then
for k, v in pairs(itemTable) do
- resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v)
+ resultTable[k] = exports[QSInv]:CanCarryItem(src, k, v)
end
elseif isStarted(CoreInv) then
- --??
-
- elseif isStarted(CodeMInv) then
for k, v in pairs(itemTable) do
- local weight = Items[k].weight
- resultTable[k] = exports[CodeMInv]:CanCarryItem(src, weight, v)
+ resultTable[k] = exports[CoreInv]:canCarry(src, k, v)
+ end
+
+ elseif isStarted(CodeMInv) then --- This really needs updating, their docs are confusing..
+ local items = getPlayerInv(src)
+ local totalWeight = 0
+ if not items then return false end
+ for _, item in pairs(items) do
+ totalWeight += (item.weight * item.amount)
+ end
+ for k, v in pairs(itemTable) do
+ local itemInfo = Items[k]
+ if not itemInfo then
+ resultTable[k] = true
+ else
+ resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight
+ end
end
elseif isStarted(OrigenInv) then
@@ -583,21 +582,18 @@ function canCarry(itemTable, src)
end
elseif isStarted(QBInv) or isStarted(PSInv) then
- local Player = Core.Functions.GetPlayer(src)
- local items = Player.PlayerData.items
- local weight, totalWeight = 0, 0
+ local items = getPlayerInv(src)
+ local totalWeight = 0
if not items then return false end
- for _, item in pairs(items) do weight += item.weight * item.amount end
-
- totalWeight = tonumber(weight)
-
+ for _, item in pairs(items) do
+ totalWeight += (item.weight * item.amount)
+ end
for k, v in pairs(itemTable) do
local itemInfo = Items[k]
if not itemInfo and not Player.Offline then
- triggerNotify(nil, 'Item does not exist', 'error', src)
resultTable[k] = true
else
- resultTable[k] = (totalWeight + (Items[k]['weight'] * v)) <= InventoryWeight
+ resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight
end
end
end
diff --git a/shared/jobfunctions.lua b/shared/jobfunctions.lua
index 2ceaf43..b338a3f 100644
--- a/shared/jobfunctions.lua
+++ b/shared/jobfunctions.lua
@@ -1,17 +1,32 @@
--- Global variable to track duty status
+--[[
+ Duty & Interaction Utilities Module
+ --------------------------------------
+ This module provides functions related to:
+ • Determining boss roles from Jobs and Gangs tables.
+ • Checking a player's job and duty status.
+ • Toggling duty state.
+ • Simulating player interactions such as hand washing, using toilets/urinals,
+ and teleporting via doors.
+]]
+
+-------------------------------------------------------------
+-- Global Duty Status
+-------------------------------------------------------------
onDuty = false
---- Scans the 'Jobs' and 'Gangs' tables to identify roles classified as Bosses.
+-------------------------------------------------------------
+-- Boss Role Detection
+-------------------------------------------------------------
+
+--- Scans the 'Jobs' and 'Gangs' tables to identify roles classified as bosses.
---
---- This function iterates through the specified role's grades within the `Jobs` or `Gangs` tables.
---- It identifies which grades are marked as bosses (`isboss`) or have bank authorization (`bankAuth`).
---- The function returns a table where each role maps to the lowest grade number that qualifies as a boss.
+--- Iterates through the specified role's grades in the Jobs or Gangs table and returns
+--- a table mapping the role to the lowest grade number that qualifies as a boss (isboss or bankAuth).
---
----@param role string The name of the job or gang role to check for boss grades.
+--- @param role string The job or gang role to check.
+--- @return table table A table with the role mapped to its boss grade number.
---
----@return table table A table containing roles mapped to their respective boss grade numbers.
----
----@usage
+--- @usage
--- ```lua
--- local bosses = makeBossRoles("police")
--- if bosses["police"] then
@@ -31,25 +46,28 @@ function makeBossRoles(role)
return boss
end
---- Checks if the player has a specific job and is on duty.
+-------------------------------------------------------------
+-- Job & Duty Checks
+-------------------------------------------------------------
+
+--- Checks if the player has a specific job (or gang) and is on duty.
---
---- This function verifies whether the player possesses the specified job and, if applicable,
---- whether they are currently on duty. It provides a notification if the player fails these checks.
+--- Verifies whether the player possesses the specified role. If the role is defined in the Jobs table,
+--- it also checks that the player is clocked in (onDuty). If the check fails, a notification is sent.
---
----@param job string The name of the job or gang to check.
+--- @param job string The job or gang to check.
+--- @return boolean Returns true if the player meets the criteria; false otherwise.
---
----@return boolean Returns `true` if the player has the job (and is on duty if required), otherwise `false`.
----
----@usage
+--- @usage
--- ```lua
--- if jobCheck("mechanic") then
---- -- Allow access to mechanic-related features
+--- -- Allow mechanic features.
--- else
---- -- Deny access or notify the player
+--- -- Deny access.
--- end
--- ```
function jobCheck(job)
- canDo = true
+ local canDo = true
if Jobs[job] then
if not hasJob(job) or not onDuty then
triggerNotify(nil, Loc[Config.Lan].error["not_clockedin"])
@@ -66,14 +84,12 @@ end
--- Toggles the player's duty status.
---
---- This function switches the player's duty state between on-duty and off-duty.
---- It integrates with QBcore's duty system if available; otherwise, it manually toggles the `onDuty` variable
---- and sends a notification to the player about their new duty status.
+--- Switches the player's duty state between on-duty and off-duty. If using QBcore,
+--- it triggers the appropriate server event. Otherwise, it manually toggles the onDuty variable and notifies the player.
---
----@usage
+--- @usage
--- ```lua
---- toggleDuty()
---- -- Player will receive a notification indicating their new duty status
+--- toggleDuty() -- Player receives a notification of their new duty status.
--- ```
function toggleDuty()
if isStarted(QBExport) or isStarted(QBXExport) then
@@ -88,22 +104,24 @@ function toggleDuty()
end
end
+-------------------------------------------------------------
+-- Interaction Functions
+-------------------------------------------------------------
+
--- Initiates the hand-washing action for the player.
---
---- This function triggers an animation and a progress bar to simulate the player washing their hands.
---- Upon completion, it sends a success notification. If the action is canceled, it notifies the player of the cancellation.
+--- Triggers an animation and a progress bar to simulate hand washing at the specified coordinates.
+--- On success, it notifies the player; if cancelled, it sends an error notification.
---
----@param data table A table containing the coordinates where the hand-washing action takes place.
---- - **coords** (`vector3`): The position where the hand-washing animation and camera are focused.
+--- @param data table A table containing:
+--- - coords (vector3): The location where the hand-washing action occurs.
---
----@return void
----
----@usage
+--- @usage
--- ```lua
--- washHands({ coords = vector3(200.0, 300.0, 40.0) })
---- -- Player will perform the hand-washing animation at the specified location
--- ```
-function washHands(data) local ped = PlayerPedId()
+function washHands(data)
+ local ped = PlayerPedId()
lookEnt(data.coords)
local cam = createTempCam(ped, data.coords)
if progressBar({
@@ -118,22 +136,21 @@ function washHands(data) local ped = PlayerPedId()
}) then
triggerNotify(nil, Loc[Config.Lan].success["washed_hands"], "success")
else
- triggerNotify(nil, Loc[Config.Lan].error["cancel"], 'error')
+ triggerNotify(nil, Loc[Config.Lan].error["cancel"], "error")
end
ClearPedTasks(ped)
end
--- Handles the player's interaction with a toilet or urinal.
---
---- This function manages the animations and progress bars associated with using a toilet or urinal.
---- Depending on whether the interaction is with a urinal (`data.urinal`), it plays the appropriate animation
---- and triggers server events upon successful completion. If the action is canceled, it notifies the player.
+--- Manages animations and progress bars for using a urinal or a toilet. If the action is successful,
+--- it triggers the appropriate server event (urinal usage) or notifies the player if cancelled.
---
----@param data table A table containing data about the toilet interaction.
---- - **urinal** (`boolean`): Indicates whether the interaction is with a urinal (`true`) or a toilet (`false`).
---- - **sitcoords** (`vector4`): The coordinates and heading for the seating animation when using a toilet.
+--- @param data table A table containing:
+--- - urinal (boolean): `true if using a urinal; false for a toilet.`
+--- - sitcoords (vector4): `Coordinates and heading for seating when using a toilet.`
---
----@usage
+--- @usage
--- ```lua
--- useToilet({ urinal = true })
--- -- Player uses a urinal with corresponding animations and notifications
@@ -154,7 +171,7 @@ function useToilet(data)
TriggerServerEvent(getScript().."server:Urinal")
else
lockInv(false)
- triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
+ triggerNotify(nil, Loc[Config.Lan].error["cancelled"], "error")
end
else
TaskStartScenarioAtPosition(PlayerPedId(), "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER", data.sitcoords.x, data.sitcoords.y, data.sitcoords.z, data.sitcoords[4], 0, 1, true)
@@ -167,24 +184,22 @@ function useToilet(data)
ClearPedTasks(PlayerPedId())
else
lockInv(false)
- triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
+ triggerNotify(nil, Loc[Config.Lan].error["cancelled"], "error")
end
end
end
--- Teleports the player to specified coordinates with a fade effect.
---
---- This function fades the screen out, moves the player to the target coordinates (`data.telecoords`),
---- sets the player's heading, and then fades the screen back in. It's commonly used for door interactions
---- or teleportation points within the game.
+--- Fades the screen out, moves the player to the target coordinates, sets the player's heading,
+--- then fades the screen back in. Commonly used for door interactions or teleportation points.
---
----@param data table A table containing teleportation data.
---- - **telecoords** (`vector4`): The target coordinates and heading for the teleportation.
+--- @param data table A table containing:
+--- - telecoords (vector4): The target coordinates and heading.
---
----@usage
+--- @usage
--- ```lua
--- useDoor({ telecoords = vector4(215.76, -810.12, 29.73, 90.0) })
---- -- Player is teleported to the specified coordinates with a fade effect
--- ```
function useDoor(data)
DoScreenFadeOut(500)
diff --git a/shared/metaHandlers.lua b/shared/metaHandlers.lua
index 8d2ae7d..81a0ebf 100644
--- a/shared/metaHandlers.lua
+++ b/shared/metaHandlers.lua
@@ -1,4 +1,24 @@
+--[[
+ Player Metadata Utilities Module
+ ----------------------------------
+ This module provides functions for retrieving and setting metadata for players
+ across different frameworks (QB, ESX, OXCore). It also registers server callbacks
+ for getting and setting metadata.
+]]
+-------------------------------------------------------------
+-- Player Retrieval
+-------------------------------------------------------------
+
+--- Retrieves the player object using the active core export.
+---
+--- @param source number The server ID of the player.
+--- @return table|nil table The player object, or nil if no supported core is detected.
+---
+--- @usage
+--- ```lua
+--- local player = GetPlayer(playerId)
+--- ```
function GetPlayer(source)
if isStarted(QBExport) then
debugPrint("^6Bridge^7: ^3GetPlayer^7() QBExport")
@@ -16,9 +36,24 @@ function GetPlayer(source)
return nil
end
--- Get Metadata
+-------------------------------------------------------------
+-- Metadata Retrieval
+-------------------------------------------------------------
+
+--- Retrieves metadata from a player object.
+---
+--- If called client-side (player is nil), it triggers a server callback to retrieve metadata.
+---
+--- @param player table|nil The player object; if nil, metadata is retrieved via a server callback.
+--- @param key string The metadata key to retrieve.
+--- @return any The value of the requested metadata, or nil if not found.
+---
+--- @usage
+--- ```lua
+--- local myMeta = GetMetadata(player, "myKey")
+--- ```
function GetMetadata(player, key)
- if not player then -- This would be called client side
+ if not player then
debugPrint("^6Bridge^7: ^3GetMetadata^7() calling server: "..key)
return triggerCallback(getScript()..":server:GetMetadata", key)
else
@@ -36,56 +71,65 @@ function GetMetadata(player, key)
return nil
end
+-- Register a server callback for retrieving metadata.
createCallback(getScript()..":server:GetMetadata", function(source, key)
- debugPrint("^6Bridge^7: ^3GetMetadata^7() Callback", source, key)
+ debugPrint("^6Bridge^7: ^3GetMetadata Callback^7 from source: "..tostring(source)..", key: "..tostring(key))
local player = GetPlayer(source)
- local Metadata = {}
if not player then
- print("Error getting metadata")
+ print("Error getting metadata: player not found for source "..tostring(source))
return
end
+
if type(key) == "table" then
+ local Metadata = {}
for _, k in ipairs(key) do
- Metadata[k] = GetMetadata(player, k).k
+ Metadata[k] = GetMetadata(player, k)
end
+ return Metadata
elseif type(key) == "string" then
return GetMetadata(player, key)
end
-
- jsonPrint(Metadata)
- return Metadata
end)
--- Set Metadata
+-------------------------------------------------------------
+-- Metadata Setting
+-------------------------------------------------------------
+
+--- Sets metadata on a player object.
+---
+--- The function updates the player's metadata using the active core export.
+---
+--- @param player table The player object.
+--- @param key string The metadata key to set.
+--- @param value any The new value for the metadata key.
+---
+--- @usage
+--- ```lua
+--- SetMetadata(player, "myKey", "newValue")
+--- ```
function SetMetadata(player, key, value)
- --if player == nil then -- This would be called client side
- -- debugPrint("^6Bridge^7: ^3SetMetadata^7() calling server")
- -- triggerCallback(getScript()..":server:SetMetadata", { key, value })
- -- else
- debugPrint("^6Bridge^7: ^3SetMetadata^7() setting metadata")
- if isStarted(QBExport) or isStarted(QBXExport) then
- debugPrint("^6Bridge^7: ^3SetMetadata^7() QBExport or QBXExport")
- player.Functions.SetMetaData(key, value)
- elseif isStarted(ESXExport) then
- debugPrint("^6Bridge^7: ^3SetMetadata^7() ESXExport")
- player.setMeta(key, value)
- elseif isStarted(OXCoreExport) then
- debugPrint("^6Bridge^7: ^3SetMetadata^7() OXCoreExport")
- player.set(key, value)
- end
- --end
+ debugPrint("^6Bridge^7: ^3SetMetadata^7() setting metadata for key: "..key)
+ if isStarted(QBExport) or isStarted(QBXExport) then
+ debugPrint("^6Bridge^7: ^3SetMetadata^7() using QBExport/QBXExport")
+ player.Functions.SetMetaData(key, value)
+ elseif isStarted(ESXExport) then
+ debugPrint("^6Bridge^7: ^3SetMetadata^7() using ESXExport")
+ player.setMeta(key, value)
+ elseif isStarted(OXCoreExport) then
+ debugPrint("^6Bridge^7: ^3SetMetadata^7() using OXCoreExport")
+ player.set(key, value)
+ end
end
-
+-- Register a server callback for setting metadata.
createCallback(getScript()..":server:SetMetadata", function(source, key, value)
- print(source, key, value)
+ debugPrint("SetMetadata callback triggered for source:", source, "key:", key, "value:", value)
local player = GetPlayer(source)
- --jsonPrint(player)
--[[if not player then
- print("Error getting metadata")
+ print("Error setting metadata: player not found for source "..tostring(source))
return false
end]]
- print("i did it")
SetMetadata(player, key, value)
+ print("Metadata set successfully.", key)
return true
end)
\ No newline at end of file
diff --git a/shared/notify.lua b/shared/notify.lua
index 3741ff5..b65a2a9 100644
--- a/shared/notify.lua
+++ b/shared/notify.lua
@@ -1,18 +1,28 @@
--- NOTIFICATIONS --
--- This function is widely used to display notifications to the player, can be used server side or client side --
+--[[
+ Notifications Module
+ ----------------------
+ This module provides a unified interface for displaying notifications using various
+ notification systems. The active system is determined by the Config.System.Notify setting.
+
+ Supported systems include:
+ • okok
+ • qb
+ • ox
+ • gta (default)
+ • esx
+]]
--- Displays notifications to the player using the configured notification system.
---
---- This function supports multiple notification systems based on the `Config.System.Notify` setting.
---- It can be triggered from both client-side and server-side scripts. Depending on the configuration,
---- it utilizes different exports or events to display the notification.
+--- Supports multiple notification systems based on Config.System.Notify. Can be triggered from both
+--- client and server contexts.
---
----@param title string|nil The title of the notification. Optional, used by certain notification systems.
----@param message string The main message content of the notification.
----@param type string The type/category of the notification (e.g., "success", "error", "info").
----@param src number|nil Optional. The server ID of the player to send the notification to. If `nil`, the notification is sent to the caller.
+--- @param title string|nil The notification title (optional for some systems).
+--- @param message string The main message content.
+--- @param type string The notification type ("success", "error", "info").
+--- @param src number|nil Optional server ID; if provided, the notification is sent to that player.
---
----@usage
+--- @usage
--- ```lua
--- -- Client-side usage without specifying a player (shows to the current player)
--- triggerNotify("Success", "You have completed the task!", "success")
@@ -21,52 +31,72 @@
--- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId)
--- ```
function triggerNotify(title, message, type, src)
- if Config.System.Notify == "okok" then
- if not src then TriggerEvent('okokNotify:Alert', title, message, 6000, type)
- else TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type) end
- elseif Config.System.Notify == "qb" then
- if not src then TriggerEvent("QBCore:Notify", message, type)
- else TriggerClientEvent("QBCore:Notify", src, message, type) end
- elseif Config.System.Notify == "ox" then
- if not src then TriggerEvent('ox_lib:notify', {title = title, description = message, type = type or "success"})
- else TriggerClientEvent('ox_lib:notify', src, { type = type or "success", title = title, description = message }) end
- elseif Config.System.Notify == "gta" then
- if not src then TriggerEvent(getScript()..":DisplayGTANotify", title, message)
- else TriggerClientEvent(getScript()..":DisplayGTANotify", src, title, message) end
+ if Config.System.Notify == "okok" then
+ if not src then
+ TriggerEvent('okokNotify:Alert', title, message, 6000, type)
+ else
+ TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type)
+ end
+ elseif Config.System.Notify == "qb" then
+ if not src then
+ TriggerEvent("QBCore:Notify", message, type)
+ else
+ TriggerClientEvent("QBCore:Notify", src, message, type)
+ end
+ elseif Config.System.Notify == "ox" then
+ if not src then
+ TriggerEvent('ox_lib:notify', { title = title, description = message, type = type or "success" })
+ else
+ TriggerClientEvent('ox_lib:notify', src, { title = title, description = message, type = type or "success" })
+ end
+ elseif Config.System.Notify == "gta" then
+ if not src then
+ TriggerEvent(getScript()..":DisplayGTANotify", title, message)
+ else
+ TriggerClientEvent(getScript()..":DisplayGTANotify", src, title, message)
+ end
elseif Config.System.Notify == "esx" then
- if not src then exports["esx_notify"]:Notify(type, 4000, message)
- else TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, title, message) end
- end
+ if not src then
+ exports["esx_notify"]:Notify(type, 4000, message)
+ else
+ TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message)
+ end
+ end
end
+-------------------------------------------------------------
+-- ESX Notifications
+-------------------------------------------------------------
+
--- Registers a server-side event to display ESX notifications to clients.
---
---- This event listens for `DisplayESXNotify` and triggers the ESX notification on the client side.
+--- Listens for DisplayESXNotify events and triggers the ESX notification on the client.
---
---- @param type string The type/category of the notification (e.g., "success", "error", "info").
---- @param title string The title of the notification.
---- @param text string The main message content of the notification.
+--- @param type string The notification type.
+--- @param title string The notification title.
+--- @param text string The notification message.
---
--- @usage
--- ```lua
---- -- Server-side event trigger
---- TriggerClientEvent(getScript()..":DisplayESXNotify", playerId, "success", "Achievement Unlocked", "You have unlocked a new achievement!")
+--- TriggerClientEvent(getScript()..":DisplayESXNotify", playerId, "success", "New achievement unlocked!")
--- ```
-RegisterNetEvent(getScript()..":DisplayESXNotify", function(type, title, text)
+RegisterNetEvent(getScript()..":DisplayESXNotify", function(type, text)
exports["esx_notify"]:Notify(type, 4000, text)
end)
---- Displays default GTA-style text notifications.
+-------------------------------------------------------------
+-- GTA-style Notifications
+-------------------------------------------------------------
+
+--- Displays GTA-style text notifications using native GTA functions.
---
---- This event handles displaying text-based notifications using GTA's native functions.
---- It supports specific scenarios by assigning different icons based on the script name.
+--- Selects an appropriate icon based on the current script (if applicable) and renders the notification.
---
----@param title string The title or identifier for the notification, used to select the appropriate icon.
----@param text string The main message content of the notification.
+--- @param title string The notification title/identifier (used to select an icon).
+--- @param text string The notification message.
---
----@usage
+--- @usage
--- ```lua
---- -- Client-side event trigger
--- TriggerEvent(getScript()..":DisplayGTANotify", "taxiname", "Taxi service has arrived.")
--- ```
RegisterNetEvent(getScript()..":DisplayGTANotify", function(title, text)
@@ -81,8 +111,13 @@ RegisterNetEvent(getScript()..":DisplayGTANotify", function(title, text)
[Loc[Config.Lan].notify["heliname"]] = "CHAR_BOATSITE2",
}
end
+
BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringKeyboardDisplay(text)
- EndTextCommandThefeedPostMessagetext(iconTable[title] or "CHAR_DEFAULT", iconTable[title] or "CHAR_DEFAULT", true, 1, title, nil, text)
+ EndTextCommandThefeedPostMessagetext(
+ iconTable[title] or "CHAR_DEFAULT",
+ iconTable[title] or "CHAR_DEFAULT",
+ true, 1, title, nil, text
+ )
EndTextCommandThefeedPostTicker(true, false)
end)
\ No newline at end of file
diff --git a/shared/phones.lua b/shared/phones.lua
new file mode 100644
index 0000000..62d9c28
--- /dev/null
+++ b/shared/phones.lua
@@ -0,0 +1,166 @@
+--[[
+ Phone Mails Module
+ ------------------
+ This module handles sending phone mails using different phone systems.
+ Supported systems include:
+ - gksphone
+ - yflip-phone
+ - qs-smartphone
+ - qs-smartphone-pro
+ - roadphone
+ - lb-phone
+ - qb-phone
+ - jpr-phonesystem
+]]
+
+--- Sends a phone mail using the detected phone system.
+--- The function iterates through a prioritized list of supported phone systems.
+--- Once an active system is found (via `isStarted`), the corresponding mail function is executed.
+---
+--- @param data table A table containing the mail data.
+--- - subject (string): The email subject.
+--- - sender (string): The sender identifier.
+--- - message (string): The email body content.
+--- - actions (table|nil): Optional action buttons for the email.
+--- @usage
+--- sendPhoneMail({
+--- subject = "Welcome!",
+--- sender = "Admin",
+--- message = "Thank you for joining our server.",
+--- actions = {
+--- { label = "Reply", action = replyFunction }
+--- }
+--- })
+function sendPhoneMail(data)
+ -- Define each supported phone system and its corresponding mail-sending function.
+ local phoneSystems = {
+ { name = "gksphone",
+ send = function(mailData)
+ exports["gksphone"]:SendNewMail(mailData)
+ end,
+ },
+ { name = "yflip-phone",
+ send = function(mailData)
+ TriggerServerEvent(getScript()..":yflip:SendMail", mailData)
+ end,
+ },
+ { name = "qs-smartphone",
+ send = function(mailData)
+ TriggerServerEvent('qs-smartphone:server:sendNewMail', mailData)
+ end,
+ },
+ { name = "qs-smartphone-pro",
+ send = function(mailData)
+ TriggerServerEvent('phone:sendNewMail', mailData)
+ end,
+ },
+ { name = "roadphone",
+ send = function(mailData)
+ -- Convert HTML line breaks to newlines for roadphone.
+ mailData.message = mailData.message:gsub("%
", "\n")
+ exports["roadphone"]:sendMail(mailData)
+ end,
+ },
+ { name = "lb-phone",
+ send = function(mailData)
+ -- Convert HTML line breaks to newlines for lb-phone.
+ mailData.message = mailData.message:gsub("%
", "\n")
+ TriggerServerEvent(getScript()..":lbphone:SendMail", mailData)
+ end,
+ },
+ { name = "qb-phone",
+ send = function(mailData)
+ TriggerServerEvent('qb-phone:server:sendNewMail', mailData)
+ end,
+ },
+ { name = "jpr-phonesystem",
+ send = function(mailData)
+ TriggerServerEvent(getScript()..":jpr:SendMail", mailData)
+ end,
+ },
+ }
+
+ local activePhone = nil
+ -- Check each phone system in order and use the first active one.
+ for _, phone in ipairs(phoneSystems) do
+ if isStarted(phone.name) then
+ activePhone = phone.name
+ phone.send(data)
+ break
+ end
+ end
+
+ if activePhone then
+ debugPrint("^6Bridge^7[^3"..activePhone.."^7]: ^2Sending mail to player")
+ else
+ print("^6Bridge^7: ^1ERROR ^2Sending mail to player ^7- ^2No supported phone found")
+ end
+end
+
+-------------------------------------------------------------
+-- Phone System Event Handlers
+-------------------------------------------------------------
+
+--- Handles sending mail for lb-phone.
+--- Listens for the `lbphone:SendMail` event and sends an email using lb-phone's API.
+---
+--- @event lbphone:SendMail
+--- @param data table The mail data.
+--- - subject (string): The email subject.
+--- - message (string): The email content.
+--- - buttons (table|nil): Optional action buttons (mapped from data.actions if present).
+RegisterNetEvent(getScript()..":lbphone:SendMail", function(data)
+ local src = source
+ local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(src)
+ local emailAddress = exports["lb-phone"]:GetEmailAddress(phoneNumber)
+ -- Map actions to buttons if provided.
+ data.buttons = data.actions or data.buttons
+
+ exports["lb-phone"]:SendMail({
+ to = emailAddress,
+ subject = data.subject,
+ message = data.message,
+ actions = data.buttons,
+ })
+end)
+
+--- Handles sending mail for yflip-phone.
+--- Listens for the `yflip:SendMail` event and sends an email using yflip-phone's API.
+---
+--- @event yflip:SendMail
+--- @param data table The mail data.
+--- - subject (string): The email subject.
+--- - sender (string): The sender identifier.
+--- - message (string): The email content.
+--- - buttons (table|nil): Optional action buttons.
+RegisterNetEvent(getScript()..":yflip:SendMail", function(data)
+ local src = source
+ exports["yflip-phone"]:SendMail({
+ title = data.subject,
+ sender = data.sender,
+ senderDisplayName = data.sender,
+ content = data.message,
+ actions = data.buttons,
+ }, 'source', src)
+end)
+
+--- Handles sending mail for jpr-phonesystem.
+--- Listens for the `jpr:SendMail` event and sends an email using jpr-phonesystem's API.
+---
+--- @event jpr:SendMail
+--- @param data table The mail data.
+--- - subject (string): The email subject.
+--- - sender (string): The sender identifier.
+--- - message (string): The email content.
+--- - buttons (table|nil): Optional action buttons.
+RegisterNetEvent(getScript()..":jpr:SendMail", function(data)
+ local src = source
+ local Player = Core.Functions.GetPlayer(src)
+ TriggerEvent('jpr-phonesystem:server:sendEmail', {
+ Assunto = data.subject, -- Email subject
+ Conteudo = data.message, -- Email content
+ Enviado = data.sender, -- Sender information
+ Destinatario = Player.PlayerData.citizenid, -- Recipient identifier
+ Event = {}, -- Optional event details
+ })
+end)
\ No newline at end of file
diff --git a/shared/playerfunctions.lua b/shared/playerfunctions.lua
index 1745b3f..0bd647e 100644
--- a/shared/playerfunctions.lua
+++ b/shared/playerfunctions.lua
@@ -1,67 +1,52 @@
---- Locks or unlocks the player's inventory.
----
---- This function freezes or unfreezes the player's position, sets the inventory busy state,
---- and toggles the ability to use the inventory and hotbar based on the `toggle` parameter.
----
---- @param toggle boolean `true` to lock the inventory, `false` to unlock.
----
---- @usage
---- ```lua
---- -- Lock the player's inventory
---- lockInv(true)
----
---- -- Unlock the player's inventory
---- lockInv(false)
---- ```
-function lockInv(toggle)
- FreezeEntityPosition(PlayerPedId(), toggle)
- LocalPlayer.state:set("inv_busy", toggle, true)
- TriggerEvent('inventory:client:busy:status', toggle)
- TriggerEvent('canUseInventoryAndHotbar:toggle', not toggle)
-end
+--[[
+ Player Utility & Server Event Handlers Module
+ ------------------------------------------------
+ This module provides utility functions for:
+ • Locking/unlocking the player's inventory.
+ • Instantly turning or gradually turning the player to face a target.
+ • Handling player needs (thirst and hunger) via server events.
+ • Charging/funding players (money removal/addition).
+ • Processing item consumption and applying effects.
+ • Checking player job/gang roles and retrieving player information.
+ • Getting active players near a coordinate.
+]]
---- Instantly turns an entity to face a specific location or another entity.
+-------------------------------------------------------------
+-- Player Movement
+-------------------------------------------------------------
+
+--- Instantly turns an entity to face a target (entity or coordinates) without animation.
---
---- This function calculates the heading from the first entity to the second entity or coordinates
---- and sets the entity's heading immediately without any animation.
----
---- @param ent number|nil The Ped entity to turn. Defaults to the player's Ped (`PlayerPedId()`).
---- @param ent2 number|vector3|nil The target entity or coordinates to face. If a vector, it uses the coordinates.
+--- @param ent number|nil The Ped to turn (defaults to player's Ped if nil).
+--- @param ent2 number|vector3|nil The target entity or coordinates to face.
---
--- @usage
--- ```lua
---- -- Make the player instantly face a specific location
--- instantLookEnt(nil, vector3(200.0, 300.0, 40.0))
----
---- -- Make one entity face another entity
--- instantLookEnt(ped1, ped2)
--- ```
function instantLookEnt(ent, ent2)
- local ent = ent or PlayerPedId()
- local p1 = GetEntityCoords(ent, true)
- local p2 = type(ent2):find("vector") and ent2 or GetEntityCoords(ent2, true)
+ local ped = ent or PlayerPedId()
+ local p1 = GetEntityCoords(ped, true)
+ local p2 = type(ent2) == "vector3" and ent2 or GetEntityCoords(ent2, true)
local dx = p2.x - p1.x
local dy = p2.y - p1.y
-
local heading = GetHeadingFromVector_2d(dx, dy)
+
debugPrint("^6Bridge^7: ^1Forced ^2Turning Player to^7: '^6"..formatCoord(p2).."^7'")
- SetEntityHeading(ent, heading)
+ SetEntityHeading(ped, heading)
end
---- Makes the player Ped look towards a specific entity or coordinates with animation.
+--- Makes the player look towards a specific target with an animated turn.
---
---- This function checks if the player is already facing the target. If not, it triggers a turning animation
---- to face the specified entity or coordinates.
+--- If the player is not already facing the target (entity or coordinates), a turning animation is triggered.
---
---- @param entity number|vector3|vector4|nil The target entity or coordinates to look at.
+--- @param entity number|vector3|vector4|nil The target to look at.
---
--- @usage
--- ```lua
---- -- Make the player look at a specific location
--- lookEnt(vector3(200.0, 300.0, 40.0))
----
---- -- Make the player look at another entity
--- lookEnt(pedEntity)
--- ```
function lookEnt(entity)
@@ -86,15 +71,12 @@ function lookEnt(entity)
end
end
---- Server event handler for handling urinal usage.
----
---- This event decreases the player's thirst based on a random amount and updates their thirst level.
----
---- @usage
---- ```lua
---- -- Triggered when a player uses a urinal
---- TriggerServerEvent(getScript()..":server:Urinal")
---- ```
+-------------------------------------------------------------
+-- Server Event Handlers for Needs
+-------------------------------------------------------------
+
+--- Server event handler for urinal usage.
+--- Decreases player's thirst by a random amount.
RegisterNetEvent(getScript()..":server:Urinal", function()
local src = source
local Player = getPlayer(src)
@@ -103,43 +85,27 @@ RegisterNetEvent(getScript()..":server:Urinal", function()
setThirst(src, getPlayer(src).thirst - thirst)
end)
---- Server event handler for setting player needs.
----
---- This event updates the player's thirst or hunger based on the provided type and amount.
+--- Server event handler for setting player needs (thirst or hunger).
---
--- @event
---- @param type string The type of need to set ("thirst" or "hunger").
---- @param amount number The amount to set the need to.
----
---- @return void
----
---- @usage
---- ```lua
---- -- Set the player's thirst level
---- TriggerServerEvent(getScript()..":server:setNeed", "thirst", 50)
----
---- -- Set the player's hunger level
---- TriggerServerEvent(getScript()..":server:setNeed", "hunger", 75)
---- ```
-RegisterNetEvent(getScript()..":server:setNeed", function(type, amount)
+--- @param type string "thirst" or "hunger".
+--- @param amount number New value to set.
+RegisterNetEvent(getScript()..":server:setNeed", function(needType, amount)
local src = source
- if type == "thirst" then
+ if needType == "thirst" then
setThirst(src, amount)
- elseif type == "hunger" then
+ elseif needType == "hunger" then
setHunger(src, amount)
end
end)
--- Sets the player's thirst level.
---
---- This function updates the player's thirst based on the active inventory system.
----
---- @param src number The server ID of the player.
---- @param thirst number The new thirst level to set.
+--- @param src number The player's server ID.
+--- @param thirst number The new thirst level.
---
--- @usage
--- ```lua
---- -- Set a player's thirst to 80
--- setThirst(playerId, 80)
--- ```
function setThirst(src, thirst)
@@ -154,14 +120,11 @@ end
--- Sets the player's hunger level.
---
---- This function updates the player's hunger based on the active inventory system.
----
---- @param src number The server ID of the player.
---- @param hunger number The new hunger level to set.
+--- @param src number The player's server ID.
+--- @param hunger number The new hunger level.
---
--- @usage
--- ```lua
---- -- Set a player's hunger to 60
--- setHunger(playerId, 60)
--- ```
function setHunger(src, hunger)
@@ -174,105 +137,103 @@ function setHunger(src, hunger)
end
end
---- Server event handler for charging a player.
+-------------------------------------------------------------
+-- Economy Event Handlers
+-------------------------------------------------------------
+
+--- Charges a player by removing money from their account.
---
---- This event removes money from a player based on the specified type ("cash" or "bank").
----
---- @event
---- @param cost number The amount of money to charge.
---- @param type string The type of money to charge ("cash" or "bank").
---- @param newsrc number|nil Optional. The server ID of the player. If `nil`, uses the event source.
+--- @param cost number The amount to charge.
+--- @param type string "cash" or "bank".
+--- @param newsrc number|nil Optional player ID; defaults to event source.
---
--- @usage
--- ```lua
---- -- Charge a player $100 in cash
--- chargePlayer(100, "cash", playerId)
----
---- -- Charge the source $250 from the bank
---- chargePlayer(250, "bank", src,)
--- ```
-function chargePlayer(cost, type, newsrc)
+function chargePlayer(cost, moneyType, newsrc)
local src = newsrc or source
local fundResource = ""
- if type == "cash" then
+
+ if moneyType == "cash" then
if isStarted(OXInv) then fundResource = OXInv
exports[OXInv]:RemoveItem(src, "money", cost)
elseif isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
elseif isStarted(ESXExport) then fundResource = ESXExport
- local Player = ESX.GetPlayerFromId(src)
- Player.removeMoney(cost, "")
+ ESX.GetPlayerFromId(src).removeMoney(cost, "")
end
- end
- if type == "bank" then
+ elseif moneyType == "bank" then
if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
elseif isStarted(ESXExport) then fundResource = ESXExport
- local Player = ESX.GetPlayerFromId(src)
- Player.removeMoney(cost, "")
+ ESX.GetPlayerFromId(src).removeMoney(cost, "")
end
end
- if fundResource == "" then print("error - check exports.lua")
+
+ if fundResource == "" then
+ print("Cannot charge player - check starter.lua")
else
- debugPrint("^6Bridge^7: ^2Charging ^2Player^7: '^6"..cost.."^7'", type, fundResource)
+ debugPrint("^6Bridge^7: ^2Charging ^2Player^7: '^6"..cost.."^7'", moneyType, fundResource)
end
end
RegisterNetEvent(getScript()..":server:ChargePlayer", chargePlayer)
---- Server event handler for funding a player.
+--- Funds a player by adding money to their account.
---
---- This event adds money to a player based on the specified type ("cash" or "bank").
----
---- @event
---- @param fund number The amount of money to add.
---- @param type string The type of money to add ("cash" or "bank").
---- @param newsrc number|nil Optional. The server ID of the player. If `nil`, uses the event source.
+--- @param fund number The amount to add.
+--- @param type string "cash" or "bank".
+--- @param newsrc number|nil Optional player ID; defaults to event source.
---
--- @usage
--- ```lua
---- -- Add $150 to a player's cash
---- fundPlayer(playerId, 150, "cash")
----
---- -- Add $300 to the event source's bank account
---- fundPlayer(playerId, 300, "bank")
+--- fundPlayer(150, "cash", playerId)
--- ```
-function fundPlayer(fund, type, newsrc)
+function fundPlayer(fund, moneyType, newsrc)
local src = newsrc or source
local fundResource = ""
- if type == "cash" then
- if isStarted(OXInv) then fundResource = OXInv
+
+ if moneyType == "cash" then
+ if isStarted(OXInv) then
+ fundResource = OXInv
exports[OXInv]:AddItem(src, "money", fund)
- elseif isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
+ elseif isStarted(QBExport) or isStarted(QBXExport) then
+ fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
- elseif isStarted(ESXExport) then fundResource = ESXExport
- local Player = ESX.GetPlayerFromId(src)
- Player.addMoney(fund, "")
+ elseif isStarted(ESXExport) then
+ fundResource = ESXExport
+ PlayESX.GetPlayerFromId(src).addMoney(fund, "")
end
- end
- if type == "bank" then
- if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
+ elseif moneyType == "bank" then
+ if isStarted(QBExport) or isStarted(QBXExport) then
+ fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
- elseif isStarted(ESXExport) then fundResource = ESXExport
- local Player = ESX.GetPlayerFromId(src)
- Player.addMoney(fund, "")
+ elseif isStarted(ESXExport) then
+ fundResource = ESXExport
+ ESX.GetPlayerFromId(src).addMoney(fund, "")
end
end
- if fundResource == "" then print("error - check exports.lua")
+
+ if fundResource == "" then
+ print("Cannot fund player - check starter.lua")
else
- debugPrint("^6Bridge^7: ^2Funding ^2Player^7: '^2"..fund.."^7'", type, fundResource)
+ debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource)
end
end
-
RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer)
+-------------------------------------------------------------
+-- Item Consumption & Effects
+-------------------------------------------------------------
+
--- Handles successful consumption of an item.
---
---- This function plays a consumption animation, removes the item from the inventory,
---- updates the player's hunger and thirst based on the item consumed,
---- handles alcohol effects, and checks for random rewards.
+--- Plays a consumption animation, removes the item, updates player needs, handles alcohol effects,
+--- and checks for random rewards.
---
---- @param itemName string The name of the item consumed.
---- @param type string The type/category of the item (e.g., "alcohol").
+--- @param itemName string The name of the consumed item.
+--- @param type string The category of the item (e.g., "alcohol").
+--- @param data table Additional data (e.g., hunger and thirst values).
---
--- @usage
--- ```lua
@@ -283,10 +244,12 @@ RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer)
--- ConsumeSuccess("beer", "alcohol")
--- ```
function ConsumeSuccess(itemName, type, data)
- local hunger = data and data.hunger or Items[itemName].hunger or nil
- local thirst = data and data.thirst or Items[itemName].thirst or nil
+ local hunger = data and data.hunger or Items[itemName].hunger
+ local thirst = data and data.thirst or Items[itemName].thirst
+
ExecuteCommand("e c")
removeItem(itemName, 1)
+
if isStarted(ESXExport) then
if hunger then
TriggerServerEvent(getScript()..":server:setNeed", "hunger", hunger * 10000)
@@ -302,7 +265,9 @@ function ConsumeSuccess(itemName, type, data)
TriggerServerEvent(getScript()..":server:setNeed", "thirst", Core.Functions.GetPlayerData().metadata["thirst"] + thirst)
end
end
- if type == "alcohol" then alcoholCount += 1
+
+ if type == "alcohol" then
+ alcoholCount = (alcoholCount or 0) + 1
if alcoholCount > 1 and alcoholCount < 4 then
TriggerEvent("evidence:client:SetStatus", "alcohol", 200)
elseif alcoholCount >= 4 then
@@ -310,19 +275,20 @@ function ConsumeSuccess(itemName, type, data)
AlienEffect()
end
end
- getRandomReward(itemName) -- check if a reward item should be given
+
+ getRandomReward(itemName)
end
---- Checks if a player has a specific job and grade.
+-------------------------------------------------------------
+-- Player Job & Information Utilities
+-------------------------------------------------------------
+
+--- Checks if a player has a specific job or gang (and optionally meets a minimum grade).
---
---- This function verifies whether the player has the specified job and, if a grade is provided,
---- whether the player's grade meets the required level. It supports multiple inventory systems.
----
---- @param job string The name of the job or gang to check.
---- @param source number|nil Optional. The server ID of the player to check. If `nil`, checks the current player.
---- @param grade number|nil Optional. The minimum grade level required.
----
---- @return boolean, boolean Returns `true` and `duty status` if the player has the job (and grade if specified), otherwise `false`.
+--- @param job string The job or gang name to check.
+--- @param source number|nil Optional player source; if nil, checks current player.
+--- @param grade number|nil Optional minimum grade level.
+--- @return boolean, boolean boolean Returns true and duty status if the check passes; false otherwise.
---
--- @usage
--- ```lua
@@ -338,7 +304,8 @@ end
--- -- Allow gang leader actions
--- end
--- ```
-function hasJob(job, source, grade) local hasJob, duty = false, true
+function hasJob(job, source, grade)
+ local hasJobFlag, duty = false, true
if source then
local src = tonumber(source)
if not src then print(tostring(source).." is not a valid player source") end
@@ -348,113 +315,116 @@ function hasJob(job, source, grade) local hasJob, duty = false, true
info = ESX.GetPlayerData(src).job
Wait(100)
end
- if info.name == job then hasJob = true end
+ if info.name == job then hasJobFlag = true end
elseif isStarted(OXCoreExport) then
local chunk = assert(load(LoadResourceFile('ox_core', ('imports/%s.lua'):format('server')), ('@@ox_core/%s'):format(file)))
chunk()
- local player = Ox.GetPlayer(tonumber(src))
+ local player = Ox.GetPlayer(src)
for k, v in pairs(player.getGroups()) do
- if k == job then hasJob = true end
+ if k == job then hasJobFlag = true end
end
elseif isStarted(QBXExport) then
local jobinfo = exports[QBXExport]:GetPlayer(src).PlayerData.job
- if jobinfo.name == job then hasJob = true
+ if jobinfo.name == job then
+ hasJobFlag = true
duty = exports[QBXExport]:GetPlayer(src).PlayerData.job.onduty
- if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
+ if grade and not (grade <= jobinfo.grade.level) then hasJobFlag = false end
end
local ganginfo = exports[QBXExport]:GetPlayer(src).PlayerData.gang
- if ganginfo.name == job then hasJob = true
- if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
+ if ganginfo.name == job then
+ hasJobFlag = true
+ if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end
end
elseif isStarted(QBExport) and not isStarted(QBXExport) then
- if Core.Functions.GetPlayer then -- support older qb-core functions
+ if Core.Functions.GetPlayer then
local player = Core.Functions.GetPlayer(src)
if not player then print("Player not found for src: "..src) end
local jobinfo = player.PlayerData.job
- if jobinfo.name == job then hasJob = true
- duty = Core.Functions.GetPlayer(src).PlayerData.job.onduty
- if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
+ if jobinfo.name == job then
+ hasJobFlag = true
+ duty = player.PlayerData.job.onduty
+ if grade and not (grade <= jobinfo.grade.level) then hasJobFlag = false end
end
- local ganginfo = Core.Functions.GetPlayer(src).PlayerData.gang
- if ganginfo.name == job then hasJob = true
- if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
+ local ganginfo = player.PlayerData.gang
+ if ganginfo.name == job then
+ hasJobFlag = true
+ if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end
end
- else -- support newer qb-core exports
+ else
local jobinfo = exports[QBExport]:GetPlayer(src).PlayerData.job
- if jobinfo.name == job then hasJob = true
+ if jobinfo.name == job then
+ hasJobFlag = true
duty = exports[QBExport]:GetPlayer(src).PlayerData.job.onduty
- if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
+ if grade and not (grade <= jobinfo.grade.level) then hasJobFlag = false end
end
local ganginfo = exports[QBExport]:GetPlayer(src).PlayerData.gang
- if ganginfo.name == job then hasJob = true
- if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
+ if ganginfo.name == job then
+ hasJobFlag = true
+ if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end
end
end
else
- print("^4ERROR^7: ^2No Core detected for hasJob ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Core detected for hasJob ^7- ^2Check ^3starter^1.^2lua^7")
end
else
- if isStarted(ESXExport) then
- while not ESX do Wait(10) end
+ -- Client-side check.
+ if isStarted(ESXExport) and ESX ~= nil then
local info = ESX.GetPlayerData().job
while not info do
info = ESX.GetPlayerData().job
Wait(100)
end
- if info.name == job then hasJob = true end
+ if info.name == job then hasJobFlag = true end
elseif isStarted(OXCoreExport) then
for k, v in pairs(exports[OXCoreExport]:GetPlayerData().groups) do
- if k == job then hasJob = true end break
+ if k == job then hasJobFlag = true break end
end
elseif isStarted(QBXExport) then
- local jobinfo = QBX.PlayerData.job
- if jobinfo.name == job then hasJob = true
- duty = QBX.PlayerData.job.onduty
- if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
+ local info = exports[QBXExport]:GetPlayerData()
+ if info.job.name == job then
+ hasJobFlag = true
+ duty = info.job.onduty
+ if grade and not (grade <= info.job.grade.level) then hasJobFlag = false end
end
- local ganginfo = QBX.PlayerData.gang
- if ganginfo.name == job then hasJob = true
- if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
+ if info.gang.name == job then
+ hasJobFlag = true
+ if grade and not (grade <= info.gang.grade.level) then hasJobFlag = false end
end
elseif isStarted(QBExport) and not isStarted(QBXExport) then
local info = nil
- Core.Functions.GetPlayerData(function(PlayerData)
- info = PlayerData
- end)
+ Core.Functions.GetPlayerData(function(PlayerData) info = PlayerData end)
local jobinfo = info.job
- if jobinfo.name == job then hasJob = true
+ if jobinfo.name == job then
+ hasJobFlag = true
duty = jobinfo.onduty
- if grade and not (grade <= jobinfo.grade.level) then hasJob = false end
+ if grade and not (grade <= jobinfo.grade.level) then hasJobFlag = false end
end
local ganginfo = info.gang
if ganginfo.name == job then
- hasJob = true
- if grade and not (grade <= ganginfo.grade.level) then hasJob = false end
+ hasJobFlag = true
+ if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end
end
-
else
- print("^4ERROR^7: ^2No Core detected for hasJob() ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Core detected for hasJob() ^7- ^2Check ^3starter^1.^2lua^7")
end
end
- return hasJob, duty
+ return hasJobFlag, duty
end
---- Retrieves basic information about a player.
+--- Retrieves basic player information (name, cash, bank, job, etc.) based on the active inventory system.
---
---- This function gathers the player's name, cash balance, and bank balance
---- based on the active inventory system. It can be called server-side or client-side.
+--- Can be called server-side (passing a player source) or client-side (for current player).
---
----@param source number|nil Optional. The server ID of the player. If `nil`, retrieves info for the current player.
+--- @param source number|nil Optional player server ID.
+--- @return table A table containing player details.
---
----@return table table A table containing the player's `name`, `cash`, and `bank` balances.
----
----@usage
+--- @usage
--- ```lua
--- -- Get information for a specific player
--- local playerInfo = getPlayer(playerId)
@@ -467,7 +437,8 @@ end
function getPlayer(source)
local Player = {}
debugPrint("^6Bridge^7: ^2Getting ^3Player^2 info^7")
- if source then -- If called from server
+
+ if source then
local src = tonumber(source)
if isStarted(ESXExport) then
local info = ESX.GetPlayerFromId(src)
@@ -494,13 +465,12 @@ function getPlayer(source)
local import = LoadResourceFile('ox_core', file)
local chunk = assert(load(import, ('@@ox_core/%s'):format(file)))
chunk()
- local player = Ox.GetPlayer(tonumber(src))
+ local player = Ox.GetPlayer(src)
Player = {
name = ('%s %s'):format(player.firstName, player.lastName),
cash = exports[OXInv]:Search(src, 'count', "money"),
bank = 0,
}
-
elseif isStarted(QBXExport) then
local info = exports[QBXExport]:GetPlayer(src)
Player = {
@@ -518,9 +488,8 @@ function getPlayer(source)
account = info.PlayerData.charinfo.account,
citizenId = info.PlayerData.citizenid,
}
-
elseif isStarted(QBExport) and not isStarted(QBXExport) then
- if Core.Functions.GetPlayer ~= nil then -- support older qb-core functions
+ if Core.Functions.GetPlayer then
local info = Core.Functions.GetPlayer(src).PlayerData
Player = {
firstname = info.charinfo.firstname,
@@ -537,9 +506,8 @@ function getPlayer(source)
account = info.charinfo.account,
citizenId = info.citizenid,
}
-
else
- local info = exports[QBExport]:GetPlayer(src).PlayerData -- this was added to new core then removed?
+ local info = exports[QBExport]:GetPlayer(src).PlayerData
Player = {
firstname = info.charinfo.firstname,
lastname = info.charinfo.lastname,
@@ -556,16 +524,15 @@ function getPlayer(source)
citizenId = info.citizenid,
}
end
-
else
- print("^4ERROR^7: ^2No Core detected for getPlayer() ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Core detected for getPlayer() - Check exports.lua")
end
else
+ -- Client-side: Get current player info.
if isStarted(ESXExport) and ESX ~= nil then
local info = ESX.GetPlayerData()
- --jsonPrint(info)
local cash, bank = 0, 0
- for k, v in pairs(ESX.GetPlayerData().accounts) do
+ for k, v in pairs(info.accounts) do
if v.name == "money" then cash = v.money end
if v.name == "bank" then bank = v.money end
end
@@ -629,12 +596,22 @@ function getPlayer(source)
citizenId = info.citizenid,
}
else
- print("^4ERROR^7: ^2No Core detected for hasJob ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Core detected for hasJob ^7- ^2Check ^3starter^1.^2lua^7")
end
end
return Player
end
+--- Retrieves all active players within a given radius from the specified coordinates.
+---
+--- @param coords vector3 The reference coordinates.
+--- @param radius number The radius within which to find players.
+--- @return table table An array of player IDs.
+---
+--- @usage
+--- ```lua
+--- local nearbyPlayers = GetPlayersFromCoords(vector3(100, 200, 30), 20)
+--- ```
function GetPlayersFromCoords(coords, radius)
local players = {}
for _, playerId in ipairs(GetActivePlayers()) do
@@ -642,7 +619,7 @@ function GetPlayersFromCoords(coords, radius)
if ped and DoesEntityExist(ped) then
local playerCoords = GetEntityCoords(ped)
if #(coords - playerCoords) <= radius then
- players[#players+1] = playerId
+ players[#players + 1] = playerId
end
end
end
diff --git a/shared/polyZone.lua b/shared/polyZone.lua
index bcc07e6..fbe5e42 100644
--- a/shared/polyZone.lua
+++ b/shared/polyZone.lua
@@ -1,45 +1,61 @@
--- This automatically detects what polyzone script it should use to create a polyzone --
--- if ox_lib is detected, it will automatically use that instead of PolyZone --
--- createPoly({ name = 'name', debug = true, points = { vec2(), vec2() }, onEnter = function() end, onExit = function() end, })
----
+--[[
+ PolyZone Management Module
+ ----------------------------
+ This module automatically detects the available polyzone library (ox_lib or PolyZone)
+ and creates polygonal and circular zones accordingly. It also provides a function to remove
+ previously created zones.
+
+ Functions:
+ • createPoly(data) - Creates a polygonal zone.
+ • createCirclePoly(data) - Creates a circular zone.
+ • removePolyZone(Location) - Removes a created zone.
+]]
+
+-------------------------------------------------------------
+-- Polygonal Zone Creation
+-------------------------------------------------------------
+
--- Creates a polygonal zone using the detected polyzone library (ox_lib or PolyZone).
---
---- This function automatically detects whether `ox_lib` or `PolyZone` is active and creates a polygonal zone accordingly.
---- It supports setting up entry and exit callbacks for the zone.
+--- Automatically checks which polyzone script is active. When using ox_lib, it converts the provided
+--- 2D points to 3D (setting a constant z value) and sets a thickness value. For PolyZone, it creates the zone
+--- and attaches onEnter and onExit callbacks.
---
----@param data table A table containing the zone configuration.
---- - **name** (`string`): The name of the zone.
---- - **debug** (`boolean`): Whether to enable debug mode for the zone.
---- - **points** (`table`): A list of `vec2` points defining the polygon.
---- - **onEnter** (`function`): Callback function to execute when a player enters the zone.
---- - **onExit** (`function`): Callback function to execute when a player exits the zone.
+--- @param data table Zone configuration table with the following keys:
+--- - name (string): The zone's identifier.
+--- - debug (boolean): Whether debug mode is enabled.
+--- - points (table): A list of vec2 points defining the polygon.
+--- - onEnter (function): Callback when a player enters the zone.
+--- - onExit (function): Callback when a player exits the zone.
---
----@return table|nil table Returns the created zone object or `nil` if creation failed.
+--- @return table|nil table Returns the created zone object or nil if creation failed.
---
---@usage
---- ```lua
---- createPoly({
---- name = 'testZone',
---- debug = true,
---- points = { vec2(100.0, 100.0), vec2(200.0, 100.0), vec2(200.0, 200.0), vec2(100.0, 200.0) },
---- onEnter = function() print("Entered Test Zone") end,
---- onExit = function() print("Exited Test Zone") end,
---- })
---- ```
+---```lua
+---createPoly({
+--- name = 'testZone',
+--- debug = true,
+--- points = { vec2(100.0, 100.0), vec2(200.0, 100.0), vec2(200.0, 200.0), vec2(100.0, 200.0) },
+--- onEnter = function() print("Entered Test Zone") end,
+--- onExit = function() print("Exited Test Zone") end,
+---})
+---```
function createPoly(data)
local Location = nil
- if isStarted(OXLibExport) then -- if it finds ox_lib, use it instead of PolyZone
+ if isStarted(OXLibExport) then
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4"..OXLibExport.."^7': "..data.name)
+ -- Convert 2D points to 3D with a fixed Z coordinate (e.g., 12.0)
for i = 1, #data.points do
data.points[i] = vec3(data.points[i].x, data.points[i].y, 12.0)
end
- data.thickness = 1000
+ data.thickness = 1000 -- Set a default thickness value
Location = lib.zones.poly(data)
elseif isStarted("PolyZone") then
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4PolyZone^7': "..data.name)
Location = PolyZone:Create(data.points, { name = data.name, debugPoly = data.debug })
Location:onPlayerInOut(function(isPointInside)
if isPointInside then data.onEnter() else data.onExit() end
+
end)
else
print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7")
@@ -47,21 +63,25 @@ function createPoly(data)
return Location
end
+-------------------------------------------------------------
+-- Circular Zone Creation
+-------------------------------------------------------------
+
--- Creates a circular zone using the detected polyzone library (ox_lib or PolyZone).
---
---- This function automatically detects whether `ox_lib` or `PolyZone` is active and creates a circular zone accordingly.
---- It supports setting up entry and exit callbacks for the zone.
+--- When using ox_lib, it creates a sphere zone. For PolyZone, it creates a CircleZone and attaches
+--- onEnter and onExit callbacks.
---
----@param data table A table containing the circular zone configuration.
---- - **name** (`string`): The name of the circular zone.
---- - **coords** (`vector3`): The center coordinates of the circle.
---- - **radius** (`number`): The radius of the circle.
---- - **onEnter** (`function`): Callback function to execute when a player enters the zone.
---- - **onExit** (`function`): Callback function to execute when a player exits the zone.
+--- @param data table Zone configuration with the following keys:
+--- - name (string): The zone's identifier.
+--- - coords (vector3): The center of the circle.
+--- - radius (number): The radius of the circle.
+--- - onEnter (function): Callback when a player enters the zone.
+--- - onExit (function): Callback when a player exits the zone.
---
----@return table|nil table Returns the created circular zone object or `nil` if creation failed.
+--- @return table|nil table Returns the created circular zone object or nil if creation failed.
---
----@usage
+--- @usage
--- ```lua
--- createCirclePoly({
--- name = 'circleZone',
@@ -73,7 +93,7 @@ end
--- ```
function createCirclePoly(data)
local Location = nil
- if isStarted(OXLibExport) then -- if it finds ox_lib, use it instead of PolyZone
+ if isStarted(OXLibExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Cricle^2 poly with ^7"..OXLibExport.." "..data.name)
Location = lib.zones.sphere(data)
elseif isStarted("PolyZone") then
@@ -87,26 +107,30 @@ function createCirclePoly(data)
end
end)
else
- print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3starter^1.^2lua^7")
end
debugPrint("^6Bridge^7: ^2Zone Stats - Coords: "..formatCoord(data.coords).." Radius: "..data.radius)
return Location
end
+-------------------------------------------------------------
+-- PolyZone Removal Function
+-------------------------------------------------------------
+
--- Removes a previously created polyzone.
---
---- This function detects the active polyzone library (`ox_lib` or `PolyZone`) and removes the specified zone accordingly.
+--- Detects the active polyzone library and calls the appropriate removal method.
---
--- @param Location table The zone object to be removed.
---
--- @usage
--- ```lua
--- local zone = createPoly({...})
---- -- Later in the code
+---
--- removePolyZone(zone)
--- ```
function removePolyZone(Location)
- if isStarted(OXLibExport) then -- if it finds ox_lib, use it instead of PolyZone
+ if isStarted(OXLibExport) then
debugPrint("^6Bridge^7: ^2Removing ^2poly with ^7"..OXLibExport)
Location:remove()
elseif isStarted("PolyZone") then
diff --git a/shared/scaleforms.lua b/shared/scaleforms.lua
deleted file mode 100644
index 2fd04bf..0000000
--- a/shared/scaleforms.lua
+++ /dev/null
@@ -1,61 +0,0 @@
-function makeInstructionalButtons(info)
- local build = RequestScaleformMovie("instructional_buttons")
- while not HasScaleformMovieLoaded(build) do Wait(0) end
-
- DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0)
- BeginScaleformMovieMethod(build, "CLEAR_ALL")
- EndScaleformMovieMethod()
- BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE")
- ScaleformMovieMethodAddParamInt(200)
- EndScaleformMovieMethod()
-
- for i = 1, #info do
- BeginScaleformMovieMethod(build, "SET_DATA_SLOT")
- ScaleformMovieMethodAddParamInt(i - 1)
- for k = 1, #info[i].keys do
- ScaleformMovieMethodAddParamPlayerNameString(GetControlInstructionalButton(2, info[i].keys[k], true))
- end
- BeginTextCommandScaleformString("STRING")
- AddTextComponentSubstringKeyboardDisplay(info[i].text)
- EndTextCommandScaleformString()
- EndScaleformMovieMethod()
- end
-
- BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS")
- EndScaleformMovieMethod()
- BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR")
- ScaleformMovieMethodAddParamInt(0)
- ScaleformMovieMethodAddParamInt(0)
- ScaleformMovieMethodAddParamInt(0)
- ScaleformMovieMethodAddParamInt(80)
- EndScaleformMovieMethod()
-
- DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0)
-end
-
--- Testing showing variables on the screen instead of only in f8
-function debugScaleForm(textTable, loc)
- if debugMode then
- -- Define the display position (top left corner)
- local loc = loc or vec2(0.05, 0.65)
-
- -- Calculate dynamic height based on the number of lines in the textTable
- local lineHeight = 0.025 -- Height of each line of text
- local totalHeight = #textTable * lineHeight -- Dynamic height based on number of lines
- local boxPadding = 0.01 -- Padding to add around the text inside the box
- local size = vec2(0.18, totalHeight + boxPadding * 2) -- Width remains fixed, height is dynamic
-
- DrawRect(loc.x + size.x / 2, loc.y + size.y / 2, size.x, size.y, 0, 0, 0, 255)
-
- for i = 1, #textTable do
- local textLine = textTable[i]
-
- SetTextScale(0.30, 0.30)
-
- BeginTextCommandDisplayText("STRING")
- AddTextComponentSubstringKeyboardDisplay(textLine)
-
- EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01)
- end
- end
-end
\ No newline at end of file
diff --git a/shared/scaleforms/bigMessageInstance.lua b/shared/scaleforms/bigMessageInstance.lua
index ae581cb..7e2827b 100644
--- a/shared/scaleforms/bigMessageInstance.lua
+++ b/shared/scaleforms/bigMessageInstance.lua
@@ -1,6 +1,17 @@
-BigMessage = {}
+--[[
+ BigMessage Module
+ -----------------
+ This module provides a flexible way to display large, attention-grabbing messages
+ on screen using a Scaleform movie ("MP_BIG_MESSAGE_FREEMODE"). It supports multiple
+ message types (mission passed, colored shard, old-style, simple shard, rank-up, weapon purchased,
+ and large multiplayer messages), including customizable transitions and durations.
+]]
+
+local BigMessage = {}
BigMessage.__index = BigMessage
+--- Creates a new BigMessage instance.
+--- @return table table A new BigMessage object.
function BigMessage:new()
local self = setmetatable({}, BigMessage)
self.scaleform = nil
@@ -15,6 +26,7 @@ function BigMessage:new()
return self
end
+--- Loads the Scaleform movie if it has not been loaded yet.
function BigMessage:Load()
if self.scaleform then return end
self.scaleform = RequestScaleformMovie("MP_BIG_MESSAGE_FREEMODE")
@@ -23,7 +35,8 @@ function BigMessage:Load()
end
end
--- Dispose of the scaleform
+--- Disposes of the Scaleform movie.
+--- If manualDispose is true, executes a transition before disposing.
function BigMessage:Dispose()
if not self.scaleform then return end
@@ -34,8 +47,8 @@ function BigMessage:Dispose()
ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion)
EndScaleformMovieMethod()
+ -- Wait a fraction of the transition duration (in milliseconds)
Wait((self.transitionDuration * 0.5) * 1000)
-
self.manualDispose = false
end
@@ -46,8 +59,10 @@ function BigMessage:Dispose()
self.isDisplaying = false
end
+--- Updates the display by drawing the Scaleform movie fullscreen.
function BigMessage:Update()
if not self.scaleform then return end
+
DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0)
if self.manualDispose then return end
@@ -60,6 +75,7 @@ function BigMessage:Update()
ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion)
EndScaleformMovieMethod()
self.transitionExecuted = true
+ -- Extend duration slightly for smooth transition
self.duration = self.duration + ((self.transitionDuration * 0.5) * 1000)
else
self:Dispose()
@@ -67,14 +83,20 @@ function BigMessage:Update()
end
end
+--- Sets the transition properties for disposing the message.
+--- @param transition string The transition function name (default: "TRANSITION_OUT").
+--- @param duration number The duration for the transition (default: 0.4).
+--- @param preventAutoExpansion boolean Whether to prevent auto-expansion (default: true).
function BigMessage:SetTransition(transition, duration, preventAutoExpansion)
self.transition = transition or "TRANSITION_OUT"
self.transitionDuration = duration or 0.4
self.transitionPreventAutoExpansion = preventAutoExpansion or true
end
+--- Starts a thread to continuously update the HUD until the message is done.
function BigMessage:StartUpdate()
if self.isDisplaying then return end
+
self.isDisplaying = true
CreateThread(function()
while self.isDisplaying do
@@ -85,10 +107,13 @@ function BigMessage:StartUpdate()
end
--- Displays a mission passed message.
----
---- @param msg string The main message to display.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
+--- @param msg string The message to display.
+--- @param duration number|nil The duration (in milliseconds) to display the message (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose the Scaleform after display (default: false).
+--- @usage
+--- ```lua
+--- BigMessage:ShowMissionPassedMessage("MISSION PASSED", 5000)
+--- ```
function BigMessage:ShowMissionPassedMessage(msg, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -109,13 +134,12 @@ function BigMessage:ShowMissionPassedMessage(msg, duration, manualDispose)
end
--- Displays a colored shard message.
----
---- @param msg string The main message to display.
+--- @param msg string The main message.
--- @param desc string The description text.
---- @param textColor number The color index for the text.
---- @param bgColor number The color index for the background.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
+--- @param textColor number The text color index.
+--- @param bgColor number The background color index.
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose the Scaleform (default: false).
function BigMessage:ShowColoredShard(msg, desc, textColor, bgColor, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -134,12 +158,9 @@ function BigMessage:ShowColoredShard(msg, desc, textColor, bgColor, duration, ma
end
--- Displays an old-style mission passed message.
----
---- @param msg string The main message to display.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
----
---- @return void
+--- @param msg string The message.
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowOldMessage(msg, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -155,13 +176,10 @@ function BigMessage:ShowOldMessage(msg, duration, manualDispose)
end
--- Displays a simple shard message.
----
---- @param msg string The main message to display.
+--- @param msg string The main message.
--- @param subtitle string The subtitle text.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
----
---- @return void
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowSimpleShard(msg, subtitle, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -178,12 +196,11 @@ function BigMessage:ShowSimpleShard(msg, subtitle, duration, manualDispose)
end
--- Displays a rank-up message.
----
---- @param msg string The main message to display.
+--- @param msg string The main message.
--- @param subtitle string The subtitle text.
--- @param rank number The rank level achieved.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowRankupMessage(msg, subtitle, rank, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -203,12 +220,11 @@ function BigMessage:ShowRankupMessage(msg, subtitle, rank, duration, manualDispo
end
--- Displays a weapon purchased message.
----
---- @param bigMessage string The main message to display.
+--- @param bigMessage string The main message.
--- @param weaponName string The name of the weapon purchased.
---- @param weaponHash number The hash identifier of the weapon.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
+--- @param weaponHash number The weapon hash.
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowWeaponPurchasedMessage(bigMessage, weaponName, weaponHash, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -228,10 +244,9 @@ function BigMessage:ShowWeaponPurchasedMessage(bigMessage, weaponName, weaponHas
end
--- Displays a large multiplayer message.
----
---- @param msg string The main message to display.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
+--- @param msg string The main message.
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowMpMessageLarge(msg, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -254,11 +269,10 @@ function BigMessage:ShowMpMessageLarge(msg, duration, manualDispose)
end
--- Displays a "Wasted" multiplayer message.
----
---- @param msg string The main message to display.
+--- @param msg string The main message.
--- @param subtitle string The subtitle text.
---- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000.
---- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
+--- @param duration number|nil Duration in milliseconds (default: 5000).
+--- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowMpWastedMessage(msg, subtitle, duration, manualDispose)
duration = duration or 5000
self:Load()
@@ -274,4 +288,19 @@ function BigMessage:ShowMpWastedMessage(msg, subtitle, duration, manualDispose)
self:StartUpdate()
end
+--- Starts the update loop for displaying the message.
+function BigMessage:StartUpdate()
+ if self.isDisplaying then return end
+ self.isDisplaying = true
+ CreateThread(function()
+ while self.isDisplaying do
+ Wait(0)
+ self:Update()
+ end
+ end)
+end
+
+-- Create an instance of BigMessage and return it.
+BigMessage = BigMessage:new()
+
return BigMessage
\ No newline at end of file
diff --git a/shared/scaleforms/countDownHandler.lua b/shared/scaleforms/countDownHandler.lua
index 3cb1fc9..6c15049 100644
--- a/shared/scaleforms/countDownHandler.lua
+++ b/shared/scaleforms/countDownHandler.lua
@@ -1,6 +1,18 @@
+--[[
+ CountdownHandler Module
+ -------------------------
+ This module provides a countdown HUD using a Scaleform movie ("COUNTDOWN").
+ It handles loading, updating, and disposing of the scaleform, as well as
+ playing sounds and displaying messages for each countdown tick.
+
+ TriggerNetEvent(getScript()..":startCountdown", 5, 25)
+]]
+
CountdownHandler = {}
CountdownHandler.__index = CountdownHandler
+--- Creates a new CountdownHandler instance.
+--- @return table table A new CountdownHandler object.
function CountdownHandler:new()
local self = setmetatable({}, CountdownHandler)
self.scaleform = nil
@@ -9,14 +21,18 @@ function CountdownHandler:new()
return self
end
+--- Loads the "COUNTDOWN" scaleform movie.
function CountdownHandler:Load()
- if self.scaleform then return end
+ if self.scaleform then
+ return
+ end
self.scaleform = RequestScaleformMovie("COUNTDOWN")
while not HasScaleformMovieLoaded(self.scaleform) do
Wait(0)
end
end
+--- Disposes of the currently loaded scaleform movie.
function CountdownHandler:Dispose()
if self.scaleform then
SetScaleformMovieAsNoLongerNeeded(self.scaleform)
@@ -24,15 +40,19 @@ function CountdownHandler:Dispose()
end
end
+--- Updates the HUD by drawing the scaleform movie fullscreen.
function CountdownHandler:Update()
if self.scaleform then
DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0)
end
end
+--- Displays a message on the countdown HUD.
+--- @param message string The message to display.
function CountdownHandler:ShowMessage(message)
local r, g, b, a = self.colour.r, self.colour.g, self.colour.b, self.colour.a
+ -- Set the message in the scaleform.
BeginScaleformMovieMethod(self.scaleform, "SET_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(message)
ScaleformMovieMethodAddParamInt(r)
@@ -41,6 +61,7 @@ function CountdownHandler:ShowMessage(message)
ScaleformMovieMethodAddParamBool(true)
EndScaleformMovieMethod()
+ -- Trigger a fade effect (optional).
BeginScaleformMovieMethod(self.scaleform, "FADE_MP")
ScaleformMovieMethodAddParamPlayerNameString(message)
ScaleformMovieMethodAddParamInt(r)
@@ -49,17 +70,14 @@ function CountdownHandler:ShowMessage(message)
EndScaleformMovieMethod()
end
---- Starts the countdown with the specified number and HUD color.
----
---- @param number number|nil The starting number for the countdown. Defaults to 3.
---- @param hudColour number|nil The HUD color index. Defaults to 18.
----
---- @return boolean `true` when the countdown has finished.
----
+--- Starts the countdown HUD.
+--- @param number number|nil The starting number for the countdown (default: 3).
+--- @param hudColour number|nil The HUD colour index (default: 18).
+--- @return boolean boolean True when the countdown has finished.
--- @usage
--- ```lua
---- -- Start a countdown of 5 seconds with HUD color 25
--- if CountdownHandler:Start(5, 25) then
+--- -- When run in an if statement, the script will wait until its finished to continue
--- print("Countdown Complete")
--- end
--- ```
@@ -68,6 +86,7 @@ function CountdownHandler:Start(number, hudColour)
number = number or 3
hudColour = hudColour or 18
+ -- Get HUD colour using framework function; alternatives could be added here.
local r, g, b, a = GetHudColour(hudColour)
self.colour = { r = r, g = g, b = b, a = a }
@@ -81,18 +100,17 @@ function CountdownHandler:Start(number, hudColour)
end
end)
- -- Begin the countdown
+ -- Countdown logic
CreateThread(function()
local currentNumber = number
while currentNumber > 0 do
- -- Play countdown sound
playSound("Count")
self:ShowMessage(tostring(currentNumber))
Wait(1000)
currentNumber = currentNumber - 1
end
- playSound("Go")
+ playSound("Go")
self:ShowMessage("GO")
finished = true
@@ -101,14 +119,15 @@ function CountdownHandler:Start(number, hudColour)
self:Dispose()
finished = true
end)
+
while not finished do Wait(10) end
return true
end
--- Create an instance of CountdownHandler
+-- Create a singleton instance of CountdownHandler.
CountdownHandler = CountdownHandler:new()
--- Optional: Register an event to start the countdown
+-- Register an event to start the countdown.
RegisterNetEvent(getScript()..":startCountdown", function(number, hudColour)
CountdownHandler:Start(number, hudColour)
end)
diff --git a/shared/scaleforms/debugScaleform.lua b/shared/scaleforms/debugScaleform.lua
index b6cc394..52f69f3 100644
--- a/shared/scaleforms/debugScaleform.lua
+++ b/shared/scaleforms/debugScaleform.lua
@@ -1,41 +1,44 @@
+-------------------------------------------------------------
+-- Debug Text Display Functionality
+-------------------------------------------------------------
---- Displays debug information on the player's screen.
+--- Draws debug text on the screen if debugMode is enabled.
---
---- This function renders a semi-transparent box with multiple lines of text for debugging purposes.
---- It is controlled by the `debugMode` flag and can be positioned dynamically on the screen.
+--- Calculates a background rectangle based on the number of text lines and renders each line on-screen.
---
---- @param textTable table A table containing strings to display.
---- @param loc vector2|nil Optional. The screen position to display the debug box. Defaults to `vec2(0.05, 0.65)`.
+--- @param textTable table An array of strings to display.
+--- @param loc vector2 (Optional) Top-left coordinate for the text box (default: vec2(0.05, 0.65)).
---
--- @usage
--- ```lua
---- debugScaleForm({
---- "Player Position: X=123.45 Y=678.90 Z=12.34",
---- "Current Action: Running",
---- })
+---CreateThread(function()
+--- while true do
+--- debugScaleForm({
+--- "Line 1: Debug info",
+--- "Line 2: More info"
+--- })
+--- Wait(0)
+--- end
+---end)
--- ```
function debugScaleForm(textTable, loc)
if debugMode then
- -- Define the display position (top left corner)
- local loc = loc or vec2(0.05, 0.65)
+ loc = loc or vec2(0.05, 0.65)
- -- Calculate dynamic height based on the number of lines in the textTable
- local lineHeight = 0.025 -- Height of each line of text
- local totalHeight = #textTable * lineHeight -- Dynamic height based on number of lines
- local boxPadding = 0.01 -- Padding to add around the text inside the box
- local size = vec2(0.18, totalHeight + boxPadding * 2) -- Width remains fixed, height is dynamic
+ local lineHeight = 0.025 -- Height per line.
+ local totalHeight = #textTable * lineHeight
+ local boxPadding = 0.01 -- Padding around the text.
+ local size = vec2(0.18, totalHeight + boxPadding * 2)
+ -- Draw background rectangle.
DrawRect(loc.x + size.x / 2, loc.y + size.y / 2, size.x, size.y, 0, 0, 0, 255)
+ -- Render each line of text.
for i = 1, #textTable do
- local textLine = textTable[i]
-
SetTextScale(0.30, 0.30)
-
BeginTextCommandDisplayText("STRING")
- AddTextComponentSubstringKeyboardDisplay(textLine)
-
+ AddTextComponentSubstringKeyboardDisplay(textTable[i])
EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01)
end
end
-end
+end
\ No newline at end of file
diff --git a/shared/scaleforms/instructionalButtons.lua b/shared/scaleforms/instructionalButtons.lua
index e1d7f35..81819db 100644
--- a/shared/scaleforms/instructionalButtons.lua
+++ b/shared/scaleforms/instructionalButtons.lua
@@ -1,30 +1,45 @@
---- Creates instructional buttons using the detected polyzone library (ox_lib or PolyZone).
+-------------------------------------------------------------
+-- Instructional Buttons Functionality
+-------------------------------------------------------------
+
+--- Loads and draws instructional buttons on-screen using a scaleform movie.
---
---- This function generates instructional buttons on the player's screen based on the provided information.
---- It supports different polyzone libraries by automatically detecting which one is active.
+--- Requests the "instructional_buttons" scaleform, clears previous data, sets clear space,
+--- creates data slots for each button option provided in `info`, and then draws the scaleform fullscreen.
---
----@param info table A table containing the instructional buttons configuration.
---- - **keys** (`table`): A list of control keys to display.
---- - **text** (`string`): The description text for the buttons.
+--- @param info table An array of tables, where each table represents a button option:
+--- - keys (table): An array of key codes (e.g., {38, 29}) to display.
+--- - text (string): The label for the button.
---
----@usage
+--- @usage
--- ```lua
---- makeInstructionalButtons({
---- { keys = { 38 }, text = "Interact" },
---- { keys = { 47 }, text = "Pick Up" },
---- })
+---CreateThread(function()
+--- while true do
+--- makeInstructionalButtons({
+--- { keys = {38, 29}, text = "Open Menu" },
+--- { keys = {45}, text = "Close Menu" }
+--- })
+--- Wait(0)
+--- end
+---end)
--- ```
function makeInstructionalButtons(info)
local build = RequestScaleformMovie("instructional_buttons")
while not HasScaleformMovieLoaded(build) do Wait(0) end
+ -- Draw the scaleform fullscreen (initial draw).
DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0)
+
+ -- Clear previous instructions.
BeginScaleformMovieMethod(build, "CLEAR_ALL")
EndScaleformMovieMethod()
+
+ -- Set clear spacing between buttons.
BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE")
ScaleformMovieMethodAddParamInt(200)
EndScaleformMovieMethod()
+ -- Add each button option to the scaleform.
for i = 1, #info do
BeginScaleformMovieMethod(build, "SET_DATA_SLOT")
ScaleformMovieMethodAddParamInt(i - 1)
@@ -37,8 +52,11 @@ function makeInstructionalButtons(info)
EndScaleformMovieMethod()
end
+ -- Draw the instructional buttons.
BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS")
EndScaleformMovieMethod()
+
+ -- Set a translucent black background.
BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR")
ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(0)
@@ -46,5 +64,6 @@ function makeInstructionalButtons(info)
ScaleformMovieMethodAddParamInt(80)
EndScaleformMovieMethod()
+ -- Final full-screen draw with full opacity.
DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0)
end
\ No newline at end of file
diff --git a/shared/scaleforms/scaleform_basic.lua b/shared/scaleforms/scaleform_basic.lua
new file mode 100644
index 0000000..15100c2
--- /dev/null
+++ b/shared/scaleforms/scaleform_basic.lua
@@ -0,0 +1,242 @@
+--[[
+ Instructional Buttons & Debug Text Module
+ -------------------------------------------
+ This module provides functions for:
+ • Displaying instructional buttons on-screen via a scaleform movie.
+ • Drawing debug text with a background rectangle when debugMode is enabled.
+ • Rendering 3D text in the world.
+ • Displaying help messages and spinners.
+]]
+
+-------------------------------------------------------------
+-- Instructional Buttons Functionality
+-------------------------------------------------------------
+
+--- Loads and draws instructional buttons on-screen using a scaleform movie.
+---
+--- Requests the "instructional_buttons" scaleform, clears previous data, sets clear space,
+--- creates data slots for each button option provided in `info`, and then draws the scaleform fullscreen.
+---
+--- @param info table An array of tables, where each table represents a button option:
+--- - keys (table): An array of key codes (e.g., {38, 29}) to display.
+--- - text (string): The label for the button.
+---
+--- @usage
+--- ```lua
+---CreateThread(function()
+--- while true do
+--- makeInstructionalButtons({
+--- { keys = {38, 29}, text = "Open Menu" },
+--- { keys = {45}, text = "Close Menu" }
+--- })
+--- Wait(0)
+--- end
+---end)
+--- ```
+function makeInstructionalButtons(info)
+ local build = RequestScaleformMovie("instructional_buttons")
+ while not HasScaleformMovieLoaded(build) do Wait(0) end
+
+ -- Draw the scaleform fullscreen (initial draw).
+ DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0)
+
+ -- Clear previous instructions.
+ BeginScaleformMovieMethod(build, "CLEAR_ALL")
+ EndScaleformMovieMethod()
+
+ -- Set clear spacing between buttons.
+ BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE")
+ ScaleformMovieMethodAddParamInt(200)
+ EndScaleformMovieMethod()
+
+ -- Add each button option to the scaleform.
+ for i = 1, #info do
+ BeginScaleformMovieMethod(build, "SET_DATA_SLOT")
+ ScaleformMovieMethodAddParamInt(i - 1)
+ for k = 1, #info[i].keys do
+ ScaleformMovieMethodAddParamPlayerNameString(GetControlInstructionalButton(2, info[i].keys[k], true))
+ end
+ BeginTextCommandScaleformString("STRING")
+ AddTextComponentSubstringKeyboardDisplay(info[i].text)
+ EndTextCommandScaleformString()
+ EndScaleformMovieMethod()
+ end
+
+ -- Draw the instructional buttons.
+ BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS")
+ EndScaleformMovieMethod()
+
+ -- Set a translucent black background.
+ BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR")
+ ScaleformMovieMethodAddParamInt(0)
+ ScaleformMovieMethodAddParamInt(0)
+ ScaleformMovieMethodAddParamInt(0)
+ ScaleformMovieMethodAddParamInt(80)
+ EndScaleformMovieMethod()
+
+ -- Final full-screen draw with full opacity.
+ DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0)
+end
+
+-------------------------------------------------------------
+-- Debug Text Display Functionality
+-------------------------------------------------------------
+
+--- Draws debug text on the screen if debugMode is enabled.
+---
+--- Calculates a background rectangle based on the number of text lines and renders each line on-screen.
+---
+--- @param textTable table An array of strings to display.
+--- @param loc vector2 (Optional) Top-left coordinate for the text box (default: vec2(0.05, 0.65)).
+---
+--- @usage
+--- ```lua
+---CreateThread(function()
+--- while true do
+--- debugScaleForm(
+--- {
+--- "Line 1: Debug info",
+--- "Line 2: More info"
+--- }
+--- )
+--- Wait(0)
+--- end
+---end)
+--- ```
+function debugScaleForm(textTable, loc)
+ if debugMode then
+ loc = loc or vec2(0.05, 0.65)
+
+ local lineHeight = 0.025 -- Height per line.
+ local totalHeight = #textTable * lineHeight
+ local boxPadding = 0.01 -- Padding around the text.
+ local size = vec2(0.18, totalHeight + boxPadding * 2)
+
+ -- Draw background rectangle.
+ DrawRect(loc.x + size.x / 2, loc.y + size.y / 2, size.x, size.y, 0, 0, 0, 255)
+
+ -- Render each line of text.
+ for i = 1, #textTable do
+ SetTextScale(0.30, 0.30)
+ BeginTextCommandDisplayText("STRING")
+ AddTextComponentSubstringKeyboardDisplay(textTable[i])
+ EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01)
+ end
+ end
+end
+
+-------------------------------------------------------------
+-- 3D Text Rendering
+-------------------------------------------------------------
+
+--- Draws 3D text at specified world coordinates.
+---
+--- Configures text properties, draws the text, and displays a background rectangle behind it.
+---
+--- @param coord table A vector3 with x, y, and z coordinates.
+--- @param text string The text to display.
+--- @param highlight boolean (Optional) If true, highlights parts of the text.
+---
+--- @usage
+--- ```lua
+--- CreateThread(function()
+--- while true do
+--- DrawText3D(vector3(100, 200, 300), "Hello World", true)
+--- Wait(0)
+--- end
+--- end)
+--- ```
+function DrawText3D(coord, text, highlight)
+ SetTextScale(0.30, 0.30)
+ SetTextFont(0)
+ SetTextProportional(1)
+ SetTextColour(255, 255, 255, 215)
+ SetTextEntry("STRING")
+ SetTextCentre(true)
+
+ local totalLength = string.len(text)
+ local textMaxLength = 99 -- max 99
+ local text = totalLength > textMaxLength and text:sub(1, totalLength - (totalLength - textMaxLength)) or text
+ AddTextComponentString(highlight and text:gsub("%~w~", "~y~") or text)
+ SetDrawOrigin(coord.x, coord.y, coord.z, 0)
+ DrawText(0.0, 0.0)
+ local count, length = GetLineCountAndMaxLength(text)
+
+ local padding = 0.005
+ local heightFactor = (count / 43) + padding
+ local weightFactor = (length / 150) + padding
+
+ local height = (heightFactor / 2) - padding / 1
+ local width = (weightFactor / 2) - padding / 1
+
+ DrawRect(0.0, height, width, heightFactor, 0, 0, 0, 150)
+ ClearDrawOrigin()
+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)
+ local lineCount, maxLength = 0, 0
+ for line in text:gmatch("[^\n]+") do
+ lineCount += 1
+ local lineLength = string.len(line)
+ if lineLength > maxLength then
+ maxLength = lineLength
+ end
+ end
+ if lineCount == 0 then lineCount = 1 end
+ return lineCount, maxLength
+end
+
+-------------------------------------------------------------
+-- Additional UI Helpers
+-------------------------------------------------------------
+
+--- Displays a help message on the screen.
+---
+--- @param text string The message to display.
+---
+--- @usage
+--- ```lua
+--- DisplayHelpMsg("Press E to interact")
+--- ```
+function DisplayHelpMsg(text)
+ BeginTextCommandDisplayHelp("STRING")
+ AddTextComponentScaleform(text)
+ EndTextCommandDisplayHelp(0, true, false, -1)
+end
+
+--- Displays a "Saving/Loading" spinner with a custom message.
+---
+--- @param text string The message to display alongside the spinner.
+---
+--- @usage
+--- ```lua
+--- displaySpinner("Saving data...")
+--- ```
+function displaySpinner(text)
+ BeginTextCommandBusyspinnerOn('STRING')
+ AddTextComponentSubstringPlayerName(text)
+ EndTextCommandBusyspinnerOn(4)
+end
+
+--- Stops the "Saving/Loading" spinner.
+---
+--- This function should only be called client-side.
+---
+--- @usage
+--- ```lua
+--- stopSpinner()
+--- ```
+function stopSpinner()
+ if not isServer() then
+ BusyspinnerOff()
+ end
+end
\ No newline at end of file
diff --git a/shared/scaleforms/timerBars.lua b/shared/scaleforms/timerBars.lua
index 1124e79..59f9fea 100644
--- a/shared/scaleforms/timerBars.lua
+++ b/shared/scaleforms/timerBars.lua
@@ -1,3 +1,21 @@
+--- Creates and displays a timer HUD on the screen.
+--- Draws a title (if provided) and a series of timer bars from the supplied data.
+---
+--- @param title string|nil Optional title to display at the top of the HUD.
+--- @param data table A table of timer bar entries. Each entry should include:
+--- - stat (string): The statistic name.
+--- - value (string): The value to display.
+--- - multi (number|nil): Optional, indicates multiple checkpoints (e.g., progress levels).
+--- @param alpha number|nil Optional alpha value (transparency) for the HUD; defaults to 255.
+---
+--- @usage
+--- ```lua
+--- createTimerHud("Timer", {
+--- { stat = "Health", value = "85%" },
+--- { stat = "Armor", value = "50%", multi = 2 },
+--- { stat = "Stamina", value = "100%" },
+--- }, 255)
+--- ```
function createTimerHud(title, data, alpha)
loadTextureDict("timerbars")
diff --git a/shared/banking.lua b/shared/societybank.lua
similarity index 53%
rename from shared/banking.lua
rename to shared/societybank.lua
index ab348bd..75395c3 100644
--- a/shared/banking.lua
+++ b/shared/societybank.lua
@@ -1,11 +1,79 @@
+--[[
+ Society Banking Module
+ ------------------------
+ This module provides functions to interact with society bank accounts across
+ different banking systems. Supported systems include:
+ • qb-banking
+ • esx_society *testing*
+ • Renewed-Banking
+ • fd_banking
+ • okokBanking
+]]
-function chargeSociety(society, amount)
- local bankScript, newAmount = "", 0
- if isStarted("Renewed-Banking") then
+--- Retrieves the current balance of a society's bank account.
+--- @param society string The identifier of the society.
+--- @return number number The current account balance.
+--- @usage
+--- ```lua
+--- local balance = getSocietyAccount("police")
+--- print("Police account balance: $"..balance)
+--- ```
+function getSocietyAccount(society)
+ local bankScript, amount = "", 0
+ if isStarted("qb-banking") then
+ bankScript = "qb-banking"
+ if not exports["qb-banking"]:GetAccount(society) then
+ if Jobs[society] then
+ print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'")
+ exports["qb-banking"]:CreateJobAccount(society, 0)
+ Wait(150)
+ elseif Gangs[society] then
+ print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'")
+ exports["qb-banking"]:CreateGangAccount(society, 0)
+ Wait(150)
+ end
+ end
+ amount = exports["qb-banking"]:GetAccountBalance(society)
+
+ elseif isStarted("esx_society") then
+ bankScript = "esx_society"
+ -- Since esx_society does not have a native client export for retrieving money,
+ -- we use a server callback to get the final amount.
+ amount = triggerCallback(getScript() .. ":getESXSocietyAccount", society) or 0
+
+ elseif isStarted("Renewed-Banking") then
bankScript = "Renewed-Banking"
- exports['Renewed-Banking']:removeAccountMoney(society, amount)
+ amount = exports["Renewed-Banking"]:getAccountMoney(society)
- elseif isStarted("qb-banking") then
+ elseif isStarted("fd_banking") then
+ bankScript = "fd_banking"
+ amount = exports["fd_banking"]:GetAccount(society)
+
+ elseif isStarted("okokBanking") then
+ bankScript = "okokBanking"
+ amount = exports['okokBanking']:GetAccount(society)
+ end
+
+ if bankScript == "" then
+ print("^1Error^7: ^3GetSocietyAccount^7: ^2No supported banking script found")
+ else
+ debugPrint("^6Bridge^7: ^3"..bankScript.."^7: ^2Retrieved account ^7'^6"..society.."^7' ($"..amount..")")
+ end
+
+ return amount
+end
+
+--- Deducts funds from a society's bank account.
+--- @param society string The identifier of the society.
+--- @param amount number The amount of money to remove.
+--- @usage
+--- ```lua
+--- chargeSociety("police", 1000)
+--- ```
+function chargeSociety(society, amount)
+ local bankScript, newAmount = "", 0
+
+ if isStarted("qb-banking") then
bankScript = "qb-banking"
if not exports["qb-banking"]:GetAccount(society) then
if Jobs[society] then
@@ -17,47 +85,73 @@ function chargeSociety(society, amount)
end
end
exports["qb-banking"]:RemoveMoney(society, amount)
+ elseif isStarted("esx_society") then
+ bankScript = "esx_society"
+ TriggerEvent("esx_society:withdrawMoney", society, amount)
+
+ elseif isStarted("Renewed-Banking") then
+ bankScript = "Renewed-Banking"
+ exports['Renewed-Banking']:removeAccountMoney(society, amount)
elseif isStarted("fd_banking") then
bankScript = "fd_banking"
exports["fd_banking"]:RemoveMoney(society, amount)
+
elseif isStarted("okokBanking") then
bankScript = "okokBanking"
exports['okokBanking']:RemoveMoney(society, amount)
end
+
if bankScript == "" then
print("^1Error^7: ^3ChargeSociety^7: ^2No supported banking script found")
else
newAmount = getSocietyAccount(society)
- debugPrint("^6Bridge^7: ^3"..bankScript.."^7: ^2Removing ^7$"..amount.." ^2from account ^7'^6"..society.."^7' ($"..newAmount..")")
+ debugPrint("^6Bridge^7: ^3"..bankScript.."^7: ^2Removing $"..amount.." from account '^6"..society.."^7' ($"..newAmount..")")
end
end
+--- Adds funds to a society's bank account.
+--- @param society string The identifier of the society.
+--- @param amount number The amount of money to add.
+--- @usage
+--- ```lua
+--- fundSociety("police", 500)
+--- ```
function fundSociety(society, amount)
local bankScript, newAmount = "", 0
- if isStarted("Renewed-Banking") then
- bankScript = "Renewed-Banking"
- exports['Renewed-Banking']:addAccountMoney(society, amount)
- newAmount = exports["Renewed-Banking"]:getAccountMoney(society)
- elseif isStarted("qb-banking") then
+
+ if isStarted("qb-banking") then
bankScript = "qb-banking"
- if not exports["qb-banking"]:GetAccount(society) then
+ if not exports["qb-banking"]:GetAccount(society) then
if Jobs[society] then
print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'")
- exports["qb-banking"]:CreateJobAccount(society, 0) Wait(150) -- make new account if return "null"
+ exports["qb-banking"]:CreateJobAccount(society, 0)
+ Wait(150)
elseif Gangs[society] then
print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'")
- exports["qb-banking"]:CreateGangAccount(society, 0) Wait(150) -- make new account if return "null"
+ exports["qb-banking"]:CreateGangAccount(society, 0)
+ Wait(150)
end
- end
+ end
exports["qb-banking"]:AddMoney(society, amount)
+ elseif isStarted("esx_society") then
+ bankScript = "esx_society"
+ -- Use the esx_society event to deposit money.
+ TriggerServerEvent('esx_society:depositMoney', society, amount)
+ -- Use callback to return the updated balance.
+ newAmount = triggerCallback(getScript() .. ":getESXSocietyAccount", society) or 0
+
+ elseif isStarted("Renewed-Banking") then
+ bankScript = "Renewed-Banking"
+ exports['Renewed-Banking']:addAccountMoney(society, amount)
+ newAmount = exports["Renewed-Banking"]:getAccountMoney(society)
elseif isStarted("fd_banking") then
bankScript = "fd_banking"
- exports.fd_banking:AddMoney(society, amount)
+ exports["fd_banking"]:AddMoney(society, amount)
elseif isStarted("okokBanking") then
bankScript = "okokBanking"
@@ -73,39 +167,12 @@ function fundSociety(society, amount)
end
end
-function getSocietyAccount(society)
- local bankScript, amount = "", 0
- if isStarted("Renewed-Banking") then
- bankScript = "Renewed-Banking"
- amount = exports["Renewed-Banking"]:getAccountMoney(society)
-
- elseif isStarted("qb-banking") then
- bankScript = "qb-banking"
- if not exports["qb-banking"]:GetAccount(society) then
- if Jobs[society] then
- print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'")
- exports["qb-banking"]:CreateJobAccount(society, 0) Wait(150) -- make new account if return "null"
- elseif Gangs[society] then
- print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'")
- exports["qb-banking"]:CreateGangAccount(society, 0) Wait(150) -- make new account if return "null"
- end
- end
- amount = exports["qb-banking"]:GetAccountBalance(society)
-
- elseif isStarted("fd_banking") then
- bankScript = "fd_banking"
- amount = exports["fd_banking"]:GetAccount(society)
-
- elseif isStarted("okokBanking") then
- bankScript = "okokBanking"
- amount = exports['okokBanking']:GetAccount(society)
-
- end
- if bankScript == "" then
- print("^1Error^7: ^3GetSocietyAccount^7: ^2No supported banking script found")
- else
- debugPrint("^6Bridge^7: ^3"..bankScript.."^7: ^2Retrieved account ^7'^6"..society.."^7' ($"..amount..")")
- end
- return amount
+-- other
+if isStarted("esx_society") then
+ createCallback(getScript() .. ":getESXSocietyAccount", function(source, society)
+ -- Example query – adjust table/field names to match your esx_society implementation.
+ local result = MySQL.scalar.await('SELECT money FROM society_money WHERE society = ?', { society })
+ return result or 0
+ end)
end
\ No newline at end of file
diff --git a/shared/stashcontrol.lua b/shared/stashcontrol.lua
index 7d0a924..dc99009 100644
--- a/shared/stashcontrol.lua
+++ b/shared/stashcontrol.lua
@@ -1,17 +1,46 @@
+--[[
+ Stash Management Module
+ -------------------------
+ This module handles stash-related operations including:
+ • Retrieving stash items (from server or local cache).
+ • Checking for required items in stashes.
+ • Opening stashes using different inventory systems.
+ • Removing items from stashes.
+ • Checking if a stash has specific items.
+]]
+
+-- Global variable to hold the current stash (used in callbacks).
local stash
+
+-- If running on the server, create a callback to retrieve stash items.
if isServer() then
- createCallback(getScript()..':server:GetStashItems',
- function(source, stashName)
- stash = getStash(stashName) return stash
- end)
+ createCallback(getScript()..':server:GetStashItems', function(source, stashName)
+ stash = getStash(stashName)
+ return stash
+ end)
end
-local stashCache ={}
+-- Local cache for stashes.
+local stashCache = {}
+
+--- Retrieves (or updates) a local stash cache entry with a timeout.
+--- When the cache is empty or expired, it triggers a server callback to update the items.
+---
+--- @param stashName string The name of the stash.
+--- @param stop boolean (Optional) If true, clears the entire stash cache.
+--- @return boolean True if items exist in cache (and recheck is skipped), false otherwise.
+---
+--- @usage
+--- ```lua
+--- local cached = GetStashTimeout("playerStash")
+--- ```
function GetStashTimeout(stashName, stop)
if stop then
stashCache = {}
return
end
+
+ -- Retrieve cache for this stash, or initialize if not present.
stash = stashCache[stashName]
if not stash then
debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache ^1not ^2found^7, ^2need to grab from server^7")
@@ -19,16 +48,21 @@ function GetStashTimeout(stashName, stop)
stash = stashCache[stashName]
else
debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache found^7")
+ ("^6Bridge^7: ^2Local Stash '^3"..stashName.."^7' cache found")
end
+
+ -- If there are already items in cache, skip recheck.
if countTable(stashCache[stashName].items) > 0 then
debugPrint("^6Bridge^7: '^3"..stashName.." ^2Items found in cache, skipping recheck")
return true
end
+
+ -- If timeout has expired, update the stash items from the server.
if stashCache[stashName].timeout <= 0 then
stashCache[stashName].items = triggerCallback(getScript()..':server:GetStashItems', stashName)
- stashCache[stashName].timeout = 15000
+ stashCache[stashName].timeout = 15000 -- Timeout in milliseconds.
CreateThread(function()
- while stash.timeout > 0 do
+ while stashCache[stashName] and stashCache[stashName].timeout > 0 do
stashCache[stashName].timeout -= 1000
Wait(1000)
end
@@ -39,46 +73,93 @@ function GetStashTimeout(stashName, stop)
return false
end
+--- Checks if the specified stashes have the required items.
+---
+--- If multiple stashes are provided (as a table), it iterates over each until all required items are found.
+---
+--- @param stashes string|table Either a single stash name or a table of stash names.
+--- @param itemTable table A table where keys are item names and values are the required amounts.
+--- @return boolean, string|nil `boolean, string` true and the stash name if found, otherwise false and nil.
+---
+--- @usage
+--- ```lua
+--- local found, stashName = checkHasItem({"playerStash", "storageStash"}, { iron = 2, wood = 5 })
+--- ```
function checkHasItem(stashes, itemTable)
if not stashes then
return hasItem(itemTable), nil
end
+
if type(stashes) == "table" then
- local succeses = 0
+ local successes = 0
local itemCount = countTable(itemTable)
- --for _, item in pairs(itemTable) do itemCount += 1 end
+ -- Iterate over each provided stash name.
for _, name in pairs(stashes) do
- Wait(10) -- add delay because qb doesn't appreciate multiple callbacks for stashes
+ Wait(10) -- Delay to avoid multiple callbacks issues.
GetStashTimeout(name)
for item, amount in pairs(itemTable) do
- debugPrint("^6Bridge^7: ^2Checking"..(name and " ^7'^6"..name.."^7'" or "").." ^2ingredients^7 - ^6"..item.."^7")
+ debugPrint("^6Bridge^7: ^2Checking "..(name and " '^3"..name.."^7'" or "").." ingredients - ^6"..item.."^7")
if stashhasItem(stashCache[name].items, item, amount) then
- succeses += 1
- if succeses == itemCount then
+ successes = successes + 1
+ if successes == itemCount then
return true, name
end
end
end
end
else
- debugPrint("^6Bridge^7: ^2Checking"..(stashes and " ^7'^6"..stashes.."^7'" or "").." ^2ingredients^7 - ^6"..k.."^7")
+ debugPrint("^6Bridge^7: ^2Checking "..(stashes and " '^3"..stashes.."^7'" or "").." ingredients")
GetStashTimeout(stashes)
return stashhasItem(stashCache[stashes].items, itemTable), stashes
end
+
return false, nil
end
+-------------------------------------------------------------
+-- Stash Opening Functions
+-------------------------------------------------------------
--- Stash Items
+--- Opens a stash using the active inventory system.
+---
+--- Checks for job or gang restrictions before opening the stash.
+---
+--- @param data table A table containing stash data:
+--- - stash (string): The stash identifier.
+--- - label (string): Display label.
+--- - maxWeight (number|nil): Maximum weight (default 600000).
+--- - slots (number|nil): Number of slots (default 40).
+--- - stashOptions (table|nil): Additional options for the stash.
+--- - job/gang (string|nil): Restriction for access.
+--- - coords (vector3): Coordinates to "look" at.
+---
+--- @usage
+--- ```lua
+--- openStash({ stash = "playerStash", label = "Player Stash", coords = vector3(100, 200, 30) })
+--- ```
function openStash(data)
- if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
+ if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
+
if isStarted(OXInv) then
exports[OXInv]:openInventory('stash', data.stash)
+
+ elseif isStarted(CoreInv) then
+ TriggerServerEvent('core_inventory:server:openInventory', data.stash, 'stash')
+
elseif isStarted(CodeMInv) then
- exports[CodeMInv]:OpenStash(data.stash, StashWeight, 100)
+ TriggerServerEvent('codem-inventory:server:openstash', data.stash, data.slots, data.maxWeight, data.label)
+
+ elseif isStarted(OrigenInv) then
+ exports[OrigenInv]:openInventory('stash', data.stash, { label = data.label })
+
elseif isStarted(QBInv) then
if QBInvNew then
- TriggerServerEvent(getScript()..':server:OpenStashQB', { stashName = data.stash, label = data.label, maxweight = data.maxWeight or 600000, slots = data.slots or 40 })
+ TriggerServerEvent(getScript()..':server:OpenStashQB', {
+ stashName = data.stash,
+ label = data.label,
+ maxweight = data.maxWeight or 600000,
+ slots = data.slots or 40
+ })
else
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions)
@@ -86,18 +167,38 @@ function openStash(data)
else
TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions)
- end
+ end
+
lookEnt(data.coords)
end
+-- Register an event for opening QB stashes.
RegisterNetEvent(getScript()..':server:OpenStashQB', function(data)
exports[QBInv]:OpenInventory(source, data.stashName, data)
end)
-function getStash(stashName) local stashResource = ""
+-------------------------------------------------------------
+-- Stash Retrieval Function
+-------------------------------------------------------------
+
+--- Retrieves stash items from the active inventory system.
+---
+--- This function converts the raw stash items into a standardized table using the global Items lookup.
+---
+--- @param stashName string The identifier for the stash.
+--- @return stashTable table A table of items from the stash.
+---
+--- @usage
+--- ```lua
+--- local items = getStash("playerStash")
+--- ```
+function getStash(stashName)
+ local stashResource = ""
if type(stashName) ~= "string" then
- return print("Stash name was not a string %s(%s)", stashName, type(stashName))
+ print("^6Bridge^7: ^2Stash name was not a string ^3"..stashName.."^7(^3"..type(stashName).."^7)")
+ return {}
end
+
local stashItems, items = {}, {}
if isStarted(OXInv) then stashResource = OXInv
stashItems = exports[OXInv]:Inventory(stashName).items
@@ -109,14 +210,15 @@ function getStash(stashName) local stashResource = ""
stashItems = exports[CoreInv]:getInventory(stashName)
elseif isStarted(CodeMInv) then stashResource = CodeMInv
- stashItems = exports[CodeMInv]:GetInventoryItems('Stash', stashName)
+ stashItems = exports[CodeMInv]:GetStashItems(stashName)
elseif isStarted(OrigenInv) then stashResource = OrigenInv
- stashItems = exports[OrigenInv]:GetStashItems(stashName)
+ stashItems = exports[OrigenInv]:getInventory(stashName)
elseif isStarted(PSInv) then stashResource = PSInv
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
if result then stashItems = json.decode(result) end
+
elseif isStarted(QBInv) then stashResource = QBInv
local result = MySQL.scalar.await("SELECT items FROM "..(QBInvNew and "inventories" or "stashitem").." WHERE identifier = ?", { stashName })
if result then stashItems = json.decode(result) end
@@ -127,8 +229,8 @@ function getStash(stashName) local stashResource = ""
for _, item in pairs(stashItems) do
local itemInfo = Items[item.name:lower()]
if itemInfo then
- local indexNum = #items+1 -- Added to help recreate missing slot numbers
- items[(item.slot and item.slot) or indexNum] = {
+ local indexNum = #items + 1 -- Fallback index if slot is missing.
+ items[(item.slot or indexNum)] = {
name = itemInfo.name or nil,
amount = tonumber(item.amount) or tonumber(item.count),
info = item.info or "",
@@ -144,16 +246,30 @@ function getStash(stashName) local stashResource = ""
}
end
end
- debugPrint("^6Bridge^7: ^3GetStashItems^7: ^2Stash information for ^7'^6"..stashName.."^7' ^2retrieved^7")
+ debugPrint("^6Bridge^7: ^3GetStashItems^7: ^2Stash information for '^6"..stashName.."^7' retrieved")
end
jsonPrint(items)
return items
end
-function stashRemoveItem(stashItems, stashName, items) local amount = amount and amount or 1
- -- print("stashItems: "..json.encode(stashItems, { indent = true}))
- -- print("stashName: "..json.encode(stashName, { indent = true}))
- -- print("items: "..json.encode(items, { indent = true}))
+-------------------------------------------------------------
+-- Stash Item Removal Function
+-------------------------------------------------------------
+
+--- Removes items from a stash using the active inventory system.
+---
+--- Iterates over the provided items and adjusts the stash contents accordingly.
+---
+--- @param stashItems table The current stash items.
+--- @param stashName string|table The stash identifier (or table of identifiers).
+--- @param items table A table of items to remove (keys are item names, values are amounts).
+---
+--- @usage
+--- ```lua
+--- stashRemoveItem(currentItems, "playerStash", { iron = 2, wood = 5 })
+--- ```
+function stashRemoveItem(stashItems, stashName, items)
+
if isStarted(OXInv) then
for k, v in pairs(items) do
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OXInv, k, v)
@@ -171,19 +287,19 @@ function stashRemoveItem(stashItems, stashName, items) local amount = amount and
end
elseif isStarted(QSInv) then
- for k, v in pairs(items) do
- for l in pairs(stashItems) do
- if stashItems[l].name == k then
- if (stashItems[l].amount - v) <= 0 then
- debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
- stashItems[l] = nil
- else
- debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
- exports[QSInv]:RemoveItemIntoStash(stashName, k, v, l)
- end
+ for k, v in pairs(items) do
+ for l in pairs(stashItems) do
+ if stashItems[l].name == k then
+ if (stashItems[l].amount - v) <= 0 then
+ debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
+ stashItems[l] = nil
+ else
+ debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
+ exports[QSInv]:RemoveItemIntoStash(stashName, k, v, l)
end
end
end
+ end
elseif isStarted(CoreInv) then
for k, v in pairs(items) do
@@ -205,8 +321,8 @@ function stashRemoveItem(stashItems, stashName, items) local amount = amount and
end
end
end
- debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
-
+ exports[CodeMInv]:UpdateStash(stashName, stashItems)
+ debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3CodeM^2 stash ^7'^6"..stashName.."^7'")
elseif isStarted(OrigenInv) then
for k, v in pairs(items) do
exports[OrigenInv]:RemoveFromStash(stashName, k, v)
@@ -228,7 +344,11 @@ function stashRemoveItem(stashItems, stashName, items) local amount = amount and
end
end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
- MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName, ['items'] = json.encode(stashItems) })
+ MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
+ ['stash'] = stashName,
+ ['items'] = json.encode(stashItems)
+ })
+
elseif isStarted(QBInv) then
if QBInvNew then
for k, v in pairs(items) do
@@ -236,36 +356,56 @@ function stashRemoveItem(stashItems, stashName, items) local amount = amount and
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName[1].."^7'")
- MySQL.Async.insert('INSERT INTO inventories (identifier, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName[1], ['items'] = json.encode(stashItems) })
+ MySQL.Async.insert('INSERT INTO inventories (identifier, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
+ ['stash'] = stashName[1],
+ ['items'] = json.encode(stashItems)
+ })
else
for k, v in pairs(items) do
for l in pairs(stashItems) do
if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then
- if Config.System.Debug then
- print("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
- end
+ debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
stashItems[l] = nil
else
- if Config.System.Debug then
- print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
- end
+ debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with "..QBInv, k, v)
stashItems[l].amount -= v
end
end
end
end
- debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'")
- MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashName, ['items'] = json.encode(stashItems) })
+ debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash '^6"..stashName.."^7'")
+ MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
+ ['stash'] = stashName,
+ ['items'] = json.encode(stashItems)
+ })
end
+
else
- print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3exports^1.^2lua^7")
+ print("^4ERROR^7: ^2No Inventory detected ^7- ^2Check ^3starter^1.^2lua^7")
end
end
RegisterNetEvent(getScript()..":server:stashRemoveItem", stashRemoveItem)
+-------------------------------------------------------------
+-- Stash Item Availability Check
+-------------------------------------------------------------
+
+--- Checks whether a stash has the required amount of specific items.
+---
+--- It iterates through the provided items and tallies available quantities.
+---
+--- @param stashItems table The items available in the stash.
+--- @param items string|table The item name or table of required items (key: item, value: amount).
+--- @param amount number (Optional) The required amount (if a single item is provided).
+--- @return boolean, table (`boolean, string`) Returns true (and a table with counts) if all items are available; false otherwise.
+---
+--- @usage
+--- ```lua
+--- local hasAll, details = stashhasItem(currentStashItems, { iron = 2, wood = 5 })
+--- ```
function stashhasItem(stashItems, items, amount)
- local invs = {OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, QBInv, PSInv}
+ local invs = { OXInv, QSInv, CoreInv, CodeMInv, OrigenInv, QBInv, PSInv }
local foundInv = ""
for _, inv in ipairs(invs) do
if isStarted(inv) then
@@ -274,9 +414,11 @@ function stashhasItem(stashItems, items, amount)
end
end
+ -- Ensure items is a table.
if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end
+
local hasTable = {}
- for item, amount in pairs(items) do
+ for item, requiredAmount in pairs(items) do
local count = 0
for _, itemData in pairs(stashItems) do
if itemData and (itemData.name == item) then
@@ -284,11 +426,13 @@ function stashhasItem(stashItems, items, amount)
end
end
- local debugMsg = string.format("^6Bridge^7: ^3stashHasItem^7[^6%s^7]: %s '%s' ^3%d^7/^3%d^7", foundInv, (count >= amount and "^5FOUND^7" or "^1NOT FOUND^7"), item, count, amount)
+ local debugMsg = string.format("^6Bridge^7: ^3stashHasItem^7[^6%s^7]: %s '%s' ^3%d^7/^3%d^7", foundInv, (count >= requiredAmount and "^5FOUND^7" or "^1NOT FOUND^7"), item, count, requiredAmount)
debugPrint(debugMsg)
- hasTable[item] = { hasItem = (count >= amount), count = count }
+ hasTable[item] = { hasItem = (count >= requiredAmount), count = count }
end
+
for k, v in pairs(hasTable) do if v.hasItem == false then return false, hasTable end end
+
return true, hasTable
-end
\ No newline at end of file
+end
diff --git a/shared/targets.lua b/shared/targets.lua
index 26cd067..0f85ff1 100644
--- a/shared/targets.lua
+++ b/shared/targets.lua
@@ -1,9 +1,31 @@
--- This is for experimental targets based on GTA in-world text prompts --
-local TextTargets = {}
+--[[
+ Experimental GTA In-World Text Prompts Targets Module
+ -------------------------------------------------------
+ This module handles the creation, removal, and management of in-world text targets
+ for interacting with entities and zones using GTA text prompts. It supports multiple
+ targeting systems: OX Target, QB Target, or a fallback using DrawText3D.
+
+ Available functionalities:
+ • createEntityTarget - Creates a target for a specific entity.
+ • createBoxTarget - Creates a box-shaped zone target.
+ • createCircleTarget - Creates a circular zone target.
+ • createModelTarget - Creates a target for specified models.
+ • removeEntityTarget - Removes a target from an entity.
+ • removeZoneTarget - Removes a zone target.
+
+ Fallback: If no targeting system is detected (or if disabled via Config.System.DontUseTarget),
+ the module uses DrawText3D prompts. This is experimental and may not work as expected.
+]]
+
+-------------------------------------------------------------
+-- Utility Data & Tables
+-------------------------------------------------------------
+
+-- Mapping of key codes to human-readable key names.
local Keys = {
[322] = "ESC", [288] = "F1", [289] = "F2", [170] = "F3", [166] = "F5",
[167] = "F6", [168] = "F7", [169] = "F8", [56] = "F9", [57] = "F10",
- [243] = "~", [157] = "1", [158] = "2", [160] = "3", [164] = "4", [165] = "5",
+ [243] = "~", [157] = "1", [158] = "2", [160] = "3", [164] = "4", [165] = "5",
[159] = "6", [161] = "7", [162] = "8", [163] = "9", [84] = "-", [83] = "=",
[177] = "BACKSPACE", [37] = "TAB",
[44] = "Q", [32] = "W", [38] = "E", [45] = "R", [245] = "T", [246] = "Y",
@@ -15,49 +37,63 @@ local Keys = {
[244] = "M", [82] = ",", [81] = "."
}
--- Target Creation --
--- Target Entities, this is more based on qb-target's style of target creation, and translates those into ox or qb-target code --
-local targetEntities = {}
+-- Tables for storing created targets for the fallback system and zone management.
+local TextTargets = {} -- For fallback DrawText3D targets.
+local targetEntities = {} -- For entity targets.
+local boxTargets = {} -- For box-shaped zone targets.
+local circleTargets = {} -- For circular zone targets.
+
+-------------------------------------------------------------
+-- Entity Target Creation
+-------------------------------------------------------------
--- Creates a target for an entity with specified options and interaction distance.
+--- Supports different targeting systems (OX Target, QB Target, or custom DrawText3D)
+--- based on the server configuration.
---
---- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
---- based on the server configuration. It translates qb-target style options into the appropriate format
---- for the detected targeting system.
+--- @param entity number The entity ID for which the target is created.
+--- @param opts table Array of option tables. Each option should include:
+--- - icon (string): The icon to display.
+--- - label (string): The text label for the option.
+--- - item (string|nil): (Optional) An associated item.
+--- - job (string|nil): (Optional) The job required to interact.
+--- - gang (string|nil): (Optional) The gang required to interact.
+--- - action (function|nil): (Optional) The function executed on selection.
+--- @param dist number The interaction distance for the target.
---
----@param entity number The entity ID to create a target for.
----@param opts table A table of option configurations for the target.
---- - **icon** (`string`): The icon to display for the option.
---- - **label** (`string`): The label text for the option.
---- - **item** (`string|nil`): (Optional) The item associated with the option.
---- - **job** (`string|nil`): (Optional) The job required to interact with the option.
---- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
---- - **action** (`function|nil`): (Optional) The function to execute when the option is selected.
----@param dist number The interaction distance for the target.
----
----@usage
+--- @usage
--- ```lua
---- createEntityTarget(entityId, {
---- { icon = "fas fa-car", label = "Open Vehicle", action = openVehicle },
---- { icon = "fas fa-lock", label = "Lock Vehicle", action = lockVehicle }
---- }, 2.5)
+---createEntityTarget(entityId, {
+--- {
+--- action = function()
+--- openStorage()
+--- end,
+--- icon = "fas fa-box",
+--- job = "police",
+--- label = "Open Storage",
+--- },
+---}, 2.0)
--- ```
function createEntityTarget(entity, opts, dist)
+ -- Store the target entity for later cleanup.
targetEntities[#targetEntities + 1] = entity
local entityCoords = GetEntityCoords(entity)
+
+ -- Fallback: Use DrawText3D if targeting systems are disabled or unavailable.
if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with ^6DrawText ^7"..entity)
+ debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with DrawText for entity ^7"..entity)
local existingTarget = nil
- for key, target in pairs(TextTargets) do
- if #(target.coords - entityCoords) < 0.01 then -- Adjust the threshold for coordinate matching
+ -- Check if a target already exists at similar coordinates.
+ for _, target in pairs(TextTargets) do
+ if #(target.coords - entityCoords) < 0.01 then
existingTarget = target
break
end
end
+ local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Predefined key codes for options.
if existingTarget then
- -- Combine options
- local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Extend key table as needed
+ -- Append new options to the existing target.
for i = 1, #opts do
local key = keyTable[#existingTarget.options + i]
opts[i].key = key
@@ -65,9 +101,8 @@ function createEntityTarget(entity, opts, dist)
existingTarget.options[#existingTarget.options + 1] = opts[i]
end
else
- -- Create new target
+ -- Create a new target entry.
local tempText = {}
- local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
for i = 1, #opts do
opts[i].key = keyTable[i]
tempText[#tempText + 1] = " ~b~[~w~"..Keys[opts[i].key].."~b~] ~w~"..opts[i].label
@@ -75,7 +110,7 @@ function createEntityTarget(entity, opts, dist)
TextTargets[entity] = { coords = vec3(entityCoords.x, entityCoords.y, entityCoords.z), buttontext = tempText, options = opts, dist = dist }
end
elseif isStarted(OXTargetExport) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with ^6"..OXTargetExport.." ^7"..entity)
+ debugPrint("^6Bridge^7: ^2Creating new ^3Entity ^2target with ^6"..OXTargetExport.." ^2for entity ^7"..entity)
local options = {}
for i = 1, #opts do
options[i] = {
@@ -91,84 +126,95 @@ function createEntityTarget(entity, opts, dist)
end
exports[OXTargetExport]:addLocalEntity(entity, options)
elseif isStarted(QBTargetExport) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Entity^2 target with ^6"..QBTargetExport.." ^7"..entity)
+ debugPrint("^6Bridge^7: ^2Creating new ^3Entity ^2target with ^6"..QBTargetExport.." ^2for entity ^7"..entity)
local options = { options = opts, distance = dist }
exports[QBTargetExport]:AddTargetEntity(entity, options)
end
end
-local boxTargets = {}
+-------------------------------------------------------------
+-- Box Zone Target Creation
+-------------------------------------------------------------
--- Creates a box-shaped target zone with specified options and interaction distance.
----
---- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
---- based on the server configuration. It translates qb-target style options into the appropriate format
---- for the detected targeting system.
----
+--- Supports different targeting systems based on the server configuration.
---@param data table A table containing the box zone configuration.
---- - **name** (`string`): The name identifier for the zone.
---- - **coords** (`vector3`): The center coordinates of the box.
---- - **width** (`number`): The width of the box.
---- - **height** (`number`): The height of the box.
---- - **options** (`table`): A table with additional options:
---- - **heading** (`number`): The rotation angle of the box.
---- - **debugPoly** (`boolean`): Whether to enable debug mode for the zone.
+--- - name (`string`): The name identifier for the zone.
+--- - coords (`vector3`): The center coordinates of the box.
+--- - width (`number`): The width of the box.
+--- - height (`number`): The height of the box.
+--- - options (`table`): A table with additional options:
+--- - heading (`number`): The rotation angle of the box.
+--- - debugPoly (`boolean`): Whether to enable debug mode for the zone.
---
---@param opts table A table of option configurations for the target.
---- - **icon** (`string`): The icon to display for the option.
---- - **label** (`string`): The label text for the option.
---- - **item** (`string|nil`): (Optional) The item associated with the option.
---- - **job** (`string|nil`): (Optional) The job required to interact with the option.
---- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
---- - **onSelect** (`function|nil`): (Optional) The function to execute when the option is selected.
+--- - icon (`string`): The icon to display for the option.
+--- - label (`string`): The label text for the option.
+--- - item (`string|nil`): (Optional) The item associated with the option.
+--- - job (`string|nil`): (Optional) The job required to interact with the option.
+--- - gang (`string|nil`): (Optional) The gang required to interact with the option.
+--- - onSelect (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target.
---
---@return string|table name identifier or target object of the created zone.
---
---@usage
---- ```lua
---- createBoxTarget({
---- name = 'storageBox',
---- coords = vector3(100.0, 200.0, 30.0),
---- width = 2.0,
---- height = 2.0,
---- options = { heading = 0, debugPoly = false }
---- }, {
---- { icon = "fas fa-box", label = "Open Storage", action = openStorage }
---- }, 1.5)
---- ```
+---```lua
+---createBoxTarget(
+--- {
+--- 'storageBox',
+--- vector3(100.0, 200.0, 30.0),
+--- 2.0,
+--- 2.0,
+--- {
+--- name = 'storageBox',
+--- heading = 100.0,
+--- debugPoly = true,
+--- minZ = 27.0
+--- maxZ = 32.0,
+--- },
+--- },
+---{
+--- {
+--- action = function()
+--- openStorage()
+--- end,
+--- icon = "fas fa-box",
+--- job = "police",
+--- label = "Open Storage",
+--- },
+---}, 2.0)
+---```
function createBoxTarget(data, opts, dist)
if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6DrawText ^7"..data[1])
+ debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6DrawText ^2 for zone ^7"..data[1])
local existingTarget = nil
- for key, target in pairs(TextTargets) do
- if #(target.coords - data[2]) < 0.01 then -- Adjust the threshold as needed for coordinate precision
+ for _, target in pairs(TextTargets) do
+ if #(target.coords - data[2]) < 0.01 then
existingTarget = target
break
end
end
- local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
+ local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
if existingTarget then
- -- Combine options
for i = 1, #opts do
local key = keyTable[#existingTarget.options + i]
opts[i].key = key
- existingTarget.buttontext[#existingTarget.buttontext+1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
- existingTarget.options[#existingTarget.options+1] = opts[i]
+ existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
+ existingTarget.options[#existingTarget.options + 1] = opts[i]
end
else
- -- Create new target
local tempText = {}
for i = 1, #opts do
opts[i].key = keyTable[i]
- tempText[#tempText+1] = " ~b~[~w~"..Keys[opts[i].key].."~b~] ~w~"..opts[i].label
+ 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 = 1.5 }
+ TextTargets[data[1]] = { coords = data[2], buttontext = tempText, options = opts, dist = dist }
end
return data[1]
elseif isStarted(OXTargetExport) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6"..OXTargetExport.." ^7"..data[1])
+ debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6"..OXTargetExport.." ^2for zone ^7"..data[1])
local options = {}
for i = 1, #opts do
options[i] = {
@@ -193,39 +239,38 @@ function createBoxTarget(data, opts, dist)
debug = data[5].debugPoly,
options = options
})
- boxTargets[#boxTargets+1] = target
+ boxTargets[#boxTargets + 1] = target
return target
elseif isStarted(QBTargetExport) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6"..QBTargetExport.." ^7"..data[1])
+ debugPrint("^6Bridge^7: ^2Creating new ^3Box^2 target with ^6"..QBTargetExport.." ^2for zone ^7"..data[1])
local options = { options = opts, distance = dist }
local target = exports[QBTargetExport]:AddBoxZone(data[1], data[2], data[3], data[4], data[5], options)
- boxTargets[#boxTargets+1] = target
+ boxTargets[#boxTargets + 1] = target
return data[1]
end
end
-local circleTargets = {}
+-------------------------------------------------------------
+-- Circle Zone Target Creation
+-------------------------------------------------------------
--- Creates a circular target zone with specified options and interaction distance.
----
---- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
---- based on the server configuration. It translates qb-target style options into the appropriate format
---- for the detected targeting system.
+--- Supports different targeting systems based on server configuration.
---
---@param data table A table containing the circle zone configuration.
---- - **name** (`string`): The name identifier for the zone.
---- - **coords** (`vector3`): The center coordinates of the circle.
---- - **radius** (`number`): The radius of the circle.
---- - **options** (`table`): A table with additional options:
---- - **debugPoly** (`boolean`): Whether to enable debug mode for the zone.
+--- - name (`string`): The name identifier for the zone.
+--- - coords (`vector3`): The center coordinates of the circle.
+--- - radius (`number`): The radius of the circle.
+--- - options (`table`): A table with additional options:
+--- - debugPoly (`boolean`): Whether to enable debug mode for the zone.
---
---@param opts table A table of option configurations for the target.
---- - **icon** (`string`): The icon to display for the option.
---- - **label** (`string`): The label text for the option.
---- - **item** (`string|nil`): (Optional) The item associated with the option.
---- - **job** (`string|nil`): (Optional) The job required to interact with the option.
---- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
---- - **onSelect** (`function|nil`): (Optional) The function to execute when the option is selected.
+--- - icon (`string`): The icon to display for the option.
+--- - label (`string`): The label text for the option.
+--- - item (`string|nil`): (Optional) The item associated with the option.
+--- - job (`string|nil`): (Optional) The job required to interact with the option.
+--- - gang (`string|nil`): (Optional) The gang required to interact with the option.
+--- - onSelect (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target.
---
---@return string|table name identifier or target object of the created zone.
@@ -243,37 +288,34 @@ local circleTargets = {}
--- ```
function createCircleTarget(data, opts, dist)
if Config.System.DontUseTarget then
- debugPrint("^6Bridge^7: ^2Creating new ^3Circle^2 target with ^6DrawText ^7"..data[1])
+ debugPrint("^6Bridge^7: ^2Creating new ^3Circle ^2target with ^6DrawText ^2for zone ^7"..data[1])
local existingTarget = nil
- for key, target in pairs(TextTargets) do
- if #(target.coords - data[2]) < 0.01 then -- Adjust the threshold for precision
+ for _, target in pairs(TextTargets) do
+ if #(target.coords - data[2]) < 0.01 then
existingTarget = target
break
end
end
+ local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
if existingTarget then
- -- Combine options
- local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Extend key table as needed
for i = 1, #opts do
local key = keyTable[#existingTarget.options + i]
opts[i].key = key
- existingTarget.buttontext[#existingTarget.buttontext+1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
- existingTarget.options[#existingTarget.options+1] = opts[i]
+ existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
+ existingTarget.options[#existingTarget.options + 1] = opts[i]
end
else
- -- Create new target
local tempText = {}
- local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
for i = 1, #opts do
opts[i].key = keyTable[i]
- tempText[#tempText+1] = " ~b~[~w~"..Keys[opts[i].key].."~b~] ~w~"..opts[i].label
+ 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 }
end
return data[1]
elseif isStarted(OXTargetExport) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Sphere^2 target with ^6"..OXTargetExport.." ^7"..data[1])
+ debugPrint("^6Bridge^7: ^2Creating new ^3Circle ^2target with ^6"..OXTargetExport.." ^2for zone ^7"..data[1])
local options = {}
for i = 1, #opts do
options[i] = {
@@ -283,7 +325,7 @@ function createCircleTarget(data, opts, dist)
groups = opts[i].job or opts[i].gang,
onSelect = opts[i].onSelect or opts[i].action,
canInteract = function(_, distance)
- return distance < dist and true or false
+ return distance < dist
end
}
end
@@ -293,45 +335,46 @@ function createCircleTarget(data, opts, dist)
debug = data[4].debugPoly,
options = options
})
- circleTargets[#circleTargets+1] = target
+ circleTargets[#circleTargets + 1] = target
return target
elseif isStarted(QBTargetExport) then
- debugPrint("^6Bridge^7: ^2Creating new ^3Circle^2 target with ^6"..QBTargetExport.." ^7"..data[1])
+ debugPrint("^6Bridge^7: ^2Creating new ^3Circle ^2target with ^6"..QBTargetExport.." ^2for zone ^7"..data[1])
local options = { options = opts, distance = dist }
local target = exports[QBTargetExport]:AddCircleZone(data[1], data[2], data[3], data[4], options)
- circleTargets[#circleTargets+1] = target
+ circleTargets[#circleTargets + 1] = target
return data[1]
end
end
-local targetEntities = {}
+-------------------------------------------------------------
+-- Model Target Creation
+-------------------------------------------------------------
---- Creates a target for an entity with specified options and interaction distance.
+--- Creates a target for models with specified options and interaction distance.
+--- Supports different targeting systems (OX Target, QB Target) based on server configuration.
---
---- This function supports different targeting systems (OX Target, QB Target, or custom DrawText3D targets)
---- based on the server configuration. It translates qb-target style options into the appropriate format
---- for the detected targeting system.
+--- @param models table Array of model identifiers.
+--- @param opts table Array of option tables (same structure as in createEntityTarget).
+--- @param dist number The interaction distance for the target.
---
----@param entity number The entity ID to create a target for.
----@param opts table A table of option configurations for the target.
---- - **icon** (`string`): The icon to display for the option.
---- - **label** (`string`): The label text for the option.
---- - **item** (`string|nil`): (Optional) The item associated with the option.
---- - **job** (`string|nil`): (Optional) The job required to interact with the option.
---- - **gang** (`string|nil`): (Optional) The gang required to interact with the option.
---- - **action** (`function|nil`): (Optional) The function to execute when the option is selected.
----@param dist number The interaction distance for the target.
----
----@usage
+--- @usage
--- ```lua
---- createEntityTarget(entityId, {
---- { icon = "fas fa-car", label = "Open Vehicle", action = openVehicle },
---- { icon = "fas fa-lock", label = "Lock Vehicle", action = lockVehicle }
---- }, 2.5)
---- ```
+---createModelTarget(
+---{ model1, model2 },
+---{
+--- {
+--- action = function()
+--- openStorage()
+--- end,
+--- icon = "fas fa-box",
+--- job = "police",
+--- label = "Open Storage",
+--- },
+---}, 2.0)
+---```
function createModelTarget(models, opts, dist)
if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then
- --
+ -- Fallback for model targets is not implemented.
elseif isStarted(OXTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Model^2 target with ^6"..OXTargetExport)
local options = {}
@@ -355,28 +398,32 @@ function createModelTarget(models, opts, dist)
end
end
+-------------------------------------------------------------
+-- Target Removal Functions
+-------------------------------------------------------------
-
--- Simple function to remove an entity target created within the script --
--- Removes a previously created entity target.
---
---- This function removes the target associated with the specified entity based on the active targeting system.
----
--- @param entity number The entity ID whose target should be removed.
---
--- @usage
+--- ```lua
--- removeEntityTarget(entityId)
+--- ```
function removeEntityTarget(entity)
- if isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveTargetEntity(entity) end
- if isStarted(OXTargetExport) then exports[OXTargetExport]:removeLocalEntity(entity, nil) end
- if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport))) then TextTargets[entity] = nil end
+ if isStarted(QBTargetExport) then
+ exports[QBTargetExport]:RemoveTargetEntity(entity)
+ end
+ if isStarted(OXTargetExport) then
+ exports[OXTargetExport]:removeLocalEntity(entity, nil)
+ end
+ if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then
+ TextTargets[entity] = nil
+ end
end
--- Simple function to remove circle or box targets in the script --
--- Removes a previously created zone target.
---
---- This function removes the target associated with the specified zone based on the active targeting system.
----
--- @param target string|table The name identifier or target object of the zone to remove.
---
--- @usage
@@ -385,54 +432,60 @@ end
--- removeZoneTarget(targetObject)
--- ```
function removeZoneTarget(target)
- if isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(target) end
- if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(target, true) end
- if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport))) then TextTargets[target] = nil end
+ if isStarted(QBTargetExport) then
+ exports[QBTargetExport]:RemoveZone(target)
+ end
+ if isStarted(OXTargetExport) then
+ exports[OXTargetExport]:removeZone(target, true)
+ end
+ if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then
+ TextTargets[target] = nil
+ end
end
--- If no target script is found, default to DrawText3D targets -- * experimental *
+-------------------------------------------------------------
+-- Fallback: DrawText3D Targets (Experimental)
+-------------------------------------------------------------
+
+-- If no targeting system is detected and this is a client script, use DrawText3D for targets.
if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport))) and not isServer() then
CreateThread(function()
while true do
local pedCoords = GetEntityCoords(PlayerPedId())
local camCoords = GetGameplayCamCoord()
- local camRotation = GetGameplayCamRot(2) -- Get camera rotation in degrees
- local camForwardVector = RotationToDirection(camRotation) -- Convert rotation to direction vector
+ local camRotation = GetGameplayCamRot(2) -- Camera rotation (degrees)
+ local camForwardVector = RotationToDirection(camRotation) -- Convert rotation to direction
local closestTarget = nil
local closestDist = math.huge
- for k, v in pairs(TextTargets) do
- local targetCoords = v.coords
- local dist = #(pedCoords - targetCoords)
- local vecToTarget = targetCoords - camCoords
-
- -- Normalize the vector to the target
+ -- Identify the closest target in front of the camera.
+ for _, target in pairs(TextTargets) do
+ local dist = #(pedCoords - target.coords)
+ local vecToTarget = target.coords - camCoords
local vecToTargetNormalized = normalizeVector(vecToTarget)
-
- -- Dot product to check if facing the target
local dot = camForwardVector.x * vecToTargetNormalized.x + camForwardVector.y * vecToTargetNormalized.y + camForwardVector.z * vecToTargetNormalized.z
+ local isFacingTarget = dot > 0.5 -- Threshold for facing target.
- local isFacingTarget = dot > 0.5 -- Adjust threshold as needed
-
- if dist <= v.dist and isFacingTarget then
+ if dist <= target.dist and isFacingTarget then
if dist < closestDist then
closestDist = dist
- closestTarget = v
+ closestTarget = target
end
end
end
- for k, v in pairs(TextTargets) do
- local isClosest = (v == closestTarget)
- if #(pedCoords - v.coords) <= v.dist then
- for i = 1, #v.options do
- if IsControlJustPressed(0, v.options[i].key) and isClosest then
- if v.options[i].onSelect then v.options[i].onSelect() end
- if v.options[i].action then v.options[i].action() end
+ -- Render the DrawText3D targets and listen for key presses.
+ for _, target in pairs(TextTargets) do
+ local isClosest = (target == closestTarget)
+ if #(pedCoords - target.coords) <= target.dist then
+ for i = 1, #target.options do
+ if IsControlJustPressed(0, target.options[i].key) and isClosest then
+ if target.options[i].onSelect then target.options[i].onSelect() end
+ if target.options[i].action then target.options[i].action() end
end
end
- DrawText3D(vec3(v.coords.x, v.coords.y, v.coords.z + 0.7), concatenateText(v.buttontext), isClosest)
+ DrawText3D(vec3(target.coords.x, target.coords.y, target.coords.z + 0.7), concatenateText(target.buttontext), isClosest)
end
end
Wait(0)
@@ -440,18 +493,34 @@ if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStar
end)
end
--- If the current loaded script is stopped, automatically remove targets --
+-------------------------------------------------------------
+-- Cleanup on Resource Stop
+-------------------------------------------------------------
+
+-- When the current resource stops, remove all targets.
onResourceStop(function()
+ -- Remove entity targets.
for i = 1, #targetEntities do
- if isStarted(OXTargetExport) then exports[OXTargetExport]:removeLocalEntity(targetEntities[i], nil)
- elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveTargetEntity(targetEntities[i]) end
+ if isStarted(OXTargetExport) then
+ exports[OXTargetExport]:removeLocalEntity(targetEntities[i], nil)
+ elseif isStarted(QBTargetExport) then
+ exports[QBTargetExport]:RemoveTargetEntity(targetEntities[i])
+ end
end
+ -- Remove box zone targets.
for i = 1, #boxTargets do
- if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(boxTargets[i], true)
- elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(boxTargets[i].name) end
+ if isStarted(OXTargetExport) then
+ exports[OXTargetExport]:removeZone(boxTargets[i], true)
+ elseif isStarted(QBTargetExport) then
+ exports[QBTargetExport]:RemoveZone(boxTargets[i].name)
+ end
end
+ -- Remove circle zone targets.
for i = 1, #circleTargets do
- if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(circleTargets[i], true)
- elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(circleTargets[i].name) end
+ if isStarted(OXTargetExport) then
+ exports[OXTargetExport]:removeZone(circleTargets[i], true)
+ elseif isStarted(QBTargetExport) then
+ exports[QBTargetExport]:RemoveZone(circleTargets[i].name)
+ end
end
end, true)
\ No newline at end of file
diff --git a/shared/vehicles.lua b/shared/vehicles.lua
index dacc044..49c068e 100644
--- a/shared/vehicles.lua
+++ b/shared/vehicles.lua
@@ -1,22 +1,29 @@
--- Get Vehicle Info --
-local lastCar = nil
-local carInfo = {}
+--[[
+ Vehicle Info & Properties Module
+ ----------------------------------
+ This module provides utilities for:
+ - Retrieving vehicle information from a Vehicles table.
+ - Getting and setting vehicle properties using the active framework.
+ - Comparing vehicle property differences.
+ - Synchronizing vehicle properties across clients.
+ - Managing network control of vehicles.
+ - Finding the closest vehicle to a given position.
+]]
---- Searches the 'Vehicles' table for a specific vehicle's 'name', 'price', and 'class'.
+-- Cached vehicle info to avoid unnecessary re-searches.
+local lastCar, carInfo = nil, {}
+
+--- Searches the 'Vehicles' table for a specific vehicle's details.
+--- If the vehicle differs from the last searched, it retrieves its model and updates the carInfo table.
+--- The table includes the vehicle's name, price, and class information.
---
---- This function checks if the provided vehicle is different from the last searched vehicle.
---- If it's a new vehicle, it retrieves its model and searches the 'Vehicles' table for matching entries.
---- It populates the `carInfo` table with the vehicle's name, price, and class.
---- If the vehicle is not found in the table, it defaults to using the vehicle's display name and sets the price to 0.
+--- @param vehicle number The entity ID of the vehicle to search for.
+--- @return table|nil table A table containing the vehicle's details or nil if the vehicle is invalid.
---
----@param vehicle number The entity ID of the vehicle to search for.
----
----@return table|nil table containing the vehicle's `name`, `price`, and `class`, or `nil` if the vehicle is invalid.
----
----@usage
+--- @usage
--- ```lua
--- local info = searchCar(vehicleEntity)
---- print(info.name, info.price, info.class)
+--- print(info.name, info.price, info.class.name, info.class.index)
--- ```
function searchCar(vehicle)
if lastCar ~= vehicle then -- If same car, use previous info
@@ -78,27 +85,26 @@ function searchCar(vehicle)
end
end
--- Vehicle Properties --
+-------------------------------------------------------------
+-- Vehicle Properties Functions
+-------------------------------------------------------------
---- Retrieves the properties of a given vehicle.
----
---- This function fetches the vehicle's properties based on the active framework (QBCore or ox).
---- It utilizes the framework's native functions or events to obtain the vehicle's mod list and other details.
+--- Retrieves the properties of a given vehicle using the active framework.
---
--- @param vehicle number The entity ID of the vehicle.
----
---- @return table|nil table containing the vehicle's properties, or `nil` if the vehicle is invalid or the framework is not detected.
+--- @return table|nil table A table containing the vehicle's properties or nil if invalid.
---
--- @usage
--- ```lua
--- local props = getVehicleProperties(vehicleEntity)
--- if props then
---- -- Manipulate vehicle properties
+--- -- Use vehicle properties
--- end
--- ```
function getVehicleProperties(vehicle)
+ if not vehicle then return nil end
+
local properties = {}
- if vehicle == nil then return nil end
if isStarted(QBExport) and not isStarted(QBXExport) then
properties = Core.Functions.GetVehicleProperties(vehicle)
debugPrint("^6Bridge^7: ^2Getting Vehicle Properties ^7[^6"..QBExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..properties.model.."^7] - [^3"..properties.plate.."^7]")
@@ -109,25 +115,22 @@ function getVehicleProperties(vehicle)
return properties
end
---- Sets the properties of a given vehicle.
+--- Sets the properties of a given vehicle if changes are detected.
+--- It compares the current properties with the new ones and applies the update using the active framework.
---
---- This function applies the provided properties to the vehicle using the active framework's functions or events.
---- It first retrieves the current properties and checks for differences before applying the new ones.
+--- @param vehicle number The entity ID of the vehicle.
+--- @param props table The new properties to apply.
---
----@param vehicle number The entity ID of the vehicle.
----@param props table The properties to set on the vehicle.
----
----@usage
+--- @usage
--- ```lua
--- setVehicleProperties(vehicleEntity, newProperties)
--- ```
function setVehicleProperties(vehicle, props)
- local oldProps = getVehicleProperties(vehicle)
if checkDifferences(vehicle, props) then
- --if debugMode then debugDifferences(vehicle, props) end
if not DoesEntityExist(vehicle) then
- print(("Unable to set vehicle properties for '%s' (entity does not exist)"):format(vehicle))
+ print("Unable to set vehicle properties for '"..vehicle.."' (entity does not exist)")
end
+
if isStarted(QBExport) and not isStarted(QBXExport) then
Core.Functions.SetVehicleProperties(vehicle, props)
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..QBExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
@@ -140,16 +143,13 @@ function setVehicleProperties(vehicle, props)
end
--- Checks for differences between the current and new vehicle properties.
+--- Compares properties using JSON encoding for deep comparison and logs differences.
---
---- This function compares each property of the vehicle to determine if any changes have been made.
---- It logs the differences for debugging purposes.
+--- @param vehicle number The entity ID of the vehicle.
+--- @param newProps table The new properties to compare.
+--- @return boolean `true` if differences are found; `false` otherwise.
---
----@param vehicle number The entity ID of the vehicle.
----@param newProps table The new properties to compare against the current ones.
----
----@return boolean `true` if differences are found, `false` otherwise.
----
----@usage
+--- @usage
--- ```lua
--- if checkDifferences(vehicleEntity, newProperties) then
--- setVehicleProperties(vehicleEntity, newProperties)
@@ -158,43 +158,41 @@ end
function checkDifferences(vehicle, newProps)
local oldProps = getVehicleProperties(vehicle)
debugPrint("^6Bridge^7: ^2Finding differences in ^3Vehicle Properties^7")
- local allow = false
+ local differencesFound = false
+
for k in pairs(oldProps) do
if json.encode(oldProps[k]) ~= json.encode(newProps[k]) then
- allow = true
+ differencesFound = true
debugPrint("^6Bridge^7: ^5Old ^7[^3"..k.."^7] - "..json.encode(oldProps[k], { indent = true }))
debugPrint("^6Bridge^7: ^5New ^7[^3"..k.."^7] - "..json.encode(newProps[k], { indent = true }))
end
end
- return allow
+
+ return differencesFound
end
---- Handles setting vehicle properties received from the server.
+-------------------------------------------------------------
+-- Vehicle Properties Synchronization
+-------------------------------------------------------------
+
+--- Event handler for setting vehicle properties received from the server.
+--- Listens for the `ox:setVehicleProperties` event and applies the properties.
---
---- This event listens for the `ox:setVehicleProperties` event and applies the received properties to the vehicle.
----
----@event
----@param netId number The network ID of the vehicle.
----@param props table The properties to set on the vehicle.
----
----@usage
---- -- Server-side: TriggerClientEvent(getScript()..":ox:setVehicleProperties", netId, properties)
+--- @event `getScript()..ox:setVehicleProperties`
+--- @param netId number The network ID of the vehicle.
+--- @param props table The new vehicle properties.
RegisterNetEvent(getScript()..":ox:setVehicleProperties", function(netId, props)
local vehicle = NetworkGetEntityFromNetworkId(netId)
local value = props
Entity(vehicle).state[getScript()..':setVehicleProperties'] = value
end)
---- Handles state bag changes for setting vehicle properties.
+--- Handles state bag changes for updating vehicle properties.
+--- When the state bag changes, the new properties are applied to the vehicle.
---
---- This handler listens for changes to the vehicle's state bag and applies the new properties accordingly.
----
----@param bagName string The name of the state bag.
----@param key string The key that changed.
----@param value table The new value of the state.
----
----@usage
---- -- Automatically handled when the state bag changes
+--- @param bagName string The state bag's name.
+--- @param key string The key that changed.
+--- @param value table The new state value.
AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagName, _, value)
if not value or not GetEntityFromStateBagName then return end
local entity = GetEntityFromStateBagName(bagName)
@@ -208,8 +206,10 @@ AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagN
end
end)
---- Pushes a vehicle to other players by syncing it.
----
+-------------------------------------------------------------
+-- Vehicle Control Functions
+-------------------------------------------------------------
+
--- This function ensures that the vehicle is controlled by the current player and is set as a mission entity.
--- It requests network control and sets the vehicle accordingly to synchronize changes across clients.
---
@@ -222,6 +222,7 @@ end)
function pushVehicle(entity)
SetVehicleModKit(entity, 0)
if entity ~= 0 and DoesEntityExist(entity) then
+ -- Request network control if not already controlled.
if not NetworkHasControlOfEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.")
NetworkRequestControlOfEntity(entity)
@@ -231,11 +232,13 @@ function pushVehicle(entity)
timeout = timeout - 100
end
if NetworkHasControlOfEntity(entity) then
- debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Network has control of entity^7.")
+ debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Network now has control of the entity^7.")
end
end
+
+ -- Set as mission entity if not already set.
if not IsEntityAMissionEntity(entity) then
- debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' &2entity^7.")
+ debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' ^2entity^7.")
SetEntityAsMissionEntity(entity, true, true)
local timeout = 2000
while timeout > 0 and not IsEntityAMissionEntity(entity) do
@@ -249,41 +252,47 @@ function pushVehicle(entity)
end
end
+--- Finds the closest vehicle to the specified coordinates.
+--- The function uses different APIs based on whether a source is provided.
+---
+--- @param coords table|vector3 (Optional) The reference coordinates. If nil, uses the player's position.
+--- @param src boolean (Optional) If true, uses GetPlayerPed(source) and GetAllVehicles.
+--- @return number|number closestVehicle|closestDistance The closest vehicle entity and its distance.
+---
+--- @usage
+--- ```lua
+--- local closestVeh, distance = getClosestVehicle({ x = 100, y = 200, z = 30 }, true)
+--- ```
function getClosestVehicle(coords, src)
- if src then
- local ped = GetPlayerPed(source)
- local vehicles = GetAllVehicles()
- local closestDistance, closestVehicle = -1, -1
- if coords then coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords end
- if not coords then coords = GetEntityCoords(ped) end
- for i = 1, #vehicles do
- local vehicleCoords = GetEntityCoords(vehicles[i])
- local distance = #(vehicleCoords - coords)
- if closestDistance == -1 or closestDistance > distance then
- closestVehicle = vehicles[i]
- closestDistance = distance
- end
- end
- return closestVehicle, closestDistance
- else
- local ped = PlayerPedId()
- local vehicles = GetGamePool('CVehicle')
- local closestDistance = -1
- local closestVehicle = -1
- if coords then
- coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords
- else
- coords = GetEntityCoords(ped)
- end
- for i = 1, #vehicles, 1 do
- local vehicleCoords = GetEntityCoords(vehicles[i])
- local distance = #(vehicleCoords - coords)
+ local ped, vehicles, closestDistance, closestVehicle
- if closestDistance == -1 or closestDistance > distance then
- closestVehicle = vehicles[i]
- closestDistance = distance
- end
- end
- return closestVehicle, closestDistance
+ if src then
+ ped = GetPlayerPed(src)
+ vehicles = GetAllVehicles()
+ else
+ ped = PlayerPedId()
+ vehicles = GetGamePool('CVehicle')
end
+
+ local closestDistance, closestVehicle = -1, -1
+
+ if coords then
+ if type(coords) == 'table' then
+ coords = vec3(coords.x, coords.y, coords.z)
+ end
+ else
+ coords = GetEntityCoords(ped)
+ end
+
+ for i = 1, #vehicles, 1 do
+ local vehicleCoords = GetEntityCoords(vehicles[i])
+ local distance = #(vehicleCoords - coords)
+
+ if closestDistance == -1 or distance < closestDistance then
+ closestDistance = distance
+ closestVehicle = vehicles[i]
+ end
+ end
+
+ return closestVehicle, closestDistance
end
\ No newline at end of file
diff --git a/shared/wrapperfunctions.lua b/shared/wrapperfunctions.lua
index 3794291..8eef846 100644
--- a/shared/wrapperfunctions.lua
+++ b/shared/wrapperfunctions.lua
@@ -1,166 +1,21 @@
--- Phone Mails
-
---- Sends a phone mail using the detected phone system.
----
---- This function detects the active phone resource (e.g., gksphone, yflip-phone, qb-phone, etc.)
---- and sends a mail using the appropriate method for that phone system.
----
---- @param data table A table containing the mail data.
---- - **subject** (`string`): The subject of the email.
---- - **sender** (`string`): The sender of the email.
---- - **message** (`string`): The body content of the email.
---- - **actions** (`table|nil`): (Optional) Action buttons associated with the email.
----
---- @usage
---- ```lua
---- sendPhoneMail({
---- subject = "Welcome!",
---- sender = "Admin",
---- message = "Thank you for joining our server.",
---- actions = {
---- { label = "Reply", action = replyFunction }
---- }
---- })
---- ```
-function sendPhoneMail(data) local phoneResource = ""
- if isStarted("gksphone") then phoneResource = "gksphone"
- exports["gksphone"]:SendNewMail(data)
-
- elseif isStarted("yflip-phone") then phoneResource = "yflip-phone"
- TriggerServerEvent(getScript()..":yflip:SendMail", data)
-
- elseif isStarted("qs-smartphone") then phoneResource = "qs-smartphone"
- TriggerServerEvent('qs-smartphone:server:sendNewMail', data)
-
- elseif isStarted("qs-smartphone-pro") then phoneResource = "qs-smartphone-pro"
- TriggerServerEvent('phone:sendNewMail', data)
-
- elseif isStarted("roadphone") then phoneResource = "roadphone"
- data.message = data.message:gsub("%
", "\n")
- exports['roadphone']:sendMail(data)
-
- elseif isStarted("lb-phone") then phoneResource = "lb-phone"
- data.message = data.message:gsub("%
", "\n")
- TriggerServerEvent(getScript()..":lbphone:SendMail", data)
-
- elseif isStarted("qb-phone") then phoneResource = "qb-phone"
- TriggerServerEvent('qb-phone:server:sendNewMail', data)
-
- elseif isStarted("jpr-phonesystem") then phoneResource = "jpr-phonesystem"
- TriggerServerEvent(getScript()..":jpr:SendMail", data)
- end
-
- if phoneResource ~= "" then debugPrint("^6Bridge^7[^3"..phoneResource.."^7]: ^2Sending mail to player")
- else print("^6Bridge^7: ^1ERROR ^2Sending mail to player ^7 - ^2No supported phone found") end
-end
-
---- Handles sending mail for lb-phone.
----
---- This event listens for the `lbphone:SendMail` event and sends an email using lb-phone's API.
----
---- @event
---- @param data table The mail data.
---- - **subject** (`string`): The subject of the email.
---- - **message** (`string`): The body content of the email.
---- - **buttons** (`table|nil`): (Optional) Action buttons associated with the email.
----
---- @usage
---- ```
---- -- Server-side:
---- TriggerClientEvent(getScript()..":lbphone:SendMail", data)
---- ```
-RegisterNetEvent(getScript()..":lbphone:SendMail", function(data)
- local src = source
- local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(src)
- local emailAddress = exports["lb-phone"]:GetEmailAddress(phoneNumber)
- if data.actions then data.buttons = data.actions end
- exports["lb-phone"]:SendMail({
- to = emailAddress,
- subject = data.subject,
- message = data.message,
- actions = data.buttons,
- })
-end)
-
---- Handles sending mail for yflip-phone.
----
---- This event listens for the `yflip:SendMail` event and sends an email using yflip-phone's API.
----
---- @event
---- @param data table The mail data.
---- - **subject** (`string`): The subject of the email.
---- - **sender** (`string`): The sender of the email.
---- - **message** (`string`): The body content of the email.
---- - **buttons** (`table|nil`): (Optional) Action buttons associated with the email.
----
---- @usage
---- ```lua
---- -- Server-side:
---- TriggerClientEvent(getScript()..":yflip:SendMail", data)
---- ```
-RegisterNetEvent(getScript()..":yflip:SendMail", function(data)
- local src = source
- exports["yflip-phone"]:SendMail({
- title = data.subject,
- sender = data.sender,
- senderDisplayName = data.sender,
- content = data.message,
- actions = data.buttons,
- }, 'source', src)
-end)
-
---- Handles sending mail for jpr-phonesystem.
----
---- This event listens for the `jpr:SendMail` event and sends an email using jpr-phonesystem's API.
----
---- @event
---- @param data table The mail data.
---- - **subject** (`string`): The subject of the email.
---- - **sender** (`string`): The sender of the email.
---- - **message** (`string`): The body content of the email.
---- - **buttons** (`table|nil`): (Optional) Action buttons associated with the email.
----
---- @return void
----
---- @usage
---- ```lua
---- -- Server-side:
---- TriggerClientEvent(getScript()..":jpr:SendMail", data)
---- ```
-RegisterNetEvent(getScript()..":jpr:SendMail", function(data)
- local src = source
- local Player = Core.Functions.GetPlayer(src)
- TriggerEvent('jpr-phonesystem:server:sendEmail', {
- Assunto = data.subject, -- Subject
- Conteudo = data.message, -- Content
- Enviado = data.sender, -- Submitted by
- Destinatario = Player.PlayerData.citizenid, -- Target
- Event = {}, -- Optional
- })
-end)
-
--- Server-Side Functions for Registering Commands, Stashes, and Shops
--- Registers a command with the active command system.
----
---- This function detects whether the server is using OXLib or qb-core for command registration
---- and registers the command accordingly.
+--- This function supports multiple command systems (OXLib, qb-core, ESX Legacy).
---
--- @param command string The name of the command to register.
--- @param options table A table containing command options.
---- - **help** (`string`): The help description for the command.
---- - **params** (`table`): A table of parameters for the command.
---- - **callback** (`function`): The function to execute when the command is called.
---- - **autocomplete** (`function|nil`): (Optional) A function for autocompletion.
---- - **restrictedGroup** (`string|nil`): (Optional) The user group required to execute the command.
+--- - help (`string`): The help description for the command.
+--- - params (`table`): A table of parameters for the command.
+--- - callback (`function`): The function to execute when the command is called.
+--- - autocomplete (`function|nil`): (Optional) A function for autocompletion.
+--- - restrictedGroup (`string|nil`): (Optional) The user group required to execute the command.
---
--- @usage
---- ````lua
---- -- Server Side:
+--- ```lua
--- registerCommand("greet", {
--- "Greets the player",
--- { name = "name", help = "Name of the player to greet" },
---- function(source, args) print("Hello, " .. args[1] .. "!") end,
+--- function(source, args) print("Hello, "..args[1].."!") end,
--- nil,
--- "admin"
--- })
@@ -170,10 +25,10 @@ function registerCommand(command, options)
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..OXLibExport, command)
lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[4])
elseif isStarted(QBExport) and not isStarted(QBXExport) then
- debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7 "..QBExport, command)
- Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] and options[5] or nil)
+ debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..QBExport, command)
+ Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
elseif isStarted(ESXExport) then
- debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7 ESX Legacy", command)
+ debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7ESX Legacy", command)
ESX.RegisterCommand(command, options[5] or 'admin', function(xPlayer, args, showError)
options[4](xPlayer.source, args, showError)
end, false, { help = options[1] })
@@ -181,19 +36,24 @@ function registerCommand(command, options)
end
--- Registers a stash with the active inventory system.
+--- Supports either OXInv or QSInv.
---
---- This function detects whether the server is using OXInv or QSInv and registers the stash accordingly.
----
---- @param name string The unique identifier for the stash.
---- @param label string The display name for the stash.
---- @param slots number|nil (Optional) The number of slots in the stash. Defaults to 50.
---- @param weight number|nil (Optional) The maximum weight the stash can hold. Defaults to 4,000,000.
---- @param owner string|nil (Optional) The owner identifier for personal stashes.
---- @param coords table|nil (Optional) The coordinates for the stash location.
----
+--- @param name string Unique stash identifier.
+--- @param label string Display name for the stash.
+--- @param slots number|nil (Optional) Number of slots (default 50).
+--- @param weight number|nil (Optional) Maximum weight (default 4000000).
+--- @param owner string|nil (Optional) Owner identifier for personal stashes.
+--- @param coords table|nil (Optional) Coordinates for the stash location.
--- @usage
--- ```lua
---- registerStash("playerStash", "Player Stash", 100, 8000000, "player123", { x = 100.0, y = 200.0, z = 30.0 })
+--- registerStash(
+--- "playerStash",
+--- "Player Stash",
+--- 100,
+--- 8000000,
+--- "player123",
+--- { x = 100.0, y = 200.0, z = 30.0 }
+--- )
--- ```
function registerStash(name, label, slots, weight, owner, coords)
if isStarted(OXInv) then
@@ -201,19 +61,25 @@ function registerStash(name, label, slots, weight, owner, coords)
exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil)
elseif isStarted(QSInv) then
debugPrint("^6Bridge^7: ^2Registering ^3QS ^2Stash^7:", name, label)
- exports[QSInv]:RegisterStash(name, slots or 50, weight or 4000000)
+ exports[QSInv]:RegisterStash(nil, name, slots or 50, weight or 4000000)
+
+ --elseif isStarted(CoreInv) then
+ -- debugPrint("^6Bridge^7: ^2Registering ^3CoreInv ^2Stash^7:", name, label)
+ -- exports[CoreInv]:openHolder(nil, name, 'stash', nil, nil, false, nil)
+
+ elseif isStarted(OrigenInv) then
+ debugPrint("^6Bridge^7: ^2Registering ^3OrigenInv ^2Stash^7:", name, label)
+ exports["origen_inventory"]:registerStash(name, label, slots or 50, weight or 4000000)
end
end
--- Registers a shop with the active inventory system.
+--- Supports either OXInv or QBInv (with QBInvNew flag).
---
---- This function detects whether the server is using OXInv or QBInv and registers the shop accordingly.
----
---- @param name string The unique identifier for the shop.
---- @param label string The display name for the shop.
---- @param items table The list of items available in the shop.
---- @param society string|nil (Optional) The society identifier for shared shops.
----
+--- @param name string Unique shop identifier.
+--- @param label string Display name for the shop.
+--- @param items table List of available shop items.
+--- @param society string|nil (Optional) Society identifier for shared shops.
--- @usage
--- ```lua
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
@@ -221,13 +87,11 @@ end
function registerShop(name, label, items, society)
if isStarted(OXInv) then
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label)
- exports[OXInv]:RegisterShop(
- name, {
- name = label,
- inventory = items,
- society = society,
- }
- )
+ exports[OXInv]:RegisterShop(name, {
+ name = label,
+ inventory = items,
+ society = society,
+ })
elseif isStarted(QBInv) and QBInvNew then
debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label)
exports[QBInv]:CreateShop({
@@ -240,25 +104,22 @@ function registerShop(name, label, items, society)
end
end
--- Server-Side Event Registration
-
if isServer() then
--- Registers an event to create an OX stash from the server.
+ --- When triggered, it calls registerStash with the provided parameters.
---
- --- @event
- --- @param name string The unique identifier for the stash.
- --- @param label string The display name for the stash.
- --- @param slots number|nil (Optional) The number of slots in the stash.
- --- @param weight number|nil (Optional) The maximum weight the stash can hold.
- --- @param owner string|nil (Optional) The owner identifier for personal stashes.
- --- @param coords table|nil (Optional) The coordinates for the stash location.
- ---
+ --- @event server:makeOXStash
+ --- @param name string Unique stash identifier.
+ --- @param label string Display name for the stash.
+ --- @param slots number|nil (Optional) Number of slots.
+ --- @param weight number|nil (Optional) Maximum weight.
+ --- @param owner string|nil (Optional) Owner identifier.
+ --- @param coords table|nil (Optional) Stash coordinates.
--- @usage
--- ```lua
- --- -- Server-side:
--- TriggerEvent(getScript()..":server:makeOXStash", name, label, slots, weight, owner, coords)
--- ```
RegisterNetEvent(getScript()..":server:makeOXStash", function(name, label, slots, weight, owner, coords)
registerStash(name, label, slots, weight, owner, coords)
end)
-end
\ No newline at end of file
+end
diff --git a/starter.lua b/starter.lua
index 996143e..b1d38ed 100644
--- a/starter.lua
+++ b/starter.lua
@@ -39,6 +39,7 @@ for _, v in pairs({ -- This is a specific load order
'duifunctions.lua',
-- Native Scaleforms
+ 'scaleforms/scaleform_basic.lua',
'scaleforms/bigMessageInstance.lua',
'scaleforms/countDownHandler.lua',
'scaleforms/debugScaleform.lua',
@@ -56,11 +57,13 @@ for _, v in pairs({ -- This is a specific load order
'wrapperfunctions.lua',
'polyZone.lua',
+ 'inventories.lua',
'itemcontrol.lua',
'playerfunctions.lua',
'metaHandlers.lua',
'jobfunctions.lua',
- 'banking.lua',
+ 'societybank.lua',
+ 'phones.lua',
-- Interactions
'targets.lua',
@@ -78,7 +81,9 @@ for _, v in pairs({ -- This is a specific load order
'scaleEntity.lua',
'vehicles.lua',
'effects.lua',
- 'versioncheck.lua'
+
+ -- Do version check last
+ '_versioncheck.lua'
}) do
if debugMode then
--print("^5Loading^7: 'jim_bridge/shared/"..v.."' ^2into ^7'"..GetCurrentResourceName().."' ...")