I am still alive

This commit is contained in:
Jim Shield
2025-03-26 21:43:48 +00:00
committed by GitHub
parent 2572f86030
commit 8611bc6d3c
21 changed files with 607 additions and 292 deletions

View File

@@ -57,6 +57,7 @@ function triggerCallback(callbackName, ...)
p:resolve(cbResult) p:resolve(cbResult)
end, ...) end, ...)
result = Citizen.Await(p) result = Citizen.Await(p)
Wait(10)
elseif isStarted(ESXExport) then elseif isStarted(ESXExport) then
local p = promise.new() local p = promise.new()
ESX.TriggerServerCallback(callbackName, function(cbResult) ESX.TriggerServerCallback(callbackName, function(cbResult)

View File

@@ -95,7 +95,7 @@ function openMenu(Menu, data)
Menu[k].args = Menu[k].params.args or {} Menu[k].args = Menu[k].params.args or {}
end end
if Menu[k].isMenuHeader then if Menu[k].isMenuHeader then
Menu[k].disabled = true Menu[k].readOnly = true
end end
end end
local menuID = 'Menu' local menuID = 'Menu'

View File

@@ -140,7 +140,7 @@ if isStarted(QBXExport) or isStarted(QBExport) then
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, hash = GetHashKey(k), price = v.price, name = v.name, brand = v.make }
end end
vehResource = OXCoreExport vehResource = OXCoreExport
@@ -159,6 +159,7 @@ elseif isStarted(ESXExport) then
Vehicles = Vehicles or {} Vehicles = Vehicles or {}
Vehicles[v.model] = { Vehicles[v.model] = {
model = v.model, model = v.model,
hash = GetHashKey(v.model),
price = v.price, price = v.price,
name = v.name, name = v.name,
brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper) brand = GetMakeNameFromVehicleModel(v.model):lower():gsub("^%l", string.upper)

View File

@@ -12,6 +12,14 @@
------------------------------------------------------------- -------------------------------------------------------------
CraftLock = false CraftLock = false
-- helper filter table for crafting menus
local excludeKeys = {
amount = true, metadata = true, description = true, info = true,
job = true, gang = true, oneUse = true, slot = true,
blueprintRef = true, craftingLevel = true, craftedItems = true,
hasCrafted = true, exp = true, anim = true, time = true,
}
------------------------------------------------------------- -------------------------------------------------------------
-- Crafting Menu -- Crafting Menu
------------------------------------------------------------- -------------------------------------------------------------
@@ -73,6 +81,7 @@ function craftingMenu(data)
for i = 1, #Recipes do for i = 1, #Recipes do
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
-- Retreive list of already crafted items from playermetadata to see if we should class this recipe as "new"
craftedItems = GetMetadata(nil, "craftedItems") or {} craftedItems = GetMetadata(nil, "craftedItems") or {}
data.craftable.craftedItems = craftedItems data.craftable.craftedItems = craftedItems
end end
@@ -88,12 +97,6 @@ function craftingMenu(data)
for i = 1, #Recipes do for i = 1, #Recipes do
if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end if not Recipes[i]["amount"] then Recipes[i]["amount"] = 1 end
for k, _ in pairs(Recipes[i]) do for k, _ in pairs(Recipes[i]) do
local excludeKeys = {
amount = true, metadata = true, description = true, info = true,
job = true, gang = true, oneUse = true, slot = true,
blueprintRef = true, craftingLevel = true, craftedItems = true,
hasCrafted = true, exp = true, anim = true, time = true,
}
if not excludeKeys[k] then if not excludeKeys[k] then
local hasjob = true local hasjob = true
if Recipes[i].job then if Recipes[i].job then
@@ -203,48 +206,79 @@ end
--- }) --- })
--- ``` --- ```
function multiCraft(data) function multiCraft(data)
local Menu = {} local max = 0
local amounts = Config.Crafting.MultiCraftAmounts local stashName = nil
local metadata = data.metadata or nil for i = 1, 100 do
-- Header for the multi-craft menu.
Menu[#Menu + 1] = {
isMenuHeader = true,
icon = invImg(metadata and metadata.image or data.item),
header = metadata and metadata.label or Items[data.item].label,
}
for k in pairsByKeys(amounts) do
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) debugPrint("")
settext = settext..(settext ~= "" and br or "")..Items[l].label..(b * k > 1 and " x"..b * k or "") itemTable[l] = (b * i)
Wait(0)
end end
local disable, stashname = checkHasItem(data.stashName, itemTable)
Menu[#Menu + 1] = { if data.stashName then
isMenuHeader = not disable, debugPrint("")
arrow = disable, local hasItems, stashname = checkHasItem(data.stashName, itemTable)
header = "Craft - x"..(k * data.craft.amount), if hasItems == true then
txt = settext, max += 1
onSelect = function() stashName = stashname
makeItem({ else
item = data.item, break
craft = data.craft, end
craftable = data.craftable, else
amount = k, debugPrint("")
coords = data.coords, local has, _ = hasItem(itemTable, nil, nil)
stashName = stashname, if has then
stashTable = data.stashName, max += 1
onBack = data.onBack, else
metadata = data.metadata, break
}) end
end, end
} Wait(10)
end end
openMenu(Menu, { header = data.craftable.Header, onBack = function() craftingMenu(data) end }) local dialog = createInput(data.craftable.Header, {
((Config.System.Menu == "ox") and {
type = "slider",
label = "How many to craft?",
required = true,
default = 1,
min = 1,
max = max
}) or nil,
((Config.System.Menu == "qb") and {
type = "number",
label = "How many to craft?"..br.."Max: "..max,
name = "amount",
isRecuired = true,
default = 1,
}) or nil,
})
if dialog then
if Config.System.Menu == "ox" then
end
if Config.System.Menu == "qb" then
if dialog["amount"] > max or dialog["amount"] < 1 or dialog["amount"] == nil or dialog["amount"] == "" then
triggerNotify(nil, "Invalid Amount", "error")
craftingMenu(data)
return
end
end
makeItem({
item = data.item,
craft = data.craft,
craftable = data.craftable,
amount = dialog["amount"] or dialog[1],
coords = data.coords,
stashName = stashName or nil,
--stashTable = data.stashName,
onBack = data.onBack,
metadata = data.metadata,
})
end
end end
------------------------------------------------------------- -------------------------------------------------------------
@@ -300,12 +334,6 @@ function makeItem(data)
for i = 1, craftAmount do for i = 1, craftAmount do
for k, v in pairs(data.craft) do for k, v in pairs(data.craft) do
local excludeKeys = {
amount = true, info = true, metadata = true, description = true,
job = true, gang = true, oneUse = true, slot = true,
blueprintRef = true, craftingLevel = true, craftedItems = true,
hasCrafted = true, exp = true, anim = true, time = 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
@@ -392,6 +420,7 @@ end
--- @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 ingredients 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.
--- @usage
RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata) RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable, stashName, metadata)
local src = source local src = source
local hasItems, hasTable = hasItem(ItemMake, 1, src) local hasItems, hasTable = hasItem(ItemMake, 1, src)
@@ -424,188 +453,6 @@ RegisterNetEvent(getScript()..":Crafting:GetItem", function(ItemMake, craftable,
end end
addItem(ItemMake, craftable.amount or 1, metadata, src) addItem(ItemMake, craftable.amount or 1, metadata, src)
-- Optionally, add experience here: -- Optionally, add experience here:
-- for example:
-- if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end -- if isStarted("core_skills") then exports["core_skills"]:AddExperience(src, 2) end
end) end)
-------------------------------------------------------------
-- Selling Menu and Animation
-------------------------------------------------------------
--- Opens a selling menu with available items and prices.
---
--- @param data table Contains selling menu data:
--- - sellTable (`table`) Table with Header and Items (item names and prices).
--- - ped (optional) (`number`) Ped entity involved.
--- - onBack (optional) (`function`) Callback for returning.
--- @usage
--- ```lua
--- sellMenu({
--- sellTable = {
--- Header = "Sell Items",
--- Items = {
--- ["gold_ring"] = 100,
--- ["diamond"] = 500,
--- },
--- },
--- ped = pedEntity,
--- onBack = function() print("Returning to previous menu") end,
--- })
--- ```
function sellMenu(data)
local origData = data
local Menu = {}
if data.sellTable.Items then
local itemList = {}
for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end
local _, hasTable = hasItem(itemList)
for k, v in pairsByKeys(data.sellTable.Items) do
Menu[#Menu + 1] = {
isMenuHeader = not hasTable[k].hasItem,
icon = invImg(k),
header = Items[k].label..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
txt = Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"],
onSelect = function()
sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end })
end,
}
end
else
for k, v in pairsByKeys(data.sellTable) do
if type(v) == "table" then
Menu[#Menu + 1] = {
arrow = true,
header = k,
txt = "Amount of items: "..countTable(v.Items),
onSelect = function()
v.onBack = function() sellMenu(origData) end
v.sellTable = data.sellTable[k]
sellMenu(v)
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,
})
end
--- Plays the selling animation and processes the sale transaction.
---
--- Checks if the player has the item, plays animations, triggers the server event for selling,
--- and then calls the onBack callback if provided.
---
--- @param data table Contains:
--- `- item: The item to sell.
--- `- price: Price per item.
--- `- ped (optional): Ped entity involved.
--- `- onBack (optional): Callback to call on completion.
---@usage
--- ```lua
--- sellAnim({
--- item = "gold_ring",
--- price = 100,
--- ped = pedEntity,
--- onBack = function() sellMenu(data) end,
--- })
--- ```
function sellAnim(data)
if not hasItem(data.item, 1) then
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error")
return
end
-- Remove any attached clipboard objects.
for _, obj in pairs(GetGamePool('CObject')) do
for _, model in pairs({ `p_cs_clipboard` }) do
if GetEntityModel(obj) == model and IsEntityAttachedToEntity(data.ped, obj) then
DeleteObject(obj)
DetachEntity(obj, 0, 0)
SetEntityAsMissionEntity(obj, true, true)
Wait(100)
DeleteEntity(obj)
end
end
end
TriggerServerEvent(getScript().."Sellitems", data)
lookEnt(data.ped)
local dict = "mp_common"
playAnim(dict, "givetake2_a", 0.3, 2)
playAnim(dict, "givetake2_b", 0.3, 2, data.ped)
Wait(2000)
StopAnimTask(PlayerPedId(), dict, "givetake2_a", 0.5)
StopAnimTask(data.ped, dict, "givetake2_b", 0.5)
if data.onBack then data.onBack() end
end
--- Server event handler for processing item sales.
--- Removes sold items from inventory and funds the player based on the sale.
RegisterNetEvent(getScript().."Sellitems", function(data)
local src = source
local hasItems, hasTable = hasItem(data.item, 1, src)
if hasItems then
removeItem(data.item, hasTable[data.item].count, src)
TriggerEvent(getScript()..":server:FundPlayer", (hasTable[data.item].count * data.price), "cash", src)
else
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error", src)
end
end)
-------------------------------------------------------------
-- Shop Interface
-------------------------------------------------------------
--- Opens a shop interface for the player.
---
--- Checks job/gang restrictions, then uses the active inventory system to open the shop.
--- @param data table Contains:
--- - shop (`string`) The shop identifier.
--- - items (`table`) The items available in the shop.
--- - coords (`vector3`) where the shop is located.
--- - job/gang (optional) (`string`) Job or gang requirements.
---@usage
--- ```lua
--- openShop({
--- shop = "weapon_shop",
--- items = weaponShopItems,
--- coords = vector3(100.0, 200.0, 300.0),
--- job = "police",
--- })
--- ```
function openShop(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if isStarted(OXInv) then
exports[OXInv]:openInventory('shop', { type = data.shop })
elseif isStarted(QBInv) then
if QBInvNew then
TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop)
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
--elseif isStarted(OrigenInv) then -- Needs testing, not sure if i did this right
-- exports[OrigenInv]:openInventory('shop', data.shop, data.items)
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
lookEnt(data.coords)
end
--- Server event handler for opening a shop using the new QB inventory system.
RegisterNetEvent(getScript()..':server:OpenShopNewQB', function(data)
exports[QBInv]:OpenShop(source, data)
end)
-------------------------------------------------------------
-- Server Callback Registration
-------------------------------------------------------------
if isServer() then
createCallback(getScript()..':server:canCarry', function(source, itemTable) local result = canCarry(itemTable, source) return result end)
end

