refactor + attempt better support for other inventories

This commit is contained in:
Jim Shield
2025-03-08 13:44:05 +00:00
committed by GitHub
parent 31a7ac2951
commit 6bfb549fd0
31 changed files with 3151 additions and 2536 deletions

View File

@@ -1,81 +1,101 @@
--- Executes a function when the player character is loaded into the game. --[[
--- Player & Resource Event Utility Functions
--- This function sets up event listeners for player loading based on the game framework detected (e.g., QB, ESX, OX). -------------------------------------------
--- This module provides functions to:
--- 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) • 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 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 --- @usage
--- ```lua --- ```lua
--- onPlayerLoaded(function() --- onPlayerLoaded(function()
--- -- Your code here --- print("Player logged in")
--- -- Your initialization code here.
--- end, true) --- end, true)
--- ``` --- ```
function onPlayerLoaded(func, onStart) function onPlayerLoaded(func, onStart)
local onPlayerName = "" local onPlayerFramework = ""
local loaded = false local loaded = false
if onStart then if onStart then
onResourceStart(function() onResourceStart(function()
if not LocalPlayer.state.isLoggedIn then if not waitForLogin() then return end
Wait(3000)
if not LocalPlayer.state.isLoggedIn then -- If the player is not logged in after waiting, skip execution loaded = true
return debugPrint("^6Bridge^7: ^3onResourceStart^7()^2 executed through ^3onPlayerLoaded^7()")
end
end
loaded = true -- Mark as already loaded
debugPrint("^6Bridge^7: ^2Loading ^3onResourceStart^7() ^2through ^3onPlayerLoaded^7()")
Wait(2000) Wait(2000)
func() func()
end, true) end, true)
end end
if not loaded then if not loaded then
local tempFunc = function() local tempFunc = function()
debugPrint("^6Bridge^7: ^2Executing ^3onPlayerLoaded^7()") debugPrint("^6Bridge^7: ^2Executing onPlayerLoaded")
func() func()
end end
if isStarted(QBExport) or isStarted(QBXExport) then onPlayerName = QBExport
if isStarted(QBExport) or isStarted(QBXExport) then
onPlayerFramework = QBExport
AddEventHandler('QBCore:Client:OnPlayerLoaded', tempFunc) AddEventHandler('QBCore:Client:OnPlayerLoaded', tempFunc)
elseif isStarted(ESXExport) then onPlayerName = ESXExport elseif isStarted(ESXExport) then
onPlayerFramework = ESXExport
AddEventHandler('esx:playerLoaded', tempFunc) AddEventHandler('esx:playerLoaded', tempFunc)
elseif isStarted(OXCoreExport) then onPlayerName = OXCoreExport elseif isStarted(OXCoreExport) then
onPlayerFramework = OXCoreExport
AddEventHandler('ox:playerLoaded', tempFunc) AddEventHandler('ox:playerLoaded', tempFunc)
end 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 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 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) function onPlayerUnload(func)
AddEventHandler('QBCore:Client:OnPlayerUnload', function() AddEventHandler('QBCore:Client:OnPlayerUnload', function() func() end)
func() AddEventHandler('ox:playerLogout', function() func() end)
end)
AddEventHandler('ox:playerLogout', function() --AddEventHandler('esx:playerLogout', function() func() end)
func() -- ^ Only server side for now, need a way to send it to client if not already available
end)
end end
-------------------------------------------------------------
-- Resource Start and Stop Events
-------------------------------------------------------------
--- Executes a function when the resource starts. --- Executes a function when the resource starts.
--- --- @param func function The function to execute.
--- This function wraps the `onResourceStart` event, allowing you to execute code when the resource starts. --- @param thisScript boolean (optional) If true, only runs when this resource starts (default true).
---
--- @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`.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- onResourceStart(function() --- onResourceStart(function()
--- -- Your code here --- print("Script ensured")
--- -- Initialization code on resource start.
--- end, true) --- end, true)
--- ``` --- ```
function onResourceStart(func, thisScript) function onResourceStart(func, thisScript)
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStart^2") debugPrint("^6Bridge^7: Registering ^3onResourceStart^7()")
AddEventHandler('onResourceStart', function(resourceName) AddEventHandler('onResourceStart', function(resourceName)
if getScript() == resourceName and (thisScript or true) then if getScript() == resourceName and (thisScript or true) then
func() func()
@@ -84,20 +104,16 @@ function onResourceStart(func, thisScript)
end end
--- Executes a function when the resource stops. --- Executes a function when the resource stops.
--- --- @param func function The function to execute.
--- This function wraps the `onResourceStop` event, allowing you to execute code when the resource stops. --- @param thisScript boolean (optional) If true, only runs when this resource stops (default true).
---
--- @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`.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- onResourceStop(function() --- onResourceStop(function()
--- -- Cleanup code here --- -- Cleanup code here.
--- end, true) --- end, true)
--- ``` --- ```
function onResourceStop(func, thisScript) function onResourceStop(func, thisScript)
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^2") debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^7()")
AddEventHandler('onResourceStop', function(resourceName) AddEventHandler('onResourceStop', function(resourceName)
if getScript() == resourceName and (thisScript or true) then if getScript() == resourceName and (thisScript or true) then
func() func()
@@ -105,17 +121,41 @@ function onResourceStop(func, thisScript)
end) end)
end end
--- Waits until the player is logged in before continuing execution. -------------------------------------------------------------
--- -- Wait for Login
--- This function blocks execution until `LocalPlayer.state.isLoggedIn` is `true`. -------------------------------------------------------------
---
---@usage --- Blocks execution until the player is logged in.
--- ```lua --- @usage
--- waitForLogin() --- waitForLogin()
--- ```
function waitForLogin() function waitForLogin()
while not LocalPlayer.state.isLoggedIn do local timeout = 10000 -- 10 seconds in milliseconds
debugPrint("Waiting") local startTime = GetGameTimer()
Wait(100) 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
end
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

View File

