fix(qb-core): add global CreateUsableItem alias for qs-inventory compatibility

qs-inventory apelează CreateUsableItem ca funcție globală, dar qb-core definea doar QBCore.Functions.CreateUseableItem (cu 'e'). Adăugat alias global + fix 16 stringuri sparte în items.lua care blocau parsarea.
This commit is contained in:
2026-04-02 00:08:19 +03:00
parent 096ccb6399
commit 978c9bc759
827 changed files with 3570 additions and 1015 deletions

2
.gitignore vendored
View File

@@ -13,3 +13,5 @@ cache/files/17mov_JobCenter/resource.rpf
_fix_dupes.py _fix_dupes.py
_check_dupes.py _check_dupes.py
_find_dupes.py _find_dupes.py
artifacts/crashes/136f1640-ac9a-4693-824a-5ccd58f1b359-full.dmp
artifacts/crashes/136f1640-ac9a-4693-824a-5ccd58f1b359.dmp

24
_find_broken.py Normal file
View File

@@ -0,0 +1,24 @@
import re
path = r'E:\FiveMserver\server\resources\[framework]\[core]\qb-core\shared\items.lua'
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
broken = []
for i, line in enumerate(lines):
stripped = line.strip()
if "['description']" not in stripped:
continue
if "['name']" not in stripped:
continue
# Count single quotes after ['description']
idx = stripped.index("['description']")
after = stripped[idx:]
# Should end with ...'}, or ...'}},
# Check if description value has matching quotes
if after.count("'") % 2 != 0:
broken.append((i+1, stripped[:120]))
print(f"Found {len(broken)} broken strings:")
for ln, txt in broken:
print(f" L{ln}: {txt}")

34
_fix_strings.py Normal file
View File

@@ -0,0 +1,34 @@
import re
path = r'E:\FiveMserver\server\resources\[framework]\[core]\qb-core\shared\items.lua'
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Fix pattern: find description strings that don't end with closing quote before },
# Pattern: ['description'] = 'text without closing quote},
# Should be: ['description'] = 'text with closing quote'},
fixed = 0
lines = content.split('\n')
new_lines = []
for line in lines:
if "['description']" in line and "['name']" in line:
idx = line.index("['description']")
after = line[idx:]
if after.count("'") % 2 != 0:
# Find the last }, and insert closing quote before it
# Pattern: some text}, -> some text'},
# Or: some text}}, -> some text'}},
line = re.sub(r"([^'])\},\s*$", r"\1'},", line)
line = re.sub(r"([^'])\}\},\s*$", r"\1'}},", line)
# Handle CRLF
line = re.sub(r"([^'])\},\r$", r"\1'},\r", line)
line = re.sub(r"([^'])\}\},\r$", r"\1'}},\r", line)
fixed += 1
new_lines.append(line)
with open(path, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines))
print(f"Fixed {fixed} broken description strings")

View File

@@ -16,4 +16,3 @@ ensure phone-props
ensure [casino] ensure [casino]
ensure [stream] ensure [stream]
ensure luxu_admin ensure luxu_admin
ensure [weapons]

Binary file not shown.

View File

@@ -0,0 +1,93 @@
--[[
Hi dear customer or developer, here you can fully configure your server's
framework or you could even duplicate this file to create your own framework.
If you do not have much experience, we recommend you download the base version
of the framework that you use in its latest version and it will work perfectly.
]]
if Config.Framework ~= 'esx' then
return
end
ESX = exports['es_extended']:getSharedObject()
function GetPlayerData()
return ESX.GetPlayerData()
end
local first = true
RegisterNetEvent('esx:playerLoaded', function()
if first then
first = false
CreateThread(function()
Wait(1000)
GetPlayerData(function(PlayerData)
PlayerJob = PlayerData.job
SetPedArmour(PlayerPedId(), PlayerData.metadata['armor'])
currentarmor = PlayerData.metadata['armor']
startedsync = true
Wait(100)
if Config.VestTexture then
local ped = PlayerPedId()
local PlayerData = GetPlayerData()
local GetArmor = GetPedArmour(ped)
currentVest = GetPedDrawableVariation(ped, 9)
currentVestTexture = GetPedTextureVariation(ped, 9)
if GetArmor >= 1 then
SetVest()
elseif GetArmor >= 51 then
SetHeavyVest()
end
end
end)
end)
end
end)
function SendTextMessage(msg, type)
if type == 'inform' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'inform'
})
end
if type == 'error' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'error'
})
end
if type == 'success' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'success'
})
end
end
function ProgressBar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
if lib.progressCircle({
duration = duration,
label = label,
position = 'bottom',
useWhileDead = useWhileDead,
canCancel = canCancel,
disable = disableControls,
anim = {
dict = animation.animDict,
clip = animation.anim,
flag = animation?.flags
},
prop = prop
}) then
onFinish()
else
if onCancel then
onCancel()
end
end
end

View File

@@ -0,0 +1,112 @@
--[[
Hi dear customer or developer, here you can fully configure your server's
framework or you could even duplicate this file to create your own framework.
If you do not have much experience, we recommend you download the base version
of the framework that you use in its latest version and it will work perfectly.
]]
if Config.Framework ~= 'qb' then
return
end
QBCore = exports['qb-core']:GetCoreObject()
function GetPlayerData()
return QBCore.Functions.GetPlayers()
end
local first = true
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
if first then
first = false
CreateThread(function()
Wait(1000)
GetPlayerData(function(PlayerData)
PlayerJob = PlayerData.job
SetPedArmour(PlayerPedId(), PlayerData.metadata['armor'])
currentarmor = PlayerData.metadata['armor']
startedsync = true
Wait(100)
if Config.VestTexture then
local ped = PlayerPedId()
local PlayerData = QS.GetPlayerData()
local GetArmor = GetPedArmour(ped)
currentVest = GetPedDrawableVariation(ped, 9)
currentVestTexture = GetPedTextureVariation(ped, 9)
if GetArmor >= 1 then
SetVest()
elseif GetArmor >= 51 then
SetHeavyVest()
end
end
end)
end)
end
end)
function SendTextMessage(msg, type)
if type == 'inform' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'inform'
})
end
if type == 'error' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'error'
})
end
if type == 'success' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'success'
})
end
end
function ShowHelpNotification(msg)
BeginTextCommandDisplayHelp('STRING')
AddTextComponentSubstringPlayerName(msg)
EndTextCommandDisplayHelp(0, 0, false, -1)
end
function DrawText3D(x, y, z, text)
SetTextScale(0.4, 0.4)
SetTextFont(4)
SetTextProportional(1)
SetTextColour(255, 255, 255, 215)
SetTextEntry('STRING')
SetTextCentre(true)
AddTextComponentString(text)
SetDrawOrigin(x, y, z, 0)
DrawText(0.0, 0.0)
ClearDrawOrigin()
end
function ProgressBar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
if lib.progressCircle({
duration = duration,
label = label,
position = 'bottom',
useWhileDead = useWhileDead,
canCancel = canCancel,
disable = disableControls,
anim = {
dict = animation.animDict,
clip = animation.anim,
flag = animation?.flags
},
prop = prop
}) then
onFinish()
else
if onCancel then
onCancel()
end
end
end

View File

@@ -0,0 +1,33 @@
fx_version 'adamant'
game 'gta5'
lua54 'yes'
shared_scripts {
'@ox_lib/init.lua',
'shared/*.lua',
'locales/*.lua',
}
server_scripts {
'server/**/**/**.lua'
}
client_scripts {
'client/**/**/**.lua'
}
escrow_ignore {
'shared/config.lua',
'locales/*.lua',
'client/custom/framework/*.lua',
'server/custom/framework/*.lua',
}
dependencies {
'qs-inventory', -- Required.
'/server:4752', -- ⚠PLEASE READ⚠ This requires at least server build 4700 or higher
}
dependency '/assetpacks'

View File

@@ -0,0 +1,8 @@
Locales['de'] = {
["ARMOR_NOTIFICATION_ALREADY_HAVE"] = "Du hast schon eine Weste an",
["ARMOR_NOTIFICATION_DONT_HAVE"] = "Du hast keine Weste!",
["ARMOR_PROGRESS_USE_ARMOR"] = "Kugelsichere Weste Anziehen ...",
["ARMOR_PROGRESS_USE_HEAVYARMOR"] = "Schwere Weste Anziehen ...",
["ARMOR_PROGRESS_REMOVE_ARMOR"] = "Weste ausziehen ...",
}