View File

@@ -515,6 +515,37 @@ function ensureNetToEnt(entNetID)
return entity return entity
end end
function sendLog(text)
local Player = getPlayer()
local coords = GetEntityCoords(PlayerPedId())
local _, _, _, hour, min, sec = GetLocalTime()
local data = {
script = debug.getinfo(2, "nSl"),
coords = coords,
localTime = { hour = hour, min = min, sec = sec },
firstname = Player.firstname,
lastname = Player.lastname,
source = Player.source,
id = Player.citizenId,
text = text,
}
debugPrint("^5Log Message^7: "..getScript().." - "..Player.firstname.." "..Player.lastname.."("..Player.source..") ["..Player.citizenId.."]", text)
TriggerServerEvent(getScript()..":server:sendlog", data)
end
function sendServerLog(data)
local hour, min, sec = os.date('%H'), os.date('%M'), os.date('%S')
data.serverTime = { house = hour, min = min, sec = sec }
--jsonPrint(data)
debugPrint("^5Log Message^7: "..getScript().." - "..data.firstname.." "..data.lastname.."("..data.source..") ["..data.id.."]", data.text)
-- Add your logger here
end
RegisterNetEvent(getScript()..":server:sendlog", sendServerLog)
------------------------------------------------------------- -------------------------------------------------------------
-- Material and Prop Functions -- Material and Prop Functions
------------------------------------------------------------- -------------------------------------------------------------