@@ -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. --- Opens a menu using the configured menu system.
--- ---
--- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`. --- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`.
--- ---
---@param Menu table A table containing the menu options to display. ---@param Menu table A table containing the menu options to display.
--- Each menu item can include: --- Each menu item can include:
--- - **header** (`string`): The text to display for the menu item. --- - header (`string`): The text to display for the menu item.
--- - **txt** (`string`, optional): Additional text or description. --- - txt (`string`, optional): Additional text or description.
--- - **icon** (`string`, optional): Icon to display with the menu item. --- - icon (`string`, optional): Icon to display with the menu item.
--- - **onSelect** (`function`, optional): Function to execute when the menu item is selected. --- - 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). --- - 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. --- - params (`table`, optional): Additional parameters, such as events and arguments.
--- - **isMenuHeader** (`boolean`, optional): Marks the item as a header. --- - isMenuHeader (`boolean`, optional): Marks the item as a header.
--- - **disabled** (`boolean`, optional): Disables the menu item if `true`. --- - disabled (`boolean`, optional): Disables the menu item if `true`.
--- ---
---@param data table A table containing configuration data for the menu. ---@param data table A table containing configuration data for the menu.
--- - **header** (`string`): The header/title of the menu. --- - header (`string`): The header/title of the menu.
--- - **headertxt** (`string`, optional): Additional header text. --- - headertxt (`string`, optional): Additional header text.
--- - **onBack** (`function`, optional): Function to call when the "Return" option is selected. --- - onBack (`function`, optional): Function to call when the "Return" option is selected.
--- - **onExit** (`function`, optional): Function to call when the menu is exited. --- - 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). --- - 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. --- - canClose (`boolean`, optional): Whether the menu can be closed by the user.
--- ---
---@usage ---@usage
--- ```lua --- ```lua
@@ -36,6 +48,7 @@
--- ``` --- ```
function openMenu(Menu, data) function openMenu(Menu, data)
if Config.System.Menu == "jim" then if Config.System.Menu == "jim" then
-- Insert "Return" option if onBack is defined.
if data.onBack then if data.onBack then
table.insert(Menu, 1, { table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left", icon = "fas fa-circle-arrow-left",
@@ -65,6 +78,7 @@ function openMenu(Menu, data)
if data.onSelected and Menu[k].arrow then if data.onSelected and Menu[k].arrow then
Menu[k].icon = "fas fa-angle-right" Menu[k].icon = "fas fa-angle-right"
end end
-- If no title, use header or txt as title/label.
if not Menu[k].title then if not Menu[k].title then
if Menu[k].header ~= nil and Menu[k].header ~= "" then if Menu[k].header ~= nil and Menu[k].header ~= "" then
Menu[k].title = Menu[k].header Menu[k].title = Menu[k].header
@@ -75,6 +89,7 @@ function openMenu(Menu, data)
Menu[k].label = Menu[k].txt Menu[k].label = Menu[k].txt
end end
end end
-- Copy parameters from 'params' if available.
if Menu[k].params then if Menu[k].params then
Menu[k].event = Menu[k].params.event Menu[k].event = Menu[k].params.event
Menu[k].args = Menu[k].params.args or {} Menu[k].args = Menu[k].params.args or {}
@@ -143,17 +158,10 @@ function openMenu(Menu, data)
end end
for k in pairs(Menu) do for k in pairs(Menu) do
if not Menu[k].params or not Menu[k].params.event then if not Menu[k].params or not Menu[k].params.event then
if Menu[k].onSelect then Menu[k].params = {
Menu[k].params = { isAction = true,
isAction = true, event = Menu[k].onSelect or function() end,
event = Menu[k].onSelect, }
}
else
Menu[k].params = {
isAction = true,
event = function() end,
}
end
end end
if not Menu[k].header then Menu[k].header = " " end if not Menu[k].header then Menu[k].header = " " end
if Menu[k].arrow then Menu[k].icon = "fas fa-angle-right" 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) exports[QBMenuExport]:openMenu(Menu)
elseif Config.System.Menu == "gta" then elseif Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(Menu), WarMenu.CreateMenu(tostring(Menu), data.header, data.headertxt or " ", {
data.header, titleColor = { 222, 255, 255 },
data.headertxt or " ", maxOptionCountOnScreen = 15,
{ width = 0.25,
titleColor = { 222, 255, 255 }, x = 0.7,
maxOptionCountOnScreen = 15, })
width = 0.25,
x = 0.7,
})
if WarMenu.IsAnyMenuOpened() then return end if WarMenu.IsAnyMenuOpened() then return end
WarMenu.OpenMenu(tostring(Menu)) WarMenu.OpenMenu(tostring(Menu))
CreateThread(function() CreateThread(function()
@@ -239,7 +244,6 @@ function openMenu(Menu, data)
onSelect = data.onBack, onSelect = data.onBack,
}) })
end end
ESX.UI.Menu.Open("default", getScript(), "Example_Menu", { ESX.UI.Menu.Open("default", getScript(), "Example_Menu", {
title = data.header, title = data.header,
align = 'top-right', align = 'top-right',
@@ -260,15 +264,11 @@ function openMenu(Menu, data)
end end
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 "<br>" br = (Config.System.Menu == "ox" or Config.System.Menu == "gta") and "\n" or "<br>"
--- Checks if the menu system is classified as 'ox' or 'gta'. --- 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.
--- 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`.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- if isOx() then --- 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. --- 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 --- @usage
--- ```lua --- ```lua

View File

@@ -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 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 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 =
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 "" 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 "" QBMenuExport = Exports.QBMenuExport or ""
-- Target exports based on what is loaded --
QBTargetExport, OXTargetExport = Exports.QBTargetExport or "", Exports.OXTargetExport or "" 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 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 end
-------------------------------------------------------------
-- Resource Variables for Items, Jobs, and Vehicles
-------------------------------------------------------------
local itemResource, jobResource, vehResource = "", "", "" 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 -- -- Loading Items
-- For example this makes it so instead of QBCore.Shared.Items[item], you can load 'Item[item]' in the script -- -------------------------------------------------------------
-- Load and compile shared items from the detected inventory system.
if isStarted(OXInv) then if isStarted(OXInv) then
itemResource = OXInv itemResource = OXInv
Items = exports[OXInv]:Items() Items = exports[OXInv]:Items()
@@ -54,14 +85,13 @@ elseif isStarted(QBExport) then
elseif isStarted(ESXExport) then elseif isStarted(ESXExport) then
itemResource = ESXExport itemResource = ESXExport
ESX = exports[ESXExport]:getSharedObject() ESX = exports[ESXExport]:getSharedObject()
--Items = ESX and ESX.Items or nil
while ESX == nil do while ESX == nil do
print("Waiting for ESX") print("Waiting for ESX")
Wait(0) Wait(0)
end end
if isServer() then if isServer() then
Items = ESX.GetItems() 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 end
CreateThread(function() CreateThread(function()
while not ESX do Wait(0) end while not ESX do Wait(0) end
@@ -72,25 +102,23 @@ elseif isStarted(ESXExport) then
end end
if not isServer() then if not isServer() then
Items = triggerCallback(getScript()..":getItems") 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) 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 isStarted(ESXExport) then
if not Items 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 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
end end
-- Load Vehicles -- -------------------------------------------------------------
-- Complies the vehicles from the detected frameworks into a table in the style of qb-cores shared vehicles.lua -- -- Loading Vehicles
-- For example, instead of using QBCore.Shared.Vehicles[vehicle] you can load 'Vehicles[vehicle]' in the script -- -------------------------------------------------------------
-- Compile vehicles from the detected frameworks into a unified table.
if isStarted(QBXExport) or isStarted(QBExport) then if isStarted(QBXExport) or isStarted(QBExport) then
Core = Core or exports[QBExport]:GetCoreObject() Core = Core or exports[QBExport]:GetCoreObject()
Vehicles = Core and Core.Shared.Vehicles Vehicles = Core and Core.Shared.Vehicles
@@ -101,15 +129,15 @@ if isStarted(QBXExport) or isStarted(QBExport) then
end) end)
end end
vehResource = QBExport vehResource = QBExport
elseif isStarted(OXCoreExport) then elseif isStarted(OXCoreExport) then
Vehicles = {} Vehicles = {}
for k, v in pairs(Ox.GetVehicleData()) do for k, v in pairs(Ox.GetVehicleData()) do
Vehicles[k] = { model = k, price = v.price, name = v.name, brand = v.make } Vehicles[k] = { model = k, price = v.price, name = v.name, brand = v.make }
end end
vehResource = OXCoreExport vehResource = OXCoreExport
elseif isStarted(ESXExport) then 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() CreateThread(function()
if isServer() then if isServer() then
createCallback(getScript()..":getVehiclesPrices", function(source) createCallback(getScript()..":getVehiclesPrices", function(source)
@@ -122,43 +150,56 @@ elseif isStarted(ESXExport) then
local TempVehicles = triggerCallback(getScript()..":getVehiclesPrices") local TempVehicles = triggerCallback(getScript()..":getVehiclesPrices")
for _, v in pairs(TempVehicles) do for _, v in pairs(TempVehicles) do
Vehicles = Vehicles or {} 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
end) end)
end end
if vehResource == nil then 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 else
debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Vehicles).." ^3Vehicles^2 from ^7"..vehResource) debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Vehicles).." ^3Vehicles^2 from ^7"..vehResource)
end end
-- Load Jobs -- -------------------------------------------------------------
-- Attempts to load the details of jobs and gangs and compile into tables -- -- Loading Jobs and Gangs
-- For example, instead of using QBCore.Shared.Jobs[job] you can load 'Jobs[job]' in the script -- -------------------------------------------------------------
if isStarted(QBXExport) then jobResource = QBXExport -- Compile jobs and gangs from the detected framework.
if isStarted(QBXExport) then
jobResource = QBXExport
Core = Core or exports[QBExport]:GetCoreObject() Core = Core or exports[QBExport]:GetCoreObject()
Jobs, Gangs = exports[QBXExport]:GetJobs(), exports[QBXExport]:GetGangs() Jobs, Gangs = exports[QBXExport]:GetJobs(), exports[QBXExport]:GetGangs()
elseif isStarted(OXCoreExport) then jobResource = OXExport elseif isStarted(OXCoreExport) then
jobResource = OXExport
CreateThread(function() CreateThread(function()
if isServer() then if isServer() then
createCallback(getScript()..":getOxGroups", function(source) 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) end)
else else
local TempJobs = triggerCallback(getScript()..":getOxGroups") local TempJobs = triggerCallback(getScript()..":getOxGroups")
Jobs = TempJobs or {} Jobs = TempJobs or {}
for k, v in pairs(TempJobs) do for k, v in pairs(TempJobs) do
local grades = {} 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 } Jobs[v.name] = { label = v.label, grades = grades }
end end
Gangs = Jobs Gangs = Jobs
end end
end) end)
elseif isStarted(QBExport) then jobResource = QBExport elseif isStarted(QBExport) then
jobResource = QBExport
Core = Core or exports[QBExport]:GetCoreObject() Core = Core or exports[QBExport]:GetCoreObject()
Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs Jobs, Gangs = Core.Shared.Jobs, Core.Shared.Gangs
if isStarted(QBExport) and not isStarted(QBXExport) then if isStarted(QBExport) and not isStarted(QBXExport) then
@@ -169,12 +210,11 @@ elseif isStarted(QBExport) then jobResource = QBExport
end end
elseif isStarted(ESXExport) then elseif isStarted(ESXExport) then
--print("^6Bridge^7: ^2Loading ^3Jobs^7/^3Gangs^2 from ^7"..ESXExport)
ESX = exports[ESXExport]:getSharedObject() ESX = exports[ESXExport]:getSharedObject()
if isServer() then if isServer() then
Jobs = ESX.GetJobs() Jobs = ESX.GetJobs()
for k, v in pairs(Jobs) do 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 Jobs[k].grades[tostring(count)].isBoss = true
end end
Gangs = Jobs Gangs = Jobs
@@ -192,6 +232,8 @@ elseif isStarted(ESXExport) then
end end
end) end)
end end
if not isStarted(ESXExport) and Jobs then 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) debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Jobs).." ^3Jobs^2 from ^7"..jobResource)
end debugPrint("^6Bridge^7: ^2Loading ^6"..countTable(Gangs).." ^3Gangs^2 from ^7"..jobResource)
end

View File

@@ -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. --- @param data table Crafting menu configuration containing:
--- It handles item availability, crafting recipes, and displays appropriate icons and labels. --- - 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. --- @usage
--- - **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
--- ```lua --- ```lua
--- craftingMenu({ ---craftingMenu({
--- craftable = { --- craftable = {
--- Header = "Weapon Crafting", --- Header = "Weapon Crafting",
--- Recipes = { --- Recipes = {
@@ -34,158 +44,103 @@ local CraftLock = false
--- }, --- },
--- }, --- },
--- coords = vector3(100.0, 200.0, 300.0), --- coords = vector3(100.0, 200.0, 300.0),
--- stashTable = "crafting_stash", ---stashTable = "crafting_stash",
--- job = "mechanic", -- Optional --- job = "mechanic",
--- onBack = function() print("Returning to previous menu") end, --- onBack = function() print("Returning to previous menu") end,
--- }) ---})
--- ```
function craftingMenu(data) function craftingMenu(data)
-- Prevent opening the menu if crafting is locked.
if CraftLock then return end 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 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 if Config.System.Menu == "jim" then
triggerNotify(nil, "Thinking", "info") triggerNotify(nil, "Thinking", "info")
else else
openMenu({ { header = "Thinking...", icon = "fas fa-hourglass-end", isMenuHeader = true } }, { header = "Crafting Menu" } ) openMenu({ { header = "Thinking...", icon = "fas fa-hourglass-end", isMenuHeader = true } }, { header = "Crafting Menu" } )
end end
-- Normalize stash name: if stashTable is provided, assign it to stashName. -- Normalize stash name.
data.stashName = data.stashTable or data.stashName data.stashName = data.stashTable or data.stashName
-- Initialize an empty menu table and a flag for job verification. local Menu = {}
local Menu, hasjob = {}, false
-- Get the list of recipes from the provided data.
local Recipes = data.craftable.Recipes local Recipes = data.craftable.Recipes
local craftedItems = {} local craftedItems = {}
-- Create a temporary table to collect required item amounts for each recipe.
local tempCarryTable = {} local tempCarryTable = {}
-- Build a table of all required ingredients (default quantity is 1).
for i = 1, #Recipes do for i = 1, #Recipes do
-- Iterate over each key in the current recipe.
for k in pairs(Recipes[i]) do for k in pairs(Recipes[i]) do
if k == "hasCrafted" and not data.craftable.craftedItems then if k == "hasCrafted" and not data.craftable.craftedItems then
craftedItems = GetMetadata(nil, "craftedItems") or {} craftedItems = GetMetadata(nil, "craftedItems") or {}
data.craftable.craftedItems = craftedItems data.craftable.craftedItems = craftedItems
end end
-- Ignore meta keys: "amount", "metadata", "job", and "gang".
if k ~= "amount" and k ~= "metadata" and k ~= "job" and k ~= "gang" then 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 tempCarryTable[k] = Recipes[i].amount or 1
end end
end 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) local canCarryTable = triggerCallback(getScript()..':server:canCarry', tempCarryTable)
-- Process each recipe to create menu entries.
-- Process each recipe to build the menu entries.
for i = 1, #Recipes do 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 if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
for k, _ in pairs(Recipes[i]) do
-- Loop through each key-value pair in the recipe.
for k, v in pairs(Recipes[i]) do
-- Skip meta keys that are not ingredients.
local excludeKeys = { local excludeKeys = {
amount = true, amount = true, metadata = true, description = true, info = true,
metadata = true, job = true, gang = true, oneUse = true, slot = true,
description = true, blueprintRef = true, craftingLevel = true, craftedItems = true,
info = true, hasCrafted = true, exp = true, anim = true, time = true,
job = true,
gang = true,
oneUse = true,
slot = true,
blueprintRef = true,
craftingLevel = true,
craftedItems = true,
hasCrafted = true,
exp = true,
} }
if not excludeKeys[k] then if not excludeKeys[k] then
local hasjob = true
-- Check job requirements if specified for the recipe.
if Recipes[i].job then if Recipes[i].job then
for l, b in pairs(Recipes[i].job) do for l, b in pairs(Recipes[i].job) do
-- hasJob returns true if the player meets the job criteria.
hasjob = hasJob(l, nil, b) hasjob = hasJob(l, nil, b)
if hasjob == true then break end if hasjob then break end
end end
else
hasjob = true
end 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 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 itemTable = {}
local metaTable = {} local metaTable = {}
-- Build ingredient details.
-- Iterate over the ingredients for the current key.
for l, b in pairs(Recipes[i][tostring(k)]) do 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 "") 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 metaTable[Items[l] and Items[l].label or "error - "..l] = b
-- Build a simple table of items required.
itemTable[l] = b itemTable[l] = b
Wait(0) -- Yield to avoid freezing the game. Wait(0)
end end
-- Wait until the server callback (canCarryTable) has returned.
while not canCarryTable do Wait(0) end 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) 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 disable then
if not canCarryTable[k] then if not canCarryTable[k] then
setheader = setheader .. " 📦" setheader = setheader.." 📦"
else else
setheader = setheader .. " ✔️" setheader = setheader.." ✔️"
end end
elseif not canCarryTable[k] then elseif not canCarryTable[k] then
setheader = setheader .. " 📦" setheader = setheader.." 📦"
end end
if Recipes[i]["hasCrafted"] ~= nil then if Recipes[i]["hasCrafted"] ~= nil and craftedItems[k] == nil then
if craftedItems[k] == nil then setheader = ""..setheader
setheader = ""..setheader
end
end end
-- Add the constructed menu item into the Menu table.
Menu[#Menu + 1] = { Menu[#Menu + 1] = {
-- Show an arrow if the item is enabled and can be carried.
arrow = not disable and canCarryTable[k], 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], 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)), icon = invImg((metadata and metadata.image) or tostring(k)),
image = 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 ""), 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, txt = (isStarted(QBMenuExport) or disable) and settext or nil,
-- Attach the metadata table containing ingredient details.
metadata = metaTable, metadata = metaTable,
-- Define the onSelect function to trigger crafting actions if the item is selectable. onSelect = (not disable and canCarryTable[k]) and function()
onSelect = ((not disable and canCarryTable[k]) and (function()
-- Build transaction data with details needed for crafting.
local transdata = { local transdata = {
item = k, item = k,
craft = data.craftable.Recipes[i], craft = data.craftable.Recipes[i],
@@ -193,23 +148,21 @@ function craftingMenu(data)
coords = data.coords, coords = data.coords,
stashName = data.stashName, stashName = data.stashName,
onBack = data.onBack, onBack = data.onBack,
metadata = metadata metadata = metadata,
} }
-- Call multiCraft or makeItem based on configuration.
if Config.Crafting.MultiCraft then if Config.Crafting.MultiCraft then
multiCraft(transdata) multiCraft(transdata)
else else
makeItem(transdata) makeItem(transdata)
end end
end) or nil), end or nil,
} }
end end
end end
Wait(0) -- Yield within the loop to maintain responsiveness. Wait(0)
end end
end end
-- Open the final crafting menu with the built Menu table and provided header/onBack configuration.
openMenu(Menu, { openMenu(Menu, {
header = data.craftable.Header, header = data.craftable.Header,
headertxt = data.craftable.Headertxt, headertxt = data.craftable.Headertxt,
@@ -217,32 +170,33 @@ function craftingMenu(data)
canClose = true, canClose = true,
onExit = function() end, onExit = function() end,
}) })
-- Trigger an action (likely camera or player focus) to look at the specified coordinates.
lookEnt(data.coords) lookEnt(data.coords)
end end
-------------------------------------------------------------
-- Multi-Craft Menu
-------------------------------------------------------------
--- Opens a menu for selecting the quantity to craft. --- 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. --- @param data table Crafting configuration containing:
--- - **item** (`string`): The item to craft. --- - item `string`) The item to craft.
--- - **craft** (`table`): The crafting recipe for the item. --- - craft (`table`) The crafting recipe.
--- - **craftable** (`table`): The crafting options and settings. --- - craftable (`table`) Crafting options.
--- - **coords** (`vector3`): The coordinates where the crafting is taking place. --- - coords (`vector3`) where crafting occurs.
--- - **stashName** (`string` or `table`, optional): The stash name(s) to check for item availability. --- - stashName (`string`) The stash name(s) for item availability.
--- - **onBack** (`function`, optional): Function to call when returning from the menu. --- - onBack (`function`) Callback when returning.
--- - **metadata** (`table`, optional): Metadata for the crafted item. --- - metadata (`table`) (optional): Metadata for the crafted item.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- multiCraft({ --- multiCraft({
--- item = "weapon_pistol", --- item = "weapon_pistol",
--- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 }, --- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 },
--- craftable = craftingOptions, --- craftable = craftingOptions,
--- coords = vector3(100.0, 200.0, 300.0), --- coords = vector3(100,200,300),
--- stashName = "crafting_stash", --- stashName = "crafting_stash",
--- onBack = function() craftingMenu(data) end, --- onBack = function() craftingMenu(data) end,
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" }, --- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
@@ -250,28 +204,31 @@ end
--- ``` --- ```
function multiCraft(data) function multiCraft(data)
local Menu = {} local Menu = {}
local success = Config.Crafting.MultiCraftAmounts local amounts = Config.Crafting.MultiCraftAmounts
local metadata = data.metadata or nil local metadata = data.metadata or nil
Menu[#Menu+1] = {
-- Header for the multi-craft menu.
Menu[#Menu + 1] = {
isMenuHeader = true, isMenuHeader = true,
icon = invImg(metadata and metadata.image or data.item), icon = invImg(metadata and metadata.image or data.item),
header = metadata and metadata.label or Items[data.item].label, 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 settext = ""
local itemTable = {} local itemTable = {}
for l, b in pairs(data.craft[data.item]) do for l, b in pairs(data.craft[data.item]) do
itemTable[l] = (b * k) 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) Wait(0)
end end
local disable, stashname = checkHasItem(data.stashName, itemTable) local disable, stashname = checkHasItem(data.stashName, itemTable)
Menu[#Menu + 1] = { Menu[#Menu + 1] = {
isMenuHeader = not disable, isMenuHeader = not disable,
arrow = disable, arrow = disable,
header = "Craft - x"..k * data.craft.amount, header = "Craft - x"..(k * data.craft.amount),
txt = settext, txt = settext,
onSelect = function () onSelect = function()
makeItem({ makeItem({
item = data.item, item = data.item,
craft = data.craft, craft = data.craft,
@@ -281,37 +238,41 @@ function multiCraft(data)
stashName = stashname, stashName = stashname,
stashTable = data.stashName, stashTable = data.stashName,
onBack = data.onBack, onBack = data.onBack,
metadata = data.metadata metadata = data.metadata,
}) })
end, end,
} }
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 end
-------------------------------------------------------------
-- Crafting Process
-------------------------------------------------------------
--- Initiates the crafting process for a specified item. --- 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. --- @param data table Crafting configuration containing:
--- - **item** (`string`): The item to craft. --- - item `string`) The item to craft.
--- - **craft** (`table`): The crafting recipe for the item. --- - craft (`table`) The crafting recipe.
--- - **craftable** (`table`): The crafting options and settings. --- - craftable (`table`) Crafting options.
--- - **amount** (`number`, optional): The quantity to craft. Default is `1`. --- - amount (`number`) (optional): Quantity to craft (default 1).
--- - **coords** (`vector3`): The coordinates where the crafting is taking place. --- - coords (`vector3`) where crafting occurs.
--- - **stashName** (`string` or `table`, optional): The stash name(s) to remove items from. --- - stashName (`string`) The stash name(s) for item availability.
--- - **stashTable** (`string` or `table`, optional): Alias for `stashName`. --- - onBack (`function`) Callback when returning.
--- - **onBack** (`function`, optional): Function to call when returning from the menu. --- - metadata (`table`) (optional): Metadata for the crafted item.
--- - **metadata** (`table`, optional): Metadata for the crafted item.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- makeItem({ --- makeItem({
--- item = "weapon_pistol", --- item = "weapon_pistol",
--- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 }, --- craft = { ["weapon_pistol"] = { ["steel"] = 5, ["plastic"] = 2 }, amount = 1 },
--- craftable = craftingOptions, --- craftable = craftingOptions,
--- amount = 2, --- amount = 2,
--- coords = vector3(100.0, 200.0, 300.0), --- coords = vector3(100,200,300),
--- stashName = "crafting_stash", --- stashName = "crafting_stash",
--- onBack = function() craftingMenu(data) end, --- onBack = function() craftingMenu(data) end,
--- metadata = { label = "Custom Pistol", image = "custom_pistol.png" }, --- metadata = { label = "Custom Pistol", image = "custom_pistol.png" },
@@ -320,40 +281,31 @@ end
function makeItem(data) function makeItem(data)
if CraftLock then return end if CraftLock then return end
CraftLock = true CraftLock = true
if data.stashTable then data.stashName = data.stashTable end 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 bartime = (data.craftable.progressBar and data.craftable.progressBar.time) or 5000
local animDict = data.craftable.Anims and data.craftable.Anims.animDict or "amb@prop_human_parking_meter@male@idle_a" local bartext = (data.craftable.progressBar and data.craftable.progressBar.label)
local anim = data.craftable.Anims and data.craftable.Anims.anim or "idle_a" or (Loc[Config.Lan].progressbar and Loc[Config.Lan].progressbar["progress_make"])
local amount = data.amount and (data.amount ~= 1) and data.amount or 1 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 metadata = data.metadata or nil
local prop = data.craftable.Anims and data.craftable.Anims.prop or nil local prop = data.craftable.Anims and data.craftable.Anims.prop or nil
local canReturn = true local canReturn = true
local crafted, crafting = true, true local crafted, crafting = true, true
local cam = createTempCam(PlayerPedId(), data.coords) local cam = createTempCam(PlayerPedId(), data.coords)
startTempCam(cam) startTempCam(cam)
for i = 1, amount do for i = 1, craftAmount do
countTable(data.craft)
for k, v in pairs(data.craft) do for k, v in pairs(data.craft) do
local excludeKeys = { local excludeKeys = {
amount = true, amount = true, info = true, metadata = true, description = true,
info = true, job = true, gang = true, oneUse = true, slot = true,
metadata = true, blueprintRef = true, craftingLevel = true, craftedItems = true,
description = true, hasCrafted = true, exp = true, anim = true, time = true,
job = true,
gang = true,
oneUse = true,
slot = true,
blueprintRef = true,
craftingLevel = true,
craftedItems = true,
hasCrafted = true,
exp = true,
} }
if not excludeKeys[k] then if not excludeKeys[k] then
if type(v) == "table" then if type(v) == "table" then
for l, b in pairs(v) do for l, b in pairs(v) do
@@ -366,7 +318,7 @@ function makeItem(data)
flag = 48, flag = 48,
icon = l, icon = l,
}) then }) 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 else
crafted, crafting = false, false crafted, crafting = false, false
break break
@@ -376,9 +328,8 @@ function makeItem(data)
if crafted then if crafted then
local craftProp = nil local craftProp = nil
if prop then if prop then
local model, pos, rot, bone = prop.model, prop.pos, prop.rot, prop.bone craftProp = makeProp({ prop = prop.model, coords = vec4(0, 0, 0, 0), true, true })
craftProp = makeProp({ 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)
AttachEntityToEntity(craftProp, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), bone), pos.x, pos.y, pos.z, rot.x, rot.y, rot.z, true, true, false, true, 1, true)
end end
if crafting and progressBar({ if crafting and progressBar({
label = bartext..((metadata and metadata.label) or Items[data.item].label), label = bartext..((metadata and metadata.label) or Items[data.item].label),
@@ -393,16 +344,14 @@ function makeItem(data)
CreateThread(function() CreateThread(function()
if data.craft["hasCrafted"] ~= nil then if data.craft["hasCrafted"] ~= nil then
debugPrint("hasCrafted Found, marking '"..data.item.."' as crafted for player") debugPrint("hasCrafted Found, marking '"..data.item.."' as crafted for player")
data.craftable.craftedItems[data.item] = true data.craftable.craftedItems[data.item] = true
triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems ) triggerCallback(getScript()..":server:SetMetadata", "craftedItems", data.craftable.craftedItems)
end end
Wait(100) Wait(100)
if data.craft["exp"] ~= nil then if data.craft["exp"] ~= nil then
craftingLevel += data.craft["exp"].give craftingLevel += data.craft["exp"].give
jsonPrint(data.craft["exp"]) 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) triggerCallback(getScript()..":server:SetMetadata", "craftingLevel", craftingLevel)
end end
end) end)
@@ -411,7 +360,6 @@ function makeItem(data)
local breakId = GetSoundId() local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0) PlaySoundFromEntity(breakId, "Drill_Pin_Break", PlayerPedId(), "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
canReturn = false canReturn = false
-- If recipe is removed it doesn't try to open menu again, it was causing blank menus for some reason
end end
else else
crafting = false crafting = false
@@ -431,16 +379,22 @@ function makeItem(data)
ClearPedTasks(PlayerPedId()) ClearPedTasks(PlayerPedId())
end end
-------------------------------------------------------------
-- Server Event Handler: Crafted Item
-------------------------------------------------------------
--- Server event handler for giving the crafted item to the player. --- 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 ItemMake string The item being crafted.
--- @param craftable table The crafting recipe and details. --- @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. --- @param metadata table (optional) Metadata for the crafted item.
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata) 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 if stashName then
local itemRemove = {} local itemRemove = {}
if type(stashName) == "table" then if type(stashName) == "table" then
@@ -468,22 +422,22 @@ RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable,
end end
end end
end end
addItem(ItemMake, amount, metadata, src) addItem(ItemMake, craftable.amount or 1, metadata, src)
--if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end -- Optionally, add experience here:
-- if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end
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 Contains selling menu data:
--- --- - sellTable (`table`) Table with Header and Items (item names and prices).
---@param data table A table containing selling menu data. --- - ped (optional) (`number`) Ped entity involved.
--- - **sellTable** (`table`): The selling options and settings. --- - onBack (optional) (`function`) Callback for returning.
--- - **Items** (`table`): A list of items that can be sold with their prices. --- @usage
--- - **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
--- ```lua --- ```lua
--- sellMenu({ --- sellMenu({
--- sellTable = { --- sellTable = {
@@ -505,10 +459,10 @@ function sellMenu(data)
for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end
local _, hasTable = hasItem(itemList) local _, hasTable = hasItem(itemList)
for k, v in pairsByKeys(data.sellTable.Items) do for k, v in pairsByKeys(data.sellTable.Items) do
Menu[#Menu +1] = { Menu[#Menu + 1] = {
isMenuHeader = not hasTable[k].hasItem, isMenuHeader = not hasTable[k].hasItem,
icon = invImg(k), icon = invImg(k),
header = Items[k].label.. (hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""), header = 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"], txt = Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"],
onSelect = function() onSelect = function()
sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end }) sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end })
@@ -518,7 +472,7 @@ function sellMenu(data)
else else
for k, v in pairsByKeys(data.sellTable) do for k, v in pairsByKeys(data.sellTable) do
if type(v) == "table" then if type(v) == "table" then
Menu[#Menu +1] = { Menu[#Menu + 1] = {
arrow = true, arrow = true,
header = k, header = k,
txt = "Amount of items: "..countTable(v.Items), txt = "Amount of items: "..countTable(v.Items),
@@ -531,19 +485,24 @@ function sellMenu(data)
end end
end 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 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. --- 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 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.
--- ---
--- @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 ---@usage
--- ```lua --- ```lua
--- sellAnim({ --- sellAnim({
@@ -558,16 +517,20 @@ function sellAnim(data)
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error") triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error")
return return
end end
for k, v in pairs(GetGamePool('CObject')) do
for _, model in pairs({`p_cs_clipboard`}) do -- Remove any attached clipboard objects.
if GetEntityModel(v) == model then for _, obj in pairs(GetGamePool('CObject')) do
if IsEntityAttachedToEntity(data.ped, v) then for _, model in pairs({ `p_cs_clipboard` }) do
DeleteObject(v) DetachEntity(v, 0, 0) SetEntityAsMissionEntity(v, true, true) if GetEntityModel(obj) == model and IsEntityAttachedToEntity(data.ped, obj) then
Wait(100) DeleteEntity(v) DeleteObject(obj)
end DetachEntity(obj, 0, 0)
SetEntityAsMissionEntity(obj, true, true)
Wait(100)
DeleteEntity(obj)
end end
end end
end end
TriggerServerEvent(getScript().."Sellitems", data) TriggerServerEvent(getScript().."Sellitems", data)
lookEnt(data.ped) lookEnt(data.ped)
local dict = "mp_common" local dict = "mp_common"
@@ -579,11 +542,8 @@ function sellAnim(data)
if data.onBack then data.onBack() end if data.onBack then data.onBack() end
end end
--- Server event handler for processing the item sale. --- Server event handler for processing item sales.
--- --- Removes sold items from inventory and funds the player based on the 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.
RegisterNetEvent(getScript().."Sellitems", function(data) RegisterNetEvent(getScript().."Sellitems", function(data)
local src = source local src = source
local hasItems, hasTable = hasItem(data.item, 1, src) local hasItems, hasTable = hasItem(data.item, 1, src)
@@ -595,17 +555,18 @@ RegisterNetEvent(getScript().."Sellitems", function(data)
end end
end) end)
-------------------------------------------------------------
-- Shop Interface
-------------------------------------------------------------
--- Opens a shop interface for the player. --- Opens a shop interface for the player.
--- ---
--- This function checks job requirements and opens the shop using the appropriate inventory system. --- Checks job/gang restrictions, then uses the active inventory system to open the shop.
--- --- @param data table Contains:
---@param data table A table containing shop data. --- - shop (`string`) The shop identifier.
--- - **shop** (`string`): The shop identifier. --- - items (`table`) The items available in the shop.
--- - **items** (`table`): The items available in the shop. --- - coords (`vector3`) where the shop is located.
--- - **coords** (`vector3`): The coordinates where the shop interaction is happening. --- - job/gang (optional) (`string`) Job or gang requirements.
--- - **job** (`string` or `table`, optional): Job(s) required to access the shop.
--- - **gang** (`string` or `table`, optional): Gang(s) required to access the shop.
---
---@usage ---@usage
--- ```lua --- ```lua
--- openShop({ --- openShop({
@@ -617,30 +578,34 @@ end)
--- ``` --- ```
function openShop(data) function openShop(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 if isStarted(OXInv) then
exports[OXInv]:openInventory('shop', { type = data.shop }) exports[OXInv]:openInventory('shop', { type = data.shop })
elseif isStarted(QBInv) then elseif isStarted(QBInv) then
if QBInvNew then if QBInvNew then
TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop) -- i hate qb-inv TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop)
else else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items) TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end end
--elseif isStarted(OrigenInv) then -- Needs testing, not sure if i did this right
-- exports[OrigenInv]:openInventory('shop', data.shop, data.items)
else else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items) TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end end
lookEnt(data.coords) lookEnt(data.coords)
end end
--- Server event handler for opening a new QB inventory shop. --- Server event handler for opening a shop using the new QB inventory system.
---
--- This event is triggered when using the new QB inventory system.
---
---@param data table The shop data to open.
RegisterNetEvent(getScript()..':server:OpenShopNewQB', function(data) RegisterNetEvent(getScript()..':server:OpenShopNewQB', function(data)
exports[QBInv]:OpenShop(source, data) exports[QBInv]:OpenShop(source, data)
end) end)
--- Server-side callback registration for checking if the player can carry items. -------------------------------------------------------------
-- Server Callback Registration
-------------------------------------------------------------
if isServer() then if isServer() then
createCallback(getScript()..':server:canCarry', function(source, itemTable) local result = canCarry(itemTable, source) return result end) createCallback(getScript()..':server:canCarry', function(source, itemTable) local result = canCarry(itemTable, source) return result end)
end end

View File

@@ -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. --- 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 image string|nil Optional image/icon identifier to display with the text.
---@param input table A table of strings, each representing a line of text to display. --- @param input table An array of strings; each string is 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 style string|nil Optional style code for default GTA popups (e.g., "~g~" for green).
---@param oxStyleTable table|nil An optional table specifying style parameters for the 'ox' draw text system. --- @param oxStyleTable table|nil Optional table specifying style parameters for the OX text UI.
--- ---
---@usage ---@usage
--- ```lua --- ```lua
--- drawText("img_link", { "Test line 1", "Test Line 2" }, "~g~") --- drawText("img_link", { "Test line 1", "Test Line 2" }, "~g~")
--- ``` --- ```
function drawText(image, input, style, oxStyleTable) local text = "" function drawText(image, input, style, oxStyleTable)
if Config.System.drawText == "qb" then local text = ""
for i = 1, #input do
text = text..input[i].."</span>"..(input[i+1] ~= nil and "<br>" or "") end
local text = text:gsub("%:", ":<span style='color:yellow'>")
if image then
text = '<img src="'..(radarTable[image] or nil)..'" style="width:12px;height:12px">'..text
end
exports[QBExport]:DrawText(text, 'left')
elseif Config.System.drawText == "ox" then if Config.System.drawText == "qb" then
for k, v in pairs(input) do -- Concatenate lines for QB system with HTML line breaks.
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
for i = 1, #input do for i = 1, #input do
text = text..input[i].."</span>"..(input[i+1] ~= nil and "<br>" or "") end text = text..input[i].."</span>"..(input[i + 1] and "<br>" or "")
local text = text:gsub("%:", ":<span style='color:yellow'>") end
if image then text = text:gsub("%:", ":<span style='color:yellow'>")
text = '<img src="'..radarTable[image]..'" style="width:12px;height:12px">'..text if image then
end text = '<img src="'..(radarTable[image] or "")..'" style="width:12px;height:12px">'..text
ESX.TextUI(text, nil) end
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].."</span>"..(input[i + 1] and "<br>" or "")
end
text = text:gsub("%:", ":<span style='color:yellow'>")
if image then
text = '<img src="'..(radarTable[image] or "")..'" style="width:12px;height:12px">'..text
end
ESX.TextUI(text, nil)
end
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() function hideText()
if Config.System.drawText == "qb" then if Config.System.drawText == "qb" then
exports[QBExport]:HideText() exports[QBExport]:HideText()

View File

@@ -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 scriptTxd = not isServer() and CreateRuntimeTxd(getScript()..'scriptTxd') or nil
customDUIList = {} customDUIList = {}
-- DUI CLIENT -------------------------------------------------------------
function createDui(name, http, size, txd) -- DUI Client Functions
--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
function DuiSelect(data) --- Creates or updates a DUI element.
local image = "" ---
for k, v in pairs(duiList[data.name]) do --- @param name string The unique name for the DUI element.
if v.tex.texn == data.texn then --- @param http string The URL to load into the DUI.
if duiList[data.name][k] then --- @param size table A table with .x and .y fields specifying the DUI dimensions.
image = "<center>- Current Image -<br>".. --- @param txd table The runtime texture dictionary where the DUI texture will be created.
"<img src="..duiList[data.name][k].url.." width=150px><br>".. --- @usage
"Size: ["..math.floor(data.size.x)..", "..math.floor(data.size.y).."]<br><br>" --- ```lua
end --- createDui("logo", "https://example.com/logo.png", { x = 512, y = 256 }, scriptTxd)
end --- ```
end function createDui(name, http, size, txd)
local dialog = exports['qb-input']:ShowInput({ if not customDUIList[name] then
header = image..Loc[Config.Lan].menu["dui_new"], local newDui = CreateDui(http, math.floor(size.x), math.floor(size.y))
submitText = Loc[Config.Lan].menu["dui_change"], while not GetDuiHandle(newDui) do Wait(0) end
inputs = { { type = 'text', isRequired = true, name = 'url', text = Loc[Config.Lan].menu["dui_url"] } } }) CreateRuntimeTextureFromDuiHandle(txd, name, GetDuiHandle(newDui))
if dialog then customDUIList[name] = newDui
if not dialog.url then return end SetDuiUrl(customDUIList[name], http)
data.url = dialog.url else
--Scan the link to see if it has an image extention otherwise, stop here. SetDuiUrl(customDUIList[name], http)
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
end end
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 = "<center>- Current Image -<br>" ..
"<img src="..duiList[data.name][k].url.." width=150px><br>" ..
"Size: ["..math.floor(data.size.x)..", "..math.floor(data.size.y).."]<br><br>"
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) 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 if tostring(data.url) ~= "-" then
createDui(data.texn, tostring(data.url), data.size, scriptTxd) createDui(data.texn, tostring(data.url), data.size, scriptTxd)
AddReplaceTexture(tostring(data.texd), tostring(data.texn), getScript()..'scriptTxd', tostring(data.texn)) AddReplaceTexture(tostring(data.texd), tostring(data.texn), getScript().."scriptTxd", tostring(data.texn))
end end
end) end)
--- Client event handler to clear DUI elements.
RegisterNetEvent(getScript()..":Client:ClearDUI", function(data) RegisterNetEvent(getScript()..":Client:ClearDUI", function(data)
if customDUIList[tostring(data.texn)] then if customDUIList[tostring(data.texn)] then
RemoveReplaceTexture(tostring(data.texd), tostring(data.texn)) RemoveReplaceTexture(tostring(data.texd), tostring(data.texn))
if IsDuiAvailable(customDUIList[tostring(data.texn)]) then if IsDuiAvailable(customDUIList[tostring(data.texn)]) then
SetDuiUrl(customDUIList[data.name], nil) SetDuiUrl(customDUIList[data.name], nil)
end end
end 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) RegisterNetEvent(getScript()..":Server:ChangeDUI", function(data)
-- if no url given, "reset" it back to preset
if not data.url then if not data.url then
for k, v in pairs(duiList[data.name]) do for k, v in pairs(duiList[data.name]) do
if v.tex.texn == data.texn then if v.tex.texn == data.texn then
debugPrint("^6Bridge^7: ^2Preset^7: ^6"..tostring(duiList[data.name][k].preset).."^7") debugPrint("^6Bridge^7: ^2Preset: ^6"..tostring(duiList[data.name][k].preset).."^7")
data.url = duiList[data.name][k].preset data.url = duiList[data.name][k].preset
end end
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 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") 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) end)
--- Server event handler to clear DUI settings.
RegisterNetEvent(getScript()..":Server:ClearDUI", function(data) RegisterNetEvent(getScript()..":Server:ClearDUI", function(data)
if data.url == "-" then if data.url == "-" then
for k, v in pairs(duiList[data.name]) do for k, v in pairs(duiList[data.name]) do
if v.tex.texn == data.texn then if v.tex.texn == data.texn then
duiList[data.name][k].url = "-" duiList[data.name][k].url = "-"
end end
end end
end end
-- Clear the DUI from loading
TriggerClientEvent(getScript()..":Client:ClearDUI", -1, data) TriggerClientEvent(getScript()..":Client:ClearDUI", -1, data)
--duiList[tostring(data.tex)].url = ""
end) end)
AddEventHandler('onResourceStop', function(r) if r ~= getScript() then return end -------------------------------------------------------------
-- Resource Cleanup
-------------------------------------------------------------
onResourceStop(function()
for k, v in pairs(duiList or {}) do for k, v in pairs(duiList or {}) do
for i = 1, #v do for i = 1, #v do
RemoveReplaceTexture(tostring(v[i].tex.texd), tostring(v[i].tex.texn)) RemoveReplaceTexture(tostring(v[i].tex.texd), tostring(v[i].tex.texn))
end end
end end
end) end, true)
-------------------------------------------------------------
-- DUI List Callback (Server)
-------------------------------------------------------------
if isServer() then if isServer() then
createCallback(getScript()..":Server:duiList", function(source) createCallback(getScript()..":Server:duiList", function(source)
return duiList return duiList
end) end)
end end

View File

@@ -1,15 +1,19 @@
--- Utility Functions for Resource Management and Debugging --[[
--- 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. 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. --- Checks if a specific resource is started.
--- --- @param script string The name of the resource.
---@param script string The name of the resource to check. --- @return boolean boolean True if the resource state contains "start", false otherwise.
---@return boolean `true` if the resource state contains "start", otherwise `false`.
---
---@usage ---@usage
--- ```lua --- ```lua
--- if isStarted("myResource") then --- if isStarted("myResource") then
@@ -22,12 +26,8 @@ end
local scriptName = nil local scriptName = nil
--- Retrieves the current resource name. --- Retrieves the current resource name, caching it for efficiency.
--- --- @return string string The current resource name.
--- Caches the resource name after the first call for efficiency.
---
--- @return string scriptName The name of the current resource.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local currentScript = getScript() --- local currentScript = getScript()
@@ -38,12 +38,8 @@ function getScript()
return scriptName return scriptName
end end
--- Determines if the current execution context is the server. --- Determines if the current context is the server.
--- --- @return boolean boolean True if running on the server, false otherwise.
--- 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`.
---
---@usage ---@usage
--- ```lua --- ```lua
--- if isServer() then --- if isServer() then
@@ -56,14 +52,13 @@ function isServer()
return IsDuplicityVersion() return IsDuplicityVersion()
end end
--[[ Debugging Functions ]]-- -------------------------------------------------------------
-- Debugging and JSON Utilities
-------------------------------------------------------------
--- Prints debug messages if debugging mode is enabled. --- Prints debug messages if debugMode is enabled.
--- --- Concatenates all arguments and prints them with debug info.
--- Concatenates all arguments and prints them along with debug information. --- @param ... any One or more values to print.
---
--- @param ... any Multiple arguments to be concatenated and printed.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- debugPrint("Player has joined:", playerName) --- debugPrint("Player has joined:", playerName)
@@ -71,15 +66,13 @@ end
function debugPrint(...) function debugPrint(...)
if debugMode then if debugMode then
local args = {...} 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"))) print(output, getDebugInfo(debug.getinfo(2, "nSl")))
end end
end end
--- Prints event-related debug messages if event debugging is enabled. --- Prints event-related debug messages if event debugging is enabled.
--- --- @param ... any One or more values to print.
--- @param ... any Multiple arguments to be printed.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- eventPrint("Event triggered:", eventName) --- eventPrint("Event triggered:", eventName)
@@ -90,24 +83,22 @@ function eventPrint(...)
end end
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) function getSortedKeys(tbl)
local keys = {} local keys = {}
for k in pairs(tbl) do keys[#keys + 1] = k end for k in pairs(tbl) do keys[#keys + 1] = k end
table.sort(keys, function(a, b) table.sort(keys, function(a, b)
local numA, numB = tonumber(a), tonumber(b) local numA, numB = tonumber(a), tonumber(b)
if numA and numB then return numA < numB if numA and numB then return numA < numB else return tostring(a) < tostring(b) end
else return tostring(a) < tostring(b) end
end) end)
return keys return keys
end end
--- Recursively colorizes a table for debug printing. --- Recursively colorizes a table for debug printing.
---
--- @param tbl table The table to colorize. --- @param tbl table The table to colorize.
--- @return table colourizedTable The colorized table. --- @return table table A new table with colorized keys and values.
---
--- @usage
--- ```lua --- ```lua
--- local colorizedData = colorizeTable(myTable) --- local colorizedData = colorizeTable(myTable)
--- jsonPrint(colorizedData) --- jsonPrint(colorizedData)
@@ -116,18 +107,19 @@ function colorizeTable(tbl)
local newData, sortedKeys = {}, getSortedKeys(tbl) local newData, sortedKeys = {}, getSortedKeys(tbl)
for _, k in ipairs(sortedKeys) do for _, k in ipairs(sortedKeys) do
local v = tbl[k] 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 end
return newData return newData
end end
--- Encodes a table into an ordered JSON string with indentation. --- Encodes a table into an ordered JSON string with indentation.
---
--- @param data table The table to encode. --- @param data table The table to encode.
--- @param indent string The string used for indentation (e.g., " "). --- @param indent string The indentation string (e.g., " ").
--- @param level number The current indentation level. --- @param level number The current level of indentation.
--- @return string The formatted JSON string. --- @return string The formatted JSON string.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local jsonString = encodeOrderedJSON(myTable, " ", 0) --- local jsonString = encodeOrderedJSON(myTable, " ", 0)
@@ -143,10 +135,8 @@ function encodeOrderedJSON(data, indent, level)
return table.concat(jsonParts) return table.concat(jsonParts)
end 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. --- @param data table The table to print.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- jsonPrint(myTable) --- jsonPrint(myTable)
@@ -158,9 +148,7 @@ function jsonPrint(data)
end end
--- Retrieves the current time formatted for debug prints. --- Retrieves the current time formatted for debug prints.
---
--- @return string string The formatted time string, e.g., "^7(14:23:45)". --- @return string string The formatted time string, e.g., "^7(14:23:45)".
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local currentTime = GetPrintTime() --- local currentTime = GetPrintTime()
@@ -177,9 +165,7 @@ function GetPrintTime()
end end
--- Generates a unique 3-character alphanumeric key. --- Generates a unique 3-character alphanumeric key.
--- --- @return string string The generated key.
--- @return string GeneratedString A randomly generated 3-character string.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local uniqueKey = keyGen() --- local uniqueKey = keyGen()
@@ -187,20 +173,28 @@ end
--- ``` --- ```
function keyGen() function keyGen()
local charset = { 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",
"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", "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" "1","2","3","4","5","6","7","8","9","0"
} }
local GeneratedID = "" 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 return GeneratedID
end end
-------------------------------------------------------------
-- Formatting and Vector Math Functions
-------------------------------------------------------------
--- Formats a number with commas as thousand separators. --- Formats a number with commas as thousand separators.
---
--- @param amount number The number to format. --- @param amount number The number to format.
--- @return string commaValue The formatted number string with commas. --- @return string string The formatted number.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local formattedNumber = cv(1000000) -- "1,000,000" --- local formattedNumber = cv(1000000) -- "1,000,000"
@@ -208,15 +202,17 @@ end
--- `` --- ``
function cv(amount) function cv(amount)
local formatted = tostring(amount or "0") 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 return formatted
end end
--- Formats a coordinate vector for debug printing. --- Formats a coordinate vector for debug printing.
--- --- @param coord table A vector3 or vector4 with x, y, z (and optional w).
--- @param coord table A vector3 or vector4 table with `x`, `y`, `z`, and optional `w` components. --- @return string string The formatted coordinate string.
--- @return string The formatted coordinate string with color codes.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local formattedCoord = formatCoord(vector3(100.0, 200.0, 300.0)) --- 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)" return "^5"..vecType.."^7("..components[1]..components[2]..components[3]..components[4].."^7)"
end end
--- Calculates the center point of a list of zones (coordinates). --- Calculates the center point of a list of coordinates.
--- --- @param tbl table An array of vector3 coordinates.
--- @param table table A table of vector3 coordinates.
--- @return vector3 vector3 The center coordinate. --- @return vector3 vector3 The center coordinate.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local center = getCenterOfZones({vector3(100, 200, 300), vector3(110, 210, 310)}) --- local center = getCenterOfZones({vector3(100, 200, 300), vector3(110, 210, 310)})
--- print("Center of Zones:", center) --- print("Center of Zones:", center)
--- ``` --- ```
function getCenterOfZones(table) function getCenterOfZones(tbl)
local totalX, totalY, totalZ = 0, 0, 0 local totalX, totalY, totalZ = 0, 0, 0
for _, coord in ipairs(tbl) do
for _, coord in ipairs(table) do
totalX = totalX + coord.x totalX = totalX + coord.x
totalY = totalY + coord.y totalY = totalY + coord.y
totalZ = totalZ + coord.z totalZ = totalZ + coord.z
end end
local count = #tbl
local count = #table
return vector3(totalX / count, totalY / count, totalZ / count) return vector3(totalX / count, totalY / count, totalZ / count)
end end
--- Counts the number of keys in a table. --- Counts the number of keys in a table.
--- --- @param tbl table The table to count.
--- @param table table The table to count keys in. --- @return number number The key count.
--- @return number number The number of keys in the table.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local count = countTable(myTable) --- local count = countTable(myTable)
--- print("Number of keys:", count) --- 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 over a table's keys in sorted order.
--- Returns an iterator that iterates over a table's keys in sorted order.
---
--- @param t table The table to iterate over. --- @param t table The table to iterate over.
--- @return function function An iterator function. --- @return function An iterator function for sorted keys.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- for k, v in pairsByKeys(myTable) do --- 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 --- end
--- ``` --- ```
function pairsByKeys(t) function pairsByKeys(t)
local t = t
if not t then if not t then
print("^1Error^7: ^3Nil ^2table recieved for ^3pairsByKeys^7(), ^2setting to ^7{} ^2to prevent break^7") print("^1Error^7: ^3Nil ^2table recieved for ^3pairsByKeys^7(), ^2setting to ^7{} ^2to prevent break^7")
t = {} 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 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 end
--- Creates a new table with consecutive numerical indices sorted by the `id` field. --- Creates a new table with consecutive numerical indices sorted by the 'id' field.
--- --- @param originalTable table The table containing entries with an 'id' field.
--- @param originalTable table The original table with entries containing an `id` field. --- @return table table A sorted table with consecutive indices.
--- @return table The new table with sorted entries and consecutive `id` values.
---
--- @usage --- @usage
--- ```lua
--- local sortedTable = createConsecutiveTable(originalTable) --- local sortedTable = createConsecutiveTable(originalTable)
--- for i, entry in ipairs(sortedTable) do --- for i, entry in ipairs(sortedTable) do
--- print(i, entry) --- print(i, entry)
--- end --- end
--- ```
function createConsecutiveTable(originalTable) function createConsecutiveTable(originalTable)
local sortedEntries = {} local sortedEntries = {}
for _, entry in pairs(originalTable) do for _, entry in pairs(originalTable) do table.insert(sortedEntries, entry) end
table.insert(sortedEntries, entry) table.sort(sortedEntries, function(a, b) return a.id < b.id end)
end
table.sort(sortedEntries, function(a, b)
return a.id < b.id
end)
local newTable = {} local newTable = {}
for newIndex, entry in ipairs(sortedEntries) do for newIndex, entry in ipairs(sortedEntries) do
entry.id = newIndex entry.id = newIndex
@@ -315,112 +301,9 @@ function createConsecutiveTable(originalTable)
return newTable return newTable
end 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. --- Concatenates a table of strings into a single string separated by newlines.
--- --- @param tbl table The table containing strings.
--- @param tbl table A table containing string elements. --- @return string string The concatenated string.
--- @return string string The concatenated string with newline separators.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local combinedText = concatenateText({"Line 1", "Line 2", "Line 3"}) --- local combinedText = concatenateText({"Line 1", "Line 2", "Line 3"})
@@ -429,74 +312,69 @@ end
function concatenateText(tbl) function concatenateText(tbl)
local result = "" local result = ""
for i = 1, #tbl do for i = 1, #tbl do
result = result..tbl[i] result = result..tbl[i]..(i < #tbl and "\n" or "")
if i < #tbl then
result = result.."\n" -- Add newline only if it's not the last element
end
end end
return result return result
end end
--- Converts rotation to a direction vector. --- Converts a rotation (degrees) to a direction vector.
--- --- @param rot vector3 A vector3 with rotation values.
--- @param rot vector3 A vector3 containing rotation values --- @return vector3 vector3 The forward direction vector.
--- @return vector3 vector3 A vector3 representing the direction.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local direction = RotationToDirection({ z = 90 }) --- local direction = RotationToDirection({ z = 90 })
--- print(direction) --- print(direction)
--- ``` --- ```
function RotationToDirection(rot) function RotationToDirection(rot)
local adjust = (math.pi / 180) 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)) 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 end
--- Creates a simple text-based progress bar. --- Creates a basic progress bar string.
--- --- @param percentage number Completion percentage (0-100).
--- @param percentage number The completion percentage (0-100). --- @return string string The progress bar (e.g., "█████░░░░░").
--- @return string string A string representing the progress bar, e.g., "█████░░░░░".
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local bar = basicBar(50) -- "█████░░░░░" --- local bar = basicBar(50) -- "█████░░░░░"
--- print(bar) --- print(bar)
--- ``` --- ```
function basicBar(percentage) function basicBar(percentage)
local percentage = math.ceil(percentage) local perc = math.ceil(percentage)
local totalBlocks = 10 local total = 10
local filledBlocks = math.floor((percentage / 100) * totalBlocks) local filled = math.floor((perc / 100) * total)
local emptyBlocks = totalBlocks - filledBlocks local empty = total - filled
return string.rep("", filled)..string.rep("", empty)
local bar = string.rep("", filledBlocks)..string.rep("", emptyBlocks)
return bar
end end
--- Normalizes a 3D vector. --- Normalizes a 3D vector.
--- --- @param vec vector3 A vector3 table.
--- @param vec vector3 A vector3 table with `x`, `y`, and `z` components. --- @return vector3 vector3 A normalized vector.
--- @return vector3 vector3 The normalized vector3.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local normalizedVec = normalizeVector(vector3(1, 2, 3)) --- local normalizedVec = normalizeVector(vector3(1, 2, 3))
--- print(normalizedVec) --- print(normalizedVec)
--- ``` --- ```
function normalizeVector(vec) function normalizeVector(vec)
local length = math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) local len = math.sqrt(vec.x^2 + vec.y^2 + vec.z^2)
if length ~= 0 then if len ~= 0 then
return vec3(vec.x / length, vec.y / length, vec.z / length) return vec3(vec.x / len, vec.y / len, vec.z / len)
else else
return vec3(0, 0, 0) return vec3(0, 0, 0)
end end
end end
--- Draws a line between two coordinates for debugging purposes. -------------------------------------------------------------
--- -- Drawing and Raycasting Functions
--- @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. --- 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 --- @usage
--- ```lua --- ```lua
--- drawLine(vector3(100, 200, 300), vector3(150, 250, 350), vector4(255, 0, 0, 255)) --- drawLine(vector3(100, 200, 300), vector3(150, 250, 350), vector4(255, 0, 0, 255))
@@ -504,21 +382,19 @@ end
function drawLine(startCoords, endCoords, col) function drawLine(startCoords, endCoords, col)
if debugMode then if debugMode then
CreateThread(function() CreateThread(function()
local showCount = 1000 local count = 1000
while showCount >= 0 do 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) 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) Wait(0)
end end
end) end)
end end
end end
--- Draws a sphere at specified coordinates for debugging purposes. --- Draws a sphere at the specified coordinates (for debugging).
--- --- @param coords vector3 The center of the sphere.
--- @param coords vector3 A vector3 table representing the center of the sphere. --- @param col vector4 A vector4 specifying color and opacity.
--- @param col vector4 A table with `x`, `y`, `z`, `w` representing the color and opacity.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- drawSphere(vector3(100, 200, 300), vector4(0, 255, 0, 255)) --- drawSphere(vector3(100, 200, 300), vector4(0, 255, 0, 255))
@@ -526,24 +402,22 @@ end
function drawSphere(coords, col) function drawSphere(coords, col)
if debugMode then if debugMode then
CreateThread(function() CreateThread(function()
local showCount = 1000 local count = 1000
while showCount >= 0 do while count >= 0 do
DrawSphere(coords.x, coords.y, coords.z, 0.5, col.x, col.y, col.z, col.w) DrawSphere(coords.x, coords.y, coords.z, 0.5, col.x, col.y, col.z, col.w)
showCount -= 1 count -= 1
Wait(10) Wait(10)
end end
end) end)
end end
end end
--- Performs a raycast between two coordinates and returns the result. --- Performs a raycast between two coordinates and returns the results.
--- --- @param startCoords vector3 The starting coordinate.
--- @param startCoords table A vector3 table representing the start point. --- @param endCoords vector3 The ending coordinate.
--- @param endCoords table A vector3 table representing the end point. --- @param entity number|nil An entity to ignore.
--- @param entity number|nil The entity to ignore during the raycast. --- @param flags number|nil Optional raycast flags (default: 4294967295).
--- @param flags number|nil Raycast flags to customize the raycast behavior. Defaults to `4294967295`. --- @return multiple Multiple values returned by GetShapeTestResultIncludingMaterial.
--- @return multiple multiple Returns multiple values from `GetShapeTestResultIncludingMaterial`.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local hit, hitPos, material = PerformRaycast(startVec, endVec, playerPed, 1) --- local hit, hitPos, material = PerformRaycast(startVec, endVec, playerPed, 1)
@@ -553,47 +427,41 @@ end
--- end --- end
--- ``` --- ```
function PerformRaycast(startCoords, endCoords, entity, flags) function PerformRaycast(startCoords, endCoords, entity, flags)
drawLine(startCoords, endCoords, vec4(0, 0, 255, 255)) 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)) local val1, val2, val3, val4, val5, val6 = GetShapeTestResult(
if val2 then StartExpensiveSynchronousShapeTestLosProbe(
--drawSphere(val3, vec4(255, 0, 255, 0.5)) startCoords.x, startCoords.y, startCoords.z,
end endCoords.x, endCoords.y, endCoords.z,
flags or 4294967295, entity, 0
)
)
return val1, val2, val3, val4, val5, val6 return val1, val2, val3, val4, val5, val6
end end
--- Adjusts the Z-coordinate of a position to align with the ground. --- 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).
--- @param coords vector4 A vector3 or vector4 table with `x`, `y`, `z`, and optional `w` components. --- @return vector3|vector4 vector The coordinates adjusted for ground level.
--- @return vector3|vector4 vector adjusted coordinate with the Z value set to the ground level.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local groundCoords = adjustForGround(playerCoords) --- local groundCoords = adjustForGround(playerCoords)
--- print("Ground Position:", groundCoords) --- print("Ground Position:", groundCoords)
--- ``` --- ```
function adjustForGround(coords) function adjustForGround(coords)
local coords = coords
local foundGround, zPos = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z + 1.0) local foundGround, zPos = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z + 1.0)
if foundGround then if foundGround then
if coords.w then if coords.w then
coords = vec4(coords.x, coords.y, zPos, coords.w) return vec4(coords.x, coords.y, zPos, coords.w)
else else
coords = vec3(coords.x, coords.y, zPos) return vec3(coords.x, coords.y, zPos)
end end
--debugPrint("^6Bridge^7: Adjusting for ground pos ", coords.z, zPos)
return coords
else else
return coords return coords
end end
end end
--- Ensures that a network vehicle exists by verifying its network ID. --- Ensures a network vehicle exists from its network ID.
--- --- @param vehNetID number The network ID.
--- @param vehNetID number The network ID of the vehicle. --- @return number number The vehicle entity, or 0 if not found.
--- @return number number The vehicle entity if it exists, otherwise `0`.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local vehicle = ensureNetToVeh(netID) --- local vehicle = ensureNetToVeh(netID)
@@ -619,16 +487,16 @@ function ensureNetToVeh(vehNetID)
return vehicle return vehicle
end end
--- Ensures that a network entity exists by verifying its network ID. --- Ensures a network entity exists from its network ID.
--- --- @param entNetID number The network ID.
--- @param entNetID number The network ID of the entity. --- @return number number The entity, or 0 if not found.
--- @return number The entity if it exists, otherwise `0`.
---
--- @usage --- @usage
--- ```lua
--- local entity = ensureNetToEnt(netID) --- local entity = ensureNetToEnt(netID)
--- if entity ~= 0 then --- if entity ~= 0 then
--- print("Entity exists:", entity) --- print("Entity exists:", entity)
--- end --- end
--- ```
function ensureNetToEnt(entNetID) function ensureNetToEnt(entNetID)
debugPrint("^6Bridge^7: ^3ensureNetToEnt^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..entNetID.."^7)") debugPrint("^6Bridge^7: ^3ensureNetToEnt^7: ^2Requesting NetworkDoesNetworkIdExist^7(^6"..entNetID.."^7)")
local timeout = 100 local timeout = 100
@@ -647,7 +515,9 @@ function ensureNetToEnt(entNetID)
return entity return entity
end end
--[[ Material Definitions ]]-- -------------------------------------------------------------
-- Material and Prop Functions
-------------------------------------------------------------
--- A table mapping material names to their corresponding hash values. --- A table mapping material names to their corresponding hash values.
--- ---
@@ -869,22 +739,17 @@ local materials = {
temp_30 = 13626292 temp_30 = 13626292
} }
--- Retrieves the ground material at a specified position. --- Retrieves the ground material at a given position.
--- --- @param coords vector3 The coordinate to test.
--- This function performs a raycast downwards from the given coordinates to determine the material type of the ground. --- @return number|nil number The material hash if hit, nil otherwise.
--- --- @return string string The material name.
--- @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.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- local materialHash, materialName = GetGroundMaterialAtPosition(vector3(100, 200, 300)) --- local matHash, matName = GetGroundMaterialAtPosition(vector3(100,200,300))
--- print("Ground material:", materialName) --- print("Material:", matName)
--- ``` --- ```
function GetGroundMaterialAtPosition(coords) function GetGroundMaterialAtPosition(coords)
local endX, endY, endZ = coords.x, coords.y, coords.z - 1.0 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 rayHandle = StartShapeTestCapsule(coords.x, coords.y, coords.z, endX, endY, endZ, 1.0, 1, playerPed, 7)
local _, hit, _, _, materialHash, _ = GetShapeTestResultIncludingMaterial(rayHandle) local _, hit, _, _, materialHash, _ = GetShapeTestResultIncludingMaterial(rayHandle)
local materialName = "Unknown" local materialName = "Unknown"
@@ -894,14 +759,10 @@ function GetGroundMaterialAtPosition(coords)
break break
end end
end end
if hit then return materialHash, materialName if hit then return materialHash, materialName else return nil, materialName end
else return nil, materialName end
end end
--- Retrieves the dimensions of a prop/model. --- Retrieves the dimensions (width, depth, height) of a prop/model.
---
--- This function loads the specified model and returns its width, depth, and height based on its bounding box.
---
--- @param model string The name or hash of the model. --- @param model string The name or hash of the model.
--- @return number number The width of the prop. --- @return number number The width of the prop.
--- @return number number The depth of the prop. --- @return number number The depth of the prop.

View File

@@ -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. --- @param title string The title or header of the input dialog.
--- It supports various input types such as radio buttons, numbers, text, and select dropdowns. --- @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. --- @return table|nil table Returns the user's input as a table if submitted, otherwise nil.
---@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`.
--- ---
---@usage ---@usage
--- ```lua --- ```lua

133
shared/inventories.lua Normal file
View File

@@ -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

View File

@@ -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 isCat, isDog, isBigDog, isSmallDog, isCoyote, isAnimal = false, false, false, false, false, false
if not isServer() then if not isServer() then
onPlayerLoaded(function() onPlayerLoaded(function()
Wait(2000) Wait(2000)
-- Reset classification flags
isCat, isDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false isCat, isDog, isBigDog, isSmallDog, isCoyote = false, false, false, false, false
-- Check if the player's Ped is an animal.
isPedAnimal() isPedAnimal()
if isAnimal then if isAnimal then
local ped = PlayerPedId() local ped = PlayerPedId()
local pedModel = GetEntityModel(ped) 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`) 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) isDog, isBigDog = isDog(ped)
isSmallDog = not isBigDog isSmallDog = not isBigDog
if isDog and pedModel == `a_c_coyote` then isDog = false end 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`) 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 if pedModel == `ft-capmonkey2` then isDog = true end
end end
end, true) 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) --- Checks if the Ped's model hash appears in any of the animal categories defined in AnimalPeds.
--- 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.
--- ---
---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`). --- @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.
---@return boolean `true` if the Ped is an animal, otherwise `false`.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
@@ -39,31 +79,24 @@ if not isServer() then
--- ``` --- ```
function isPedAnimal(ped) function isPedAnimal(ped)
local PedModel = GetEntityModel(ped or PlayerPedId()) local PedModel = GetEntityModel(ped or PlayerPedId())
for _, animalCategory in pairs(AnimalPeds) do
for _, animalTypeTable in pairs(AnimalPeds) do for animalModelHash, _ in pairs(animalCategory) do
for animalModelHash, _ in pairs(animalTypeTable) do
if PedModel == animalModelHash then if PedModel == animalModelHash then
isAnimal = true isAnimal = true
break debugPrint("^6Bridge^7: ^2Ped is Animal")
return true
end end
end end
if isAnimal then
debugPrint("^6Bridge^7: ^2Ped is Animal^1")
break
end
end end
return false
return isAnimal
end 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) --- Iterates through the CatPeds table and returns true if the Ped's model matches.
--- matches any of the model hashes listed under `AnimalPeds.CatPeds`. It returns `true` if a match is found.
--- ---
---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`). --- @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.
---@return boolean `true` if the Ped is a cat, otherwise `false`.
--- ---
---@usage ---@usage
--- ```lua --- ```lua
@@ -78,24 +111,20 @@ if not isServer() then
--- ``` --- ```
function isCat(ped) function isCat(ped)
local PedModel = GetEntityModel(ped or PlayerPedId()) local PedModel = GetEntityModel(ped or PlayerPedId())
for k, v in pairs(AnimalPeds.CatPeds) do for modelHash, _ in pairs(AnimalPeds.CatPeds) do
if PedModel == k then if PedModel == modelHash then
return true return true
end end
end end
return false return false
end 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) --- Checks the BigDogs and SmallDogs tables to see if the Ped's model matches any dog model.
--- 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`.
--- ---
---@param ped number|nil Optional. The Ped entity to check. Defaults to the player's Ped (`PlayerPedId()`). --- @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,
---@return boolean, boolean|nil Returns `true` and `true` if the Ped is a big dog,
--- `true` and `false` if it's a small dog, --- `true` and `false` if it's a small dog,
--- or `false` and `nil` if it's not a dog. --- or `false` and `nil` if it's not a dog.
--- ---
@@ -124,27 +153,24 @@ if not isServer() then
--- ``` --- ```
function isDog(ped) function isDog(ped)
local PedModel = GetEntityModel(ped or PlayerPedId()) local PedModel = GetEntityModel(ped or PlayerPedId())
for k, v in pairs(AnimalPeds.BigDogs) do for modelHash, _ in pairs(AnimalPeds.BigDogs) do
if PedModel == k then if PedModel == modelHash then
return true, true return true, true
end end
end end
for modelHash, _ in pairs(AnimalPeds.SmallDogs) do
for k, v in pairs(AnimalPeds.SmallDogs) do if PedModel == modelHash then
if PedModel == k then
return true, false return true, false
end end
end end
return false, nil return false, nil
end 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 --- Iterates through every category in AnimalPeds and collects 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.
--- ---
---@return table table A table containing all animal model hashes. --- @return table table A table containing all animal model hashes.
--- ---
---@usage ---@usage
--- ```lua --- ```lua
@@ -154,289 +180,83 @@ if not isServer() then
--- end --- end
--- ``` --- ```
function getAnimalModels() function getAnimalModels()
local animalTable = {} local animalModels = {}
for k in pairs(AnimalPeds) do for _, animalCategory in pairs(AnimalPeds) do
for v in pairs(AnimalPeds[k]) do for modelHash, _ in pairs(animalCategory) do
animalTable[#animalTable+1] = v table.insert(animalModels, modelHash)
end end
end end
return animalTable return animalModels
end end
end end
-------------------------------------------------------------
-- Animal Models Data
-------------------------------------------------------------
-- Define the animal models and their associated animations.
AnimalPeds = { AnimalPeds = {
BigDogs = { 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_chop`] = { [`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" },
deathAnim = "dead_right", deathDict = "creatures@chop@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@chop@getup", [`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" },
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" },
[`a_c_k9`] = { [`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" },
deathAnim = "dead_right", deathDict = "creatures@chop@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@chop@getup", [`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" },
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" },
[`a_c_husky`] = { [`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" },
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", [`bernard`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
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" },
[`a_c_retriever`] = { [`dane`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", [`abdog`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
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" },
[`a_c_shepherd`] = { [`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" },
deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", [`chowchow`] = { deathAnim = "dead_right", deathDict = "creatures@rottweiler@move", exitAnim = "getup_r", exitDict = "creatures@rottweiler@getup", sitAnim = "sit", sitDict = "creatures@rottweiler@in_vehicle@std_car" },
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_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 = { 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" },
[`a_c_poodle`] = { [`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" },
deathAnim = "dead_right", deathDict = "creatures@pug@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@pug@getup", [`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" },
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-chihuahua`] = { [`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" },
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 = { 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" },
[`bshorthair`] = { [`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" },
deathAnim = "dead_right", deathDict = "creatures@cat@move", [`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" },
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 = { 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" },
[`ft-raccoon`] = { [`a_c_hen`] = { deathAnim = "dead_right", deathDict = "creatures@hen@move", exitAnim = "getup_r", exitDict = "creatures@hen@getup" },
deathAnim = "dead_right", deathDict = "creatures@cat@move", [`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" },
exitAnim = "getup_r", exitDict = "creatures@cat@getup", [`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" },
sitAnim = "base", sitDict = "creatures@cat@amb@world_cat_sleeping_ledge@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_hen`] = { [`a_c_boar`] = { deathAnim = "dead_right", deathDict = "creatures@boar@move", exitAnim = "getup_r", exitDict = "creatures@boar@getup" },
deathAnim = "dead_right", deathDict = "creatures@hen@move", [`a_c_boar_02`] = { deathAnim = "dead_right", deathDict = "creatures@boar@move", exitAnim = "getup_r", exitDict = "creatures@boar@getup" },
exitAnim = "getup_r", exitDict = "creatures@hen@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_rabbit_01`] = { [`a_c_sharkhammer`] = { deathAnim = "dead_right", deathDict = "creatures@sharkhammer@move", exitAnim = "getup_r", exitDict = "creatures@sharkhammer@getup" },
deathAnim = "dead_right", deathDict = "creatures@rabbit@move", [`a_c_sharktiger`] = { deathAnim = "dead_right", deathDict = "creatures@sharktiger@move", exitAnim = "getup_r", exitDict = "creatures@sharktiger@getup" },
exitAnim = "getup_r", exitDict = "creatures@rabbit@getup", [`a_c_crow`] = { deathAnim = "dead_down", deathDict = "creatures@crow@move", exitAnim = "nill", exitDict = "creatures@pug@move" },
sitAnim = "idle_c", sitDict = "creatures@coyote@amb@world_coyote_howl@base" [`a_c_pigeon`] = { deathAnim = "dead_down", deathDict = "creatures@pigeon@move", exitAnim = "nill", exitDict = "creatures@pug@move" },
},
[`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
},
}, },
Monekys = { Monekys = {
[`ft-chimpanzee`] = { [`ft-chimpanzee`] = { deathAnim = "dead", deathDict = "dead_a", exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0" },
deathAnim = "dead", deathDict = "dead_a", [`a_c_chimp`] = { deathAnim = "dead", deathDict = "dead_a", exitAnim = "get_up@sat_on_floor@to_stand", exitDict = "getup_0" },
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" },
[`a_c_chimp`] = { [`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" },
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"
},
} }
} }

View File

@@ -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. --- @param item string The name of the item.
--- It checks which inventory system is active and registers the usable item accordingly. --- @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. --- @usage
---@param funct function The function to execute when the item is used.
---
---@usage
--- ```lua --- ```lua
--- createUseableItem("health_potion", function(source) --- createUseableItem("health_potion", function(source)
--- -- Code to consume the health potion --- -- Code to consume the health potion
@@ -23,23 +39,24 @@ function createUseableItem(item, funct)
elseif isStarted(QBXExport) then elseif isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qbx_core", item) debugPrint("^6Bridge^7: ^2Registering item as ^3Usable^2 with ^7qbx_core", item)
exports[QBXExport]:CreateUseableItem(item, funct) exports[QBXExport]:CreateUseableItem(item, funct)
else
print("^4ERROR^7: No supported framework detected for registering usable item: ^3"..item.."^7")
end end
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. --- @param item string The item name.
--- It supports multiple inventory systems such as OXInv, QSInv, CoreInv, OrigenInv, QBInv, and CodeMInv. --- @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. --- @usage
---@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
--- ```lua --- ```lua
--- local imageLink = invImg("health_potion") --- local imageLink = invImg("health_potion")
--- if imageLink ~= "" then --- if imageLink ~= "" then print(imageLink) end
--- print(imageLink)
--- end
--- ``` --- ```
function invImg(item) function invImg(item)
local imgLink = "" local imgLink = ""
@@ -50,33 +67,39 @@ function invImg(item)
imgLink = "nui://"..QSInv.."/html/images/"..(Items[item].image or "") imgLink = "nui://"..QSInv.."/html/images/"..(Items[item].image or "")
elseif isStarted(CoreInv) then elseif isStarted(CoreInv) then
imgLink = "nui://"..CoreInv.."/html/img/"..(Items[item].image or "") 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 elseif isStarted(OrigenInv) then
imgLink = "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "") imgLink = "nui://"..OrigenInv.."/html/img/"..(Items[item].image or "")
elseif isStarted(QBInv) then elseif isStarted(QBInv) then
imgLink = "nui://"..QBInv.."/html/images/"..(Items[item].image or "") imgLink = "nui://"..QBInv.."/html/images/"..(Items[item].image or "")
elseif isStarted(CodeMInv) then
imgLink = "nui://"..CodeMInv.."/html/itemimages/"..(Items[item].image or "")
else 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
end end
return imgLink return imgLink
end end
-------------------------------------------------------------
-- Adding and Removing Items
-------------------------------------------------------------
--- Adds an item to a player's inventory. --- 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 item string The item name.
---@param amount number The quantity of the item to add. --- @param amount number The quantity to add.
---@param info table|nil Additional information or metadata for the item. --- @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 --- ```lua
--- addItem("health_potion", 2, { quality = "high" }) --- addItem("health_potion", 2, { quality = "high" })
--- ``` --- ```
function addItem(item, amount, info, src) 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 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 if src then
TriggerEvent(getScript()..":server:toggleItem", true, item, amount, src, info) TriggerEvent(getScript()..":server:toggleItem", true, item, amount, src, info)
else else
@@ -86,17 +109,23 @@ end
--- Removes an item from a player's inventory. --- 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 item string The item name.
---@param amount number The quantity of the item to remove. --- @param amount number The quantity to remove.
--- @param src number|nil Optional player source.
--- @param slot number|nil Optional inventory slot.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- removeItem("health_potion", 1) --- removeItem("health_potion", 1)
--- ``` --- ```
function removeItem(item, amount, src, slot) 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 if src then
TriggerEvent(getScript()..":server:toggleItem", false, item, amount, src, nil, slot) TriggerEvent(getScript()..":server:toggleItem", false, item, amount, src, nil, slot)
else else
@@ -104,161 +133,188 @@ function removeItem(item, amount, src, slot)
end end
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. --- This function validates the item, then calls the appropriate export functions based on the active inventory system.
--- It supports multiple inventory systems and includes exploit protection to prevent duplication. --- It also includes exploit protection via the dupeWarn function.
--- ---
---@param give boolean Indicates whether to add (`true`) or remove (`false`) the item. --- @param give boolean True to add the item, false to remove.
---@param item string The name of the item to toggle. --- @param item string The item name.
---@param amount number The quantity of the item to toggle. --- @param amount number The quantity.
---@param newsrc number|nil The source ID of the player. If `nil`, it defaults to the event source. --- @param newsrc number|nil The player source; defaults to event source.
---@param info table|nil Additional information or metadata for the item. --- @param info table|nil Additional metadata.
--- @param slot number|nil Optional inventory slot.
--- ---
---@usage --- @usage
--- ```lua --- ```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) 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 src = newsrc or source
local addremove = (tostring(give) == "true" and "addItem" or "removeItem") local action = (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 or 1
local remamount = (amount and amount or 1)
if item == nil then return end 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 give == 0 or give == false then
if hasItem(item, amount and amount or 1, src) then -- Check if the player has the item if not hasItem(item, amount or 1, src) then
if isStarted(OXInv) then dupeWarn(src, item, amount)
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")
elseif isStarted(CoreInv) then else
if isStarted(QBExport) then if isStarted(OXInv) then invName = OXInv
Core.Functions.GetPlayer(src).Functions.RemoveItem(item, amount, nil) exports[OXInv]:RemoveItem(src, item, remamount, 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")
elseif isStarted(OrigenInv) then elseif isStarted(QSInv) then invName = QSInv
local success = exports[OrigenInv]:RemoveItem(src, item, amount) exports[QSInv]:RemoveItem(src, item, remamount)
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(CodeMInv) then elseif isStarted(CoreInv) then invName = CoreInv
local success = exports[CodeMInv]:RemoveItem(src, item, amount) exports[CoreInv]:removeItem(src, item, remamount)
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(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 while remamount > 0 do
if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then if Core.Functions.GetPlayer(src).Functions.RemoveItem(item, 1, slot) then
remamount -= 1 remamount -= 1
else else
print("^1Error removing "..data.item.." Amount left to remove: "..remamount.."^7") print("^1Error removing "..item.." Amount left: "..remamount)
break break
end end
end end
if Config.Crafting.showItemBox then 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 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 invName = PSInv
elseif isStarted(PSInv) then
while remamount > 0 do 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 remamount -= 1
else else
print("^1Error removing "..data.item.." Amount left to remove: "..remamount.."^7") print("^1Error removing "..item.." Amount left: "..remamount)
break break
end end
end end
if Config.Crafting.showItemBox then 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 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 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 end
else else
local amount = amount and amount or 1 local amountToAdd = amount or 1
if isStarted(OXInv) then if isStarted(OXInv) then invName = OXInv
local success = exports[OXInv]:AddItem(src, item, amount or 1, info) exports[OXInv]:AddItem(src, item, amountToAdd, info, slot)
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)) 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 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 elseif isStarted(PSInv) then invName = PSInv
local success = exports[QSInv]:AddItem(src, item, amount) if Core.Functions.GetPlayer(src).Functions.AddItem(item, amountToAdd, nil, info) then
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
if Config.Crafting.showItemBox 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
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 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 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. --- @param src number The player's source ID.
--- It logs the incident and kicks the player if `debugMode` is not enabled. --- @param item string The item name.
---
--- @param src number The source ID of the player attempting the exploit.
--- @param item string The name of the item being exploited.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- dupeWarn(playerId, "health_potion") --- dupeWarn(playerId, "health_potion")
--- ``` --- ```
function dupeWarn(src, item) function dupeWarn(src, item, amount)
local name = getPlayer(src).name 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 if not debugMode then
DropPlayer(src, name.."("..tostring(src)..") Kicked for suspected duplicating items: "..item) DropPlayer(src, name.."("..tostring(src)..") Kicked for suspected duplicating items: "..item)
end end
print("^5DupeWarn^7: "..name.."(^1"..tostring(src).."^7) ^2Dropped from server - exploit protection detected an item not being found in players inventory^7") print("^5DupeWarn^7: "..name.."(^1"..tostring(src).."^7) ^2Dropped from server - exploit protection detected an item not being found in players inventory^7")
end 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, --- If durability reaches zero or below, the tool is removed and a break sound is played.
--- it removes the tool from the player's inventory and plays a breaking sound.
--- ---
--- @param data table A table containing data about the tool being used. --- @param data table Contains:
--- - **item** (`string`): The name of the tool item. --- - item (string): The tool's name.
--- - **damage** (`number`): The amount of durability damage to apply. --- - damage (number): The damage % to apply.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
@@ -277,19 +333,20 @@ function breakTool(data) -- WIP
end end
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. --- @param item string The item name.
--- @return number|nil The durability of the item. Returns `nil` if not found. --- @return number|nil number The durability, or nil if not found.
--- @return number|nil The slot number of the item. Returns `nil` if not found. --- @return number|nil number The slot number, or nil if not found.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- local durability, slot = getDurability("drill") --- local durability, slot = getDurability("drill")
--- if durability then --- if durability then
--- print("Durability:", durability) --- print("Durability:", durability)
--- print("Slot:", slot)
--- end --- end
--- ``` --- ```
function getDurability(item) function getDurability(item)
@@ -297,7 +354,7 @@ function getDurability(item)
local durability = nil local durability = nil
if isStarted(QBInv) or isStarted(PSInv) then if isStarted(QBInv) or isStarted(PSInv) then
local itemcheck = Core.Functions.GetPlayerData().items 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.name == item then
if v.slot <= lowestSlot then if v.slot <= lowestSlot then
lowestSlot = v.slot lowestSlot = v.slot
@@ -309,47 +366,86 @@ function getDurability(item)
if isStarted(OXInv) then if isStarted(OXInv) then
local itemcheck = exports[OXInv]:Search('slots', item) 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 if v.slot <= lowestSlot then
debugPrint(v.slot, itemcheck[k].metadata.durability) debugPrint(v.slot, itemcheck[k].metadata.durability)
lowestSlot = v.slot lowestSlot = v.slot
durability = itemcheck[k].metadata.durability durability = v.metadata.durability
end end
end end
end end
if isStarted(QSInv) then if isStarted(QSInv) then
local itemcheck = exports[QSInv]:getUserInventory() 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 if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot 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 end
end end
if isStarted(OrigenInv) then if isStarted(OrigenInv) then
local itemcheck = exports[OrigenInv]:getPlayerInventory() 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 if v.name == item and v.slot <= lowestSlot then
lowestSlot = v.slot lowestSlot = v.slot
durability = itemcheck[k].metadata.durability durability = v.metadata.durability
end end
end 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 return durability, lowestSlot
end 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. --- @param data table Contains:
--- - **item** (`string`): The name of the item. --- - item (string): The item name.
--- - **slot** (`number`): The slot number of the item in the inventory. --- - slot (number): The inventory slot.
--- - **metadata** (`table`): The metadata to set for the item. --- - metadata (table): The metadata to set.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- TriggerServerEvent("script:server:setMetaData", { item = "drill", slot = 5, metadata = { durability = 80 } }) --- 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].info = data.metadata
Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability Player.PlayerData.items[data.slot].description = "HP : "..data.metadata.durability
Player.Functions.SetInventory(Player.PlayerData.items) Player.Functions.SetInventory(Player.PlayerData.items)
end
if isStarted(OXInv) then elseif isStarted(OXInv) then
exports[OXInv]:SetMetadata(source, data.slot, data.metadata) exports[OXInv]:SetDurability(src, data.slot, data.metadata.durability)
end
if isStarted(QSInv) then elseif isStarted(QSInv) then
exports[QSInv]:SetItemMetadata(source, data.slot, data.metadata) exports[QSInv]:SetItemMetadata(src, data.slot, data.metadata)
end
if isStarted(OrigenInv) then elseif isStarted(CoreInv) then
local item = exports[OrigenInv]:GetItemBySlot(source, data.slot) exports[CoreInv]:setMetadata(src, data.slot, data.metadata)
if item then
exports[OrigenInv]:SetItemData(source, item.name, "durability", data.metadata.durability) elseif isStarted(CodeMInv) then
end exports[CodeMInv]:SetItemMetadata(src, data.slot, data.metadata)
elseif isStarted(OrigenInv) then
exports[OrigenInv]:setMetadata(src, data.slot, data.metadata)
end end
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. --- Checks if the item qualifies for a reward, removes the item, then calculates a random reward based on rarity.
--- It supports multiple inventory systems and provides detailed feedback on item availability.
--- ---
---@param items string|table A single item name or a table of item names with their required amounts. --- @param itemName string The item name to check.
---@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.
--- ---
---@usage ---@usage
--- ```lua --- ```lua
--- getRandomReward("gold_ring") --- getRandomReward("gold_ring")
--- ``` --- ```
function getRandomReward(itemName) -- Intended for job scripts function getRandomReward(itemName)
if Config.Rewards.RewardPool then if Config.Rewards.RewardPool then
local reward = false 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 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 end
if reward then if reward then
removeItem(itemName, 1) removeItem(itemName, 1)
local totalRarity = 0 local totalRarity = 0
for i=1, #Config.Rewards.RewardPool do for i = 1, #Config.Rewards.RewardPool do
totalRarity += Config.Rewards.RewardPool[i].rarity totalRarity += Config.Rewards.RewardPool[i].rarity
end 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) 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 local currentRarity = 0
for i=1, #Config.Rewards.RewardPool do for i = 1, #Config.Rewards.RewardPool do
currentRarity += Config.Rewards.RewardPool[i].rarity currentRarity += Config.Rewards.RewardPool[i].rarity
if randomNum <= currentRarity then if randomNum <= currentRarity then
debugPrint("^6Bridge^7: ^3getRandomReward^7: ^2Selected toy ^7'^6"..Config.Rewards.RewardPool[i].item.."^7'") 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
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. --- Calculates the current total weight in the player's inventory and determines whether adding the new items would exceed capacity.
--- It considers the weight of each item and the player's current inventory weight.
--- ---
---@param itemTable table A table where keys are item names and values are the quantities to check. --- @param itemTable table A table where keys are item names and values are required quantities.
---@param src number The source ID of the player. --- @param src number The player's source ID.
---@return table A table where keys are item names and values are booleans indicating if the player can carry the specified quantity. --- @return table A table mapping each item to a boolean indicating if it can be carried.
--- ---
---@usage --- @usage
--- ```lua --- local carryCheck = canCarry({ ["health_potion"] = 2, ["mana_potion"] = 3 }, playerId)
--- local canCarry = canCarry({ ["health_potion"] = 2, ["mana_potion"] = 3 }, playerId) --- if carryCheck["health_potion"] and carryCheck["mana_potion"] then
--- if canCarry["health_potion"] and canCarry["mana_potion"] then --- -- Player can carry items.
--- -- Proceed with adding items
--- else --- else
--- -- Inform the player they can't carry all items --- -- Notify player.
--- end --- end
--- ```
function canCarry(itemTable, src) function canCarry(itemTable, src)
local resultTable = {} local resultTable = {}
if src then if src then
@@ -565,16 +552,28 @@ function canCarry(itemTable, src)
elseif isStarted(QSInv) then elseif isStarted(QSInv) then
for k, v in pairs(itemTable) do for k, v in pairs(itemTable) do
resultTable[k] = exports[OXInv]:CanCarryItem(src, k, v) resultTable[k] = exports[QSInv]:CanCarryItem(src, k, v)
end end
elseif isStarted(CoreInv) then elseif isStarted(CoreInv) then
--??
elseif isStarted(CodeMInv) then
for k, v in pairs(itemTable) do for k, v in pairs(itemTable) do
local weight = Items[k].weight resultTable[k] = exports[CoreInv]:canCarry(src, k, v)
resultTable[k] = exports[CodeMInv]:CanCarryItem(src, weight, 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 end
elseif isStarted(OrigenInv) then elseif isStarted(OrigenInv) then
@@ -583,21 +582,18 @@ function canCarry(itemTable, src)
end end
elseif isStarted(QBInv) or isStarted(PSInv) then elseif isStarted(QBInv) or isStarted(PSInv) then
local Player = Core.Functions.GetPlayer(src) local items = getPlayerInv(src)
local items = Player.PlayerData.items local totalWeight = 0
local weight, totalWeight = 0, 0
if not items then return false end if not items then return false end
for _, item in pairs(items) do weight += item.weight * item.amount end for _, item in pairs(items) do
totalWeight += (item.weight * item.amount)
totalWeight = tonumber(weight) end
for k, v in pairs(itemTable) do for k, v in pairs(itemTable) do
local itemInfo = Items[k] local itemInfo = Items[k]
if not itemInfo and not Player.Offline then if not itemInfo and not Player.Offline then
triggerNotify(nil, 'Item does not exist', 'error', src)
resultTable[k] = true resultTable[k] = true
else else
resultTable[k] = (totalWeight + (Items[k]['weight'] * v)) <= InventoryWeight resultTable[k] = (totalWeight + (itemInfo.weight * v)) <= InventoryWeight
end end
end end
end end

View File

@@ -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 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. --- Iterates through the specified role's grades in the Jobs or Gangs table and returns
--- It identifies which grades are marked as bosses (`isboss`) or have bank authorization (`bankAuth`). --- a table mapping the role to the lowest grade number that qualifies as a boss (isboss or bankAuth).
--- The function returns a table where each role maps to the lowest grade number that qualifies as a boss.
--- ---
---@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 --- ```lua
--- local bosses = makeBossRoles("police") --- local bosses = makeBossRoles("police")
--- if bosses["police"] then --- if bosses["police"] then
@@ -31,25 +46,28 @@ function makeBossRoles(role)
return boss return boss
end 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, --- Verifies whether the player possesses the specified role. If the role is defined in the Jobs table,
--- whether they are currently on duty. It provides a notification if the player fails these checks. --- 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 --- ```lua
--- if jobCheck("mechanic") then --- if jobCheck("mechanic") then
--- -- Allow access to mechanic-related features --- -- Allow mechanic features.
--- else --- else
--- -- Deny access or notify the player --- -- Deny access.
--- end --- end
--- ``` --- ```
function jobCheck(job) function jobCheck(job)
canDo = true local canDo = true
if Jobs[job] then if Jobs[job] then
if not hasJob(job) or not onDuty then if not hasJob(job) or not onDuty then
triggerNotify(nil, Loc[Config.Lan].error["not_clockedin"]) triggerNotify(nil, Loc[Config.Lan].error["not_clockedin"])
@@ -66,14 +84,12 @@ end
--- Toggles the player's duty status. --- Toggles the player's duty status.
--- ---
--- This function switches the player's duty state between on-duty and off-duty. --- Switches the player's duty state between on-duty and off-duty. If using QBcore,
--- It integrates with QBcore's duty system if available; otherwise, it manually toggles the `onDuty` variable --- it triggers the appropriate server event. Otherwise, it manually toggles the onDuty variable and notifies the player.
--- and sends a notification to the player about their new duty status.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- toggleDuty() --- toggleDuty() -- Player receives a notification of their new duty status.
--- -- Player will receive a notification indicating their new duty status
--- ``` --- ```
function toggleDuty() function toggleDuty()
if isStarted(QBExport) or isStarted(QBXExport) then if isStarted(QBExport) or isStarted(QBXExport) then
@@ -88,22 +104,24 @@ function toggleDuty()
end end
end end
-------------------------------------------------------------
-- Interaction Functions
-------------------------------------------------------------
--- Initiates the hand-washing action for the player. --- Initiates the hand-washing action for the player.
--- ---
--- This function triggers an animation and a progress bar to simulate the player washing their hands. --- Triggers an animation and a progress bar to simulate hand washing at the specified coordinates.
--- Upon completion, it sends a success notification. If the action is canceled, it notifies the player of the cancellation. --- 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. --- @param data table A table containing:
--- - **coords** (`vector3`): The position where the hand-washing animation and camera are focused. --- - coords (vector3): The location where the hand-washing action occurs.
--- ---
---@return void --- @usage
---
---@usage
--- ```lua --- ```lua
--- washHands({ coords = vector3(200.0, 300.0, 40.0) }) --- 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) lookEnt(data.coords)
local cam = createTempCam(ped, data.coords) local cam = createTempCam(ped, data.coords)
if progressBar({ if progressBar({
@@ -118,22 +136,21 @@ function washHands(data) local ped = PlayerPedId()
}) then }) then
triggerNotify(nil, Loc[Config.Lan].success["washed_hands"], "success") triggerNotify(nil, Loc[Config.Lan].success["washed_hands"], "success")
else else
triggerNotify(nil, Loc[Config.Lan].error["cancel"], 'error') triggerNotify(nil, Loc[Config.Lan].error["cancel"], "error")
end end
ClearPedTasks(ped) ClearPedTasks(ped)
end end
--- Handles the player's interaction with a toilet or urinal. --- 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. --- Manages animations and progress bars for using a urinal or a toilet. If the action is successful,
--- Depending on whether the interaction is with a urinal (`data.urinal`), it plays the appropriate animation --- it triggers the appropriate server event (urinal usage) or notifies the player if cancelled.
--- and triggers server events upon successful completion. If the action is canceled, it notifies the player.
--- ---
---@param data table A table containing data about the toilet interaction. --- @param data table A table containing:
--- - **urinal** (`boolean`): Indicates whether the interaction is with a urinal (`true`) or a toilet (`false`). --- - urinal (boolean): `true if using a urinal; false for a toilet.`
--- - **sitcoords** (`vector4`): The coordinates and heading for the seating animation when using a toilet. --- - sitcoords (vector4): `Coordinates and heading for seating when using a toilet.`
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- useToilet({ urinal = true }) --- useToilet({ urinal = true })
--- -- Player uses a urinal with corresponding animations and notifications --- -- Player uses a urinal with corresponding animations and notifications
@@ -154,7 +171,7 @@ function useToilet(data)
TriggerServerEvent(getScript().."server:Urinal") TriggerServerEvent(getScript().."server:Urinal")
else else
lockInv(false) lockInv(false)
triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error') triggerNotify(nil, Loc[Config.Lan].error["cancelled"], "error")
end end
else else
TaskStartScenarioAtPosition(PlayerPedId(), "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER", data.sitcoords.x, data.sitcoords.y, data.sitcoords.z, data.sitcoords[4], 0, 1, true) 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()) ClearPedTasks(PlayerPedId())
else else
lockInv(false) lockInv(false)
triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error') triggerNotify(nil, Loc[Config.Lan].error["cancelled"], "error")
end end
end end
end end
--- Teleports the player to specified coordinates with a fade effect. --- 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`), --- Fades the screen out, moves the player to the target coordinates, sets the player's heading,
--- sets the player's heading, and then fades the screen back in. It's commonly used for door interactions --- then fades the screen back in. Commonly used for door interactions or teleportation points.
--- or teleportation points within the game.
--- ---
---@param data table A table containing teleportation data. --- @param data table A table containing:
--- - **telecoords** (`vector4`): The target coordinates and heading for the teleportation. --- - telecoords (vector4): The target coordinates and heading.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- useDoor({ telecoords = vector4(215.76, -810.12, 29.73, 90.0) }) --- 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) function useDoor(data)
DoScreenFadeOut(500) DoScreenFadeOut(500)

View File

@@ -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) function GetPlayer(source)
if isStarted(QBExport) then if isStarted(QBExport) then
debugPrint("^6Bridge^7: ^3GetPlayer^7() QBExport") debugPrint("^6Bridge^7: ^3GetPlayer^7() QBExport")
@@ -16,9 +36,24 @@ function GetPlayer(source)
return nil return nil
end 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) 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) debugPrint("^6Bridge^7: ^3GetMetadata^7() calling server: "..key)
return triggerCallback(getScript()..":server:GetMetadata", key) return triggerCallback(getScript()..":server:GetMetadata", key)
else else
@@ -36,56 +71,65 @@ function GetMetadata(player, key)
return nil return nil
end end
-- Register a server callback for retrieving metadata.
createCallback(getScript()..":server:GetMetadata", function(source, key) 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 player = GetPlayer(source)
local Metadata = {}
if not player then if not player then
print("Error getting metadata") print("Error getting metadata: player not found for source "..tostring(source))
return return
end end
if type(key) == "table" then if type(key) == "table" then
local Metadata = {}
for _, k in ipairs(key) do for _, k in ipairs(key) do
Metadata[k] = GetMetadata(player, k).k Metadata[k] = GetMetadata(player, k)
end end
return Metadata
elseif type(key) == "string" then elseif type(key) == "string" then
return GetMetadata(player, key) return GetMetadata(player, key)
end end
jsonPrint(Metadata)
return Metadata
end) 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) function SetMetadata(player, key, value)
--if player == nil then -- This would be called client side debugPrint("^6Bridge^7: ^3SetMetadata^7() setting metadata for key: "..key)
-- debugPrint("^6Bridge^7: ^3SetMetadata^7() calling server") if isStarted(QBExport) or isStarted(QBXExport) then
-- triggerCallback(getScript()..":server:SetMetadata", { key, value }) debugPrint("^6Bridge^7: ^3SetMetadata^7() using QBExport/QBXExport")
-- else player.Functions.SetMetaData(key, value)
debugPrint("^6Bridge^7: ^3SetMetadata^7() setting metadata") elseif isStarted(ESXExport) then
if isStarted(QBExport) or isStarted(QBXExport) then debugPrint("^6Bridge^7: ^3SetMetadata^7() using ESXExport")
debugPrint("^6Bridge^7: ^3SetMetadata^7() QBExport or QBXExport") player.setMeta(key, value)
player.Functions.SetMetaData(key, value) elseif isStarted(OXCoreExport) then
elseif isStarted(ESXExport) then debugPrint("^6Bridge^7: ^3SetMetadata^7() using OXCoreExport")
debugPrint("^6Bridge^7: ^3SetMetadata^7() ESXExport") player.set(key, value)
player.setMeta(key, value) end
elseif isStarted(OXCoreExport) then
debugPrint("^6Bridge^7: ^3SetMetadata^7() OXCoreExport")
player.set(key, value)
end
--end
end end
-- Register a server callback for setting metadata.
createCallback(getScript()..":server:SetMetadata", function(source, key, value) 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) local player = GetPlayer(source)
--jsonPrint(player)
--[[if not player then --[[if not player then
print("Error getting metadata") print("Error setting metadata: player not found for source "..tostring(source))
return false return false
end]] end]]
print("i did it")
SetMetadata(player, key, value) SetMetadata(player, key, value)
print("Metadata set successfully.", key)
return true return true
end) end)

View File

@@ -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. --- Displays notifications to the player using the configured notification system.
--- ---
--- This function supports multiple notification systems based on the `Config.System.Notify` setting. --- Supports multiple notification systems based on Config.System.Notify. Can be triggered from both
--- It can be triggered from both client-side and server-side scripts. Depending on the configuration, --- client and server contexts.
--- it utilizes different exports or events to display the notification.
--- ---
---@param title string|nil The title of the notification. Optional, used by certain notification systems. --- @param title string|nil The notification title (optional for some systems).
---@param message string The main message content of the notification. --- @param message string The main message content.
---@param type string The type/category of the notification (e.g., "success", "error", "info"). --- @param type string The notification type ("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 src number|nil Optional server ID; if provided, the notification is sent to that player.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- -- Client-side usage without specifying a player (shows to the current player) --- -- Client-side usage without specifying a player (shows to the current player)
--- triggerNotify("Success", "You have completed the task!", "success") --- triggerNotify("Success", "You have completed the task!", "success")
@@ -21,52 +31,72 @@
--- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId) --- triggerNotify("Alert", "You have been warned for misconduct.", "error", playerId)
--- ``` --- ```
function triggerNotify(title, message, type, src) function triggerNotify(title, message, type, src)
if Config.System.Notify == "okok" then if Config.System.Notify == "okok" then
if not src then TriggerEvent('okokNotify:Alert', title, message, 6000, type) if not src then
else TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type) end TriggerEvent('okokNotify:Alert', title, message, 6000, type)
elseif Config.System.Notify == "qb" then else
if not src then TriggerEvent("QBCore:Notify", message, type) TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type)
else TriggerClientEvent("QBCore:Notify", src, message, type) end end
elseif Config.System.Notify == "ox" then elseif Config.System.Notify == "qb" then
if not src then TriggerEvent('ox_lib:notify', {title = title, description = message, type = type or "success"}) if not src then
else TriggerClientEvent('ox_lib:notify', src, { type = type or "success", title = title, description = message }) end TriggerEvent("QBCore:Notify", message, type)
elseif Config.System.Notify == "gta" then else
if not src then TriggerEvent(getScript()..":DisplayGTANotify", title, message) TriggerClientEvent("QBCore:Notify", src, message, type)
else TriggerClientEvent(getScript()..":DisplayGTANotify", src, title, message) end 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 elseif Config.System.Notify == "esx" then
if not src then exports["esx_notify"]:Notify(type, 4000, message) if not src then
else TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, title, message) end exports["esx_notify"]:Notify(type, 4000, message)
end else
TriggerClientEvent(getScript()..":DisplayESXNotify", src, type, message)
end
end
end end
-------------------------------------------------------------
-- ESX Notifications
-------------------------------------------------------------
--- Registers a server-side event to display ESX notifications to clients. --- 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 type string The notification type.
--- @param title string The title of the notification. --- @param title string The notification title.
--- @param text string The main message content of the notification. --- @param text string The notification message.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Server-side event trigger --- TriggerClientEvent(getScript()..":DisplayESXNotify", playerId, "success", "New achievement unlocked!")
--- TriggerClientEvent(getScript()..":DisplayESXNotify", playerId, "success", "Achievement Unlocked", "You have unlocked a new achievement!")
--- ``` --- ```
RegisterNetEvent(getScript()..":DisplayESXNotify", function(type, title, text) RegisterNetEvent(getScript()..":DisplayESXNotify", function(type, text)
exports["esx_notify"]:Notify(type, 4000, text) exports["esx_notify"]:Notify(type, 4000, text)
end) 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. --- Selects an appropriate icon based on the current script (if applicable) and renders the notification.
--- It supports specific scenarios by assigning different icons based on the script name.
--- ---
---@param title string The title or identifier for the notification, used to select the appropriate icon. --- @param title string The notification title/identifier (used to select an icon).
---@param text string The main message content of the notification. --- @param text string The notification message.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- -- Client-side event trigger
--- TriggerEvent(getScript()..":DisplayGTANotify", "taxiname", "Taxi service has arrived.") --- TriggerEvent(getScript()..":DisplayGTANotify", "taxiname", "Taxi service has arrived.")
--- ``` --- ```
RegisterNetEvent(getScript()..":DisplayGTANotify", function(title, text) RegisterNetEvent(getScript()..":DisplayGTANotify", function(title, text)
@@ -81,8 +111,13 @@ RegisterNetEvent(getScript()..":DisplayGTANotify", function(title, text)
[Loc[Config.Lan].notify["heliname"]] = "CHAR_BOATSITE2", [Loc[Config.Lan].notify["heliname"]] = "CHAR_BOATSITE2",
} }
end end
BeginTextCommandThefeedPost("STRING") BeginTextCommandThefeedPost("STRING")
AddTextComponentSubstringKeyboardDisplay(text) 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) EndTextCommandThefeedPostTicker(true, false)
end) end)

166
shared/phones.lua Normal file
View File

@@ -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("%<br>", "\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("%<br>", "\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)

View File

@@ -1,67 +1,52 @@
--- Locks or unlocks the player's inventory. --[[
--- Player Utility & Server Event Handlers Module
--- 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. This module provides utility functions for:
--- • Locking/unlocking the player's inventory.
--- @param toggle boolean `true` to lock the inventory, `false` to unlock. • Instantly turning or gradually turning the player to face a target.
--- • Handling player needs (thirst and hunger) via server events.
--- @usage • Charging/funding players (money removal/addition).
--- ```lua • Processing item consumption and applying effects.
--- -- Lock the player's inventory • Checking player job/gang roles and retrieving player information.
--- lockInv(true) • Getting active players near a coordinate.
--- ]]
--- -- 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
--- 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 --- @param ent number|nil The Ped to turn (defaults to player's Ped if nil).
--- and sets the entity's heading immediately without any animation. --- @param ent2 number|vector3|nil The target entity or coordinates to face.
---
--- @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.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Make the player instantly face a specific location
--- instantLookEnt(nil, vector3(200.0, 300.0, 40.0)) --- instantLookEnt(nil, vector3(200.0, 300.0, 40.0))
---
--- -- Make one entity face another entity
--- instantLookEnt(ped1, ped2) --- instantLookEnt(ped1, ped2)
--- ``` --- ```
function instantLookEnt(ent, ent2) function instantLookEnt(ent, ent2)
local ent = ent or PlayerPedId() local ped = ent or PlayerPedId()
local p1 = GetEntityCoords(ent, true) local p1 = GetEntityCoords(ped, true)
local p2 = type(ent2):find("vector") and ent2 or GetEntityCoords(ent2, true) local p2 = type(ent2) == "vector3" and ent2 or GetEntityCoords(ent2, true)
local dx = p2.x - p1.x local dx = p2.x - p1.x
local dy = p2.y - p1.y local dy = p2.y - p1.y
local heading = GetHeadingFromVector_2d(dx, dy) local heading = GetHeadingFromVector_2d(dx, dy)
debugPrint("^6Bridge^7: ^1Forced ^2Turning Player to^7: '^6"..formatCoord(p2).."^7'") debugPrint("^6Bridge^7: ^1Forced ^2Turning Player to^7: '^6"..formatCoord(p2).."^7'")
SetEntityHeading(ent, heading) SetEntityHeading(ped, heading)
end 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 --- If the player is not already facing the target (entity or coordinates), a turning animation is triggered.
--- to face the specified entity or coordinates.
--- ---
--- @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 --- @usage
--- ```lua --- ```lua
--- -- Make the player look at a specific location
--- lookEnt(vector3(200.0, 300.0, 40.0)) --- lookEnt(vector3(200.0, 300.0, 40.0))
---
--- -- Make the player look at another entity
--- lookEnt(pedEntity) --- lookEnt(pedEntity)
--- ``` --- ```
function lookEnt(entity) function lookEnt(entity)
@@ -86,15 +71,12 @@ function lookEnt(entity)
end end
end end
--- Server event handler for handling urinal usage. -------------------------------------------------------------
--- -- Server Event Handlers for Needs
--- This event decreases the player's thirst based on a random amount and updates their thirst level. -------------------------------------------------------------
---
--- @usage --- Server event handler for urinal usage.
--- ```lua --- Decreases player's thirst by a random amount.
--- -- Triggered when a player uses a urinal
--- TriggerServerEvent(getScript()..":server:Urinal")
--- ```
RegisterNetEvent(getScript()..":server:Urinal", function() RegisterNetEvent(getScript()..":server:Urinal", function()
local src = source local src = source
local Player = getPlayer(src) local Player = getPlayer(src)
@@ -103,43 +85,27 @@ RegisterNetEvent(getScript()..":server:Urinal", function()
setThirst(src, getPlayer(src).thirst - thirst) setThirst(src, getPlayer(src).thirst - thirst)
end) end)
--- Server event handler for setting player needs. --- Server event handler for setting player needs (thirst or hunger).
---
--- This event updates the player's thirst or hunger based on the provided type and amount.
--- ---
--- @event --- @event
--- @param type string The type of need to set ("thirst" or "hunger"). --- @param type string "thirst" or "hunger".
--- @param amount number The amount to set the need to. --- @param amount number New value to set.
--- RegisterNetEvent(getScript()..":server:setNeed", function(needType, amount)
--- @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)
local src = source local src = source
if type == "thirst" then if needType == "thirst" then
setThirst(src, amount) setThirst(src, amount)
elseif type == "hunger" then elseif needType == "hunger" then
setHunger(src, amount) setHunger(src, amount)
end end
end) end)
--- Sets the player's thirst level. --- Sets the player's thirst level.
--- ---
--- This function updates the player's thirst based on the active inventory system. --- @param src number The player's server ID.
--- --- @param thirst number The new thirst level.
--- @param src number The server ID of the player.
--- @param thirst number The new thirst level to set.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Set a player's thirst to 80
--- setThirst(playerId, 80) --- setThirst(playerId, 80)
--- ``` --- ```
function setThirst(src, thirst) function setThirst(src, thirst)
@@ -154,14 +120,11 @@ end
--- Sets the player's hunger level. --- Sets the player's hunger level.
--- ---
--- This function updates the player's hunger based on the active inventory system. --- @param src number The player's server ID.
--- --- @param hunger number The new hunger level.
--- @param src number The server ID of the player.
--- @param hunger number The new hunger level to set.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Set a player's hunger to 60
--- setHunger(playerId, 60) --- setHunger(playerId, 60)
--- ``` --- ```
function setHunger(src, hunger) function setHunger(src, hunger)
@@ -174,105 +137,103 @@ function setHunger(src, hunger)
end end
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"). --- @param cost number The amount to charge.
--- --- @param type string "cash" or "bank".
--- @event --- @param newsrc number|nil Optional player ID; defaults to event source.
--- @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.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Charge a player $100 in cash
--- chargePlayer(100, "cash", playerId) --- 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 src = newsrc or source
local fundResource = "" local fundResource = ""
if type == "cash" then
if moneyType == "cash" then
if isStarted(OXInv) then fundResource = OXInv if isStarted(OXInv) then fundResource = OXInv
exports[OXInv]:RemoveItem(src, "money", cost) exports[OXInv]:RemoveItem(src, "money", cost)
elseif isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport elseif isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost) Core.Functions.GetPlayer(src).Functions.RemoveMoney("cash", cost)
elseif isStarted(ESXExport) then fundResource = ESXExport elseif isStarted(ESXExport) then fundResource = ESXExport
local Player = ESX.GetPlayerFromId(src) ESX.GetPlayerFromId(src).removeMoney(cost, "")
Player.removeMoney(cost, "")
end end
end elseif moneyType == "bank" then
if type == "bank" then
if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost) Core.Functions.GetPlayer(src).Functions.RemoveMoney("bank", cost)
elseif isStarted(ESXExport) then fundResource = ESXExport elseif isStarted(ESXExport) then fundResource = ESXExport
local Player = ESX.GetPlayerFromId(src) ESX.GetPlayerFromId(src).removeMoney(cost, "")
Player.removeMoney(cost, "")
end end
end end
if fundResource == "" then print("error - check exports.lua")
if fundResource == "" then
print("Cannot charge player - check starter.lua")
else 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
end end
RegisterNetEvent(getScript()..":server:ChargePlayer", chargePlayer) 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"). --- @param fund number The amount to add.
--- --- @param type string "cash" or "bank".
--- @event --- @param newsrc number|nil Optional player ID; defaults to event source.
--- @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.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Add $150 to a player's cash --- fundPlayer(150, "cash", playerId)
--- fundPlayer(playerId, 150, "cash")
---
--- -- Add $300 to the event source's bank account
--- fundPlayer(playerId, 300, "bank")
--- ``` --- ```
function fundPlayer(fund, type, newsrc) function fundPlayer(fund, moneyType, newsrc)
local src = newsrc or source local src = newsrc or source
local fundResource = "" 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) 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) Core.Functions.GetPlayer(src).Functions.AddMoney("cash", fund)
elseif isStarted(ESXExport) then fundResource = ESXExport elseif isStarted(ESXExport) then
local Player = ESX.GetPlayerFromId(src) fundResource = ESXExport
Player.addMoney(fund, "") PlayESX.GetPlayerFromId(src).addMoney(fund, "")
end end
end elseif moneyType == "bank" then
if type == "bank" then if isStarted(QBExport) or isStarted(QBXExport) then
if isStarted(QBExport) or isStarted(QBXExport) then fundResource = QBExport fundResource = QBExport
Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund) Core.Functions.GetPlayer(src).Functions.AddMoney("bank", fund)
elseif isStarted(ESXExport) then fundResource = ESXExport elseif isStarted(ESXExport) then
local Player = ESX.GetPlayerFromId(src) fundResource = ESXExport
Player.addMoney(fund, "") ESX.GetPlayerFromId(src).addMoney(fund, "")
end end
end end
if fundResource == "" then print("error - check exports.lua")
if fundResource == "" then
print("Cannot fund player - check starter.lua")
else else
debugPrint("^6Bridge^7: ^2Funding ^2Player^7: '^2"..fund.."^7'", type, fundResource) debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource)
end end
end end
RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer) RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer)
-------------------------------------------------------------
-- Item Consumption & Effects
-------------------------------------------------------------
--- Handles successful consumption of an item. --- Handles successful consumption of an item.
--- ---
--- This function plays a consumption animation, removes the item from the inventory, --- Plays a consumption animation, removes the item, updates player needs, handles alcohol effects,
--- updates the player's hunger and thirst based on the item consumed, --- and checks for random rewards.
--- handles alcohol effects, and checks for random rewards.
--- ---
--- @param itemName string The name of the item consumed. --- @param itemName string The name of the consumed item.
--- @param type string The type/category of the item (e.g., "alcohol"). --- @param type string The category of the item (e.g., "alcohol").
--- @param data table Additional data (e.g., hunger and thirst values).
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
@@ -283,10 +244,12 @@ RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer)
--- ConsumeSuccess("beer", "alcohol") --- ConsumeSuccess("beer", "alcohol")
--- ``` --- ```
function ConsumeSuccess(itemName, type, data) function ConsumeSuccess(itemName, type, data)
local hunger = data and data.hunger or Items[itemName].hunger or nil local hunger = data and data.hunger or Items[itemName].hunger
local thirst = data and data.thirst or Items[itemName].thirst or nil local thirst = data and data.thirst or Items[itemName].thirst
ExecuteCommand("e c") ExecuteCommand("e c")
removeItem(itemName, 1) removeItem(itemName, 1)
if isStarted(ESXExport) then if isStarted(ESXExport) then
if hunger then if hunger then
TriggerServerEvent(getScript()..":server:setNeed", "hunger", hunger * 10000) 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) TriggerServerEvent(getScript()..":server:setNeed", "thirst", Core.Functions.GetPlayerData().metadata["thirst"] + thirst)
end end
end end
if type == "alcohol" then alcoholCount += 1
if type == "alcohol" then
alcoholCount = (alcoholCount or 0) + 1
if alcoholCount > 1 and alcoholCount < 4 then if alcoholCount > 1 and alcoholCount < 4 then
TriggerEvent("evidence:client:SetStatus", "alcohol", 200) TriggerEvent("evidence:client:SetStatus", "alcohol", 200)
elseif alcoholCount >= 4 then elseif alcoholCount >= 4 then
@@ -310,19 +275,20 @@ function ConsumeSuccess(itemName, type, data)
AlienEffect() AlienEffect()
end end
end end
getRandomReward(itemName) -- check if a reward item should be given
getRandomReward(itemName)
end 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, --- @param job string The job or gang name to check.
--- whether the player's grade meets the required level. It supports multiple inventory systems. --- @param source number|nil Optional player source; if nil, checks current player.
--- --- @param grade number|nil Optional minimum grade level.
--- @param job string The name of the job or gang to check. --- @return boolean, boolean boolean Returns true and duty status if the check passes; false otherwise.
--- @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`.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
@@ -338,7 +304,8 @@ end
--- -- Allow gang leader actions --- -- Allow gang leader actions
--- end --- end
--- ``` --- ```
function hasJob(job, source, grade) local hasJob, duty = false, true function hasJob(job, source, grade)
local hasJobFlag, duty = false, true
if source then if source then
local src = tonumber(source) local src = tonumber(source)
if not src then print(tostring(source).." is not a valid player source") end 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 info = ESX.GetPlayerData(src).job
Wait(100) Wait(100)
end end
if info.name == job then hasJob = true end if info.name == job then hasJobFlag = true end
elseif isStarted(OXCoreExport) then elseif isStarted(OXCoreExport) then
local chunk = assert(load(LoadResourceFile('ox_core', ('imports/%s.lua'):format('server')), ('@@ox_core/%s'):format(file))) local chunk = assert(load(LoadResourceFile('ox_core', ('imports/%s.lua'):format('server')), ('@@ox_core/%s'):format(file)))
chunk() chunk()
local player = Ox.GetPlayer(tonumber(src)) local player = Ox.GetPlayer(src)
for k, v in pairs(player.getGroups()) do for k, v in pairs(player.getGroups()) do
if k == job then hasJob = true end if k == job then hasJobFlag = true end
end end
elseif isStarted(QBXExport) then elseif isStarted(QBXExport) then
local jobinfo = exports[QBXExport]:GetPlayer(src).PlayerData.job 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 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 end
local ganginfo = exports[QBXExport]:GetPlayer(src).PlayerData.gang local ganginfo = exports[QBXExport]:GetPlayer(src).PlayerData.gang
if ganginfo.name == job then hasJob = true if ganginfo.name == job then
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 end
elseif isStarted(QBExport) and not isStarted(QBXExport) then 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) local player = Core.Functions.GetPlayer(src)
if not player then print("Player not found for src: "..src) end if not player then print("Player not found for src: "..src) end
local jobinfo = player.PlayerData.job local jobinfo = player.PlayerData.job
if jobinfo.name == job then hasJob = true if jobinfo.name == job then
duty = Core.Functions.GetPlayer(src).PlayerData.job.onduty hasJobFlag = true
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end duty = player.PlayerData.job.onduty
if grade and not (grade <= jobinfo.grade.level) then hasJobFlag = false end
end end
local ganginfo = Core.Functions.GetPlayer(src).PlayerData.gang local ganginfo = player.PlayerData.gang
if ganginfo.name == job then hasJob = true if ganginfo.name == job then
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 end
else -- support newer qb-core exports else
local jobinfo = exports[QBExport]:GetPlayer(src).PlayerData.job 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 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 end
local ganginfo = exports[QBExport]:GetPlayer(src).PlayerData.gang local ganginfo = exports[QBExport]:GetPlayer(src).PlayerData.gang
if ganginfo.name == job then hasJob = true if ganginfo.name == job then
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 end
end end
else 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
else else
if isStarted(ESXExport) then -- Client-side check.
while not ESX do Wait(10) end if isStarted(ESXExport) and ESX ~= nil then
local info = ESX.GetPlayerData().job local info = ESX.GetPlayerData().job
while not info do while not info do
info = ESX.GetPlayerData().job info = ESX.GetPlayerData().job
Wait(100) Wait(100)
end end
if info.name == job then hasJob = true end if info.name == job then hasJobFlag = true end
elseif isStarted(OXCoreExport) then elseif isStarted(OXCoreExport) then
for k, v in pairs(exports[OXCoreExport]:GetPlayerData().groups) do 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 end
elseif isStarted(QBXExport) then elseif isStarted(QBXExport) then
local jobinfo = QBX.PlayerData.job local info = exports[QBXExport]:GetPlayerData()
if jobinfo.name == job then hasJob = true if info.job.name == job then
duty = QBX.PlayerData.job.onduty hasJobFlag = true
if grade and not (grade <= jobinfo.grade.level) then hasJob = false end duty = info.job.onduty
if grade and not (grade <= info.job.grade.level) then hasJobFlag = false end
end end
local ganginfo = QBX.PlayerData.gang if info.gang.name == job then
if ganginfo.name == job then hasJob = true hasJobFlag = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end if grade and not (grade <= info.gang.grade.level) then hasJobFlag = false end
end end
elseif isStarted(QBExport) and not isStarted(QBXExport) then elseif isStarted(QBExport) and not isStarted(QBXExport) then
local info = nil local info = nil
Core.Functions.GetPlayerData(function(PlayerData) Core.Functions.GetPlayerData(function(PlayerData) info = PlayerData end)
info = PlayerData
end)
local jobinfo = info.job local jobinfo = info.job
if jobinfo.name == job then hasJob = true if jobinfo.name == job then
hasJobFlag = true
duty = jobinfo.onduty 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 end
local ganginfo = info.gang local ganginfo = info.gang
if ganginfo.name == job then if ganginfo.name == job then
hasJob = true hasJobFlag = true
if grade and not (grade <= ganginfo.grade.level) then hasJob = false end if grade and not (grade <= ganginfo.grade.level) then hasJobFlag = false end
end end
else 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
end end
return hasJob, duty return hasJobFlag, duty
end 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 --- Can be called server-side (passing a player source) or client-side (for current player).
--- based on the active inventory system. It can be called server-side or client-side.
--- ---
---@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 --- ```lua
--- -- Get information for a specific player --- -- Get information for a specific player
--- local playerInfo = getPlayer(playerId) --- local playerInfo = getPlayer(playerId)
@@ -467,7 +437,8 @@ end
function getPlayer(source) function getPlayer(source)
local Player = {} local Player = {}
debugPrint("^6Bridge^7: ^2Getting ^3Player^2 info^7") debugPrint("^6Bridge^7: ^2Getting ^3Player^2 info^7")
if source then -- If called from server
if source then
local src = tonumber(source) local src = tonumber(source)
if isStarted(ESXExport) then if isStarted(ESXExport) then
local info = ESX.GetPlayerFromId(src) local info = ESX.GetPlayerFromId(src)
@@ -494,13 +465,12 @@ function getPlayer(source)
local import = LoadResourceFile('ox_core', file) local import = LoadResourceFile('ox_core', file)
local chunk = assert(load(import, ('@@ox_core/%s'):format(file))) local chunk = assert(load(import, ('@@ox_core/%s'):format(file)))
chunk() chunk()
local player = Ox.GetPlayer(tonumber(src)) local player = Ox.GetPlayer(src)
Player = { Player = {
name = ('%s %s'):format(player.firstName, player.lastName), name = ('%s %s'):format(player.firstName, player.lastName),
cash = exports[OXInv]:Search(src, 'count', "money"), cash = exports[OXInv]:Search(src, 'count', "money"),
bank = 0, bank = 0,
} }
elseif isStarted(QBXExport) then elseif isStarted(QBXExport) then
local info = exports[QBXExport]:GetPlayer(src) local info = exports[QBXExport]:GetPlayer(src)
Player = { Player = {
@@ -518,9 +488,8 @@ function getPlayer(source)
account = info.PlayerData.charinfo.account, account = info.PlayerData.charinfo.account,
citizenId = info.PlayerData.citizenid, citizenId = info.PlayerData.citizenid,
} }
elseif isStarted(QBExport) and not isStarted(QBXExport) then 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 local info = Core.Functions.GetPlayer(src).PlayerData
Player = { Player = {
firstname = info.charinfo.firstname, firstname = info.charinfo.firstname,
@@ -537,9 +506,8 @@ function getPlayer(source)
account = info.charinfo.account, account = info.charinfo.account,
citizenId = info.citizenid, citizenId = info.citizenid,
} }
else else
local info = exports[QBExport]:GetPlayer(src).PlayerData -- this was added to new core then removed? local info = exports[QBExport]:GetPlayer(src).PlayerData
Player = { Player = {
firstname = info.charinfo.firstname, firstname = info.charinfo.firstname,
lastname = info.charinfo.lastname, lastname = info.charinfo.lastname,
@@ -556,16 +524,15 @@ function getPlayer(source)
citizenId = info.citizenid, citizenId = info.citizenid,
} }
end end
else 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 end
else else
-- Client-side: Get current player info.
if isStarted(ESXExport) and ESX ~= nil then if isStarted(ESXExport) and ESX ~= nil then
local info = ESX.GetPlayerData() local info = ESX.GetPlayerData()
--jsonPrint(info)
local cash, bank = 0, 0 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 == "money" then cash = v.money end
if v.name == "bank" then bank = v.money end if v.name == "bank" then bank = v.money end
end end
@@ -629,12 +596,22 @@ function getPlayer(source)
citizenId = info.citizenid, citizenId = info.citizenid,
} }
else 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
end end
return Player return Player
end 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) function GetPlayersFromCoords(coords, radius)
local players = {} local players = {}
for _, playerId in ipairs(GetActivePlayers()) do for _, playerId in ipairs(GetActivePlayers()) do
@@ -642,7 +619,7 @@ function GetPlayersFromCoords(coords, radius)
if ped and DoesEntityExist(ped) then if ped and DoesEntityExist(ped) then
local playerCoords = GetEntityCoords(ped) local playerCoords = GetEntityCoords(ped)
if #(coords - playerCoords) <= radius then if #(coords - playerCoords) <= radius then
players[#players+1] = playerId players[#players + 1] = playerId
end end
end end
end end

View File

@@ -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 -- PolyZone Management Module
-- createPoly({ name = 'name', debug = true, points = { vec2(), vec2() }, onEnter = function() end, onExit = function() end, }) ----------------------------
--- 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). --- 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. --- Automatically checks which polyzone script is active. When using ox_lib, it converts the provided
--- It supports setting up entry and exit callbacks for the zone. --- 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. --- @param data table Zone configuration table with the following keys:
--- - **name** (`string`): The name of the zone. --- - name (string): The zone's identifier.
--- - **debug** (`boolean`): Whether to enable debug mode for the zone. --- - debug (boolean): Whether debug mode is enabled.
--- - **points** (`table`): A list of `vec2` points defining the polygon. --- - points (table): A list of vec2 points defining the polygon.
--- - **onEnter** (`function`): Callback function to execute when a player enters the zone. --- - onEnter (function): Callback when a player enters the zone.
--- - **onExit** (`function`): Callback function to execute when a player exits 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 ---@usage
--- ```lua ---```lua
--- createPoly({ ---createPoly({
--- name = 'testZone', --- name = 'testZone',
--- debug = true, --- debug = true,
--- points = { vec2(100.0, 100.0), vec2(200.0, 100.0), vec2(200.0, 200.0), vec2(100.0, 200.0) }, --- 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, --- onEnter = function() print("Entered Test Zone") end,
--- onExit = function() print("Exited Test Zone") end, --- onExit = function() print("Exited Test Zone") end,
--- }) ---})
--- ``` ---```
function createPoly(data) function createPoly(data)
local Location = nil 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) 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 for i = 1, #data.points do
data.points[i] = vec3(data.points[i].x, data.points[i].y, 12.0) data.points[i] = vec3(data.points[i].x, data.points[i].y, 12.0)
end end
data.thickness = 1000 data.thickness = 1000 -- Set a default thickness value
Location = lib.zones.poly(data) Location = lib.zones.poly(data)
elseif isStarted("PolyZone") then elseif isStarted("PolyZone") then
debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4PolyZone^7': "..data.name) debugPrint("^6Bridge^7: ^2Creating new poly with ^7'^4PolyZone^7': "..data.name)
Location = PolyZone:Create(data.points, { name = data.name, debugPoly = data.debug }) Location = PolyZone:Create(data.points, { name = data.name, debugPoly = data.debug })
Location:onPlayerInOut(function(isPointInside) Location:onPlayerInOut(function(isPointInside)
if isPointInside then data.onEnter() else data.onExit() end if isPointInside then data.onEnter() else data.onExit() end
end) end)
else else
print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7") print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7")
@@ -47,21 +63,25 @@ function createPoly(data)
return Location return Location
end end
-------------------------------------------------------------
-- Circular Zone Creation
-------------------------------------------------------------
--- Creates a circular zone using the detected polyzone library (ox_lib or PolyZone). --- 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. --- When using ox_lib, it creates a sphere zone. For PolyZone, it creates a CircleZone and attaches
--- It supports setting up entry and exit callbacks for the zone. --- onEnter and onExit callbacks.
--- ---
---@param data table A table containing the circular zone configuration. --- @param data table Zone configuration with the following keys:
--- - **name** (`string`): The name of the circular zone. --- - name (string): The zone's identifier.
--- - **coords** (`vector3`): The center coordinates of the circle. --- - coords (vector3): The center of the circle.
--- - **radius** (`number`): The radius of the circle. --- - radius (number): The radius of the circle.
--- - **onEnter** (`function`): Callback function to execute when a player enters the zone. --- - onEnter (function): Callback when a player enters the zone.
--- - **onExit** (`function`): Callback function to execute when a player exits 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 --- ```lua
--- createCirclePoly({ --- createCirclePoly({
--- name = 'circleZone', --- name = 'circleZone',
@@ -73,7 +93,7 @@ end
--- ``` --- ```
function createCirclePoly(data) function createCirclePoly(data)
local Location = nil 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) debugPrint("^6Bridge^7: ^2Creating new ^3Cricle^2 poly with ^7"..OXLibExport.." "..data.name)
Location = lib.zones.sphere(data) Location = lib.zones.sphere(data)
elseif isStarted("PolyZone") then elseif isStarted("PolyZone") then
@@ -87,26 +107,30 @@ function createCirclePoly(data)
end end
end) end)
else else
print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3exports^1.^2lua^7") print("^4ERROR^7: ^2No PolyZone creation script detected ^7- ^2Check ^3starter^1.^2lua^7")
end end
debugPrint("^6Bridge^7: ^2Zone Stats - Coords: "..formatCoord(data.coords).." Radius: "..data.radius) debugPrint("^6Bridge^7: ^2Zone Stats - Coords: "..formatCoord(data.coords).." Radius: "..data.radius)
return Location return Location
end end
-------------------------------------------------------------
-- PolyZone Removal Function
-------------------------------------------------------------
--- Removes a previously created polyzone. --- 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. --- @param Location table The zone object to be removed.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- local zone = createPoly({...}) --- local zone = createPoly({...})
--- -- Later in the code ---
--- removePolyZone(zone) --- removePolyZone(zone)
--- ``` --- ```
function removePolyZone(Location) 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) debugPrint("^6Bridge^7: ^2Removing ^2poly with ^7"..OXLibExport)
Location:remove() Location:remove()
elseif isStarted("PolyZone") then elseif isStarted("PolyZone") then

View File

@@ -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

View File

@@ -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 BigMessage.__index = BigMessage
--- Creates a new BigMessage instance.
--- @return table table A new BigMessage object.
function BigMessage:new() function BigMessage:new()
local self = setmetatable({}, BigMessage) local self = setmetatable({}, BigMessage)
self.scaleform = nil self.scaleform = nil
@@ -15,6 +26,7 @@ function BigMessage:new()
return self return self
end end
--- Loads the Scaleform movie if it has not been loaded yet.
function BigMessage:Load() function BigMessage:Load()
if self.scaleform then return end if self.scaleform then return end
self.scaleform = RequestScaleformMovie("MP_BIG_MESSAGE_FREEMODE") self.scaleform = RequestScaleformMovie("MP_BIG_MESSAGE_FREEMODE")
@@ -23,7 +35,8 @@ function BigMessage:Load()
end end
end end
-- Dispose of the scaleform --- Disposes of the Scaleform movie.
--- If manualDispose is true, executes a transition before disposing.
function BigMessage:Dispose() function BigMessage:Dispose()
if not self.scaleform then return end if not self.scaleform then return end
@@ -34,8 +47,8 @@ function BigMessage:Dispose()
ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion) ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion)
EndScaleformMovieMethod() EndScaleformMovieMethod()
-- Wait a fraction of the transition duration (in milliseconds)
Wait((self.transitionDuration * 0.5) * 1000) Wait((self.transitionDuration * 0.5) * 1000)
self.manualDispose = false self.manualDispose = false
end end
@@ -46,8 +59,10 @@ function BigMessage:Dispose()
self.isDisplaying = false self.isDisplaying = false
end end
--- Updates the display by drawing the Scaleform movie fullscreen.
function BigMessage:Update() function BigMessage:Update()
if not self.scaleform then return end if not self.scaleform then return end
DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0) DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0)
if self.manualDispose then return end if self.manualDispose then return end
@@ -60,6 +75,7 @@ function BigMessage:Update()
ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion) ScaleformMovieMethodAddParamBool(self.transitionPreventAutoExpansion)
EndScaleformMovieMethod() EndScaleformMovieMethod()
self.transitionExecuted = true self.transitionExecuted = true
-- Extend duration slightly for smooth transition
self.duration = self.duration + ((self.transitionDuration * 0.5) * 1000) self.duration = self.duration + ((self.transitionDuration * 0.5) * 1000)
else else
self:Dispose() self:Dispose()
@@ -67,14 +83,20 @@ function BigMessage:Update()
end end
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) function BigMessage:SetTransition(transition, duration, preventAutoExpansion)
self.transition = transition or "TRANSITION_OUT" self.transition = transition or "TRANSITION_OUT"
self.transitionDuration = duration or 0.4 self.transitionDuration = duration or 0.4
self.transitionPreventAutoExpansion = preventAutoExpansion or true self.transitionPreventAutoExpansion = preventAutoExpansion or true
end end
--- Starts a thread to continuously update the HUD until the message is done.
function BigMessage:StartUpdate() function BigMessage:StartUpdate()
if self.isDisplaying then return end if self.isDisplaying then return end
self.isDisplaying = true self.isDisplaying = true
CreateThread(function() CreateThread(function()
while self.isDisplaying do while self.isDisplaying do
@@ -85,10 +107,13 @@ function BigMessage:StartUpdate()
end end
--- Displays a mission passed message. --- Displays a mission passed message.
--- --- @param msg string The message to display.
--- @param msg string The main message to display. --- @param duration number|nil The duration (in milliseconds) to display the message (default: 5000).
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param manualDispose boolean|nil Whether to manually dispose the Scaleform after display (default: false).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false. --- @usage
--- ```lua
--- BigMessage:ShowMissionPassedMessage("MISSION PASSED", 5000)
--- ```
function BigMessage:ShowMissionPassedMessage(msg, duration, manualDispose) function BigMessage:ShowMissionPassedMessage(msg, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -109,13 +134,12 @@ function BigMessage:ShowMissionPassedMessage(msg, duration, manualDispose)
end end
--- Displays a colored shard message. --- Displays a colored shard message.
--- --- @param msg string The main message.
--- @param msg string The main message to display.
--- @param desc string The description text. --- @param desc string The description text.
--- @param textColor number The color index for the text. --- @param textColor number The text color index.
--- @param bgColor number The color index for the background. --- @param bgColor number The background color index.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false. --- @param manualDispose boolean|nil Whether to manually dispose the Scaleform (default: false).
function BigMessage:ShowColoredShard(msg, desc, textColor, bgColor, duration, manualDispose) function BigMessage:ShowColoredShard(msg, desc, textColor, bgColor, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -134,12 +158,9 @@ function BigMessage:ShowColoredShard(msg, desc, textColor, bgColor, duration, ma
end end
--- Displays an old-style mission passed message. --- Displays an old-style mission passed message.
--- --- @param msg string The message.
--- @param msg string The main message to display. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param manualDispose boolean|nil Whether to manually dispose (default: false).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
---
--- @return void
function BigMessage:ShowOldMessage(msg, duration, manualDispose) function BigMessage:ShowOldMessage(msg, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -155,13 +176,10 @@ function BigMessage:ShowOldMessage(msg, duration, manualDispose)
end end
--- Displays a simple shard message. --- Displays a simple shard message.
--- --- @param msg string The main message.
--- @param msg string The main message to display.
--- @param subtitle string The subtitle text. --- @param subtitle string The subtitle text.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false. --- @param manualDispose boolean|nil Whether to manually dispose (default: false).
---
--- @return void
function BigMessage:ShowSimpleShard(msg, subtitle, duration, manualDispose) function BigMessage:ShowSimpleShard(msg, subtitle, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -178,12 +196,11 @@ function BigMessage:ShowSimpleShard(msg, subtitle, duration, manualDispose)
end end
--- Displays a rank-up message. --- Displays a rank-up message.
--- --- @param msg string The main message.
--- @param msg string The main message to display.
--- @param subtitle string The subtitle text. --- @param subtitle string The subtitle text.
--- @param rank number The rank level achieved. --- @param rank number The rank level achieved.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false. --- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowRankupMessage(msg, subtitle, rank, duration, manualDispose) function BigMessage:ShowRankupMessage(msg, subtitle, rank, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -203,12 +220,11 @@ function BigMessage:ShowRankupMessage(msg, subtitle, rank, duration, manualDispo
end end
--- Displays a weapon purchased message. --- Displays a weapon purchased message.
--- --- @param bigMessage string The main message.
--- @param bigMessage string The main message to display.
--- @param weaponName string The name of the weapon purchased. --- @param weaponName string The name of the weapon purchased.
--- @param weaponHash number The hash identifier of the weapon. --- @param weaponHash number The weapon hash.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false. --- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowWeaponPurchasedMessage(bigMessage, weaponName, weaponHash, duration, manualDispose) function BigMessage:ShowWeaponPurchasedMessage(bigMessage, weaponName, weaponHash, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -228,10 +244,9 @@ function BigMessage:ShowWeaponPurchasedMessage(bigMessage, weaponName, weaponHas
end end
--- Displays a large multiplayer message. --- Displays a large multiplayer message.
--- --- @param msg string The main message.
--- @param msg string The main message to display. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param manualDispose boolean|nil Whether to manually dispose (default: false).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false.
function BigMessage:ShowMpMessageLarge(msg, duration, manualDispose) function BigMessage:ShowMpMessageLarge(msg, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -254,11 +269,10 @@ function BigMessage:ShowMpMessageLarge(msg, duration, manualDispose)
end end
--- Displays a "Wasted" multiplayer message. --- Displays a "Wasted" multiplayer message.
--- --- @param msg string The main message.
--- @param msg string The main message to display.
--- @param subtitle string The subtitle text. --- @param subtitle string The subtitle text.
--- @param duration number|nil The duration in milliseconds the message should be displayed. Defaults to 5000. --- @param duration number|nil Duration in milliseconds (default: 5000).
--- @param manualDispose boolean|nil Whether to manually dispose of the scaleform after the message. Defaults to false. --- @param manualDispose boolean|nil Whether to manually dispose (default: false).
function BigMessage:ShowMpWastedMessage(msg, subtitle, duration, manualDispose) function BigMessage:ShowMpWastedMessage(msg, subtitle, duration, manualDispose)
duration = duration or 5000 duration = duration or 5000
self:Load() self:Load()
@@ -274,4 +288,19 @@ function BigMessage:ShowMpWastedMessage(msg, subtitle, duration, manualDispose)
self:StartUpdate() self:StartUpdate()
end 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 return BigMessage

View File

@@ -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 = {}
CountdownHandler.__index = CountdownHandler CountdownHandler.__index = CountdownHandler
--- Creates a new CountdownHandler instance.
--- @return table table A new CountdownHandler object.
function CountdownHandler:new() function CountdownHandler:new()
local self = setmetatable({}, CountdownHandler) local self = setmetatable({}, CountdownHandler)
self.scaleform = nil self.scaleform = nil
@@ -9,14 +21,18 @@ function CountdownHandler:new()
return self return self
end end
--- Loads the "COUNTDOWN" scaleform movie.
function CountdownHandler:Load() function CountdownHandler:Load()
if self.scaleform then return end if self.scaleform then
return
end
self.scaleform = RequestScaleformMovie("COUNTDOWN") self.scaleform = RequestScaleformMovie("COUNTDOWN")
while not HasScaleformMovieLoaded(self.scaleform) do while not HasScaleformMovieLoaded(self.scaleform) do
Wait(0) Wait(0)
end end
end end
--- Disposes of the currently loaded scaleform movie.
function CountdownHandler:Dispose() function CountdownHandler:Dispose()
if self.scaleform then if self.scaleform then
SetScaleformMovieAsNoLongerNeeded(self.scaleform) SetScaleformMovieAsNoLongerNeeded(self.scaleform)
@@ -24,15 +40,19 @@ function CountdownHandler:Dispose()
end end
end end
--- Updates the HUD by drawing the scaleform movie fullscreen.
function CountdownHandler:Update() function CountdownHandler:Update()
if self.scaleform then if self.scaleform then
DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0) DrawScaleformMovieFullscreen(self.scaleform, 255, 255, 255, 255, 0)
end end
end end
--- Displays a message on the countdown HUD.
--- @param message string The message to display.
function CountdownHandler:ShowMessage(message) function CountdownHandler:ShowMessage(message)
local r, g, b, a = self.colour.r, self.colour.g, self.colour.b, self.colour.a 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") BeginScaleformMovieMethod(self.scaleform, "SET_MESSAGE")
ScaleformMovieMethodAddParamPlayerNameString(message) ScaleformMovieMethodAddParamPlayerNameString(message)
ScaleformMovieMethodAddParamInt(r) ScaleformMovieMethodAddParamInt(r)
@@ -41,6 +61,7 @@ function CountdownHandler:ShowMessage(message)
ScaleformMovieMethodAddParamBool(true) ScaleformMovieMethodAddParamBool(true)
EndScaleformMovieMethod() EndScaleformMovieMethod()
-- Trigger a fade effect (optional).
BeginScaleformMovieMethod(self.scaleform, "FADE_MP") BeginScaleformMovieMethod(self.scaleform, "FADE_MP")
ScaleformMovieMethodAddParamPlayerNameString(message) ScaleformMovieMethodAddParamPlayerNameString(message)
ScaleformMovieMethodAddParamInt(r) ScaleformMovieMethodAddParamInt(r)
@@ -49,17 +70,14 @@ function CountdownHandler:ShowMessage(message)
EndScaleformMovieMethod() EndScaleformMovieMethod()
end end
--- Starts the countdown with the specified number and HUD color. --- Starts the countdown HUD.
--- --- @param number number|nil The starting number for the countdown (default: 3).
--- @param number number|nil The starting number for the countdown. Defaults to 3. --- @param hudColour number|nil The HUD colour index (default: 18).
--- @param hudColour number|nil The HUD color index. Defaults to 18. --- @return boolean boolean True when the countdown has finished.
---
--- @return boolean `true` when the countdown has finished.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Start a countdown of 5 seconds with HUD color 25
--- if CountdownHandler:Start(5, 25) then --- if CountdownHandler:Start(5, 25) then
--- -- When run in an if statement, the script will wait until its finished to continue
--- print("Countdown Complete") --- print("Countdown Complete")
--- end --- end
--- ``` --- ```
@@ -68,6 +86,7 @@ function CountdownHandler:Start(number, hudColour)
number = number or 3 number = number or 3
hudColour = hudColour or 18 hudColour = hudColour or 18
-- Get HUD colour using framework function; alternatives could be added here.
local r, g, b, a = GetHudColour(hudColour) local r, g, b, a = GetHudColour(hudColour)
self.colour = { r = r, g = g, b = b, a = a } self.colour = { r = r, g = g, b = b, a = a }
@@ -81,18 +100,17 @@ function CountdownHandler:Start(number, hudColour)
end end
end) end)
-- Begin the countdown -- Countdown logic
CreateThread(function() CreateThread(function()
local currentNumber = number local currentNumber = number
while currentNumber > 0 do while currentNumber > 0 do
-- Play countdown sound
playSound("Count") playSound("Count")
self:ShowMessage(tostring(currentNumber)) self:ShowMessage(tostring(currentNumber))
Wait(1000) Wait(1000)
currentNumber = currentNumber - 1 currentNumber = currentNumber - 1
end end
playSound("Go")
playSound("Go")
self:ShowMessage("GO") self:ShowMessage("GO")
finished = true finished = true
@@ -101,14 +119,15 @@ function CountdownHandler:Start(number, hudColour)
self:Dispose() self:Dispose()
finished = true finished = true
end) end)
while not finished do Wait(10) end while not finished do Wait(10) end
return true return true
end end
-- Create an instance of CountdownHandler -- Create a singleton instance of CountdownHandler.
CountdownHandler = CountdownHandler:new() CountdownHandler = CountdownHandler:new()
-- Optional: Register an event to start the countdown -- Register an event to start the countdown.
RegisterNetEvent(getScript()..":startCountdown", function(number, hudColour) RegisterNetEvent(getScript()..":startCountdown", function(number, hudColour)
CountdownHandler:Start(number, hudColour) CountdownHandler:Start(number, hudColour)
end) end)

View File

@@ -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. --- Calculates a background rectangle based on the number of text lines and renders each line on-screen.
--- It is controlled by the `debugMode` flag and can be positioned dynamically on the screen.
--- ---
--- @param textTable table A table containing strings to display. --- @param textTable table An array of strings to display.
--- @param loc vector2|nil Optional. The screen position to display the debug box. Defaults to `vec2(0.05, 0.65)`. --- @param loc vector2 (Optional) Top-left coordinate for the text box (default: vec2(0.05, 0.65)).
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- debugScaleForm({ ---CreateThread(function()
--- "Player Position: X=123.45 Y=678.90 Z=12.34", --- while true do
--- "Current Action: Running", --- debugScaleForm({
--- }) --- "Line 1: Debug info",
--- "Line 2: More info"
--- })
--- Wait(0)
--- end
---end)
--- ``` --- ```
function debugScaleForm(textTable, loc) function debugScaleForm(textTable, loc)
if debugMode then if debugMode then
-- Define the display position (top left corner) loc = loc or vec2(0.05, 0.65)
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 per line.
local lineHeight = 0.025 -- Height of each line of text local totalHeight = #textTable * lineHeight
local totalHeight = #textTable * lineHeight -- Dynamic height based on number of lines local boxPadding = 0.01 -- Padding around the text.
local boxPadding = 0.01 -- Padding to add around the text inside the box local size = vec2(0.18, totalHeight + boxPadding * 2)
local size = vec2(0.18, totalHeight + boxPadding * 2) -- Width remains fixed, height is dynamic
-- Draw background rectangle.
DrawRect(loc.x + size.x / 2, loc.y + size.y / 2, size.x, size.y, 0, 0, 0, 255) 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 for i = 1, #textTable do
local textLine = textTable[i]
SetTextScale(0.30, 0.30) SetTextScale(0.30, 0.30)
BeginTextCommandDisplayText("STRING") BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringKeyboardDisplay(textLine) AddTextComponentSubstringKeyboardDisplay(textTable[i])
EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01) EndTextCommandDisplayText(loc.x + 0.005, loc.y + (i - 1) * lineHeight + 0.01)
end end
end end
end end

View File

@@ -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. --- Requests the "instructional_buttons" scaleform, clears previous data, sets clear space,
--- It supports different polyzone libraries by automatically detecting which one is active. --- 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. --- @param info table An array of tables, where each table represents a button option:
--- - **keys** (`table`): A list of control keys to display. --- - keys (table): An array of key codes (e.g., {38, 29}) to display.
--- - **text** (`string`): The description text for the buttons. --- - text (string): The label for the button.
--- ---
---@usage --- @usage
--- ```lua --- ```lua
--- makeInstructionalButtons({ ---CreateThread(function()
--- { keys = { 38 }, text = "Interact" }, --- while true do
--- { keys = { 47 }, text = "Pick Up" }, --- makeInstructionalButtons({
--- }) --- { keys = {38, 29}, text = "Open Menu" },
--- { keys = {45}, text = "Close Menu" }
--- })
--- Wait(0)
--- end
---end)
--- ``` --- ```
function makeInstructionalButtons(info) function makeInstructionalButtons(info)
local build = RequestScaleformMovie("instructional_buttons") local build = RequestScaleformMovie("instructional_buttons")
while not HasScaleformMovieLoaded(build) do Wait(0) end while not HasScaleformMovieLoaded(build) do Wait(0) end
-- Draw the scaleform fullscreen (initial draw).
DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0) DrawScaleformMovieFullscreen(build, 255, 255, 255, 0, 0)
-- Clear previous instructions.
BeginScaleformMovieMethod(build, "CLEAR_ALL") BeginScaleformMovieMethod(build, "CLEAR_ALL")
EndScaleformMovieMethod() EndScaleformMovieMethod()
-- Set clear spacing between buttons.
BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE") BeginScaleformMovieMethod(build, "SET_CLEAR_SPACE")
ScaleformMovieMethodAddParamInt(200) ScaleformMovieMethodAddParamInt(200)
EndScaleformMovieMethod() EndScaleformMovieMethod()
-- Add each button option to the scaleform.
for i = 1, #info do for i = 1, #info do
BeginScaleformMovieMethod(build, "SET_DATA_SLOT") BeginScaleformMovieMethod(build, "SET_DATA_SLOT")
ScaleformMovieMethodAddParamInt(i - 1) ScaleformMovieMethodAddParamInt(i - 1)
@@ -37,8 +52,11 @@ function makeInstructionalButtons(info)
EndScaleformMovieMethod() EndScaleformMovieMethod()
end end
-- Draw the instructional buttons.
BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS") BeginScaleformMovieMethod(build, "DRAW_INSTRUCTIONAL_BUTTONS")
EndScaleformMovieMethod() EndScaleformMovieMethod()
-- Set a translucent black background.
BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR") BeginScaleformMovieMethod(build, "SET_BACKGROUND_COLOUR")
ScaleformMovieMethodAddParamInt(0) ScaleformMovieMethodAddParamInt(0)
ScaleformMovieMethodAddParamInt(0) ScaleformMovieMethodAddParamInt(0)
@@ -46,5 +64,6 @@ function makeInstructionalButtons(info)
ScaleformMovieMethodAddParamInt(80) ScaleformMovieMethodAddParamInt(80)
EndScaleformMovieMethod() EndScaleformMovieMethod()
-- Final full-screen draw with full opacity.
DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0) DrawScaleformMovieFullscreen(build, 255, 255, 255, 255, 0)
end end

View File

@@ -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

View File

@@ -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) function createTimerHud(title, data, alpha)
loadTextureDict("timerbars") loadTextureDict("timerbars")

View File

@@ -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) --- Retrieves the current balance of a society's bank account.
local bankScript, newAmount = "", 0 --- @param society string The identifier of the society.
if isStarted("Renewed-Banking") then --- @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" 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" bankScript = "qb-banking"
if not exports["qb-banking"]:GetAccount(society) then if not exports["qb-banking"]:GetAccount(society) then
if Jobs[society] then if Jobs[society] then
@@ -17,47 +85,73 @@ function chargeSociety(society, amount)
end end
end end
exports["qb-banking"]:RemoveMoney(society, amount) 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 elseif isStarted("fd_banking") then
bankScript = "fd_banking" bankScript = "fd_banking"
exports["fd_banking"]:RemoveMoney(society, amount) exports["fd_banking"]:RemoveMoney(society, amount)
elseif isStarted("okokBanking") then elseif isStarted("okokBanking") then
bankScript = "okokBanking" bankScript = "okokBanking"
exports['okokBanking']:RemoveMoney(society, amount) exports['okokBanking']:RemoveMoney(society, amount)
end end
if bankScript == "" then if bankScript == "" then
print("^1Error^7: ^3ChargeSociety^7: ^2No supported banking script found") print("^1Error^7: ^3ChargeSociety^7: ^2No supported banking script found")
else else
newAmount = getSocietyAccount(society) 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
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) function fundSociety(society, amount)
local bankScript, newAmount = "", 0 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" bankScript = "qb-banking"
if not exports["qb-banking"]:GetAccount(society) then if not exports["qb-banking"]:GetAccount(society) then
if Jobs[society] then if Jobs[society] then
print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'") 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 elseif Gangs[society] then
print("^6Bridge^7: ^2Making new bank account in ^7'^3qb-banking^7' ^2for ^7'^3"..society.."^7'") 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 end
exports["qb-banking"]:AddMoney(society, amount) 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 elseif isStarted("fd_banking") then
bankScript = "fd_banking" bankScript = "fd_banking"
exports.fd_banking:AddMoney(society, amount) exports["fd_banking"]:AddMoney(society, amount)
elseif isStarted("okokBanking") then elseif isStarted("okokBanking") then
bankScript = "okokBanking" bankScript = "okokBanking"
@@ -73,39 +167,12 @@ function fundSociety(society, amount)
end end
end end
function getSocietyAccount(society)
local bankScript, amount = "", 0 -- other
if isStarted("Renewed-Banking") then if isStarted("esx_society") then
bankScript = "Renewed-Banking" createCallback(getScript() .. ":getESXSocietyAccount", function(source, society)
amount = exports["Renewed-Banking"]:getAccountMoney(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 })
elseif isStarted("qb-banking") then return result or 0
bankScript = "qb-banking" end)
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
end end

View File

@@ -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 local stash
-- If running on the server, create a callback to retrieve stash items.
if isServer() then if isServer() then
createCallback(getScript()..':server:GetStashItems', createCallback(getScript()..':server:GetStashItems', function(source, stashName)
function(source, stashName) stash = getStash(stashName)
stash = getStash(stashName) return stash return stash
end) end)
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) function GetStashTimeout(stashName, stop)
if stop then if stop then
stashCache = {} stashCache = {}
return return
end end
-- Retrieve cache for this stash, or initialize if not present.
stash = stashCache[stashName] stash = stashCache[stashName]
if not stash then if not stash then
debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache ^1not ^2found^7, ^2need to grab from server^7") 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] stash = stashCache[stashName]
else else
debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache found^7") debugPrint("^6Bridge^7: ^2Local Stash ^7'^3"..stashName.."^7'^2 cache found^7")
("^6Bridge^7: ^2Local Stash '^3"..stashName.."^7' cache found")
end end
-- If there are already items in cache, skip recheck.
if countTable(stashCache[stashName].items) > 0 then if countTable(stashCache[stashName].items) > 0 then
debugPrint("^6Bridge^7: '^3"..stashName.." ^2Items found in cache, skipping recheck") debugPrint("^6Bridge^7: '^3"..stashName.." ^2Items found in cache, skipping recheck")
return true return true
end end
-- If timeout has expired, update the stash items from the server.
if stashCache[stashName].timeout <= 0 then if stashCache[stashName].timeout <= 0 then
stashCache[stashName].items = triggerCallback(getScript()..':server:GetStashItems', stashName) stashCache[stashName].items = triggerCallback(getScript()..':server:GetStashItems', stashName)
stashCache[stashName].timeout = 15000 stashCache[stashName].timeout = 15000 -- Timeout in milliseconds.
CreateThread(function() CreateThread(function()
while stash.timeout > 0 do while stashCache[stashName] and stashCache[stashName].timeout > 0 do
stashCache[stashName].timeout -= 1000 stashCache[stashName].timeout -= 1000
Wait(1000) Wait(1000)
end end
@@ -39,46 +73,93 @@ function GetStashTimeout(stashName, stop)
return false return false
end 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) function checkHasItem(stashes, itemTable)
if not stashes then if not stashes then
return hasItem(itemTable), nil return hasItem(itemTable), nil
end end
if type(stashes) == "table" then if type(stashes) == "table" then
local succeses = 0 local successes = 0
local itemCount = countTable(itemTable) 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 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) GetStashTimeout(name)
for item, amount in pairs(itemTable) do 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 if stashhasItem(stashCache[name].items, item, amount) then
succeses += 1 successes = successes + 1
if succeses == itemCount then if successes == itemCount then
return true, name return true, name
end end
end end
end end
end end
else 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) GetStashTimeout(stashes)
return stashhasItem(stashCache[stashes].items, itemTable), stashes return stashhasItem(stashCache[stashes].items, itemTable), stashes
end end
return false, nil return false, nil
end 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) 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 if isStarted(OXInv) then
exports[OXInv]:openInventory('stash', data.stash) exports[OXInv]:openInventory('stash', data.stash)
elseif isStarted(CoreInv) then
TriggerServerEvent('core_inventory:server:openInventory', data.stash, 'stash')
elseif isStarted(CodeMInv) then 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 elseif isStarted(QBInv) then
if QBInvNew 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 else
TriggerEvent("inventory:client:SetCurrentStash", data.stash) TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions) TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions)
@@ -86,18 +167,38 @@ function openStash(data)
else else
TriggerEvent("inventory:client:SetCurrentStash", data.stash) TriggerEvent("inventory:client:SetCurrentStash", data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions) TriggerServerEvent("inventory:server:OpenInventory", "stash", data.stash, data.stashOptions)
end end
lookEnt(data.coords) lookEnt(data.coords)
end end
-- Register an event for opening QB stashes.
RegisterNetEvent(getScript()..':server:OpenStashQB', function(data) RegisterNetEvent(getScript()..':server:OpenStashQB', function(data)
exports[QBInv]:OpenInventory(source, data.stashName, data) exports[QBInv]:OpenInventory(source, data.stashName, data)
end) 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 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 end
local stashItems, items = {}, {} local stashItems, items = {}, {}
if isStarted(OXInv) then stashResource = OXInv if isStarted(OXInv) then stashResource = OXInv
stashItems = exports[OXInv]:Inventory(stashName).items stashItems = exports[OXInv]:Inventory(stashName).items
@@ -109,14 +210,15 @@ function getStash(stashName) local stashResource = ""
stashItems = exports[CoreInv]:getInventory(stashName) stashItems = exports[CoreInv]:getInventory(stashName)
elseif isStarted(CodeMInv) then stashResource = CodeMInv elseif isStarted(CodeMInv) then stashResource = CodeMInv
stashItems = exports[CodeMInv]:GetInventoryItems('Stash', stashName) stashItems = exports[CodeMInv]:GetStashItems(stashName)
elseif isStarted(OrigenInv) then stashResource = OrigenInv elseif isStarted(OrigenInv) then stashResource = OrigenInv
stashItems = exports[OrigenInv]:GetStashItems(stashName) stashItems = exports[OrigenInv]:getInventory(stashName)
elseif isStarted(PSInv) then stashResource = PSInv elseif isStarted(PSInv) then stashResource = PSInv
local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName }) local result = MySQL.scalar.await('SELECT items FROM stashitems WHERE stash = ?', { stashName })
if result then stashItems = json.decode(result) end if result then stashItems = json.decode(result) end
elseif isStarted(QBInv) then stashResource = QBInv elseif isStarted(QBInv) then stashResource = QBInv
local result = MySQL.scalar.await("SELECT items FROM "..(QBInvNew and "inventories" or "stashitem").." WHERE identifier = ?", { stashName }) local result = MySQL.scalar.await("SELECT items FROM "..(QBInvNew and "inventories" or "stashitem").." WHERE identifier = ?", { stashName })
if result then stashItems = json.decode(result) end if result then stashItems = json.decode(result) end
@@ -127,8 +229,8 @@ function getStash(stashName) local stashResource = ""
for _, item in pairs(stashItems) do for _, item in pairs(stashItems) do
local itemInfo = Items[item.name:lower()] local itemInfo = Items[item.name:lower()]
if itemInfo then if itemInfo then
local indexNum = #items+1 -- Added to help recreate missing slot numbers local indexNum = #items + 1 -- Fallback index if slot is missing.
items[(item.slot and item.slot) or indexNum] = { items[(item.slot or indexNum)] = {
name = itemInfo.name or nil, name = itemInfo.name or nil,
amount = tonumber(item.amount) or tonumber(item.count), amount = tonumber(item.amount) or tonumber(item.count),
info = item.info or "", info = item.info or "",
@@ -144,16 +246,30 @@ function getStash(stashName) local stashResource = ""
} }
end end
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 end
jsonPrint(items) jsonPrint(items)
return items return items
end end
function stashRemoveItem(stashItems, stashName, items) local amount = amount and amount or 1 -------------------------------------------------------------
-- print("stashItems: "..json.encode(stashItems, { indent = true})) -- Stash Item Removal Function
-- print("stashName: "..json.encode(stashName, { indent = true})) -------------------------------------------------------------
-- print("items: "..json.encode(items, { indent = true}))
--- 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 if isStarted(OXInv) then
for k, v in pairs(items) do for k, v in pairs(items) do
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..OXInv, k, v) 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 end
elseif isStarted(QSInv) then elseif isStarted(QSInv) then
for k, v in pairs(items) do for k, v in pairs(items) do
for l in pairs(stashItems) do for l in pairs(stashItems) do
if stashItems[l].name == k then if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then if (stashItems[l].amount - v) <= 0 then
debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v) debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
stashItems[l] = nil stashItems[l] = nil
else else
debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v) debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
exports[QSInv]:RemoveItemIntoStash(stashName, k, v, l) exports[QSInv]:RemoveItemIntoStash(stashName, k, v, l)
end
end end
end end
end end
end
elseif isStarted(CoreInv) then elseif isStarted(CoreInv) then
for k, v in pairs(items) do for k, v in pairs(items) do
@@ -205,8 +321,8 @@ function stashRemoveItem(stashItems, stashName, items) local amount = amount and
end end
end 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 elseif isStarted(OrigenInv) then
for k, v in pairs(items) do for k, v in pairs(items) do
exports[OrigenInv]:RemoveFromStash(stashName, k, v) exports[OrigenInv]:RemoveFromStash(stashName, k, v)
@@ -228,7 +344,11 @@ function stashRemoveItem(stashItems, stashName, items) local amount = amount and
end end
end end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'") 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 elseif isStarted(QBInv) then
if QBInvNew then if QBInvNew then
for k, v in pairs(items) do 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) debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
end end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName[1].."^7'") 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 else
for k, v in pairs(items) do for k, v in pairs(items) do
for l in pairs(stashItems) do for l in pairs(stashItems) do
if stashItems[l].name == k then if stashItems[l].name == k then
if (stashItems[l].amount - v) <= 0 then if (stashItems[l].amount - v) <= 0 then
if Config.System.Debug then debugPrint("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
print("^6Bridge^7: ^2None of this item left in stash ^3Stash^7", k, v)
end
stashItems[l] = nil stashItems[l] = nil
else else
if Config.System.Debug then debugPrint("^6Bridge^7: ^2Removing item from ^3Stash^2 with "..QBInv, k, v)
print("^6Bridge^7: ^2Removing item from ^3Stash^2 with ^7"..QBInv, k, v)
end
stashItems[l].amount -= v stashItems[l].amount -= v
end end
end end
end end
end end
debugPrint("^6Bridge^7: ^3saveStash^7: ^2Saving ^3QB^2 stash ^7'^6"..stashName.."^7'") 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) }) MySQL.Async.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', {
['stash'] = stashName,
['items'] = json.encode(stashItems)
})
end end
else 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
end end
RegisterNetEvent(getScript()..":server:stashRemoveItem", stashRemoveItem) 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) 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 = "" local foundInv = ""
for _, inv in ipairs(invs) do for _, inv in ipairs(invs) do
if isStarted(inv) then if isStarted(inv) then
@@ -274,9 +414,11 @@ function stashhasItem(stashItems, items, amount)
end end
end end
-- Ensure items is a table.
if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end if type(items) ~= "table" then items = { [items] = amount and amount or 1, } end
local hasTable = {} local hasTable = {}
for item, amount in pairs(items) do for item, requiredAmount in pairs(items) do
local count = 0 local count = 0
for _, itemData in pairs(stashItems) do for _, itemData in pairs(stashItems) do
if itemData and (itemData.name == item) then if itemData and (itemData.name == item) then
@@ -284,11 +426,13 @@ function stashhasItem(stashItems, items, amount)
end end
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) debugPrint(debugMsg)
hasTable[item] = { hasItem = (count >= amount), count = count } hasTable[item] = { hasItem = (count >= requiredAmount), count = count }
end end
for k, v in pairs(hasTable) do if v.hasItem == false then return false, hasTable end end for k, v in pairs(hasTable) do if v.hasItem == false then return false, hasTable end end
return true, hasTable return true, hasTable
end end