View File

@@ -0,0 +1,8 @@
Locales['en'] = {
["ARMOR_NOTIFICATION_ALREADY_HAVE"] = "You already have a bulletproof vest",
["ARMOR_NOTIFICATION_DONT_HAVE"] = "You don't have a bulletproof vest",
["ARMOR_PROGRESS_USE_ARMOR"] = "Placing Armor ...",
["ARMOR_PROGRESS_USE_HEAVYARMOR"] = "Placing Heavy Armor ...",
["ARMOR_PROGRESS_REMOVE_ARMOR"] = "Removing Armor ...",
}

View File

@@ -0,0 +1,8 @@
Locales['es'] = {
["ARMOR_NOTIFICATION_ALREADY_HAVE"] = "Ya tienes un chaleco antibalas",
["ARMOR_NOTIFICATION_DONT_HAVE"] = "No tienes un chaleco antibalas",
["ARMOR_PROGRESS_USE_ARMOR"] = "Colocando Armor...",
["ARMOR_PROGRESS_USE_HEAVYARMOR"] = "Colocando Heavy Armor...",
["ARMOR_PROGRESS_REMOVE_ARMOR"] = "Quitando Armor...",
}

View File

@@ -0,0 +1,8 @@
Locales['it'] = {
["ARMOR_NOTIFICATION_ALREADY_HAVE"] = "Hai già un giubbotto anti-proiettile",
["ARMOR_NOTIFICATION_DONT_HAVE"] = "Non hai un giubbotto anti-proiettile",
["ARMOR_PROGRESS_USE_ARMOR"] = "Indossando giubbotto anti-proiettile...",
["ARMOR_PROGRESS_USE_HEAVYARMOR"] = "Indossando giubbotto anti-proiettile pesante...",
["ARMOR_PROGRESS_REMOVE_ARMOR"] = "Togliendo armatura...",
}

View File

@@ -0,0 +1,34 @@
--[[
Hi dear customer or developer, here you can fully configure your server's
framework or you could even duplicate this file to create your own framework.
If you do not have much experience, we recommend you download the base version
of the framework that you use in its latest version and it will work perfectly.
]]
if Config.Framework ~= 'esx' then
return
end
ESX = exports['es_extended']:getSharedObject()
function RegisterUsableItem(name, cb)
exports['qs-inventory']:CreateUsableItem(name, cb)
end
function GetPlayerFromId(source)
return ESX.GetPlayerFromId(source)
end
function GetIdentifier(source)
return ESX.GetPlayerFromId(source).identifier
end
function AddItem(source, item, count, slot)
exports['qs-inventory']:AddItem(source, item, count, slot)
end
function RemoveItem(source, item, count)
local xPlayer = GetPlayerFromId(source)
xPlayer.removeInventoryItem(item, count)
end

View File

@@ -0,0 +1,34 @@
--[[
Hi dear customer or developer, here you can fully configure your server's
framework or you could even duplicate this file to create your own framework.
If you do not have much experience, we recommend you download the base version
of the framework that you use in its latest version and it will work perfectly.
]]
if Config.Framework ~= 'qb' then
return
end
QBCore = exports['qb-core']:GetCoreObject()
function RegisterUsableItem(name, cb)
exports['qs-inventory']:CreateUsableItem(name, cb)
end
function GetPlayerFromId(player)
return QBCore.Functions.GetPlayer(player)
end
function GetIdentifier(player)
return QBCore.Functions.GetPlayer(player).PlayerData.citizenid
end
function AddItem(source, item, count, slot)
exports['qs-inventory']:AddItem(source, item, count, slot)
end
function RemoveItem(source, item, count)
local xPlayer = GetPlayerFromId(source)
xPlayer.Functions.RemoveItem(item, count)
end

View File