View File

@@ -97,13 +97,34 @@ function createInput(title, opts)
default = opts[i].default, default = opts[i].default,
} }
end end
if opts[i].type == "slider" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
isRequired = opts[i].required,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
::skip:: ::skip::
end end
dialog = exports[OXLibExport]:inputDialog(title, options) dialog = exports[OXLibExport]:inputDialog(title, options)
return dialog return dialog
elseif Config.System.Menu == "qb" then elseif Config.System.Menu == "qb" then
dialog = exports['qb-input']:ShowInput({ header = title, submitText = "Accept", inputs = opts }) for k, v in pairs(opts) do
currentNum += 1
if opts[k] == nil then
currentNum -= 1
else
options[currentNum] = opts[k]
end
end
dialog = exports['qb-input']:ShowInput(
{ header = title, submitText = "Accept", inputs = options }
)
return dialog return dialog
elseif Config.System.Menu == "gta" then elseif Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(opts), WarMenu.CreateMenu(tostring(opts),
title, title,

View File

@@ -59,7 +59,11 @@ function hasItem(items, amount, src)
debugPrint(foundMessage) debugPrint(foundMessage)
hasTable[item] = { hasItem = count >= amt, count = count } hasTable[item] = { hasItem = count >= amt, count = count }
end end
for k, v in pairs(hasTable) do if not v.hasItem then return false, hasTable end end for k, v in pairs(hasTable) do
if not v.hasItem then
return false, hasTable
end
end
return true, hasTable return true, hasTable
end end
end end

View File

@@ -12,6 +12,7 @@
• Granting random rewards from a reward pool. • Granting random rewards from a reward pool.
• Checking if a player can carry specific items based on weight. • Checking if a player can carry specific items based on weight.
]] ]]
validTokens = {}
------------------------------------------------------------- -------------------------------------------------------------
-- Registering Usable Items -- Registering Usable Items
@@ -74,7 +75,7 @@ function invImg(item)
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 "")
else else
print("^4ERROR^7: ^2No Inventory detected for invImg - Check exports.lua") print("^4ERROR^7: ^2No Inventory detected for invImg - Check starter.lua")
end end
end end
return imgLink return imgLink
@@ -103,7 +104,8 @@ function addItem(item, amount, info, src)
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
TriggerServerEvent(getScript()..":server:toggleItem", true, item, amount, nil, info) TriggerServerEvent(getScript()..":server:toggleItem", true, item, amount, currentToken, info)
currentToken = nil -- clear client cached token
end end
end end
@@ -159,7 +161,25 @@ RegisterNetEvent(getScript()..":server:toggleItem", function(give, item, amount,
return return
end end
local src = newsrc or source local src = source or newsrc
if (give == true or give == 1) then
if newsrc == nil then -- must be coming from client this would be blank
debugPrint("^1Auth^7: ^1No token recieved^7")
dupeWarn(src, item, "Auth: Player "..src.." attempted to spawn "..item.." without an auth token")
else
if type(newsrc) ~= "number" then -- checks if the newsrc is a source or token, if number its coming form the server itself
debugPrint("^1Auth^7: ^2Auth token received^7, ^2checking against server cache^7..")
if newsrc ~= validTokens[src] then
debugPrint("^1Auth^7: ^1Tokens don't match! ^7", newsrc, validTokens[src])
dupeWarn(src, item, "Auth: "..src.." attempted to spawn "..item.." with an incorrect auth token")
else
debugPrint("^1Auth^7: ^2Client and Server Auth tokens match^7!", newsrc, validTokens[src])
validTokens[src] = nil
end
end
end
end
local action = (tostring(give) == "true" and "addItem" or "removeItem") local action = (tostring(give) == "true" and "addItem" or "removeItem")
local remamount = amount or 1 local remamount = amount or 1
if item == nil then return end if item == nil then return end
@@ -599,4 +619,62 @@ function canCarry(itemTable, src)
end end
end end
return resultTable return resultTable
end end
-------------------------------------------------------------
-- Server Callback Registration
-------------------------------------------------------------
currentToken = nil
if isServer() then
createCallback(getScript()..":server:canCarry", function(source, itemTable)
local result = canCarry(itemTable, source)
return result
end)
local AuthEvent = getScript()..":"..keyGen()..keyGen()..keyGen()..keyGen()..":"..keyGen()..keyGen()..keyGen()..keyGen()
validTokens = validTokens or {}
createCallback(AuthEvent, function(source)
local src = source
local token = keyGen()..keyGen()..keyGen()..keyGen() -- Use a secure random generator here
debugPrint("^1Auth^7:^2 Player Source^7: "..src.." ^2requested new token^7:", token)
validTokens[src] = token
timeOutAuth(validTokens[src], src) -- Give script 10 seconds, then clear token
return token
end)
function timeOutAuth(token, src)
local token = token
SetTimeout(10000, function()
if token == validTokens[src] then
print("^1--------------------------------------------^7")
print("^7Clearing token for player ^1"..src.."^7", token)
print("^7This shouldn't happen unless a token has been called by a player or script and it hasn't been used")
print("^1--------------------------------------------^7")
end
end)
end
RegisterNetEvent(getScript()..":clearAuthToken", function()
local src = source
debugPrint("^1Auth^7: ^2Manually removing token for Player Source^7:", src, validTokens[src])
validTokens[src] = nil
end)
receivedEvent = {}
createCallback(getScript()..":callback:GetAuthEvent", function(source)
local src = source
debugPrint("^1Auth^7: ^2Player Source^7: "..src.." ^2requested ^3AuthEvent^7", AuthEvent)
if not receivedEvent[src] then receivedEvent[src] = true
return AuthEvent
else
print("^1Auth^7: ^1Player ^7"..src.." ^1tried to request auth token more than once^7")
return ""
end
end)
else
onResourceStart(function()
debugPrint("^1Auth^7: ^2Requesting ^3Auth Event^7")
AuthEvent = triggerCallback(getScript()..":callback:GetAuthEvent")
end, true)
end

View File

@@ -22,15 +22,21 @@ function createTempCam(ent, coords)
triggerNotify(nil, "ModCam Created", "success") triggerNotify(nil, "ModCam Created", "success")
end end
local camCoords = nil local camCoords = nil
local pointCoords = nil
if type(ent) ~= "vector3" then if type(ent) ~= "vector3" then
camCoords = GetOffsetFromEntityInWorldCoords(ent, 1.0, -0.3, 0.8) camCoords = GetOffsetFromEntityInWorldCoords(ent, 1.0, -0.3, 0.8)
else else
camCoords = ent camCoords = ent
end end
-- Create the camera with specified parameters
cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z + 0.5, 1.0, 0.0, 0.0, 60.00, false, 0) cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z + 0.5, 1.0, 0.0, 0.0, 60.00, false, 0)
-- Point the camera at the target coordinates
PointCamAtCoord(cam, coords) if type(coords) == "number" then
SetCamCoord(cam, GetCamCoord(cam) + vec3(0, 0, 1.0))
PointCamAtEntity(cam, coords)
else
PointCamAtCoord(cam, coords)
end
end end
return cam return cam
end end