View File

@@ -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 = { local Keys = {
[322] = "ESC", [288] = "F1", [289] = "F2", [170] = "F3", [166] = "F5", [322] = "ESC", [288] = "F1", [289] = "F2", [170] = "F3", [166] = "F5",
[167] = "F6", [168] = "F7", [169] = "F8", [56] = "F9", [57] = "F10", [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] = "=", [159] = "6", [161] = "7", [162] = "8", [163] = "9", [84] = "-", [83] = "=",
[177] = "BACKSPACE", [37] = "TAB", [177] = "BACKSPACE", [37] = "TAB",
[44] = "Q", [32] = "W", [38] = "E", [45] = "R", [245] = "T", [246] = "Y", [44] = "Q", [32] = "W", [38] = "E", [45] = "R", [245] = "T", [246] = "Y",
@@ -15,49 +37,63 @@ local Keys = {
[244] = "M", [82] = ",", [81] = "." [244] = "M", [82] = ",", [81] = "."
} }
-- Target Creation -- -- Tables for storing created targets for the fallback system and zone management.
-- Target Entities, this is more based on qb-target's style of target creation, and translates those into ox or qb-target code -- local TextTargets = {} -- For fallback DrawText3D targets.
local targetEntities = {} 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. --- 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) --- @param entity number The entity ID for which the target is created.
--- based on the server configuration. It translates qb-target style options into the appropriate format --- @param opts table Array of option tables. Each option should include:
--- for the detected targeting system. --- - 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. --- @usage
---@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
--- ```lua --- ```lua
--- createEntityTarget(entityId, { ---createEntityTarget(entityId, {
--- { icon = "fas fa-car", label = "Open Vehicle", action = openVehicle }, --- {
--- { icon = "fas fa-lock", label = "Lock Vehicle", action = lockVehicle } --- action = function()
--- }, 2.5) --- openStorage()
--- end,
--- icon = "fas fa-box",
--- job = "police",
--- label = "Open Storage",
--- },
---}, 2.0)
--- ``` --- ```
function createEntityTarget(entity, opts, dist) function createEntityTarget(entity, opts, dist)
-- Store the target entity for later cleanup.
targetEntities[#targetEntities + 1] = entity targetEntities[#targetEntities + 1] = entity
local entityCoords = GetEntityCoords(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 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 local existingTarget = nil
for key, target in pairs(TextTargets) do -- Check if a target already exists at similar coordinates.
if #(target.coords - entityCoords) < 0.01 then -- Adjust the threshold for coordinate matching for _, target in pairs(TextTargets) do
if #(target.coords - entityCoords) < 0.01 then
existingTarget = target existingTarget = target
break break
end end
end end
local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Predefined key codes for options.
if existingTarget then if existingTarget then
-- Combine options -- Append new options to the existing target.
local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Extend key table as needed
for i = 1, #opts do for i = 1, #opts do
local key = keyTable[#existingTarget.options + i] local key = keyTable[#existingTarget.options + i]
opts[i].key = key opts[i].key = key
@@ -65,9 +101,8 @@ function createEntityTarget(entity, opts, dist)
existingTarget.options[#existingTarget.options + 1] = opts[i] existingTarget.options[#existingTarget.options + 1] = opts[i]
end end
else else
-- Create new target -- Create a new target entry.
local tempText = {} local tempText = {}
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
for i = 1, #opts do for i = 1, #opts do
opts[i].key = keyTable[i] 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
@@ -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 } TextTargets[entity] = { coords = vec3(entityCoords.x, entityCoords.y, entityCoords.z), buttontext = tempText, options = opts, dist = dist }
end end
elseif isStarted(OXTargetExport) then 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 = {} local options = {}
for i = 1, #opts do for i = 1, #opts do
options[i] = { options[i] = {
@@ -91,84 +126,95 @@ function createEntityTarget(entity, opts, dist)
end end
exports[OXTargetExport]:addLocalEntity(entity, options) exports[OXTargetExport]:addLocalEntity(entity, options)
elseif isStarted(QBTargetExport) then 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 } local options = { options = opts, distance = dist }
exports[QBTargetExport]:AddTargetEntity(entity, options) exports[QBTargetExport]:AddTargetEntity(entity, options)
end end
end end
local boxTargets = {} -------------------------------------------------------------
-- Box Zone Target Creation
-------------------------------------------------------------
--- Creates a box-shaped target zone with specified options and interaction distance. --- Creates a box-shaped target zone with specified options and interaction distance.
--- --- Supports different targeting systems 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 data table A table containing the box zone configuration. ---@param data table A table containing the box zone configuration.
--- - **name** (`string`): The name identifier for the zone. --- - name (`string`): The name identifier for the zone.
--- - **coords** (`vector3`): The center coordinates of the box. --- - coords (`vector3`): The center coordinates of the box.
--- - **width** (`number`): The width of the box. --- - width (`number`): The width of the box.
--- - **height** (`number`): The height of the box. --- - height (`number`): The height of the box.
--- - **options** (`table`): A table with additional options: --- - options (`table`): A table with additional options:
--- - **heading** (`number`): The rotation angle of the box. --- - heading (`number`): The rotation angle of the box.
--- - **debugPoly** (`boolean`): Whether to enable debug mode for the zone. --- - debugPoly (`boolean`): Whether to enable debug mode for the zone.
--- ---
---@param opts table A table of option configurations for the target. ---@param opts table A table of option configurations for the target.
--- - **icon** (`string`): The icon to display for the option. --- - icon (`string`): The icon to display for the option.
--- - **label** (`string`): The label text for the option. --- - label (`string`): The label text for the option.
--- - **item** (`string|nil`): (Optional) The item associated with the option. --- - item (`string|nil`): (Optional) The item associated with the option.
--- - **job** (`string|nil`): (Optional) The job required to interact 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. --- - 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. --- - onSelect (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target. ---@param dist number The interaction distance for the target.
--- ---
---@return string|table name identifier or target object of the created zone. ---@return string|table name identifier or target object of the created zone.
--- ---
---@usage ---@usage
--- ```lua ---```lua
--- createBoxTarget({ ---createBoxTarget(
--- name = 'storageBox', --- {
--- coords = vector3(100.0, 200.0, 30.0), --- 'storageBox',
--- width = 2.0, --- vector3(100.0, 200.0, 30.0),
--- height = 2.0, --- 2.0,
--- options = { heading = 0, debugPoly = false } --- 2.0,
--- }, { --- {
--- { icon = "fas fa-box", label = "Open Storage", action = openStorage } --- name = 'storageBox',
--- }, 1.5) --- 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) function createBoxTarget(data, opts, dist)
if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then 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 local existingTarget = nil
for key, target in pairs(TextTargets) do for _, target in pairs(TextTargets) do
if #(target.coords - data[2]) < 0.01 then -- Adjust the threshold as needed for coordinate precision if #(target.coords - data[2]) < 0.01 then
existingTarget = target existingTarget = target
break break
end end
end end
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
if existingTarget then if existingTarget then
-- Combine options
for i = 1, #opts do for i = 1, #opts do
local key = keyTable[#existingTarget.options + i] local key = keyTable[#existingTarget.options + i]
opts[i].key = key opts[i].key = key
existingTarget.buttontext[#existingTarget.buttontext+1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
existingTarget.options[#existingTarget.options+1] = opts[i] existingTarget.options[#existingTarget.options + 1] = opts[i]
end end
else else
-- Create new target
local tempText = {} local tempText = {}
for i = 1, #opts do for i = 1, #opts do
opts[i].key = keyTable[i] 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 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 end
return data[1] return data[1]
elseif isStarted(OXTargetExport) then 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 = {} local options = {}
for i = 1, #opts do for i = 1, #opts do
options[i] = { options[i] = {
@@ -193,39 +239,38 @@ function createBoxTarget(data, opts, dist)
debug = data[5].debugPoly, debug = data[5].debugPoly,
options = options options = options
}) })
boxTargets[#boxTargets+1] = target boxTargets[#boxTargets + 1] = target
return target return target
elseif isStarted(QBTargetExport) then 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 options = { options = opts, distance = dist }
local target = exports[QBTargetExport]:AddBoxZone(data[1], data[2], data[3], data[4], data[5], options) 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] return data[1]
end end
end end
local circleTargets = {} -------------------------------------------------------------
-- Circle Zone Target Creation
-------------------------------------------------------------
--- Creates a circular target zone with specified options and interaction distance. --- Creates a circular target zone with specified options and interaction distance.
--- --- Supports different targeting systems 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 data table A table containing the circle zone configuration. ---@param data table A table containing the circle zone configuration.
--- - **name** (`string`): The name identifier for the zone. --- - name (`string`): The name identifier for the zone.
--- - **coords** (`vector3`): The center coordinates of the circle. --- - coords (`vector3`): The center coordinates of the circle.
--- - **radius** (`number`): The radius of the circle. --- - radius (`number`): The radius of the circle.
--- - **options** (`table`): A table with additional options: --- - options (`table`): A table with additional options:
--- - **debugPoly** (`boolean`): Whether to enable debug mode for the zone. --- - debugPoly (`boolean`): Whether to enable debug mode for the zone.
--- ---
---@param opts table A table of option configurations for the target. ---@param opts table A table of option configurations for the target.
--- - **icon** (`string`): The icon to display for the option. --- - icon (`string`): The icon to display for the option.
--- - **label** (`string`): The label text for the option. --- - label (`string`): The label text for the option.
--- - **item** (`string|nil`): (Optional) The item associated with the option. --- - item (`string|nil`): (Optional) The item associated with the option.
--- - **job** (`string|nil`): (Optional) The job required to interact 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. --- - 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. --- - onSelect (`function|nil`): (Optional) The function to execute when the option is selected.
---@param dist number The interaction distance for the target. ---@param dist number The interaction distance for the target.
--- ---
---@return string|table name identifier or target object of the created zone. ---@return string|table name identifier or target object of the created zone.
@@ -243,37 +288,34 @@ local circleTargets = {}
--- ``` --- ```
function createCircleTarget(data, opts, dist) function createCircleTarget(data, opts, dist)
if Config.System.DontUseTarget then 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 local existingTarget = nil
for key, target in pairs(TextTargets) do for _, target in pairs(TextTargets) do
if #(target.coords - data[2]) < 0.01 then -- Adjust the threshold for precision if #(target.coords - data[2]) < 0.01 then
existingTarget = target existingTarget = target
break break
end end
end end
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
if existingTarget then if existingTarget then
-- Combine options
local keyTable = { 38, 29, 303, 45, 46, 47, 48 } -- Extend key table as needed
for i = 1, #opts do for i = 1, #opts do
local key = keyTable[#existingTarget.options + i] local key = keyTable[#existingTarget.options + i]
opts[i].key = key opts[i].key = key
existingTarget.buttontext[#existingTarget.buttontext+1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label existingTarget.buttontext[#existingTarget.buttontext + 1] = " ~b~[~w~"..Keys[key].."~b~] ~w~"..opts[i].label
existingTarget.options[#existingTarget.options+1] = opts[i] existingTarget.options[#existingTarget.options + 1] = opts[i]
end end
else else
-- Create new target
local tempText = {} local tempText = {}
local keyTable = { 38, 29, 303, 45, 46, 47, 48 }
for i = 1, #opts do for i = 1, #opts do
opts[i].key = keyTable[i] 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 end
TextTargets[data[1]] = { coords = data[2], buttontext = tempText, options = opts, dist = dist } TextTargets[data[1]] = { coords = data[2], buttontext = tempText, options = opts, dist = dist }
end end
return data[1] return data[1]
elseif isStarted(OXTargetExport) then 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 = {} local options = {}
for i = 1, #opts do for i = 1, #opts do
options[i] = { options[i] = {
@@ -283,7 +325,7 @@ function createCircleTarget(data, opts, dist)
groups = opts[i].job or opts[i].gang, groups = opts[i].job or opts[i].gang,
onSelect = opts[i].onSelect or opts[i].action, onSelect = opts[i].onSelect or opts[i].action,
canInteract = function(_, distance) canInteract = function(_, distance)
return distance < dist and true or false return distance < dist
end end
} }
end end
@@ -293,45 +335,46 @@ function createCircleTarget(data, opts, dist)
debug = data[4].debugPoly, debug = data[4].debugPoly,
options = options options = options
}) })
circleTargets[#circleTargets+1] = target circleTargets[#circleTargets + 1] = target
return target return target
elseif isStarted(QBTargetExport) then 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 options = { options = opts, distance = dist }
local target = exports[QBTargetExport]:AddCircleZone(data[1], data[2], data[3], data[4], options) 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] return data[1]
end end
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) --- @param models table Array of model identifiers.
--- based on the server configuration. It translates qb-target style options into the appropriate format --- @param opts table Array of option tables (same structure as in createEntityTarget).
--- for the detected targeting system. --- @param dist number The interaction distance for the target.
--- ---
---@param entity number The entity ID to create a target for. --- @usage
---@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
--- ```lua --- ```lua
--- createEntityTarget(entityId, { ---createModelTarget(
--- { icon = "fas fa-car", label = "Open Vehicle", action = openVehicle }, ---{ model1, model2 },
--- { icon = "fas fa-lock", label = "Lock Vehicle", action = lockVehicle } ---{
--- }, 2.5) --- {
--- ``` --- action = function()
--- openStorage()
--- end,
--- icon = "fas fa-box",
--- job = "police",
--- label = "Open Storage",
--- },
---}, 2.0)
---```
function createModelTarget(models, opts, dist) function createModelTarget(models, opts, dist)
if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then if Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport)) then
-- -- Fallback for model targets is not implemented.
elseif isStarted(OXTargetExport) then elseif isStarted(OXTargetExport) then
debugPrint("^6Bridge^7: ^2Creating new ^3Model^2 target with ^6"..OXTargetExport) debugPrint("^6Bridge^7: ^2Creating new ^3Model^2 target with ^6"..OXTargetExport)
local options = {} local options = {}
@@ -355,28 +398,32 @@ function createModelTarget(models, opts, dist)
end end
end end
-------------------------------------------------------------
-- Target Removal Functions
-------------------------------------------------------------
-- Simple function to remove an entity target created within the script --
--- Removes a previously created entity target. --- 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. --- @param entity number The entity ID whose target should be removed.
--- ---
--- @usage --- @usage
--- ```lua
--- removeEntityTarget(entityId) --- removeEntityTarget(entityId)
--- ```
function removeEntityTarget(entity) function removeEntityTarget(entity)
if isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveTargetEntity(entity) end if isStarted(QBTargetExport) then
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeLocalEntity(entity, nil) end exports[QBTargetExport]:RemoveTargetEntity(entity)
if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport))) then TextTargets[entity] = nil end 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 end
-- Simple function to remove circle or box targets in the script --
--- Removes a previously created zone target. --- 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. --- @param target string|table The name identifier or target object of the zone to remove.
--- ---
--- @usage --- @usage
@@ -385,54 +432,60 @@ end
--- removeZoneTarget(targetObject) --- removeZoneTarget(targetObject)
--- ``` --- ```
function removeZoneTarget(target) function removeZoneTarget(target)
if isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(target) end if isStarted(QBTargetExport) then
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(target, true) end exports[QBTargetExport]:RemoveZone(target)
if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport))) then TextTargets[target] = nil end 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 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 if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStarted(QBTargetExport))) and not isServer() then
CreateThread(function() CreateThread(function()
while true do while true do
local pedCoords = GetEntityCoords(PlayerPedId()) local pedCoords = GetEntityCoords(PlayerPedId())
local camCoords = GetGameplayCamCoord() local camCoords = GetGameplayCamCoord()
local camRotation = GetGameplayCamRot(2) -- Get camera rotation in degrees local camRotation = GetGameplayCamRot(2) -- Camera rotation (degrees)
local camForwardVector = RotationToDirection(camRotation) -- Convert rotation to direction vector local camForwardVector = RotationToDirection(camRotation) -- Convert rotation to direction
local closestTarget = nil local closestTarget = nil
local closestDist = math.huge local closestDist = math.huge
for k, v in pairs(TextTargets) do -- Identify the closest target in front of the camera.
local targetCoords = v.coords for _, target in pairs(TextTargets) do
local dist = #(pedCoords - targetCoords) local dist = #(pedCoords - target.coords)
local vecToTarget = targetCoords - camCoords local vecToTarget = target.coords - camCoords
-- Normalize the vector to the target
local vecToTargetNormalized = normalizeVector(vecToTarget) 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 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 <= target.dist and isFacingTarget then
if dist <= v.dist and isFacingTarget then
if dist < closestDist then if dist < closestDist then
closestDist = dist closestDist = dist
closestTarget = v closestTarget = target
end end
end end
end end
for k, v in pairs(TextTargets) do -- Render the DrawText3D targets and listen for key presses.
local isClosest = (v == closestTarget) for _, target in pairs(TextTargets) do
if #(pedCoords - v.coords) <= v.dist then local isClosest = (target == closestTarget)
for i = 1, #v.options do if #(pedCoords - target.coords) <= target.dist then
if IsControlJustPressed(0, v.options[i].key) and isClosest then for i = 1, #target.options do
if v.options[i].onSelect then v.options[i].onSelect() end if IsControlJustPressed(0, target.options[i].key) and isClosest then
if v.options[i].action then v.options[i].action() end if target.options[i].onSelect then target.options[i].onSelect() end
if target.options[i].action then target.options[i].action() end
end 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
end end
Wait(0) Wait(0)
@@ -440,18 +493,34 @@ if (Config.System.DontUseTarget or (not isStarted(OXTargetExport) and not isStar
end) end)
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() onResourceStop(function()
-- Remove entity targets.
for i = 1, #targetEntities do for i = 1, #targetEntities do
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeLocalEntity(targetEntities[i], nil) if isStarted(OXTargetExport) then
elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveTargetEntity(targetEntities[i]) end exports[OXTargetExport]:removeLocalEntity(targetEntities[i], nil)
elseif isStarted(QBTargetExport) then
exports[QBTargetExport]:RemoveTargetEntity(targetEntities[i])
end
end end
-- Remove box zone targets.
for i = 1, #boxTargets do for i = 1, #boxTargets do
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(boxTargets[i], true) if isStarted(OXTargetExport) then
elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(boxTargets[i].name) end exports[OXTargetExport]:removeZone(boxTargets[i], true)
elseif isStarted(QBTargetExport) then
exports[QBTargetExport]:RemoveZone(boxTargets[i].name)
end
end end
-- Remove circle zone targets.
for i = 1, #circleTargets do for i = 1, #circleTargets do
if isStarted(OXTargetExport) then exports[OXTargetExport]:removeZone(circleTargets[i], true) if isStarted(OXTargetExport) then
elseif isStarted(QBTargetExport) then exports[QBTargetExport]:RemoveZone(circleTargets[i].name) end exports[OXTargetExport]:removeZone(circleTargets[i], true)
elseif isStarted(QBTargetExport) then
exports[QBTargetExport]:RemoveZone(circleTargets[i].name)
end
end end
end, true) end, true)

View File

@@ -1,22 +1,29 @@
-- Get Vehicle Info -- --[[
local lastCar = nil Vehicle Info & Properties Module
local carInfo = {} ----------------------------------
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. --- @param vehicle number The entity ID of the vehicle to search for.
--- If it's a new vehicle, it retrieves its model and searches the 'Vehicles' table for matching entries. --- @return table|nil table A table containing the vehicle's details or nil if the vehicle is invalid.
--- 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. --- @usage
---
---@return table|nil table containing the vehicle's `name`, `price`, and `class`, or `nil` if the vehicle is invalid.
---
---@usage
--- ```lua --- ```lua
--- local info = searchCar(vehicleEntity) --- 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) function searchCar(vehicle)
if lastCar ~= vehicle then -- If same car, use previous info if lastCar ~= vehicle then -- If same car, use previous info
@@ -78,27 +85,26 @@ function searchCar(vehicle)
end end
end end
-- Vehicle Properties -- -------------------------------------------------------------
-- Vehicle Properties Functions
-------------------------------------------------------------
--- Retrieves the properties of a given vehicle. --- Retrieves the properties of a given vehicle using the active framework.
---
--- 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.
--- ---
--- @param vehicle number The entity ID of the vehicle. --- @param vehicle number The entity ID of the vehicle.
--- --- @return table|nil table A table containing the vehicle's properties or nil if invalid.
--- @return table|nil table containing the vehicle's properties, or `nil` if the vehicle is invalid or the framework is not detected.
--- ---
--- @usage --- @usage
--- ```lua --- ```lua
--- local props = getVehicleProperties(vehicleEntity) --- local props = getVehicleProperties(vehicleEntity)
--- if props then --- if props then
--- -- Manipulate vehicle properties --- -- Use vehicle properties
--- end --- end
--- ``` --- ```
function getVehicleProperties(vehicle) function getVehicleProperties(vehicle)
if not vehicle then return nil end
local properties = {} local properties = {}
if vehicle == nil then return nil end
if isStarted(QBExport) and not isStarted(QBXExport) then if isStarted(QBExport) and not isStarted(QBXExport) then
properties = Core.Functions.GetVehicleProperties(vehicle) 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]") 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 return properties
end 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. --- @param vehicle number The entity ID of the vehicle.
--- It first retrieves the current properties and checks for differences before applying the new ones. --- @param props table The new properties to apply.
--- ---
---@param vehicle number The entity ID of the vehicle. --- @usage
---@param props table The properties to set on the vehicle.
---
---@usage
--- ```lua --- ```lua
--- setVehicleProperties(vehicleEntity, newProperties) --- setVehicleProperties(vehicleEntity, newProperties)
--- ``` --- ```
function setVehicleProperties(vehicle, props) function setVehicleProperties(vehicle, props)
local oldProps = getVehicleProperties(vehicle)
if checkDifferences(vehicle, props) then if checkDifferences(vehicle, props) then
--if debugMode then debugDifferences(vehicle, props) end
if not DoesEntityExist(vehicle) then 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 end
if isStarted(QBExport) and not isStarted(QBXExport) then if isStarted(QBExport) and not isStarted(QBXExport) then
Core.Functions.SetVehicleProperties(vehicle, props) 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]") 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 end
--- Checks for differences between the current and new vehicle properties. --- 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. --- @param vehicle number The entity ID of the vehicle.
--- It logs the differences for debugging purposes. --- @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. --- @usage
---@param newProps table The new properties to compare against the current ones.
---
---@return boolean `true` if differences are found, `false` otherwise.
---
---@usage
--- ```lua --- ```lua
--- if checkDifferences(vehicleEntity, newProperties) then --- if checkDifferences(vehicleEntity, newProperties) then
--- setVehicleProperties(vehicleEntity, newProperties) --- setVehicleProperties(vehicleEntity, newProperties)
@@ -158,43 +158,41 @@ end
function checkDifferences(vehicle, newProps) function checkDifferences(vehicle, newProps)
local oldProps = getVehicleProperties(vehicle) local oldProps = getVehicleProperties(vehicle)
debugPrint("^6Bridge^7: ^2Finding differences in ^3Vehicle Properties^7") debugPrint("^6Bridge^7: ^2Finding differences in ^3Vehicle Properties^7")
local allow = false local differencesFound = false
for k in pairs(oldProps) do for k in pairs(oldProps) do
if json.encode(oldProps[k]) ~= json.encode(newProps[k]) then 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: ^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 })) debugPrint("^6Bridge^7: ^5New ^7[^3"..k.."^7] - "..json.encode(newProps[k], { indent = true }))
end end
end end
return allow
return differencesFound
end 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 `getScript()..ox:setVehicleProperties`
--- --- @param netId number The network ID of the vehicle.
---@event --- @param props table The new vehicle properties.
---@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)
RegisterNetEvent(getScript()..":ox:setVehicleProperties", function(netId, props) RegisterNetEvent(getScript()..":ox:setVehicleProperties", function(netId, props)
local vehicle = NetworkGetEntityFromNetworkId(netId) local vehicle = NetworkGetEntityFromNetworkId(netId)
local value = props local value = props
Entity(vehicle).state[getScript()..':setVehicleProperties'] = value Entity(vehicle).state[getScript()..':setVehicleProperties'] = value
end) 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 state bag's name.
--- --- @param key string The key that changed.
---@param bagName string The name of the state bag. --- @param value table The new state value.
---@param key string The key that changed.
---@param value table The new value of the state.
---
---@usage
--- -- Automatically handled when the state bag changes
AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagName, _, value) AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagName, _, value)
if not value or not GetEntityFromStateBagName then return end if not value or not GetEntityFromStateBagName then return end
local entity = GetEntityFromStateBagName(bagName) local entity = GetEntityFromStateBagName(bagName)
@@ -208,8 +206,10 @@ AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagN
end end
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. --- 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. --- It requests network control and sets the vehicle accordingly to synchronize changes across clients.
--- ---
@@ -222,6 +222,7 @@ end)
function pushVehicle(entity) function pushVehicle(entity)
SetVehicleModKit(entity, 0) SetVehicleModKit(entity, 0)
if entity ~= 0 and DoesEntityExist(entity) then if entity ~= 0 and DoesEntityExist(entity) then
-- Request network control if not already controlled.
if not NetworkHasControlOfEntity(entity) then if not NetworkHasControlOfEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.") debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.")
NetworkRequestControlOfEntity(entity) NetworkRequestControlOfEntity(entity)
@@ -231,11 +232,13 @@ function pushVehicle(entity)
timeout = timeout - 100 timeout = timeout - 100
end end
if NetworkHasControlOfEntity(entity) then 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
end end
-- Set as mission entity if not already set.
if not IsEntityAMissionEntity(entity) then 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) SetEntityAsMissionEntity(entity, true, true)
local timeout = 2000 local timeout = 2000
while timeout > 0 and not IsEntityAMissionEntity(entity) do while timeout > 0 and not IsEntityAMissionEntity(entity) do
@@ -249,41 +252,47 @@ function pushVehicle(entity)
end end
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) function getClosestVehicle(coords, src)
if src then local ped, vehicles, closestDistance, closestVehicle
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)
if closestDistance == -1 or closestDistance > distance then if src then
closestVehicle = vehicles[i] ped = GetPlayerPed(src)
closestDistance = distance vehicles = GetAllVehicles()
end else
end ped = PlayerPedId()
return closestVehicle, closestDistance vehicles = GetGamePool('CVehicle')
end 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 end