@@ -0,0 +1,103 @@
Config = {}
Locales = {}
local esxHas = GetResourceState('es_extended') == 'started'
local qbHas = GetResourceState('qb-core') == 'started'
local qbxHas = GetResourceState('qbx_core') == 'started'
Config.Framework = esxHas and 'esx' or qbHas and 'qb' or qbxHas and 'qb' or 'esx'
--[[
Set the primary language for the resource.
Choose one of the default languages located in locales/*.
If your desired language is not listed, feel free to create your own!
]]
Config.Language = 'en'
--[[
General configuration settings for the resource.
Customize each option as needed.
]]
Config.Progressbar = { -- Timer durations for progress bars (in milliseconds).
UseArmor = 5000, -- Duration for applying regular armor.
UseHeavyArmor = 5000, -- Duration for applying heavy armor.
ResetArmor = 2500 -- Duration for removing the armor.
}
Config.SetPedArmour = { -- Amount of armor applied to the player.
UseArmor = 50, -- Armor value for regular armor.
UseHeavyArmor = 100, -- Armor value for heavy armor.
ResetArmor = 0 -- Armor value when the vest is removed.
}
--[[
Command used to remove the player's vest.
]]
Config.ResetArmor = 'resetarmor' -- Command to reset/remove your vest.
--[[
Configuration to check if a player has a vest equipped.
]]
Config.VestTexture = true -- Should vest textures be used? (true = Yes, false = No)
Config.CheckVest = {
check = false, -- Enable automatic vest checks? (true = Yes, false = No)
time = 30000 -- Frequency of checks (in milliseconds). Ignored if check = false.
}
--[[
Vest configuration based on player gender.
Customize the vest components for both male and female characters.
]]
Config.Vest = {
male = {
['bproof_1'] = 6, -- Main vest component ID for males.
['bproof_2'] = 1 -- Secondary vest texture ID for males.
},
female = {
['bproof_1'] = 0, -- Main vest component ID for females.
['bproof_2'] = 0 -- Secondary vest texture ID for females.
},
maleHeavy = {
['bproof_1'] = 27, -- Main heavy vest component ID for males.
['bproof_2'] = 2 -- Secondary heavy vest texture ID for males.
},
femaleHeavy = {
['bproof_1'] = 6, -- Main heavy vest component ID for females.
['bproof_2'] = 0 -- Secondary heavy vest texture ID for females.
}
}
--[[
Editable functions to handle vest application or removal.
These functions are only executed if VestTexture is set to true.
]]
function SetVest() -- Function to apply the regular vest texture.
local isMale = GetEntityModel(PlayerPedId()) == joaat('mp_m_freemode_01')
local vest = isMale and Config.Vest.male or Config.Vest.female
SetPedComponentVariation(PlayerPedId(), 9, vest['bproof_1'], vest['bproof_2'], 0)
end
function SetHeavyVest() -- Function to apply the heavy vest texture.
local isMale = GetEntityModel(PlayerPedId()) == joaat('mp_m_freemode_01')
local vest = isMale and Config.Vest.maleHeavy or Config.Vest.femaleHeavy
SetPedComponentVariation(PlayerPedId(), 9, vest['bproof_1'], vest['bproof_2'], 0)
end
function RemoveVest() -- Function to remove the vest texture.
SetPedComponentVariation(PlayerPedId(), 9, 0, 1, 0)
end
--[[
Debug mode configuration.
When enabled, detailed debug prints/logs are displayed for development purposes.
Use this only during development/testing phases.
]]
Config.Debug = false

View File

@@ -0,0 +1,90 @@
--[[
Hi dear customer or developer, here you can fully configure your server's
framework or you could even duplicate this file to create your own framework.
If you do not have much experience, we recommend you download the base version
of the framework that you use in its latest version and it will work perfectly.
]]
if Config.Framework ~= 'esx' then
return
end
ESX = exports['es_extended']:getSharedObject()
RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(xPlayer)
Wait(10000)
StartThread()
end)
AddEventHandler('onClientResourceStart', function(resourceName)
if (GetCurrentResourceName() == resourceName) then
ESX = exports['es_extended']:getSharedObject()
Wait(10000)
StartThread()
end
end)
function getSex()
local promise = promise.new()
ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin)
promise:resolve(skin.sex)
end)
return Citizen.Await(promise)
end
function OpenStash(metadata)
local other = {}
other.maxweight = metadata.weight
other.slots = metadata.slots
TriggerServerEvent('inventory:server:OpenInventory', 'stash', 'Backpack_' .. metadata.ID, other)
TriggerEvent('inventory:client:SetCurrentStash', 'Backpack_' .. metadata.ID)
repeat Wait(1000) until IsNuiFocused() == false
TriggerEvent('backpacks:client:close', metadata.ID)
end
function SendTextMessage(msg, type)
if type == 'inform' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'inform'
})
end
if type == 'error' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'error'
})
end
if type == 'success' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'success'
})
end
end
function Progressbar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
if lib.progressCircle({
duration = duration,
label = label,
position = 'bottom',
useWhileDead = useWhileDead,
canCancel = canCancel,
disable = disableControls,
anim = {
dict = animation.animDict,
clip = animation.anim,
flag = animation?.flags
},
prop = prop
}) then
onFinish()
else
onCancel()
end
end

View File

@@ -0,0 +1,85 @@
--[[
Hi dear customer or developer, here you can fully configure your server's
framework or you could even duplicate this file to create your own framework.
If you do not have much experience, we recommend you download the base version
of the framework that you use in its latest version and it will work perfectly.
]]
if Config.Framework ~= 'qb' then
return
end
QBCore = exports['qb-core']:GetCoreObject()
RegisterNetEvent('QBCore:Client:OnPlayerLoaded')
AddEventHandler('QBCore:Client:OnPlayerLoaded', function(xPlayer)
Wait(10000)
StartThread()
end)
AddEventHandler('onClientResourceStart', function(resourceName)
if (GetCurrentResourceName() == resourceName) then
QBCore = exports['qb-core']:GetCoreObject()
Wait(10000)
StartThread()
end
end)
function getSex()
return QBCore.Functions.GetPlayerData()?.charinfo?.gender
end
function OpenStash(metadata)
local other = {}
other.maxweight = metadata.weight
other.slots = metadata.slots
TriggerServerEvent('inventory:server:OpenInventory', 'stash', 'Backpack_' .. metadata.ID, other)
TriggerEvent('inventory:client:SetCurrentStash', 'Backpack_' .. metadata.ID)
repeat Wait(1000) until IsNuiFocused() == false
TriggerEvent('backpacks:client:close', metadata.ID)
end
function SendTextMessage(msg, type)
if type == 'inform' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'inform'
})
end
if type == 'error' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'error'
})
end
if type == 'success' then
lib.notify({
title = 'Inventory',
description = msg,
type = 'success'
})
end
end
function Progressbar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
if lib.progressCircle({
duration = duration,
label = label,
position = 'bottom',
useWhileDead = useWhileDead,
canCancel = canCancel,
disable = disableControls,
anim = {
dict = animation.animDict,
clip = animation.anim,
flag = animation?.flags
},
prop = prop
}) then
onFinish()
else
onCancel()
end
end

View File

@@ -0,0 +1,47 @@
if Config.Menu ~= 'esx_menu_default' then
return
end
function CreateBackpack(ID)
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'create_password',
{
title = Lang('CREATE_PASSWORD'),
}, function(data, menu)
local length = string.len(data.value)
if length <= 0 then
SendTextMessage(Lang('BAD_PASSWORD'), 'error')
elseif length < Config.PasswordLength.min then
SendTextMessage(Lang('MORE_PASSWORD'), 'error')
elseif length > Config.PasswordLength.max then
SendTextMessage(Lang('LESS_PASSWORD'), 'error')
else
SendTextMessage(Lang('ADDED_PASSWORD'), 'success')
TriggerServerEvent('backpacks:server:add_password', { ID = ID, password = data.value })
menu.close()
end
end, function(data, menu)
menu.close()
end)
end
function CheckMeta(backpack_metadata)
if backpack_metadata.locked then
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'enter_password',
{
title = Lang('INTRODUCE_PASSWORD'),
}, function(data, menu)
if backpack_metadata.password == data.value then
menu.close()
backpack_metadata.trypassword = data.value
OpenBackpack(backpack_metadata)
else
SendTextMessage(Lang('BAD_PASSWORD'), 'error')
menu.close()
end
end, function(data, menu)
menu.close()
end)
else
OpenBackpack(backpack_metadata)
end
end

View File

@@ -0,0 +1,48 @@
if Config.Menu ~= 'ox_lib' then
return
end
function CreateBackpack(ID)
if not lib then
print('You need to uncomment the ox_lib export on line 10 of qs-backpacks/fxmanifest.lua')
return
end
local keyboard1 = lib.inputDialog(Lang('CREATE_PASSWORD') .. ' Min ' .. Config.PasswordLength.min .. ' Max ' .. Config.PasswordLength.max, { Lang('INTRODUCE_PASSWORD_2') })
if not keyboard1 then return end
local pass = tostring(keyboard1[1])
local length = string.len(pass)
if length <= 0 then
SendTextMessage(Lang('BAD_PASSWORD'), 'error')
return
end
if length < Config.PasswordLength.min then
SendTextMessage(Lang('MORE_PASSWORD'), 'error')
return
end
if length > Config.PasswordLength.max then
SendTextMessage(Lang('LESS_PASSWORD'), 'error')
return
end
SendTextMessage(Lang('ADDED_PASSWORD'), 'success')
TriggerServerEvent('backpacks:server:add_password', { ID = ID, password = pass })
end
function CheckMeta(backpack_metadata)
if backpack_metadata.locked then
if not lib then
print('You need to uncomment the ox_lib export on line 10 of qs-backpacks/fxmanifest.lua')
return
end
local data = lib.inputDialog(Lang('INTRODUCE_PASSWORD'), { Lang('INTRODUCE_PASSWORD_2') })
if not data then
SendTextMessage(Lang('BAD_PASSWORD'), 'error')
return
end
backpack_metadata.trypassword = data[1]
OpenBackpack(backpack_metadata)
else
OpenBackpack(backpack_metadata)
end
end

View File

@@ -0,0 +1,64 @@
if Config.Menu ~= 'qb-menu' then
return
end
function CreateBackpack(ID)
local inputData = exports['qb-input']:ShowInput({
header = Lang('CREATE_PASSWORD') .. ' Min ' .. Config.PasswordLength.min .. ' Max ' .. Config.PasswordLength.max,
inputs = {
{
type = 'password',
isRequired = true,
name = 'pass', -- name of the input should be unique otherwise it might override
type = 'password', -- type of the input
text = Lang('INTRODUCE_PASSWORD_2'),
},
}
})
if inputData then
if not inputData.pass then return end
local length = string.len(inputData.pass)
if length <= 0 then
SendTextMessage(Lang('BAD_PASSWORD'), 'error')
return
end
if length < Config.PasswordLength.min then
SendTextMessage(Lang('MORE_PASSWORD'), 'error')
return
end
if length > Config.PasswordLength.max then
SendTextMessage(Lang('LESS_PASSWORD'), 'error')
return
end
SendTextMessage(Lang('ADDED_PASSWORD'), 'success')
TriggerServerEvent('backpacks:server:add_password', { ID = ID, password = inputData.pass })
end
end
function CheckMeta(backpack_metadata)
if backpack_metadata.locked then
local inputData = exports['qb-input']:ShowInput({
header = Lang('INTRODUCE_PASSWORD'),
inputs = {
{
type = 'password',
isRequired = true,
name = 'pass',
text = Lang('INTRODUCE_PASSWORD')
},
}
})
if inputData then
if not inputData.pass then
SendTextMessage(Lang('BAD_PASSWORD'), 'error')
return
end
backpack_metadata.trypassword = inputData.pass
OpenBackpack(backpack_metadata)
end
else
OpenBackpack(backpack_metadata)
end
end

View File

@@ -0,0 +1,32 @@
if Config.SkinScript ~= 'esx_skin' then
return
end
function putClothes(backpack)
ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin)
local clothes = {
male = { ['bags_1'] = backpack.cloth['male'].bag['item'], ['bags_2'] = backpack.cloth['male'].bag['texture'] },
female = { ['bags_1'] = backpack.cloth['female'].bag['item'], ['bags_2'] = backpack.cloth['male'].bag['texture'] },
}
if skin.sex == 0 then
TriggerEvent('skinchanger:loadClothes', skin, clothes.male)
elseif skin.sex == 1 then
TriggerEvent('skinchanger:loadClothes', skin, clothes.female)
end
end)
end
function RemoveClothes()
local cloth = {
male = { ['bags_1'] = 0 },
female = { ['bags_1'] = 0 },
}
ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin)
if skin.sex == 0 then
TriggerEvent('skinchanger:loadClothes', skin, cloth.male)
elseif skin.sex == 1 then
TriggerEvent('skinchanger:loadClothes', skin, cloth.female)
end
end)
end

View File

@@ -0,0 +1,19 @@
if Config.SkinScript ~= 'illenium-appearance' then
return
end
function putClothes(backpack)
if getSex() == 0 then
TriggerEvent('qb-clothing:client:loadOutfit', { outfitData = backpack.cloth['male'] })
else
TriggerEvent('qb-clothing:client:loadOutfit', { outfitData = backpack.cloth['female'] })
end
end
function RemoveClothes()
TriggerEvent('qb-clothing:client:loadOutfit', {
outfitData = {
['bag'] = { item = -1, texture = 0 }
}
})
end

View File

@@ -0,0 +1,19 @@
if Config.SkinScript ~= 'qb-clothing' then
return
end
function putClothes(backpack)
if getSex() == 0 then
TriggerEvent('qb-clothing:client:loadOutfit', { outfitData = backpack.cloth['male'] })
else
TriggerEvent('qb-clothing:client:loadOutfit', { outfitData = backpack.cloth['female'] })
end
end
function RemoveClothes()
TriggerEvent('qb-clothing:client:loadOutfit', {
outfitData = {
['bag'] = { item = -1, texture = 0 }
}
})
end

View File

@@ -0,0 +1,34 @@
fx_version 'adamant'
game 'gta5'
lua54 'yes'
shared_scripts {
'@ox_lib/init.lua',
'shared/*.lua',
'locales/*.lua',
'client/shared.lua'
}
server_scripts {
'server/**/**/**.lua'
}
client_scripts {
'client/**/**/**.lua'
}
escrow_ignore {
'shared/*.lua',
'locales/*.lua',
'client/custom/**/*.lua'
}
dependencies {
'/server:4752', -- ⚠PLEASE READ⚠ This requires at least server build 4700 or higher
'/assetpacks',
'qs-inventory'
}
dependency '/assetpacks'

View File

@@ -0,0 +1,60 @@
# FOR QBCore `qb-core/shared/items`
```lua
['backpack'] = { ['name'] = 'backpack', ['label'] = 'backpack', ['weight'] = 0, ['type'] = 'item', ['image'] = 'backpack.png', ['unique'] = true, ['useable'] = true, ['shouldClose'] = true, ['combinable'] = nil, ['description'] = 'No have' },
['backpack2'] = { ['name'] = 'backpack2', ['label'] = 'backpack2', ['weight'] = 0, ['type'] = 'item', ['image'] = 'backpack2.png', ['unique'] = true, ['useable'] = true, ['shouldClose'] = true, ['combinable'] = nil, ['description'] = 'No have' },
['briefcase'] = { ['name'] = 'briefcase', ['label'] = 'briefcase', ['weight'] = 0, ['type'] = 'item', ['image'] = 'briefcase.png', ['unique'] = true, ['useable'] = true, ['shouldClose'] = true, ['combinable'] = nil, ['description'] = 'No have' },
['paramedicbag'] = { ['name'] = 'paramedicbag', ['label'] = 'paramedicbag', ['weight'] = 0, ['type'] = 'item', ['image'] = 'paramedicbag.png', ['unique'] = true, ['useable'] = true, ['shouldClose'] = true, ['combinable'] = nil, ['description'] = 'No have' },
```
# FOR ESX `qs-inventory/shared/items.lua`
```lua
['backpack'] = {
['name'] = 'backpack',
['label'] = 'backpack',
['weight'] = 0,
['type'] = 'item',
['image'] = 'backpack.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'No have'
},
['backpack2'] = {
['name'] = 'backpack2',
['label'] = 'backpack2',
['weight'] = 0,
['type'] = 'item',
['image'] = 'backpack2.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'No have'
},
['briefcase'] = {
['name'] = 'briefcase',
['label'] = 'briefcase',
['weight'] = 0,
['type'] = 'item',
['image'] = 'briefcase.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'No have'
},
['paramedicbag'] = {
['name'] = 'paramedicbag',
['label'] = 'paramedicbag',
['weight'] = 0,
['type'] = 'item',
['image'] = 'paramedicbag.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'No have'
},
```

View File

@@ -0,0 +1,14 @@
Locales['cs'] = {
['INTRODUCE_PASSWORD'] = 'Zadejte heslo',
['INTRODUCE_PASSWORD_2'] = 'Heslo',
['CREATE_PASSWORD'] = 'Vytvořit heslo',
['OPEN'] = 'Otevírám...',
['CLOSE'] = 'Zavírám...',
['BAD_PASSWORD'] = 'Nesprávné heslo...',
['ADDED_PASSWORD'] = 'Přidáno heslo..',
['MORE_PASSWORD'] = 'Heslo musí mít více znaků',
['LESS_PASSWORD'] = 'Heslo musí mít méně znaků',
['EMPLY_PASSWORD'] = 'Musíte něco vložit',
['NO_BACKPACK'] = 'Batoh nemáte po ruce.',
['FAILED_PASSWORD'] = 'Nepodařilo se přidat heslo.',
}

View File

@@ -0,0 +1,14 @@
Locales['de'] = {
['INTRODUCE_PASSWORD'] = 'Passwort eingeben',
['INTRODUCE_PASSWORD_2'] = 'Passwort',
['CREATE_PASSWORD'] = 'Erstelle ein Passwort',
['OPEN'] = 'Öffnen...',
['CLOSE'] = 'Schließen...',
['BAD_PASSWORD'] = 'Falsches Passwort...',
['ADDED_PASSWORD'] = 'Passwort hinzugefügt...',
['MORE_PASSWORD'] = 'Das Passwort muss länger sein',
['LESS_PASSWORD'] = 'Das Passwort muss kürzer sein',
['EMPLY_PASSWORD'] = 'Du musst ein Passwort eingeben',
['NO_BACKPACK'] = 'Der Rucksack befindet sich nicht in deiner Hand.',
['FAILED_PASSWORD'] = 'Passwort konnte nicht hinzugefügt werden.',
}

View File

@@ -0,0 +1,14 @@
Locales['en'] = {
['INTRODUCE_PASSWORD'] = 'Enter the password',
['INTRODUCE_PASSWORD_2'] = 'Password',
['CREATE_PASSWORD'] = 'Create a password',
['OPEN'] = 'Opening...',
['CLOSE'] = 'Closing...',
['BAD_PASSWORD'] = 'Incorrect password...',
['ADDED_PASSWORD'] = 'Added password..',
['MORE_PASSWORD'] = 'The password must have more characters',
['LESS_PASSWORD'] = 'The password must have fewer characters',
['EMPLY_PASSWORD'] = 'You need to put something',
['NO_BACKPACK'] = 'Backpack is not on your hand.',
['FAILED_PASSWORD'] = 'Failed to add password.',
}

View File

@@ -0,0 +1,14 @@
Locales['es'] = {
['INTRODUCE_PASSWORD'] = 'Introduce la contraseña',
['INTRODUCE_PASSWORD_2'] = 'Contraseña',
['CREATE_PASSWORD'] = 'Crea una contraseña',
['OPEN'] = 'Abriendo...',
['CLOSE'] = 'Cerrando...',
['BAD_PASSWORD'] = 'Contraseña incorrecta...',
['ADDED_PASSWORD'] = 'Contraseña incorrecta...',
['MORE_PASSWORD'] = 'La contraseña tiene que tener mas caracteres',
['LESS_PASSWORD'] = 'La contraseña tiene que tener menos caracteres',
['EMPLY_PASSWORD'] = 'Necesitas poner algo',
['NO_BACKPACK'] = 'La mochila no esta en tu mano.',
['FAILED_PASSWORD'] = 'No pudiste agregar la contraseña.',
}

View File

@@ -0,0 +1,14 @@
Locales['fr'] = {
['INTRODUCE_PASSWORD'] = 'Entrer un mot de passe',
['INTRODUCE_PASSWORD_2'] = 'Mot de passe',
['CREATE_PASSWORD'] = 'Créez votre mot de passe',
['OPEN'] = 'Ouverture...',
['CLOSE'] = 'Fermeture...',
['BAD_PASSWORD'] = 'Mot de passe incorrect...',
['ADDED_PASSWORD'] = 'Nouveau mot de passe..',
['MORE_PASSWORD'] = 'Votre mot de passe est trop court.',
['LESS_PASSWORD'] = 'Votre mot de passe est trop long.',
['EMPLY_PASSWORD'] = 'Vous devez remplir les champs.',
['NO_BACKPACK'] = "Le sac n'est pas dans votre inventaire.",
['FAILED_PASSWORD'] = "Erreur lors de l'ajout du mot de passe.",
}

View File

@@ -0,0 +1,14 @@
Locales['hu'] = {
['INTRODUCE_PASSWORD'] = 'Írja be a jelszót',
['INTRODUCE_PASSWORD_2'] = 'Jelszó',
['CREATE_PASSWORD'] = 'Hozzon létre egy jelszót',
['OPEN'] = 'Nyítás...',
['CLOSE'] = 'Zárás...',
['BAD_PASSWORD'] = 'hibás jelszó...',
['ADDED_PASSWORD'] = 'Hozzáadta jelszót..',
['MORE_PASSWORD'] = 'A jelszónak több karakterből kell állnia',
['LESS_PASSWORD'] = 'A jelszónak kevesebb karakterből kell állnia',
['EMPLY_PASSWORD'] = 'Be kell tenni valamit,',
['NO_BACKPACK'] = 'A hátizsák nincs a kezedben.',
['FAILED_PASSWORD'] = 'Nem sikerült a jelszó hozzáadása.',
}

View File

@@ -0,0 +1,14 @@
Locales['nl'] = {
['INTRODUCE_PASSWORD'] = 'Vul wachtwoord in',
['INTRODUCE_PASSWORD_2'] = 'Wachtwoord',
['CREATE_PASSWORD'] = 'Wachtwoord aanmaken',
['OPEN'] = 'Openen...',
['CLOSE'] = 'Sluiten...',
['BAD_PASSWORD'] = 'Onjuist wachtwoord...',
['ADDED_PASSWORD'] = 'Wachtwoord toegevoegd..',
['MORE_PASSWORD'] = 'Het wachtwoord voldoet niet aan de hoeveelheid tekens',
['LESS_PASSWORD'] = 'Het wachtwoord heeft teveel tekens',
['EMPLY_PASSWORD'] = 'Je moet iets ingeven',
['NO_BACKPACK'] = 'Je draagt momenteel geen tas in je hand.',
['FAILED_PASSWORD'] = 'Wachtwoord toevoegen mislukt.',
}

View File

@@ -0,0 +1,14 @@
Locales['pl'] = {
['INTRODUCE_PASSWORD'] = 'Wprowadź hasło',
['INTRODUCE_PASSWORD_2'] = 'Podaj hasło',
['CREATE_PASSWORD'] = 'Utwórz hasło',
['OPEN'] = 'Otwarcie...',
['CLOSE'] = 'Zamknięcie...',
['BAD_PASSWORD'] = 'Nieprawidłowe hasło...',
['ADDED_PASSWORD'] = 'Dodane hasło...',
['MORE_PASSWORD'] = 'Hasło musi mieć więcej znaków',
['LESS_PASSWORD'] = 'Hasło musi mieć mniej znaków',
['EMPLY_PASSWORD'] = 'Musisz coś wpisać',
['NO_BACKPACK'] = 'Plecak nie jest pod ręką',
['FAILED_PASSWORD'] = 'Nie udało się dodać hasła.',
}

View File

@@ -0,0 +1,14 @@
Locales['pt'] = {
['INTRODUCE_PASSWORD'] = 'Insira a senha',
['INTRODUCE_PASSWORD_2'] = 'Senha',
['CREATE_PASSWORD'] = 'Criar senha',
['OPEN'] = 'Abrindo...',
['CLOSE'] = 'Fechando...',
['BAD_PASSWORD'] = 'Senha incorreta...',
['ADDED_PASSWORD'] = 'Password adicionada.',
['MORE_PASSWORD'] = 'Senha muito curta',
['LESS_PASSWORD'] = 'Senha muito longa',
['EMPLY_PASSWORD'] = 'É necessário inserir algum valor',
['NO_BACKPACK'] = 'A mochila não está na sua mão.',
['FAILED_PASSWORD'] = 'Erro ao adicionar a senha.',
}

View File

@@ -0,0 +1,151 @@
Config = Config or {}
-- Clarification if you are going to create more backpacks you have to add the information in these parts:
-- qs-inventory/config/metadata.js
-- Save information
--[[
} else if (itemData.name == "YOUR_BACKPACK_NAME") {
$(".item-info-title").html("<p>" + label + "</p>");
$(".item-info-description").html(
"<p><strong>ID: </strong><span>" +
itemData.info.ID +
"</span></p><p><strong>Weight: </strong><span>" +
itemData.info.weight +
"</span></p><p><strong>Slots: </strong><span>" +
itemData.info.slots +
"</span></p>"
);
]]
-- qs-inventory/server/custom/GiveItemToPlayer.lua
-- For giveitem admin
--[[
elseif itemData["name"] == "YOUR_BACKPACK_NAME" then
info.ID = 'backpack_'..math.random(111111,999999)
info.weight = 10000
info.slots = 10
]]
-- (IMPORTANT INTEGRATION WITH qs-advancedshops or qs-shops)
-- If you want to add the backpack to a qs-shop, you must add an ID to the item. this is the example :
--[[
qs-shops/config/config.lua or qs-advancedshops/config/shops.lua
[1] = {
name = "backpack",
label = 'Backpack',
price = 250,
amount = 100,
info = {}, -- If you put here info = {}, it will automatically take the weight of your configuration
type = "item",
slot = 1,
},
[2] = {
name = "my_custom_backpack",
label = 'UwU Backpack',
price = 250,
amount = 100,
info = { ID = 'ID_'..math.random(111111,999999), weight = 10000 , slots = 10}, -- If you put this information, it will take the information you put in it
type = "item",
slot = 2,
},
]]
-- (important) do not use both prop and cloth at same time just one.
Config.Items = {
['backpack'] = { --- Item name
slots = 10, -- Change in `qs-inventory/server/custom/GiveItemToPlayer.lua`
weight = 100000, -- Change in `qs-inventory/server/custom/GiveItemToPlayer.lua`
locked = false, -- If you want to have a password change false to true
prop = {
model = 'vw_prop_vw_backpack_01a',
animation = {
dict = 'amb@world_human_hiker_standing@female@base',
anim = 'base',
bone = 'Back', -- LeftHand | RightHand
attaching_position = {
x = -0.20, -- Up - Down
y = -0.10, -- Forward Backward
z = 0.0, -- Left - Right
x_rotation = 10.0,
y_rotation = 90.0,
z_rotation = 175.0,
}
},
},
},
['backpack2'] = {
slots = 6, -- Change in `qs-inventory/server/custom/GiveItemToPlayer.lua`
weight = 10000, -- Change in `qs-inventory/server/custom/GiveItemToPlayer.lua`
cloth = {
male = {
['bag'] = { item = 45, texture = 0 }
},
female = {
['bag'] = { item = 45, texture = 0 }
}
}
},
['briefcase'] = {
slots = 3,
weight = 5000,
locked = true, -- If you want to have a password change false to true
prop = {
model = 'prop_ld_suitcase_01',
animation = {
dict = 'missheistdocksprep1hold_cellphone',
anim = 'static',
bone = 'RightHand',
attaching_position = {
x = 0.10,
y = 0.0,
z = 0.0,
x_rotation = 0.0,
y_rotation = 280.0,
z_rotation = 53.0,
}
},
},
},
['paramedicbag'] = {
slots = 10, -- Change in `qs-inventory/server/custom/GiveItemToPlayer.lua`
weight = 10000, -- Change in `qs-inventory/server/custom/GiveItemToPlayer.lua`
prop = {
model = 'xm_prop_smug_crate_s_medical',
animation = {
dict = 'missheistdocksprep1hold_cellphone',
anim = 'static',
bone = 'RightHand',
attaching_position = {
x = 0.29,
y = -0.05,
z = 0.0,
x_rotation = -25.0,
y_rotation = 280.0,
z_rotation = 75.0,
}
},
}
},
}
Config.Bones = {
bones = {
['RightHand'] = {
bone = 57005,
current_active_porp = nil,
slot = -1,
},
['Back'] = {
bone = 24818,
current_active_porp = nil,
slot = -1,
},
['LeftHand'] = {
bone = 18905,
current_active_porp = nil,
slot = -1,
},
}
}

View File

@@ -0,0 +1,92 @@
Config = Config or {}
Locales = Locales or {}
local esxHas = GetResourceState('es_extended') == 'started'
local qbHas = GetResourceState('qb-core') == 'started'
local qbxHas = GetResourceState('qbx_core') == 'started'
Config.Framework = esxHas and 'esx' or qbHas and 'qb' or qbxHas and 'qb' or 'esx'
--[[
Language settings.
Define the language file located in the locales folder.
]]
Config.Language = 'en' -- Set your lang in locales folder
--[[
Skin script configuration.
Supported options:
- 'qb-clothing': For servers using QBCore's clothing script.
- 'illenium-appearance': For servers using Illenium Appearance.
- 'esx_skin': For servers using the ESX Skin system.
]]
Config.SkinScript = 'illenium-appearance' -- 'qb-clothing', 'illenium-appearance', 'esx_skin'
--[[
Menu system configuration.
Supported options:
- 'qb-menu': For QBCore's menu system.
- 'ox_lib': For Ox Library's menu system.
- 'esx_menu_default': For ESX's default menu system.
]]
Config.Menu = 'qb-menu' -- 'qb-menu', 'ox_lib', 'esx_menu_default'
--[[
Hotbar slots configuration.
Specify the slots that will act as your hotbar.
Use an array of numbers, where each number represents a slot.
]]
Config.Hotbar = {
1, 2, 3, 4, 5
}
--[[
Backpack opening/closing duration.
Configure the time (in seconds) it takes to open or close the backpack.
]]
Config.duration = {
open = 1, -- Time in seconds to open the backpack.
close = 1 -- Time in seconds to close the backpack.
}
--[[
Password length settings.
Define the minimum and maximum length for passwords when required.
]]
Config.PasswordLength = {
min = 3, -- Minimum password length.
max = 5 -- Maximum password length.
}
--[[
Animation configuration for different backpack actions.
Each action includes:
- Dict: The animation dictionary used for the action.
- Anim: The specific animation name.
- Flag: The animation flag (e.g., 49 = upper body only).
]]
Config.Animation = {
close = { -- Animation for closing the backpack.
Dict = 'clothingshirt', -- Animation dictionary.
Anim = 'try_shirt_positive_d', -- Animation name.
Flag = 49 -- Animation flag.
},
open = { -- Animation for opening the backpack.
Dict = 'clothingshirt', -- Animation dictionary.
Anim = 'try_shirt_positive_d', -- Animation name.
Flag = 49 -- Animation flag.
},
inBackpack = { -- Animation for interacting with items in the backpack.
Dict = 'clothingshirt', -- Animation dictionary.
Anim = 'try_shirt_positive_d' -- Animation name.
},
}

View File

@@ -1,3 +1,34 @@
local function SyncEquippedWeaponAmmoOnOpen(inventory)
if not inventory then return inventory end
local ped = cache.ped
local weaponHash = GetSelectedPedWeapon(ped)
local weaponData = WeaponList and WeaponList[weaponHash]
if not weaponData or weaponData.name == 'weapon_unarmed' then
return inventory
end
local ammo = GetAmmoInPedWeapon(ped, weaponHash) or 0
-- Prefer the equipped slot if CurrentWeaponData is available.
local preferredSlot = CurrentWeaponData and CurrentWeaponData.slot
if preferredSlot and inventory[preferredSlot] and inventory[preferredSlot].name == weaponData.name then
inventory[preferredSlot].info = inventory[preferredSlot].info or {}
inventory[preferredSlot].info.ammo = ammo
return inventory
end
for _, item in pairs(inventory) do
if item and item.type == 'weapon' and item.name == weaponData.name then
item.info = item.info or {}
item.info.ammo = ammo
break
end
end
return inventory
end
local checkDistanceInventories = { local checkDistanceInventories = {
'shop', 'shop',
'stash', 'stash',
@@ -38,6 +69,7 @@ RegisterNetEvent(Config.InventoryPrefix .. ':client:OpenInventory', function(Pla
end end
inventory = data.inventory inventory = data.inventory
inventory = SyncEquippedWeaponAmmoOnOpen(inventory)
other = data.other other = data.other
data = GetPlayerData() data = GetPlayerData()

View File

@@ -2,7 +2,7 @@ if not GetResourceState('jobs_creator') == 'missing' then
return return
end end
-- Warning('Started the compatibility module with jobs_creator') Warning('Started the compatibility module with jobs_creator')
CreateThread(function() CreateThread(function()
local other = {} local other = {}

View File

@@ -15,28 +15,22 @@ local function LoadAnimDict(dict)
end end
end end
RegisterKeyMapping('+handsup', Lang('INVENTORY_KEYMAPPING_HANDSUP_LABEL'), 'keyboard', Config.KeyBinds.handsup) RegisterKeyMapping('handsup', Lang('INVENTORY_KEYMAPPING_HANDSUP_LABEL'), 'keyboard', Config.KeyBinds.handsup)
local handsUpActive = false RegisterCommand('handsup', function()
RegisterCommand('+handsup', function()
if not IsPedInAnyVehicle(PlayerPedId(), false) and GetEntityHealth(PlayerPedId()) > 1 then if not IsPedInAnyVehicle(PlayerPedId(), false) and GetEntityHealth(PlayerPedId()) > 1 then
if canHandsUp and not handsUpActive then
handsUpActive = true
RequestAnimDict(lib) RequestAnimDict(lib)
while not HasAnimDictLoaded(lib) do while not HasAnimDictLoaded(lib) do
Wait(100) Wait(100)
end end
SetCurrentPedWeapon(PlayerPedId(), GetHashKey('WEAPON_UNARMED'), true) SetCurrentPedWeapon(PlayerPedId(), GetHashKey('WEAPON_UNARMED'), true)
if IsEntityPlayingAnim(PlayerPedId(), lib, anim, 3) then
ClearPedSecondaryTask(PlayerPedId())
else
if canHandsUp then
TaskPlayAnim(PlayerPedId(), lib, anim, 2.0, 2.5, -1, 49, 0, 0, 0, 0) TaskPlayAnim(PlayerPedId(), lib, anim, 2.0, 2.5, -1, 49, 0, 0, 0, 0)
end end
end end
end)
RegisterCommand('-handsup', function()
if handsUpActive then
handsUpActive = false
ClearPedSecondaryTask(PlayerPedId())
end end
end) end)

View File

@@ -0,0 +1,31 @@
if not Config.Debug then
return
end
Debug('Debug Command', 'inventory:openStash command initialized')
RegisterCommand('inventory:openStash', function(source, args)
local id = args[1] or source
local stashId = 'inventory_debug_stash' .. id
TriggerServerEvent(Config.InventoryPrefix .. ':server:OpenInventory', 'stash', stashId)
TriggerEvent(Config.InventoryPrefix .. ':client:SetCurrentStash', stashId)
end)
Debug('Debug Command', 'inventory:search command initialized')
RegisterCommand('inventory:search', function(source, args)
TriggerServerEvent(Config.InventoryPrefix .. ':server:OpenInventory', 'otherplayer', tonumber(args[1]))
end)
Debug('Debug Command', 'inventory:openOther command initialized')
RegisterCommand('inventory:openOther', function(source, args, raw)
local target = tonumber(args[1])
if not target then
Debug('Debug Command', 'Invalid target ID')
return
end
TriggerServerEvent(Config.InventoryPrefix .. ':server:OpenInventory', 'otherplayer', target)
end)
TriggerEvent('chat:addSuggestion', '/inventory:openOther', 'Open inventory of another player', {
{ name = 'targetId', help = 'Target player ID' },
})

View File

@@ -2,6 +2,8 @@ local PlayerData = GetPlayerData()
local CanShoot, MultiplierAmount = true, 0 local CanShoot, MultiplierAmount = true, 0
CurrentWeaponData = {} CurrentWeaponData = {}
local weaponTrackingActive = false
exports('GetCurrentWeapon', function() exports('GetCurrentWeapon', function()
return CurrentWeaponData return CurrentWeaponData
end) end)
@@ -10,6 +12,55 @@ lib.callback.register('weapons:client:GetCurrentWeapon', function()
return CurrentWeaponData return CurrentWeaponData
end) end)
local function SyncWeaponToServer()
if not CurrentWeaponData or not CurrentWeaponData.info or not CurrentWeaponData.name then return end
local ammo = CurrentWeaponData.info.ammo or 0
TriggerServerEvent('weapons:server:UpdateWeaponAmmo', CurrentWeaponData, ammo)
if MultiplierAmount > 0 then
TriggerServerEvent('weapons:server:UpdateWeaponQuality', CurrentWeaponData, MultiplierAmount, ammo)
MultiplierAmount = 0
end
end
local function StartWeaponTracking()
if weaponTrackingActive then return end
weaponTrackingActive = true
CreateThread(function()
while weaponTrackingActive and CurrentWeaponData and next(CurrentWeaponData) do
local ped = PlayerPedId()
local weapon = GetSelectedPedWeapon(ped)
if weapon ~= `WEAPON_UNARMED` and CurrentWeaponData.info then
CurrentWeaponData.info.ammo = GetAmmoInPedWeapon(ped, weapon)
end
if IsPedShooting(ped) then
if CanShoot then
if weapon and weapon ~= 0 and WeaponList[weapon] then
MultiplierAmount = MultiplierAmount + 1
end
else
if weapon ~= `WEAPON_UNARMED` then
TriggerEvent(Config.InventoryPrefix .. ':client:CheckWeapon', WeaponList[weapon]['name'])
SendTextMessage(Lang('INVENTORY_NOTIFICATION_WEAPON_BROKEN'), 'error')
MultiplierAmount = 0
end
end
end
Wait(100)
end
weaponTrackingActive = false
end)
end
local function StopWeaponTracking()
weaponTrackingActive = false
end
CreateThread(function() CreateThread(function()
while not Config.WeaponsOnVehicle do while not Config.WeaponsOnVehicle do
Wait(250) Wait(250)
@@ -92,12 +143,19 @@ RegisterNetEvent('addAttachment', function(component, urltint)
end) end)
RegisterNetEvent('weapons:client:SetCurrentWeapon', function(data, bool) RegisterNetEvent('weapons:client:SetCurrentWeapon', function(data, bool)
if data ~= false then if CurrentWeaponData and next(CurrentWeaponData) then
SyncWeaponToServer()
StopWeaponTracking()
end
if data ~= false and data ~= nil then
CurrentWeaponData = data CurrentWeaponData = data
CanShoot = bool
StartWeaponTracking()
else else
CurrentWeaponData = {} CurrentWeaponData = {}
end
CanShoot = bool CanShoot = bool
end
end) end)
RegisterNetEvent('weapons:client:SetWeaponQuality', function(amount) RegisterNetEvent('weapons:client:SetWeaponQuality', function(amount)
@@ -291,52 +349,6 @@ CreateThread(function()
SetWeaponsNoAutoswap(true) SetWeaponsNoAutoswap(true)
end) end)
LastUpdatedAmmoTime = nil
CreateThread(function()
while true do
local ped = PlayerPedId()
if GetSelectedPedWeapon(ped) ~= `WEAPON_UNARMED` and CurrentWeaponData?.info and (IsControlJustReleased(0, 24) or IsDisabledControlJustReleased(0, 24)) then
local weapon = GetSelectedPedWeapon(ped)
local ammo = GetAmmoInPedWeapon(ped, weapon)
TriggerServerEvent('weapons:server:UpdateWeaponAmmo', CurrentWeaponData, tonumber(ammo))
CurrentWeaponData.info.ammo = ammo
LastUpdatedAmmoTime = GetGameTimer()
if MultiplierAmount > 0 then
TriggerServerEvent('weapons:server:UpdateWeaponQuality', CurrentWeaponData, MultiplierAmount, ammo)
MultiplierAmount = 0
end
end
Wait(0)
end
end)
CreateThread(function()
while true do
local ped = PlayerPedId()
if CurrentWeaponData and next(CurrentWeaponData) then
if IsPedShooting(ped) or IsControlJustPressed(0, 24) then
local weapon = GetSelectedPedWeapon(ped)
if CanShoot then
if weapon and weapon ~= 0 and WeaponList[weapon] then
TriggerServerCallback('prison:server:checkThrowable', function(result)
if result or GetAmmoInPedWeapon(ped, weapon) <= 0 then return end
MultiplierAmount += 1
end, weapon)
Wait(200)
end
else
if weapon ~= `WEAPON_UNARMED` then
TriggerEvent(Config.InventoryPrefix .. ':client:CheckWeapon', WeaponList[weapon]['name'])
SendTextMessage(Lang('INVENTORY_NOTIFICATION_WEAPON_BROKEN'), 'error')
MultiplierAmount = 0
end
end
end
end
Wait(0)
end
end)
RegisterNetEvent(Config.InventoryPrefix .. ':client:LegacyFuel', function(fuel) RegisterNetEvent(Config.InventoryPrefix .. ':client:LegacyFuel', function(fuel)
Debug('Your gasoline can has: %', fuel) Debug('Your gasoline can has: %', fuel)
TriggerServerEvent('weapons:server:UpdateWeaponAmmo', CurrentWeaponData, fuel) TriggerServerEvent('weapons:server:UpdateWeaponAmmo', CurrentWeaponData, fuel)

View File

@@ -36,7 +36,7 @@ Locales = Locales or {} -- [CORE] Language packs container.
-- 'ar','bg','ca','cs','da','de','el','en','es','fa','fr','hi','hu','it','ja', -- 'ar','bg','ca','cs','da','de','el','en','es','fa','fr','hi','hu','it','ja',
-- 'ko','nl','no','pl','pt','ro','ru','sl','sv','th','tr','zh-CN','zh-TW' -- 'ko','nl','no','pl','pt','ro','ru','sl','sv','th','tr','zh-CN','zh-TW'
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
Config.Language = 'ro' -- [EDIT] Config.Language = 'en' -- [EDIT]
--[[ [INFO] --[[ [INFO]
Choose your preferred language! Choose your preferred language!
@@ -123,7 +123,7 @@ Config.KQPlaceableItems = false -- [EDIT] Use KQ
-- [INFO] Changing weight/slots can require wipes to avoid dupes. Be cautious. -- [INFO] Changing weight/slots can require wipes to avoid dupes. Be cautious.
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
Config.InventoryWeight = { -- [EDIT] Config.InventoryWeight = { -- [EDIT]
weight = 5000, -- [INFO] Max weight (grams). weight = 120000, -- [INFO] Max weight (grams).
slots = 41, -- [INFO] Set 40 to remove protected 6th slot. slots = 41, -- [INFO] Set 40 to remove protected 6th slot.
} }
@@ -138,7 +138,7 @@ Config.DropWeight = { -- [EDIT]
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
-- Label Change [EDIT] -- Label Change [EDIT]
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
Config.LabelChange = false -- [EDIT] Allow item renaming. Config.LabelChange = true -- [EDIT] Allow item renaming.
Config.LabelChangePrice = false -- [EDIT] Price or false for free. Config.LabelChangePrice = false -- [EDIT] Price or false for free.
Config.BlockedLabelChangeItems = { -- [EDIT] Items that cannot be renamed. Config.BlockedLabelChangeItems = { -- [EDIT] Items that cannot be renamed.
money = true, money = true,
@@ -358,8 +358,8 @@ Config.InventoryOptions = {
['thirst'] = true, -- [EDIT] Show thirst ['thirst'] = true, -- [EDIT] Show thirst
['id'] = true, -- [EDIT] Show player ID ['id'] = true, -- [EDIT] Show player ID
['money'] = true, -- [EDIT] Show cash ['money'] = true, -- [EDIT] Show cash
['bank'] = false, -- [EDIT] Show bank balance ['bank'] = true, -- [EDIT] Show bank balance
['blackmoney'] = false, -- [EDIT] Show black money ['blackmoney'] = true, -- [EDIT] Show black money
} }
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
@@ -442,7 +442,7 @@ Config.KeyBinds = { -- [EDIT]
-- Debug & Development Tools [EDIT] -- Debug & Development Tools [EDIT]
-- [INFO] Enables development logs and debugging prints. Use only during testing. -- [INFO] Enables development logs and debugging prints. Use only during testing.
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
Config.Debug = false -- [EDIT] Detailed console prints Config.Debug = true -- [EDIT] Detailed console prints
Config.ZoneDebug = false -- [EDIT] Display additional zone debug info Config.ZoneDebug = false -- [EDIT] Display additional zone debug info
Config.InventoryPrefix = 'inventory' -- [ADV] Internal prefix; changing requires code adjustments Config.InventoryPrefix = 'inventory' -- [ADV] Internal prefix; changing requires code adjustments
Config.SaveInventoryInterval = 12500 -- [EDIT] Autosave interval (ms) Config.SaveInventoryInterval = 12500 -- [EDIT] Autosave interval (ms)

View File

@@ -107,130 +107,130 @@ end
-- [INFO] Define mesas de crafteo por job/ubicación, con blip y recetas propias. -- [INFO] Define mesas de crafteo por job/ubicación, con blip y recetas propias.
--────────────────────────────────────────────────────────────────────────────── --──────────────────────────────────────────────────────────────────────────────
Config.CraftingTables = { Config.CraftingTables = {
-- [1] = { [1] = {
-- name = 'Police Crafting', name = 'Police Crafting',
-- isjob = 'police', isjob = 'police',
-- grades = 'all', grades = 'all',
-- text = '[E] - Police Craft', text = '[E] - Police Craft',
-- blip = { blip = {
-- enabled = true, enabled = true,
-- title = 'Police Crafting', title = 'Police Crafting',
-- scale = 1.0, scale = 1.0,
-- display = 4, display = 4,
-- colour = 0, colour = 0,
-- id = 365 id = 365
-- }, },
-- location = vec3(459.771423, -989.050537, 24.898926), location = vec3(459.771423, -989.050537, 24.898926),
-- items = { items = {
-- [1] = { [1] = {
-- name = 'weapon_pistol', name = 'weapon_pistol',
-- amount = 50, amount = 50,
-- info = {}, info = {},
-- costs = { costs = {
-- ['iron'] = 80, ['iron'] = 80,
-- ['metalscrap'] = 70, ['metalscrap'] = 70,
-- ['rubber'] = 8, ['rubber'] = 8,
-- ['steel'] = 60, ['steel'] = 60,
-- ['lockpick'] = 5, ['lockpick'] = 5,
-- }, },
-- type = 'weapon', type = 'weapon',
-- slot = 1, slot = 1,
-- rep = 'attachmentcraftingrep', rep = 'attachmentcraftingrep',
-- points = 1, points = 1,
-- threshold = 0, threshold = 0,
-- time = 5500, time = 5500,
-- chance = 100 chance = 100
-- }, },
-- [2] = { [2] = {
-- name = 'weapon_smg', name = 'weapon_smg',
-- amount = 1, amount = 1,
-- info = {}, info = {},
-- costs = { costs = {
-- ['iron'] = 80, ['iron'] = 80,
-- ['metalscrap'] = 120, ['metalscrap'] = 120,
-- ['rubber'] = 10, ['rubber'] = 10,
-- ['steel'] = 65, ['steel'] = 65,
-- ['lockpick'] = 10, ['lockpick'] = 10,
-- }, },
-- type = 'weapon', type = 'weapon',
-- slot = 2, slot = 2,
-- rep = 'attachmentcraftingrep', rep = 'attachmentcraftingrep',
-- points = 1, points = 1,
-- threshold = 0, threshold = 0,
-- time = 8500, time = 8500,
-- chance = 100 chance = 100
-- }, },
-- [3] = { [3] = {
-- name = 'weapon_carbinerifle', name = 'weapon_carbinerifle',
-- amount = 1, amount = 1,
-- info = {}, info = {},
-- costs = { costs = {
-- ['iron'] = 120, ['iron'] = 120,
-- ['metalscrap'] = 120, ['metalscrap'] = 120,
-- ['rubber'] = 20, ['rubber'] = 20,
-- ['steel'] = 90, ['steel'] = 90,
-- ['lockpick'] = 14, ['lockpick'] = 14,
-- }, },
-- type = 'weapon', type = 'weapon',
-- slot = 3, slot = 3,
-- rep = 'craftingrep', rep = 'craftingrep',
-- points = 2, points = 2,
-- threshold = 0, threshold = 0,
-- time = 12000, time = 12000,
-- chance = 100 chance = 100
-- } }
-- } }
-- }, },
-- [2] = { [2] = {
-- name = 'Attachment Crafting', name = 'Attachment Crafting',
-- isjob = false, isjob = false,
-- grades = 'all', grades = 'all',
-- text = '[E] - Craft Attachment', text = '[E] - Craft Attachment',
-- blip = { blip = {
-- enabled = true, enabled = true,
-- title = 'Attachment Crafting', title = 'Attachment Crafting',
-- scale = 1.0, scale = 1.0,
-- display = 4, display = 4,
-- colour = 0, colour = 0,
-- id = 365 id = 365
-- }, },
-- location = vec3(90.303299, 3745.503418, 39.771484), location = vec3(90.303299, 3745.503418, 39.771484),
-- items = { items = {
-- [1] = { [1] = {
-- name = 'pistol_extendedclip', name = 'pistol_extendedclip',
-- amount = 50, amount = 50,
-- info = {}, info = {},
-- costs = { costs = {
-- ['metalscrap'] = 140, ['metalscrap'] = 140,
-- ['steel'] = 250, ['steel'] = 250,
-- ['rubber'] = 60, ['rubber'] = 60,
-- }, },
-- type = 'item', type = 'item',
-- slot = 1, slot = 1,
-- rep = 'attachmentcraftingrep', rep = 'attachmentcraftingrep',
-- points = 1, points = 1,
-- threshold = 0, threshold = 0,
-- time = 8000, time = 8000,
-- chance = 90 chance = 90
-- }, },
-- [2] = { [2] = {
-- name = 'pistol_suppressor', name = 'pistol_suppressor',
-- amount = 50, amount = 50,
-- info = {}, info = {},
-- costs = { costs = {
-- ['metalscrap'] = 165, ['metalscrap'] = 165,
-- ['steel'] = 285, ['steel'] = 285,
-- ['rubber'] = 75, ['rubber'] = 75,
-- }, },
-- type = 'item', type = 'item',
-- slot = 2, slot = 2,
-- rep = 'attachmentcraftingrep', rep = 'attachmentcraftingrep',
-- points = 1, points = 1,
-- threshold = 0, threshold = 0,
-- time = 8000, time = 8000,
-- chance = 90 chance = 90
-- }, },
-- } }
-- }, },
-- Continue with the same structure for the other Crafting Tables... -- Continue with the same structure for the other Crafting Tables...
} }

View File

@@ -676,6 +676,7 @@ Config.WeaponAttachments = {
holographic = { component = 'COMPONENT_AT_SIGHTS', item = 'sniper_holoscope' }, holographic = { component = 'COMPONENT_AT_SIGHTS', item = 'sniper_holoscope' },
smallscope = { component = 'COMPONENT_AT_SCOPE_SMALL_MK2', item = 'sniper_smallscope' }, smallscope = { component = 'COMPONENT_AT_SCOPE_SMALL_MK2', item = 'sniper_smallscope' },
largescope = { component = 'COMPONENT_AT_SCOPE_LARGE_MK2', item = 'sniper_largescope' }, largescope = { component = 'COMPONENT_AT_SCOPE_LARGE_MK2', item = 'sniper_largescope' },
thermalscope = { component = 'COMPONENT_AT_SCOPE_THERMAL', item = 'sniper_thermalscope' },
suppressor = { component = 'COMPONENT_AT_SR_SUPP_03', item = 'sniper_suppressor' }, suppressor = { component = 'COMPONENT_AT_SR_SUPP_03', item = 'sniper_suppressor' },
squaredmuzzle = { component = 'COMPONENT_AT_MUZZLE_08', item = 'sniper_squaredmuzzle' }, squaredmuzzle = { component = 'COMPONENT_AT_MUZZLE_08', item = 'sniper_squaredmuzzle' },
barrel = { component = 'COMPONENT_AT_SR_BARREL_02', item = 'sniper_barrel' }, barrel = { component = 'COMPONENT_AT_SR_BARREL_02', item = 'sniper_barrel' },

View File

@@ -4,7 +4,7 @@ game 'gta5'
lua54 'yes' lua54 'yes'
version '3.7.16' version '3.7.22'
name 'qs-inventory' name 'qs-inventory'
author 'Quasar Store' author 'Quasar Store'
@@ -74,7 +74,8 @@ escrow_ignore {
'server/custom/webhook/*.lua', 'server/custom/webhook/*.lua',
'server/custom/misc/*.lua', 'server/custom/misc/*.lua',
'client/modules/weapons.lua', 'client/modules/weapons.lua',
'server/modules/weapons.lua' 'server/modules/weapons.lua',
'client/modules/debug.lua'
} }
dependencies { dependencies {

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Some files were not shown because too many files have changed in this diff Show More