View File

@@ -137,11 +137,11 @@ end
--- ``` --- ```
function loadScriptBank(bank) function loadScriptBank(bank)
local timeout = 2000 local timeout = 2000
debugPrint("^6Debug^7: ^2Loading ^3Script ^2AudioBank^7...") debugPrint("^6Bridge^7: ^2Loading ^3Script ^2AudioBank^7...")
while not RequestScriptAudioBank(bank, 0) do Wait(10) timeout -= 10 if timeout <= 0 then break end end while not RequestScriptAudioBank(bank, false) do Wait(10) timeout -= 10 if timeout <= 0 then break end end
local success = RequestScriptAudioBank(bank, 0) local success = RequestScriptAudioBank(bank, false)
debugPrint("^6Debug^7: "..(success and "^3Successfully ^2loaded^7: '^4" or "^1Failed to ^2load^7: '^4")..bank.."^7'") debugPrint("^6Bridge^7: "..(success and "^3Successfully ^2loaded^7: '^4" or "^1Failed to ^2load^7: '^4")..bank.."^7'")
return success return success
end end
@@ -159,14 +159,14 @@ end
--- ``` --- ```
function loadAmbientBank(bank) function loadAmbientBank(bank)
local timeout = 2000 local timeout = 2000
debugPrint("^6Debug^7: ^2Loading ^3Ambient ^2AudioBank^7...") debugPrint("^6Bridge^7: ^2Loading ^3Ambient ^2AudioBank^7...")
while not RequestAmbientAudioBank(bank, 0) do while not RequestAmbientAudioBank(bank, 0) do
Wait(10) Wait(10)
timeout -= 10 timeout -= 10
if timeout <= 0 then break end if timeout <= 0 then break end
end end
local success = RequestAmbientAudioBank(bank, 0) local success = RequestAmbientAudioBank(bank, 0)
debugPrint("^6Debug^7: "..(success and "^3Successfully ^2loaded^7: '^4" or "^1Failed to ^2load^7: '^4")..bank.."^7'") debugPrint("^6Bridge^7: "..(success and "^3Successfully ^2loaded^7: '^4" or "^1Failed to ^2load^7: '^4")..bank.."^7'")
return success return success
end end
@@ -224,16 +224,18 @@ end
--- ```lua --- ```lua
--- playGameSound('DLC_HEIST_HACKING_SNAKE_SOUNDS', 'Beep', vector3(0, 0, 0), false, 15.0) --- playGameSound('DLC_HEIST_HACKING_SNAKE_SOUNDS', 'Beep', vector3(0, 0, 0), false, 15.0)
--- ``` --- ```
function playGameSound(bank, sound, coords, synced, range) function playGameSound(audioBank, soundSet, soundRef, coords, synced, range)
debugPrint("^6Debug^7: ^2Attempting to play: ^3"..sound.." ^7(^4"..bank.."^7')") debugPrint("^6Bridge^7: ^2Attempting to play: ^3"..soundRef.." ^7('^4"..audioBank.."^7')")
loadScriptBank(audioBank)
local range = range or 10.0 local range = range or 10.0
local soundId = GetSoundId() local soundId = GetSoundId()
while not soundId do Wait(10) end while not soundId do Wait(10) end
if type(coords) == "vector3" or type(coords) == "vector4" then if type(coords) == "vector3" or type(coords) == "vector4" then
debugPrint("^6Debug^7: ^2Playing sound from Coord^7: "..formatCoord(coords.xyz)) debugPrint("^6Bridge^7: ^2Playing sound from Coord^7: "..formatCoord(coords.xyz))
PlaySoundFromCoord(soundId, sound, coords.x, coords.y, coords.z, bank, synced, range, 0) PlaySoundFromCoord(soundId, soundRef, coords.x, coords.y, coords.z, soundSet, synced, range, 0)
else else
debugPrint("^6Debug^7: ^2Playing sound from Entity^7: ^4"..coords.."^7") debugPrint("^6Bridge^7: ^2Playing sound from Entity^7: ^4"..coords.."^7")
PlaySoundFromEntity(soundId, sound, coords, bank, synced, 0) PlaySoundFromEntity(soundId, soundRef, coords, soundSet, synced, 1.0)
end end
ReleaseScriptAudioBank(audioBank)
end end