View File

@@ -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("%<br>", "\n")
exports['roadphone']:sendMail(data)
elseif isStarted("lb-phone") then phoneResource = "lb-phone"
data.message = data.message:gsub("%<br>", "\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. --- Registers a command with the active command system.
--- --- This function supports multiple command systems (OXLib, qb-core, ESX Legacy).
--- This function detects whether the server is using OXLib or qb-core for command registration
--- and registers the command accordingly.
--- ---
--- @param command string The name of the command to register. --- @param command string The name of the command to register.
--- @param options table A table containing command options. --- @param options table A table containing command options.
--- - **help** (`string`): The help description for the command. --- - help (`string`): The help description for the command.
--- - **params** (`table`): A table of parameters for the command. --- - params (`table`): A table of parameters for the command.
--- - **callback** (`function`): The function to execute when the command is called. --- - callback (`function`): The function to execute when the command is called.
--- - **autocomplete** (`function|nil`): (Optional) A function for autocompletion. --- - autocomplete (`function|nil`): (Optional) A function for autocompletion.
--- - **restrictedGroup** (`string|nil`): (Optional) The user group required to execute the command. --- - restrictedGroup (`string|nil`): (Optional) The user group required to execute the command.
--- ---
--- @usage --- @usage
--- ````lua --- ```lua
--- -- Server Side:
--- registerCommand("greet", { --- registerCommand("greet", {
--- "Greets the player", --- "Greets the player",
--- { name = "name", help = "Name of the player to greet" }, --- { 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, --- nil,
--- "admin" --- "admin"
--- }) --- })
@@ -170,10 +25,10 @@ function registerCommand(command, options)
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7"..OXLibExport, command) 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]) lib.addCommand(command, { help = options[1], restricted = options[5] and "group."..options[5] or nil }, options[4])
elseif isStarted(QBExport) and not isStarted(QBXExport) then elseif isStarted(QBExport) and not isStarted(QBXExport) then
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^7 "..QBExport, command) 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) Core.Commands.Add(command, options[1], options[2], options[3], options[4], options[5] or nil)
elseif isStarted(ESXExport) then elseif isStarted(ESXExport) then
debugPrint("^6Bridge^7: ^2Registering ^3Command^2 with ^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) ESX.RegisterCommand(command, options[5] or 'admin', function(xPlayer, args, showError)
options[4](xPlayer.source, args, showError) options[4](xPlayer.source, args, showError)
end, false, { help = options[1] }) end, false, { help = options[1] })
@@ -181,19 +36,24 @@ function registerCommand(command, options)
end end
--- Registers a stash with the active inventory system. --- 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 Unique stash identifier.
--- --- @param label string Display name for the stash.
--- @param name string The unique identifier for the stash. --- @param slots number|nil (Optional) Number of slots (default 50).
--- @param label string The display name for the stash. --- @param weight number|nil (Optional) Maximum weight (default 4000000).
--- @param slots number|nil (Optional) The number of slots in the stash. Defaults to 50. --- @param owner string|nil (Optional) Owner identifier for personal stashes.
--- @param weight number|nil (Optional) The maximum weight the stash can hold. Defaults to 4,000,000. --- @param coords table|nil (Optional) Coordinates for the stash location.
--- @param owner string|nil (Optional) The owner identifier for personal stashes.
--- @param coords table|nil (Optional) The coordinates for the stash location.
---
--- @usage --- @usage
--- ```lua --- ```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) function registerStash(name, label, slots, weight, owner, coords)
if isStarted(OXInv) then 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) exports[OXInv]:RegisterStash(name, label, slots or 50, weight or 4000000, owner or nil)
elseif isStarted(QSInv) then elseif isStarted(QSInv) then
debugPrint("^6Bridge^7: ^2Registering ^3QS ^2Stash^7:", name, label) 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
end end
--- Registers a shop with the active inventory system. --- 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 Unique shop identifier.
--- --- @param label string Display name for the shop.
--- @param name string The unique identifier for the shop. --- @param items table List of available shop items.
--- @param label string The display name for the shop. --- @param society string|nil (Optional) Society identifier for shared shops.
--- @param items table The list of items available in the shop.
--- @param society string|nil (Optional) The society identifier for shared shops.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons") --- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
@@ -221,13 +87,11 @@ end
function registerShop(name, label, items, society) function registerShop(name, label, items, society)
if isStarted(OXInv) then if isStarted(OXInv) then
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label) debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label)
exports[OXInv]:RegisterShop( exports[OXInv]:RegisterShop(name, {
name, { name = label,
name = label, inventory = items,
inventory = items, society = society,
society = society, })
}
)
elseif isStarted(QBInv) and QBInvNew then elseif isStarted(QBInv) and QBInvNew then
debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label) debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label)
exports[QBInv]:CreateShop({ exports[QBInv]:CreateShop({
@@ -240,25 +104,22 @@ function registerShop(name, label, items, society)
end end
end end
-- Server-Side Event Registration
if isServer() then if isServer() then
--- Registers an event to create an OX stash from the server. --- Registers an event to create an OX stash from the server.
--- When triggered, it calls registerStash with the provided parameters.
--- ---
--- @event --- @event server:makeOXStash
--- @param name string The unique identifier for the stash. --- @param name string Unique stash identifier.
--- @param label string The display name for the stash. --- @param label string Display name for the stash.
--- @param slots number|nil (Optional) The number of slots in the stash. --- @param slots number|nil (Optional) Number of slots.
--- @param weight number|nil (Optional) The maximum weight the stash can hold. --- @param weight number|nil (Optional) Maximum weight.
--- @param owner string|nil (Optional) The owner identifier for personal stashes. --- @param owner string|nil (Optional) Owner identifier.
--- @param coords table|nil (Optional) The coordinates for the stash location. --- @param coords table|nil (Optional) Stash coordinates.
---
--- @usage --- @usage
--- ```lua --- ```lua
--- -- Server-side:
--- TriggerEvent(getScript()..":server:makeOXStash", name, label, slots, weight, owner, coords) --- TriggerEvent(getScript()..":server:makeOXStash", name, label, slots, weight, owner, coords)
--- ``` --- ```
RegisterNetEvent(getScript()..":server:makeOXStash", function(name, label, slots, weight, owner, coords) RegisterNetEvent(getScript()..":server:makeOXStash", function(name, label, slots, weight, owner, coords)
registerStash(name, label, slots, weight, owner, coords) registerStash(name, label, slots, weight, owner, coords)
end) end)
end end

View File

@@ -39,6 +39,7 @@ for _, v in pairs({ -- This is a specific load order
'duifunctions.lua', 'duifunctions.lua',
-- Native Scaleforms -- Native Scaleforms
'scaleforms/scaleform_basic.lua',
'scaleforms/bigMessageInstance.lua', 'scaleforms/bigMessageInstance.lua',
'scaleforms/countDownHandler.lua', 'scaleforms/countDownHandler.lua',
'scaleforms/debugScaleform.lua', 'scaleforms/debugScaleform.lua',
@@ -56,11 +57,13 @@ for _, v in pairs({ -- This is a specific load order
'wrapperfunctions.lua', 'wrapperfunctions.lua',
'polyZone.lua', 'polyZone.lua',
'inventories.lua',
'itemcontrol.lua', 'itemcontrol.lua',
'playerfunctions.lua', 'playerfunctions.lua',
'metaHandlers.lua', 'metaHandlers.lua',
'jobfunctions.lua', 'jobfunctions.lua',
'banking.lua', 'societybank.lua',
'phones.lua',
-- Interactions -- Interactions
'targets.lua', 'targets.lua',
@@ -78,7 +81,9 @@ for _, v in pairs({ -- This is a specific load order
'scaleEntity.lua', 'scaleEntity.lua',
'vehicles.lua', 'vehicles.lua',
'effects.lua', 'effects.lua',
'versioncheck.lua'
-- Do version check last
'_versioncheck.lua'
}) do }) do
if debugMode then if debugMode then
--print("^5Loading^7: 'jim_bridge/shared/"..v.."' ^2into ^7'"..GetCurrentResourceName().."' ...") --print("^5Loading^7: 'jim_bridge/shared/"..v.."' ^2into ^7'"..GetCurrentResourceName().."' ...")