View File

@@ -31,6 +31,60 @@ function makeVeh(model, coords)
return veh return veh
end end
local distanceVehicles = {}
--- Creates a vehicle that spawns when the player enters a designated polyzone area.
---
--- This function sets up a circular polyzone; when the player enters the zone, the vehicle is spawned,
--- and when the player exits, the vehicle is deleted.
---
---@param data table A table containing vehicle data.
--- - **vehicle** `string`: The model name or hash of the vehicle to spawn.
--- - **coords** `vector4`: The coordinates where the vehicle will be placed. Should include x, y, z, and w (heading).
---@param freeze boolean (optional) Whether to freeze the vehicle in place. Defaults to `false`.
---@param synced boolean (optional) Whether the vehicle should be synced across clients. Defaults to `false`.
function makeDistVehicle(data, radius, onEnter, onExit)
local vehicle = nil
local zoneId = keyGen() .. keyGen()
local zone = createCirclePoly({
name = zoneId,
coords = vec3(data.coords.x, data.coords.y, data.coords.z),
radius = radius,
onEnter = function()
vehicle = makeVeh(data.model, data.coords)
if onEnter then
debugPrint("makeDistVehicle onEnter running")
onEnter(vehicle)
end
end,
onExit = function()
deleteVehicle(vehicle)
if onExit then
debugPrint("makeDistVehicle onExit running")
onExit(vehicle)
end
end,
debug = debugMode,
})
distanceVehicles[zoneId] = { zone = zone, vehicle = vehicle }
return zoneId
end
--- Removes a specific distance-based vehicle spawning zone.
---
---@param zoneId string The unique identifier of the zone to remove.
function removeDistVehicleZone(zoneId)
if distanceVehicles[zoneId].zone then
removePolyZone(distanceVehicles[zoneId].zone) -- Adjust this if your polyzone library uses a different removal method.
if distanceVehicles[zoneId].vehicle then
deleteVehicle(distanceVehicles[zoneId].vehicle)
end
distanceVehicles[zoneId] = nil
print("Removed polyzone for zoneId: " .. zoneId)
else
print("No zone found with zoneId: " .. zoneId)
end
end
--- Attempts to gain network control of a vehicle and set it as a mission entity. --- Attempts to gain network control of a vehicle and set it as a mission entity.
--- ---
--- This function forces synchronization of a vehicle with other players by requesting network control and setting the vehicle as a mission entity. --- This function forces synchronization of a vehicle with other players by requesting network control and setting the vehicle as a mission entity.
@@ -67,6 +121,20 @@ function pushVehicle(entity)
end end
end end
--- Deletes a spawned vehicle.
---
---@param vehicle number The handle of the vehicle entity to delete.
function deleteVehicle(vehicle)
if vehicle then
debugPrint("^6Bridge^7: ^2Destroying Vehicle^7: '^6" .. vehicle .. "^7'")
if IsEntityAttachedToEntity(vehicle, PlayerPedId()) then
SetEntityAsMissionEntity(vehicle)
DetachEntity(vehicle, true, true)
end
DeleteVehicle(vehicle)
end
end
--- Cleans up all created vehicles when the resource stops. --- Cleans up all created vehicles when the resource stops.
onResourceStop(function(r) onResourceStop(function(r)
for i = 1, #Vehicles do DeleteVehicle(Vehicles[i]) end for i = 1, #Vehicles do DeleteVehicle(Vehicles[i]) end

View File

@@ -220,7 +220,6 @@ function fundPlayer(fund, moneyType, newsrc)
debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource) debugPrint("^6Bridge^7: ^2Funding Player: '^2"..fund.."^7'", moneyType, fundResource)
end end
end end
RegisterNetEvent(getScript()..":server:FundPlayer", fundPlayer)
------------------------------------------------------------- -------------------------------------------------------------
-- Item Consumption & Effects -- Item Consumption & Effects
@@ -458,7 +457,7 @@ function getPlayer(source)
--gangBoss = info.gang.isboss, --gangBoss = info.gang.isboss,
onDuty = info.job.onDuty, onDuty = info.job.onDuty,
--account = info.charinfo.account, --account = info.charinfo.account,
--citizenId = info.citizenid, citizenId = info.citizenid,
} }
elseif isStarted(OXCoreExport) then elseif isStarted(OXCoreExport) then
@@ -468,9 +467,20 @@ function getPlayer(source)
chunk() chunk()
local player = Ox.GetPlayer(src) local player = Ox.GetPlayer(src)
Player = { Player = {
firstname = player.firstName,
lastname = player.lastName ,
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,
source = src,
--job = OxPlayer.getGroups(),
--jobBoss = info.job.isboss,
--gang = OxPlayer.getGroups(),
--gangBoss = info.gang.isboss,
--onDuty = info.job.onduty,
--account = info.charinfo.account,
citizenId = player.stateId,
} }
elseif isStarted(QBXExport) then elseif isStarted(QBXExport) then
local info = exports[QBXExport]:GetPlayer(src) local info = exports[QBXExport]:GetPlayer(src)
@@ -513,7 +523,7 @@ function getPlayer(source)
} }
end end
else else
print("^4ERROR^7: ^2No Core detected for getPlayer() - Check exports.lua") print("^4ERROR^7: ^2No Core detected for getPlayer() - Check starter.lua")
end end
else else
-- Client-side: Get current player info. -- Client-side: Get current player info.
@@ -527,7 +537,9 @@ function getPlayer(source)
Player = { Player = {
firstname = info.firstName, firstname = info.firstName,
lastname = info.lastName, lastname = info.lastName,
name = info.firstName.." "..info.lastName,
cash = cash,
bank = bank,
source = GetPlayerServerId(PlayerId()), source = GetPlayerServerId(PlayerId()),
job = info.job.name, job = info.job.name,
--jobBoss = info.job.isboss, --jobBoss = info.job.isboss,
@@ -535,30 +547,23 @@ function getPlayer(source)
--gangBoss = info.gang.isboss, --gangBoss = info.gang.isboss,
onDuty = info.job.onDuty, onDuty = info.job.onDuty,
--account = info.charinfo.account, --account = info.charinfo.account,
--citizenId = info.citizenid, citizenId = info.identifier,
name = info.firstName.." "..info.lastName,
cash = cash,
bank = bank,
} }
elseif isStarted(OXCoreExport) then elseif isStarted(OXCoreExport) then
--local info = exports[OXCoreExport]:GetPlayerData()
Player = { Player = {
firstname = OxPlayer.get("firstName"), firstname = OxPlayer.get("firstName"),
lastname = OxPlayer.get("lastName"), lastname = OxPlayer.get("lastName"),
name = OxPlayer.get("firstName").." "..OxPlayer.get("lastName"), name = OxPlayer.get("firstName").." "..OxPlayer.get("lastName"),
cash = exports[OXInv]:Search('count', "money"), cash = exports[OXInv]:Search('count', "money"),
bank = 0, bank = 0,
--source = info.source, source = GetPlayerServerId(PlayerId()),
job = OxPlayer.getGroups(), job = OxPlayer.getGroups(),
--jobBoss = info.job.isboss, --jobBoss = info.job.isboss,
gang = OxPlayer.getGroups(), gang = OxPlayer.getGroups(),
--gangBoss = info.gang.isboss, --gangBoss = info.gang.isboss,
--onDuty = info.job.onduty, --onDuty = info.job.onduty,
--account = info.charinfo.account, --account = info.charinfo.account,
citizenId = OxPlayer.get("stateId"), citizenId = OxPlayer.userId,
} }
elseif isStarted(QBXExport) then elseif isStarted(QBXExport) then
local info = exports[QBXExport]:GetPlayerData() local info = exports[QBXExport]:GetPlayerData()

View File

@@ -31,7 +31,7 @@ function debugScaleForm(textTable, loc)
local size = vec2(0.18, totalHeight + boxPadding * 2) local size = vec2(0.18, totalHeight + boxPadding * 2)
-- Draw background rectangle. -- 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, 200)
-- Render each line of text. -- Render each line of text.
for i = 1, #textTable do for i = 1, #textTable do

206
shared/shops.lua Normal file
View File

@@ -0,0 +1,206 @@
-------------------------------------------------------------
-- Selling Menu and Animation
-------------------------------------------------------------
--- Opens a selling menu with available items and prices.
---
--- @param data table Contains selling menu data:
--- - sellTable (`table`) Table with Header and Items (item names and prices).
--- - ped (optional) (`number`) Ped entity involved.
--- - onBack (optional) (`function`) Callback for returning.
--- @usage
--- ```lua
--- sellMenu({
--- sellTable = {
--- Header = "Sell Items",
--- Items = {
--- ["gold_ring"] = 100,
--- ["diamond"] = 500,
--- },
--- },
--- ped = pedEntity,
--- onBack = function() print("Returning to previous menu") end,
--- })
--- ```
function sellMenu(data)
local origData = data
local Menu = {}
if data.sellTable.Items then
local itemList = {}
for k, v in pairs(data.sellTable.Items) do itemList[k] = 1 end
local _, hasTable = hasItem(itemList)
for k, v in pairsByKeys(data.sellTable.Items) do
Menu[#Menu + 1] = {
isMenuHeader = not hasTable[k].hasItem,
icon = invImg(k),
header = Items[k].label..(hasTable[k].hasItem and "💰 (x"..hasTable[k].count..")" or ""),
txt = Loc[Config.Lan].info["sell_all"]..v.." "..Loc[Config.Lan].info["sell_each"],
onSelect = function()
sellAnim({ item = k, price = v, ped = data.ped, onBack = function() sellMenu(data) end })
end,
}
end
else
for k, v in pairsByKeys(data.sellTable) do
if type(v) == "table" then
Menu[#Menu + 1] = {
arrow = true,
header = k,
txt = "Amount of items: "..countTable(v.Items),
onSelect = function()
v.onBack = function() sellMenu(origData) end
v.sellTable = data.sellTable[k]
sellMenu(v)
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,
})
end
--- Plays the selling animation and processes the sale transaction.
---
--- Checks if the player has the item, plays animations, triggers the server event for selling,
--- and then calls the onBack callback if provided.
---
--- @param data table Contains:
--- `- item: The item to sell.
--- `- price: Price per item.
--- `- ped (optional): Ped entity involved.
--- `- onBack (optional): Callback to call on completion.
---@usage
--- ```lua
--- sellAnim({
--- item = "gold_ring",
--- price = 100,
--- ped = pedEntity,
--- onBack = function() sellMenu(data) end,
--- })
--- ```
function sellAnim(data)
if not hasItem(data.item, 1) then
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error")
return
end
-- Remove any attached clipboard objects.
for _, obj in pairs(GetGamePool('CObject')) do
for _, model in pairs({ `p_cs_clipboard` }) do
if GetEntityModel(obj) == model and IsEntityAttachedToEntity(data.ped, obj) then
DeleteObject(obj)
DetachEntity(obj, 0, 0)
SetEntityAsMissionEntity(obj, true, true)
Wait(100)
DeleteEntity(obj)
end
end
end
TriggerServerEvent(getScript().."Sellitems", data)
lookEnt(data.ped)
local dict = "mp_common"
playAnim(dict, "givetake2_a", 0.3, 2)
playAnim(dict, "givetake2_b", 0.3, 2, data.ped)
Wait(2000)
StopAnimTask(PlayerPedId(), dict, "givetake2_a", 0.5)
StopAnimTask(data.ped, dict, "givetake2_b", 0.5)
if data.onBack then data.onBack() end
end
--- Server event handler for processing item sales.
--- Removes sold items from inventory and funds the player based on the sale.
RegisterNetEvent(getScript().."Sellitems", function(data)
local src = source
local hasItems, hasTable = hasItem(data.item, 1, src)
if hasItems then
removeItem(data.item, hasTable[data.item].count, src)
fundPlayer((hasTable[data.item].count * data.price), "cash", src)
else
triggerNotify(nil, Loc[Config.Lan].error["dont_have"].." "..Items[data.item].label, "error", src)
end
end)
-------------------------------------------------------------
-- Shop Interface
-------------------------------------------------------------
--- Opens a shop interface for the player.
---
--- Checks job/gang restrictions, then uses the active inventory system to open the shop.
--- @param data table Contains:
--- - shop (`string`) The shop identifier.
--- - items (`table`) The items available in the shop.
--- - coords (`vector3`) where the shop is located.
--- - job/gang (optional) (`string`) Job or gang requirements.
---@usage
--- ```lua
--- openShop({
--- shop = "weapon_shop",
--- items = weaponShopItems,
--- coords = vector3(100.0, 200.0, 300.0),
--- job = "police",
--- })
--- ```
function openShop(data)
if (data.job or data.gang) and not jobCheck(data.job or data.gang) then return end
if isStarted(OXInv) then
exports[OXInv]:openInventory('shop', { type = data.shop })
elseif isStarted(QBInv) then
if QBInvNew then
TriggerServerEvent(getScript()..':server:OpenShopNewQB', data.shop)
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
--elseif isStarted(OrigenInv) then -- Needs testing, not sure if i did this right
-- exports[OrigenInv]:openInventory('shop', data.shop, data.items)
else
TriggerServerEvent(Config.General.JimShops and "jim-shops:ShopOpen" or "inventory:server:OpenInventory", "shop", data.items.label, data.items)
end
lookEnt(data.coords)
end
--- Server event handler for opening a shop using the new QB inventory system.
RegisterNetEvent(getScript()..':server:OpenShopNewQB', function(data)
exports[QBInv]:OpenShop(source, data)
end)
--- Registers a shop with the active inventory system.
--- Supports either OXInv or QBInv (with QBInvNew flag).
---
--- @param name string Unique shop identifier.
--- @param label string Display name for the shop.
--- @param items table List of available shop items.
--- @param society string|nil (Optional) Society identifier for shared shops.
--- @usage
--- ```lua
--- registerShop("weaponShop", "Weapon Shop", weaponItems, "society_weapons")
--- ```
function registerShop(name, label, items, society)
if isStarted(OXInv) then
debugPrint("^6Bridge^7: ^2Registering ^3OX ^2Store^7:", name, label)
exports[OXInv]:RegisterShop(name, {
name = label,
inventory = items,
society = society,
})
elseif isStarted(QBInv) and QBInvNew then
debugPrint("^6Bridge^7: ^2Registering ^3QB ^2Store^7:", name, label)
exports[QBInv]:CreateShop({
name = name,
label = label,
slots = #items,
items = items,
society = society,
})
end
end

36
shared/skillcheck.lua Normal file
View File

@@ -0,0 +1,36 @@
function skillCheck(data)
local result = false
if Config.System.skillCheck == "qb" then
local Skillbar = exports["qb-minigames"]:Skillbar()
if Skillbar then
result = true
else
result = false
end
elseif Config.System.skillCheck == "ox" then
local Skillbar = exports[OXLibExport]:skillCheck(
{
"easy",
"easy",
"easy"
},
{
"1",
"2",
"3",
"4"
})
if Skillbar then
result = true
else
result = false
end
else
result = true
end
return result
end

View File

@@ -99,7 +99,7 @@ function checkHasItem(stashes, itemTable)
for item, amount in pairs(itemTable) do for item, amount in pairs(itemTable) do
debugPrint("^6Bridge^7: ^2Checking "..(name and " '^3"..name.."^7'" or "").." ingredients - ^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
successes = successes + 1 successes += 1
if successes == itemCount then if successes == itemCount then
return true, name return true, name
end end
@@ -271,7 +271,6 @@ 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)
if type(stashName) == "table" then if type(stashName) == "table" then
for _, name in pairs(stashName) do for _, name in pairs(stashName) do
local success = exports[OXInv]:RemoveItem(name, k, v) local success = exports[OXInv]:RemoveItem(name, k, v)

View File

@@ -119,9 +119,8 @@ function createEntityTarget(entity, opts, dist)
item = opts[i].item or nil, item = opts[i].item or nil,
groups = opts[i].job or opts[i].gang, groups = opts[i].job or opts[i].gang,
onSelect = opts[i].action, onSelect = opts[i].action,
canInteract = function(_, distance) distance = dist,
return distance < dist and true or false canInteract = opts[i].canInteract or nil,
end
} }
end end
exports[OXTargetExport]:addLocalEntity(entity, options) exports[OXTargetExport]:addLocalEntity(entity, options)
@@ -223,9 +222,8 @@ function createBoxTarget(data, opts, dist)
item = opts[i].item or nil, item = opts[i].item or nil,
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) distance = dist,
return distance < dist and true or false canInteract = opts[i].canInteract or nil,
end
} }
end end
if not data[5].useZ then if not data[5].useZ then
@@ -324,9 +322,8 @@ function createCircleTarget(data, opts, dist)
item = opts[i].item or nil, item = opts[i].item or nil,
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) distance = dist,
return distance < dist canInteract = opts[i].canInteract or nil,
end
} }
end end
local target = exports[OXTargetExport]:addSphereZone({ local target = exports[OXTargetExport]:addSphereZone({
@@ -385,9 +382,8 @@ function createModelTarget(models, opts, dist)
item = opts[i].item or nil, item = opts[i].item or nil,
groups = opts[i].job or opts[i].gang, groups = opts[i].job or opts[i].gang,
onSelect = opts[i].action, onSelect = opts[i].action,
canInteract = function(_, distance) distance = dist,
return distance < dist and true or false canInteract = opts[i].canInteract or nil,
end
} }
end end
exports[OXTargetExport]:addModel(models, options) exports[OXTargetExport]:addModel(models, options)

View File

@@ -128,14 +128,15 @@ end
function setVehicleProperties(vehicle, props) function setVehicleProperties(vehicle, props)
if checkDifferences(vehicle, props) then if checkDifferences(vehicle, props) then
if not DoesEntityExist(vehicle) then if not DoesEntityExist(vehicle) then
print("Unable to set vehicle properties for '"..vehicle.."' (entity does not exist)") print("Unable to set vehicle properties for '"..vehicle.."' (^1entity does not exist^7)")
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]")
else elseif isStarted(OXLibExport) then
TriggerServerEvent(getScript()..":ox:setVehicleProperties", VehToNet(vehicle), props) lib.setVehicleProperties(vehicle, props, false)
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
end end
else else
debugPrint("^6Bridge^7: ^2No Changes Found ^7 [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]") debugPrint("^6Bridge^7: ^2No Changes Found ^7 [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
@@ -158,7 +159,7 @@ 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 differencesFound = false local differencesFound = true
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
@@ -199,7 +200,7 @@ AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagN
local networked = not bagName:find('localEntity') local networked = not bagName:find('localEntity')
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..entity.."^7] - [^3"..GetEntityModel(entity).."^7] - [^3"..value.plate.."^7]") debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..entity.."^7] - [^3"..GetEntityModel(entity).."^7] - [^3"..value.plate.."^7]")
if networked and NetworkGetEntityOwner(entity) ~= cache.playerId then return end if networked then return end
if lib.setVehicleProperties(entity, value) then if lib.setVehicleProperties(entity, value) then
Entity(entity).state:set('setVehicleProperties', nil, true) Entity(entity).state:set('setVehicleProperties', nil, true)

View File

@@ -119,7 +119,18 @@ if isServer() then
--- ```lua --- ```lua
--- 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, token)
local src = source or nil
if src then
debugPrint("^1Auth^7: ^2Auth token received^7, ^2checking against server cache^7..")
if token ~= validTokens[src] then
debugPrint("^1Auth^7: ^1Tokens don't match! ^7", token, validTokens[src])
else
debugPrint("^1Auth^7: ^2Client and Server Auth tokens match^7!", token, validTokens[src])
validTokens[src] = nil
end
end
registerStash(name, label, slots, weight, owner, coords) registerStash(name, label, slots, weight, owner, coords)
end) end)
end end

View File

@@ -71,9 +71,11 @@ for _, v in pairs({ -- This is a specific load order
'input.lua', 'input.lua',
'notify.lua', 'notify.lua',
'drawText.lua', 'drawText.lua',
'skillcheck.lua',
-- Crafting / Shops / Stashes -- Crafting / Shops / Stashes
'crafting.lua', 'crafting.lua',
'shops.lua',
'stashcontrol.lua', 'stashcontrol.lua',
-- Kind of "other" -- Kind of "other"

View File

@@ -1 +1 @@
1.2 2.0