reset branch

This commit is contained in:
Kakarot
2025-05-10 09:29:59 -05:00
parent 49ba87e216
commit a437265c68
44 changed files with 6371 additions and 15718 deletions
+60
View File
@@ -0,0 +1,60 @@
local function hideText()
SendNUIMessage({
action = 'HIDE_TEXT',
})
end
local function drawText(text, position)
if type(position) ~= 'string' then position = 'left' end
SendNUIMessage({
action = 'DRAW_TEXT',
data = {
text = text,
position = position
}
})
end
local function changeText(text, position)
if type(position) ~= 'string' then position = 'left' end
SendNUIMessage({
action = 'CHANGE_TEXT',
data = {
text = text,
position = position
}
})
end
local function keyPressed()
CreateThread(function() -- Not sure if a thread is needed but why not eh?
SendNUIMessage({
action = 'KEY_PRESSED',
})
Wait(500)
hideText()
end)
end
RegisterNetEvent('qb-core:client:DrawText', function(text, position)
drawText(text, position)
end)
RegisterNetEvent('qb-core:client:ChangeText', function(text, position)
changeText(text, position)
end)
RegisterNetEvent('qb-core:client:HideText', function()
hideText()
end)
RegisterNetEvent('qb-core:client:KeyPressed', function()
keyPressed()
end)
exports('DrawText', drawText)
exports('ChangeText', changeText)
exports('HideText', hideText)
exports('KeyPressed', keyPressed)
-20
View File
@@ -1,20 +0,0 @@
RegisterNUICallback('saveConfig', function(data, cb)
local file = data.file
local configData = data.data
TriggerServerEvent('qb-core:server:configEditor', file, configData)
print("Saving config for file:", file)
cb('ok')
end)
RegisterNUICallback('closeEditor', function(data, cb)
SetNuiFocus(false, false)
cb('ok')
end)
RegisterNetEvent('qb-core:client:configEditor', function(allData)
SendNUIMessage({
action = 'populateData',
data = allData
})
SetNuiFocus(true, true)
end)
+279
View File
@@ -0,0 +1,279 @@
-- Player load and unload handling
-- New method for checking if logged in across all scripts (optional)
-- if LocalPlayer.state['isLoggedIn'] then
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
ShutdownLoadingScreenNui()
LocalPlayer.state:set('isLoggedIn', true, false)
if not QBCore.Config.Server.PVP then return end
SetCanAttackFriendly(PlayerPedId(), true, false)
NetworkSetFriendlyFireOption(true)
end)
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
LocalPlayer.state:set('isLoggedIn', false, false)
end)
RegisterNetEvent('QBCore:Client:PvpHasToggled', function(pvp_state)
SetCanAttackFriendly(PlayerPedId(), pvp_state, false)
NetworkSetFriendlyFireOption(pvp_state)
end)
-- Teleport Commands
RegisterNetEvent('QBCore:Command:TeleportToPlayer', function(coords)
local ped = PlayerPedId()
SetPedCoordsKeepVehicle(ped, coords.x, coords.y, coords.z)
end)
RegisterNetEvent('QBCore:Command:TeleportToCoords', function(x, y, z, h)
local ped = PlayerPedId()
SetPedCoordsKeepVehicle(ped, x, y, z)
SetEntityHeading(ped, h or GetEntityHeading(ped))
end)
RegisterNetEvent('QBCore:Command:GoToMarker', function()
local PlayerPedId = PlayerPedId
local GetEntityCoords = GetEntityCoords
local GetGroundZFor_3dCoord = GetGroundZFor_3dCoord
local blipMarker <const> = GetFirstBlipInfoId(8)
if not DoesBlipExist(blipMarker) then
QBCore.Functions.Notify(Lang:t('error.no_waypoint'), 'error', 5000)
return 'marker'
end
-- Fade screen to hide how clients get teleported.
DoScreenFadeOut(650)
while not IsScreenFadedOut() do
Wait(0)
end
local ped, coords <const> = PlayerPedId(), GetBlipInfoIdCoord(blipMarker)
local vehicle = GetVehiclePedIsIn(ped, false)
local oldCoords <const> = GetEntityCoords(ped)
-- Unpack coords instead of having to unpack them while iterating.
-- 825.0 seems to be the max a player can reach while 0.0 being the lowest.
local x, y, groundZ, Z_START = coords['x'], coords['y'], 850.0, 950.0
local found = false
if vehicle > 0 then
FreezeEntityPosition(vehicle, true)
else
FreezeEntityPosition(ped, true)
end
for i = Z_START, 0, -25.0 do
local z = i
if (i % 2) ~= 0 then
z = Z_START - i
end
NewLoadSceneStart(x, y, z, x, y, z, 50.0, 0)
local curTime = GetGameTimer()
while IsNetworkLoadingScene() do
if GetGameTimer() - curTime > 1000 then
break
end
Wait(0)
end
NewLoadSceneStop()
SetPedCoordsKeepVehicle(ped, x, y, z)
while not HasCollisionLoadedAroundEntity(ped) do
RequestCollisionAtCoord(x, y, z)
if GetGameTimer() - curTime > 1000 then
break
end
Wait(0)
end
-- Get ground coord. As mentioned in the natives, this only works if the client is in render distance.
found, groundZ = GetGroundZFor_3dCoord(x, y, z, false);
if found then
Wait(0)
SetPedCoordsKeepVehicle(ped, x, y, groundZ)
break
end
Wait(0)
end
-- Remove black screen once the loop has ended.
DoScreenFadeIn(650)
if vehicle > 0 then
FreezeEntityPosition(vehicle, false)
else
FreezeEntityPosition(ped, false)
end
if not found then
-- If we can't find the coords, set the coords to the old ones.
-- We don't unpack them before since they aren't in a loop and only called once.
SetPedCoordsKeepVehicle(ped, oldCoords['x'], oldCoords['y'], oldCoords['z'] - 1.0)
QBCore.Functions.Notify(Lang:t('error.tp_error'), 'error', 5000)
end
-- If Z coord was found, set coords in found coords.
SetPedCoordsKeepVehicle(ped, x, y, groundZ)
QBCore.Functions.Notify(Lang:t('success.teleported_waypoint'), 'success', 5000)
end)
-- Vehicle Commands
RegisterNetEvent('QBCore:Command:SpawnVehicle', function(vehName)
local ped = PlayerPedId()
local hash = joaat(vehName)
local veh = GetVehiclePedIsUsing(ped)
if not IsModelInCdimage(hash) then return end
RequestModel(hash)
while not HasModelLoaded(hash) do
Wait(0)
end
if IsPedInAnyVehicle(ped) then
SetEntityAsMissionEntity(veh, true, true)
DeleteVehicle(veh)
end
local vehicle = CreateVehicle(hash, GetEntityCoords(ped), GetEntityHeading(ped), true, false)
TaskWarpPedIntoVehicle(ped, vehicle, -1)
SetVehicleFuelLevel(vehicle, 100.0)
SetVehicleDirtLevel(vehicle, 0.0)
SetModelAsNoLongerNeeded(hash)
TriggerEvent('vehiclekeys:client:SetOwner', QBCore.Functions.GetPlate(vehicle))
end)
RegisterNetEvent('QBCore:Command:DeleteVehicle', function()
local ped = PlayerPedId()
local veh = GetVehiclePedIsUsing(ped)
if veh ~= 0 then
SetEntityAsMissionEntity(veh, true, true)
DeleteVehicle(veh)
else
local pcoords = GetEntityCoords(ped)
local vehicles = GetGamePool('CVehicle')
for _, v in pairs(vehicles) do
if #(pcoords - GetEntityCoords(v)) <= 5.0 then
SetEntityAsMissionEntity(v, true, true)
DeleteVehicle(v)
end
end
end
end)
RegisterNetEvent('QBCore:Client:VehicleInfo', function(info)
local plate = QBCore.Functions.GetPlate(info.vehicle)
local hasKeys = true
if GetResourceState('qb-vehiclekeys') == 'started' then
hasKeys = exports['qb-vehiclekeys']:HasKeys(plate)
end
local data = {
vehicle = info.vehicle,
seat = info.seat,
name = info.modelName,
plate = plate,
driver = GetPedInVehicleSeat(info.vehicle, -1),
inseat = GetPedInVehicleSeat(info.vehicle, info.seat),
haskeys = hasKeys
}
TriggerEvent('QBCore:Client:' .. info.event .. 'Vehicle', data)
end)
-- Other stuff
RegisterNetEvent('QBCore:Player:SetPlayerData', function(val)
QBCore.PlayerData = val
end)
RegisterNetEvent('QBCore:Player:UpdatePlayerData', function()
TriggerServerEvent('QBCore:UpdatePlayer')
end)
RegisterNetEvent('QBCore:Notify', function(text, type, length, icon)
QBCore.Functions.Notify(text, type, length, icon)
end)
-- This event is exploitable and should not be used. It has been deprecated, and will be removed soon.
RegisterNetEvent('QBCore:Client:UseItem', function(item)
QBCore.Debug(string.format('%s triggered QBCore:Client:UseItem by ID %s with the following data. This event is deprecated due to exploitation, and will be removed soon. Check qb-inventory for the right use on this event.', GetInvokingResource(), GetPlayerServerId(PlayerId())))
QBCore.Debug(item)
end)
RegisterNUICallback('getNotifyConfig', function(_, cb)
cb(QBCore.Config.Notify)
end)
-- Callback Events --
-- Client Callback
RegisterNetEvent('QBCore:Client:TriggerClientCallback', function(name, ...)
if not QBCore.ClientCallbacks[name] then return end
QBCore.ClientCallbacks[name](function(...)
TriggerServerEvent('QBCore:Server:TriggerClientCallback', name, ...)
end, ...)
end)
-- Server Callback
RegisterNetEvent('QBCore:Client:TriggerCallback', function(name, ...)
if QBCore.ServerCallbacks[name] then
QBCore.ServerCallbacks[name].promise:resolve(...)
if QBCore.ServerCallbacks[name].callback then
QBCore.ServerCallbacks[name].callback(...)
end
QBCore.ServerCallbacks[name] = nil
end
end)
-- Me command
local function Draw3DText(coords, str)
local onScreen, worldX, worldY = World3dToScreen2d(coords.x, coords.y, coords.z)
local camCoords = GetGameplayCamCoord()
local scale = 200 / (GetGameplayCamFov() * #(camCoords - coords))
if onScreen then
SetTextScale(1.0, 0.5 * scale)
SetTextFont(4)
SetTextColour(255, 255, 255, 255)
SetTextEdge(2, 0, 0, 0, 150)
SetTextProportional(1)
SetTextOutline()
SetTextCentre(1)
BeginTextCommandDisplayText('STRING')
AddTextComponentSubstringPlayerName(str)
EndTextCommandDisplayText(worldX, worldY)
end
end
RegisterNetEvent('QBCore:Command:ShowMe3D', function(senderId, msg)
local sender = GetPlayerFromServerId(senderId)
CreateThread(function()
local displayTime = 5000 + GetGameTimer()
while displayTime > GetGameTimer() do
local targetPed = GetPlayerPed(sender)
local tCoords = GetEntityCoords(targetPed)
Draw3DText(tCoords, msg)
Wait(0)
end
end)
end)
-- Listen to Shared being updated
RegisterNetEvent('QBCore:Client:OnSharedUpdate', function(tableName, key, value)
QBCore.Shared[tableName][key] = value
TriggerEvent('QBCore:Client:UpdateObject')
end)
RegisterNetEvent('QBCore:Client:OnSharedUpdateMultiple', function(tableName, values)
for key, value in pairs(values) do
QBCore.Shared[tableName][key] = value
end
TriggerEvent('QBCore:Client:UpdateObject')
end)
RegisterNetEvent('QBCore:Client:SharedUpdate', function(table)
QBCore.Shared = table
end)
+1077 -2
View File
File diff suppressed because it is too large Load Diff
+3 -13
View File
@@ -1,11 +1,6 @@
-- CreateThread(function()
-- while not NetworkIsSessionStarted() do Wait(500) end
-- print('player has loaded at this point')
-- end)
CreateThread(function()
while true do
local sleep = 1000
local sleep = 0
if LocalPlayer.state.isLoggedIn then
sleep = (1000 * 60) * QBCore.Config.UpdateInterval
TriggerServerEvent('QBCore:UpdatePlayer')
@@ -17,16 +12,11 @@ end)
CreateThread(function()
while true do
if LocalPlayer.state.isLoggedIn then
if (LocalPlayer.state['metadata:hunger'] <= 0 or LocalPlayer.state['metadata:thirst'] <= 0)
and not (LocalPlayer.state['metadata:isdead'] or LocalPlayer.state['metadata:inlaststand']) then
if (QBCore.PlayerData.metadata['hunger'] <= 0 or QBCore.PlayerData.metadata['thirst'] <= 0) and not (QBCore.PlayerData.metadata['isdead'] or QBCore.PlayerData.metadata['inlaststand']) then
local ped = PlayerPedId()
local currentHealth = GetEntityHealth(ped)
local decreaseThreshold = math.random(5, 10)
if currentHealth - decreaseThreshold > 0 then
SetEntityHealth(ped, currentHealth - decreaseThreshold)
else
SetEntityHealth(ped, 0)
end
SetEntityHealth(ped, currentHealth - decreaseThreshold)
end
end
Wait(QBCore.Config.StatusInterval)
+50
View File
@@ -0,0 +1,50 @@
QBCore = {}
QBCore.PlayerData = {}
QBCore.Config = QBConfig
QBCore.Shared = QBShared
QBCore.ClientCallbacks = {}
QBCore.ServerCallbacks = {}
-- Get the full QBCore object (default behavior):
-- local QBCore = GetCoreObject()
-- Get only specific parts of QBCore:
-- local QBCore = GetCoreObject({'Players', 'Config'})
local function GetCoreObject(filters)
if not filters then return QBCore end
local results = {}
for i = 1, #filters do
local key = filters[i]
if QBCore[key] then
results[key] = QBCore[key]
end
end
return results
end
exports('GetCoreObject', GetCoreObject)
local function GetSharedItems()
return QBShared.Items
end
exports('GetSharedItems', GetSharedItems)
local function GetSharedVehicles()
return QBShared.Vehicles
end
exports('GetSharedVehicles', GetSharedVehicles)
local function GetSharedWeapons()
return QBShared.Weapons
end
exports('GetSharedWeapons', GetSharedWeapons)
local function GetSharedJobs()
return QBShared.Jobs
end
exports('GetSharedJobs', GetSharedJobs)
local function GetSharedGangs()
return QBShared.Gangs
end
exports('GetSharedGangs', GetSharedGangs)
+157
View File
@@ -0,0 +1,157 @@
QBConfig = {}
QBConfig.MaxPlayers = GetConvarInt('sv_maxclients', 48) -- Gets max players from config file, default 48
QBConfig.DefaultSpawn = vector4(-1035.71, -2731.87, 12.86, 0.0)
QBConfig.UpdateInterval = 5 -- how often to update player data in minutes
QBConfig.StatusInterval = 5000 -- how often to check hunger/thirst status in milliseconds
QBConfig.Money = {}
QBConfig.Money.MoneyTypes = { cash = 500, bank = 5000, crypto = 0 } -- type = startamount - Add or remove money types for your server (for ex. blackmoney = 0), remember once added it will not be removed from the database!
QBConfig.Money.DontAllowMinus = { 'cash', 'crypto' } -- Money that is not allowed going in minus
QBConfig.Money.MinusLimit = -5000 -- The maximum amount you can be negative
QBConfig.Money.PayCheckTimeOut = 10 -- The time in minutes that it will give the paycheck
QBConfig.Money.PayCheckSociety = false -- If true paycheck will come from the society account that the player is employed at, requires qb-management
QBConfig.Player = {}
QBConfig.Player.HungerRate = 4.2 -- Rate at which hunger goes down.
QBConfig.Player.ThirstRate = 3.8 -- Rate at which thirst goes down.
QBConfig.Player.Bloodtypes = {
'A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-',
}
QBConfig.Player.PlayerDefaults = {
citizenid = function() return QBCore.Player.CreateCitizenId() end,
cid = 1,
money = function()
local moneyDefaults = {}
for moneytype, startamount in pairs(QBConfig.Money.MoneyTypes) do
moneyDefaults[moneytype] = startamount
end
return moneyDefaults
end,
optin = true,
charinfo = {
firstname = 'Firstname',
lastname = 'Lastname',
birthdate = '00-00-0000',
gender = 0,
nationality = 'USA',
phone = function() return QBCore.Functions.CreatePhoneNumber() end,
account = function() return QBCore.Functions.CreateAccountNumber() end
},
job = {
name = 'unemployed',
label = 'Civilian',
payment = 10,
type = 'none',
onduty = false,
isboss = false,
grade = {
name = 'Freelancer',
level = 0
}
},
gang = {
name = 'none',
label = 'No Gang Affiliation',
isboss = false,
grade = {
name = 'none',
level = 0
}
},
metadata = {
hunger = 100,
thirst = 100,
stress = 0,
isdead = false,
inlaststand = false,
armor = 0,
ishandcuffed = false,
tracker = false,
injail = 0,
jailitems = {},
status = {},
phone = {},
rep = {},
currentapartment = nil,
callsign = 'NO CALLSIGN',
bloodtype = function() return QBConfig.Player.Bloodtypes[math.random(1, #QBConfig.Player.Bloodtypes)] end,
fingerprint = function() return QBCore.Player.CreateFingerId() end,
walletid = function() return QBCore.Player.CreateWalletId() end,
criminalrecord = {
hasRecord = false,
date = nil
},
licences = {
driver = true,
business = false,
weapon = false
},
inside = {
house = nil,
apartment = {
apartmentType = nil,
apartmentId = nil,
}
},
phonedata = {
SerialNumber = function() return QBCore.Player.CreateSerialNumber() end,
InstalledApps = {}
}
},
position = QBConfig.DefaultSpawn,
items = {},
}
QBConfig.Server = {} -- General server config
QBConfig.Server.Closed = false -- Set server closed (no one can join except people with ace permission 'qbadmin.join')
QBConfig.Server.ClosedReason = 'Server Closed' -- Reason message to display when people can't join the server
QBConfig.Server.Uptime = 0 -- Time the server has been up.
QBConfig.Server.Whitelist = false -- Enable or disable whitelist on the server
QBConfig.Server.WhitelistPermission = 'admin' -- Permission that's able to enter the server when the whitelist is on
QBConfig.Server.PVP = true -- Enable or disable pvp on the server (Ability to shoot other players)
QBConfig.Server.Discord = '' -- Discord invite link
QBConfig.Server.CheckDuplicateLicense = true -- Check for duplicate rockstar license on join
QBConfig.Server.Permissions = { 'god', 'admin', 'mod' } -- Add as many groups as you want here after creating them in your server.cfg
QBConfig.Commands = {} -- Command Configuration
QBConfig.Commands.OOCColor = { 255, 151, 133 } -- RGB color code for the OOC command
QBConfig.Notify = {}
QBConfig.Notify.NotificationStyling = {
group = false, -- Allow notifications to stack with a badge instead of repeating
position = 'right', -- top-left | top-right | bottom-left | bottom-right | top | bottom | left | right | center
progress = true -- Display Progress Bar
}
-- These are how you define different notification variants
-- The "color" key is background of the notification
-- The "icon" key is the css-icon code, this project uses `Material Icons` & `Font Awesome`
QBConfig.Notify.VariantDefinitions = {
success = {
classes = 'success',
icon = 'check_circle'
},
primary = {
classes = 'primary',
icon = 'notifications'
},
warning = {
classes = 'warning',
icon = 'warning'
},
error = {
classes = 'error',
icon = 'error'
},
police = {
classes = 'police',
icon = 'local_police'
},
ambulance = {
classes = 'ambulance',
icon = 'fas fa-ambulance'
}
}
+26 -14
View File
@@ -2,37 +2,49 @@ fx_version 'cerulean'
game 'gta5'
lua54 'yes'
author 'Kakarot'
description 'Core resource for the framework'
version '2.0.0'
description 'Core resource for the framework, contains all the core functionality and features'
version '1.3.0'
shared_scripts {
'config.lua',
'shared/locale.lua',
'locale/en.lua',
'locale/*.lua',
'shared/lua/core.lua',
'shared/lua/loader.lua',
'shared/lua/functions.lua',
'shared/main.lua',
'shared/items.lua',
'shared/jobs.lua',
'shared/vehicles.lua',
'shared/gangs.lua',
'shared/weapons.lua',
'shared/locations.lua'
}
client_scripts {
'client/*.lua'
'client/main.lua',
'client/functions.lua',
'client/loops.lua',
'client/events.lua',
'client/drawtext.lua'
}
server_scripts {
'@oxmysql/lib/MySQL.lua',
'server/*.lua',
'server/main.lua',
'server/functions.lua',
'server/player.lua',
'server/events.lua',
'server/commands.lua',
'server/exports.lua',
'server/debug.lua'
}
ui_page 'html/index.html'
files {
'html/index.html',
'html/style.css',
'html/script.js',
'shared/json/jobs.json',
'shared/json/gangs.json',
'shared/json/items.json',
'shared/json/vehicles.json',
'shared/json/weapons.json'
'html/css/style.css',
'html/css/drawtext.css',
'html/js/*.js'
}
dependency 'oxmysql'
+124
View File
@@ -0,0 +1,124 @@
:root {
/* Typography */
--font-primary: "Exo 2", sans-serif;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;
--font-weight-light: 300;
/* Colors */
--md-primary: #1c75d2;
--md-primary-container: #6facec;
--md-error: #c10114;
--md-error-container: #fe4255;
--md-success: #20bb44;
--md-success-container: #6ae587;
--md-warning: #ff9800;
--md-warning-container: #ffc107;
--md-info: #2197f2;
--md-info-container: #7ac1f7;
--md-surface: #fffbff;
--md-on-surface: #1c1b1f;
/* Custom Variables */
--primary-bg: rgba(23, 23, 23, 90%);
--active-bg: var(--md-error);
--font-color: white;
/* Elevation */
--md-elevation-1: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);
/* Border Radius */
--md-radius-small: 5px;
--md-radius-medium: 10px;
--md-radius-extra-small: 0.15rem;
}
#drawtext-container {
display: none;
width: 100%;
height: 100%;
overflow: hidden;
padding: 0;
margin: 0;
font-family: var(--font-primary) !important;
font-weight: var(--font-weight-light);
}
.text {
position: absolute;
background: var(--primary-bg);
color: var(--font-color);
margin-top: 0.5rem;
padding: 0.45rem;
border-radius: var(--md-radius-extra-small);
box-shadow: var(--md-elevation-1);
}
@media (width: 3840px) and (height: 2160px) {
#drawtext-container {
display: none;
width: 100%;
height: 100%;
overflow: hidden;
padding: 0;
margin: 0;
font-family: var(--font-primary) !important;
font-weight: var(--font-weight-light);
font-size: 1.5vh;
}
}
.text.pressed {
background: var(--active-bg);
}
.top {
left: 45vw;
top: -100px;
}
.top.show {
transition: 0.5s;
top: 10px;
opacity: 1;
}
.top.hide {
transition: 0.5s;
top: -100px;
opacity: 0;
}
.right {
top: 50%;
right: -100px;
}
.right.show {
transition: 0.5s;
right: 10px;
opacity: 1;
}
.right.hide {
transition: 0.5s;
right: -100px;
opacity: 0;
}
.left {
top: 50%;
left: -100px;
}
.left.show {
transition: 0.5s;
left: 10px;
opacity: 1;
}
.left.hide {
transition: 0.5s;
left: -100px;
opacity: 0;
}
+148
View File
@@ -0,0 +1,148 @@
:root {
/* Typography */
--font-primary: "Exo 2", sans-serif;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;
--font-weight-light: 300;
/* Colors */
--md-primary: #0061a4;
--md-on-primary: #ffffff;
--md-primary-container: #d1e4ff;
--md-on-primary-container: #001d36;
--md-error: #ba1a1a;
--md-on-error: #ffffff;
--md-error-container: #ffdad6;
--md-on-error-container: #410002;
--md-success: #006e1c;
--md-on-success: #ffffff;
--md-success-container: #9df996;
--md-on-success-container: #002106;
--md-warning: #7d5800;
--md-on-warning: #ffffff;
--md-warning-container: #ffdf93;
--md-on-warning-container: #271900;
--md-info: #0062a1;
--md-on-info: #ffffff;
--md-info-container: #d0e4ff;
--md-on-info-container: #001d35;
/* Surface colors */
--md-surface: #fcfcff;
--md-on-surface: #1a1c1e;
--md-surface-variant: #dfe2eb;
--md-on-surface-variant: #43474e;
/* Elevation */
--md-elevation-1: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);
/* Border Radius */
--md-radius-small: 4px;
--md-radius-medium: 8px;
}
* {
font-family: var(--font-primary);
font-weight: var(--font-weight-regular);
}
html::-webkit-scrollbar {
display: none;
}
@media (width: 3840px) and (height: 2160px) {
.success {
background-color: rgba(23, 23, 23, 90%);
color: var(--md-on-surface);
box-shadow: var(--md-elevation-1);
border-left: 0.5rem solid var(--md-success);
font-size: 1.5vh;
}
.primary {
background-color: rgba(23, 23, 23, 90%);
color: var(--md-on-surface);
box-shadow: var(--md-elevation-1);
border-left: 5px solid var(--md-primary);
font-size: 1.5vh;
}
.error {
background-color: rgba(23, 23, 23, 90%);
color: var(--md-on-surface);
box-shadow: var(--md-elevation-1);
border-left: 5px solid var(--md-error);
font-size: 1.5vh;
}
.warning {
background-color: rgba(23, 23, 23, 90%);
color: var(--md-on-surface);
box-shadow: var(--md-elevation-1);
border-left: 5px solid var(--md-warning);
font-size: 1.5vh;
}
.police {
background-color: rgba(23, 23, 23, 90%);
color: var(--md-on-surface);
box-shadow: var(--md-elevation-1);
border-left: 5px solid var(--md-info);
font-size: 1.5vh;
}
.ambulance {
background-color: rgba(23, 23, 23, 90%);
color: var(--md-on-surface);
box-shadow: var(--md-elevation-1);
border-left: 5px solid var(--md-error);
font-size: 1.5vh;
}
}
.success {
background-color: var(--md-success-container);
color: var(--md-on-success-container);
padding: 12px 16px;
font-weight: var(--font-weight-medium);
}
.primary {
background-color: var(--md-primary-container);
color: var(--md-on-primary-container);
padding: 12px 16px;
font-weight: var(--font-weight-medium);
}
.warning {
background-color: var(--md-warning-container);
color: var(--md-on-warning-container);
padding: 12px 16px;
font-weight: var(--font-weight-medium);
}
.error {
background-color: var(--md-error-container);
color: var(--md-on-error-container);
padding: 12px 16px;
font-weight: var(--font-weight-medium);
}
.police {
background-color: var(--md-info-container);
color: var(--md-on-info-container);
padding: 12px 16px;
font-weight: var(--font-weight-medium);
}
.ambulance {
background-color: var(--md-error-container);
color: var(--md-on-error-container);
padding: 12px 16px;
font-weight: var(--font-weight-medium);
}
+14 -97
View File
@@ -1,103 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>QB Config Editor</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0"/>
<link rel="stylesheet" href="style.css"/>
<link href="https://fonts.googleapis.com/css2?family=Exo+2:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/quasar@2.12.0/dist/quasar.prod.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" type="text/css" />
<link href="css/style.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/quasar@2.12.0/dist/quasar.umd.prod.js" defer></script>
<script type="module" src="js/app.js"></script>
<script src="js/drawtext.js"></script>
<link rel="stylesheet" href="css/drawtext.css" />
</head>
<body>
<div id="editor-container">
<header>
<h1>QB Config Editor</h1>
<div class="controls">
<button id="save-btn">
<span class="material-symbols-rounded">save</span>
Save Changes
</button>
<button id="close-btn">
<span class="material-symbols-rounded">close</span>
Close
</button>
</div>
</header>
<div class="content">
<div class="sidebar">
<h2>Files</h2>
<ul class="file-list">
<li data-file="config">
<span class="material-symbols-rounded">settings</span>
Server Config
</li>
<li data-file="playerdata">
<span class="material-symbols-rounded">account_circle</span>
Player Data
</li>
<li data-file="jobs">
<span class="material-symbols-rounded">work</span>
Jobs
</li>
<li data-file="gangs">
<span class="material-symbols-rounded">work</span>
Gangs
</li>
<li data-file="items">
<span class="material-symbols-rounded">inventory_2</span>
Items
</li>
<li data-file="vehicles">
<span class="material-symbols-rounded">directions_car</span>
Vehicles
</li>
<li data-file="weapons">
<span class="material-symbols-rounded">bolt</span>
Weapons
</li>
</ul>
</div>
<div class="main-content">
<div class="search-container">
<div class="search-wrapper">
<span class="material-symbols-rounded search-icon">search</span>
<input type="text" id="search-input" placeholder="Search..." aria-label="Search through config items"/>
</div>
</div>
<div id="data-container"></div>
</div>
</div>
<div id="edit-modal" class="hidden">
<div class="modal-content">
<div class="modal-header">
<h2>Edit Item</h2>
<button id="close-modal-btn" aria-label="Close Modal">
<span class="material-symbols-rounded">close</span>
</button>
</div>
<div class="modal-body">
<div id="editor-form"></div>
</div>
<div class="modal-footer">
<button id="cancel-edit-btn">Cancel</button>
<button id="save-item-btn">
<span class="material-symbols-rounded">check</span>
Save
</button>
</div>
</div>
</div>
<div id="q-app" style="min-height: 100vh"></div>
<div id="drawtext-container">
<div id="text" class="text"></div>
</div>
<!-- Floating Action Button -->
<button id="add-item-fab" class="fab" aria-label="Add New Item">
<span class="material-symbols-rounded">add</span>
</button>
<script src="script.js"></script>
</body>
</html>
+65
View File
@@ -0,0 +1,65 @@
import { determineStyleFromVariant, fetchNotifyConfig, NOTIFY_CONFIG } from "./config.js";
const { useQuasar } = Quasar;
const { onMounted, onUnmounted } = Vue;
const fetchNui = async (evName, data) => {
const resourceName = window.GetParentResourceName();
const rawResp = await fetch(`https://${resourceName}/${evName}`, {
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json; charset=UTF8",
},
method: "POST",
});
return await rawResp.json();
};
window.fetchNui = fetchNui;
const app = Vue.createApp({
setup() {
const $q = useQuasar();
const showNotif = async ({ data }) => {
if (data?.action !== "notify") return;
const { text, length, type, caption, icon: dataIcon } = data;
let { classes, icon } = determineStyleFromVariant(type);
if (dataIcon) {
icon = dataIcon;
}
if (!NOTIFY_CONFIG) {
console.error("The notification config did not load properly, trying again for next time");
await fetchNotifyConfig();
if (NOTIFY_CONFIG) return showNotif({ data });
}
$q.notify({
message: text,
multiLine: text.length > 100,
group: NOTIFY_CONFIG.NotificationStyling.group ?? false,
progress: NOTIFY_CONFIG.NotificationStyling.progress ?? true,
position: NOTIFY_CONFIG.NotificationStyling.position ?? "right",
timeout: length,
caption,
classes,
icon,
});
};
onMounted(() => {
window.addEventListener("message", showNotif);
});
onUnmounted(() => {
window.removeEventListener("message", showNotif);
});
return {};
},
});
app.use(Quasar, { config: {} });
app.mount("#q-app");
+53
View File
@@ -0,0 +1,53 @@
export let NOTIFY_CONFIG = null;
const defaultConfig = {
NotificationStyling: {
group: true,
position: "top-right",
progress: true,
},
VariantDefinitions: {
success: {
classes: "success",
icon: "done",
},
primary: {
classes: "primary",
icon: "info",
},
error: {
classes: "error",
icon: "dangerous",
},
police: {
classes: "police",
icon: "local_police",
},
ambulance: {
classes: "ambulance",
icon: "fas fa-ambulance",
},
},
};
export const determineStyleFromVariant = (variant) => {
const variantData = NOTIFY_CONFIG.VariantDefinitions[variant];
if (!variantData) throw new Error(`Style of type: ${variant}, does not exist in the config`);
return variantData;
};
export const fetchNotifyConfig = async () => {
try {
NOTIFY_CONFIG = await window.fetchNui("getNotifyConfig", {});
if (!NOTIFY_CONFIG) {
NOTIFY_CONFIG = defaultConfig;
}
} catch (error) {
console.error("Failed to fetch notification config, using default", error);
NOTIFY_CONFIG = defaultConfig;
}
};
window.addEventListener("load", async () => {
await fetchNotifyConfig();
});
+124
View File
@@ -0,0 +1,124 @@
let direction = null;
const drawText = async (textData) => {
const text = document.getElementById("text");
let { position } = textData;
switch (textData.position) {
case "left":
addClass(text, position);
direction = "left";
break;
case "top":
addClass(text, position);
direction = "top";
break;
case "right":
addClass(text, position);
direction = "right";
break;
default:
addClass(text, "left");
direction = "left";
break;
}
text.innerHTML = textData.text;
document.getElementById("drawtext-container").style.display = "block";
await sleep(100);
addClass(text, "show");
};
const changeText = async (textData) => {
const text = document.getElementById("text");
let { position } = textData;
removeClass(text, "show");
addClass(text, "pressed");
addClass(text, "hide");
await sleep(500);
removeClass(text, "left");
removeClass(text, "right");
removeClass(text, "top");
removeClass(text, "bottom");
removeClass(text, "hide");
removeClass(text, "pressed");
switch (textData.position) {
case "left":
addClass(text, position);
direction = "left";
break;
case "top":
addClass(text, position);
direction = "top";
break;
case "right":
addClass(text, position);
direction = "right";
break;
default:
addClass(text, "left");
direction = "left";
break;
}
text.innerHTML = textData.text;
await sleep(100);
text.classList.add("show");
};
const hideText = async () => {
const text = document.getElementById("text");
removeClass(text, "show");
addClass(text, "hide");
setTimeout(() => {
removeClass(text, "left");
removeClass(text, "right");
removeClass(text, "top");
removeClass(text, "bottom");
removeClass(text, "hide");
removeClass(text, "pressed");
document.getElementById("drawtext-container").style.display = "none";
}, 1000);
};
const keyPressed = () => {
const text = document.getElementById("text");
addClass(text, "pressed");
};
window.addEventListener("message", (event) => {
const data = event.data;
const action = data.action;
const textData = data.data;
switch (action) {
case "DRAW_TEXT":
return drawText(textData);
case "CHANGE_TEXT":
return changeText(textData);
case "HIDE_TEXT":
return hideText();
case "KEY_PRESSED":
return keyPressed();
default:
return;
}
});
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const removeClass = (element, name) => {
if (element.classList.contains(name)) {
element.classList.remove(name);
}
};
const addClass = (element, name) => {
if (!element.classList.contains(name)) {
element.classList.add(name);
}
};
-579
View File
@@ -1,579 +0,0 @@
// Main data store
let configData = {};
let currentFile = "config";
let currentEditItem = null;
let editPath = null;
// Elements
const dataContainer = document.getElementById("data-container");
const searchInput = document.getElementById("search-input");
const saveBtn = document.getElementById("save-btn");
const closeBtn = document.getElementById("close-btn");
const editModal = document.getElementById("edit-modal");
const closeModalBtn = document.getElementById("close-modal-btn");
const saveItemBtn = document.getElementById("save-item-btn");
const cancelEditBtn = document.getElementById("cancel-edit-btn");
const editorForm = document.getElementById("editor-form");
const fileListItems = document.querySelectorAll(".file-list li");
const fabButton = document.getElementById("add-item-fab");
let fileInput;
// Initialize
document.addEventListener("DOMContentLoaded", () => {
// Add event listeners
searchInput.addEventListener("input", filterData);
saveBtn.addEventListener("click", saveChanges);
closeBtn.addEventListener("click", closeEditor);
closeModalBtn.addEventListener("click", closeModal);
saveItemBtn.addEventListener("click", saveItemChanges);
cancelEditBtn.addEventListener("click", closeModal);
fabButton.addEventListener("click", addItemModal);
fileListItems.forEach((item) => {
item.addEventListener("click", () => {
// Update active state
fileListItems.forEach((i) => i.classList.remove("active"));
item.classList.add("active");
// Change current file
currentFile = item.dataset.file;
// Load file data - in a real implementation, this would trigger a fetch to the server
// For now, we'll just use the data we already have
currentFile = item.dataset.file;
renderData();
});
});
// Listen for NUI messages from FiveM
window.addEventListener("message", function (event) {
const data = event.data;
if (data.action === "populateData") {
// Expect data.data to be an object with each file's data
configData = data.data;
renderData();
}
});
});
const templates = {
jobs: {
defaultDuty: false,
offDutyPay: false,
type: "",
label: "",
grades: [
{
name: "",
isboss: false,
payment: 0,
},
],
},
gangs: {
label: "",
grades: [
{
name: "",
isboss: false,
level: 0,
},
],
},
items: {
unique: false,
shouldClose: false,
useable: false,
name: "",
label: "",
weight: 0,
type: "",
image: "",
description: "",
ammotype: "",
},
vehicles: {
brand: "",
price: 0,
name: "",
category: "",
model: "",
type: "",
shop: ["pdm"],
},
weapons: {
name: "",
label: "",
damageReason: "",
weapontype: "",
ammotype: "",
},
};
function addItemModal() {
const modal = document.getElementById("edit-modal");
const editorForm = document.getElementById("editor-form");
// Clear the form and set it up for adding a new item
editorForm.innerHTML = ""; // Clear any existing content
modal.classList.remove("hidden"); // Show the modal
// Get the template for the current category
const template = templates[currentFile] || {};
// Add fields for the new item based on the template
Object.entries(template).forEach(([key, value]) => {
const formGroup = document.createElement("div");
formGroup.className = "form-group";
const label = document.createElement("label");
label.textContent = key;
formGroup.appendChild(label);
if (typeof value === "boolean" || typeof value === "string" || typeof value === "number") {
const field = createInputField({
key,
value,
onChange: () => {}, // No need to handle it now, we'll read from DOM on save
});
formGroup.appendChild(field.querySelector("input"));
} else if (Array.isArray(value)) {
const arrayField = createArrayField({
key,
array: value,
onItemChange: () => {}, // Again, no live tracking needed
onItemRemove: (index) => {
value.splice(index, 1);
addItemModal(); // re-render modal
},
onItemAdd: () => {
// Clone first item or fallback
if (value.length > 0 && typeof value[0] === "object") {
const newItem = {};
Object.entries(value[0]).forEach(([k, v]) => {
newItem[k] = typeof v === "number" ? 0 : typeof v === "boolean" ? false : "";
});
value.push(newItem);
} else {
value.push("");
}
addItemModal(); // re-render modal
},
});
arrayField.id = `new-item-${key}`;
formGroup.appendChild(arrayField);
} else {
const input = document.createElement("input");
input.type = "text";
input.className = "form-control";
input.value = value;
formGroup.appendChild(input);
}
editorForm.appendChild(formGroup);
});
// Add Save and Cancel buttons
const saveButton = document.getElementById("save-item-btn");
saveButton.onclick = saveNewItem;
const cancelButton = document.getElementById("cancel-edit-btn");
cancelButton.onclick = () => {
modal.classList.add("hidden"); // Hide the modal
};
}
function saveNewItem() {
const template = templates[currentFile] || {};
const newItem = {};
// Populate the new item based on the template
Object.entries(template).forEach(([key, value]) => {
if (Array.isArray(value)) {
// Handle arrays
const arrayContainer = document.getElementById(`new-item-${key}`);
const arrayItems = Array.from(arrayContainer.querySelectorAll(".array-item"));
newItem[key] = arrayItems.map((itemGroup) => {
const item = {};
Array.from(itemGroup.querySelectorAll("input")).forEach((input) => {
const itemKey = input.dataset.itemKey;
item[itemKey] = input.type === "number" ? parseFloat(input.value) || 0 : input.value;
});
return item;
});
} else {
// Handle simple values
const input = document.getElementById(`new-item-${key}`);
if (input) {
newItem[key] = input.type === "checkbox" ? input.checked : input.value;
}
}
});
// Add the new item to the current category
if (!configData[currentFile]) {
configData[currentFile] = {};
}
const itemName = document.getElementById("new-item-name").value.trim();
if (!itemName) {
alert("Please provide a name for the new item.");
return;
}
configData[currentFile][itemName] = newItem;
// Update the UI
renderData();
// Close the modal
document.getElementById("edit-modal").classList.add("hidden");
}
// Render data in the container
function renderData() {
dataContainer.innerHTML = "";
if (!configData[currentFile]) {
dataContainer.innerHTML = "<p>No data available for this file.</p>";
return;
}
const data = configData[currentFile];
const searchText = searchInput.value.toLowerCase();
// For object-based data (like jobs)
Object.entries(data).forEach(([key, value]) => {
// If there's a search term, filter the data
if (searchText && !key.toLowerCase().includes(searchText) && !JSON.stringify(value).toLowerCase().includes(searchText)) {
return;
}
const card = document.createElement("div");
card.className = "data-card";
card.dataset.key = key;
// Create card header
const header = document.createElement("h3");
header.textContent = key;
card.appendChild(header);
// Create properties section
const properties = document.createElement("div");
properties.className = "data-properties";
// Add main properties (not going too deep)
Object.entries(value).forEach(([propKey, propValue]) => {
// Skip complex objects for the card view
if (typeof propValue === "object" && propValue !== null) {
return;
}
const property = document.createElement("div");
property.className = "property";
const label = document.createElement("span");
label.className = "property-label";
label.textContent = propKey;
const val = document.createElement("span");
val.className = "property-value";
val.textContent = propValue;
property.appendChild(label);
property.appendChild(val);
properties.appendChild(property);
});
card.appendChild(properties);
// Add click event to edit
card.addEventListener("click", () => {
editItem(key, value);
});
dataContainer.appendChild(card);
});
}
// Create form for editing
function createForm(obj) {
editorForm.innerHTML = "";
const sortedKeys = Object.keys(obj).sort((a, b) => {
const typeOrder = (value) => {
if (typeof value === "boolean") return 0;
if (typeof value === "string" || typeof value === "number") return 1;
if (Array.isArray(value)) return 2;
return 3;
};
return typeOrder(obj[a]) - typeOrder(obj[b]);
});
sortedKeys.forEach((key) => {
const value = obj[key];
const formGroup = document.createElement("div");
formGroup.className = "form-group";
const label = document.createElement("label");
label.htmlFor = `field-${key}`;
label.textContent = key;
formGroup.appendChild(label);
if (typeof value === "boolean" || typeof value === "string" || typeof value === "number") {
const field = createInputField({
key,
value,
onChange: (val) => updateValue(key, val),
});
formGroup.appendChild(field.querySelector("input"));
} else if (Array.isArray(value)) {
const arrayField = createArrayField({
key,
array: value,
onItemChange: (index, itemKey, val) => {
if (itemKey) {
updateArrayObjectValue(key, index, itemKey, val);
} else {
updateArrayValue(key, index, val);
}
},
onItemRemove: (index) => removeArrayItem(key, index),
onItemAdd: () => addArrayItem(key, value),
});
formGroup.appendChild(arrayField);
} else {
// Fallback for objects or unknown types
const objectContainer = document.createElement("div");
objectContainer.className = "complex-object";
const header = document.createElement("div");
header.className = "complex-object-header";
objectContainer.appendChild(header);
const textarea = document.createElement("textarea");
textarea.className = "form-control";
textarea.value = JSON.stringify(value, null, 2);
textarea.dataset.key = key;
textarea.addEventListener("input", (e) => {
try {
const parsed = JSON.parse(e.target.value);
updateValue(key, parsed);
textarea.style.borderColor = "#3d3d3d";
} catch (err) {
textarea.style.borderColor = "#ff4d4d";
}
});
objectContainer.appendChild(textarea);
formGroup.appendChild(objectContainer);
}
editorForm.appendChild(formGroup);
});
}
// UTILS
function createInputField({ key, value, onChange }) {
const formGroup = document.createElement("div");
formGroup.className = "form-group";
const label = document.createElement("label");
label.textContent = key;
formGroup.appendChild(label);
const input = document.createElement("input");
if (typeof value === "boolean") {
input.type = "checkbox";
input.checked = value;
} else if (typeof value === "number") {
input.type = "number";
input.value = value;
} else {
input.type = "text";
input.value = value;
}
input.className = "form-control";
input.addEventListener("input", (e) => {
let newValue;
if (input.type === "checkbox") {
newValue = input.checked;
} else if (input.type === "number") {
newValue = parseFloat(e.target.value) || 0;
} else {
newValue = e.target.value;
}
onChange(newValue);
});
formGroup.appendChild(input);
return formGroup;
}
function createArrayField({ key, array, onItemChange, onItemRemove, onItemAdd }) {
const container = document.createElement("div");
container.className = "complex-object";
const header = document.createElement("div");
header.className = "complex-object-header";
const addBtn = document.createElement("button");
addBtn.className = "add-item-btn";
addBtn.textContent = "Add Item";
addBtn.addEventListener("click", (e) => {
e.preventDefault();
onItemAdd();
});
header.appendChild(addBtn);
container.appendChild(header);
array.forEach((item, index) => {
const arrayItem = document.createElement("div");
arrayItem.className = "array-item";
// Remove button
const removeBtn = document.createElement("button");
removeBtn.className = "remove-item-btn";
removeBtn.textContent = "x";
removeBtn.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
onItemRemove(index);
});
arrayItem.appendChild(removeBtn);
if (typeof item === "object" && item !== null) {
Object.entries(item).forEach(([itemKey, itemValue]) => {
const field = createInputField({
key: itemKey,
value: itemValue,
onChange: (val) => onItemChange(index, itemKey, val),
});
arrayItem.appendChild(field);
});
} else {
const input = document.createElement("input");
input.className = "form-control";
input.value = item;
input.addEventListener("input", (e) => {
onItemChange(index, null, e.target.value);
});
arrayItem.appendChild(input);
}
container.appendChild(arrayItem);
});
return container;
}
// Filter data based on search input
function filterData() {
renderData();
}
// Edit an item
function editItem(key, value) {
currentEditItem = { key, value: JSON.parse(JSON.stringify(value)) };
editPath = null; // Reset path for root level editing
// Create form for editing
createForm(value);
// Show the modal
editModal.classList.remove("hidden");
}
function setDeepValue(obj, pathArray, value) {
const lastKey = pathArray.pop();
const target = pathArray.reduce((acc, key) => acc[key], obj);
target[lastKey] = value;
}
function updateValue(key, value) {
const path = editPath ? editPath.split(".").concat(key) : [key];
setDeepValue(currentEditItem.value, path, value);
}
function updateArrayValue(arrayKey, index, value) {
const path = editPath ? editPath.split(".").concat([arrayKey, index]) : [arrayKey, index];
setDeepValue(currentEditItem.value, path, value);
}
function updateArrayObjectValue(arrayKey, index, itemKey, value) {
const path = editPath ? editPath.split(".").concat([arrayKey, index, itemKey]) : [arrayKey, index, itemKey];
setDeepValue(currentEditItem.value, path, value);
}
function updateArray(arrayKey, action, index = null, newItem = null) {
const path = editPath ? editPath.split(".").concat([arrayKey]) : [arrayKey];
const target = path.reduce((acc, key) => acc[key], currentEditItem.value);
if (action === "add") {
target.push(newItem);
} else if (action === "remove" && index !== null) {
target.splice(index, 1);
}
createForm(currentEditItem.value);
}
function addArrayItem(arrayKey, array) {
let newItem;
if (array.length > 0 && typeof array[0] === "object" && array[0] !== null) {
newItem = {};
Object.keys(array[0]).forEach((key) => {
const sample = array[0][key];
newItem[key] = typeof sample === "number" ? 0 : typeof sample === "boolean" ? false : "";
});
} else {
newItem = typeof array[0] === "number" ? 0 : "";
}
updateArray(arrayKey, "add", null, newItem);
}
function removeArrayItem(arrayKey, index) {
updateArray(arrayKey, "remove", index);
}
// Save changes to the edited item
function saveItemChanges() {
configData[currentFile][currentEditItem.key] = currentEditItem.value;
closeModal();
renderData();
}
// Close the edit modal
function closeModal() {
editModal.classList.add("hidden");
currentEditItem = null;
editPath = null;
}
// Save all changes to the server
function saveChanges() {
// Send data back to the client
fetch(`https://${GetParentResourceName()}/saveConfig`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
body: JSON.stringify({
file: currentFile,
data: configData[currentFile],
}),
}).catch((err) => console.error("Error:", err));
}
// Close the editor
function closeEditor() {
fetch(`https://${GetParentResourceName()}/closeEditor`, {
method: "POST",
}).catch((err) => console.error("Error:", err));
}
-674
View File
@@ -1,674 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Exo+2:wght@300;400;500;600;700&display=swap");
:root {
/* Primary colors */
--md-primary: #f44336;
--md-on-primary: #ffffff;
--md-primary-container: #ffdad6;
--md-on-primary-container: #410002;
/* Secondary colors */
--md-secondary: #d32f2f;
--md-on-secondary: #ffffff;
--md-secondary-container: #ffdad5;
--md-on-secondary-container: #410001;
/* Tertiary colors */
--md-tertiary: #ff8a65;
--md-on-tertiary: #ffffff;
--md-tertiary-container: #ffdacc;
--md-on-tertiary-container: #410002;
/* Surface colors (dark theme) */
--md-surface: #1c1b1f;
--md-on-surface: #e6e1e5;
--md-surface-variant: #49454f;
--md-on-surface-variant: #c9c5d0;
--md-surface-container-lowest: #0f0d13;
--md-surface-container-low: #1d1b20;
--md-surface-container: #211f26;
--md-surface-container-high: #2b2930;
--md-surface-container-highest: #36343b;
/* Error colors */
--md-error: #b3261e;
--md-on-error: #ffffff;
--md-error-container: #93000a;
--md-on-error-container: #ffdad5;
/* Notification colors */
--md-success: #9bd880;
--md-on-success: #193800;
--md-success-container: #275000;
--md-on-success-container: #b6f397;
--md-warning: #ffba47;
--md-on-warning: #422b00;
--md-warning-container: #5f3f00;
--md-on-warning-container: #ffddb0;
--md-info: #b3c5ff;
--md-on-info: #002a77;
--md-info-container: #003ea7;
--md-on-info-container: #dae1ff;
/* Neutrals */
--md-outline: #79747e;
--md-outline-variant: #49454f;
--md-inverse-surface: #e6e1e5;
--md-inverse-on-surface: #1c1b1f;
--md-scrim: rgba(0, 0, 0, 0.6);
--md-shadow: rgba(0, 0, 0, 0.15);
/* Elevation */
--md-elevation-1: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);
--md-elevation-2: 0px 2px 6px 2px rgba(0, 0, 0, 0.15);
--md-elevation-3: 0px 4px 8px 3px rgba(0, 0, 0, 0.15);
/* Shapes */
--md-radius-small: 8px;
--md-radius-medium: 12px;
--md-radius-large: 16px;
/* Typography */
--font-primary: "Exo 2", sans-serif;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: var(--font-primary);
}
body {
background-color: transparent;
color: var(--md-on-surface);
overflow: hidden;
}
#editor-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80vw;
height: 80vh;
background-color: var(--md-surface);
border-radius: var(--md-radius-medium);
box-shadow: var(--md-elevation-3);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Header */
header {
padding: 16px;
background-color: var(--md-surface-container-high);
display: flex;
justify-content: space-between;
align-items: center;
}
header h1 {
font-size: 22px;
color: var(--md-primary);
}
.controls {
display: flex;
gap: 12px;
}
button {
padding: 8px 16px;
border: none;
border-radius: var(--md-radius-small);
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
transition: background-color 0.2s;
}
#save-btn {
background-color: var(--md-success);
color: var(--md-on-success);
}
#save-btn:hover {
box-shadow: var(--md-elevation-1);
filter: brightness(1.1);
}
#close-btn {
background-color: var(--md-secondary);
color: var(--md-on-secondary);
}
#close-btn:hover {
box-shadow: var(--md-elevation-1);
filter: brightness(1.1);
}
/* Floating Action Button */
.fab {
position: fixed;
right: 24px;
bottom: 24px;
width: 56px;
height: 56px;
border-radius: 50%;
background-color: var(--md-primary);
color: var(--md-on-primary);
box-shadow: var(--md-elevation-3);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s;
}
.fab:hover {
transform: translateY(-4px);
box-shadow: var(--md-elevation-2);
}
.fab:active {
transform: translateY(0);
}
/* Content layout */
.content {
display: flex;
flex: 1;
overflow: hidden;
height: calc(100vh - 130px);
max-height: calc(100% - 60px);
}
.sidebar {
background-color: var(--md-surface-container-high);
width: 220px;
padding: 16px;
}
.sidebar h2 {
font-size: 16px;
font-weight: 500;
margin-bottom: 16px;
color: var(--md-on-surface);
}
.file-list {
list-style: none;
}
.file-list li {
padding: 12px;
border-radius: var(--md-radius-small);
cursor: pointer;
display: flex;
gap: 12px;
}
.file-list li .material-symbols-rounded {
font-size: 20px;
}
.file-list li:hover {
background-color: var(--md-surface-container-high);
transform: translateY(-2px);
box-shadow: var(--md-elevation-1);
}
.file-list li.active {
background-color: var(--md-primary);
color: var(--md-on-primary);
transform: translateY(-2px);
box-shadow: var(--md-elevation-1);
}
.main-content {
flex: 1;
padding: 16px;
overflow-y: auto;
display: flex;
flex-direction: column;
background-color: var(--md-surface-container-low);
}
/* Search Input */
.search-container {
margin-bottom: 16px;
}
.search-wrapper {
position: relative;
width: 100%;
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--md-outline);
pointer-events: none;
}
#search-input {
width: 100%;
padding: 12px 12px 12px 40px;
border: 1px solid var(--md-outline-variant);
border-radius: var(--md-radius-small);
background-color: var(--md-surface-container);
color: var(--md-on-surface);
}
#search-input:focus {
border-color: var(--md-primary);
outline: none;
}
#data-container {
flex: 1;
overflow-y: auto;
max-height: calc(100vh - 200px);
scrollbar-width: thin; /* For Firefox */
scrollbar-color: var(--md-outline-variant) transparent;
}
#data-container::-webkit-scrollbar {
width: 6px; /* Scrollbar width for WebKit browsers */
}
#data-container::-webkit-scrollbar-thumb {
background-color: var(--md-outline-variant); /* Thumb color */
border-radius: 3px; /* Rounded corners for the thumb */
}
#data-container::-webkit-scrollbar-track {
background-color: transparent; /* Track color */
}
/* Cards for displaying data items */
.data-card {
background-color: var(--md-surface-container);
border-radius: var(--md-radius-medium);
padding: 16px;
margin-bottom: 12px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
overflow: hidden;
word-break: break-word;
position: relative;
}
.data-card::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--md-primary);
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
.data-card:hover {
background-color: var(--md-surface-container-high);
transform: translateY(-2px);
box-shadow: var(--md-elevation-1);
}
.data-card:hover::after {
opacity: 0.05;
}
.data-card h3 {
color: var(--md-primary);
margin-bottom: 12px;
font-weight: 500;
}
.data-properties {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 12px;
}
.property {
display: flex;
flex-direction: column;
}
.property-label {
font-size: 12px;
color: var(--md-outline);
}
.property-value {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
/* Modal */
#edit-modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
place-items: center; /* Perfectly centers the modal */
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(2px);
z-index: 1000;
}
#edit-modal.hidden {
animation: modal-disappear 0.2s ease-out forwards;
display: none;
}
.modal-content {
background-color: var(--md-surface-container);
border-radius: var(--md-radius-large);
width: 600px;
max-width: 90%;
max-height: 90vh;
display: flex;
flex-direction: column;
box-shadow: var(--md-elevation-3);
animation: modal-appear 0.2s ease-out;
overflow: hidden;
}
.modal-header {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--md-outline-variant);
background-color: var(--md-surface-container-high);
}
.modal-header h2 {
font-size: 20px;
font-weight: 400;
color: var(--md-primary);
}
#close-modal-btn {
background: none;
color: var(--md-on-surface-variant);
font-size: 24px;
padding: 0;
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
#close-modal-btn:hover {
color: var(--md-error);
background-color: var(--md-surface-container-high);
}
.modal-body {
padding: 24px;
overflow-y: auto;
max-height: 60vh;
scrollbar-width: thin;
scrollbar-color: var(--md-outline-variant) transparent;
}
.modal-body::-webkit-scrollbar {
width: 6px;
}
.modal-body::-webkit-scrollbar-thumb {
background-color: var(--md-outline-variant);
border-radius: 3px;
}
.modal-footer {
padding: 16px 24px;
display: flex;
justify-content: flex-end;
gap: 12px;
border-top: 1px solid var(--md-outline-variant);
background-color: var(--md-surface-container-high);
}
#save-item-btn {
background-color: var(--md-success);
color: var(--md-on-success);
}
#save-item-btn:hover {
box-shadow: var(--md-elevation-1);
filter: brightness(1.1);
}
#cancel-edit-btn {
background-color: var(--md-secondary);
color: var(--md-on-secondary);
}
#cancel-edit-btn:hover {
box-shadow: var(--md-elevation-1);
}
/* Form elements */
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: var(--md-on-surface);
font-size: 14px;
}
.form-control {
width: 100%;
padding: 12px;
border: 1px solid var(--md-outline-variant);
border-radius: var(--md-radius-small);
background-color: var(--md-surface-container-low);
color: var(--md-on-surface);
transition: border-color 0.2s;
scrollbar-width: thin;
scrollbar-color: var(--md-outline-variant) transparent;
}
.form-control:focus {
border-color: var(--md-primary);
outline: none;
}
textarea.form-control {
min-height: 100px;
font-family: monospace;
}
.form-control::-webkit-scrollbar {
width: 6px;
}
.form-control::-webkit-scrollbar-thumb {
background-color: var(--md-outline-variant);
border-radius: 3px;
}
.complex-object {
border: 1px solid var(--md-outline-variant);
border-radius: var(--md-radius-small);
padding: 12px;
margin-top: 8px;
background-color: var(--md-surface-container);
}
.complex-object-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.complex-object-title {
font-weight: 500;
color: var(--md-primary);
}
.add-item-btn {
background-color: var(--md-primary-container);
color: var(--md-on-primary-container);
padding: 4px 8px;
font-size: 12px;
border-radius: var(--md-radius-small);
}
.add-item-btn:hover {
filter: brightness(1.1);
}
/* For handling arrays */
.array-item {
border: 1px solid var(--md-outline-variant);
border-radius: var(--md-radius-small);
padding: 12px;
margin-bottom: 12px;
position: relative;
background-color: var(--md-surface-container-low);
}
.remove-item-btn {
position: absolute;
top: 8px;
right: 8px;
background-color: var(--md-error-container);
color: var(--md-on-error-container);
width: 24px;
height: 24px;
font-size: 12px;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.2s;
}
.remove-item-btn:hover {
filter: brightness(1.1);
}
/* Improved checkbox styling */
input[type="checkbox"] {
/* Remove default styling */
appearance: none;
-webkit-appearance: none;
/* Create custom checkbox */
width: 20px;
height: 20px;
border: 2px solid var(--md-outline-variant);
border-radius: 4px;
background-color: var(--md-surface-container-low);
position: relative;
cursor: pointer;
vertical-align: middle;
margin-right: 8px;
transition: all 0.2s;
}
/* Checked state */
input[type="checkbox"]:checked {
background-color: var(--md-primary);
border-color: var(--md-primary);
}
/* Checkmark */
input[type="checkbox"]:checked::after {
content: "✓";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: var(--md-on-primary);
font-size: 14px;
opacity: 0;
animation: fadeIn 0.2s forwards;
}
/* Hover state */
input[type="checkbox"]:hover {
border-color: var(--md-primary);
}
input[type="checkbox"]:active {
transform: scale(0.9);
}
/* Add this to the form-group for boolean/checkbox fields */
.form-group.checkbox-group {
display: flex;
align-items: center;
}
.form-group.checkbox-group label {
margin-bottom: 0;
margin-left: 8px;
order: 2;
}
.form-group.checkbox-group input {
order: 1;
}
@keyframes modal-appear {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes modal-disappear {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.9);
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translate(-50%, -60%);
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}
+114 -15
View File
@@ -1,22 +1,121 @@
local Translations = {
error = {
not_online = 'Player online nist !',
wrong_format = 'Vorodi sahih nist !',
missing_args = 'Vorodi naghes ast (x, y, z)',
missing_args2 = 'Tamam vorodi hara vared konid !',
no_access = 'Shoma dastresi nadarid !',
company_too_poor = 'Sherkat shoma, pul kafi baraye hoghogh dadan nadarad !',
item_not_exist = 'In item vojod nadarad',
too_heavy = 'Inventory kheyli sangin ast !'
not_online = 'بازیکن آنلاین نیست',
wrong_format = 'فرمت نادرست',
missing_args = 'همه اطلاعات وارد نشده است (x, y, z)',
missing_args2 = 'باید همه فیلدهای لازم پر شوند!',
no_access = 'شما به این دستور دسترسی ندارید',
company_too_poor = 'کارفرمای شما پول کافی ندارد',
item_not_exist = 'آیتم وجود ندارد',
too_heavy = 'فضای کافی در کیف شما نیست',
location_not_exist = 'مکان وجود ندارد',
duplicate_license = 'لایسنس راکستار تکراری یافت شد',
no_valid_license = 'لایسنس راکستار معتبر یافت نشد',
not_whitelisted = 'شما در این سرور لیست سفید نیستید',
server_already_open = 'سرور قبلاً باز است',
server_already_closed = 'سرور قبلاً بسته است',
no_permission = 'شما اجازه این کار را ندارید...',
no_waypoint = 'هیچ نقطه‌ای تنظیم نشده است.',
tp_error = 'خطا در هنگام انتقال.',
},
success = {
server_opened = 'سرور باز شد',
server_closed = 'سرور بسته شد',
teleported_waypoint = 'به نقطه مشخص شده منتقل شدید.',
},
success = {},
info = {
received_paycheck = 'Hoghogh shoma variz shod : $%{value}',
job_info = 'Shoghl: %{value} | Daraje: %{value2} | Dar hal kar: %{value3}',
gang_info = 'Gang: %{value} | Daraje: %{value2}',
on_duty = 'Shoma dar hal kar hastid (on-duty)!',
off_duty = 'Shoma az kar kharej shodid (off-duty)!'
}
received_paycheck = 'شما حقوق خود را به مبلغ $%{value} دریافت کردید',
job_info = 'شغل: %{value} | سطح: %{value2} | در حال کار: %{value3}',
gang_info = 'گروه: %{value} | سطح: %{value2}',
on_duty = 'شما اکنون در حال کار هستید!',
off_duty = 'شما اکنون خارج از کار هستید!',
checking_ban = 'سلام %s. در حال بررسی وضعیت ممنوعیت شما هستیم.',
join_server = 'خوش آمدید %s به {نام سرور}.',
checking_whitelisted = 'سلام %s. در حال بررسی وضعیت مجاز بودن شما هستیم.',
exploit_banned = 'شما به دلیل تقلب ممنوع شده‌اید. برای اطلاعات بیشتر به دیسکورد ما مراجعه کنید: %{discord}',
exploit_dropped = 'شما به دلیل تقلب از سرور خارج شدید',
},
command = {
tp = {
help = 'انتقال به بازیکن یا مختصات (فقط ادمین)',
params = {
x = { name = 'id/x', help = 'شناسه بازیکن یا مختصات X' },
y = { name = 'y', help = 'مختصات Y' },
z = { name = 'z', help = 'مختصات Z' },
},
},
tpm = { help = 'انتقال به نقطه مشخص شده (فقط ادمین)' },
togglepvp = { help = 'تغییر وضعیت پی وی پی (فقط ادمین)' },
addpermission = {
help = 'ادمینی به بازیکن',
params = {
id = { name = 'id', help = 'شناسه بازیکن' },
permission = { name = 'permission', help = 'سطح ادمینی' },
},
},
removepermission = {
help = 'حذف ادمینی از بازیکن',
params = {
id = { name = 'id', help = 'شناسه بازیکن' },
permission = { name = 'permission', help = 'سطح ادمینی' },
},
},
openserver = { help = 'باز کردن سرور برای همه (فقط ادمین)' },
closeserver = {
help = 'بستن سرور برای کسانی که ادمینی ندارند (فقط ادمین)',
params = {
reason = { name = 'reason', help = 'دلیل بستن (اختیاری)' },
},
},
car = {
help = 'اسپاون کردن خودرو (فقط ادمین)',
params = {
model = { name = 'model', help = 'نام مدل خودرو' },
},
},
dv = { help = 'حذف خودرو (فقط ادمین)' },
givemoney = {
help = 'دادن پول به بازیکن (فقط ادمین)',
params = {
id = { name = 'id', help = 'شناسه بازیکن' },
moneytype = { name = 'moneytype', help = 'نوع پول (نقدی، بانکی، کریپتو)' },
amount = { name = 'amount', help = 'مقدار پول' },
},
},
setmoney = {
help = 'تنظیم مقدار پول بازیکن (فقط ادمین)',
params = {
id = { name = 'id', help = 'شناسه بازیکن' },
moneytype = { name = 'moneytype', help = 'نوع پول (نقدی، بانکی، کریپتو)' },
amount = { name = 'amount', help = 'مقدار پول' },
},
},
job = { help = 'بررسی شغل' },
setjob = {
help = 'تنظیم شغل بازیکن (فقط ادمین)',
params = {
id = { name = 'id', help = 'شناسه بازیکن' },
job = { name = 'job', help = 'نام شغل' },
grade = { name = 'grade', help = 'سطح شغل' },
},
},
gang = { help = 'بررسی گنگ' },
setgang = {
help = 'تنظیم گنگ بازیکن (فقط ادمین)',
params = {
id = { name = 'id', help = 'شناسه بازیکن' },
gang = { name = 'gang', help = 'نام گنگ' },
grade = { name = 'grade', help = 'سطح گنگ' },
},
},
ooc = { help = 'پیام خارج از آر پی' },
me = {
help = 'ارسال پیام محلی',
params = {
message = { name = 'message', help = 'پیام' }
},
},
},
}
if GetConvar('qb_locale', 'en') == 'fa' then
+5 -5
View File
@@ -23,7 +23,7 @@ local Translations = {
success = {
server_opened = 'O Servidor abriu',
server_closed = 'O Servidor fechou',
teleported_waypoint = 'Teleportado para o waypoint.',
teleported_waypoint = 'Foste teletransportado para o waypoint.',
},
info = {
received_paycheck = 'Recebeste o pagamento de %{value}€',
@@ -34,12 +34,12 @@ local Translations = {
checking_ban = 'Olá %s. Estamos a verificar se estás banido.',
join_server = 'Bem vindo %s ao {Server Name}.',
checking_whitelisted = 'Bem vindo %s. Estamos a verificiar se estás na whitelist.',
exploit_banned = 'Foste banidos por cheats. Para mais informações visita o nosso discord: %{discord}',
exploit_dropped = 'Foste kickado por cheats!',
exploit_banned = 'Foste banido por cheats. Para mais informações visita o nosso discord: %{discord}',
exploit_dropped = 'Foste expulso por cheats!',
},
command = {
tp = {
help = 'TP para jogador ou coordenadas (Apenas Admin)',
help = 'TP para um jogador ou coordenadas (Apenas Admin)',
params = {
x = { name = 'id/x', help = 'ID do jogador ou posição X'},
y = { name = 'y', help = 'Posição Y'},
@@ -64,7 +64,7 @@ local Translations = {
},
openserver = { help = 'Abrir o Servidor para todos (Apenas Admin)' },
closeserver = {
help = 'Fechar o servidor para todos excepto Admins (Apenas Admin)',
help = 'Fechar o servidor para todos exceto Admins (Apenas Admin)',
params = {
reason = { name = 'reason', help = 'Razão para fechar(opcional)' },
},
+256 -18
View File
@@ -1,15 +1,11 @@
-- Handle Chat Input
QBCore.Commands = {}
QBCore.Commands.List = {}
QBCore.Commands.IgnoreList = { -- Ignore old perm levels while keeping backwards compatibility
['god'] = true, -- We don't need to create an ace because god is allowed all commands
['user'] = true -- We don't need to create an ace because builtin.everyone
}
AddEventHandler('chatMessage', function(_, _, message)
if string.sub(message, 1, 1) == '/' then
CancelEvent()
return
end
end)
-- Add Aces
CreateThread(function()
CreateThread(function() -- Add ace to node for perm checking
local permissions = QBCore.Config.Server.Permissions
for i = 1, #permissions do
local permission = permissions[i]
@@ -20,11 +16,11 @@ end)
-- Register & Refresh Commands
function QBCore.Commands.Add(name, help, arguments, argsrequired, callback, permission, ...)
local restricted = true
if not permission then permission = 'user' end
if permission == 'user' then restricted = false end
local restricted = true -- Default to restricted for all commands
if not permission then permission = 'user' end -- some commands don't pass permission level
if permission == 'user' then restricted = false end -- allow all users to use command
RegisterCommand(name, function(source, args, rawCommand)
RegisterCommand(name, function(source, args, rawCommand) -- Register command within fivem
if argsrequired and #args < #arguments then
return TriggerClientEvent('chat:addMessage', source, {
color = { 255, 0, 0 },
@@ -37,18 +33,18 @@ function QBCore.Commands.Add(name, help, arguments, argsrequired, callback, perm
local extraPerms = ... and table.pack(...) or nil
if extraPerms then
extraPerms[extraPerms.n + 1] = permission
extraPerms[extraPerms.n + 1] = permission -- The `n` field is the number of arguments in the packed table
extraPerms.n += 1
permission = extraPerms
for i = 1, permission.n do
if not QBCore.Commands.IgnoreList[permission[i]] then
if not QBCore.Commands.IgnoreList[permission[i]] then -- only create aces for extra perm levels
ExecuteCommand(('add_ace qbcore.%s command.%s allow'):format(permission[i], name))
end
end
permission.n = nil
else
permission = tostring(permission:lower())
if not QBCore.Commands.IgnoreList[permission] then
if not QBCore.Commands.IgnoreList[permission] then -- only create aces for extra perm levels
ExecuteCommand(('add_ace qbcore.%s command.%s allow'):format(permission, name))
end
end
@@ -83,3 +79,245 @@ function QBCore.Commands.Refresh(source)
TriggerClientEvent('chat:addSuggestions', src, suggestions)
end
end
-- Teleport
QBCore.Commands.Add('tp', Lang:t('command.tp.help'), { { name = Lang:t('command.tp.params.x.name'), help = Lang:t('command.tp.params.x.help') }, { name = Lang:t('command.tp.params.y.name'), help = Lang:t('command.tp.params.y.help') }, { name = Lang:t('command.tp.params.z.name'), help = Lang:t('command.tp.params.z.help') } }, false, function(source, args)
if args[1] and not args[2] and not args[3] then
if tonumber(args[1]) then
local target = GetPlayerPed(tonumber(args[1]))
if target ~= 0 then
local coords = GetEntityCoords(target)
TriggerClientEvent('QBCore:Command:TeleportToPlayer', source, coords)
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
else
local location = QBShared.Locations[args[1]]
if location then
TriggerClientEvent('QBCore:Command:TeleportToCoords', source, location.x, location.y, location.z, location.w)
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.location_not_exist'), 'error')
end
end
else
if args[1] and args[2] and args[3] then
local x = tonumber((args[1]:gsub(',', ''))) + .0
local y = tonumber((args[2]:gsub(',', ''))) + .0
local z = tonumber((args[3]:gsub(',', ''))) + .0
local heading = args[4] and tonumber((args[4]:gsub(',', ''))) + .0 or false
if x ~= 0 and y ~= 0 and z ~= 0 then
TriggerClientEvent('QBCore:Command:TeleportToCoords', source, x, y, z, heading)
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.wrong_format'), 'error')
end
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.missing_args'), 'error')
end
end
end, 'admin')
QBCore.Commands.Add('tpm', Lang:t('command.tpm.help'), {}, false, function(source)
TriggerClientEvent('QBCore:Command:GoToMarker', source)
end, 'admin')
QBCore.Commands.Add('togglepvp', Lang:t('command.togglepvp.help'), {}, false, function()
QBCore.Config.Server.PVP = not QBCore.Config.Server.PVP
TriggerClientEvent('QBCore:Client:PvpHasToggled', -1, QBCore.Config.Server.PVP)
end, 'admin')
-- Permissions
QBCore.Commands.Add('addpermission', Lang:t('command.addpermission.help'), { { name = Lang:t('command.addpermission.params.id.name'), help = Lang:t('command.addpermission.params.id.help') }, { name = Lang:t('command.addpermission.params.permission.name'), help = Lang:t('command.addpermission.params.permission.help') } }, true, function(source, args)
local Player = QBCore.Functions.GetPlayer(tonumber(args[1]))
local permission = tostring(args[2]):lower()
if Player then
QBCore.Functions.AddPermission(Player.PlayerData.source, permission)
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
end, 'god')
QBCore.Commands.Add('removepermission', Lang:t('command.removepermission.help'), { { name = Lang:t('command.removepermission.params.id.name'), help = Lang:t('command.removepermission.params.id.help') }, { name = Lang:t('command.removepermission.params.permission.name'), help = Lang:t('command.removepermission.params.permission.help') } }, true, function(source, args)
local Player = QBCore.Functions.GetPlayer(tonumber(args[1]))
local permission = tostring(args[2]):lower()
if Player then
QBCore.Functions.RemovePermission(Player.PlayerData.source, permission)
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
end, 'god')
-- Open & Close Server
QBCore.Commands.Add('openserver', Lang:t('command.openserver.help'), {}, false, function(source)
if not QBCore.Config.Server.Closed then
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.server_already_open'), 'error')
return
end
if QBCore.Functions.HasPermission(source, 'admin') then
QBCore.Config.Server.Closed = false
TriggerClientEvent('QBCore:Notify', source, Lang:t('success.server_opened'), 'success')
else
QBCore.Functions.Kick(source, Lang:t('error.no_permission'), nil, nil)
end
end, 'admin')
QBCore.Commands.Add('closeserver', Lang:t('command.closeserver.help'), { { name = Lang:t('command.closeserver.params.reason.name'), help = Lang:t('command.closeserver.params.reason.help') } }, false, function(source, args)
if QBCore.Config.Server.Closed then
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.server_already_closed'), 'error')
return
end
if QBCore.Functions.HasPermission(source, 'admin') then
local reason = args[1] or 'No reason specified'
QBCore.Config.Server.Closed = true
QBCore.Config.Server.ClosedReason = reason
for k in pairs(QBCore.Players) do
if not QBCore.Functions.HasPermission(k, QBCore.Config.Server.WhitelistPermission) then
QBCore.Functions.Kick(k, reason, nil, nil)
end
end
TriggerClientEvent('QBCore:Notify', source, Lang:t('success.server_closed'), 'success')
else
QBCore.Functions.Kick(source, Lang:t('error.no_permission'), nil, nil)
end
end, 'admin')
-- Vehicle
QBCore.Commands.Add('car', Lang:t('command.car.help'), { { name = Lang:t('command.car.params.model.name'), help = Lang:t('command.car.params.model.help') } }, true, function(source, args)
TriggerClientEvent('QBCore:Command:SpawnVehicle', source, args[1])
end, 'admin')
QBCore.Commands.Add('dv', Lang:t('command.dv.help'), {}, false, function(source)
TriggerClientEvent('QBCore:Command:DeleteVehicle', source)
end, 'admin')
QBCore.Commands.Add('dvall', Lang:t('command.dvall.help'), {}, false, function()
local vehicles = GetAllVehicles()
for _, vehicle in ipairs(vehicles) do
DeleteEntity(vehicle)
end
end, 'admin')
-- Peds
QBCore.Commands.Add('dvp', Lang:t('command.dvp.help'), {}, false, function()
local peds = GetAllPeds()
for _, ped in ipairs(peds) do
DeleteEntity(ped)
end
end, 'admin')
-- Objects
QBCore.Commands.Add('dvo', Lang:t('command.dvo.help'), {}, false, function()
local objects = GetAllObjects()
for _, object in ipairs(objects) do
DeleteEntity(object)
end
end, 'admin')
-- Money
QBCore.Commands.Add('givemoney', Lang:t('command.givemoney.help'), { { name = Lang:t('command.givemoney.params.id.name'), help = Lang:t('command.givemoney.params.id.help') }, { name = Lang:t('command.givemoney.params.moneytype.name'), help = Lang:t('command.givemoney.params.moneytype.help') }, { name = Lang:t('command.givemoney.params.amount.name'), help = Lang:t('command.givemoney.params.amount.help') } }, true, function(source, args)
local Player = QBCore.Functions.GetPlayer(tonumber(args[1]))
if Player then
Player.Functions.AddMoney(tostring(args[2]), tonumber(args[3]), 'Admin give money')
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
end, 'admin')
QBCore.Commands.Add('setmoney', Lang:t('command.setmoney.help'), { { name = Lang:t('command.setmoney.params.id.name'), help = Lang:t('command.setmoney.params.id.help') }, { name = Lang:t('command.setmoney.params.moneytype.name'), help = Lang:t('command.setmoney.params.moneytype.help') }, { name = Lang:t('command.setmoney.params.amount.name'), help = Lang:t('command.setmoney.params.amount.help') } }, true, function(source, args)
local Player = QBCore.Functions.GetPlayer(tonumber(args[1]))
if Player then
Player.Functions.SetMoney(tostring(args[2]), tonumber(args[3]))
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
end, 'admin')
-- Job
QBCore.Commands.Add('job', Lang:t('command.job.help'), {}, false, function(source)
local PlayerJob = QBCore.Functions.GetPlayer(source).PlayerData.job
TriggerClientEvent('QBCore:Notify', source, Lang:t('info.job_info', { value = PlayerJob.label, value2 = PlayerJob.grade.name, value3 = PlayerJob.onduty }))
end, 'user')
QBCore.Commands.Add('setjob', Lang:t('command.setjob.help'), { { name = Lang:t('command.setjob.params.id.name'), help = Lang:t('command.setjob.params.id.help') }, { name = Lang:t('command.setjob.params.job.name'), help = Lang:t('command.setjob.params.job.help') }, { name = Lang:t('command.setjob.params.grade.name'), help = Lang:t('command.setjob.params.grade.help') } }, true, function(source, args)
local Player = QBCore.Functions.GetPlayer(tonumber(args[1]))
if Player then
Player.Functions.SetJob(tostring(args[2]), tonumber(args[3]))
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
end, 'admin')
-- Gang
QBCore.Commands.Add('gang', Lang:t('command.gang.help'), {}, false, function(source)
local PlayerGang = QBCore.Functions.GetPlayer(source).PlayerData.gang
TriggerClientEvent('QBCore:Notify', source, Lang:t('info.gang_info', { value = PlayerGang.label, value2 = PlayerGang.grade.name }))
end, 'user')
QBCore.Commands.Add('setgang', Lang:t('command.setgang.help'), { { name = Lang:t('command.setgang.params.id.name'), help = Lang:t('command.setgang.params.id.help') }, { name = Lang:t('command.setgang.params.gang.name'), help = Lang:t('command.setgang.params.gang.help') }, { name = Lang:t('command.setgang.params.grade.name'), help = Lang:t('command.setgang.params.grade.help') } }, true, function(source, args)
local Player = QBCore.Functions.GetPlayer(tonumber(args[1]))
if Player then
Player.Functions.SetGang(tostring(args[2]), tonumber(args[3]))
else
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.not_online'), 'error')
end
end, 'admin')
-- Out of Character Chat
QBCore.Commands.Add('ooc', Lang:t('command.ooc.help'), {}, false, function(source, args)
local message = table.concat(args, ' ')
local Players = QBCore.Functions.GetPlayers()
local Player = QBCore.Functions.GetPlayer(source)
local playerCoords = GetEntityCoords(GetPlayerPed(source))
for _, v in pairs(Players) do
if v == source then
TriggerClientEvent('chat:addMessage', v, {
color = QBCore.Config.Commands.OOCColor,
multiline = true,
args = { 'OOC | ' .. GetPlayerName(source), message }
})
elseif #(playerCoords - GetEntityCoords(GetPlayerPed(v))) < 20.0 then
TriggerClientEvent('chat:addMessage', v, {
color = QBCore.Config.Commands.OOCColor,
multiline = true,
args = { 'OOC | ' .. GetPlayerName(source), message }
})
elseif QBCore.Functions.HasPermission(v, 'admin') then
if QBCore.Functions.IsOptin(v) then
TriggerClientEvent('chat:addMessage', v, {
color = QBCore.Config.Commands.OOCColor,
multiline = true,
args = { 'Proximity OOC | ' .. GetPlayerName(source), message }
})
TriggerEvent('qb-log:server:CreateLog', 'ooc', 'OOC', 'white', '**' .. GetPlayerName(source) .. '** (CitizenID: ' .. Player.PlayerData.citizenid .. ' | ID: ' .. source .. ') **Message:** ' .. message, false)
end
end
end
end, 'user')
-- Me command
QBCore.Commands.Add('me', Lang:t('command.me.help'), { { name = Lang:t('command.me.params.message.name'), help = Lang:t('command.me.params.message.help') } }, false, function(source, args)
if #args < 1 then
TriggerClientEvent('QBCore:Notify', source, Lang:t('error.missing_args2'), 'error')
return
end
local ped = GetPlayerPed(source)
local pCoords = GetEntityCoords(ped)
local msg = table.concat(args, ' '):gsub('[~<].-[>~]', '')
local Players = QBCore.Functions.GetPlayers()
for i = 1, #Players do
local Player = Players[i]
local target = GetPlayerPed(Player)
local tCoords = GetEntityCoords(target)
if target == ped or #(pCoords - tCoords) < 20 then
TriggerClientEvent('QBCore:Command:ShowMe3D', Player, source, msg)
end
end
end, 'user')
+44
View File
@@ -0,0 +1,44 @@
local function tPrint(tbl, indent)
indent = indent or 0
if type(tbl) == 'table' then
for k, v in pairs(tbl) do
local tblType = type(v)
local formatting = ('%s ^3%s:^0'):format(string.rep(' ', indent), k)
if tblType == 'table' then
print(formatting)
tPrint(v, indent + 1)
elseif tblType == 'boolean' then
print(('%s^1 %s ^0'):format(formatting, v))
elseif tblType == 'function' then
print(('%s^9 %s ^0'):format(formatting, v))
elseif tblType == 'number' then
print(('%s^5 %s ^0'):format(formatting, v))
elseif tblType == 'string' then
print(("%s ^2'%s' ^0"):format(formatting, v))
else
print(('%s^2 %s ^0'):format(formatting, v))
end
end
else
print(('%s ^0%s'):format(string.rep(' ', indent), tbl))
end
end
RegisterServerEvent('QBCore:DebugSomething', function(tbl, indent, resource)
print(('\x1b[4m\x1b[36m[ %s : DEBUG]\x1b[0m'):format(resource))
tPrint(tbl, indent)
print('\x1b[4m\x1b[36m[ END DEBUG ]\x1b[0m')
end)
function QBCore.Debug(tbl, indent)
TriggerEvent('QBCore:DebugSomething', tbl, indent, GetInvokingResource() or 'qb-core')
end
function QBCore.ShowError(resource, msg)
print('\x1b[31m[' .. resource .. ':ERROR]\x1b[0m ' .. msg)
end
function QBCore.ShowSuccess(resource, msg)
print('\x1b[32m[' .. resource .. ':LOG]\x1b[0m ' .. msg)
end
-56
View File
@@ -1,56 +0,0 @@
RegisterCommand('qbeditor', function(source)
local files = {
config = LoadResourceFile('qb-core', 'shared/json/config.json'),
gangs = LoadResourceFile('qb-core', 'shared/json/gangs.json'),
items = LoadResourceFile('qb-core', 'shared/json/items.json'),
jobs = LoadResourceFile('qb-core', 'shared/json/jobs.json'),
playerdata = LoadResourceFile('qb-core', 'shared/json/player_defaults.json'),
vehicles = LoadResourceFile('qb-core', 'shared/json/vehicles.json'),
weapons = LoadResourceFile('qb-core', 'shared/json/weapons.json'),
}
local allData = {}
for key, fileContent in pairs(files) do
if not fileContent then
print('^1[QBCore] Failed to load ' .. key .. ' JSON File')
else
allData[key] = json.decode(fileContent)
end
end
TriggerClientEvent('qb-core:client:configEditor', source, allData)
end, true)
RegisterNetEvent('qb-core:server:configEditor', function(fileName, fileData)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
if not QBCore.Functions.HasPermission('god') then
local filePaths = {
['config'] = 'shared/json/config.json',
['gangs'] = 'shared/json/gangs.json',
['items'] = 'shared/json/items.json',
['jobs'] = 'shared/json/jobs.json',
['playerdata'] = 'shared/json/player_defaults.json',
['vehicles'] = 'shared/json/vehicles.json',
['weapons'] = 'shared/json/weapons.json'
}
local filePath = filePaths[fileName]
if not filePath then
print('^1[QBCore] Invalid file name: ' .. fileName)
return
end
local encodedData = json.encode(fileData, { indent = true })
local saved = SaveResourceFile('qb-core', filePath, encodedData, -1)
if saved then
print('^2[QBCore] Player ' .. GetPlayerName(src) .. ' (ID: ' .. src .. ') successfully saved ' .. fileName .. ' configuration')
TriggerClientEvent('QBCore:Notify', src, 'Configuration saved successfully', 'success')
else
print('^1[QBCore] Failed to save ' .. fileName .. ' configuration')
TriggerClientEvent('QBCore:Notify', src, 'Failed to save configuration', 'error')
end
else
print('^1[QBCore] Player ' .. GetPlayerName(src) .. ' (ID: ' .. src .. ') attempted to save ' .. fileName .. ' configuration without permission')
TriggerClientEvent('QBCore:Notify', src, 'You don\'t have permission to edit configs', 'error')
end
end)
+285
View File
@@ -0,0 +1,285 @@
-- Event Handler
AddEventHandler('chatMessage', function(_, _, message)
if string.sub(message, 1, 1) == '/' then
CancelEvent()
return
end
end)
AddEventHandler('playerDropped', function(reason)
local src = source
if not QBCore.Players[src] then return end
local Player = QBCore.Players[src]
TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Dropped', 'red', '**' .. GetPlayerName(src) .. '** (' .. Player.PlayerData.license .. ') left..' .. '\n **Reason:** ' .. reason)
TriggerEvent('QBCore:Server:PlayerDropped', Player)
Player.Functions.Save()
QBCore.Player_Buckets[Player.PlayerData.license] = nil
QBCore.Players[src] = nil
end)
AddEventHandler("onResourceStop", function(resName)
for i,v in pairs(QBCore.UsableItems) do
if v.resource == resName then
QBCore.UsableItems[i] = nil
end
end
end)
-- Player Connecting
local readyFunction = MySQL.ready
local databaseConnected, bansTableExists = readyFunction == nil, readyFunction == nil
if readyFunction ~= nil then
MySQL.ready(function()
databaseConnected = true
local DatabaseInfo = QBCore.Functions.GetDatabaseInfo()
if not DatabaseInfo or not DatabaseInfo.exists then return end
local result = MySQL.query.await('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = "bans";', {DatabaseInfo.database})
if result and result[1] then
bansTableExists = true
end
end)
end
local function onPlayerConnecting(name, _, deferrals)
local src = source
deferrals.defer()
if QBCore.Config.Server.Closed and not IsPlayerAceAllowed(src, 'qbadmin.join') then
return deferrals.done(QBCore.Config.Server.ClosedReason)
end
if not databaseConnected then
return deferrals.done(Lang:t('error.connecting_database_error'))
end
if QBCore.Config.Server.Whitelist then
Wait(0)
deferrals.update(string.format(Lang:t('info.checking_whitelisted'), name))
if not QBCore.Functions.IsWhitelisted(src) then
return deferrals.done(Lang:t('error.not_whitelisted'))
end
end
Wait(0)
deferrals.update(string.format('Hello %s. Your license is being checked', name))
local license = QBCore.Functions.GetIdentifier(src, 'license')
if not license then
return deferrals.done(Lang:t('error.no_valid_license'))
elseif QBCore.Config.Server.CheckDuplicateLicense and QBCore.Functions.IsLicenseInUse(license) then
return deferrals.done(Lang:t('error.duplicate_license'))
end
Wait(0)
deferrals.update(string.format(Lang:t('info.checking_ban'), name))
if not bansTableExists then
return deferrals.done(Lang:t('error.ban_table_not_found'))
end
local success, isBanned, reason = pcall(QBCore.Functions.IsPlayerBanned, src)
if not success then return deferrals.done(Lang:t('error.connecting_database_error')) end
if isBanned then return deferrals.done(reason) end
Wait(0)
deferrals.update(string.format(Lang:t('info.join_server'), name))
deferrals.done()
TriggerClientEvent('QBCore:Client:SharedUpdate', src, QBCore.Shared)
end
AddEventHandler('playerConnecting', onPlayerConnecting)
-- Open & Close Server (prevents players from joining)
RegisterNetEvent('QBCore:Server:CloseServer', function(reason)
local src = source
if QBCore.Functions.HasPermission(src, 'admin') then
reason = reason or 'No reason specified'
QBCore.Config.Server.Closed = true
QBCore.Config.Server.ClosedReason = reason
for k in pairs(QBCore.Players) do
if not QBCore.Functions.HasPermission(k, QBCore.Config.Server.WhitelistPermission) then
QBCore.Functions.Kick(k, reason, nil, nil)
end
end
else
QBCore.Functions.Kick(src, Lang:t('error.no_permission'), nil, nil)
end
end)
RegisterNetEvent('QBCore:Server:OpenServer', function()
local src = source
if QBCore.Functions.HasPermission(src, 'admin') then
QBCore.Config.Server.Closed = false
else
QBCore.Functions.Kick(src, Lang:t('error.no_permission'), nil, nil)
end
end)
-- Callback Events --
-- Client Callback
RegisterNetEvent('QBCore:Server:TriggerClientCallback', function(name, ...)
if QBCore.ClientCallbacks[name] then
QBCore.ClientCallbacks[name].promise:resolve(...)
if QBCore.ClientCallbacks[name].callback then
QBCore.ClientCallbacks[name].callback(...)
end
QBCore.ClientCallbacks[name] = nil
end
end)
-- Server Callback
RegisterNetEvent('QBCore:Server:TriggerCallback', function(name, ...)
if not QBCore.ServerCallbacks[name] then return end
local src = source
QBCore.ServerCallbacks[name](src, function(...)
TriggerClientEvent('QBCore:Client:TriggerCallback', src, name, ...)
end, ...)
end)
-- Player
RegisterNetEvent('QBCore:UpdatePlayer', function()
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local newHunger = Player.PlayerData.metadata['hunger'] - QBCore.Config.Player.HungerRate
local newThirst = Player.PlayerData.metadata['thirst'] - QBCore.Config.Player.ThirstRate
if newHunger <= 0 then
newHunger = 0
end
if newThirst <= 0 then
newThirst = 0
end
Player.Functions.SetMetaData('thirst', newThirst)
Player.Functions.SetMetaData('hunger', newHunger)
TriggerClientEvent('hud:client:UpdateNeeds', src, newHunger, newThirst)
Player.Functions.Save()
end)
RegisterNetEvent('QBCore:ToggleDuty', function()
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
if Player.PlayerData.job.onduty then
Player.Functions.SetJobDuty(false)
TriggerClientEvent('QBCore:Notify', src, Lang:t('info.off_duty'))
else
Player.Functions.SetJobDuty(true)
TriggerClientEvent('QBCore:Notify', src, Lang:t('info.on_duty'))
end
TriggerEvent('QBCore:Server:SetDuty', src, Player.PlayerData.job.onduty)
TriggerClientEvent('QBCore:Client:SetDuty', src, Player.PlayerData.job.onduty)
end)
-- BaseEvents
-- Vehicles
RegisterServerEvent('baseevents:enteringVehicle', function(veh, seat, modelName)
local src = source
local data = {
vehicle = veh,
seat = seat,
name = modelName,
event = 'Entering'
}
TriggerClientEvent('QBCore:Client:VehicleInfo', src, data)
end)
RegisterServerEvent('baseevents:enteredVehicle', function(veh, seat, modelName)
local src = source
local data = {
vehicle = veh,
seat = seat,
name = modelName,
event = 'Entered'
}
TriggerClientEvent('QBCore:Client:VehicleInfo', src, data)
end)
RegisterServerEvent('baseevents:enteringAborted', function()
local src = source
TriggerClientEvent('QBCore:Client:AbortVehicleEntering', src)
end)
RegisterServerEvent('baseevents:leftVehicle', function(veh, seat, modelName)
local src = source
local data = {
vehicle = veh,
seat = seat,
name = modelName,
event = 'Left'
}
TriggerClientEvent('QBCore:Client:VehicleInfo', src, data)
end)
-- Items
-- This event is exploitable and should not be used. It has been deprecated, and will be removed soon.
RegisterNetEvent('QBCore:Server:UseItem', function(item)
print(string.format('%s triggered QBCore:Server:UseItem by ID %s with the following data. This event is deprecated due to exploitation, and will be removed soon. Check qb-inventory for the right use on this event.', GetInvokingResource(), source))
QBCore.Debug(item)
end)
-- This event is exploitable and should not be used. It has been deprecated, and will be removed soon. function(itemName, amount, slot)
RegisterNetEvent('QBCore:Server:RemoveItem', function(itemName, amount)
local src = source
print(string.format('%s triggered QBCore:Server:RemoveItem by ID %s for %s %s. This event is deprecated due to exploitation, and will be removed soon. Adjust your events accordingly to do this server side with player functions.', GetInvokingResource(), src, amount, itemName))
end)
-- This event is exploitable and should not be used. It has been deprecated, and will be removed soon. function(itemName, amount, slot, info)
RegisterNetEvent('QBCore:Server:AddItem', function(itemName, amount)
local src = source
print(string.format('%s triggered QBCore:Server:AddItem by ID %s for %s %s. This event is deprecated due to exploitation, and will be removed soon. Adjust your events accordingly to do this server side with player functions.', GetInvokingResource(), src, amount, itemName))
end)
-- Non-Chat Command Calling (ex: qb-adminmenu)
RegisterNetEvent('QBCore:CallCommand', function(command, args)
local src = source
if not QBCore.Commands.List[command] then return end
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local hasPerm = QBCore.Functions.HasPermission(src, 'command.' .. QBCore.Commands.List[command].name)
if hasPerm then
if QBCore.Commands.List[command].argsrequired and #QBCore.Commands.List[command].arguments ~= 0 and not args[#QBCore.Commands.List[command].arguments] then
TriggerClientEvent('QBCore:Notify', src, Lang:t('error.missing_args2'), 'error')
else
QBCore.Commands.List[command].callback(src, args)
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t('error.no_access'), 'error')
end
end)
-- Use this for player vehicle spawning
-- Vehicle server-side spawning callback (netId)
-- use the netid on the client with the NetworkGetEntityFromNetworkId native
-- convert it to a vehicle via the NetToVeh native
QBCore.Functions.CreateCallback('QBCore:Server:SpawnVehicle', function(source, cb, model, coords, warp)
local veh = QBCore.Functions.SpawnVehicle(source, model, coords, warp)
cb(NetworkGetNetworkIdFromEntity(veh))
end)
-- Use this for long distance vehicle spawning
-- vehicle server-side spawning callback (netId)
-- use the netid on the client with the NetworkGetEntityFromNetworkId native
-- convert it to a vehicle via the NetToVeh native
QBCore.Functions.CreateCallback('QBCore:Server:CreateVehicle', function(source, cb, model, coords, warp)
local veh = QBCore.Functions.CreateAutomobile(source, model, coords, warp)
cb(NetworkGetNetworkIdFromEntity(veh))
end)
--QBCore.Functions.CreateCallback('QBCore:HasItem', function(source, cb, items, amount)
-- https://github.com/qbcore-framework/qb-inventory/blob/e4ef156d93dd1727234d388c3f25110c350b3bcf/server/main.lua#L2066
--end)
+336
View File
@@ -0,0 +1,336 @@
-- Add or change (a) method(s) in the QBCore.Functions table
local function SetMethod(methodName, handler)
if type(methodName) ~= 'string' then
return false, 'invalid_method_name'
end
QBCore.Functions[methodName] = handler
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.SetMethod = SetMethod
exports('SetMethod', SetMethod)
-- Add or change (a) field(s) in the QBCore table
local function SetField(fieldName, data)
if type(fieldName) ~= 'string' then
return false, 'invalid_field_name'
end
QBCore[fieldName] = data
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.SetField = SetField
exports('SetField', SetField)
-- Single add job function which should only be used if you planning on adding a single job
local function AddJob(jobName, job)
if type(jobName) ~= 'string' then
return false, 'invalid_job_name'
end
if QBCore.Shared.Jobs[jobName] then
return false, 'job_exists'
end
QBCore.Shared.Jobs[jobName] = job
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Jobs', jobName, job)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.AddJob = AddJob
exports('AddJob', AddJob)
-- Multiple Add Jobs
local function AddJobs(jobs)
local shouldContinue = true
local message = 'success'
local errorItem = nil
for key, value in pairs(jobs) do
if type(key) ~= 'string' then
message = 'invalid_job_name'
shouldContinue = false
errorItem = jobs[key]
break
end
if QBCore.Shared.Jobs[key] then
message = 'job_exists'
shouldContinue = false
errorItem = jobs[key]
break
end
QBCore.Shared.Jobs[key] = value
end
if not shouldContinue then return false, message, errorItem end
TriggerClientEvent('QBCore:Client:OnSharedUpdateMultiple', -1, 'Jobs', jobs)
TriggerEvent('QBCore:Server:UpdateObject')
return true, message, nil
end
QBCore.Functions.AddJobs = AddJobs
exports('AddJobs', AddJobs)
-- Single Remove Job
local function RemoveJob(jobName)
if type(jobName) ~= 'string' then
return false, 'invalid_job_name'
end
if not QBCore.Shared.Jobs[jobName] then
return false, 'job_not_exists'
end
QBCore.Shared.Jobs[jobName] = nil
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Jobs', jobName, nil)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.RemoveJob = RemoveJob
exports('RemoveJob', RemoveJob)
-- Single Update Job
local function UpdateJob(jobName, job)
if type(jobName) ~= 'string' then
return false, 'invalid_job_name'
end
if not QBCore.Shared.Jobs[jobName] then
return false, 'job_not_exists'
end
QBCore.Shared.Jobs[jobName] = job
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Jobs', jobName, job)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.UpdateJob = UpdateJob
exports('UpdateJob', UpdateJob)
-- Single add item
local function AddItem(itemName, item)
if type(itemName) ~= 'string' then
return false, 'invalid_item_name'
end
if QBCore.Shared.Items[itemName] then
return false, 'item_exists'
end
QBCore.Shared.Items[itemName] = item
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Items', itemName, item)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.AddItem = AddItem
exports('AddItem', AddItem)
-- Single update item
local function UpdateItem(itemName, item)
if type(itemName) ~= 'string' then
return false, 'invalid_item_name'
end
if not QBCore.Shared.Items[itemName] then
return false, 'item_not_exists'
end
QBCore.Shared.Items[itemName] = item
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Items', itemName, item)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.UpdateItem = UpdateItem
exports('UpdateItem', UpdateItem)
-- Multiple Add Items
local function AddItems(items)
local shouldContinue = true
local message = 'success'
local errorItem = nil
for key, value in pairs(items) do
if type(key) ~= 'string' then
message = 'invalid_item_name'
shouldContinue = false
errorItem = items[key]
break
end
if QBCore.Shared.Items[key] then
message = 'item_exists'
shouldContinue = false
errorItem = items[key]
break
end
QBCore.Shared.Items[key] = value
end
if not shouldContinue then return false, message, errorItem end
TriggerClientEvent('QBCore:Client:OnSharedUpdateMultiple', -1, 'Items', items)
TriggerEvent('QBCore:Server:UpdateObject')
return true, message, nil
end
QBCore.Functions.AddItems = AddItems
exports('AddItems', AddItems)
-- Single Remove Item
local function RemoveItem(itemName)
if type(itemName) ~= 'string' then
return false, 'invalid_item_name'
end
if not QBCore.Shared.Items[itemName] then
return false, 'item_not_exists'
end
QBCore.Shared.Items[itemName] = nil
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Items', itemName, nil)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.RemoveItem = RemoveItem
exports('RemoveItem', RemoveItem)
-- Single Add Gang
local function AddGang(gangName, gang)
if type(gangName) ~= 'string' then
return false, 'invalid_gang_name'
end
if QBCore.Shared.Gangs[gangName] then
return false, 'gang_exists'
end
QBCore.Shared.Gangs[gangName] = gang
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Gangs', gangName, gang)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.AddGang = AddGang
exports('AddGang', AddGang)
-- Multiple Add Gangs
local function AddGangs(gangs)
local shouldContinue = true
local message = 'success'
local errorItem = nil
for key, value in pairs(gangs) do
if type(key) ~= 'string' then
message = 'invalid_gang_name'
shouldContinue = false
errorItem = gangs[key]
break
end
if QBCore.Shared.Gangs[key] then
message = 'gang_exists'
shouldContinue = false
errorItem = gangs[key]
break
end
QBCore.Shared.Gangs[key] = value
end
if not shouldContinue then return false, message, errorItem end
TriggerClientEvent('QBCore:Client:OnSharedUpdateMultiple', -1, 'Gangs', gangs)
TriggerEvent('QBCore:Server:UpdateObject')
return true, message, nil
end
QBCore.Functions.AddGangs = AddGangs
exports('AddGangs', AddGangs)
-- Single Remove Gang
local function RemoveGang(gangName)
if type(gangName) ~= 'string' then
return false, 'invalid_gang_name'
end
if not QBCore.Shared.Gangs[gangName] then
return false, 'gang_not_exists'
end
QBCore.Shared.Gangs[gangName] = nil
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Gangs', gangName, nil)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.RemoveGang = RemoveGang
exports('RemoveGang', RemoveGang)
-- Single Update Gang
local function UpdateGang(gangName, gang)
if type(gangName) ~= 'string' then
return false, 'invalid_gang_name'
end
if not QBCore.Shared.Gangs[gangName] then
return false, 'gang_not_exists'
end
QBCore.Shared.Gangs[gangName] = gang
TriggerClientEvent('QBCore:Client:OnSharedUpdate', -1, 'Gangs', gangName, gang)
TriggerEvent('QBCore:Server:UpdateObject')
return true, 'success'
end
QBCore.Functions.UpdateGang = UpdateGang
exports('UpdateGang', UpdateGang)
local resourceName = GetCurrentResourceName()
local function GetCoreVersion(InvokingResource)
local resourceVersion = GetResourceMetadata(resourceName, 'version')
if InvokingResource and InvokingResource ~= '' then
print(('%s called qbcore version check: %s'):format(InvokingResource or 'Unknown Resource', resourceVersion))
end
return resourceVersion
end
QBCore.Functions.GetCoreVersion = GetCoreVersion
exports('GetCoreVersion', GetCoreVersion)
local function ExploitBan(playerId, origin)
local name = GetPlayerName(playerId)
MySQL.insert('INSERT INTO bans (name, license, discord, ip, reason, expire, bannedby) VALUES (?, ?, ?, ?, ?, ?, ?)', {
name,
QBCore.Functions.GetIdentifier(playerId, 'license'),
QBCore.Functions.GetIdentifier(playerId, 'discord'),
QBCore.Functions.GetIdentifier(playerId, 'ip'),
origin,
2147483647,
'Anti Cheat'
})
DropPlayer(playerId, Lang:t('info.exploit_banned', { discord = QBCore.Config.Server.Discord }))
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'red', name .. ' has been banned for exploiting ' .. origin, true)
end
exports('ExploitBan', ExploitBan)
+671 -112
View File
@@ -1,14 +1,444 @@
-- Callbacks
QBCore.Functions = {}
QBCore.Player_Buckets = {}
QBCore.Entity_Buckets = {}
QBCore.UsableItems = {}
function QBCore.Functions.CreateCallback(name, cb)
QBCore.ServerCallbacks[name] = cb
-- Getters
-- Get your player first and then trigger a function on them
-- ex: local player = QBCore.Functions.GetPlayer(source)
-- ex: local example = player.Functions.functionname(parameter)
---Gets the coordinates of an entity
---@param entity number
---@return vector4
function QBCore.Functions.GetCoords(entity)
local coords = GetEntityCoords(entity, false)
local heading = GetEntityHeading(entity)
return vector4(coords.x, coords.y, coords.z, heading)
end
---Gets player identifier of the given type
---@param source any
---@param idtype string
---@return string?
function QBCore.Functions.GetIdentifier(source, idtype)
if GetConvarInt('sv_fxdkMode', 0) == 1 then return 'license:fxdk' end
return GetPlayerIdentifierByType(source, idtype or 'license')
end
---Gets a players server id (source). Returns 0 if no player is found.
---@param identifier string
---@return number
function QBCore.Functions.GetSource(identifier)
for src, _ in pairs(QBCore.Players) do
local idens = GetPlayerIdentifiers(src)
for _, id in pairs(idens) do
if identifier == id then
return src
end
end
end
return 0
end
---Get player with given server id (source)
---@param source any
---@return table
function QBCore.Functions.GetPlayer(source)
if tonumber(source) ~= nil then -- If a number is a string ("1"), this will still correctly identify the index to use.
return QBCore.Players[tonumber(source)]
else
return QBCore.Players[QBCore.Functions.GetSource(source)]
end
end
---Get player by citizen id
---@param citizenid string
---@return table?
function QBCore.Functions.GetPlayerByCitizenId(citizenid)
for src in pairs(QBCore.Players) do
if QBCore.Players[src].PlayerData.citizenid == citizenid then
return QBCore.Players[src]
end
end
return nil
end
---Get offline player by citizen id
---@param citizenid string
---@return table?
function QBCore.Functions.GetOfflinePlayerByCitizenId(citizenid)
return QBCore.Player.GetOfflinePlayer(citizenid)
end
---Get player by license
---@param license string
---@return table?
function QBCore.Functions.GetPlayerByLicense(license)
return QBCore.Player.GetPlayerByLicense(license)
end
---Get player by phone number
---@param number number
---@return table?
function QBCore.Functions.GetPlayerByPhone(number)
for src in pairs(QBCore.Players) do
if QBCore.Players[src].PlayerData.charinfo.phone == number then
return QBCore.Players[src]
end
end
return nil
end
---Get player by account id
---@param account string
---@return table?
function QBCore.Functions.GetPlayerByAccount(account)
for src in pairs(QBCore.Players) do
if QBCore.Players[src].PlayerData.charinfo.account == account then
return QBCore.Players[src]
end
end
return nil
end
---Get player passing property and value to check exists
---@param property string
---@param value string
---@return table?
function QBCore.Functions.GetPlayerByCharInfo(property, value)
for src in pairs(QBCore.Players) do
local charinfo = QBCore.Players[src].PlayerData.charinfo
if charinfo[property] ~= nil and charinfo[property] == value then
return QBCore.Players[src]
end
end
return nil
end
---Get all players. Returns the server ids of all players.
---@return table
function QBCore.Functions.GetPlayers()
local sources = {}
for k in pairs(QBCore.Players) do
sources[#sources + 1] = k
end
return sources
end
---Will return an array of QB Player class instances
---unlike the GetPlayers() wrapper which only returns IDs
---@return table
function QBCore.Functions.GetQBPlayers()
return QBCore.Players
end
---Gets a list of all on duty players of a specified job and the number
---@param job string
---@return table, number
function QBCore.Functions.GetPlayersOnDuty(job)
local players = {}
local count = 0
for src, Player in pairs(QBCore.Players) do
if Player.PlayerData.job.name == job then
if Player.PlayerData.job.onduty then
players[#players + 1] = src
count += 1
end
end
end
return players, count
end
---Returns only the amount of players on duty for the specified job
---@param job string
---@return number
function QBCore.Functions.GetDutyCount(job)
local count = 0
for _, Player in pairs(QBCore.Players) do
if Player.PlayerData.job.name == job then
if Player.PlayerData.job.onduty then
count += 1
end
end
end
return count
end
--- @param source number source player's server ID.
--- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used.
--- @return string closestPlayer - The Player that is closest to the source player (or the provided coordinates). Returns -1 if no Players are found.
--- @return number closestDistance - The distance to the closest Player. Returns -1 if no Players are found.
function QBCore.Functions.GetClosestPlayer(source, coords)
local ped = GetPlayerPed(source)
local players = GetPlayers()
local closestDistance, closestPlayer = -1, -1
if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end
if not coords then coords = GetEntityCoords(ped) end
for i = 1, #players do
local playerId = players[i]
local playerPed = GetPlayerPed(playerId)
if playerPed ~= ped then
local playerCoords = GetEntityCoords(playerPed)
local distance = #(playerCoords - coords)
if closestDistance == -1 or distance < closestDistance then
closestPlayer = playerId
closestDistance = distance
end
end
end
return closestPlayer, closestDistance
end
--- @param source number source player's server ID.
--- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used.
--- @return number closestObject - The Object that is closest to the source player (or the provided coordinates). Returns -1 if no Objects are found.
--- @return number closestDistance - The distance to the closest Object. Returns -1 if no Objects are found.
function QBCore.Functions.GetClosestObject(source, coords)
local ped = GetPlayerPed(source)
local objects = GetAllObjects()
local closestDistance, closestObject = -1, -1
if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end
if not coords then coords = GetEntityCoords(ped) end
for i = 1, #objects do
local objectCoords = GetEntityCoords(objects[i])
local distance = #(objectCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestObject = objects[i]
closestDistance = distance
end
end
return closestObject, closestDistance
end
--- @param source number source player's server ID.
--- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used.
--- @return number closestVehicle - The Vehicle that is closest to the source player (or the provided coordinates). Returns -1 if no Vehicles are found.
--- @return number closestDistance - The distance to the closest Vehicle. Returns -1 if no Vehicles are found.
function QBCore.Functions.GetClosestVehicle(source, coords)
local ped = GetPlayerPed(source)
local vehicles = GetAllVehicles()
local closestDistance, closestVehicle = -1, -1
if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end
if not coords then coords = GetEntityCoords(ped) end
for i = 1, #vehicles do
local vehicleCoords = GetEntityCoords(vehicles[i])
local distance = #(vehicleCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestVehicle = vehicles[i]
closestDistance = distance
end
end
return closestVehicle, closestDistance
end
--- @param source number source player's server ID.
--- @param coords vector The coordinates to calculate the distance from. Can be a table with x, y, z fields or a vector3. If not provided, the source player's Ped's coordinates are used.
--- @return number closestPed - The Ped that is closest to the source player (or the provided coordinates). Returns -1 if no Peds are found.
--- @return number closestDistance - The distance to the closest Ped. Returns -1 if no Peds are found.
function QBCore.Functions.GetClosestPed(source, coords)
local ped = GetPlayerPed(source)
local peds = GetAllPeds()
local closestDistance, closestPed = -1, -1
if coords then coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords end
if not coords then coords = GetEntityCoords(ped) end
for i = 1, #peds do
if peds[i] ~= ped then
local pedCoords = GetEntityCoords(peds[i])
local distance = #(pedCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestPed = peds[i]
closestDistance = distance
end
end
end
return closestPed, closestDistance
end
-- Routing buckets (Only touch if you know what you are doing)
---Returns the objects related to buckets, first returned value is the player buckets, second one is entity buckets
---@return table, table
function QBCore.Functions.GetBucketObjects()
return QBCore.Player_Buckets, QBCore.Entity_Buckets
end
---Will set the provided player id / source into the provided bucket id
---@param source any
---@param bucket any
---@return boolean
function QBCore.Functions.SetPlayerBucket(source, bucket)
if source and bucket then
local plicense = QBCore.Functions.GetIdentifier(source, 'license')
Player(source).state:set('instance', bucket, true)
SetPlayerRoutingBucket(source, bucket)
QBCore.Player_Buckets[plicense] = { id = source, bucket = bucket }
return true
else
return false
end
end
---Will set any entity into the provided bucket, for example peds / vehicles / props / etc.
---@param entity number
---@param bucket number
---@return boolean
function QBCore.Functions.SetEntityBucket(entity, bucket)
if entity and bucket then
SetEntityRoutingBucket(entity, bucket)
QBCore.Entity_Buckets[entity] = { id = entity, bucket = bucket }
return true
else
return false
end
end
---Will return an array of all the player ids inside the current bucket
---@param bucket number
---@return table|boolean
function QBCore.Functions.GetPlayersInBucket(bucket)
local curr_bucket_pool = {}
if QBCore.Player_Buckets and next(QBCore.Player_Buckets) then
for _, v in pairs(QBCore.Player_Buckets) do
if v.bucket == bucket then
curr_bucket_pool[#curr_bucket_pool + 1] = v.id
end
end
return curr_bucket_pool
else
return false
end
end
---Will return an array of all the entities inside the current bucket
---(not for player entities, use GetPlayersInBucket for that)
---@param bucket number
---@return table|boolean
function QBCore.Functions.GetEntitiesInBucket(bucket)
local curr_bucket_pool = {}
if QBCore.Entity_Buckets and next(QBCore.Entity_Buckets) then
for _, v in pairs(QBCore.Entity_Buckets) do
if v.bucket == bucket then
curr_bucket_pool[#curr_bucket_pool + 1] = v.id
end
end
return curr_bucket_pool
else
return false
end
end
---Server side vehicle creation with optional callback
---the CreateVehicle RPC still uses the client for creation so players must be near
---@param source any
---@param model any
---@param coords vector
---@param warp boolean
---@return number
function QBCore.Functions.SpawnVehicle(source, model, coords, warp)
local ped = GetPlayerPed(source)
model = type(model) == 'string' and joaat(model) or model
if not coords then coords = GetEntityCoords(ped) end
local heading = coords.w and coords.w or 0.0
local veh = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, true)
while not DoesEntityExist(veh) do Wait(0) end
if warp then
while GetVehiclePedIsIn(ped) ~= veh do
Wait(0)
TaskWarpPedIntoVehicle(ped, veh, -1)
end
end
while NetworkGetEntityOwner(veh) ~= source do Wait(0) end
return veh
end
---Server side vehicle creation with optional callback
---the CreateAutomobile native is still experimental but doesn't use client for creation
---doesn't work for all vehicles!
---comment
---@param source any
---@param model any
---@param coords vector
---@param warp boolean
---@return number
function QBCore.Functions.CreateAutomobile(source, model, coords, warp)
model = type(model) == 'string' and joaat(model) or model
if not coords then coords = GetEntityCoords(GetPlayerPed(source)) end
local heading = coords.w and coords.w or 0.0
local CreateAutomobile = `CREATE_AUTOMOBILE`
local veh = Citizen.InvokeNative(CreateAutomobile, model, coords, heading, true, true)
while not DoesEntityExist(veh) do Wait(0) end
if warp then TaskWarpPedIntoVehicle(GetPlayerPed(source), veh, -1) end
return veh
end
--- New & more reliable server side native for creating vehicles
---comment
---@param source any
---@param model any
---@param vehtype any
-- The appropriate vehicle type for the model info.
-- Can be one of automobile, bike, boat, heli, plane, submarine, trailer, and (potentially), train.
-- This should be the same type as the type field in vehicles.meta.
---@param coords vector
---@param warp boolean
---@return number
function QBCore.Functions.CreateVehicle(source, model, vehtype, coords, warp)
model = type(model) == 'string' and joaat(model) or model
vehtype = type(vehtype) == 'string' and tostring(vehtype) or vehtype
if not coords then coords = GetEntityCoords(GetPlayerPed(source)) end
local heading = coords.w and coords.w or 0.0
local veh = CreateVehicleServerSetter(model, vehtype, coords, heading)
while not DoesEntityExist(veh) do Wait(0) end
if warp then TaskWarpPedIntoVehicle(GetPlayerPed(source), veh, -1) end
return veh
end
function PaycheckInterval()
if not next(QBCore.Players) then
SetTimeout(QBCore.Config.Money.PayCheckTimeOut * (60 * 1000), PaycheckInterval) -- Prevent paychecks from stopping forever once 0 players
return
end
for _, Player in pairs(QBCore.Players) do
if not Player then return end
local payment = QBShared.Jobs[Player.PlayerData.job.name]['grades'][tostring(Player.PlayerData.job.grade.level)].payment
if not payment then payment = Player.PlayerData.job.payment end
if Player.PlayerData.job and payment > 0 and (QBShared.Jobs[Player.PlayerData.job.name].offDutyPay or Player.PlayerData.job.onduty) then
if QBCore.Config.Money.PayCheckSociety then
local account = exports['qb-banking']:GetAccountBalance(Player.PlayerData.job.name)
if account ~= 0 then
if account < payment then
TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('error.company_too_poor'), 'error')
else
Player.Functions.AddMoney('bank', payment, 'paycheck')
exports['qb-banking']:RemoveMoney(Player.PlayerData.job.name, payment, 'Employee Paycheck')
TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('info.received_paycheck', { value = payment }))
end
else
Player.Functions.AddMoney('bank', payment, 'paycheck')
TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('info.received_paycheck', { value = payment }))
end
else
Player.Functions.AddMoney('bank', payment, 'paycheck')
TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t('info.received_paycheck', { value = payment }))
end
end
end
SetTimeout(QBCore.Config.Money.PayCheckTimeOut * (60 * 1000), PaycheckInterval)
end
-- Callback Functions --
---Trigger Client Callback
---@param name string
---@param source any
---@param cb function
---@param ... any
function QBCore.Functions.TriggerClientCallback(name, source, ...)
local cb = nil
local args = { ... }
if QBCore.Functions.IsFunction(args[1]) then
if QBCore.Shared.IsFunction(args[1]) then
cb = args[1]
table.remove(args, 1)
end
@@ -26,8 +456,201 @@ function QBCore.Functions.TriggerClientCallback(name, source, ...)
end
end
-- Functions
---Create Server Callback
---@param name string
---@param cb function
function QBCore.Functions.CreateCallback(name, cb)
QBCore.ServerCallbacks[name] = cb
end
-- Items
---Create a usable item
---@param item string
---@param data function
function QBCore.Functions.CreateUseableItem(item, data)
local rawFunc = nil
if type(data) == 'table' then
if rawget(data, '__cfx_functionReference') then
rawFunc = data
elseif data.cb and rawget(data.cb, '__cfx_functionReference') then
rawFunc = data.cb
elseif data.callback and rawget(data.callback, '__cfx_functionReference') then
rawFunc = data.callback
end
elseif type(data) == 'function' then
rawFunc = data
end
if rawFunc then
QBCore.UsableItems[item] = {
func = rawFunc,
resource = GetInvokingResource()
}
end
end
---Checks if the given item is usable
---@param item string
---@return any
function QBCore.Functions.CanUseItem(item)
return QBCore.UsableItems[item]
end
---Use item
---@param source any
---@param item string
function QBCore.Functions.UseItem(source, item)
if GetResourceState('qb-inventory') == 'missing' then return end
exports['qb-inventory']:UseItem(source, item)
end
---Kick Player
---@param source any
---@param reason string
---@param setKickReason boolean
---@param deferrals boolean
function QBCore.Functions.Kick(source, reason, setKickReason, deferrals)
reason = '\n' .. reason .. '\n🔸 Check our Discord for further information: ' .. QBCore.Config.Server.Discord
if setKickReason then
setKickReason(reason)
end
CreateThread(function()
if deferrals then
deferrals.update(reason)
Wait(2500)
end
if source then
DropPlayer(source, reason)
end
for _ = 0, 4 do
while true do
if source then
if GetPlayerPing(source) >= 0 then
break
end
Wait(100)
CreateThread(function()
DropPlayer(source, reason)
end)
end
end
Wait(5000)
end
end)
end
---Check if player is whitelisted, kept like this for backwards compatibility or future plans
---@param source any
---@return boolean
function QBCore.Functions.IsWhitelisted(source)
if not QBCore.Config.Server.Whitelist then return true end
if QBCore.Functions.HasPermission(source, QBCore.Config.Server.WhitelistPermission) then return true end
return false
end
-- Setting & Removing Permissions
---Add permission for player
---@param source any
---@param permission string
function QBCore.Functions.AddPermission(source, permission)
if not IsPlayerAceAllowed(source, permission) then
ExecuteCommand(('add_principal player.%s qbcore.%s'):format(source, permission))
QBCore.Commands.Refresh(source)
end
end
---Remove permission from player
---@param source any
---@param permission string
function QBCore.Functions.RemovePermission(source, permission)
if permission then
if IsPlayerAceAllowed(source, permission) then
ExecuteCommand(('remove_principal player.%s qbcore.%s'):format(source, permission))
QBCore.Commands.Refresh(source)
end
else
for _, v in pairs(QBCore.Config.Server.Permissions) do
if IsPlayerAceAllowed(source, v) then
ExecuteCommand(('remove_principal player.%s qbcore.%s'):format(source, v))
QBCore.Commands.Refresh(source)
end
end
end
end
-- Checking for Permission Level
---Check if player has permission
---@param source any
---@param permission string
---@return boolean
function QBCore.Functions.HasPermission(source, permission)
if type(permission) == 'string' then
if IsPlayerAceAllowed(source, permission) then return true end
elseif type(permission) == 'table' then
for _, permLevel in pairs(permission) do
if IsPlayerAceAllowed(source, permLevel) then return true end
end
end
return false
end
---Get the players permissions
---@param source any
---@return table
function QBCore.Functions.GetPermission(source)
local src = source
local perms = {}
for _, v in pairs(QBCore.Config.Server.Permissions) do
if IsPlayerAceAllowed(src, v) then
perms[v] = true
end
end
return perms
end
---Get admin messages opt-in state for player
---@param source any
---@return boolean
function QBCore.Functions.IsOptin(source)
local license = QBCore.Functions.GetIdentifier(source, 'license')
if not license or not QBCore.Functions.HasPermission(source, 'admin') then return false end
local Player = QBCore.Functions.GetPlayer(source)
return Player.PlayerData.optin
end
---Toggle opt-in to admin messages
---@param source any
function QBCore.Functions.ToggleOptin(source)
local license = QBCore.Functions.GetIdentifier(source, 'license')
if not license or not QBCore.Functions.HasPermission(source, 'admin') then return end
local Player = QBCore.Functions.GetPlayer(source)
Player.PlayerData.optin = not Player.PlayerData.optin
Player.Functions.SetPlayerData('optin', Player.PlayerData.optin)
end
---Check if player is banned
---@param source any
---@return boolean, string?
function QBCore.Functions.IsPlayerBanned(source)
local plicense = QBCore.Functions.GetIdentifier(source, 'license')
local result = MySQL.single.await('SELECT id, reason, expire FROM bans WHERE license = ?', { plicense })
if not result then return false end
if os.time() < result.expire then
local timeTable = os.date('*t', tonumber(result.expire))
return true, 'You have been banned from the server:\n' .. result.reason .. '\nYour ban expires ' .. timeTable.day .. '/' .. timeTable.month .. '/' .. timeTable.year .. ' ' .. timeTable.hour .. ':' .. timeTable.min .. '\n'
else
MySQL.query('DELETE FROM bans WHERE id = ?', { result.id })
end
return false
end
-- Retrieves information about the database connection.
--- @return table; A table containing the database information.
function QBCore.Functions.GetDatabaseInfo()
local details = {
exists = false,
@@ -56,11 +679,9 @@ function QBCore.Functions.GetDatabaseInfo()
end
end
function QBCore.Functions.GetIdentifier(source, idtype)
if GetConvarInt('sv_fxdkMode', 0) == 1 then return 'license:fxdk' end
return GetPlayerIdentifierByType(source, idtype or 'license')
end
---Check for duplicate license
---@param license any
---@return boolean
function QBCore.Functions.IsLicenseInUse(license)
local players = GetPlayers()
for _, player in pairs(players) do
@@ -70,111 +691,49 @@ function QBCore.Functions.IsLicenseInUse(license)
return false
end
function QBCore.Functions.HasPermission(source, permission)
if type(permission) == 'string' then
if IsPlayerAceAllowed(source, permission) then return true end
elseif type(permission) == 'table' then
for _, permLevel in pairs(permission) do
if IsPlayerAceAllowed(source, permLevel) then return true end
end
-- Utility functions
---Check if a player has an item [deprecated]
---@param source any
---@param items table|string
---@param amount number
---@return boolean
function QBCore.Functions.HasItem(source, items, amount)
if GetResourceState('qb-inventory') == 'missing' then return end
return exports['qb-inventory']:HasItem(source, items, amount)
end
---Notify
---@param source any
---@param text string
---@param type string
---@param length number
function QBCore.Functions.Notify(source, text, type, length)
TriggerClientEvent('QBCore:Notify', source, text, type, length)
end
---???? ... ok
---@param source any
---@param data any
---@param pattern any
---@return boolean
function QBCore.Functions.PrepForSQL(source, data, pattern)
data = tostring(data)
local src = source
local player = QBCore.Functions.GetPlayer(src)
local result = string.match(data, pattern)
if not result or string.len(result) ~= string.len(data) then
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'SQL Exploit Attempted', 'red', string.format('%s attempted to exploit SQL!', player.PlayerData.license))
return false
end
return false
return true
end
function QBCore.Functions.IsWhitelisted(source)
if not QBCore.Config.Server.Whitelist then return true end
if QBCore.Functions.HasPermission(source, QBCore.Config.Server.WhitelistPermission) then return true end
return false
end
function QBCore.Functions.IsPlayerBanned(source)
local plicense = QBCore.Functions.GetIdentifier(source, 'license')
local result = MySQL.single.await('SELECT id, reason, expire FROM bans WHERE license = ?', { plicense })
if not result then return false end
if os.time() < result.expire then
local timeTable = os.date('*t', tonumber(result.expire))
return true, 'You have been banned from the server:\n' .. result.reason .. '\nYour ban expires ' .. timeTable.day .. '/' .. timeTable.month .. '/' .. timeTable.year .. ' ' .. timeTable.hour .. ':' .. timeTable.min .. '\n'
else
MySQL.query('DELETE FROM bans WHERE id = ?', { result.id })
end
return false
end
function QBCore.Functions.GetPlayer(source)
if tonumber(source) then
return QBCore.Players[tonumber(source)]
else
return QBCore.Players[QBCore.Functions.GetSource(source)]
for functionName, func in pairs(QBCore.Functions) do
if type(func) == 'function' then
exports(functionName, func)
end
end
function QBCore.Functions.GetQBPlayers()
return QBCore.Players
end
function QBCore.Functions.CreateUseableItem(item, data)
local rawFunc = nil
if type(data) == 'table' then
if rawget(data, '__cfx_functionReference') then
rawFunc = data
elseif data.cb and rawget(data.cb, '__cfx_functionReference') then
rawFunc = data.cb
elseif data.callback and rawget(data.callback, '__cfx_functionReference') then
rawFunc = data.callback
end
elseif type(data) == 'function' then
rawFunc = data
end
if rawFunc then
QBCore.UsableItems[item] = {
func = rawFunc,
resource = GetInvokingResource()
}
end
end
-- Player Setup
function QBCore.Functions.CreateCitizenId()
local CitizenId = tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper()
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE citizenid = ?) AS uniqueCheck', { CitizenId })
if result == 0 then return CitizenId end
return QBCore.Functions.CreateCitizenId()
end
function QBCore.Functions.CreateAccountNumber()
local AccountNumber = 'US0' .. math.random(1, 9) .. 'QBCore' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.account")) = ?) AS uniqueCheck', { AccountNumber })
if result == 0 then return AccountNumber end
return QBCore.Functions.CreateAccountNumber()
end
function QBCore.Functions.CreatePhoneNumber()
local PhoneNumber = math.random(100, 999) .. math.random(1000000, 9999999)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.phone")) = ?) AS uniqueCheck', { PhoneNumber })
if result == 0 then return PhoneNumber end
return QBCore.Functions.CreatePhoneNumber()
end
function QBCore.Functions.CreateFingerId()
local FingerId = tostring(QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(1) .. QBCore.Shared.RandomInt(2) .. QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(4))
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.fingerprint")) = ?) AS uniqueCheck', { FingerId })
if result == 0 then return FingerId end
return QBCore.Functions.CreateFingerId()
end
function QBCore.Functions.CreateWalletId()
local WalletId = 'QB-' .. math.random(11111111, 99999999)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.walletid")) = ?) AS uniqueCheck', { WalletId })
if result == 0 then return WalletId end
return QBCore.Functions.CreateWalletId()
end
function QBCore.Functions.CreateSerialNumber()
local SerialNumber = math.random(11111111, 99999999)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.phonedata.SerialNumber")) = ?) AS uniqueCheck', { SerialNumber })
if result == 0 then return SerialNumber end
return QBCore.Functions.CreateSerialNumber()
end
-- Access a specific function directly:
-- exports['qb-core']:Notify(source, 'Hello Player!')
+42 -67
View File
@@ -1,74 +1,49 @@
-- Database Verification
QBCore = {}
QBCore.Config = QBConfig
QBCore.Shared = QBShared
QBCore.ClientCallbacks = {}
QBCore.ServerCallbacks = {}
local readyFunction = MySQL.ready
local databaseConnected, bansTableExists = readyFunction == nil, readyFunction == nil
if readyFunction ~= nil then
MySQL.ready(function()
databaseConnected = true
local DatabaseInfo = QBCore.Functions.GetDatabaseInfo()
if not DatabaseInfo or not DatabaseInfo.exists then return end
local result = MySQL.query.await('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = "bans";', { DatabaseInfo.database })
if result and result[1] then
bansTableExists = true
-- Get the full QBCore object (default behavior):
-- local QBCore = GetCoreObject()
-- Get only specific parts of QBCore:
-- local QBCore = GetCoreObject({'Players', 'Config'})
local function GetCoreObject(filters)
if not filters then return QBCore end
local results = {}
for i = 1, #filters do
local key = filters[i]
if QBCore[key] then
results[key] = QBCore[key]
end
end)
end
return results
end
exports('GetCoreObject', GetCoreObject)
-- Handle player connect
local function GetSharedItems()
return QBShared.Items
end
exports('GetSharedItems', GetSharedItems)
AddEventHandler('playerConnecting', function(name, _, deferrals)
local src = source
deferrals.defer()
if QBCore.Config.Server.Closed and not IsPlayerAceAllowed(src, 'qbadmin.join') then
return deferrals.done(QBCore.Config.Server.ClosedReason)
end
if not databaseConnected then
return deferrals.done(Lang:t('error.connecting_database_error'))
end
if QBCore.Config.Server.Whitelist then
Wait(0)
deferrals.update(string.format(Lang:t('info.checking_whitelisted'), name))
if not QBCore.Functions.IsWhitelisted(src) then
return deferrals.done(Lang:t('error.not_whitelisted'))
end
end
Wait(0)
deferrals.update(string.format('Hello %s. Your license is being checked', name))
local license = QBCore.Functions.GetIdentifier(src, 'license')
if not license then
return deferrals.done(Lang:t('error.no_valid_license'))
elseif QBCore.Config.Server.CheckDuplicateLicense and QBCore.Functions.IsLicenseInUse(license) then
return deferrals.done(Lang:t('error.duplicate_license'))
end
Wait(0)
deferrals.update(string.format(Lang:t('info.checking_ban'), name))
if not bansTableExists then
return deferrals.done(Lang:t('error.ban_table_not_found'))
end
local success, isBanned, reason = pcall(QBCore.Functions.IsPlayerBanned, src)
if not success then return deferrals.done(Lang:t('error.connecting_database_error')) end
if isBanned then return deferrals.done(reason) end
Wait(0)
deferrals.update(string.format(Lang:t('info.join_server'), name))
deferrals.done()
end)
local function GetSharedVehicles()
return QBShared.Vehicles
end
exports('GetSharedVehicles', GetSharedVehicles)
-- Handle player disconnect
local function GetSharedWeapons()
return QBShared.Weapons
end
exports('GetSharedWeapons', GetSharedWeapons)
AddEventHandler('playerDropped', function(reason)
local src = source
local state = Player(src).state
if not state['citizenid'] then return end
local playerName = GetPlayerName(src) or 'Unknown'
local license = state['license'] or 'Unknown License'
TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Dropped', 'red', '**' .. playerName .. '** (' .. license .. ') left..\n **Reason:** ' .. reason)
TriggerEvent('QBCore:Server:PlayerDropped', src)
QBCore.Player.Save(src)
if GetResourceState('qb-inventory') ~= 'missing' then
exports['qb-inventory']:SaveInventory(src)
end
for _, key in pairs(GetStateBagKeys('player:' .. src)) do
state:set(key, nil, true)
end
QBCore.Player_Buckets[license] = nil
end)
local function GetSharedJobs()
return QBShared.Jobs
end
exports('GetSharedJobs', GetSharedJobs)
local function GetSharedGangs()
return QBShared.Gangs
end
exports('GetSharedGangs', GetSharedGangs)
+629 -306
View File
@@ -1,297 +1,93 @@
-- Backwards Compat
QBCore.Players = {}
QBCore.Player = {}
local function extractData(source, prefix)
local state = Player(source).state
local data = {}
for key, value in pairs(state) do
if key:find('^' .. prefix) then
local shortKey = key:gsub(prefix, '')
data[shortKey] = value or 'Unknown'
end
end
return data
end
local function CreateLegacyPlayerData(player)
local source = player.source
local state = Player(source).state
local legacy = {}
legacy.license = QBCore.Functions.GetIdentifier(source, 'license')
legacy.source = source
legacy.name = GetPlayerName(source)
legacy.citizenid = state['citizenid']
legacy.money = extractData(source, 'money:')
legacy.job = extractData(source, 'job:')
legacy.gang = extractData(source, 'gang:')
legacy.metadata = extractData(source, 'metadata:')
legacy.charinfo = extractData(source, 'charinfo:')
legacy.position = extractData(source, 'position:')
setmetatable(legacy, {
__index = function(tbl, key)
if key == 'money' then
return extractData(source, 'money:')
elseif key == 'job' then
return extractData(source, 'job:')
elseif key == 'gang' then
return extractData(source, 'gang:')
elseif key == 'metadata' then
return extractData(source, 'metadata:')
elseif key == 'charinfo' then
return extractData(source, 'charinfo:')
elseif key == 'position' then
return extractData(source, 'position:')
else
return rawget(tbl, key)
end
end,
__newindex = function(tbl, key, value)
local state = Player(source).state
if key == 'money' and type(value) == 'table' then
for subkey, subvalue in pairs(value) do
state:set('money:' .. subkey, subvalue, true)
end
elseif key == 'job' and type(value) == 'table' then
for subkey, subvalue in pairs(value) do
state:set('job:' .. subkey, subvalue, true)
end
elseif key == 'gang' and type(value) == 'table' then
for subkey, subvalue in pairs(value) do
state:set('gang:' .. subkey, subvalue, true)
end
elseif key == 'metadata' and type(value) == 'table' then
for subkey, subvalue in pairs(value) do
state:set('metadata:' .. subkey, subvalue, true)
end
elseif key == 'charinfo' and type(value) == 'table' then
for subkey, subvalue in pairs(value) do
state:set('charinfo:' .. subkey, subvalue, true)
end
elseif key == 'position' and type(value) == 'table' then
for subkey, subvalue in pairs(value) do
state:set('position:' .. subkey, subvalue, true)
end
else
rawset(tbl, key, value)
end
end,
})
return legacy
end
-- JSON STUFF
local DynamicDefaults = {
['citizenid'] = QBCore.Functions.CreateCitizenId,
['charinfo.phone'] = QBCore.Functions.CreatePhoneNumber,
['charinfo.account'] = QBCore.Functions.CreateAccountNumber,
['metadata.bloodtype'] = function() return QBCore.Functions.GetRandomElement(QBCore.Config.Player.Bloodtypes) end,
['metadata.fingerprint'] = QBCore.Functions.CreateFingerId,
['metadata.walletid'] = QBCore.Functions.CreateWalletId
}
local function ApplyDynamicDefaults(target)
for field, func in pairs(DynamicDefaults) do
local ref = target
local keys = {}
for key in field:gmatch('[^.]+') do table.insert(keys, key) end
for i = 1, #keys - 1 do
ref[keys[i]] = ref[keys[i]] or {}
ref = ref[keys[i]]
end
ref[keys[#keys]] = ref[keys[#keys]] or func()
end
end
local function LoadPlayerDefaults()
local success, defaults = pcall(function()
return json.decode(LoadResourceFile(resourceName, 'shared/player_defaults.json'))
end)
if not success or not defaults or not next(defaults) then
print('^1[ERROR]^0 Could not load player_defaults.json. Ensure the file is valid JSON and not empty.')
return {}
end
ApplyDynamicDefaults(defaults)
return defaults
end
local function MergePlayerData(target, defaults, debug)
for key, value in pairs(defaults) do
if type(value) == 'table' then
target[key] = target[key] or {}
MergePlayerData(target[key], value, debug)
else
if not debug then
if target[key] == nil then
print(('^2[INFO]^0 Added new data field: %s = %s'):format(key, tostring(value)))
elseif target[key] ~= value then
print(('^3[INFO]^0 Updated data field: %s = %s'):format(key, tostring(value)))
end
end
target[key] = (target[key] == nil or target[key] == '') and value or target[key]
end
end
end
-- Player Creation
local function InitializePlayerStateBag(source, playerData)
local state = Player(source).state
local defaults = LoadPlayerDefaults()
for _, key in pairs(GetStateBagKeys('player:' .. source)) do
if not defaults[key] then
print(('^1[INFO]^0 Removed outdated data field: %s'):format(key))
end
state:set(key, nil, true)
end
MergePlayerData(playerData, defaults)
local function populatePlayerState(data, prefix)
for key, value in pairs(data) do
if type(value) == 'table' then
populatePlayerState(value, prefix .. key .. ':')
else
state:set(prefix .. key, value, true)
end
end
end
populatePlayerState(playerData, '')
end
-- On player login get their data or set defaults
-- Don't touch any of this unless you know what you are doing
-- Will cause major issues!
local resourceName = GetCurrentResourceName()
function QBCore.Player.Login(source, citizenid, newData)
if not source or source == '' then
if source and source ~= '' then
if citizenid then
local license = QBCore.Functions.GetIdentifier(source, 'license')
local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid })
if PlayerData and license == PlayerData.license then
PlayerData.money = json.decode(PlayerData.money)
PlayerData.job = json.decode(PlayerData.job)
PlayerData.gang = json.decode(PlayerData.gang)
PlayerData.position = json.decode(PlayerData.position)
PlayerData.metadata = json.decode(PlayerData.metadata)
PlayerData.charinfo = json.decode(PlayerData.charinfo)
QBCore.Player.CheckPlayerData(source, PlayerData)
else
DropPlayer(source, Lang:t('info.exploit_dropped'))
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Joining Exploit', false)
end
else
QBCore.Player.CheckPlayerData(source, newData)
end
return true
else
QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.LOGIN - NO SOURCE GIVEN!')
return false
end
if citizenid then
local license = QBCore.Functions.GetIdentifier(source, 'license')
local data = MySQL.prepare.await('SELECT * FROM players WHERE citizenid = ?', { citizenid })
if not data then
print('^1[ERROR]^0 Failed to load data for Citizen ID:', citizenid)
DropPlayer(source, 'Failed to load your character data. Please contact staff.')
return false
end
if data and license == data.license then
local success, playerData = pcall(function()
return {
money = json.decode(data.money) or {},
job = json.decode(data.job) or {},
gang = json.decode(data.gang) or {},
position = json.decode(data.position) or {},
metadata = json.decode(data.metadata) or {},
charinfo = json.decode(data.charinfo) or {}
}
end)
if not success then
print('^1[ERROR]^0 Failed to decode player data for Citizen ID:', citizenid)
DropPlayer(source, 'Failed to decode your character data. Please contact staff.')
return false
end
InitializePlayerStateBag(source, playerData)
else
DropPlayer(source, Lang:t('info.exploit_dropped'))
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Joining Exploit', false)
end
else
InitializePlayerStateBag(source, newData or {})
end
return true
end
function QBCore.Player.CreatePlayer(PlayerData, Offline)
local self = {}
self.Offline = Offline
self.Functions = {}
function self.Functions.Notify(text, type, length)
TriggerClientEvent('QBCore:Notify', PlayerData.source, text, type, length)
function QBCore.Player.GetOfflinePlayer(citizenid)
if citizenid then
local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid })
if PlayerData then
PlayerData.money = json.decode(PlayerData.money)
PlayerData.job = json.decode(PlayerData.job)
PlayerData.gang = json.decode(PlayerData.gang)
PlayerData.position = json.decode(PlayerData.position)
PlayerData.metadata = json.decode(PlayerData.metadata)
PlayerData.charinfo = json.decode(PlayerData.charinfo)
return QBCore.Player.CheckPlayerData(nil, PlayerData)
end
end
return nil
end
function self.Functions.AddMethod(methodName, handler)
self.Functions[methodName] = handler
function QBCore.Player.GetPlayerByLicense(license)
if license then
local source = QBCore.Functions.GetSource(license)
if source > 0 then
return QBCore.Players[source]
else
return QBCore.Player.GetOfflinePlayerByLicense(license)
end
end
return nil
end
function self.Functions.AddField(fieldName, data)
self[fieldName] = data
local state = Player(PlayerData.source).state
state:set(fieldName, data, true)
function QBCore.Player.GetOfflinePlayerByLicense(license)
if license then
local PlayerData = MySQL.prepare.await('SELECT * FROM players where license = ?', { license })
if PlayerData then
PlayerData.money = json.decode(PlayerData.money)
PlayerData.job = json.decode(PlayerData.job)
PlayerData.gang = json.decode(PlayerData.gang)
PlayerData.position = json.decode(PlayerData.position)
PlayerData.metadata = json.decode(PlayerData.metadata)
PlayerData.charinfo = json.decode(PlayerData.charinfo)
return QBCore.Player.CheckPlayerData(nil, PlayerData)
end
end
return nil
end
local function CreateDynamicGetSet(prefix)
return {
Get = function(key)
local state = Player(PlayerData.source).state
return state[prefix .. key] or nil
end,
Set = function(key, value)
local state = Player(PlayerData.source).state
state:set(prefix .. key, value, true)
return true
end
}
local function applyDefaults(playerData, defaults)
for key, value in pairs(defaults) do
if type(value) == 'function' then
playerData[key] = playerData[key] or value()
elseif type(value) == 'table' then
playerData[key] = playerData[key] or {}
applyDefaults(playerData[key], value)
else
playerData[key] = playerData[key] or value
end
end
local function CreateDynamicIncremental(prefix)
return {
Get = function(key)
local state = Player(PlayerData.source).state
return state[prefix .. key] or 0
end,
Set = function(key, value)
local state = Player(PlayerData.source).state
state:set(prefix .. key, value, true)
return true
end,
Add = function(key, value)
local state = Player(PlayerData.source).state
local currentValue = state[prefix .. key] or 0
state:set(prefix .. key, currentValue + value, true)
return true
end,
Remove = function(key, value)
local state = Player(PlayerData.source).state
local currentValue = state[prefix .. key] or 0
if currentValue < value then return false end
state:set(prefix .. key, currentValue - value, true)
return true
end
}
end
self.Metadata = CreateDynamicGetSet('metadata:')
self.Job = CreateDynamicGetSet('job:')
self.Gang = CreateDynamicGetSet('gang:')
self.Money = CreateDynamicIncremental('money:')
self.CharInfo = CreateDynamicGetSet('charinfo:')
self.Position = CreateDynamicGetSet('position:')
self.PlayerData = CreateLegacyPlayerData(PlayerData)
if not self.Offline then
QBCore.Players[PlayerData.source] = self
QBCore.Player.Save(PlayerData.source)
TriggerEvent('QBCore:Server:PlayerLoaded', self)
end
return self
end
function QBCore.Player.CheckPlayerData(source, PlayerData)
@@ -304,40 +100,567 @@ function QBCore.Player.CheckPlayerData(source, PlayerData)
PlayerData.name = GetPlayerName(source)
end
InitializePlayerStateBag(source, PlayerData)
local validatedJob = false
if PlayerData.job and PlayerData.job.name ~= nil and PlayerData.job.grade and PlayerData.job.grade.level ~= nil then
local jobInfo = QBCore.Shared.Jobs[PlayerData.job.name]
if jobInfo then
local jobGradeInfo = jobInfo.grades[tostring(PlayerData.job.grade.level)]
if jobGradeInfo then
PlayerData.job.label = jobInfo.label
PlayerData.job.grade.name = jobGradeInfo.name
PlayerData.job.payment = jobGradeInfo.payment
PlayerData.job.grade.isboss = jobGradeInfo.isboss or false
PlayerData.job.isboss = jobGradeInfo.isboss or false
validatedJob = true
end
end
end
if validatedJob == false then
-- set to nil, as the default job (unemployed) will be added by `applyDefaults`
PlayerData.job = nil
end
local validatedGang = false
if PlayerData.gang and PlayerData.gang.name ~= nil and PlayerData.gang.grade and PlayerData.gang.grade.level ~= nil then
local gangInfo = QBCore.Shared.Gangs[PlayerData.gang.name]
if gangInfo then
local gangGradeInfo = gangInfo.grades[tostring(PlayerData.gang.grade.level)]
if gangGradeInfo then
PlayerData.gang.label = gangInfo.label
PlayerData.gang.grade.name = gangGradeInfo.name
PlayerData.gang.payment = gangGradeInfo.payment
PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false
PlayerData.gang.isboss = gangGradeInfo.isboss or false
validatedGang = true
end
end
end
if validatedGang == false then
-- set to nil, as the default gang (unemployed) will be added by `applyDefaults`
PlayerData.gang = nil
end
applyDefaults(PlayerData, QBCore.Config.Player.PlayerDefaults)
if GetResourceState('qb-inventory') ~= 'missing' then
PlayerData.items = exports['qb-inventory']:LoadInventory(PlayerData.source, PlayerData.citizenid)
end
return QBCore.Player.CreatePlayer(PlayerData, Offline)
end
local function BuildPlayerSaveData(source)
local state = Player(source).state
-- On player logout
return {
citizenid = state['citizenid'],
money = json.encode(extractData(source, 'money:')),
metadata = json.encode(extractData(source, 'metadata:')),
position = json.encode(extractData(source, 'position:')),
job = json.encode(extractData(source, 'job:')),
gang = json.encode(extractData(source, 'gang:'))
}
function QBCore.Player.Logout(source)
TriggerClientEvent('QBCore:Client:OnPlayerUnload', source)
TriggerEvent('QBCore:Server:OnPlayerUnload', source)
TriggerClientEvent('QBCore:Player:UpdatePlayerData', source)
Wait(200)
QBCore.Players[source] = nil
end
-- Create a new character
-- Don't touch any of this unless you know what you are doing
-- Will cause major issues!
function QBCore.Player.CreatePlayer(PlayerData, Offline)
local self = {}
self.Functions = {}
self.PlayerData = PlayerData
self.Offline = Offline
function self.Functions.UpdatePlayerData()
if self.Offline then return end
TriggerEvent('QBCore:Player:SetPlayerData', self.PlayerData)
TriggerClientEvent('QBCore:Player:SetPlayerData', self.PlayerData.source, self.PlayerData)
end
function self.Functions.SetJob(job, grade)
job = job:lower()
grade = grade or '0'
if not QBCore.Shared.Jobs[job] then return false end
self.PlayerData.job = {
name = job,
label = QBCore.Shared.Jobs[job].label,
onduty = QBCore.Shared.Jobs[job].defaultDuty,
type = QBCore.Shared.Jobs[job].type or 'none',
grade = {
name = 'No Grades',
level = 0,
payment = 30,
isboss = false
}
}
local gradeKey = tostring(grade)
local jobGradeInfo = QBCore.Shared.Jobs[job].grades[gradeKey]
if jobGradeInfo then
self.PlayerData.job.grade.name = jobGradeInfo.name
self.PlayerData.job.grade.level = tonumber(gradeKey)
self.PlayerData.job.grade.payment = jobGradeInfo.payment
self.PlayerData.job.grade.isboss = jobGradeInfo.isboss or false
self.PlayerData.job.isboss = jobGradeInfo.isboss or false
end
if not self.Offline then
self.Functions.UpdatePlayerData()
TriggerEvent('QBCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
TriggerClientEvent('QBCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
end
return true
end
function self.Functions.SetGang(gang, grade)
gang = gang:lower()
grade = grade or '0'
if not QBCore.Shared.Gangs[gang] then return false end
self.PlayerData.gang = {
name = gang,
label = QBCore.Shared.Gangs[gang].label,
grade = {
name = 'No Grades',
level = 0,
isboss = false
}
}
local gradeKey = tostring(grade)
local gangGradeInfo = QBCore.Shared.Gangs[gang].grades[gradeKey]
if gangGradeInfo then
self.PlayerData.gang.grade.name = gangGradeInfo.name
self.PlayerData.gang.grade.level = tonumber(gradeKey)
self.PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false
self.PlayerData.gang.isboss = gangGradeInfo.isboss or false
end
if not self.Offline then
self.Functions.UpdatePlayerData()
TriggerEvent('QBCore:Server:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang)
TriggerClientEvent('QBCore:Client:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang)
end
return true
end
function self.Functions.Notify(text, type, length)
TriggerClientEvent('QBCore:Notify', self.PlayerData.source, text, type, length)
end
function self.Functions.HasItem(items, amount)
return QBCore.Functions.HasItem(self.PlayerData.source, items, amount)
end
function self.Functions.GetName()
local charinfo = self.PlayerData.charinfo
return charinfo.firstname .. ' ' .. charinfo.lastname
end
function self.Functions.SetJobDuty(onDuty)
self.PlayerData.job.onduty = not not onDuty
TriggerEvent('QBCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
TriggerClientEvent('QBCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
self.Functions.UpdatePlayerData()
end
function self.Functions.SetPlayerData(key, val)
if not key or type(key) ~= 'string' then return end
self.PlayerData[key] = val
self.Functions.UpdatePlayerData()
end
function self.Functions.SetMetaData(meta, val)
if not meta or type(meta) ~= 'string' then return end
if meta == 'hunger' or meta == 'thirst' then
val = val > 100 and 100 or val
end
self.PlayerData.metadata[meta] = val
self.Functions.UpdatePlayerData()
end
function self.Functions.GetMetaData(meta)
if not meta or type(meta) ~= 'string' then return end
return self.PlayerData.metadata[meta]
end
function self.Functions.AddRep(rep, amount)
if not rep or not amount then return end
local addAmount = tonumber(amount)
local currentRep = self.PlayerData.metadata['rep'][rep] or 0
self.PlayerData.metadata['rep'][rep] = currentRep + addAmount
self.Functions.UpdatePlayerData()
end
function self.Functions.RemoveRep(rep, amount)
if not rep or not amount then return end
local removeAmount = tonumber(amount)
local currentRep = self.PlayerData.metadata['rep'][rep] or 0
if currentRep - removeAmount < 0 then
self.PlayerData.metadata['rep'][rep] = 0
else
self.PlayerData.metadata['rep'][rep] = currentRep - removeAmount
end
self.Functions.UpdatePlayerData()
end
function self.Functions.GetRep(rep)
if not rep then return end
return self.PlayerData.metadata['rep'][rep] or 0
end
function self.Functions.AddMoney(moneytype, amount, reason)
reason = reason or 'unknown'
moneytype = moneytype:lower()
amount = tonumber(amount)
if amount < 0 then return end
if not self.PlayerData.money[moneytype] then return false end
self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] + amount
if not self.Offline then
self.Functions.UpdatePlayerData()
if amount > 100000 then
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true)
else
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason)
end
TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, false)
TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'add', reason)
TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'add', reason)
end
return true
end
function self.Functions.RemoveMoney(moneytype, amount, reason)
reason = reason or 'unknown'
moneytype = moneytype:lower()
amount = tonumber(amount)
if amount < 0 then return end
if not self.PlayerData.money[moneytype] then return false end
for _, mtype in pairs(QBCore.Config.Money.DontAllowMinus) do
if mtype == moneytype then
if (self.PlayerData.money[moneytype] - amount) < 0 then
return false
end
end
end
if self.PlayerData.money[moneytype] - amount < QBCore.Config.Money.MinusLimit then return false end
self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] - amount
if not self.Offline then
self.Functions.UpdatePlayerData()
if amount > 100000 then
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true)
else
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason)
end
TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, true)
if moneytype == 'bank' then
TriggerClientEvent('qb-phone:client:RemoveBankMoney', self.PlayerData.source, amount)
end
TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'remove', reason)
TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'remove', reason)
end
return true
end
function self.Functions.SetMoney(moneytype, amount, reason)
reason = reason or 'unknown'
moneytype = moneytype:lower()
amount = tonumber(amount)
if amount < 0 then return false end
if not self.PlayerData.money[moneytype] then return false end
local difference = amount - self.PlayerData.money[moneytype]
self.PlayerData.money[moneytype] = amount
if not self.Offline then
self.Functions.UpdatePlayerData()
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'SetMoney', 'green', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') set, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason)
TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, math.abs(difference), difference < 0)
TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'set', reason)
TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, 'set', reason)
end
return true
end
function self.Functions.GetMoney(moneytype)
if not moneytype then return false end
moneytype = moneytype:lower()
return self.PlayerData.money[moneytype]
end
function self.Functions.Save()
if self.Offline then
QBCore.Player.SaveOffline(self.PlayerData)
else
QBCore.Player.Save(self.PlayerData.source)
end
end
function self.Functions.Logout()
if self.Offline then return end
QBCore.Player.Logout(self.PlayerData.source)
end
function self.Functions.AddMethod(methodName, handler)
self.Functions[methodName] = handler
end
function self.Functions.AddField(fieldName, data)
self[fieldName] = data
end
if self.Offline then
return self
else
QBCore.Players[self.PlayerData.source] = self
QBCore.Player.Save(self.PlayerData.source)
TriggerEvent('QBCore:Server:PlayerLoaded', self)
self.Functions.UpdatePlayerData()
end
end
-- Add a new function to the Functions table of the player class
-- Use-case:
--[[
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, "functionName", function(oneArg, orMore)
-- do something here
end)
end)
]]
function QBCore.Functions.AddPlayerMethod(ids, methodName, handler)
local idType = type(ids)
if idType == 'number' then
if ids == -1 then
for _, v in pairs(QBCore.Players) do
v.Functions.AddMethod(methodName, handler)
end
else
if not QBCore.Players[ids] then return end
QBCore.Players[ids].Functions.AddMethod(methodName, handler)
end
elseif idType == 'table' and table.type(ids) == 'array' then
for i = 1, #ids do
QBCore.Functions.AddPlayerMethod(ids[i], methodName, handler)
end
end
end
-- Add a new field table of the player class
-- Use-case:
--[[
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
QBCore.Functions.AddPlayerField(Player.PlayerData.source, "fieldName", "fieldData")
end)
]]
function QBCore.Functions.AddPlayerField(ids, fieldName, data)
local idType = type(ids)
if idType == 'number' then
if ids == -1 then
for _, v in pairs(QBCore.Players) do
v.Functions.AddField(fieldName, data)
end
else
if not QBCore.Players[ids] then return end
QBCore.Players[ids].Functions.AddField(fieldName, data)
end
elseif idType == 'table' and table.type(ids) == 'array' then
for i = 1, #ids do
QBCore.Functions.AddPlayerField(ids[i], fieldName, data)
end
end
end
-- Save player info to database (make sure citizenid is the primary key in your database)
function QBCore.Player.Save(source)
local saveData = BuildPlayerSaveData(source)
if not saveData.citizenid or not saveData.license then
print('^1[ERROR]^0 Skipped saving data for player [%s] due to missing critical data.'):format(GetPlayerName(source))
return
local ped = GetPlayerPed(source)
local pcoords = GetEntityCoords(ped)
local PlayerData = QBCore.Players[source].PlayerData
if PlayerData then
MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', {
citizenid = PlayerData.citizenid,
cid = tonumber(PlayerData.cid),
license = PlayerData.license,
name = PlayerData.name,
money = json.encode(PlayerData.money),
charinfo = json.encode(PlayerData.charinfo),
job = json.encode(PlayerData.job),
gang = json.encode(PlayerData.gang),
position = json.encode(pcoords),
metadata = json.encode(PlayerData.metadata)
})
if GetResourceState('qb-inventory') ~= 'missing' then exports['qb-inventory']:SaveInventory(source) end
QBCore.ShowSuccess(resourceName, PlayerData.name .. ' PLAYER SAVED!')
else
QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.SAVE - PLAYERDATA IS EMPTY!')
end
MySQL.insert(
'INSERT INTO players (citizenid, money, metadata, position, job, gang) VALUES (:citizenid, :money, :metadata, :position, :job, :gang) ON DUPLICATE KEY UPDATE money = :money, metadata = :metadata, position = :position, job = :job, gang = :gang',
saveData
)
if GetResourceState('qb-inventory') ~= 'missing' then
exports['qb-inventory']:SaveInventory(source)
end
QBCore.ShowSuccess(resourceName, 'Player ' .. GetPlayerName(source) .. ' data saved successfully.')
end
function QBCore.Player.SaveOffline(PlayerData)
if PlayerData then
MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', {
citizenid = PlayerData.citizenid,
cid = tonumber(PlayerData.cid),
license = PlayerData.license,
name = PlayerData.name,
money = json.encode(PlayerData.money),
charinfo = json.encode(PlayerData.charinfo),
job = json.encode(PlayerData.job),
gang = json.encode(PlayerData.gang),
position = json.encode(PlayerData.position),
metadata = json.encode(PlayerData.metadata)
})
if GetResourceState('qb-inventory') ~= 'missing' then exports['qb-inventory']:SaveInventory(PlayerData, true) end
QBCore.ShowSuccess(resourceName, PlayerData.name .. ' OFFLINE PLAYER SAVED!')
else
QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.SAVEOFFLINE - PLAYERDATA IS EMPTY!')
end
end
-- Delete character
local playertables = { -- Add tables as needed
{ table = 'players' },
{ table = 'apartments' },
{ table = 'bank_accounts' },
{ table = 'crypto_transactions' },
{ table = 'phone_invoices' },
{ table = 'phone_messages' },
{ table = 'playerskins' },
{ table = 'player_contacts' },
{ table = 'player_houses' },
{ table = 'player_mails' },
{ table = 'player_outfits' },
{ table = 'player_vehicles' }
}
function QBCore.Player.DeleteCharacter(source, citizenid)
local license = QBCore.Functions.GetIdentifier(source, 'license')
local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid })
if license == result then
local query = 'DELETE FROM %s WHERE citizenid = ?'
local tableCount = #playertables
local queries = table.create(tableCount, 0)
for i = 1, tableCount do
local v = playertables[i]
queries[i] = { query = query:format(v.table), values = { citizenid } }
end
MySQL.transaction(queries, function(result2)
if result2 then
TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Character Deleted', 'red', '**' .. GetPlayerName(source) .. '** ' .. license .. ' deleted **' .. citizenid .. '**..')
end
end)
else
DropPlayer(source, Lang:t('info.exploit_dropped'))
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Deletion Exploit', true)
end
end
function QBCore.Player.ForceDeleteCharacter(citizenid)
local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid })
if result then
local query = 'DELETE FROM %s WHERE citizenid = ?'
local tableCount = #playertables
local queries = table.create(tableCount, 0)
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid)
if Player then
DropPlayer(Player.PlayerData.source, 'An admin deleted the character which you are currently using')
end
for i = 1, tableCount do
local v = playertables[i]
queries[i] = { query = query:format(v.table), values = { citizenid } }
end
MySQL.transaction(queries, function(result2)
if result2 then
TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Character Force Deleted', 'red', 'Character **' .. citizenid .. '** got deleted')
end
end)
end
end
-- Inventory Backwards Compatibility
function QBCore.Player.SaveInventory(source)
if GetResourceState('qb-inventory') == 'missing' then return end
exports['qb-inventory']:SaveInventory(source, false)
end
function QBCore.Player.SaveOfflineInventory(PlayerData)
if GetResourceState('qb-inventory') == 'missing' then return end
exports['qb-inventory']:SaveInventory(PlayerData, true)
end
function QBCore.Player.GetTotalWeight(items)
if GetResourceState('qb-inventory') == 'missing' then return end
return exports['qb-inventory']:GetTotalWeight(items)
end
function QBCore.Player.GetSlotsByItem(items, itemName)
if GetResourceState('qb-inventory') == 'missing' then return end
return exports['qb-inventory']:GetSlotsByItem(items, itemName)
end
function QBCore.Player.GetFirstSlotByItem(items, itemName)
if GetResourceState('qb-inventory') == 'missing' then return end
return exports['qb-inventory']:GetFirstSlotByItem(items, itemName)
end
-- Util Functions
function QBCore.Player.CreateCitizenId()
local CitizenId = tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper()
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE citizenid = ?) AS uniqueCheck', { CitizenId })
if result == 0 then return CitizenId end
return QBCore.Player.CreateCitizenId()
end
function QBCore.Functions.CreateAccountNumber()
local AccountNumber = 'US0' .. math.random(1, 9) .. 'QBCore' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.account")) = ?) AS uniqueCheck', { AccountNumber })
if result == 0 then return AccountNumber end
return QBCore.Functions.CreateAccountNumber()
end
function QBCore.Functions.CreatePhoneNumber()
local PhoneNumber = math.random(100, 999) .. math.random(1000000, 9999999)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.phone")) = ?) AS uniqueCheck', { PhoneNumber })
if result == 0 then return PhoneNumber end
return QBCore.Functions.CreatePhoneNumber()
end
function QBCore.Player.CreateFingerId()
local FingerId = tostring(QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(1) .. QBCore.Shared.RandomInt(2) .. QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(4))
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.fingerprint")) = ?) AS uniqueCheck', { FingerId })
if result == 0 then return FingerId end
return QBCore.Player.CreateFingerId()
end
function QBCore.Player.CreateWalletId()
local WalletId = 'QB-' .. math.random(11111111, 99999999)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.walletid")) = ?) AS uniqueCheck', { WalletId })
if result == 0 then return WalletId end
return QBCore.Player.CreateWalletId()
end
function QBCore.Player.CreateSerialNumber()
local SerialNumber = math.random(11111111, 99999999)
local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.phonedata.SerialNumber")) = ?) AS uniqueCheck', { SerialNumber })
if result == 0 then return SerialNumber end
return QBCore.Player.CreateSerialNumber()
end
PaycheckInterval() -- This starts the paycheck system
+58
View File
@@ -0,0 +1,58 @@
QBShared = QBShared or {}
QBShared.Gangs = {
none = { label = 'No Gang', grades = { ['0'] = { name = 'Unaffiliated' } } },
lostmc = {
label = 'The Lost MC',
grades = {
['0'] = { name = 'Recruit' },
['1'] = { name = 'Enforcer' },
['2'] = { name = 'Shot Caller' },
['3'] = { name = 'Boss', isboss = true },
},
},
ballas = {
label = 'Ballas',
grades = {
['0'] = { name = 'Recruit' },
['1'] = { name = 'Enforcer' },
['2'] = { name = 'Shot Caller' },
['3'] = { name = 'Boss', isboss = true },
},
},
vagos = {
label = 'Vagos',
grades = {
['0'] = { name = 'Recruit' },
['1'] = { name = 'Enforcer' },
['2'] = { name = 'Shot Caller' },
['3'] = { name = 'Boss', isboss = true },
},
},
cartel = {
label = 'Cartel',
grades = {
['0'] = { name = 'Recruit' },
['1'] = { name = 'Enforcer' },
['2'] = { name = 'Shot Caller' },
['3'] = { name = 'Boss', isboss = true },
},
},
families = {
label = 'Families',
grades = {
['0'] = { name = 'Recruit' },
['1'] = { name = 'Enforcer' },
['2'] = { name = 'Shot Caller' },
['3'] = { name = 'Boss', isboss = true },
},
},
triads = {
label = 'Triads',
grades = {
['0'] = { name = 'Recruit' },
['1'] = { name = 'Enforcer' },
['2'] = { name = 'Shot Caller' },
['3'] = { name = 'Boss', isboss = true },
},
}
}
+390
View File
@@ -0,0 +1,390 @@
QBShared = QBShared or {}
QBShared.Items = {
-- WEAPONS
-- Melee
weapon_unarmed = { name = 'weapon_unarmed', label = 'Fists', weight = 1000, type = 'weapon', ammotype = nil, image = 'placeholder.png', unique = true, useable = false, description = 'Fisticuffs' },
weapon_dagger = { name = 'weapon_dagger', label = 'Dagger', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_dagger.png', unique = true, useable = false, description = 'A short knife with a pointed and edged blade, used as a weapon' },
weapon_bat = { name = 'weapon_bat', label = 'Bat', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_bat.png', unique = true, useable = false, description = 'Used for hitting a ball in sports (or other things)' },
weapon_bottle = { name = 'weapon_bottle', label = 'Broken Bottle', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_bottle.png', unique = true, useable = false, description = 'A broken bottle' },
weapon_crowbar = { name = 'weapon_crowbar', label = 'Crowbar', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_crowbar.png', unique = true, useable = false, description = 'An iron bar with a flattened end, used as a lever' },
weapon_flashlight = { name = 'weapon_flashlight', label = 'Flashlight', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_flashlight.png', unique = true, useable = false, description = 'A battery-operated portable light' },
weapon_golfclub = { name = 'weapon_golfclub', label = 'Golfclub', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_golfclub.png', unique = true, useable = false, description = 'A club used to hit the ball in golf' },
weapon_hammer = { name = 'weapon_hammer', label = 'Hammer', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_hammer.png', unique = true, useable = false, description = 'Used for jobs such as breaking things (legs) and driving in nails' },
weapon_hatchet = { name = 'weapon_hatchet', label = 'Hatchet', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_hatchet.png', unique = true, useable = false, description = 'A small axe with a short handle for use in one hand' },
weapon_knuckle = { name = 'weapon_knuckle', label = 'Knuckle', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_knuckle.png', unique = true, useable = false, description = 'A metal guard worn over the knuckles in fighting, especially to increase the effect of the blows' },
weapon_knife = { name = 'weapon_knife', label = 'Knife', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_knife.png', unique = true, useable = false, description = 'An instrument composed of a blade fixed into a handle, used for cutting or as a weapon' },
weapon_machete = { name = 'weapon_machete', label = 'Machete', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_machete.png', unique = true, useable = false, description = 'A broad, heavy knife used as a weapon' },
weapon_switchblade = { name = 'weapon_switchblade', label = 'Switchblade', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_switchblade.png', unique = true, useable = false, description = 'A knife with a blade that springs out from the handle when a button is pressed' },
weapon_nightstick = { name = 'weapon_nightstick', label = 'Nightstick', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_nightstick.png', unique = true, useable = false, description = 'A police officer\'s club or billy' },
weapon_wrench = { name = 'weapon_wrench', label = 'Wrench', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_wrench.png', unique = true, useable = false, description = 'A tool used for gripping and turning nuts, bolts, pipes, etc' },
weapon_battleaxe = { name = 'weapon_battleaxe', label = 'Battle Axe', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_battleaxe.png', unique = true, useable = false, description = 'A large broad-bladed axe used in ancient warfare' },
weapon_poolcue = { name = 'weapon_poolcue', label = 'Poolcue', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_poolcue.png', unique = true, useable = false, description = 'A stick used to strike a ball, usually the cue ball (or other things)' },
weapon_briefcase = { name = 'weapon_briefcase', label = 'Briefcase', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_briefcase.png', unique = true, useable = false, description = 'A briefcase for storing important documents' },
weapon_briefcase_02 = { name = 'weapon_briefcase_02', label = 'Suitcase', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_briefcase2.png', unique = true, useable = false, description = 'Wonderfull for nice vacation to Liberty City' },
weapon_garbagebag = { name = 'weapon_garbagebag', label = 'Garbage Bag', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_garbagebag.png', unique = true, useable = false, description = 'A garbage bag' },
weapon_handcuffs = { name = 'weapon_handcuffs', label = 'Handcuffs', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_handcuffs.png', unique = true, useable = false, description = 'A pair of lockable linked metal rings for securing a prisoner\'s wrists' },
weapon_bread = { name = 'weapon_bread', label = 'Baquette', weight = 1000, type = 'weapon', ammotype = nil, image = 'baquette.png', unique = true, useable = false, description = 'Bread...?' },
weapon_stone_hatchet = { name = 'weapon_stone_hatchet', label = 'Stone Hatchet', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_stone_hatchet.png', unique = true, useable = true, description = 'Stone Hatchet' },
weapon_candycane = { name = 'weapon_candycane', label = 'Candy Cane', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_candycane', unique = true, useable = true, description = 'Candy Cane' },
-- Handguns
weapon_pistol = { name = 'weapon_pistol', label = 'Walther P99', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_pistol.png', unique = true, useable = false, description = 'A small firearm designed to be held in one hand' },
weapon_pistol_mk2 = { name = 'weapon_pistol_mk2', label = 'Pistol Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_pistol_mk2.png', unique = true, useable = false, description = 'An upgraded small firearm designed to be held in one hand' },
weapon_combatpistol = { name = 'weapon_combatpistol', label = 'Combat Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_combatpistol.png', unique = true, useable = false, description = 'A combat version small firearm designed to be held in one hand' },
weapon_appistol = { name = 'weapon_appistol', label = 'AP Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_appistol.png', unique = true, useable = false, description = 'A small firearm designed to be held in one hand that is automatic' },
weapon_stungun = { name = 'weapon_stungun', label = 'Taser', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_stungun.png', unique = true, useable = false, description = 'A weapon firing barbs attached by wires to batteries, causing temporary paralysis' },
weapon_pistol50 = { name = 'weapon_pistol50', label = 'Pistol .50', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_pistol50.png', unique = true, useable = false, description = 'A .50 caliber firearm designed to be held with both hands' },
weapon_snspistol = { name = 'weapon_snspistol', label = 'SNS Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_snspistol.png', unique = true, useable = false, description = 'A very small firearm designed to be easily concealed' },
weapon_heavypistol = { name = 'weapon_heavypistol', label = 'Heavy Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_heavypistol.png', unique = true, useable = false, description = 'A hefty firearm designed to be held in one hand (or attempted)' },
weapon_vintagepistol = { name = 'weapon_vintagepistol', label = 'Vintage Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_vintagepistol.png', unique = true, useable = false, description = 'An antique firearm designed to be held in one hand' },
weapon_flaregun = { name = 'weapon_flaregun', label = 'Flare Gun', weight = 1000, type = 'weapon', ammotype = 'AMMO_FLARE', image = 'weapon_flaregun.png', unique = true, useable = false, description = 'A handgun for firing signal rockets' },
weapon_marksmanpistol = { name = 'weapon_marksmanpistol', label = 'Marksman Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_marksmanpistol.png', unique = true, useable = false, description = 'A very accurate small firearm designed to be held in one hand' },
weapon_revolver = { name = 'weapon_revolver', label = 'Revolver', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_revolver.png', unique = true, useable = false, description = 'A pistol with revolving chambers enabling several shots to be fired without reloading' },
weapon_revolver_mk2 = { name = 'weapon_revolver_mk2', label = 'Violence', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_revolver_mk2.png', unique = true, useable = true, description = 'da Violence' },
weapon_doubleaction = { name = 'weapon_doubleaction', label = 'Double Action Revolver', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_doubleaction.png', unique = true, useable = true, description = 'Double Action Revolver' },
weapon_snspistol_mk2 = { name = 'weapon_snspistol_mk2', label = 'SNS Pistol Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_snspistol_mk2.png', unique = true, useable = true, description = 'SNS Pistol MK2' },
weapon_raypistol = { name = 'weapon_raypistol', label = 'Up-n-Atomizer', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_raypistol.png', unique = true, useable = true, description = 'Weapon Raypistol' },
weapon_ceramicpistol = { name = 'weapon_ceramicpistol', label = 'Ceramic Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_ceramicpistol.png', unique = true, useable = true, description = 'Weapon Ceramicpistol' },
weapon_navyrevolver = { name = 'weapon_navyrevolver', label = 'Navy Revolver', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_navyrevolver.png', unique = true, useable = true, description = 'Weapon Navyrevolver' },
weapon_gadgetpistol = { name = 'weapon_gadgetpistol', label = 'Perico Pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_gadgetpistol.png', unique = true, useable = true, description = 'Weapon Gadgetpistol' },
weapon_pistolxm3 = { name = 'weapon_pistolxm3', label = 'Pistol XM3', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_pistolxm3.png', unique = true, useable = true, description = 'Pistol XM3' },
-- Submachine Guns
weapon_microsmg = { name = 'weapon_microsmg', label = 'Micro SMG', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_microsmg.png', unique = true, useable = false, description = 'A handheld light weight machine gun' },
weapon_smg = { name = 'weapon_smg', label = 'SMG', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_smg.png', unique = true, useable = false, description = 'A handheld light weight machine gun' },
weapon_smg_mk2 = { name = 'weapon_smg_mk2', label = 'SMG Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_smg_mk2.png', unique = true, useable = true, description = 'SMG MK2' },
weapon_assaultsmg = { name = 'weapon_assaultsmg', label = 'Assault SMG', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_assaultsmg.png', unique = true, useable = false, description = 'An assault version of a handheld light weight machine gun' },
weapon_combatpdw = { name = 'weapon_combatpdw', label = 'Combat PDW', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_combatpdw.png', unique = true, useable = false, description = 'A combat version of a handheld light weight machine gun' },
weapon_machinepistol = { name = 'weapon_machinepistol', label = 'Tec-9', weight = 1000, type = 'weapon', ammotype = 'AMMO_PISTOL', image = 'weapon_machinepistol.png', unique = true, useable = false, description = 'A self-loading pistol capable of burst or fully automatic fire' },
weapon_minismg = { name = 'weapon_minismg', label = 'Mini SMG', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_minismg.png', unique = true, useable = false, description = 'A mini handheld light weight machine gun' },
weapon_raycarbine = { name = 'weapon_raycarbine', label = 'Unholy Hellbringer', weight = 1000, type = 'weapon', ammotype = 'AMMO_SMG', image = 'weapon_raycarbine.png', unique = true, useable = true, description = 'Weapon Raycarbine' },
-- Shotguns
weapon_pumpshotgun = { name = 'weapon_pumpshotgun', label = 'Pump Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_pumpshotgun.png', unique = true, useable = false, description = 'A pump-action smoothbore gun for firing small shot at short range' },
weapon_sawnoffshotgun = { name = 'weapon_sawnoffshotgun', label = 'Sawn-off Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_sawnoffshotgun.png', unique = true, useable = false, description = 'A sawn-off smoothbore gun for firing small shot at short range' },
weapon_assaultshotgun = { name = 'weapon_assaultshotgun', label = 'Assault Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_assaultshotgun.png', unique = true, useable = false, description = 'An assault version of asmoothbore gun for firing small shot at short range' },
weapon_bullpupshotgun = { name = 'weapon_bullpupshotgun', label = 'Bullpup Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_bullpupshotgun.png', unique = true, useable = false, description = 'A compact smoothbore gun for firing small shot at short range' },
weapon_musket = { name = 'weapon_musket', label = 'Musket', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_musket.png', unique = true, useable = false, description = 'An infantryman\'s light gun with a long barrel, typically smooth-bored, muzzleloading, and fired from the shoulder' },
weapon_heavyshotgun = { name = 'weapon_heavyshotgun', label = 'Heavy Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_heavyshotgun.png', unique = true, useable = false, description = 'A large smoothbore gun for firing small shot at short range' },
weapon_dbshotgun = { name = 'weapon_dbshotgun', label = 'Double-barrel Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_dbshotgun.png', unique = true, useable = false, description = 'A shotgun with two parallel barrels, allowing two single shots to be fired in quick succession' },
weapon_autoshotgun = { name = 'weapon_autoshotgun', label = 'Auto Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_autoshotgun.png', unique = true, useable = false, description = 'A shotgun capable of rapid continous fire' },
weapon_pumpshotgun_mk2 = { name = 'weapon_pumpshotgun_mk2', label = 'Pumpshotgun Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_pumpshotgun_mk2.png', unique = true, useable = true, description = 'Pumpshotgun MK2' },
weapon_combatshotgun = { name = 'weapon_combatshotgun', label = 'Combat Shotgun', weight = 1000, type = 'weapon', ammotype = 'AMMO_SHOTGUN', image = 'weapon_combatshotgun.png', unique = true, useable = true, description = 'Weapon Combatshotgun' },
-- Assault Rifles
weapon_assaultrifle = { name = 'weapon_assaultrifle', label = 'Assault Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_assaultrifle.png', unique = true, useable = false, description = 'A rapid-fire, magazine-fed automatic rifle designed for infantry use' },
weapon_assaultrifle_mk2 = { name = 'weapon_assaultrifle_mk2', label = 'Assault Rifle Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_assaultrifle_mk2.png', unique = true, useable = true, description = 'Assault Rifle MK2' },
weapon_carbinerifle = { name = 'weapon_carbinerifle', label = 'Carbine Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_carbinerifle.png', unique = true, useable = false, description = 'A light weight automatic rifle' },
weapon_carbinerifle_mk2 = { name = 'weapon_carbinerifle_mk2', label = 'Carbine Rifle Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_carbinerifle_mk2.png', unique = true, useable = true, description = 'Carbine Rifle MK2' },
weapon_advancedrifle = { name = 'weapon_advancedrifle', label = 'Advanced Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_advancedrifle.png', unique = true, useable = false, description = 'An assault version of a rapid-fire, magazine-fed automatic rifle designed for infantry use' },
weapon_specialcarbine = { name = 'weapon_specialcarbine', label = 'Special Carbine', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_specialcarbine.png', unique = true, useable = false, description = 'An extremely versatile assault rifle for any combat situation' },
weapon_bullpuprifle = { name = 'weapon_bullpuprifle', label = 'Bullpup Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_bullpuprifle.png', unique = true, useable = false, description = 'A compact automatic assault rifle' },
weapon_compactrifle = { name = 'weapon_compactrifle', label = 'Compact Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_compactrifle.png', unique = true, useable = false, description = 'A compact version of an assault rifle' },
weapon_specialcarbine_mk2 = { name = 'weapon_specialcarbine_mk2', label = 'Special Carbine Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_specialcarbine_mk2.png', unique = true, useable = true, description = 'Weapon Wpecialcarbine MK2' },
weapon_bullpuprifle_mk2 = { name = 'weapon_bullpuprifle_mk2', label = 'Bullpup Rifle Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_bullpuprifle_mk2.png', unique = true, useable = true, description = 'Bull Puprifle MK2' },
weapon_militaryrifle = { name = 'weapon_militaryrifle', label = 'Military Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_RIFLE', image = 'weapon_militaryrifle.png', unique = true, useable = true, description = 'Weapon Militaryrifle' },
-- Light Machine Guns
weapon_mg = { name = 'weapon_mg', label = 'Machinegun', weight = 1000, type = 'weapon', ammotype = 'AMMO_MG', image = 'weapon_mg.png', unique = true, useable = false, description = 'An automatic gun that fires bullets in rapid succession for as long as the trigger is pressed' },
weapon_combatmg = { name = 'weapon_combatmg', label = 'Combat MG', weight = 1000, type = 'weapon', ammotype = 'AMMO_MG', image = 'weapon_combatmg.png', unique = true, useable = false, description = 'A combat version of an automatic gun that fires bullets in rapid succession for as long as the trigger is pressed' },
weapon_gusenberg = { name = 'weapon_gusenberg', label = 'Thompson SMG', weight = 1000, type = 'weapon', ammotype = 'AMMO_MG', image = 'weapon_gusenberg.png', unique = true, useable = false, description = 'An automatic rifle commonly referred to as a tommy gun' },
weapon_combatmg_mk2 = { name = 'weapon_combatmg_mk2', label = 'Combat MG Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_MG', image = 'weapon_combatmg_mk2.png', unique = true, useable = true, description = 'Weapon Combatmg MK2' },
-- Sniper Rifles
weapon_sniperrifle = { name = 'weapon_sniperrifle', label = 'Sniper Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_SNIPER', image = 'weapon_sniperrifle.png', unique = true, useable = false, description = 'A high-precision, long-range rifle' },
weapon_heavysniper = { name = 'weapon_heavysniper', label = 'Heavy Sniper', weight = 1000, type = 'weapon', ammotype = 'AMMO_SNIPER', image = 'weapon_heavysniper.png', unique = true, useable = false, description = 'An upgraded high-precision, long-range rifle' },
weapon_marksmanrifle = { name = 'weapon_marksmanrifle', label = 'Marksman Rifle', weight = 1000, type = 'weapon', ammotype = 'AMMO_SNIPER', image = 'weapon_marksmanrifle.png', unique = true, useable = false, description = 'A very accurate single-fire rifle' },
weapon_remotesniper = { name = 'weapon_remotesniper', label = 'Remote Sniper', weight = 1000, type = 'weapon', ammotype = 'AMMO_SNIPER_REMOTE', image = 'weapon_remotesniper.png', unique = true, useable = false, description = 'A portable high-precision, long-range rifle' },
weapon_heavysniper_mk2 = { name = 'weapon_heavysniper_mk2', label = 'Heavy Sniper Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_SNIPER', image = 'weapon_heavysniper_mk2.png', unique = true, useable = true, description = 'Weapon Heavysniper MK2' },
weapon_marksmanrifle_mk2 = { name = 'weapon_marksmanrifle_mk2', label = 'Marksman Rifle Mk II', weight = 1000, type = 'weapon', ammotype = 'AMMO_SNIPER', image = 'weapon_marksmanrifle_mk2.png', unique = true, useable = true, description = 'Weapon Marksmanrifle MK2' },
-- Heavy Weapons
weapon_rpg = { name = 'weapon_rpg', label = 'RPG', weight = 1000, type = 'weapon', ammotype = 'AMMO_RPG', image = 'weapon_rpg.png', unique = true, useable = false, description = 'A rocket-propelled grenade launcher' },
weapon_grenadelauncher = { name = 'weapon_grenadelauncher', label = 'Grenade Launcher', weight = 1000, type = 'weapon', ammotype = 'AMMO_GRENADELAUNCHER', image = 'weapon_grenadelauncher.png', unique = true, useable = false, description = 'A weapon that fires a specially-designed large-caliber projectile, often with an explosive, smoke or gas warhead' },
weapon_grenadelauncher_smoke = { name = 'weapon_grenadelauncher_smoke', label = 'Smoke Grenade Launcher', weight = 1000, type = 'weapon', ammotype = 'AMMO_GRENADELAUNCHER', image = 'weapon_smokegrenade.png', unique = true, useable = false, description = 'A bomb that produces a lot of smoke when it explodes' },
weapon_minigun = { name = 'weapon_minigun', label = 'Minigun', weight = 1000, type = 'weapon', ammotype = 'AMMO_MINIGUN', image = 'weapon_minigun.png', unique = true, useable = false, description = 'A portable machine gun consisting of a rotating cluster of six barrels and capable of variable rates of fire of up to 6,000 rounds per minute' },
weapon_firework = { name = 'weapon_firework', label = 'Firework Launcher', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_firework.png', unique = true, useable = false, description = 'A device containing gunpowder and other combustible chemicals that causes a spectacular explosion when ignited' },
weapon_railgun = { name = 'weapon_railgun', label = 'Railgun', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_railgun.png', unique = true, useable = false, description = 'A weapon that uses electromagnetic force to launch high velocity projectiles' },
weapon_railgunxm3 = { name = 'weapon_railgunxm3', label = 'Railgun XM3', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_railgunxm3.png', unique = true, useable = false, description = 'A weapon that uses electromagnetic force to launch high velocity projectiles' },
weapon_hominglauncher = { name = 'weapon_hominglauncher', label = 'Homing Launcher', weight = 1000, type = 'weapon', ammotype = 'AMMO_STINGER', image = 'weapon_hominglauncher.png', unique = true, useable = false, description = 'A weapon fitted with an electronic device that enables it to find and hit a target' },
weapon_compactlauncher = { name = 'weapon_compactlauncher', label = 'Compact Launcher', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_compactlauncher.png', unique = true, useable = false, description = 'A compact grenade launcher' },
weapon_rayminigun = { name = 'weapon_rayminigun', label = 'Widowmaker', weight = 1000, type = 'weapon', ammotype = 'AMMO_MINIGUN', image = 'weapon_rayminigun.png', unique = true, useable = true, description = 'Weapon Rayminigun' },
-- Throwables
weapon_grenade = { name = 'weapon_grenade', label = 'Grenade', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_grenade.png', unique = true, useable = false, description = 'A handheld throwable bomb' },
weapon_bzgas = { name = 'weapon_bzgas', label = 'BZ Gas', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_bzgas.png', unique = true, useable = false, description = 'A cannister of gas that causes extreme pain' },
weapon_molotov = { name = 'weapon_molotov', label = 'Molotov', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_molotov.png', unique = true, useable = false, description = 'A crude bomb made of a bottle filled with a flammable liquid and fitted with a wick for lighting' },
weapon_stickybomb = { name = 'weapon_stickybomb', label = 'C4', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_stickybomb.png', unique = true, useable = false, description = 'An explosive charge covered with an adhesive that when thrown against an object sticks until it explodes' },
weapon_proxmine = { name = 'weapon_proxmine', label = 'Proxmine Grenade', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_proximitymine.png', unique = true, useable = false, description = 'A bomb placed on the ground that detonates when going within its proximity' },
weapon_snowball = { name = 'weapon_snowball', label = 'Snowball', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_snowball.png', unique = true, useable = false, description = 'A ball of packed snow, especially one made for throwing at other people for fun' },
weapon_pipebomb = { name = 'weapon_pipebomb', label = 'Pipe Bomb', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_pipebomb.png', unique = true, useable = false, description = 'A homemade bomb, the components of which are contained in a pipe' },
weapon_ball = { name = 'weapon_ball', label = 'Ball', weight = 1000, type = 'weapon', ammotype = 'AMMO_BALL', image = 'weapon_ball.png', unique = true, useable = false, description = 'A solid or hollow spherical or egg-shaped object that is kicked, thrown, or hit in a game' },
weapon_smokegrenade = { name = 'weapon_smokegrenade', label = 'Smoke Grenade', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_c4.png', unique = true, useable = false, description = 'An explosive charge that can be remotely detonated' },
weapon_flare = { name = 'weapon_flare', label = 'Flare pistol', weight = 1000, type = 'weapon', ammotype = 'AMMO_FLARE', image = 'weapon_flare.png', unique = true, useable = false, description = 'A small pyrotechnic devices used for illumination and signalling' },
-- Miscellaneous
weapon_petrolcan = { name = 'weapon_petrolcan', label = 'Petrol Can', weight = 1000, type = 'weapon', ammotype = 'AMMO_PETROLCAN', image = 'weapon_petrolcan.png', unique = true, useable = false, description = 'A robust liquid container made from pressed steel' },
weapon_fireextinguisher = { name = 'weapon_fireextinguisher', label = 'Fire Extinguisher', weight = 1000, type = 'weapon', ammotype = nil, image = 'weapon_fireextinguisher.png', unique = true, useable = false, description = 'A portable device that discharges a jet of water, foam, gas, or other material to extinguish a fire' },
weapon_hazardcan = { name = 'weapon_hazardcan', label = 'Hazardous Jerry Can', weight = 1000, type = 'weapon', ammotype = 'AMMO_PETROLCAN', image = 'weapon_hazardcan.png', unique = true, useable = true, description = 'Weapon Hazardcan' },
-- Weapon Attachments
clip_attachment = { name = 'clip_attachment', label = 'Clip', weight = 1000, type = 'item', image = 'clip_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A clip for a weapon' },
drum_attachment = { name = 'drum_attachment', label = 'Drum', weight = 1000, type = 'item', image = 'drum_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A drum for a weapon' },
flashlight_attachment = { name = 'flashlight_attachment', label = 'Flashlight', weight = 1000, type = 'item', image = 'flashlight_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A flashlight for a weapon' },
suppressor_attachment = { name = 'suppressor_attachment', label = 'Suppressor', weight = 1000, type = 'item', image = 'suppressor_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A suppressor for a weapon' },
smallscope_attachment = { name = 'smallscope_attachment', label = 'Small Scope', weight = 1000, type = 'item', image = 'smallscope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A small scope for a weapon' },
medscope_attachment = { name = 'medscope_attachment', label = 'Medium Scope', weight = 1000, type = 'item', image = 'medscope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A medium scope for a weapon' },
largescope_attachment = { name = 'largescope_attachment', label = 'Large Scope', weight = 1000, type = 'item', image = 'largescope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A large scope for a weapon' },
holoscope_attachment = { name = 'holoscope_attachment', label = 'Holo Scope', weight = 1000, type = 'item', image = 'holoscope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A holo scope for a weapon' },
advscope_attachment = { name = 'advscope_attachment', label = 'Advanced Scope', weight = 1000, type = 'item', image = 'advscope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'An advanced scope for a weapon' },
nvscope_attachment = { name = 'nvscope_attachment', label = 'Night Vision Scope', weight = 1000, type = 'item', image = 'nvscope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A night vision scope for a weapon' },
thermalscope_attachment = { name = 'thermalscope_attachment', label = 'Thermal Scope', weight = 1000, type = 'item', image = 'thermalscope_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A thermal scope for a weapon' },
flat_muzzle_brake = { name = 'flat_muzzle_brake', label = 'Flat Muzzle Brake', weight = 1000, type = 'item', image = 'flat_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
tactical_muzzle_brake = { name = 'tactical_muzzle_brake', label = 'Tactical Muzzle Brake', weight = 1000, type = 'item', image = 'tactical_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brakee for a weapon' },
fat_end_muzzle_brake = { name = 'fat_end_muzzle_brake', label = 'Fat End Muzzle Brake', weight = 1000, type = 'item', image = 'fat_end_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
precision_muzzle_brake = { name = 'precision_muzzle_brake', label = 'Precision Muzzle Brake', weight = 1000, type = 'item', image = 'precision_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
heavy_duty_muzzle_brake = { name = 'heavy_duty_muzzle_brake', label = 'HD Muzzle Brake', weight = 1000, type = 'item', image = 'heavy_duty_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
slanted_muzzle_brake = { name = 'slanted_muzzle_brake', label = 'Slanted Muzzle Brake', weight = 1000, type = 'item', image = 'slanted_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
split_end_muzzle_brake = { name = 'split_end_muzzle_brake', label = 'Split End Muzzle Brake', weight = 1000, type = 'item', image = 'split_end_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
squared_muzzle_brake = { name = 'squared_muzzle_brake', label = 'Squared Muzzle Brake', weight = 1000, type = 'item', image = 'squared_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
bellend_muzzle_brake = { name = 'bellend_muzzle_brake', label = 'Bellend Muzzle Brake', weight = 1000, type = 'item', image = 'bellend_muzzle_brake.png', unique = false, useable = true, shouldClose = true, description = 'A muzzle brake for a weapon' },
barrel_attachment = { name = 'barrel_attachment', label = 'Barrel', weight = 1000, type = 'item', image = 'barrel_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A barrel for a weapon' },
grip_attachment = { name = 'grip_attachment', label = 'Grip', weight = 1000, type = 'item', image = 'grip_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A grip for a weapon' },
comp_attachment = { name = 'comp_attachment', label = 'Compensator', weight = 1000, type = 'item', image = 'comp_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A compensator for a weapon' },
luxuryfinish_attachment = { name = 'luxuryfinish_attachment', label = 'Luxury Finish', weight = 1000, type = 'item', image = 'luxuryfinish_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A luxury finish for a weapon' },
digicamo_attachment = { name = 'digicamo_attachment', label = 'Digital Camo', weight = 1000, type = 'item', image = 'digicamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A digital camo for a weapon' },
brushcamo_attachment = { name = 'brushcamo_attachment', label = 'Brushstroke Camo', weight = 1000, type = 'item', image = 'brushcamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A brushstroke camo for a weapon' },
woodcamo_attachment = { name = 'woodcamo_attachment', label = 'Woodland Camo', weight = 1000, type = 'item', image = 'woodcamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A woodland camo for a weapon' },
skullcamo_attachment = { name = 'skullcamo_attachment', label = 'Skull Camo', weight = 1000, type = 'item', image = 'skullcamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A skull camo for a weapon' },
sessantacamo_attachment = { name = 'sessantacamo_attachment', label = 'Sessanta Nove Camo', weight = 1000, type = 'item', image = 'sessantacamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A sessanta nove camo for a weapon' },
perseuscamo_attachment = { name = 'perseuscamo_attachment', label = 'Perseus Camo', weight = 1000, type = 'item', image = 'perseuscamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A perseus camo for a weapon' },
leopardcamo_attachment = { name = 'leopardcamo_attachment', label = 'Leopard Camo', weight = 1000, type = 'item', image = 'leopardcamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A leopard camo for a weapon' },
zebracamo_attachment = { name = 'zebracamo_attachment', label = 'Zebra Camo', weight = 1000, type = 'item', image = 'zebracamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A zebra camo for a weapon' },
geocamo_attachment = { name = 'geocamo_attachment', label = 'Geometric Camo', weight = 1000, type = 'item', image = 'geocamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A geometric camo for a weapon' },
boomcamo_attachment = { name = 'boomcamo_attachment', label = 'Boom Camo', weight = 1000, type = 'item', image = 'boomcamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A boom camo for a weapon' },
patriotcamo_attachment = { name = 'patriotcamo_attachment', label = 'Patriot Camo', weight = 1000, type = 'item', image = 'patriotcamo_attachment.png', unique = false, useable = true, shouldClose = true, description = 'A patriot camo for a weapon' },
-- Weapon Tints
weapontint_0 = { name = 'weapontint_0', label = 'Default Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Default/Black Weapon Tint' },
weapontint_1 = { name = 'weapontint_1', label = 'Green Tint', weight = 1000, type = 'item', image = 'weapontint_green.png', unique = false, useable = true, shouldClose = true, description = 'Green Weapon Tint' },
weapontint_2 = { name = 'weapontint_2', label = 'Gold Tint', weight = 1000, type = 'item', image = 'weapontint_gold.png', unique = false, useable = true, shouldClose = true, description = 'Gold Weapon Tint' },
weapontint_3 = { name = 'weapontint_3', label = 'Pink Tint', weight = 1000, type = 'item', image = 'weapontint_pink.png', unique = false, useable = true, shouldClose = true, description = 'Pink Weapon Tint' },
weapontint_4 = { name = 'weapontint_4', label = 'Army Tint', weight = 1000, type = 'item', image = 'weapontint_army.png', unique = false, useable = true, shouldClose = true, description = 'Army Weapon Tint' },
weapontint_5 = { name = 'weapontint_5', label = 'LSPD Tint', weight = 1000, type = 'item', image = 'weapontint_lspd.png', unique = false, useable = true, shouldClose = true, description = 'LSPD Weapon Tint' },
weapontint_6 = { name = 'weapontint_6', label = 'Orange Tint', weight = 1000, type = 'item', image = 'weapontint_orange.png', unique = false, useable = true, shouldClose = true, description = 'Orange Weapon Tint' },
weapontint_7 = { name = 'weapontint_7', label = 'Platinum Tint', weight = 1000, type = 'item', image = 'weapontint_plat.png', unique = false, useable = true, shouldClose = true, description = 'Platinum Weapon Tint' },
weapontint_mk2_0 = { name = 'weapontint_mk2_0', label = 'Classic Black Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Black Weapon Tint for MK2 Weapons' },
weapontint_mk2_1 = { name = 'weapontint_mk2_1', label = 'Classic Gray Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Gray Weapon Tint for MK2 Weapons' },
weapontint_mk2_2 = { name = 'weapontint_mk2_2', label = 'Classic Two-Tone Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Two-Tone Weapon Tint for MK2 Weapons' },
weapontint_mk2_3 = { name = 'weapontint_mk2_3', label = 'Classic White Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic White Weapon Tint for MK2 Weapons' },
weapontint_mk2_4 = { name = 'weapontint_mk2_4', label = 'Classic Beige Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Beige Weapon Tint for MK2 Weapons' },
weapontint_mk2_5 = { name = 'weapontint_mk2_5', label = 'Classic Green Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Green Weapon Tint for MK2 Weapons' },
weapontint_mk2_6 = { name = 'weapontint_mk2_6', label = 'Classic Blue Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Blue Weapon Tint for MK2 Weapons' },
weapontint_mk2_7 = { name = 'weapontint_mk2_7', label = 'Classic Earth Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Earth Weapon Tint for MK2 Weapons' },
weapontint_mk2_8 = { name = 'weapontint_mk2_8', label = 'Classic Brown & Black Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Classic Brown & Black Weapon Tint for MK2 Weapons' },
weapontint_mk2_9 = { name = 'weapontint_mk2_9', label = 'Red Contrast Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Red Contrast Weapon Tint for MK2 Weapons' },
weapontint_mk2_10 = { name = 'weapontint_mk2_10', label = 'Blue Contrast Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Blue Contrast Weapon Tint for MK2 Weapons' },
weapontint_mk2_11 = { name = 'weapontint_mk2_11', label = 'Yellow Contrast Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Yellow Contrast Weapon Tint for MK2 Weapons' },
weapontint_mk2_12 = { name = 'weapontint_mk2_12', label = 'Orange Contrast Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Orange Contrast Weapon Tint for MK2 Weapons' },
weapontint_mk2_13 = { name = 'weapontint_mk2_13', label = 'Bold Pink Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Pink Weapon Tint for MK2 Weapons' },
weapontint_mk2_14 = { name = 'weapontint_mk2_14', label = 'Bold Purple & Yellow Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Purple & Yellow Weapon Tint for MK2 Weapons' },
weapontint_mk2_15 = { name = 'weapontint_mk2_15', label = 'Bold Orange Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Orange Weapon Tint for MK2 Weapons' },
weapontint_mk2_16 = { name = 'weapontint_mk2_16', label = 'Bold Green & Purple Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Green & Purple Weapon Tint for MK2 Weapons' },
weapontint_mk2_17 = { name = 'weapontint_mk2_17', label = 'Bold Red Features Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Red Features Weapon Tint for MK2 Weapons' },
weapontint_mk2_18 = { name = 'weapontint_mk2_18', label = 'Bold Green Features Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Green Features Weapon Tint for MK2 Weapons' },
weapontint_mk2_19 = { name = 'weapontint_mk2_19', label = 'Bold Cyan Features Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Cyan Features Weapon Tint for MK2 Weapons' },
weapontint_mk2_20 = { name = 'weapontint_mk2_20', label = 'Bold Yellow Features Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Yellow Features Weapon Tint for MK2 Weapons' },
weapontint_mk2_21 = { name = 'weapontint_mk2_21', label = 'Bold Red & White Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Red & White Weapon Tint for MK2 Weapons' },
weapontint_mk2_22 = { name = 'weapontint_mk2_22', label = 'Bold Blue & White Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Bold Blue & White Weapon Tint for MK2 Weapons' },
weapontint_mk2_23 = { name = 'weapontint_mk2_23', label = 'Metallic Gold Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Gold Weapon Tint for MK2 Weapons' },
weapontint_mk2_24 = { name = 'weapontint_mk2_24', label = 'Metallic Platinum Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Platinum Weapon Tint for MK2 Weapons' },
weapontint_mk2_25 = { name = 'weapontint_mk2_25', label = 'Metallic Gray & Lilac Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Gray & Lilac Weapon Tint for MK2 Weapons' },
weapontint_mk2_26 = { name = 'weapontint_mk2_26', label = 'Metallic Purple & Lime Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Purple & Lime Weapon Tint for MK2 Weapons' },
weapontint_mk2_27 = { name = 'weapontint_mk2_27', label = 'Metallic Red Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Red Weapon Tint for MK2 Weapons' },
weapontint_mk2_28 = { name = 'weapontint_mk2_28', label = 'Metallic Green Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Green Weapon Tint for MK2 Weapons' },
weapontint_mk2_29 = { name = 'weapontint_mk2_29', label = 'Metallic Blue Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Blue Weapon Tint for MK2 Weapons' },
weapontint_mk2_30 = { name = 'weapontint_mk2_30', label = 'Metallic White & Aqua Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic White & Aqua Weapon Tint for MK2 Weapons' },
weapontint_mk2_31 = { name = 'weapontint_mk2_31', label = 'Metallic Orange & Yellow Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Orange & Yellow Weapon Tint for MK2 Weapons' },
weapontint_mk2_32 = { name = 'weapontint_mk2_32', label = 'Metallic Red and Yellow Tint', weight = 1000, type = 'item', image = 'weapontint_black.png', unique = false, useable = true, shouldClose = true, description = 'Metallic Red and Yellow Weapon Tint for MK2 Weapons' },
-- ITEMS
-- Ammo ITEMS
pistol_ammo = { name = 'pistol_ammo', label = 'Pistol ammo', weight = 200, type = 'item', image = 'pistol_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for Pistols' },
rifle_ammo = { name = 'rifle_ammo', label = 'Rifle ammo', weight = 1000, type = 'item', image = 'rifle_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for Rifles' },
smg_ammo = { name = 'smg_ammo', label = 'SMG ammo', weight = 500, type = 'item', image = 'smg_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for Sub Machine Guns' },
shotgun_ammo = { name = 'shotgun_ammo', label = 'Shotgun ammo', weight = 500, type = 'item', image = 'shotgun_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for Shotguns' },
mg_ammo = { name = 'mg_ammo', label = 'MG ammo', weight = 1000, type = 'item', image = 'mg_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for Machine Guns' },
snp_ammo = { name = 'snp_ammo', label = 'Sniper ammo', weight = 1000, type = 'item', image = 'rifle_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for Sniper Rifles' },
emp_ammo = { name = 'emp_ammo', label = 'EMP Ammo', weight = 200, type = 'item', image = 'emp_ammo.png', unique = false, useable = true, shouldClose = true, description = 'Ammo for EMP Launcher' },
-- Card ITEMS
id_card = { name = 'id_card', label = 'ID Card', weight = 0, type = 'item', image = 'id_card.png', unique = true, useable = true, shouldClose = false, description = 'A card containing all your information to identify yourself' },
driver_license = { name = 'driver_license', label = 'Drivers License', weight = 0, type = 'item', image = 'driver_license.png', unique = true, useable = true, shouldClose = false, description = 'Permit to show you can drive a vehicle' },
lawyerpass = { name = 'lawyerpass', label = 'Lawyer Pass', weight = 0, type = 'item', image = 'lawyerpass.png', unique = true, useable = true, shouldClose = false, description = 'Pass exclusive to lawyers to show they can represent a suspect' },
weaponlicense = { name = 'weaponlicense', label = 'Weapon License', weight = 0, type = 'item', image = 'weapon_license.png', unique = true, useable = true, shouldClose = true, description = 'Weapon License' },
bank_card = { name = 'bank_card', label = 'Bank Card', weight = 0, type = 'item', image = 'bank_card.png', unique = true, useable = true, shouldClose = true, description = 'Used to access ATM' },
security_card_01 = { name = 'security_card_01', label = 'Security Card A', weight = 0, type = 'item', image = 'security_card_01.png', unique = false, useable = true, shouldClose = true, description = 'A security card... I wonder what it goes to' },
security_card_02 = { name = 'security_card_02', label = 'Security Card B', weight = 0, type = 'item', image = 'security_card_02.png', unique = false, useable = true, shouldClose = true, description = 'A security card... I wonder what it goes to' },
-- Eat ITEMS
tosti = { name = 'tosti', label = 'Grilled Cheese Sandwich', weight = 200, type = 'item', image = 'tosti.png', unique = false, useable = true, shouldClose = true, description = 'Nice to eat' },
twerks_candy = { name = 'twerks_candy', label = 'Twerks', weight = 100, type = 'item', image = 'twerks_candy.png', unique = false, useable = true, shouldClose = true, description = 'Some delicious candy :O' },
snikkel_candy = { name = 'snikkel_candy', label = 'Snikkel', weight = 100, type = 'item', image = 'snikkel_candy.png', unique = false, useable = true, shouldClose = true, description = 'Some delicious candy :O' },
sandwich = { name = 'sandwich', label = 'Sandwich', weight = 200, type = 'item', image = 'sandwich.png', unique = false, useable = true, shouldClose = true, description = 'Nice bread for your stomach' },
-- Drink ITEMS
water_bottle = { name = 'water_bottle', label = 'Bottle of Water', weight = 500, type = 'item', image = 'water_bottle.png', unique = false, useable = true, shouldClose = true, description = 'For all the thirsty out there' },
coffee = { name = 'coffee', label = 'Coffee', weight = 200, type = 'item', image = 'coffee.png', unique = false, useable = true, shouldClose = true, description = 'Pump 4 Caffeine' },
kurkakola = { name = 'kurkakola', label = 'Cola', weight = 500, type = 'item', image = 'cola.png', unique = false, useable = true, shouldClose = true, description = 'For all the thirsty out there' },
-- Alcohol
beer = { name = 'beer', label = 'Beer', weight = 500, type = 'item', image = 'beer.png', unique = false, useable = true, shouldClose = true, description = 'Nothing like a good cold beer!' },
whiskey = { name = 'whiskey', label = 'Whiskey', weight = 500, type = 'item', image = 'whiskey.png', unique = false, useable = true, shouldClose = true, description = 'For all the thirsty out there' },
vodka = { name = 'vodka', label = 'Vodka', weight = 500, type = 'item', image = 'vodka.png', unique = false, useable = true, shouldClose = true, description = 'For all the thirsty out there' },
grape = { name = 'grape', label = 'Grape', weight = 100, type = 'item', image = 'grape.png', unique = false, useable = true, shouldClose = false, description = 'Mmmmh yummie, grapes' },
wine = { name = 'wine', label = 'Wine', weight = 300, type = 'item', image = 'wine.png', unique = false, useable = true, shouldClose = false, description = 'Some good wine to drink on a fine evening' },
grapejuice = { name = 'grapejuice', label = 'Grape Juice', weight = 200, type = 'item', image = 'grapejuice.png', unique = false, useable = true, shouldClose = false, description = 'Grape juice is said to be healthy' },
-- Drugs
joint = { name = 'joint', label = 'Joint', weight = 0, type = 'item', image = 'joint.png', unique = false, useable = true, shouldClose = true, description = 'Sidney would be very proud at you' },
cokebaggy = { name = 'cokebaggy', label = 'Bag of Coke', weight = 0, type = 'item', image = 'cocaine_baggy.png', unique = false, useable = true, shouldClose = true, description = 'To get happy real quick' },
crack_baggy = { name = 'crack_baggy', label = 'Bag of Crack', weight = 0, type = 'item', image = 'crack_baggy.png', unique = false, useable = true, shouldClose = true, description = 'To get happy faster' },
xtcbaggy = { name = 'xtcbaggy', label = 'Bag of XTC', weight = 0, type = 'item', image = 'xtc_baggy.png', unique = false, useable = true, shouldClose = true, description = 'Pop those pills baby' },
coke_brick = { name = 'coke_brick', label = 'Coke Brick', weight = 1000, type = 'item', image = 'coke_brick.png', unique = true, useable = false, shouldClose = true, description = 'Heavy package of cocaine, mostly used for deals and takes a lot of space' },
weed_brick = { name = 'weed_brick', label = 'Weed Brick', weight = 1000, type = 'item', image = 'weed_brick.png', unique = false, useable = false, shouldClose = true, description = '1KG Weed Brick to sell to large customers.' },
coke_small_brick = { name = 'coke_small_brick', label = 'Coke Package', weight = 350, type = 'item', image = 'coke_small_brick.png', unique = true, useable = false, shouldClose = true, description = 'Small package of cocaine, mostly used for deals and takes a lot of space' },
oxy = { name = 'oxy', label = 'Prescription Oxy', weight = 0, type = 'item', image = 'oxy.png', unique = false, useable = true, shouldClose = true, description = 'The Label Has Been Ripped Off' },
meth = { name = 'meth', label = 'Meth', weight = 100, type = 'item', image = 'meth_baggy.png', unique = false, useable = true, shouldClose = true, description = 'A baggie of Meth' },
rolling_paper = { name = 'rolling_paper', label = 'Rolling Paper', weight = 0, type = 'item', image = 'rolling_paper.png', unique = false, useable = false, shouldClose = true, description = 'Paper made specifically for encasing and smoking tobacco or cannabis.' },
-- Seed And Weed
weed_whitewidow = { name = 'weed_whitewidow', label = 'White Widow 2g', weight = 200, type = 'item', image = 'weed_baggy.png', unique = false, useable = true, shouldClose = false, description = 'A weed bag with 2g White Widow' },
weed_skunk = { name = 'weed_skunk', label = 'Skunk 2g', weight = 200, type = 'item', image = 'weed_baggy.png', unique = false, useable = true, shouldClose = false, description = 'A weed bag with 2g Skunk' },
weed_purplehaze = { name = 'weed_purplehaze', label = 'Purple Haze 2g', weight = 200, type = 'item', image = 'weed_baggy.png', unique = false, useable = true, shouldClose = false, description = 'A weed bag with 2g Purple Haze' },
weed_ogkush = { name = 'weed_ogkush', label = 'OGKush 2g', weight = 200, type = 'item', image = 'weed_baggy.png', unique = false, useable = true, shouldClose = false, description = 'A weed bag with 2g OG Kush' },
weed_amnesia = { name = 'weed_amnesia', label = 'Amnesia 2g', weight = 200, type = 'item', image = 'weed_baggy.png', unique = false, useable = true, shouldClose = false, description = 'A weed bag with 2g Amnesia' },
weed_ak47 = { name = 'weed_ak47', label = 'AK47 2g', weight = 200, type = 'item', image = 'weed_baggy.png', unique = false, useable = true, shouldClose = false, description = 'A weed bag with 2g AK47' },
weed_whitewidow_seed = { name = 'weed_whitewidow_seed', label = 'White Widow Seed', weight = 0, type = 'item', image = 'weed_seed.png', unique = false, useable = true, shouldClose = false, description = 'A weed seed of White Widow' },
weed_skunk_seed = { name = 'weed_skunk_seed', label = 'Skunk Seed', weight = 0, type = 'item', image = 'weed_seed.png', unique = false, useable = true, shouldClose = true, description = 'A weed seed of Skunk' },
weed_purplehaze_seed = { name = 'weed_purplehaze_seed', label = 'Purple Haze Seed', weight = 0, type = 'item', image = 'weed_seed.png', unique = false, useable = true, shouldClose = true, description = 'A weed seed of Purple Haze' },
weed_ogkush_seed = { name = 'weed_ogkush_seed', label = 'OGKush Seed', weight = 0, type = 'item', image = 'weed_seed.png', unique = false, useable = true, shouldClose = true, description = 'A weed seed of OG Kush' },
weed_amnesia_seed = { name = 'weed_amnesia_seed', label = 'Amnesia Seed', weight = 0, type = 'item', image = 'weed_seed.png', unique = false, useable = true, shouldClose = true, description = 'A weed seed of Amnesia' },
weed_ak47_seed = { name = 'weed_ak47_seed', label = 'AK47 Seed', weight = 0, type = 'item', image = 'weed_seed.png', unique = false, useable = true, shouldClose = true, description = 'A weed seed of AK47' },
empty_weed_bag = { name = 'empty_weed_bag', label = 'Empty Weed Bag', weight = 0, type = 'item', image = 'weed_baggy_empty.png', unique = false, useable = true, shouldClose = true, description = 'A small empty bag' },
weed_nutrition = { name = 'weed_nutrition', label = 'Plant Fertilizer', weight = 2000, type = 'item', image = 'weed_nutrition.png', unique = false, useable = true, shouldClose = true, description = 'Plant nutrition' },
-- Material
plastic = { name = 'plastic', label = 'Plastic', weight = 100, type = 'item', image = 'plastic.png', unique = false, useable = false, shouldClose = false, description = 'RECYCLE! - Greta Thunberg 2019' },
metalscrap = { name = 'metalscrap', label = 'Metal Scrap', weight = 100, type = 'item', image = 'metalscrap.png', unique = false, useable = false, shouldClose = false, description = 'You can probably make something nice out of this' },
copper = { name = 'copper', label = 'Copper', weight = 100, type = 'item', image = 'copper.png', unique = false, useable = false, shouldClose = false, description = 'Nice piece of metal that you can probably use for something' },
aluminum = { name = 'aluminum', label = 'Aluminium', weight = 100, type = 'item', image = 'aluminum.png', unique = false, useable = false, shouldClose = false, description = 'Nice piece of metal that you can probably use for something' },
aluminumoxide = { name = 'aluminumoxide', label = 'Aluminium Powder', weight = 100, type = 'item', image = 'aluminumoxide.png', unique = false, useable = false, shouldClose = false, description = 'Some powder to mix with' },
iron = { name = 'iron', label = 'Iron', weight = 100, type = 'item', image = 'iron.png', unique = false, useable = false, shouldClose = false, description = 'Handy piece of metal that you can probably use for something' },
ironoxide = { name = 'ironoxide', label = 'Iron Powder', weight = 100, type = 'item', image = 'ironoxide.png', unique = false, useable = false, shouldClose = false, description = 'Some powder to mix with.' },
steel = { name = 'steel', label = 'Steel', weight = 100, type = 'item', image = 'steel.png', unique = false, useable = false, shouldClose = false, description = 'Nice piece of metal that you can probably use for something' },
rubber = { name = 'rubber', label = 'Rubber', weight = 100, type = 'item', image = 'rubber.png', unique = false, useable = false, shouldClose = false, description = 'Rubber, I believe you can make your own rubber ducky with it :D' },
glass = { name = 'glass', label = 'Glass', weight = 100, type = 'item', image = 'glass.png', unique = false, useable = false, shouldClose = false, description = 'It is very fragile, watch out' },
-- Tools
lockpick = { name = 'lockpick', label = 'Lockpick', weight = 300, type = 'item', image = 'lockpick.png', unique = false, useable = true, shouldClose = true, description = 'Very useful if you lose your keys a lot.. or if you want to use it for something else...' },
advancedlockpick = { name = 'advancedlockpick', label = 'Advanced Lockpick', weight = 500, type = 'item', image = 'advancedlockpick.png', unique = false, useable = true, shouldClose = true, description = 'If you lose your keys a lot this is very useful... Also useful to open your beers' },
electronickit = { name = 'electronickit', label = 'Electronic Kit', weight = 100, type = 'item', image = 'electronickit.png', unique = false, useable = true, shouldClose = true, description = 'If you\'ve always wanted to build a robot you can maybe start here. Maybe you\'ll be the new Elon Musk?' },
gatecrack = { name = 'gatecrack', label = 'Gatecrack', weight = 0, type = 'item', image = 'usb_device.png', unique = false, useable = false, shouldClose = true, description = 'Handy software to tear down some fences' },
thermite = { name = 'thermite', label = 'Thermite', weight = 1000, type = 'item', image = 'thermite.png', unique = false, useable = true, shouldClose = true, description = 'Sometimes you\'d wish for everything to burn' },
trojan_usb = { name = 'trojan_usb', label = 'Trojan USB', weight = 0, type = 'item', image = 'usb_device.png', unique = false, useable = false, shouldClose = true, description = 'Handy software to shut down some systems' },
screwdriverset = { name = 'screwdriverset', label = 'Toolkit', weight = 1000, type = 'item', image = 'screwdriverset.png', unique = false, useable = false, shouldClose = false, description = 'Very useful to screw... screws...' },
drill = { name = 'drill', label = 'Drill', weight = 20000, type = 'item', image = 'drill.png', unique = false, useable = false, shouldClose = false, description = 'The real deal...' },
-- Vehicle Tools
nitrous = { name = 'nitrous', label = 'Nitrous', weight = 1000, type = 'item', image = 'nitrous.png', unique = false, useable = true, shouldClose = true, description = 'Speed up, gas pedal! :D' },
repairkit = { name = 'repairkit', label = 'Repairkit', weight = 2500, type = 'item', image = 'repairkit.png', unique = false, useable = true, shouldClose = true, description = 'A nice toolbox with stuff to repair your vehicle' },
advancedrepairkit = { name = 'advancedrepairkit', label = 'Advanced Repairkit', weight = 4000, type = 'item', image = 'advancedkit.png', unique = false, useable = true, shouldClose = true, description = 'A nice toolbox with stuff to repair your vehicle' },
cleaningkit = { name = 'cleaningkit', label = 'Cleaning Kit', weight = 250, type = 'item', image = 'cleaningkit.png', unique = false, useable = true, shouldClose = true, description = 'A microfiber cloth with some soap will let your car sparkle again!' },
tunerlaptop = { name = 'tunerlaptop', label = 'Tunerchip', weight = 2000, type = 'item', image = 'tunerchip.png', unique = true, useable = true, shouldClose = true, description = 'With this tunerchip you can get your car on steroids... If you know what you\'re doing' },
harness = { name = 'harness', label = 'Race Harness', weight = 1000, type = 'item', image = 'harness.png', unique = true, useable = true, shouldClose = true, description = 'Racing Harness so no matter what you stay in the car' },
jerry_can = { name = 'jerry_can', label = 'Jerrycan 20L', weight = 20000, type = 'item', image = 'jerry_can.png', unique = false, useable = true, shouldClose = true, description = 'A can full of Fuel' },
tirerepairkit = { name = 'tirerepairkit', label = 'Tire Repair Kit', weight = 1000, type = 'item', image = 'tirerepairkit.png', unique = false, useable = true, shouldClose = true, description = 'A kit to repair your tires' },
-- Mechanic Parts
veh_toolbox = { name = 'veh_toolbox', label = 'Toolbox', weight = 1000, type = 'item', image = 'veh_toolbox.png', unique = false, useable = true, shouldClose = true, description = 'Check vehicle status' },
veh_armor = { name = 'veh_armor', label = 'Armor', weight = 1000, type = 'item', image = 'veh_armor.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle armor' },
veh_brakes = { name = 'veh_brakes', label = 'Brakes', weight = 1000, type = 'item', image = 'veh_brakes.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle brakes' },
veh_engine = { name = 'veh_engine', label = 'Engine', weight = 1000, type = 'item', image = 'veh_engine.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle engine' },
veh_suspension = { name = 'veh_suspension', label = 'Suspension', weight = 1000, type = 'item', image = 'veh_suspension.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle suspension' },
veh_transmission = { name = 'veh_transmission', label = 'Transmission', weight = 1000, type = 'item', image = 'veh_transmission.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle transmission' },
veh_turbo = { name = 'veh_turbo', label = 'Turbo', weight = 1000, type = 'item', image = 'veh_turbo.png', unique = false, useable = true, shouldClose = true, description = 'Install vehicle turbo' },
veh_interior = { name = 'veh_interior', label = 'Interior', weight = 1000, type = 'item', image = 'veh_interior.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle interior' },
veh_exterior = { name = 'veh_exterior', label = 'Exterior', weight = 1000, type = 'item', image = 'veh_exterior.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle exterior' },
veh_wheels = { name = 'veh_wheels', label = 'Wheels', weight = 1000, type = 'item', image = 'veh_wheels.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle wheels' },
veh_neons = { name = 'veh_neons', label = 'Neons', weight = 1000, type = 'item', image = 'veh_neons.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle neons' },
veh_xenons = { name = 'veh_xenons', label = 'Xenons', weight = 1000, type = 'item', image = 'veh_xenons.png', unique = false, useable = true, shouldClose = true, description = 'Upgrade vehicle xenons' },
veh_tint = { name = 'veh_tint', label = 'Tints', weight = 1000, type = 'item', image = 'veh_tint.png', unique = false, useable = true, shouldClose = true, description = 'Install vehicle tint' },
veh_plates = { name = 'veh_plates', label = 'Plates', weight = 1000, type = 'item', image = 'veh_plates.png', unique = false, useable = true, shouldClose = true, description = 'Install vehicle plates' },
-- Medication
firstaid = { name = 'firstaid', label = 'First Aid', weight = 2500, type = 'item', image = 'firstaid.png', unique = false, useable = true, shouldClose = true, description = 'You can use this First Aid kit to get people back on their feet' },
bandage = { name = 'bandage', label = 'Bandage', weight = 0, type = 'item', image = 'bandage.png', unique = false, useable = true, shouldClose = true, description = 'A bandage works every time' },
ifaks = { name = 'ifaks', label = 'ifaks', weight = 200, type = 'item', image = 'ifaks.png', unique = false, useable = true, shouldClose = true, description = 'ifaks for healing and a complete stress remover.' },
painkillers = { name = 'painkillers', label = 'Painkillers', weight = 0, type = 'item', image = 'painkillers.png', unique = false, useable = true, shouldClose = true, description = 'For pain you can\'t stand anymore, take this pill that\'d make you feel great again' },
walkstick = { name = 'walkstick', label = 'Walking Stick', weight = 1000, type = 'item', image = 'walkstick.png', unique = false, useable = true, shouldClose = true, description = 'Walking stick for ya\'ll grannies out there.. HAHA' },
-- Communication
phone = { name = 'phone', label = 'Phone', weight = 700, type = 'item', image = 'phone.png', unique = true, useable = false, shouldClose = false, description = 'Neat phone ya got there' },
radio = { name = 'radio', label = 'Radio', weight = 2000, type = 'item', image = 'radio.png', unique = true, useable = true, shouldClose = true, description = 'You can communicate with this through a signal' },
iphone = { name = 'iphone', label = 'iPhone', weight = 1000, type = 'item', image = 'iphone.png', unique = false, useable = false, shouldClose = true, description = 'Very expensive phone' },
samsungphone = { name = 'samsungphone', label = 'Samsung S10', weight = 1000, type = 'item', image = 'samsungphone.png', unique = false, useable = false, shouldClose = true, description = 'Very expensive phone' },
laptop = { name = 'laptop', label = 'Laptop', weight = 4000, type = 'item', image = 'laptop.png', unique = false, useable = false, shouldClose = true, description = 'Expensive laptop' },
tablet = { name = 'tablet', label = 'Tablet', weight = 2000, type = 'item', image = 'tablet.png', unique = false, useable = false, shouldClose = true, description = 'Expensive tablet' },
fitbit = { name = 'fitbit', label = 'Fitbit', weight = 500, type = 'item', image = 'fitbit.png', unique = true, useable = true, shouldClose = true, description = 'I like fitbit' },
radioscanner = { name = 'radioscanner', label = 'Radio Scanner', weight = 1000, type = 'item', image = 'radioscanner.png', unique = false, useable = false, shouldClose = true, description = 'With this you can get some police alerts. Not 100% effective however' },
pinger = { name = 'pinger', label = 'Pinger', weight = 1000, type = 'item', image = 'pinger.png', unique = false, useable = false, shouldClose = true, description = 'With a pinger and your phone you can send out your location' },
cryptostick = { name = 'cryptostick', label = 'Crypto Stick', weight = 200, type = 'item', image = 'cryptostick.png', unique = true, useable = true, shouldClose = true, description = 'Why would someone ever buy money that doesn\'t exist.. How many would it contain..?' },
-- Theft and Jewelry
rolex = { name = 'rolex', label = 'Golden Watch', weight = 1500, type = 'item', image = 'rolex.png', unique = false, useable = false, shouldClose = true, description = 'A golden watch seems like the jackpot to me!' },
diamond_ring = { name = 'diamond_ring', label = 'Diamond Ring', weight = 1500, type = 'item', image = 'diamond_ring.png', unique = false, useable = false, shouldClose = true, description = 'A diamond ring seems like the jackpot to me!' },
diamond = { name = 'diamond', label = 'Diamond', weight = 1000, type = 'item', image = 'diamond.png', unique = false, useable = false, shouldClose = true, description = 'A diamond seems like the jackpot to me!' },
goldchain = { name = 'goldchain', label = 'Golden Chain', weight = 1500, type = 'item', image = 'goldchain.png', unique = false, useable = false, shouldClose = true, description = 'A golden chain seems like the jackpot to me!' },
tenkgoldchain = { name = 'tenkgoldchain', label = '10k Gold Chain', weight = 2000, type = 'item', image = '10kgoldchain.png', unique = false, useable = false, shouldClose = true, description = '10 carat golden chain' },
goldbar = { name = 'goldbar', label = 'Gold Bar', weight = 7000, type = 'item', image = 'goldbar.png', unique = false, useable = false, shouldClose = true, description = 'Looks pretty expensive to me' },
-- Cops Tools
armor = { name = 'armor', label = 'Armor', weight = 5000, type = 'item', image = 'armor.png', unique = false, useable = true, shouldClose = true, description = 'Some protection won\'t hurt... right?' },
heavyarmor = { name = 'heavyarmor', label = 'Heavy Armor', weight = 5000, type = 'item', image = 'armor.png', unique = false, useable = true, shouldClose = true, description = 'Some protection won\'t hurt... right?' },
handcuffs = { name = 'handcuffs', label = 'Handcuffs', weight = 100, type = 'item', image = 'handcuffs.png', unique = false, useable = true, shouldClose = true, description = 'Comes in handy when people misbehave. Maybe it can be used for something else?' },
police_stormram = { name = 'police_stormram', label = 'Stormram', weight = 18000, type = 'item', image = 'police_stormram.png', unique = false, useable = true, shouldClose = true, description = 'A nice tool to break into doors' },
empty_evidence_bag = { name = 'empty_evidence_bag', label = 'Empty Evidence Bag', weight = 0, type = 'item', image = 'evidence.png', unique = false, useable = false, shouldClose = false, description = 'Used a lot to keep DNA from blood, bullet shells and more' },
filled_evidence_bag = { name = 'filled_evidence_bag', label = 'Evidence Bag', weight = 200, type = 'item', image = 'evidence.png', unique = true, useable = false, shouldClose = false, description = 'A filled evidence bag to see who committed the crime >:(' },
-- Firework Tools
firework1 = { name = 'firework1', label = '2Brothers', weight = 1000, type = 'item', image = 'firework1.png', unique = false, useable = true, shouldClose = true, description = 'Fireworks' },
firework2 = { name = 'firework2', label = 'Poppelers', weight = 1000, type = 'item', image = 'firework2.png', unique = false, useable = true, shouldClose = true, description = 'Fireworks' },
firework3 = { name = 'firework3', label = 'WipeOut', weight = 1000, type = 'item', image = 'firework3.png', unique = false, useable = true, shouldClose = true, description = 'Fireworks' },
firework4 = { name = 'firework4', label = 'Weeping Willow', weight = 1000, type = 'item', image = 'firework4.png', unique = false, useable = true, shouldClose = true, description = 'Fireworks' },
-- Sea Tools
dendrogyra_coral = { name = 'dendrogyra_coral', label = 'Dendrogyra', weight = 1000, type = 'item', image = 'dendrogyra_coral.png', unique = false, useable = false, shouldClose = true, description = 'Its also known as pillar coral' },
antipatharia_coral = { name = 'antipatharia_coral', label = 'Antipatharia', weight = 1000, type = 'item', image = 'antipatharia_coral.png', unique = false, useable = false, shouldClose = true, description = 'Its also known as black corals or thorn corals' },
diving_gear = { name = 'diving_gear', label = 'Diving Gear', weight = 30000, type = 'item', image = 'diving_gear.png', unique = true, useable = true, shouldClose = true, description = 'An oxygen tank and a rebreather' },
diving_fill = { name = 'diving_fill', label = 'Diving Tube', weight = 3000, type = 'item', image = 'diving_tube.png', unique = true, useable = true, shouldClose = true, description = 'An oxygen tube and a rebreather' },
-- Other Tools
casinochips = { name = 'casinochips', label = 'Casino Chips', weight = 0, type = 'item', image = 'casinochips.png', unique = false, useable = false, shouldClose = false, description = 'Chips For Casino Gambling' },
stickynote = { name = 'stickynote', label = 'Sticky note', weight = 0, type = 'item', image = 'stickynote.png', unique = true, useable = false, shouldClose = false, description = 'Sometimes handy to remember something :)' },
moneybag = { name = 'moneybag', label = 'Money Bag', weight = 0, type = 'item', image = 'moneybag.png', unique = true, useable = true, shouldClose = true, description = 'A bag with cash' },
parachute = { name = 'parachute', label = 'Parachute', weight = 30000, type = 'item', image = 'parachute.png', unique = true, useable = true, shouldClose = true, description = 'The sky is the limit! Woohoo!' },
binoculars = { name = 'binoculars', label = 'Binoculars', weight = 600, type = 'item', image = 'binoculars.png', unique = false, useable = true, shouldClose = true, description = 'Sneaky Breaky...' },
lighter = { name = 'lighter', label = 'Lighter', weight = 0, type = 'item', image = 'lighter.png', unique = false, useable = false, shouldClose = true, description = 'On new years eve a nice fire to stand next to' },
certificate = { name = 'certificate', label = 'Certificate', weight = 0, type = 'item', image = 'certificate.png', unique = false, useable = false, shouldClose = true, description = 'Certificate that proves you own certain stuff' },
markedbills = { name = 'markedbills', label = 'Marked Money', weight = 1000, type = 'item', image = 'markedbills.png', unique = true, useable = false, shouldClose = true, description = 'Money?' },
labkey = { name = 'labkey', label = 'Key', weight = 500, type = 'item', image = 'labkey.png', unique = true, useable = true, shouldClose = true, description = 'Key for a lock...?' },
printerdocument = { name = 'printerdocument', label = 'Document', weight = 500, type = 'item', image = 'printerdocument.png', unique = true, useable = true, shouldClose = true, description = 'A nice document' },
newscam = { name = 'newscam', label = 'News Camera', weight = 100, type = 'item', image = 'newscam.png', unique = true, useable = true, shouldClose = true, description = 'A camera for the news' },
newsmic = { name = 'newsmic', label = 'News Microphone', weight = 100, type = 'item', image = 'newsmic.png', unique = true, useable = true, shouldClose = true, description = 'A microphone for the news' },
newsbmic = { name = 'newsbmic', label = 'Boom Microphone', weight = 100, type = 'item', image = 'newsbmic.png', unique = true, useable = true, shouldClose = true, description = 'A Useable BoomMic' },
-- Crafting table's
item_bench = {name = "item_bench", label = "Workbench", weight = 15000, type = "item", image = "workbench.png", unique = true, useable = true, shouldClose = false, combinable = nil, description = "A workbench to craft items."},
attachment_bench = {name = "attachment_bench", label = "Attachment Workbench", weight = 15000, type = "item", image = "attworkbench.png", unique = true, useable = true, shouldClose = false, combinable = nil, description = "A workbench for crafting attachments."},
}
+142
View File
@@ -0,0 +1,142 @@
QBShared = QBShared or {}
QBShared.ForceJobDefaultDutyAtLogin = true -- true: Force duty state to jobdefaultDuty | false: set duty state from database last saved
QBShared.Jobs = {
unemployed = { label = 'Civilian', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Freelancer', payment = 10 } } },
bus = { label = 'Bus', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Driver', payment = 50 } } },
judge = { label = 'Honorary', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Judge', payment = 100 } } },
lawyer = { label = 'Law Firm', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Associate', payment = 50 } } },
reporter = { label = 'Reporter', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Journalist', payment = 50 } } },
trucker = { label = 'Trucker', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Driver', payment = 50 } } },
tow = { label = 'Towing', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Driver', payment = 50 } } },
garbage = { label = 'Garbage', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Collector', payment = 50 } } },
vineyard = { label = 'Vineyard', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Picker', payment = 50 } } },
hotdog = { label = 'Hotdog', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Sales', payment = 50 } } },
police = {
label = 'Law Enforcement',
type = 'leo',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Officer', payment = 75 },
['2'] = { name = 'Sergeant', payment = 100 },
['3'] = { name = 'Lieutenant', payment = 125 },
['4'] = { name = 'Chief', isboss = true, payment = 150 },
},
},
ambulance = {
label = 'EMS',
type = 'ems',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Paramedic', payment = 75 },
['2'] = { name = 'Doctor', payment = 100 },
['3'] = { name = 'Surgeon', payment = 125 },
['4'] = { name = 'Chief', isboss = true, payment = 150 },
},
},
realestate = {
label = 'Real Estate',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'House Sales', payment = 75 },
['2'] = { name = 'Business Sales', payment = 100 },
['3'] = { name = 'Broker', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
taxi = {
label = 'Taxi',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Driver', payment = 75 },
['2'] = { name = 'Event Driver', payment = 100 },
['3'] = { name = 'Sales', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
cardealer = {
label = 'Vehicle Dealer',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Showroom Sales', payment = 75 },
['2'] = { name = 'Business Sales', payment = 100 },
['3'] = { name = 'Finance', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
mechanic = {
label = 'LS Customs',
type = 'mechanic',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Novice', payment = 75 },
['2'] = { name = 'Experienced', payment = 100 },
['3'] = { name = 'Advanced', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
mechanic2 = {
label = 'LS Customs',
type = 'mechanic',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Novice', payment = 75 },
['2'] = { name = 'Experienced', payment = 100 },
['3'] = { name = 'Advanced', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
mechanic3 = {
label = 'LS Customs',
type = 'mechanic',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Novice', payment = 75 },
['2'] = { name = 'Experienced', payment = 100 },
['3'] = { name = 'Advanced', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
beeker = {
label = 'Beeker\'s Garage',
type = 'mechanic',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Novice', payment = 75 },
['2'] = { name = 'Experienced', payment = 100 },
['3'] = { name = 'Advanced', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
bennys = {
label = 'Benny\'s Original Motor Works',
type = 'mechanic',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Novice', payment = 75 },
['2'] = { name = 'Experienced', payment = 100 },
['3'] = { name = 'Advanced', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
}
-44
View File
@@ -1,44 +0,0 @@
{
"General": {
"cash": {
"amount": 500,
"allowminus": false,
"minuslimit": -5000
},
"bank": {
"amount": 5000,
"allowminus": false,
"minuslimit": -50000
},
"crypto": {
"amount": 0,
"allowminus": false,
"minuslimit": -5000
},
"DefaultSpawn": { "x": -1035.71, "y": -2731.87, "z": 12.86, "h": 0.0 },
"UpdateInterval": 5,
"StatusInterval": 5000,
"ForceJobDefaultDutyAtLogin": true,
"HungerRate": 4.2,
"ThirstRate": 3.8,
"PayCheckTimeOut": 10,
"PayCheckSociety": false,
"Bloodtypes": ["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"],
"items": [
{ "item": "driver_license", "amount": 1 },
{ "item": "id_card", "amount": 1 },
{ "item": "phone", "amount": 1 }
]
},
"Server": {
"Closed": false,
"ClosedReason": "Server Closed",
"Uptime": 0,
"Whitelist": false,
"WhitelistPermission": "admin",
"PVP": true,
"Discord": "",
"CheckDuplicateLicense": true,
"Permissions": ["god", "admin", "mod"]
}
}
-118
View File
@@ -1,118 +0,0 @@
{
"none": {
"label": "No Gang",
"grades": [
{
"name": "Unaffiliated"
}
]
},
"lostmc": {
"label": "The Lost MC",
"grades": [
{
"name": "Recruit"
},
{
"name": "Enforcer"
},
{
"name": "Shot Caller"
},
{
"name": "Boss",
"isboss": true
}
]
},
"ballas": {
"label": "Ballas",
"grades": [
{
"name": "Recruit"
},
{
"name": "Enforcer"
},
{
"name": "Shot Caller"
},
{
"name": "Boss",
"isboss": true
}
]
},
"vagos": {
"label": "Vagos",
"grades": [
{
"name": "Recruit"
},
{
"name": "Enforcer"
},
{
"name": "Shot Caller"
},
{
"name": "Boss",
"isboss": true
}
]
},
"cartel": {
"label": "Cartel",
"grades": [
{
"name": "Recruit"
},
{
"name": "Enforcer"
},
{
"name": "Shot Caller"
},
{
"name": "Boss",
"isboss": true
}
]
},
"families": {
"label": "Families",
"grades": [
{
"name": "Recruit"
},
{
"name": "Enforcer"
},
{
"name": "Shot Caller"
},
{
"name": "Boss",
"isboss": true
}
]
},
"triads": {
"label": "Triads",
"grades": [
{
"name": "Recruit"
},
{
"name": "Enforcer"
},
{
"name": "Shot Caller"
},
{
"name": "Boss",
"isboss": true
}
]
}
}
File diff suppressed because it is too large Load Diff
-399
View File
@@ -1,399 +0,0 @@
{
"unemployed": {
"label": "Civilian",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Freelancer",
"payment": 10
}
]
},
"bus": {
"label": "Bus",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Driver",
"payment": 50
}
]
},
"judge": {
"label": "Honorary",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Judge",
"payment": 100
}
]
},
"lawyer": {
"label": "Law Firm",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Associate",
"payment": 50
}
]
},
"reporter": {
"label": "Reporter",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Journalist",
"payment": 50
}
]
},
"trucker": {
"label": "Trucker",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Driver",
"payment": 50
}
]
},
"tow": {
"label": "Towing",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Driver",
"payment": 50
}
]
},
"garbage": {
"label": "Garbage",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Collector",
"payment": 50
}
]
},
"vineyard": {
"label": "Vineyard",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Picker",
"payment": 50
}
]
},
"hotdog": {
"label": "Hotdog",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Sales",
"payment": 50
}
]
},
"police": {
"label": "Law Enforcement",
"type": "leo",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Officer",
"payment": 75
},
{
"name": "Sergeant",
"payment": 100
},
{
"name": "Lieutenant",
"payment": 125
},
{
"name": "Chief",
"isboss": true,
"payment": 150
}
]
},
"ambulance": {
"label": "EMS",
"type": "ems",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Paramedic",
"payment": 75
},
{
"name": "Doctor",
"payment": 100
},
{
"name": "Surgeon",
"payment": 125
},
{
"name": "Chief",
"isboss": true,
"payment": 150
}
]
},
"realestate": {
"label": "Real Estate",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "House Sales",
"payment": 75
},
{
"name": "Business Sales",
"payment": 100
},
{
"name": "Broker",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"taxi": {
"label": "Taxi",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Driver",
"payment": 75
},
{
"name": "Event Driver",
"payment": 100
},
{
"name": "Sales",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"cardealer": {
"label": "Vehicle Dealer",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Showroom Sales",
"payment": 75
},
{
"name": "Business Sales",
"payment": 100
},
{
"name": "Finance",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"mechanic": {
"label": "LS Customs",
"type": "mechanic",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Novice",
"payment": 75
},
{
"name": "Experienced",
"payment": 100
},
{
"name": "Advanced",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"mechanic2": {
"label": "LS Customs",
"type": "mechanic",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Novice",
"payment": 75
},
{
"name": "Experienced",
"payment": 100
},
{
"name": "Advanced",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"mechanic3": {
"label": "LS Customs",
"type": "mechanic",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Novice",
"payment": 75
},
{
"name": "Experienced",
"payment": 100
},
{
"name": "Advanced",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"beeker": {
"label": "Beeker's Garage",
"type": "mechanic",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Novice",
"payment": 75
},
{
"name": "Experienced",
"payment": 100
},
{
"name": "Advanced",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
},
"bennys": {
"label": "Benny's Original Motor Works",
"type": "mechanic",
"defaultDuty": true,
"offDutyPay": false,
"grades": [
{
"name": "Recruit",
"payment": 50
},
{
"name": "Novice",
"payment": 75
},
{
"name": "Experienced",
"payment": 100
},
{
"name": "Advanced",
"payment": 125
},
{
"name": "Manager",
"isboss": true,
"payment": 150
}
]
}
}
-90
View File
@@ -1,90 +0,0 @@
{
"citizenid": null,
"cid": 1,
"money": {
"cash": 500,
"bank": 5000,
"crypto": 0
},
"optin": true,
"charinfo": {
"firstname": "Firstname",
"lastname": "Lastname",
"birthdate": "00-00-0000",
"gender": 0,
"nationality": "USA",
"phone": null,
"account": null
},
"job": {
"name": "unemployed",
"label": "Civilian",
"payment": 10,
"type": "none",
"onduty": false,
"isboss": false,
"grade": {
"name": "Freelancer",
"level": 1,
"payment": 10,
"isboss": false
}
},
"gang": {
"name": "none",
"label": "No Gang Affiliation",
"isboss": false,
"grade": {
"name": "none",
"level": 1,
"isboss": false
}
},
"metadata": {
"hunger": 100,
"thirst": 100,
"stress": 0,
"isdead": false,
"inlaststand": false,
"armor": 0,
"ishandcuffed": false,
"tracker": false,
"injail": 0,
"jailitems": [],
"status": {},
"phone": {},
"rep": {},
"currentapartment": null,
"callsign": "NO CALLSIGN",
"bloodtype": null,
"fingerprint": null,
"walletid": null,
"criminalrecord": {
"hasRecord": false,
"date": null
},
"licences": {
"driver": true,
"business": false,
"weapon": false
},
"inside": {
"house": null,
"apartment": {
"apartmentType": null,
"apartmentId": null
}
},
"phonedata": {
"SerialNumber": null,
"InstalledApps": []
}
},
"position": {
"x": 1000,
"y": 1000,
"z": 1000,
"h": 1000
},
"items": []
}
File diff suppressed because it is too large Load Diff
-884
View File
@@ -1,884 +0,0 @@
[
{
"name": "weapon_unarmed",
"label": "Fists",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_dagger",
"label": "Dagger",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_bat",
"label": "Bat",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_bottle",
"label": "Broken Bottle",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_crowbar",
"label": "Crowbar",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_flashlight",
"label": "Flashlight",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_golfclub",
"label": "Golfclub",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_hammer",
"label": "Hammer",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_hatchet",
"label": "Hatchet",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_knuckle",
"label": "Knuckle",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_knife",
"label": "Knife",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_machete",
"label": "Machete",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_switchblade",
"label": "Switchblade",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_nightstick",
"label": "Nightstick",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_wrench",
"label": "Wrench",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_battleaxe",
"label": "Battle Axe",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_poolcue",
"label": "Poolcue",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_briefcase",
"label": "Briefcase",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_briefcase_02",
"label": "Briefcase",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_garbagebag",
"label": "Garbage Bag",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_handcuffs",
"label": "Handcuffs",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_bread",
"label": "Baquette",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee killed / Whacked / Executed / Beat down / Murdered / Battered"
},
{
"name": "weapon_stone_hatchet",
"label": "Stone Hatchet",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Knifed / Stabbed / Eviscerated"
},
{
"name": "weapon_candycane",
"label": "Candy Cane",
"weapontype": "Melee",
"ammotype": null,
"damagereason": "Melee Killed / Whacked / Executed / Beat down / Musrdered / Battered / Candy Caned"
},
{
"name": "weapon_pistol",
"label": "Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_pistol_mk2",
"label": "Pistol Mk2",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_combatpistol",
"label": "Combat Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_appistol",
"label": "AP Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_stungun",
"label": "Taser",
"weapontype": "Pistol",
"ammotype": "AMMO_STUNGUN",
"damagereason": "Died"
},
{
"name": "weapon_pistol50",
"label": "Pistol .50 Cal",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_snspistol",
"label": "SNS Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_snspistol_mk2",
"label": "SNS Pistol MK2",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_heavypistol",
"label": "Heavy Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_vintagepistol",
"label": "Vintage Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_flaregun",
"label": "Flare Gun",
"weapontype": "Pistol",
"ammotype": "AMMO_FLARE",
"damagereason": "Died"
},
{
"name": "weapon_marksmanpistol",
"label": "Marksman Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_revolver",
"label": "Revolver",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_revolver_mk2",
"label": "Revolver MK2",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_doubleaction",
"label": "Double Action Revolver",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_raypistol",
"label": "Ray Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_ceramicpistol",
"label": "Ceramic Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_navyrevolver",
"label": "Navy Revolver",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_gadgetpistol",
"label": "Gadget Pistol",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plugged / Bust a cap in"
},
{
"name": "weapon_stungun_mp",
"label": "Taser",
"weapontype": "Pistol",
"ammotype": "AMMO_STUNGUN",
"damagereason": "Died"
},
{
"name": "weapon_pistolxm3",
"label": "Pistol XM3",
"weapontype": "Pistol",
"ammotype": "AMMO_PISTOL",
"damagereason": "Pistoled / Blasted / Plaugged / Bust a cap in"
},
{
"name": "weapon_microsmg",
"label": "Micro SMG",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_smg",
"label": "SMG",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_smg_mk2",
"label": "SMG MK2",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_assaultsmg",
"label": "Assault SMG",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_combatpdw",
"label": "Combat PDW",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_machinepistol",
"label": "Tec-9",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_PISTOL",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_minismg",
"label": "Mini SMG",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_raycarbine",
"label": "Raycarbine",
"weapontype": "Submachine Gun",
"ammotype": "AMMO_SMG",
"damagereason": "Riddled / Drilled / Finished / Submachine Gunned"
},
{
"name": "weapon_pumpshotgun",
"label": "Pump Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_pumpshotgun_mk2",
"label": "Pump Shotgun MK2",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_sawnoffshotgun",
"label": "Sawn-off Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_assaultshotgun",
"label": "Assault Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_bullpupshotgun",
"label": "Bullpup Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_musket",
"label": "Musket",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_heavyshotgun",
"label": "Heavy Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_dbshotgun",
"label": "Double-barrel Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_autoshotgun",
"label": "Auto Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_combatshotgun",
"label": "Combat Shotgun",
"weapontype": "Shotgun",
"ammotype": "AMMO_SHOTGUN",
"damagereason": "Devastated / Pulverized / Shotgunned"
},
{
"name": "weapon_assaultrifle",
"label": "Assault Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_assaultrifle_mk2",
"label": "Assault Rifle MK2",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_carbinerifle",
"label": "Carbine Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_carbinerifle_mk2",
"label": "Carbine Rifle MK2",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_advancedrifle",
"label": "Advanced Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_specialcarbine",
"label": "Special Carbine",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_specialcarbine_mk2",
"label": "Specialcarbine MK2",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_bullpuprifle",
"label": "Bullpup Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_bullpuprifle_mk2",
"label": "Bull Puprifle MK2",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_compactrifle",
"label": "Compact Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_militaryrifle",
"label": "Military Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_heavyrifle",
"label": "Heavy Rifle",
"weapontype": "Assault Rifle",
"ammotype": "AMMO_RIFLE",
"damagereason": "Ended / Rifled / Shot down / Floored"
},
{
"name": "weapon_mg",
"label": "Machinegun",
"weapontype": "Light Machine Gun",
"ammotype": "AMMO_MG",
"damagereason": "Machine gunned / Sprayed / Ruined"
},
{
"name": "weapon_combatmg",
"label": "Combat MG",
"weapontype": "Light Machine Gun",
"ammotype": "AMMO_MG",
"damagereason": "Machine gunned / Sprayed / Ruined"
},
{
"name": "weapon_combatmg_mk2",
"label": "Combat MG MK2",
"weapontype": "Light Machine Gun",
"ammotype": "AMMO_MG",
"damagereason": "Machine gunned / Sprayed / Ruined"
},
{
"name": "weapon_gusenberg",
"label": "Thompson SMG",
"weapontype": "Light Machine Gun",
"ammotype": "AMMO_MG",
"damagereason": "Machine gunned / Sprayed / Ruined"
},
{
"name": "weapon_sniperrifle",
"label": "Sniper Rifle",
"weapontype": "Sniper Rifle",
"ammotype": "AMMO_SNIPER",
"damagereason": "Sniped / Picked off / Scoped"
},
{
"name": "weapon_heavysniper",
"label": "Heavy Sniper",
"weapontype": "Sniper Rifle",
"ammotype": "AMMO_SNIPER",
"damagereason": "Sniped / Picked off / Scoped"
},
{
"name": "weapon_heavysniper_mk2",
"label": "Heavysniper MK2",
"weapontype": "Sniper Rifle",
"ammotype": "AMMO_SNIPER",
"damagereason": "Sniped / Picked off / Scoped"
},
{
"name": "weapon_marksmanrifle",
"label": "Marksman Rifle",
"weapontype": "Sniper Rifle",
"ammotype": "AMMO_SNIPER",
"damagereason": "Sniped / Picked off / Scoped"
},
{
"name": "weapon_marksmanrifle_mk2",
"label": "Marksman Rifle MK2",
"weapontype": "Sniper Rifle",
"ammotype": "AMMO_SNIPER",
"damagereason": "Sniped / Picked off / Scoped"
},
{
"name": "weapon_remotesniper",
"label": "Remote Sniper",
"weapontype": "Sniper Rifle",
"ammotype": "AMMO_SNIPER_REMOTE",
"damagereason": "Sniped / Picked off / Scoped"
},
{
"name": "weapon_rpg",
"label": "RPG",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_RPG",
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_grenadelauncher",
"label": "Grenade Launcher",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_GRENADELAUNCHER",
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_grenadelauncher_smoke",
"label": "Smoke Grenade Launcher",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_GRENADELAUNCHER",
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_minigun",
"label": "Minigun",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_MINIGUN",
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_firework",
"label": "Firework Launcher",
"weapontype": "Heavy Weapons",
"ammotype": null,
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_railgun",
"label": "Railgun",
"weapontype": "Heavy Weapons",
"ammotype": null,
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_railgunxm3",
"label": "Railgun XM3",
"weapontype": "Heavy Weapons",
"ammotype": null,
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_hominglauncher",
"label": "Homing Launcher",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_STINGER",
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_compactlauncher",
"label": "Compact Launcher",
"weapontype": "Heavy Weapons",
"ammotype": null,
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_rayminigun",
"label": "Ray Minigun",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_MINIGUN",
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_emplauncher",
"label": "EMP Launcher",
"weapontype": "Heavy Weapons",
"ammotype": "AMMO_EMPLAUNCHER",
"damagereason": "Died"
},
{
"name": "weapon_grenade",
"label": "Grenade",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Bombed / Exploded / Detonated / Blew up"
},
{
"name": "weapon_bzgas",
"label": "BZ Gas",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_molotov",
"label": "Molotov",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Torched / Flambeed / Barbecued"
},
{
"name": "weapon_stickybomb",
"label": "C4",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Bombed / Exploded / Detonated / Blew up"
},
{
"name": "weapon_proxmine",
"label": "Proxmine Grenade",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Bombed / Exploded / Detonated / Blew up"
},
{
"name": "weapon_snowball",
"label": "Snowball",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_pipebomb",
"label": "Pipe Bomb",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Bombed / Exploded / Detonated / Blew up"
},
{
"name": "weapon_ball",
"label": "Ball",
"weapontype": "Throwable",
"ammotype": "AMMO_BALL",
"damagereason": "Died"
},
{
"name": "weapon_smokegrenade",
"label": "Smoke Grenade",
"weapontype": "Throwable",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_flare",
"label": "Flare pistol",
"weapontype": "Throwable",
"ammotype": "AMMO_FLARE",
"damagereason": "Died"
},
{
"name": "weapon_petrolcan",
"label": "Petrol Can",
"weapontype": "Miscellaneous",
"ammotype": "AMMO_PETROLCAN",
"damagereason": "Died"
},
{
"name": "gadget_parachute",
"label": "Parachute",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_fireextinguisher",
"label": "Fire Extinguisher",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_hazardcan",
"label": "Hazardcan",
"weapontype": "Miscellaneous",
"ammotype": "AMMO_PETROLCAN",
"damagereason": "Died"
},
{
"name": "weapon_fertilizercan",
"label": "Fertilizer Can",
"weapontype": "Miscellaneous",
"ammotype": "AMMO_FERTILIZERCAN",
"damagereason": "Died"
},
{
"name": "weapon_barbed_wire",
"label": "Barbed Wire",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Prodded"
},
{
"name": "weapon_drowning",
"label": "Drowning",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_drowning_in_vehicle",
"label": "Drowning in a Vehicle",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_bleeding",
"label": "Bleeding",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Bled out"
},
{
"name": "weapon_electric_fence",
"label": "Electric Fence",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Fried"
},
{
"name": "weapon_explosion",
"label": "Explosion",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated"
},
{
"name": "weapon_fall",
"label": "Fall",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Committed suicide"
},
{
"name": "weapon_exhaustion",
"label": "Exhaustion",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_hit_by_water_cannon",
"label": "Water Cannon",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Died"
},
{
"name": "weapon_rammed_by_car",
"label": "Rammed - Vehicle",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Flattened / Ran over / Ran down"
},
{
"name": "weapon_run_over_by_car",
"label": "Run Over - Vehicle",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Flattened / Ran over / Ran down"
},
{
"name": "weapon_heli_crash",
"label": "Heli Crash",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Helicopter Crash"
},
{
"name": "weapon_fire",
"label": "Fire",
"weapontype": "Miscellaneous",
"ammotype": null,
"damagereason": "Torched / Flambeed / Barbecued"
},
{
"name": "weapon_animal",
"label": "Animal",
"weapontype": "Animals",
"ammotype": null,
"damagereason": "Mauled"
},
{
"name": "weapon_cougar",
"label": "Cougar",
"weapontype": "Animals",
"ammotype": null,
"damagereason": "Mauled"
}
]
+2 -1
View File
@@ -1,3 +1,4 @@
--- @class Locale
Locale = {}
Locale.__index = Locale
@@ -101,7 +102,7 @@ function Locale:t(key, subs)
-- At this point we know whether the phrase does not exist for this key
else
if self.warnOnMissing then
print(('^3Warning: Missing phrase for key: "%s"'):format(key))
print(('^3Warning: Missing phrase for key: "%s"^0'):format(key))
end
if self.fallback then
return self.fallback:t(key, subs)
+108
View File
@@ -0,0 +1,108 @@
QBShared.Locations = {
-- Base game MLO interiors
aircraft_carrier_int = vector4(3081.0042, -4693.6875, 15.2623, 76.8169),
apt_1_int = vector4(-1452.4602, -540.2056, 74.0443, 31.9129),
apt_2_int = vector4(-912.7861, -365.2444, 114.2748, 110.0791),
apt_3_int = vector4(-603.3033, 58.9509, 98.2002, 81.6158),
apt_4_int = vector4(-784.6159, 323.849, 211.9972, 260.1097),
apt_5_int = vector4(-31.1088, -595.1442, 80.0309, 238.7146),
apt_6_int = vector4(-603.0135, 59.0137, -182.3809, 79.5818),
apt_hi_1_int = vector4(-18.3525, -590.9888, 90.1148, 331.3955),
apt_hi_2_int = vector4(-1450.2595, -525.3569, 56.929, 29.844),
bahama_mamas_int = vector4(-1387.0503, -588.4022, 30.3195, 124.4977),
biker_clubhouse_1_int = vector4(1121.1473, -3152.606, -37.0627, 354.0713),
biker_clubhouse_2_int = vector4(997.1274, -3157.5261, -38.9071, 239.8958),
casino_garage_int = vector4(1380.0403, 178.7124, -48.9942, 351.2701),
casino_hotel_floor_5_int = vector4(2508.6404, -262.1918, -39.1218, 183.9682),
casino_interior_int = vector4(1089.1294, 207.2294, -48.9997, 320.0139),
casino_loading_bay_int = vector4(2519.0271, -279.1287, -64.7228, 273.2338),
casino_nightclub_int = vector4(1578.2496, 253.7532, -46.0051, 174.0727),
casino_offices_int = vector4(2485.0115, -250.7915, -55.1238, 270.4959),
casino_security_int = vector4(2548.1143, -267.3465, -58.723, 192.4636),
casino_vault_int = vector4(2506.9446, -238.5225, -70.7371, 268.6321),
casino_vault_lobby_int = vector4(2465.6365, -278.9171, -70.6942, 264.7971),
cocaine_lockup_int = vector4(1088.6353, -3187.8801, -38.9935, 178.8853),
counterfeit_cash_int = vector4(1138.2513, -3198.7056, -39.6657, 66.1039),
document_forgery_int = vector4(1173.4496, -3196.7759, -39.008, 77.9179),
doomsday_facility_int = vector4(453.0641, 4820.2095, -58.9997, 87.7778),
exec_apt_1_int = vector4(-786.7757, 315.4629, 217.6385, 269.4638),
exec_apt_2_int = vector4(-773.9349, 342.1293, 196.6862, 87.2747),
exec_apt_3_int = vector4(-787.0633, 315.7905, 187.9135, 276.2593),
fib_floor_47_int = vector4(136.714, -765.5745, 234.152, 76.3983),
fib_floor_49_int = vector4(136.2863, -765.8568, 242.1519, 79.0446),
fib_top_floor_int = vector4(156.6616, -759.0523, 258.1518, 83.4114),
franklins_house_int = vector4(-14.1716, -1440.7046, 31.1015, 2.7505),
house_hi_1_int = vector4(-174.1817, 497.7469, 137.6537, 195.447),
house_hi_2_int = vector4(342.0325, 437.7024, 149.3897, 125.2541),
house_hi_3_int = vector4(373.5483, 423.4114, 145.9079, 162.7296),
house_hi_4_int = vector4(-682.3972, 592.3556, 145.3927, 233.6731),
house_hi_5_int = vector4(-758.5825, 619.0045, 144.1539, 118.2023),
house_hi_6_int = vector4(-860.154, 690.9542, 152.8608, 192.5502),
house_hi_7_int = vector4(117.227, 559.6552, 184.3049, 190.2912),
house_low_1_int = vector4(266.0239, -1007.0026, -100.9048, 357.9751),
house_mid_1_int = vector4(346.855, -1012.7548, -99.1963, 355.3892),
iaa_facility_1_int = vector4(2154.9517, 2921.0366, -61.9025, 96.9736),
iaa_facility_2_int = vector4(2154.6731, 2921.0784, -81.0755, 268.6461),
lesters_factory_int = vector4(717.8725, -974.6326, 24.9142, 359.5224),
lesters_house_int = vector4(1273.9886, -1719.4897, 54.7715, 11.6602),
meth_lab_int = vector4(997.4726, -3200.6846, -36.3937, 307.2153),
morgue_int = vector4(274.9287, -1361.0305, 24.5378, 48.8693),
motel_room_int = vector4(151.379, -1007.7512, -99.0, 326.917),
movie_theatre_int = vector4(-1437.0271, -243.3148, 16.8335, 200.8791),
nightclub_1_int = vector4(-1569.494, -3016.9558, -74.4061, 359.8158),
office_1_int = vector4(-140.6184, -619.2825, 168.8203, 183.2728),
office_2_int = vector4(-77.2845, -828.4749, 243.3858, 330.7214),
office_3_int = vector4(-1579.7123, -562.8762, 108.5229, 217.1494),
office_4_int = vector4(-1394.6268, -479.7602, 72.0421, 273.3521),
psychiatrist_office_int = vector4(-1902.1859, -572.3267, 19.0972, 106.6428),
shell_1_int = vector4(404.9589, -957.7651, -99.0042, 1.7754),
submarine_int = vector4(512.8119, 4881.4795, -62.5867, 359.146),
uniondepository_int = vector4(5.8001, -708.6383, 16.131, 348.7656),
weed_farm_int = vector4(1066.0164, -3183.4456, -39.1635, 96.9736),
-- Unknown/Random/Vanilla
burgershot = vector4(-1199.0568, -882.4495, 13.3500, 209.1105),
casino = vector4(923.2289, 47.3113, 81.1063, 237.6052),
-- Gabz
arcade = vector4(-1649.6089, -1083.9313, 13.1575, 46.4121),
beanmachinelegion = vector4(116.16, -1022.99, 29.3, 0.0),
bowling = vector4(761.5008, -777.7256, 26.3078, 90.5581),
pizzaria = vector4(790.4561, -758.4601, 26.7424, 270.2329),
catcafe = vector4(-580.8388, -1072.7872, 22.3296, 359.0078),
carmeet = vector4(958.8237, -1699.6659, 29.5574, 71.2731),
popsdiner = vector4(1595.9753, 6448.6421, 25.3170, 28.3026),
harmony = vector4(1183.0693, 2648.5313, 37.8363, 194.0603),
haters = vector4(-1117.1525, -1439.4297, 5.1075, 103.4411),
hayes = vector4(-1435.7040, -445.7360, 35.5964, 220.1762),
pdm = vector4(-48.2113, -1105.4769, 27.2634, 339.5899),
bennys = vector4(-47.5289, -1042.6086, 28.3532, 247.9748),
lamesaauto = vector4(720.4776, -1092.1934, 22.2866, 310.1469),
lostmc = vector4(982.6339, -104.7095, 74.8488, 30.8738),
pillbox = vector4(298.1153, -584.2825, 43.2609, 252.7553),
mirrorparkhouse1 = vector4(945.9535, -652.9119, 58.0228, 90.5541),
pacificbank = vector4(229.9529, 214.3890, 105.5561, 294.6791),
paletoliquor = vector4(-154.2287, 6328.8682, 31.5665, 133.1992),
paletogasstation = vector4(120.2420, 6625.4722, 31.9580, 36.5687),
pinkcage = vector4(323.9055, -203.2524, 54.0866, 186.8505),
ponsonbys1 = vector4(-165.2497, -304.3988, 38.07126, 0.0),
ponsonbys2 = vector4(-1448.1, -236.8420, 48.15098, 0.0),
ponsonbys3 = vector4(-709.8120, -150.6267, 35.75312, 0.0),
rangerstation = vector4(387.3204, 790.1508, 187.6927, 4.7656),
recordastudio = vector4(473.3006, -109.4360, 62.7418, 350.8488),
suburban1 = vector4(124.9756, -217.6290, 55.81879, 0.0),
suburban2 = vector4(617.4776, 2757.4810, 43.34935, 0.0),
suburban3 = vector4(-1195.8690, -773.5746, 18.58485, 0.0),
suburban4 = vector4(-3170.9670, 1049.9310, 22.12445, 0.0),
triadrecords = vector4(-829.0061, -698.2049, 28.0583, 291.2052),
tuner = vector4(157.5888, -3017.9968, 7.0400, 94.0695),
vu = vector4(129.4555, -1299.6754, 29.2327, 27.6378),
-- Patoche
luxerydealership = vector4(-1273.22, -371.11, 36.64, 301.8),
-- Unclejust
digitalden = vector4(-656.28, -849.92, 24.51, 167.42),
ifruitstore1 = vector4(-646.78, -288.17, 35.49, 297.73),
ifruitstore2 = vector4(-778.7451, -598.2717, 30.2772, 181.0197)
}
-46
View File
@@ -1,46 +0,0 @@
QBCore = {
Config = {},
Shared = {
Jobs = {},
Gangs = {},
Items = {},
Vehicles = {},
Weapons = {},
VehicleHashes = {},
},
Player = {},
Players = {},
PlayerData = {},
Functions = {},
ClientCallbacks = {},
ServerCallbacks = {},
Player_Buckets = {},
Entity_Buckets = {},
UsableItems = {},
Commands = {
List = {},
IgnoreList = {
['god'] = true,
['user'] = true
}
}
}
local function GetCoreObject(filters)
if not filters then return QBCore end
local results = {}
for i = 1, #filters do
local key = filters[i]
if QBCore[key] then
results[key] = QBCore[key]
end
end
return results
end
exports('GetCoreObject', GetCoreObject)
exports('GetSharedItems', function() return QBCore.Shared.Items end)
exports('GetSharedVehicles', function() return QBCore.Shared.Vehicles end)
exports('GetSharedWeapons', function() return QBCore.Shared.Weapons end)
exports('GetSharedJobs', function() return QBCore.Shared.Jobs end)
exports('GetSharedGangs', function() return QBCore.Shared.Gangs end)
-523
View File
@@ -1,523 +0,0 @@
function QBCore.Functions.GetVehicles()
if IsDuplicityVersion() then
return GetAllVehicles()
else
return GetGamePool('CVehicle')
end
end
function QBCore.Functions.GetObjects()
if IsDuplicityVersion() then
return GetAllObjects()
else
return GetGamePool('CObject')
end
end
function QBCore.Functions.GetPeds()
if IsDuplicityVersion() then
local pedPool = GetAllPeds()
local peds = {}
for i = 1, #pedPool do
if NetworkGetEntityOwner(pedPool[i]) == 0 then
table.insert(peds, pedPool[i])
end
end
return peds
else
local pedPool = GetGamePool('CPed')
local peds = {}
for i = 1, #pedPool do
if not IsPedAPlayer(pedPool[i]) then
table.insert(peds, pedPool[i])
end
end
return peds
end
end
function QBCore.Functions.GetPlayers()
if IsDuplicityVersion() then
local sources = {}
for k in pairs(QBCore.Players) do
sources[#sources + 1] = k
end
return sources
else
return GetActivePlayers()
end
end
function QBCore.Functions.GetCoords(entity)
local coords = GetEntityCoords(entity, false)
local heading = GetEntityHeading(entity)
return vector4(coords.x, coords.y, coords.z, heading)
end
function QBCore.Functions.HasItem(...)
if GetResourceState('qb-inventory') == 'missing' then return false end
if IsDuplicityVersion() then
local source, items, amount = ...
return exports['qb-inventory']:HasItem(source, items, amount)
else
local items, amount = ...
return exports['qb-inventory']:HasItem(items, amount)
end
end
function QBCore.Functions.Notify(...)
if IsDuplicityVersion() then
local source, text, texttype, length = ...
TriggerClientEvent('QBCore:Notify', source, text, texttype, length)
else
local text, texttype, length, icon = ...
local message = {
action = 'notify',
type = texttype or 'primary',
length = length or 5000,
}
if type(text) == 'table' then
message.text = text.text or 'Placeholder'
message.caption = text.caption or 'Placeholder'
else
message.text = text
end
if icon then
message.icon = icon
end
SendNUIMessage(message)
end
end
function QBCore.Functions.SpawnVehicle(...)
if IsDuplicityVersion() then
local source, model, coords, warp = ...
local ped = GetPlayerPed(source)
model = type(model) == 'string' and joaat(model) or model
if not coords then coords = GetEntityCoords(ped) end
local heading = coords.w and coords.w or 0.0
local veh = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, true)
while not DoesEntityExist(veh) do Wait(0) end
if warp then
while GetVehiclePedIsIn(ped, false) ~= veh do
Wait(0)
TaskWarpPedIntoVehicle(ped, veh, -1)
end
end
while NetworkGetEntityOwner(veh) ~= source do Wait(0) end
return veh
else
local model, cb, coords, isnetworked, teleportInto = ...
local ped = PlayerPedId()
model = type(model) == 'string' and joaat(model) or model
if not IsModelInCdimage(model) then return end
if coords then
coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
isnetworked = isnetworked == nil or isnetworked
QBCore.Functions.LoadModel(model)
local veh = CreateVehicle(model, coords.x, coords.y, coords.z, coords.w or 0.0, isnetworked, false)
local netid = NetworkGetNetworkIdFromEntity(veh)
SetVehicleHasBeenOwnedByPlayer(veh, true)
SetNetworkIdCanMigrate(netid, true)
SetVehicleNeedsToBeHotwired(veh, false)
SetVehRadioStation(veh, 'OFF')
SetVehicleFuelLevel(veh, 100.0)
SetModelAsNoLongerNeeded(model)
if teleportInto then TaskWarpPedIntoVehicle(PlayerPedId(), veh, -1) end
if cb then cb(veh) end
end
end
function QBCore.Functions.GetClosestPed(...)
if IsDuplicityVersion() then
local source, coords = ...
local ped = GetPlayerPed(source)
local peds = GetAllPeds()
local closestDistance, closestPed = -1, -1
if coords then
coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
for i = 1, #peds do
if peds[i] ~= ped then
local pedCoords = GetEntityCoords(peds[i])
local distance = #(pedCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestPed = peds[i]
closestDistance = distance
end
end
end
return closestPed, closestDistance
else
local coords, ignoreList = ...
local ped = PlayerPedId()
if coords then
coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
ignoreList = ignoreList or {}
local peds = QBCore.Functions.GetPeds(ignoreList)
local closestDistance, closestPed = -1, -1
for i = 1, #peds do
local pedCoords = GetEntityCoords(peds[i])
local distance = #(pedCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestPed = peds[i]
closestDistance = distance
end
end
return closestPed, closestDistance
end
end
function QBCore.Functions.GetClosestVehicle(...)
if IsDuplicityVersion() then
local source, coords = ...
local ped = GetPlayerPed(source)
local vehicles = GetAllVehicles()
local closestDistance, closestVehicle = -1, -1
if coords then
coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
for i = 1, #vehicles do
local vehicleCoords = GetEntityCoords(vehicles[i])
local distance = #(vehicleCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestVehicle = vehicles[i]
closestDistance = distance
end
end
return closestVehicle, closestDistance
else
local coords = ...
local ped = PlayerPedId()
local vehicles = GetGamePool('CVehicle')
local closestDistance, closestVehicle = -1, -1
if coords then
coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
for i = 1, #vehicles do
local vehicleCoords = GetEntityCoords(vehicles[i])
local distance = #(vehicleCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestVehicle = vehicles[i]
closestDistance = distance
end
end
return closestVehicle, closestDistance
end
end
function QBCore.Functions.GetClosestObject(...)
if IsDuplicityVersion() then
local source, coords = ...
local ped = GetPlayerPed(source)
local objects = GetAllObjects()
local closestDistance, closestObject = -1, -1
if coords then
coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
for i = 1, #objects do
local objectCoords = GetEntityCoords(objects[i])
local distance = #(objectCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestObject = objects[i]
closestDistance = distance
end
end
return closestObject, closestDistance
else
local coords = ...
local ped = PlayerPedId()
local objects = GetGamePool('CObject')
local closestDistance, closestObject = -1, -1
if coords then
coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
for i = 1, #objects do
local objectCoords = GetEntityCoords(objects[i])
local distance = #(objectCoords - coords)
if closestDistance == -1 or closestDistance > distance then
closestObject = objects[i]
closestDistance = distance
end
end
return closestObject, closestDistance
end
end
function QBCore.Functions.GetClosestPlayer(...)
if IsDuplicityVersion() then
local source, coords = ...
local ped = GetPlayerPed(source)
local players = GetPlayers()
local closestDistance, closestPlayer = -1, -1
if coords then
coords = type(coords) == 'table' and vector3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
for i = 1, #players do
local playerId = players[i]
local playerPed = GetPlayerPed(playerId)
if playerPed ~= ped then
local playerCoords = GetEntityCoords(playerPed)
local distance = #(playerCoords - coords)
if closestDistance == -1 or distance < closestDistance then
closestPlayer = playerId
closestDistance = distance
end
end
end
return closestPlayer, closestDistance
else
local coords = ...
local ped = PlayerPedId()
if coords then
coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords
else
coords = GetEntityCoords(ped)
end
local closestPlayers = QBCore.Functions.GetPlayersFromCoords(coords)
local closestDistance, closestPlayer = -1, -1
for i = 1, #closestPlayers do
if closestPlayers[i] ~= PlayerId() and closestPlayers[i] ~= -1 then
local pos = GetEntityCoords(GetPlayerPed(closestPlayers[i]))
local distance = #(pos - coords)
if closestDistance == -1 or closestDistance > distance then
closestPlayer = closestPlayers[i]
closestDistance = distance
end
end
end
return closestPlayer, closestDistance
end
end
local StringCharset = {}
local NumberCharset = {}
for i = 48, 57 do NumberCharset[#NumberCharset + 1] = string.char(i) end
for i = 65, 90 do StringCharset[#StringCharset + 1] = string.char(i) end
for i = 97, 122 do StringCharset[#StringCharset + 1] = string.char(i) end
-- String Functions
function QBCore.Functions.RandomStr(length)
local result = ''
for i = 1, length do
result = result .. StringCharset[math.random(1, #StringCharset)]
end
return result
end
function QBCore.Functions.SplitStr(str, delimiter)
local result = {}
local from = 1
local delim_from, delim_to = string.find(str, delimiter, from)
while delim_from do
result[#result + 1] = string.sub(str, from, delim_from - 1)
from = delim_to + 1
delim_from, delim_to = string.find(str, delimiter, from)
end
result[#result + 1] = string.sub(str, from)
return result
end
function QBCore.Functions.Trim(value)
if not value then return nil end
return (string.gsub(value, '^%s*(.-)%s*$', '%1'))
end
function QBCore.Functions.FirstToUpper(value)
if not value then return nil end
return (value:gsub('^%l', string.upper))
end
function QBCore.Functions.SplitLines(str)
local lines = {}
for line in str:gmatch('[^\r\n]+') do
table.insert(lines, line)
end
return lines
end
function QBCore.Functions.NormalizeString(str)
return str:gsub('[^%w]', ''):lower()
end
-- Number Functions
function QBCore.Functions.RandomInt(length)
local result = ''
for i = 1, length do
result = result .. NumberCharset[math.random(1, #NumberCharset)]
end
return result
end
function QBCore.Functions.Round(value, numDecimalPlaces)
if not numDecimalPlaces then return math.floor(value + 0.5) end
local power = 10 ^ numDecimalPlaces
return math.floor((value * power) + 0.5) / (power)
end
function QBCore.Functions.Clamp(value, min, max)
return math.max(min, math.min(value, max))
end
-- Table Functions
function QBCore.Functions.TableLength(tbl)
local count = 0
for _ in pairs(tbl) do count = count + 1 end
return count
end
function QBCore.Functions.ShuffleTable(tbl)
for i = #tbl, 2, -1 do
local j = math.random(1, i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
return tbl
end
function QBCore.Functions.DeepCopy(tbl)
if type(tbl) ~= 'table' then return tbl end
local copy = {}
for k, v in pairs(tbl) do
copy[k] = QBCore.Functions.DeepCopy(v)
end
return copy
end
function QBCore.Functions.ContainsKey(tbl, key)
return tbl[key] ~= nil
end
function QBCore.Functions.Contains(tbl, value)
for _, v in pairs(tbl) do
if v == value then return true end
end
return false
end
function QBCore.Functions.MergeTables(tbl1, tbl2)
local merged = QBCore.Functions.DeepCopy(tbl1)
for k, v in pairs(tbl2) do
merged[k] = v
end
return merged
end
function QBCore.Functions.ReverseTable(tbl)
local reversed = {}
for i = #tbl, 1, -1 do
table.insert(reversed, tbl[i])
end
return reversed
end
function QBCore.Functions.RandomWeightedChoice(choices)
local totalWeight = 0
for _, choice in pairs(choices) do
totalWeight = totalWeight + choice.weight
end
local rand = math.random() * totalWeight
local cumulative = 0
for key, choice in pairs(choices) do
cumulative = cumulative + choice.weight
if rand <= cumulative then return key, choice end
end
end
function QBCore.Functions.GetRandomElement(tbl)
return tbl[math.random(1, #tbl)]
end
function QBCore.Functions.PrintTable(tbl, indent)
indent = indent or 0
if type(tbl) == 'table' then
for k, v in pairs(tbl) do
local tblType = type(v)
local formatting = ('%s ^3%s:^0'):format(string.rep(' ', indent), k)
if tblType == 'table' then
print(formatting)
QBCore.Functions.PrintTable(v, indent + 1)
elseif tblType == 'boolean' then
print(('%s^1 %s ^0'):format(formatting, tostring(v)))
elseif tblType == 'function' then
print(('%s^9 %s ^0'):format(formatting, tostring(v)))
elseif tblType == 'number' then
print(('%s^5 %s ^0'):format(formatting, v))
elseif tblType == 'string' then
print(("%s ^2'%s' ^0"):format(formatting, v))
else
print(('%s^2 %s ^0'):format(formatting, tostring(v)))
end
end
else
print(('%s^0 %s'):format(string.rep(' ', indent), tostring(tbl)))
end
end
function QBCore.Functions.Debug(tbl, indent, resource)
local resource_name = resource or GetInvokingResource() or 'qb-core'
print(('\x1b[4m\x1b[36m[ %s : DEBUG]\x1b[0m'):format(resource_name))
QBCore.Functions.PrintTable(tbl, indent)
print('\x1b[4m\x1b[36m[ END DEBUG ]\x1b[0m')
end
function QBCore.Functions.ShowError(resource, msg)
local resource_name = resource or GetInvokingResource() or 'qb-core'
print('\x1b[31m[' .. resource_name .. ':ERROR]\x1b[0m ' .. msg)
end
function QBCore.Functions.ShowSuccess(resource, msg)
local resource_name = resource or GetInvokingResource() or 'qb-core'
print('\x1b[32m[' .. resource_name .. ':LOG]\x1b[0m ' .. msg)
end
function QBCore.Functions.IsFunction(value)
if type(value) == 'table' then
return value.__cfx_functionReference ~= nil and type(value.__cfx_functionReference) == 'string'
end
return type(value) == 'function'
end
-70
View File
@@ -1,70 +0,0 @@
local resourceName = GetCurrentResourceName()
-- Load Config
if IsDuplicityVersion() then
local configData = LoadResourceFile(resourceName, 'shared/json/config.json')
if not configData then return print('^1[QBCore] Failed to Load Config JSON File') end
QBCore.Config = json.decode(configData) or {}
print('^2[QBCore] Loaded Config.json')
end
-- Load Vehicles
local Vehicles = LoadResourceFile(resourceName, 'shared/json/vehicles.json')
if not Vehicles then return print('^1[QBCore] Failed to Load Vehicles JSON File') end
Vehicles = json.decode(Vehicles)
for i = 1, #Vehicles do
local hash = joaat(Vehicles[i].model)
QBCore.Shared.Vehicles[Vehicles[i].model] = {
spawncode = Vehicles[i].model,
name = Vehicles[i].name,
brand = Vehicles[i].brand,
model = Vehicles[i].model,
price = Vehicles[i].price,
category = Vehicles[i].category,
hash = hash,
type = Vehicles[i].type,
shop = Vehicles[i].shop
}
QBCore.Shared.VehicleHashes[hash] = QBCore.Shared.Vehicles[Vehicles[i].model]
end
print('^2[QBCore] Loaded Vehicles.json')
-- Load Jobs
local Jobs = LoadResourceFile(resourceName, 'shared/json/jobs.json')
if not Jobs then return print('^1[QBCore] Failed to Load Jobs JSON File') end
QBCore.Shared.Jobs = json.decode(Jobs) or {}
print('^2[QBCore] Loaded Jobs.json')
-- Load Gangs
local Gangs = LoadResourceFile(resourceName, 'shared/json/gangs.json')
if not Gangs then return print('^1[QBCore] Failed to Load Gangs JSON File') end
QBCore.Shared.Gangs = json.decode(Gangs) or {}
print('^2[QBCore] Loaded Gangs.json')
-- Load Items
local Items = LoadResourceFile(resourceName, 'shared/json/items.json')
if not Items then return print('^1[QBCore] Failed to Load Items JSON File') end
QBCore.Shared.Items = json.decode(Items) or {}
print('^2[QBCore] Loaded Items.json')
-- Load Weapons
local Weapons = LoadResourceFile(resourceName, 'shared/json/weapons.json')
if not Weapons then return print('^1[QBCore] Failed to Load Weapons JSON File') end
Weapons = json.decode(Weapons)
for i = 1, #Weapons do
local weaponHash = joaat(Weapons[i].name)
QBCore.Shared.Weapons[weaponHash] = {
name = Weapons[i].name,
label = Weapons[i].label,
weapontype = Weapons[i].weapontype,
ammotype = Weapons[i].ammotype,
damagereason = Weapons[i].damagereason
}
end
print('^2[QBCore] Loaded Weapons.json')
+175
View File
@@ -0,0 +1,175 @@
QBShared = QBShared or {}
local StringCharset = {}
local NumberCharset = {}
QBShared.StarterItems = {
['phone'] = { amount = 1, item = 'phone' },
['id_card'] = { amount = 1, item = 'id_card' },
['driver_license'] = { amount = 1, item = 'driver_license' },
}
for i = 48, 57 do NumberCharset[#NumberCharset + 1] = string.char(i) end
for i = 65, 90 do StringCharset[#StringCharset + 1] = string.char(i) end
for i = 97, 122 do StringCharset[#StringCharset + 1] = string.char(i) end
function QBShared.RandomStr(length)
if length <= 0 then return '' end
return QBShared.RandomStr(length - 1) .. StringCharset[math.random(1, #StringCharset)]
end
function QBShared.RandomInt(length)
if length <= 0 then return '' end
return QBShared.RandomInt(length - 1) .. NumberCharset[math.random(1, #NumberCharset)]
end
function QBShared.SplitStr(str, delimiter)
local result = {}
local from = 1
local delim_from, delim_to = string.find(str, delimiter, from)
while delim_from do
result[#result + 1] = string.sub(str, from, delim_from - 1)
from = delim_to + 1
delim_from, delim_to = string.find(str, delimiter, from)
end
result[#result + 1] = string.sub(str, from)
return result
end
function QBShared.Trim(value)
if not value then return nil end
return (string.gsub(value, '^%s*(.-)%s*$', '%1'))
end
function QBShared.FirstToUpper(value)
if not value then return nil end
return (value:gsub("^%l", string.upper))
end
function QBShared.Round(value, numDecimalPlaces)
if not numDecimalPlaces then return math.floor(value + 0.5) end
local power = 10 ^ numDecimalPlaces
return math.floor((value * power) + 0.5) / (power)
end
function QBShared.ChangeVehicleExtra(vehicle, extra, enable)
if DoesExtraExist(vehicle, extra) then
if enable then
SetVehicleExtra(vehicle, extra, false)
if not IsVehicleExtraTurnedOn(vehicle, extra) then
QBShared.ChangeVehicleExtra(vehicle, extra, enable)
end
else
SetVehicleExtra(vehicle, extra, true)
if IsVehicleExtraTurnedOn(vehicle, extra) then
QBShared.ChangeVehicleExtra(vehicle, extra, enable)
end
end
end
end
function QBShared.IsFunction(value)
if type(value) == 'table' then
return value.__cfx_functionReference ~= nil and type(value.__cfx_functionReference) == "string"
end
return type(value) == 'function'
end
function QBShared.SetDefaultVehicleExtras(vehicle, config)
-- Clear Extras
for i = 1, 20 do
if DoesExtraExist(vehicle, i) then
SetVehicleExtra(vehicle, i, 1)
end
end
for id, enabled in pairs(config) do
if type(enabled) ~= 'boolean' then
enabled = true
end
QBShared.ChangeVehicleExtra(vehicle, tonumber(id), enabled)
end
end
QBShared.MaleNoGloves = {
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
[7] = true,
[8] = true,
[9] = true,
[10] = true,
[11] = true,
[12] = true,
[13] = true,
[14] = true,
[15] = true,
[18] = true,
[26] = true,
[52] = true,
[53] = true,
[54] = true,
[55] = true,
[56] = true,
[57] = true,
[58] = true,
[59] = true,
[60] = true,
[61] = true,
[62] = true,
[112] = true,
[113] = true,
[114] = true,
[118] = true,
[125] = true,
[132] = true
}
QBShared.FemaleNoGloves = {
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
[7] = true,
[8] = true,
[9] = true,
[10] = true,
[11] = true,
[12] = true,
[13] = true,
[14] = true,
[15] = true,
[19] = true,
[59] = true,
[60] = true,
[61] = true,
[62] = true,
[63] = true,
[64] = true,
[65] = true,
[66] = true,
[67] = true,
[68] = true,
[69] = true,
[70] = true,
[71] = true,
[129] = true,
[130] = true,
[131] = true,
[135] = true,
[142] = true,
[149] = true,
[153] = true,
[157] = true,
[161] = true,
[165] = true
}
+783
View File
@@ -0,0 +1,783 @@
QBShared = QBShared or {}
QBShared.Vehicles = QBShared.Vehicles or {}
local Vehicles = {
--- Compacts (0)
{
model = 'asbo', -- This has to match the spawn code of the vehicle
name = 'Asbo', -- This is the display of the vehicle
brand = 'Maxwell', -- This is the vehicle's brand
price = 4000, -- The price that the vehicle sells for
category = 'compacts', -- Catgegory of the vehilce, stick with GetVehicleClass() options https://docs.fivem.net/natives/?_0x29439776AAA00A62
type = 'automobile', -- Vehicle type, refer here https://docs.fivem.net/natives/?_0x6AE51D4B & here https://docs.fivem.net/natives/?_0xA273060E
shop = 'pdm', -- Can be a single shop or multiple shops. For multiple shops for example {'shopname1','shopname2','shopname3'}
},
{ model = 'blista', name = 'Blista', brand = 'Dinka', price = 13000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'brioso', name = 'Brioso R/A', brand = 'Grotti', price = 20000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'club', name = 'Club', brand = 'BF', price = 8000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'dilettante', name = 'Dilettante', brand = 'Karin', price = 9000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'dilettante2', name = 'Dilettante Patrol', brand = 'Karin', price = 12000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'kanjo', name = 'Blista Kanjo', brand = 'Dinka', price = 12000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'issi2', name = 'Issi', brand = 'Weeny', price = 7000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'issi3', name = 'Issi Classic', brand = 'Weeny', price = 5000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'issi4', name = 'Issi Arena', brand = 'Weeny', price = 80000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'issi5', name = 'Issi Future Shock', brand = 'Weeny', price = 80000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'issi6', name = 'Issi Nightmare', brand = 'Weeny', price = 80000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'panto', name = 'Panto', brand = 'Benefactor', price = 3200, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'prairie', name = 'Prairie', brand = 'Bollokan', price = 30000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'rhapsody', name = 'Rhapsody', brand = 'Declasse', price = 10000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'brioso2', name = 'Brioso 300', brand = 'Grotti', price = 12000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'weevil', name = 'Weevil', brand = 'BF', price = 9000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'issi7', name = 'Issi Sport', brand = 'Weeny', price = 100000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'blista2', name = 'Blista Compact', brand = 'Dinka', price = 18950, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'blista3', name = 'Blista Go Go Monkey', brand = 'Dinka', price = 15000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'brioso3', name = 'Brioso 300 Widebody', brand = 'Grotti', price = 125000, category = 'compacts', type = 'automobile', shop = 'pdm' },
{ model = 'boor', name = 'Boor', brand = 'Karin', price = 23000, category = 'compacts', type = 'automobile', shop = 'pdm' },
--- Sedans (1)
{ model = 'asea', name = 'Asea', brand = 'Declasse', price = 2500, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'asterope', name = 'Asterope', brand = 'Karin', price = 11000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'cog55', name = 'Cognoscenti 55', brand = 'Enus', price = 22000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'cognoscenti', name = 'Cognoscenti', brand = 'Enus', price = 22500, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'emperor', name = 'Emperor', brand = 'Albany', price = 4250, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'fugitive', name = 'Fugitive', brand = 'Cheval', price = 20000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'glendale', name = 'Glendale', brand = 'Benefactor', price = 3400, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'glendale2', name = 'Glendale Custom', brand = 'Benefactor', price = 12000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'ingot', name = 'Ingot', brand = 'Vulcar', price = 4999, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'intruder', name = 'Intruder', brand = 'Karin', price = 11250, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'premier', name = 'Premier', brand = 'Declasse', price = 12000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'primo', name = 'Primo', brand = 'Albany', price = 5000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'primo2', name = 'Primo Custom', brand = 'Albany', price = 14500, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'regina', name = 'Regina', brand = 'Dundreary', price = 7000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'stafford', name = 'Stafford', brand = 'Enus', price = 30000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'stanier', name = 'Stanier', brand = 'Vapid', price = 19000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'stratum', name = 'Stratum', brand = 'Zirconium', price = 15000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'stretch', name = 'Stretch', brand = 'Dundreary', price = 19000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'superd', name = 'Super Diamond', brand = 'Enus', price = 17000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'surge', name = 'Surge', brand = 'Cheval', price = 20000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'tailgater', name = 'Tailgater', brand = 'Obey', price = 22000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'warrener', name = 'Warrener', brand = 'Vulcar', price = 4000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'washington', name = 'Washington', brand = 'Albany', price = 7000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'tailgater2', name = 'Tailgater S', brand = 'Obey', price = 51000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'cinquemila', name = 'Lampadati', brand = 'Cinquemila', price = 125000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'iwagen', name = 'Obey', brand = 'I-Wagen', price = 225000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'astron', name = 'Astron', brand = 'Pfister', price = 150000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'baller7', name = 'Baller ST', brand = 'Gallivanter', price = 145000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'comet7', name = 'Comet', brand = 'S2 Cabrio', price = 25000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'deity', name = 'Deity', brand = 'Enus', price = 505000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'jubilee', name = 'Jubilee', brand = 'Enus', price = 485000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'oracle', name = 'Oracle', brand = 'Übermacht', price = 22000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'schafter2', name = 'Schafter', brand = 'Benefactor', price = 16000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'warrener2', name = 'Warrener HKR', brand = 'Vulcar', price = 30000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'rhinehart', name = 'Rhinehart', brand = 'Übermacht', price = 105000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'eudora', name = 'Eudora', brand = 'Willard', price = 17000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'asterope2', name = 'Asterope GZ', brand = 'Karin', price = 459000, category = 'sedans', type = 'automobile', shop = 'pdm' },
{ model = 'impaler5', name = 'Impaler SZ', brand = 'Declasse', price = 768000, category = 'sedans', type = 'automobile', shop = 'pdm' },
--- SUV (2)
{ model = 'baller', name = 'Baller', brand = 'Gallivanter', price = 22000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'baller2', name = 'Baller II', brand = 'Gallivanter', price = 15000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'baller3', name = 'Baller LE', brand = 'Gallivanter', price = 15000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'baller4', name = 'Baller LE LWB', brand = 'Gallivanter', price = 29000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'baller5', name = 'Baller LE (Armored)', brand = 'Gallivanter', price = 78000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'baller6', name = 'Baller LE LWB (Armored)', brand = 'Gallivanter', price = 82000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'bjxl', name = 'BeeJay XL', brand = 'Karin', price = 19000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'cavalcade', name = 'Cavalcade', brand = 'Albany', price = 14000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'cavalcade2', name = 'Cavalcade II', brand = 'Albany', price = 16500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'contender', name = 'Contender', brand = 'Vapid', price = 35000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'dubsta', name = 'Dubsta', brand = 'Benefactor', price = 19000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'dubsta2', name = 'Dubsta Luxury', brand = 'Benefactor', price = 19500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'fq2', name = 'FQ2', brand = 'Fathom', price = 18500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'granger', name = 'Granger', brand = 'Declasse', price = 22000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'gresley', name = 'Gresley', brand = 'Bravado', price = 25000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'habanero', name = 'Habanero', brand = 'Emperor', price = 20000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'huntley', name = 'Huntley S', brand = 'Enus', price = 24500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'landstalker', name = 'Landstalker', brand = 'Dundreary', price = 12000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'landstalker2', name = 'Landstalker XL', brand = 'Dundreary', price = 26000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'novak', name = 'Novak', brand = 'Lampadati', price = 70000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'patriot', name = 'Patriot', brand = 'Mammoth', price = 21000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'patriot2', name = 'Patriot Stretch', brand = 'Mammoth', price = 21000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'radi', name = 'Radius', brand = 'Vapid', price = 18000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'rebla', name = 'Rebla GTS', brand = 'Übermacht', price = 21000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'rocoto', name = 'Rocoto', brand = 'Obey', price = 13000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'seminole', name = 'Seminole', brand = 'Canis', price = 20000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'seminole2', name = 'Seminole Frontier', brand = 'Canis', price = 13000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'serrano', name = 'Serrano', brand = 'Benefactor', price = 48000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'toros', name = 'Toros', brand = 'Pegassi', price = 65000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'xls', name = 'XLS', brand = 'Benefactor', price = 17000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'granger2', name = 'Granger 3600LX', brand = 'Declasse', price = 221000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'patriot3', name = 'Patriot Military', brand = 'Mil-Spec', price = 270000, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'aleutian', name = 'Aleutian', brand = 'Vapid', price = 183500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'baller8', name = 'Baller ST-D', brand = 'Gallivanter', price = 171500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'cavalcade3', name = 'Cavalcade XL', brand = 'Albany', price = 166500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'dorado', name = 'Dorado', brand = 'Bravado', price = 137500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'vivanite', name = 'Vivanite', brand = 'Karin', price = 160500, category = 'suvs', type = 'automobile', shop = 'pdm' },
{ model = 'castigator', name = 'Castigator', brand = 'Canis', price = 160500, category = 'suvs', type = 'automobile', shop = 'pdm' },
--- Coupes (3)
{ model = 'cogcabrio', name = 'Cognoscenti Cabrio', brand = 'Enus', price = 30000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'exemplar', name = 'Exemplar', brand = 'Dewbauchee', price = 40000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'f620', name = 'F620', brand = 'Ocelot', price = 32500, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'felon', name = 'Felon', brand = 'Lampadati', price = 31000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'felon2', name = 'Felon GT', brand = 'Lampadati', price = 37000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'jackal', name = 'Jackal', brand = 'Ocelot', price = 19000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'oracle2', name = 'Oracle XS', brand = 'Übermacht', price = 28000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'sentinel', name = 'Sentinel', brand = 'Übermacht', price = 30000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'sentinel2', name = 'Sentinel XS', brand = 'Übermacht', price = 33000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'windsor', name = 'Windsor', brand = 'Enus', price = 27000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'windsor2', name = 'Windsor Drop', brand = 'Enus', price = 34000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'zion', name = 'Zion', brand = 'Übermacht', price = 22000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'zion2', name = 'Zion Cabrio', brand = 'Übermacht', price = 28000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'previon', name = 'Previon', brand = 'Karin', price = 149000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'champion', name = 'Champion', brand = 'Dewbauchee', price = 205000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'futo', name = 'Futo', brand = 'Karin', price = 17500, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'sentinel3', name = 'Sentinel Classic', brand = 'Übermacht', price = 70000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'kanjosj', name = 'Kanjo SJ', brand = 'Dinka', price = 143000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'postlude', name = 'Postlude', brand = 'Dinka', price = 90000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'tahoma', name = 'Tahoma Coupe', brand = 'Declasse', price = 12000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'broadway', name = 'Broadway', brand = 'Classique', price = 20000, category = 'coupes', type = 'automobile', shop = 'pdm' },
{ model = 'fr36', name = 'FR36', brand = 'Fathom', price = 161000, category = 'coupes', type = 'automobile', shop = 'pdm' },
--- Muscle (4)
{ model = 'blade', name = 'Blade', brand = 'Vapid', price = 23500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'buccaneer', name = 'Buccaneer', brand = 'Albany', price = 22500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'buccaneer2', name = 'Buccaneer Rider', brand = 'Albany', price = 24500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'chino', name = 'Chino', brand = 'Vapid', price = 5000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'chino2', name = 'Chino Luxe', brand = 'Vapid', price = 8000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'clique', name = 'Clique', brand = 'Vapid', price = 20000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'coquette3', name = 'Coquette BlackFin', brand = 'Invetero', price = 180000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'deviant', name = 'Deviant', brand = 'Schyster', price = 70000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator', name = 'Dominator', brand = 'Vapid', price = 62500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator2', name = 'Pißwasser Dominator', brand = 'Vapid', price = 50000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator3', name = 'Dominator GTX', brand = 'Vapid', price = 70000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator4', name = 'Dominator Arena', brand = 'Vapid', price = 200000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator7', name = 'Dominator ASP', brand = 'Vapid', price = 110000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator8', name = 'Dominator GTT', brand = 'Vapid', price = 80000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dukes', name = 'Dukes', brand = 'Imponte', price = 23500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dukes2', name = 'Duke O\'Death', brand = 'Imponte', price = 60000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dukes3', name = 'Beater Dukes', brand = 'Imponte', price = 45000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'faction', name = 'Faction', brand = 'Willard', price = 17000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'faction2', name = 'Faction Rider', brand = 'Willard', price = 19000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'faction3', name = 'Faction Custom Donk', brand = 'Willard', price = 35000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'ellie', name = 'Ellie', brand = 'Vapid', price = 42250, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'gauntlet', name = 'Gauntlet', brand = 'Bravado', price = 28500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'gauntlet2', name = 'Redwood Gauntlet', brand = 'Bravado', price = 70000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'gauntlet3', name = 'Classic Gauntlet', brand = 'Bravado', price = 75000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'gauntlet4', name = 'Gauntlet Hellfire', brand = 'Bravado', price = 80000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'gauntlet5', name = 'Gauntlet Classic Custom', brand = 'Bravado', price = 120000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'hermes', name = 'Hermes', brand = 'Albany', price = 535000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'hotknife', name = 'Hotknife', brand = 'Vapid', price = 90000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'hustler', name = 'Hustler', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'impaler', name = 'Impaler', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'impaler2', name = 'Impaler Arena', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'impaler3', name = 'Impaler Future Shock', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'impaler4', name = 'Impaler Nightmare', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'imperator', name = 'Imperator Arena', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'imperator2', name = 'imperator Future Shock', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'imperator3', name = 'Imperator Nightmare', brand = 'Vapid', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'lurcher', name = 'Lurcher', brand = 'Bravado', price = 21000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'nightshade', name = 'Nightshade', brand = 'Imponte', price = 70000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'phoenix', name = 'Phoenix', brand = 'Imponte', price = 65000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'picador', name = 'Picador', brand = 'Cheval', price = 20000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'ratloader2', name = 'Ratloader', brand = 'Ratloader2', price = 20000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'ruiner', name = 'Ruiner', brand = 'Imponte', price = 29000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'ruiner2', name = 'Ruiner 2000', brand = 'Imponte', price = 50000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'sabregt', name = 'Sabre GT Turbo', brand = 'Declasse', price = 23000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'sabregt2', name = 'Sabre GT Turbo Custom', brand = 'Declasse', price = 26500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'slamvan', name = 'Slam Van', brand = 'Vapid', price = 30000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'slamvan2', name = 'Lost Slam Van', brand = 'Vapid', price = 90000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'slamvan3', name = 'Slam Van Custom', brand = 'Vapid', price = 17000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'stalion', name = 'Stallion', brand = 'Declasse', price = 33000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'stalion2', name = 'Stallion Burgershot', brand = 'Declasse', price = 40000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'tampa', name = 'Tampa', brand = 'Declasse', price = 24500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'tulip', name = 'Tulip', brand = 'Declasse', price = 80000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'vamos', name = 'Vamos', brand = 'Declasse', price = 30000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'vigero', name = 'Vigero', brand = 'Declasse', price = 39500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'virgo', name = 'Virgo', brand = 'Albany', price = 22000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'virgo2', name = 'Virgo Custom Classic', brand = 'Dundreary', price = 21000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'virgo3', name = 'Virgo Classic', brand = 'Dundreary', price = 21000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'voodoo', name = 'Voodoo', brand = 'Declasse', price = 13000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'yosemite', name = 'Yosemite', brand = 'Declasse', price = 19500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'yosemite2', name = 'Yosemite Drift', brand = 'Declasse', price = 55000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'buffalo4', name = 'Buffalo STX', brand = 'Bravado', price = 345000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'manana', name = 'Manana', brand = 'Albany', price = 12800, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'manana2', name = 'Manana Custom', brand = 'Albany', price = 24000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'tampa2', name = 'Drift Tampa', brand = 'Declasse', price = 80000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'ruiner4', name = 'Ruiner ZZ-8', brand = 'Imponte', price = 85000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'vigero2', name = 'Vigero ZX', brand = 'Declasse', price = 105000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'weevil2', name = 'Weevil Custom', brand = 'BF', price = 95000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'buffalo5', name = 'Buffalo EVX', brand = 'Bravado', price = 214000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'tulip2', name = 'Tulip M-100', brand = 'Declasse', price = 80000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'clique2', name = 'Clique Wagon', brand = 'Vapid', price = 102500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'brigham', name = 'Brigham', brand = 'Albany', price = 149900, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'greenwood', name = 'Greenwood', brand = 'Bravado', price = 105000, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'dominator9', name = 'Dominator GT', brand = 'Vapid', price = 219500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'impaler6', name = 'Impaler LX', brand = 'Declasse', price = 146500, category = 'muscle', type = 'automobile', shop = 'pdm' },
{ model = 'vigero3', name = 'Vigero ZX Convertible', brand = 'Declasse', price = 229500, category = 'muscle', type = 'automobile', shop = 'pdm' },
--- Sports Classic (5)
{ model = 'ardent', name = 'Ardent', brand = 'Ocelot', price = 30000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'btype', name = 'Roosevelt', brand = 'Albany', price = 75000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'btype2', name = 'Franken Stange', brand = 'Albany', price = 87000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'btype3', name = 'Roosevelt Valor', brand = 'Albany', price = 63000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'casco', name = 'Casco', brand = 'Lampadati', price = 100000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'deluxo', name = 'Deluxo', brand = 'Imponte', price = 55000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'dynasty', name = 'Dynasty', brand = 'Weeny', price = 25000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'fagaloa', name = 'Fagaloa', brand = 'Vulcar', price = 13000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'feltzer3', name = 'Stirling GT', brand = 'Benefactor', price = 115000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'gt500', name = 'GT500', brand = 'Grotti', price = 130000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'infernus2', name = 'Infernus Classic', brand = 'Pegassi', price = 245000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'jb700', name = 'JB 700', brand = 'Dewbauchee', price = 240000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'jb7002', name = 'JB 700W', brand = 'Dewbauchee', price = 40000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'mamba', name = 'Mamba', brand = 'Declasse', price = 140000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'michelli', name = 'Michelli GT', brand = 'Lampadati', price = 30000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'monroe', name = 'Monroe', brand = 'Pegassi', price = 115000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'nebula', name = 'Nebula', brand = 'Vulcar', price = 22000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'peyote', name = 'Peyote', brand = 'Vapid', price = 23500, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'peyote3', name = 'Peyote Custom', brand = 'Vapid', price = 48000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'pigalle', name = 'Pigalle', brand = 'Lampadati', price = 92000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'rapidgt3', name = 'Rapid GT Classic', brand = 'Dewbauchee', price = 90000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'retinue', name = 'Retinue', brand = 'Vapid', price = 32000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'retinue2', name = 'Retinue MKII', brand = 'Vapid', price = 38000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'savestra', name = 'Savestra', brand = 'Annis', price = 67000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'stinger', name = 'Stinger', brand = 'Grotti', price = 39500, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'stingergt', name = 'Stinger GT', brand = 'Grotti', price = 70000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'stromberg', name = 'Stromberg', brand = 'Ocelot', price = 80000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'swinger', name = 'Swinger', brand = 'Ocelot', price = 221000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'torero', name = 'Torero', brand = 'Pegassi', price = 84000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'tornado', name = 'Tornado', brand = 'Declasse', price = 21000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'tornado2', name = 'Tornado Convertible', brand = 'Declasse', price = 22000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'tornado5', name = 'Tornado Custom', brand = 'Declasse', price = 22000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'turismo2', name = 'Turismo Classic', brand = 'Grotti', price = 170000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'viseris', name = 'Viseris', brand = 'Lampadati', price = 210000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'z190', name = '190Z', brand = 'Karin', price = 78000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'ztype', name = 'Z-Type', brand = 'Truffade', price = 270000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'zion3', name = 'Zion Classic', brand = 'Übermacht', price = 45000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'cheburek', name = 'Cheburek', brand = 'Rune', price = 7000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'toreador', name = 'Toreador', brand = 'Pegassi', price = 50000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'peyote2', name = 'Peyote Gasser', brand = 'Vapid', price = 40000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'coquette2', name = 'Coquette Classic', brand = 'Invetero', price = 165000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'envisage', name = 'Envisage', brand = 'Bollokan', price = 190000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
{ model = 'driftnebula', name = 'Nebula Turbo', brand = 'Vulcar', price = 100000, category = 'sportsclassics', type = 'automobile', shop = 'pdm' },
--- Sports (6)
{ model = 'alpha', name = 'Alpha', brand = 'Albany', price = 53000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'banshee', name = 'Banshee', brand = 'Bravado', price = 56000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'bestiagts', name = 'Bestia GTS', brand = 'Grotti', price = 37000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'buffalo', name = 'Buffalo', brand = 'Bravado', price = 18750, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'buffalo2', name = 'Buffalo S', brand = 'Bravado', price = 24500, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'carbonizzare', name = 'Carbonizzare', brand = 'Grotti', price = 155000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'comet2', name = 'Comet', brand = 'Pfister', price = 130000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'comet3', name = 'Comet Retro Custom', brand = 'Pfister', price = 175000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'comet4', name = 'Comet Safari', brand = 'Pfister', price = 110000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'comet5', name = 'Comet SR', brand = 'Pfister', price = 155000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'coquette', name = 'Coquette', brand = 'Invetero', price = 145000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'coquette4', name = 'Coquette D10', brand = 'Invetero', price = 220000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'drafter', name = '8F Drafter', brand = 'Obey', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'elegy', name = 'Elegy Retro Custom', brand = 'Annis', price = 145000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'elegy2', name = 'Elegy RH8', brand = 'Annis', price = 150000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'feltzer2', name = 'Feltzer', brand = 'Benefactor', price = 97000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'flashgt', name = 'Flash GT', brand = 'Vapid', price = 48000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'furoregt', name = 'Furore GT', brand = 'Lampadati', price = 78000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'gb200', name = 'GB 200', brand = 'Vapid', price = 140000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'komoda', name = 'Komoda', brand = 'Lampadati', price = 55000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'imorgon', name = 'Imorgon', brand = 'Överflöd', price = 120000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'italigto', name = 'Itali GTO', brand = 'Progen', price = 260000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'jugular', name = 'Jugular', brand = 'Ocelot', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'jester', name = 'Jester', brand = 'Dinka', price = 132250, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'jester2', name = 'Jester Racecar', brand = 'Dinka', price = 210000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'jester3', name = 'Jester Classic', brand = 'Dinka', price = 85000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'khamelion', name = 'Khamelion', brand = 'Hijak', price = 90000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'kuruma', name = 'Kuruma', brand = 'Karin', price = 72000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'kuruma2', name = 'kuruma2', brand = 'Karin2', price = 72000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'locust', name = 'Locust', brand = 'Ocelot', price = 200000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'lynx', name = 'Lynx', brand = 'Ocelot', price = 150000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'massacro', name = 'Massacro', brand = 'Dewbauchee', price = 110000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'massacro2', name = 'Massacro Racecar', brand = 'Dewbauchee', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'neo', name = 'Neo', brand = 'Vysser', price = 230000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'neon', name = 'Neon', brand = 'Pfister', price = 220000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'ninef', name = '9F', brand = 'Obey', price = 95000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'ninef2', name = '9F Cabrio', brand = 'Obey', price = 105000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'omnis', name = 'Omnis', brand = 'Wow', price = 90000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'paragon', name = 'Paragon', brand = 'Enus', price = 60000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'pariah', name = 'Pariah', brand = 'Ocelot', price = 90000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'penumbra', name = 'Penumbra', brand = 'Maibatsu', price = 22000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'penumbra2', name = 'Penumbra FF', brand = 'Maibatsu', price = 30000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'rapidgt', name = 'Rapid GT', brand = 'Dewbauchee', price = 86000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'rapidgt2', name = 'Rapid GT Convertible', brand = 'Dewbauchee', price = 92000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'raptor', name = 'Raptor', brand = 'BF', price = 90000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'revolter', name = 'Revolter', brand = 'Übermacht', price = 95000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'ruston', name = 'Ruston', brand = 'Hijak', price = 130000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'schafter3', name = 'Schafter V12', brand = 'Benefactor', price = 35000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'schafter4', name = 'Schafter LWB', brand = 'Benefactor', price = 21000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'schlagen', name = 'Schlagen GT', brand = 'Benefactor', price = 160000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'schwarzer', name = 'Schwartzer', brand = 'Benefactor', price = 47000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'seven70', name = 'Seven-70', brand = 'Dewbauchee', price = 140000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'specter', name = 'Specter', brand = 'Dewbauchee', price = 160000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'streiter', name = 'Streiter', brand = 'Benefactor', price = 40000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sugoi', name = 'Sugoi', brand = 'Dinka', price = 85000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sultan', name = 'Sultan', brand = 'Karin', price = 50000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sultan2', name = 'Sultan Custom', brand = 'Karin', price = 55000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'surano', name = 'Surano', brand = 'Benefactor', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'tropos', name = 'Tropos Rallye', brand = 'Lampadati', price = 65000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'verlierer2', name = 'Verlierer', brand = 'Bravado', price = 90500, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'vstr', name = 'V-STR', brand = 'Albany', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'italirsx', name = 'Itali RSX', brand = 'Progen', price = 260000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'zr350', name = 'ZR350', brand = 'Annis', price = 38000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'calico', name = 'Calico GTF', brand = 'Karin', price = 39000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'futo2', name = 'Futo GTX', brand = 'Karin', price = 39000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'euros', name = 'Euros', brand = 'Annis', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'jester4', name = 'Jester RR', brand = 'Dinka', price = 240000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'remus', name = 'Remus', brand = 'Annis', price = 48000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'comet6', name = 'Comet S2', brand = 'Pfister', price = 230000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'growler', name = 'Growler', brand = 'Pfister', price = 205000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'vectre', name = 'Vectre', brand = 'Emperor', price = 80000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'cypher', name = 'Cypher', brand = 'Übermacht', price = 155000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sultan3', name = 'Sultan Classic Custom', brand = 'Karin', price = 56000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'rt3000', name = 'RT3000', brand = 'Dinka', price = 65000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sultanrs', name = 'Sultan RS', brand = 'Karin', price = 76500, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'visione', name = 'Visione', brand = 'Grotti', price = 750000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'cheetah2', name = 'Cheetah Classic', brand = 'Grotti', price = 195000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'stingertt', name = 'Itali GTO Stinger TT', brand = 'Maibatsu', price = 238000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'omnisegt', name = 'Omnis e-GT', brand = 'Obey', price = 185000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sentinel4', name = 'Sentinel Classic Widebody', brand = 'Übermacht', price = 140000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'sm722', name = 'SM722', brand = 'Benefactor', price = 125000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'tenf', name = '10F', brand = 'Obey', price = 185000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'tenf2', name = '10F Widebody', brand = 'Obey', price = 215000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'everon2', name = 'Everon Hotring', brand = 'Karin', price = 80000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'issi8', name = 'Issi Rally', brand = 'Weeny', price = 10000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'corsita', name = 'Corsita', brand = 'Lampadati', price = 90000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'gauntlet6', name = 'Hotring Hellfire', brand = 'Bravado', price = 181000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'coureur', name = 'La Coureuse', brand = 'Penaud', price = 199000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'r300', name = '300R', brand = 'Annis', price = 56000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'panthere', name = 'Panthere', brand = 'Toundra', price = 55000, category = 'sports', type = 'automobile', shop = 'pdm' },
{ model = 'driftsentinel', name = 'Drift Sentinel Classic', brand = 'Ubermacht', price = 150000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'paragon3', name = 'Paragon S', brand = 'Enus', price = 220000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'eurosX32', name = 'Euros X32', brand = 'Annis', price = 180000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'vorschlaghammer', name = 'Vorschlaghammer', brand = 'Pfister', price = 250000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'driftcypher', name = 'Drift Cypher', brand = 'Ubermacht', price = 160000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'coquette5', name = 'Coquette D1', brand = 'Invetero', price = 220000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'pipistrello', name = 'Pipistrello', brand = 'Overflod', price = 240000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'niobe', name = 'Niobe', brand = 'Ubermacht', price = 180000, category = 'sports', type = 'automobile', shop = 'luxury' },
{ model = 'driftvorschlag', name = 'Vorschlaghammer', brand = 'Pfister', price = 250000, category = 'sports', type = 'automobile', shop = 'luxury' },
--- Super (7)
{ model = 'adder', name = 'Adder', brand = 'Truffade', price = 280000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'autarch', name = 'Autarch', brand = 'Överflöd', price = 224000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'banshee2', name = 'Banshee 900R', brand = 'Bravado', price = 120000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'bullet', name = 'Bullet', brand = 'Vapid', price = 120000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'cheetah', name = 'Cheetah', brand = 'Grotti', price = 214000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'cyclone', name = 'Cyclone', brand = 'Coil', price = 300000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'entity2', name = 'Entity XXR', brand = 'Överflöd', price = 164000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'entityxf', name = 'Entity XF', brand = 'Överflöd', price = 180000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'emerus', name = 'Emerus', brand = 'Progen', price = 220000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'fmj', name = 'FMJ', brand = 'Vapid', price = 125000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'furia', name = 'Furia', brand = 'Grotti', price = 230000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'gp1', name = 'GP1', brand = 'Progen', price = 110000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'infernus', name = 'Infernus', brand = 'Pegassi', price = 235000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'italigtb', name = 'Itali GTB', brand = 'Progen', price = 170000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'italigtb2', name = 'Itali GTB Custom', brand = 'Progen', price = 250000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'krieger', name = 'Krieger', brand = 'Benefactor', price = 222000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'le7b', name = 'RE-7B', brand = 'Annis', price = 260000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'nero', name = 'Nero', brand = 'Truffade', price = 200000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'nero2', name = 'Nero Custom', brand = 'Truffade', price = 260000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'osiris', name = 'Osiris', brand = 'Pegassi', price = 220000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'penetrator', name = 'Penetrator', brand = 'Ocelot', price = 130000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'pfister811', name = '811', brand = 'Pfister', price = 220000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'prototipo', name = 'X80 Proto', brand = 'Grotti', price = 235000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'reaper', name = 'Reaper', brand = 'Pegassi', price = 100000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 's80', name = 'S80RR', brand = 'Annis', price = 205000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'sc1', name = 'SC1', brand = 'Übermacht', price = 90000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'sheava', name = 'ETR1', brand = 'Emperor', price = 220000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 't20', name = 'T20', brand = 'Progen', price = 1650000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'taipan', name = 'Taipan', brand = 'Cheval', price = 1850000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'tempesta', name = 'Tempesta', brand = 'Pegassi', price = 120000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'tezeract', name = 'Tezeract', brand = 'Pegassi', price = 220000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'thrax', name = 'Thrax', brand = 'Truffade', price = 180000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'tigon', name = 'Tigon', brand = 'Lampadati', price = 240000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'turismor', name = 'Turismo R', brand = 'Grotti', price = 140000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'tyrant', name = 'Tyrant', brand = 'Överflöd', price = 2100000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'tyrus', name = 'Tyrus', brand = 'Progen', price = 230000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'vacca', name = 'Vacca', brand = 'Pegassi', price = 105000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'vagner', name = 'Vagner', brand = 'Dewbauchee', price = 1660000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'voltic', name = 'Voltic', brand = 'Coil', price = 120000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'voltic2', name = 'Rocket Voltic', brand = 'Coil', price = 9830400, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'xa21', name = 'XA-21', brand = 'Ocelot', price = 180000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'zentorno', name = 'Zentorno', brand = 'Pegassi', price = 340000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'zorrusso', name = 'Zorrusso', brand = 'Pegassi', price = 277000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'ignus', name = 'Ignus', brand = 'Pegassi', price = 1120000, category = 'super', type = 'automobile', shop = 'pdm' },
{ model = 'zeno', name = 'Zeno', brand = 'Överflöd', price = 1350000, category = 'super', type = 'automobile', shop = 'pdm' },
{ model = 'deveste', name = 'Deveste', brand = 'Principe', price = 234000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'lm87', name = 'LM87', brand = 'Benefactor', price = 155000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'torero2', name = 'Torero XO', brand = 'Pegassi', price = 245000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'entity3', name = 'Entity MT', brand = 'Overflod', price = 200000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'virtue', name = 'Virtue', brand = 'Ocelot', price = 72000, category = 'super', type = 'automobile', shop = 'luxury' },
{ model = 'turismo3', name = 'Turismo Omaggio', brand = 'Grotti', price = 284500, category = 'super', type = 'automobile', shop = 'luxury' },
--- Motorcycles (8)
{ model = 'akuma', name = 'Akuma', brand = 'Dinka', price = 55000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'avarus', name = 'Avarus', brand = 'LCC', price = 20000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'bagger', name = 'Bagger', brand = 'WMC', price = 13500, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'bati', name = 'Bati 801', brand = 'Pegassi', price = 24000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'bati2', name = 'Bati 801RR', brand = 'Pegassi', price = 19000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'bf400', name = 'BF400', brand = 'Nagasaki', price = 22000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'carbonrs', name = 'Carbon RS', brand = 'Nagasaki', price = 22000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'chimera', name = 'Chimera', brand = 'Nagasaki', price = 21000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'cliffhanger', name = 'Cliffhanger', brand = 'Western', price = 28500, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'daemon', name = 'Daemon', brand = 'WMC', price = 14000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'daemon2', name = 'Daemon Custom', brand = 'Western', price = 23000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'defiler', name = 'Defiler', brand = 'Shitzu', price = 30000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'deathbike', name = 'Deathbike Apocalypse', brand = 'Deathbike', price = 30000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'deathbike2', name = 'Deathbike Future Shock', brand = 'Deathbike', price = 30000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'deathbike3', name = 'Deathbike Nightmare', brand = 'Deathbike', price = 30000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'diablous', name = 'Diablous', brand = 'Principe', price = 30000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'diablous2', name = 'Diablous Custom', brand = 'Principe', price = 38000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'double', name = 'Double-T', brand = 'Dinka', price = 28000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'enduro', name = 'Enduro', brand = 'Dinka', price = 5500, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'esskey', name = 'Esskey', brand = 'Pegassi', price = 12000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'faggio', name = 'Faggio Sport', brand = 'Pegassi', price = 2000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'faggio2', name = 'Faggio', brand = 'Pegassi', price = 1900, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'faggio3', name = 'Faggio Mod', brand = 'Pegassi', price = 2500, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'fcr', name = 'FCR 1000', brand = 'Pegassi', price = 5000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'fcr2', name = 'FCR 1000 Custom', brand = 'Pegassi', price = 19000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'gargoyle', name = 'Gargoyle', brand = 'Western', price = 32000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'hakuchou', name = 'Hakuchou', brand = 'Shitzu', price = 17000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'hakuchou2', name = 'Hakuchou Drag', brand = 'Shitzu', price = 45000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'hexer', name = 'Hexer', brand = 'LCC', price = 16000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'innovation', name = 'Innovation', brand = 'LLC', price = 33500, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'lectro', name = 'Lectro', brand = 'Principe', price = 28000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'manchez', name = 'Manchez', brand = 'Maibatsu', price = 8300, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'nemesis', name = 'Nemesis', brand = 'Principe', price = 20000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'nightblade', name = 'Nightblade', brand = 'WMC', price = 23000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'oppressor', name = 'Oppressor', brand = 'Pegassi', price = 9999999, category = 'motorcycles', type = 'bike', shop = 'luxury' },
{ model = 'pcj', name = 'PCJ-600', brand = 'Shitzu', price = 15000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'ratbike', name = 'Rat Bike', brand = 'Western', price = 3000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'ruffian', name = 'Ruffian', brand = 'Pegassi', price = 25000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'sanchez', name = 'Sanchez Livery', brand = 'Maibatsu', price = 5300, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'sanchez2', name = 'Sanchez', brand = 'Maibatsu', price = 5300, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'sanctus', name = 'Sanctus', brand = 'LCC', price = 35000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'shotaro', name = 'Shotaro', brand = 'Nagasaki', price = 320000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'sovereign', name = 'Sovereign', brand = 'WMC', price = 8000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'stryder', name = 'Stryder', brand = 'Nagasaki', price = 50000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'thrust', name = 'Thrust', brand = 'Dinka', price = 22000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'vader', name = 'Vader', brand = 'Shitzu', price = 7200, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'vindicator', name = 'Vindicator', brand = 'Dinka', price = 19000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'vortex', name = 'Vortex', brand = 'Pegassi', price = 31000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'wolfsbane', name = 'Wolfsbane', brand = 'Western', price = 14000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'zombiea', name = 'Zombie Bobber', brand = 'Western', price = 28000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'zombieb', name = 'Zombie Chopper', brand = 'Western', price = 27000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'manchez2', name = 'Manchez Scout', brand = 'Maibatsu', price = 14000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'shinobi', name = 'Shinobi', brand = 'Nagasaki', price = 25000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'reever', name = 'Reever', brand = 'Western', price = 25000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'manchez3', name = 'Manchez Scout Classic', brand = 'Maibatsu', price = 15000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'powersurge', name = 'Powersurge', brand = 'Western', price = 7000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
{ model = 'pizzaboy', name = 'Pizza Boy', brand = 'Pegassi', price = 50000, category = 'motorcycles', type = 'bike', shop = 'pdm' },
--- Off-Road (9)
{ model = 'bfinjection', name = 'Bf Injection', brand = 'Annis', price = 9000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'bifta', name = 'Bifta', brand = 'Annis', price = 15500, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'blazer', name = 'Blazer', brand = 'Annis', price = 7500, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'blazer2', name = 'Blazer Lifeguard', brand = 'Nagasaki', price = 7000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'blazer3', name = 'Blazer Hot Rod', brand = 'Nagasaki', price = 7000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'blazer4', name = 'Blazer Sport', brand = 'Annis', price = 9250, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'blazer5', name = 'Blazer Aqua', brand = 'Nagasaki', price = 40000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'brawler', name = 'Brawler', brand = 'Annis', price = 40000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'caracara', name = 'Caracara', brand = 'Vapid', price = 60000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'caracara2', name = 'Caracara 4x4', brand = 'Vapid', price = 80000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'dubsta3', name = 'Dubsta 6x6', brand = 'Annis', price = 34000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'dune', name = 'Dune Buggy', brand = 'Annis', price = 14000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'everon', name = 'Everon', brand = 'Karin', price = 60000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'freecrawler', name = 'Freecrawler', brand = 'Canis', price = 24000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'hellion', name = 'Hellion', brand = 'Annis', price = 38000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'kalahari', name = 'Kalahari', brand = 'Canis', price = 14000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'kamacho', name = 'Kamacho', brand = 'Canis', price = 50000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'mesa3', name = 'Mesa Merryweather', brand = 'Canis', price = 400000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'outlaw', name = 'Outlaw', brand = 'Nagasaki', price = 15000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'rancherxl', name = 'Rancher XL', brand = 'Declasse', price = 24000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'rebel2', name = 'Rebel', brand = 'Vapid', price = 20000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'riata', name = 'Riata', brand = 'Vapid', price = 380000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'sandking', name = 'Sandking XL', brand = 'Vapid', price = 25000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'sandking2', name = 'Sandking SWB', brand = 'Vapid', price = 38000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'trophytruck', name = 'Trophy Truck', brand = 'Vapid', price = 60000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'trophytruck2', name = 'Desert Raid', brand = 'Vapid', price = 80000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'vagrant', name = 'Vagrant', brand = 'Maxwell', price = 50000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'verus', name = 'Verus', brand = 'Dinka', price = 20000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'winky', name = 'Winky', brand = 'Vapid', price = 10000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'yosemite3', name = 'Yosemite Rancher', brand = 'Declasse', price = 425000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'mesa', name = 'Mesa', brand = 'Canis', price = 12000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'ratel', name = 'Ratel', brand = 'Vapid', price = 199000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'l35', name = 'Walton L35', brand = 'Declasse', price = 167000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'monstrociti', name = 'MonstroCiti', brand = 'Maibatsu', price = 48000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'draugur', name = 'Draugur', brand = 'Declasse', price = 99000, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'terminus', name = 'Terminus', brand = 'Canis', price = 187750, category = 'offroad', type = 'automobile', shop = 'pdm' },
{ model = 'yosemite4', name = 'Yosemite 1500', brand = 'Declasse', price = 187750, category = 'offroad', type = 'automobile', shop = 'pdm' },
--- Industrial (10)
{ model = 'guardian', name = 'Guardian', brand = 'Vapid', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'mixer2', name = 'Mixer II', brand = 'HVY', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'tiptruck2', name = 'Tipper II', brand = 'Brute', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'tiptruck', name = 'Tipper', brand = 'Brute', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'rubble', name = 'Rubble', brand = 'Jobuilt', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'mixer', name = 'Mixer', brand = 'HVY', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'flatbed', name = 'Flatbed Truck', brand = 'MTL', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'dump', name = 'Dump Truck', brand = 'HVY', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'bulldozer', name = 'Dozer', brand = 'HVY', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'handler', name = 'Dock Handler', brand = 'HVY', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
{ model = 'cutter', name = 'Cutter', brand = 'HVY', price = 30000, category = 'industrial', type = 'automobile', shop = 'truck' },
--- Utility (11)
{ model = 'slamtruck', name = 'Slam Truck', brand = 'Vapid', price = 100000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'caddy3', name = 'Caddy (Bunker)', brand = 'Nagasaki', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'caddy2', name = 'Caddy (Civilian)', brand = 'Nagasaki', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'caddy3', name = 'Caddy (Golf)', brand = 'Nagasaki', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'utillitruck', name = 'Utility Truck (Cherry Picker)', brand = 'Brute', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'utillitruck2', name = 'Utility Truck (Van)', brand = 'Brute', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'utillitruck3', name = 'Utility Truck (Contender)', brand = 'Vapid', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'tractor', name = 'Tractor', brand = 'Stanley', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'tractor2', name = 'Fieldmaster', brand = 'Stanley', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'tractor3', name = 'Fieldmaster', brand = 'Stanley', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'towtruck', name = 'Tow Truck (Large)', brand = 'Vapid', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'towtruck2', name = 'Tow Truck (Small)', brand = 'Vapid', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'scrap', name = 'Scrap Truck', brand = 'Vapid', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'sadler', name = 'Sadler', brand = 'Vapid', price = 20000, category = 'utility', type = 'automobile', shop = 'pdm' },
{ model = 'ripley', name = 'Ripley', brand = 'HVY', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'mower', name = 'Lawn Mower', brand = 'Jacksheepe', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'forklift', name = 'Forklift', brand = 'HVY', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'docktug', name = 'Docktug', brand = 'HVY', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'airtug', name = 'Airtug', brand = 'HVY', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'trailers5', name = 'Trailer (Christmas)', brand = 'Unknown', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
{ model = 'tvtrailer2', name = 'Trailer (TV)', brand = 'Unknown', price = 30000, category = 'utility', type = 'automobile', shop = 'truck' },
--- Vans (12)
{ model = 'bison', name = 'Bison', brand = 'Bravado', price = 18000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'bobcatxl', name = 'Bobcat XL Open', brand = 'Vapid', price = 13500, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'burrito3', name = 'Burrito', brand = 'Declasse', price = 4000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'gburrito2', name = 'Burrito Custom', brand = 'Declasse', price = 11500, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'rumpo', name = 'Rumpo', brand = 'Bravado', price = 9000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'journey', name = 'Journey', brand = 'Zirconium', price = 6500, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'minivan', name = 'Minivan', brand = 'Vapid', price = 7000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'minivan2', name = 'Minivan Custom', brand = 'Vapid', price = 10000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'paradise', name = 'Paradise', brand = 'Bravado', price = 9000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'rumpo3', name = 'Rumpo Custom', brand = 'Bravado', price = 19500, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'speedo', name = 'Speedo', brand = 'Vapid', price = 10000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'speedo4', name = 'Speedo Custom', brand = 'Vapid', price = 15000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'surfer', name = 'Surfer', brand = 'BF', price = 9000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'youga3', name = 'Youga Classic 4x4', brand = 'Bravado', price = 15000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'youga', name = 'Youga', brand = 'Bravado', price = 8000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'youga2', name = 'Youga Classic', brand = 'Bravado', price = 14500, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'youga4', name = 'Youga Custom', brand = 'Bravado', price = 85000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'moonbeam', name = 'Moonbeam', brand = 'Declasse', price = 13000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'moonbeam2', name = 'Moonbeam Custom', brand = 'Declasse', price = 15000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'boxville', name = 'Boxville LSDWP', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'truck' },
{ model = 'boxville2', name = 'Boxville Go Postal', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'truck' },
{ model = 'boxville3', name = 'Boxville Humane Labs', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'truck' },
{ model = 'boxville4', name = 'Boxville Post OP', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'truck' },
{ model = 'boxville5', name = 'Armored Boxville', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'none' },
{ model = 'pony', name = 'Pony', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'truck' },
{ model = 'pony2', name = 'Pony (Smoke on the water)', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'truck' },
{ model = 'journey2', name = 'Journey II', brand = 'Zirconium', price = 7000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'surfer3', name = 'Surfer Custom', brand = 'BF', price = 15000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'speedo5', name = 'Speedo Custom', brand = 'Vapid', price = 238000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'mule2', name = 'Mule', brand = 'Maibatsu', price = 40000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'mule3', name = 'Mule', brand = 'Maibatsu', price = 40000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'taco', name = 'Taco Truck', brand = 'Brute', price = 45000, category = 'vans', type = 'automobile', shop = 'pdm' },
{ model = 'boxville6', name = 'Boxville (LSDS)', brand = 'Brute', price = 47500, category = 'vans', type = 'automobile', shop = 'pdm' },
--- Cycles (13)
{ model = 'bmx', name = 'BMX', brand = 'Bike', price = 160, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'cruiser', name = 'Cruiser', brand = 'Bike', price = 510, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'fixter', name = 'Fixter', brand = 'Bike', price = 225, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'scorcher', name = 'Scorcher', brand = 'Bike', price = 280, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'tribike', name = 'Whippet Race Bike', brand = 'Bike', price = 500, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'tribike2', name = 'Endurex Race Bike', brand = 'Bike', price = 700, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'tribike3', name = 'Tri-Cycles Race Bike', brand = 'Bike', price = 520, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'inductor', name = 'Inductor', brand = 'Coil', price = 5000, category = 'cycles', type = 'bike', shop = 'pdm' },
{ model = 'inductor2', name = 'Junk Energy Inductor', brand = 'Coil', price = 5000, category = 'cycles', type = 'bike', shop = 'pdm' },
--- Boats (14)
{ model = 'avisa', name = 'Avisa', brand = 'Kraken Subs', price = 40000, category = 'boats', type = 'boat', shop = 'none' },
{ model = 'patrolboat', name = 'Kurtz 31 Patrol Boat', brand = 'Unknown', price = 40000, category = 'boats', type = 'boat', shop = 'none' },
{ model = 'longfin', name = 'Longfin', brand = 'Shitzu', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'tug', name = 'Tug', brand = 'Buckingham', price = 40000, category = 'boats', type = 'boat', shop = 'none' },
{ model = 'toro', name = 'Toro', brand = 'Lampadati', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'toro2', name = 'Toro Yacht', brand = 'Lampadati', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'submersible2', name = 'Kraken', brand = 'Kraken Subs', price = 40000, category = 'boats', type = 'boat', shop = 'none' },
{ model = 'speeder', name = 'Speeder', brand = 'Pegassi', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'speeder2', name = 'Speeder Yacht', brand = 'Pegassi', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'tropic', name = 'Tropic', brand = 'Shitzu', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'tropic2', name = 'Tropic Yacht', brand = 'Shitzu', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'suntrap', name = 'Suntrap', brand = 'Shitzu', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'submersible', name = 'Submersible', brand = 'Kraken Subs', price = 40000, category = 'boats', type = 'boat', shop = 'none' },
{ model = 'squalo', name = 'Squalo', brand = 'Shitzu', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'seashark', name = 'Seashark', brand = 'Speedophile', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'seashark3', name = 'Seashark Yacht', brand = 'Speedophile', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'marquis', name = 'Marquis', brand = 'Dinka', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'jetmax', name = 'Jetmax', brand = 'Shitzu', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'dinghy', name = 'Dinghy 2-Seater', brand = 'Nagasaki', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'dinghy2', name = 'Dinghy 4-Seater', brand = 'Nagasaki', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'dinghy3', name = 'Dinghy (Heist)', brand = 'Nagasaki', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
{ model = 'dinghy4', name = 'Dinghy Yacht', brand = 'Nagasaki', price = 40000, category = 'boats', type = 'boat', shop = 'boats' },
--- Helicopters (15)
{ model = 'conada2', name = 'Weaponized Conada', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'conada', name = 'Conada', brand = 'Buckingham', price = 115000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'seasparrow2', name = 'Sparrow', brand = 'Unknown', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'annihilator2', name = 'Annihilator Stealth', brand = 'Western Company', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'seasparrow', name = 'Sea Sparrow', brand = 'Unknown', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'akula', name = 'Akula', brand = 'Unknown', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'hunter', name = 'FH-1 Hunter', brand = 'Unknown', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'havok', name = 'Havok', brand = 'Nagasaki', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'volatus', name = 'Volatus', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'supervolito2', name = 'SuperVolito Carbon', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'supervolito', name = 'SuperVolito', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'swift2', name = 'Swift Deluxe', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'valkyrie', name = 'Valkyrie', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'savage', name = 'Savage', brand = 'Unknown', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'swift', name = 'Swift', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'annihilator', name = 'Annihilator', brand = 'Western Company', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'cargobob2', name = 'Cargobob Jetsam', brand = 'Western Company', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'skylift', name = 'Skylift', brand = 'HVY', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'maverick', name = 'Maverick', brand = 'Buckingham', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'frogger', name = 'Frogger', brand = 'Maibatsu', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'frogger2', name = 'Frogger', brand = 'Maibatsu', price = 52000, category = 'helicopters', type = 'heli', shop = 'air' },
{ model = 'cargobob', name = 'Cargobob', brand = 'Western Company', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'cargobob3', name = 'Cargobob', brand = 'Western Company', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'seasparrow3', name = 'Sparrow (Prop)', brand = 'Unknown', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'buzzard', name = 'Buzzard Attack Chopper', brand = 'Nagasaki', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
{ model = 'buzzard2', name = 'Buzzard', brand = 'Nagasaki', price = 52000, category = 'helicopters', type = 'heli', shop = 'none' },
--- Planes (16)
{ model = 'streamer216', name = 'Streamer216', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'raiju', name = 'F-160 Raiju', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'alkonost', name = 'RO-86 Alkonost', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'strikeforce', name = 'B-11 Strikeforce', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'blimp3', name = 'Blimp', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'avenger', name = 'Avenger', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'avenger2', name = 'Avenger', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'volatol', name = 'Volatol', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'nokota', name = 'P-45 Nokota', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'seabreeze', name = 'Seabreeze', brand = 'Western Company', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'pyro', name = 'Pyro', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'mogul', name = 'Mogul', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'howard', name = 'Howard NX-25', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'bombushka', name = 'RM-10 Bombushka', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'molotok', name = 'V-65 Molotok', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'microlight', name = 'Ultralight', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'tula', name = 'Tula', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'rogue', name = 'Rogue', brand = 'Western Company', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'starling', name = 'LF-22 Starling', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'alphaz1', name = 'Alpha-Z1', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'nimbus', name = 'Nimbus', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'luxor2', name = 'Luxor Deluxe', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'velum2', name = 'Velum 5-seater', brand = 'JoBuilt', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'hydra', name = 'Hydra', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'blimp2', name = 'Xero Blimp', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'dodo', name = 'Dodo', brand = 'Mammoth', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'miljet', name = 'Miljet', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'besra', name = 'Besra', brand = 'Western Company', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'vestra', name = 'Vestra', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'cargoplane', name = 'Cargo Plane', brand = 'JoBuilt', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'velum', name = 'Velum', brand = 'JoBuilt', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'titan', name = 'Titan', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'shamal', name = 'Shamal', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'lazer', name = 'P-996 Lazer', brand = 'JoBuilt', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'mammatus', name = 'Mammatus', brand = 'JoBuilt', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'stunt', name = 'Mallard', brand = 'Western Company', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'luxor', name = 'Luxor', brand = 'Buckingham', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'jet', name = 'Jet', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
{ model = 'duster', name = 'Duster', brand = 'Western Company', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'cuban800', name = 'Cuban 800', brand = 'Western Company', price = 45000, category = 'planes', type = 'plane', shop = 'air' },
{ model = 'blimp', name = 'Atomic Blimp', brand = 'Unknown', price = 45000, category = 'planes', type = 'plane', shop = 'none' },
--- Service (17)
{ model = 'brickade', name = 'Brickade', brand = 'MTL', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'brickade2', name = 'Brickade 6x6', brand = 'MTL', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'pbus2', name = 'Festival Bus', brand = 'Unknown', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'wastelander', name = 'Wastelander', brand = 'MTL', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'rallytruck', name = 'Dune', brand = 'MTL', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'metrotrain', name = 'Metro Train', brand = 'Unknown', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'freight', name = 'Freight Train', brand = 'Unknown', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'cablecar', name = 'Cable Car', brand = 'Unknown', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'trash', name = 'Trashmaster', brand = 'JoBuilt', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'trash2', name = 'Trashmaster', brand = 'JoBuilt', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'tourbus', name = 'Tour Bus', brand = 'Brute', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'taxi', name = 'Taxi', brand = 'Vapid', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'rentalbus', name = 'Rental Shuttle Bus', brand = 'Brute', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'coach', name = 'Dashound', brand = 'Brute', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'bus', name = 'Bus', brand = 'Brute', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
{ model = 'airbus', name = 'Airport Bus', brand = 'Brute', price = 100000, category = 'service', type = 'automobile', shop = 'none' },
--- Emergency (18)
{ model = 'riot', name = 'Police Riot', brand = 'Brute', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'riot2', name = 'RCV', brand = 'Unknown', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'pbus', name = 'Police Prison Bus', brand = 'Vapid', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'police', name = 'Police Cruiser', brand = 'Vapid', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'police2', name = 'Police Buffalo', brand = 'Vapid', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'police3', name = 'Police Interceptor', brand = 'Vapid', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'police4', name = 'Unmarked Cruiser', brand = 'Vapid', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'sheriff', name = 'Sheriff SUV', brand = 'Declasse', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'sheriff2', name = 'Sheriff Cruiser', brand = 'Vapid', price = 100000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'policeold1', name = 'Police Rancher', brand = 'Declasse', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'policeold2', name = 'Police Roadcruiser', brand = 'Albany', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'policet', name = 'Police Transporter', brand = 'Vapid', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'policeb', name = 'Police Bike', brand = 'Vapid', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'polmav', name = 'Police Maverick', brand = 'Buckingham', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'ambulance', name = 'Ambulance', brand = 'Brute', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'firetruk', name = 'Fire Truck', brand = 'MTL', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'lguard', name = 'Lifeguard', brand = 'Declasse', price = 110000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'seashark2', name = 'Seashark Lifeguard', brand = 'Speedophile', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'pranger', name = 'Park Ranger', brand = 'Declasse', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'fbi', name = 'FIB Buffalo', brand = 'Bravado', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'fbi2', name = 'FIB Granger', brand = 'Declasse', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'predator', name = 'Police Predator', brand = 'Unknown', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'polgauntlet', name = 'Gauntlet Interceptor', brand = 'Bravado', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'police5', name = 'Stanier LE Cruiser', brand = 'Vapid', price = 40000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'polimpaler5', name = 'Impaler SZ Cruiser', brand = 'Declasse', price = 80000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'polimpaler6', name = 'Impaler LX Cruiser', brand = 'Declasse', price = 90000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'poldominator10', name = 'Dominator FX Interceptor', brand = 'Vapid', price = 230000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'policet3', name = 'Burrito (Bail Enforcement)', brand = 'Declasse', price = 60000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'polgreenwood', name = 'Greenwood Cruiser', brand = 'Bravado', price = 80000, category = 'emergency', type = 'automobile', shop = 'none' },
{ model = 'poldorado', name = 'Dorado Cruiser', brand = 'Vapid', price = 80000, category = 'emergency', type = 'automobile', shop = 'none' },
--- Military (19)
{ model = 'vetir', name = 'Vetir', brand = 'Unknown', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'kosatka', name = 'Kosatka', brand = 'Rune', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'minitank', name = 'RC Tank', brand = 'Unknown', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'scarab', name = 'Scarab', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'terbyte', name = 'Terrorbyte', brand = 'Benefactor', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'thruster', name = 'Thruster', brand = 'Mammoth', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'khanjali', name = 'TM-02 Khanjali Tank', brand = 'Unknown', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'chernobog', name = 'Chernobog', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'barrage', name = 'Barrage', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'trailerlarge', name = 'Mobile Operations Center', brand = 'Unknown', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'halftrack', name = 'Half-track', brand = 'Bravado', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'apc', name = 'APC Tank', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'trailersmall2', name = 'Anti-Aircraft Trailer', brand = 'Vom Feuer', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'rhino', name = 'Rhino Tank', brand = 'Unknown', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'crusader', name = 'Crusader', brand = 'Canis', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'barracks', name = 'Barracks', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'barracks2', name = 'Barracks Semi', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
{ model = 'barracks3', name = 'Barracks', brand = 'HVY', price = 100000, category = 'military', type = 'automobile', shop = 'none' },
--- Commercial (20)
{ model = 'cerberus', name = 'Apocalypse Cerberus', brand = 'MTL', price = 100000, category = 'commercial', type = 'automobile', shop = 'none' },
{ model = 'pounder2', name = 'Pounder Custom', brand = 'MTL', price = 55000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'mule4', name = 'Mule Custom', brand = 'Maibatsu', price = 40000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'phantom3', name = 'Phantom Custom', brand = 'Jobuilt', price = 110000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'hauler2', name = 'Hauler Custom', brand = 'Jobuilt', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'phantom2', name = 'Phantom Wedge', brand = 'Jobuilt', price = 100000, category = 'commercial', type = 'automobile', shop = 'none' },
{ model = 'mule5', name = 'Mule (Heist)', brand = 'Maibatsu', price = 40000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'stockade', name = 'Stockade', brand = 'Brute', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'pounder', name = 'Pounder', brand = 'MTL', price = 55000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'phantom', name = 'Phantom', brand = 'Jobuilt', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'packer', name = 'Packer', brand = 'MTL', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'mule', name = 'Mule', brand = 'Maibatsu', price = 40000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'hauler', name = 'Hauler', brand = 'Jobuilt', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'biff', name = 'Biff', brand = 'Brute', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'benson', name = 'Benson', brand = 'Vapid', price = 55000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'benson2', name = 'Benson (Cluckin Bell)', brand = 'Vapid', price = 55000, category = 'commercial', type = 'automobile', shop = 'truck' },
{ model = 'phantom4', name = 'Phantom (Christmas)', brand = 'Vapid', price = 100000, category = 'commercial', type = 'automobile', shop = 'truck' },
--- Trains (21)
--- Open Wheel (22)
{ model = 'openwheel2', name = 'DR1', brand = 'Declasse', price = 100000, category = 'openwheel', type = 'automobile', shop = 'none' },
{ model = 'openwheel1', name = 'BR8', brand = 'Benefactor', price = 100000, category = 'openwheel', type = 'automobile', shop = 'none' },
{ model = 'formula2', name = 'R88', brand = 'Ocelot', price = 100000, category = 'openwheel', type = 'automobile', shop = 'none' },
{ model = 'formula', name = 'PR4', brand = 'Progen', price = 100000, category = 'openwheel', type = 'automobile', shop = 'none' },
}
QBShared.VehicleHashes = QBShared.VehicleHashes or {}
for i = 1, #Vehicles do
local hash = joaat(Vehicles[i].model)
QBShared.Vehicles[Vehicles[i].model] = {
spawncode = Vehicles[i].model,
name = Vehicles[i].name,
brand = Vehicles[i].brand,
model = Vehicles[i].model,
price = Vehicles[i].price,
category = Vehicles[i].category,
hash = hash,
type = Vehicles[i].type,
shop = Vehicles[i].shop
}
QBShared.VehicleHashes[hash] = QBShared.Vehicles[Vehicles[i].model]
end
+151
View File
@@ -0,0 +1,151 @@
QBShared = QBShared or {}
QBShared.Weapons = {
-- // WEAPONS
-- Melee
[`weapon_unarmed`] = { name = 'weapon_unarmed', label = 'Fists', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_dagger`] = { name = 'weapon_dagger', label = 'Dagger', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_bat`] = { name = 'weapon_bat', label = 'Bat', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_bottle`] = { name = 'weapon_bottle', label = 'Broken Bottle', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_crowbar`] = { name = 'weapon_crowbar', label = 'Crowbar', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_flashlight`] = { name = 'weapon_flashlight', label = 'Flashlight', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_golfclub`] = { name = 'weapon_golfclub', label = 'Golfclub', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_hammer`] = { name = 'weapon_hammer', label = 'Hammer', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_hatchet`] = { name = 'weapon_hatchet', label = 'Hatchet', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_knuckle`] = { name = 'weapon_knuckle', label = 'Knuckle', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_knife`] = { name = 'weapon_knife', label = 'Knife', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_machete`] = { name = 'weapon_machete', label = 'Machete', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_switchblade`] = { name = 'weapon_switchblade', label = 'Switchblade', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_nightstick`] = { name = 'weapon_nightstick', label = 'Nightstick', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_wrench`] = { name = 'weapon_wrench', label = 'Wrench', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_battleaxe`] = { name = 'weapon_battleaxe', label = 'Battle Axe', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_poolcue`] = { name = 'weapon_poolcue', label = 'Poolcue', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_briefcase`] = { name = 'weapon_briefcase', label = 'Briefcase', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_briefcase_02`] = { name = 'weapon_briefcase_02', label = 'Briefcase', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_garbagebag`] = { name = 'weapon_garbagebag', label = 'Garbage Bag', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_handcuffs`] = { name = 'weapon_handcuffs', label = 'Handcuffs', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_bread`] = { name = 'weapon_bread', label = 'Baquette', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },
[`weapon_stone_hatchet`] = { name = 'weapon_stone_hatchet', label = 'Stone Hatchet', weapontype = 'Melee', ammotype = nil, damagereason = 'Knifed / Stabbed / Eviscerated' },
[`weapon_candycane`] = { name = 'weapon_candycane', label = 'Candy Cane', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee Killed / Whacked / Executed / Beat down / Musrdered / Battered / Candy Caned' },
-- Handguns
[`weapon_pistol`] = { name = 'weapon_pistol', label = 'Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_pistol_mk2`] = { name = 'weapon_pistol_mk2', label = 'Pistol Mk2', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_combatpistol`] = { name = 'weapon_combatpistol', label = 'Combat Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_appistol`] = { name = 'weapon_appistol', label = 'AP Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_stungun`] = { name = 'weapon_stungun', label = 'Taser', weapontype = 'Pistol', ammotype = 'AMMO_STUNGUN', damagereason = 'Died' },
[`weapon_pistol50`] = { name = 'weapon_pistol50', label = 'Pistol .50 Cal', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_snspistol`] = { name = 'weapon_snspistol', label = 'SNS Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_snspistol_mk2`] = { name = 'weapon_snspistol_mk2', label = 'SNS Pistol MK2', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_heavypistol`] = { name = 'weapon_heavypistol', label = 'Heavy Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_vintagepistol`] = { name = 'weapon_vintagepistol', label = 'Vintage Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_flaregun`] = { name = 'weapon_flaregun', label = 'Flare Gun', weapontype = 'Pistol', ammotype = 'AMMO_FLARE', damagereason = 'Died' },
[`weapon_marksmanpistol`] = { name = 'weapon_marksmanpistol', label = 'Marksman Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_revolver`] = { name = 'weapon_revolver', label = 'Revolver', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_revolver_mk2`] = { name = 'weapon_revolver_mk2', label = 'Revolver MK2', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_doubleaction`] = { name = 'weapon_doubleaction', label = 'Double Action Revolver', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_raypistol`] = { name = 'weapon_raypistol', label = 'Ray Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_ceramicpistol`] = { name = 'weapon_ceramicpistol', label = 'Ceramic Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_navyrevolver`] = { name = 'weapon_navyrevolver', label = 'Navy Revolver', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_gadgetpistol`] = { name = 'weapon_gadgetpistol', label = 'Gadget Pistol', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plugged / Bust a cap in' },
[`weapon_stungun_mp`] = { name = 'weapon_stungun_mp', label = 'Taser', weapontype = 'Pistol', ammotype = 'AMMO_STUNGUN', damagereason = 'Died' },
[`weapon_pistolxm3`] = { name = 'weapon_pistolxm3', label = 'Pistol XM3', weapontype = 'Pistol', ammotype = 'AMMO_PISTOL', damagereason = 'Pistoled / Blasted / Plaugged / Bust a cap in' },
-- Submachine Guns
[`weapon_microsmg`] = { name = 'weapon_microsmg', label = 'Micro SMG', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_smg`] = { name = 'weapon_smg', label = 'SMG', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_smg_mk2`] = { name = 'weapon_smg_mk2', label = 'SMG MK2', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_assaultsmg`] = { name = 'weapon_assaultsmg', label = 'Assault SMG', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_combatpdw`] = { name = 'weapon_combatpdw', label = 'Combat PDW', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_machinepistol`] = { name = 'weapon_machinepistol', label = 'Tec-9', weapontype = 'Submachine Gun', ammotype = 'AMMO_PISTOL', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_minismg`] = { name = 'weapon_minismg', label = 'Mini SMG', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
[`weapon_raycarbine`] = { name = 'weapon_raycarbine', label = 'Raycarbine', weapontype = 'Submachine Gun', ammotype = 'AMMO_SMG', damagereason = 'Riddled / Drilled / Finished / Submachine Gunned' },
-- Shotguns
[`weapon_pumpshotgun`] = { name = 'weapon_pumpshotgun', label = 'Pump Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_pumpshotgun_mk2`] = { name = 'weapon_pumpshotgun_mk2', label = 'Pump Shotgun MK2', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_sawnoffshotgun`] = { name = 'weapon_sawnoffshotgun', label = 'Sawn-off Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_assaultshotgun`] = { name = 'weapon_assaultshotgun', label = 'Assault Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_bullpupshotgun`] = { name = 'weapon_bullpupshotgun', label = 'Bullpup Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_musket`] = { name = 'weapon_musket', label = 'Musket', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_heavyshotgun`] = { name = 'weapon_heavyshotgun', label = 'Heavy Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_dbshotgun`] = { name = 'weapon_dbshotgun', label = 'Double-barrel Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_autoshotgun`] = { name = 'weapon_autoshotgun', label = 'Auto Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
[`weapon_combatshotgun`] = { name = 'weapon_combatshotgun', label = 'Combat Shotgun', weapontype = 'Shotgun', ammotype = 'AMMO_SHOTGUN', damagereason = 'Devastated / Pulverized / Shotgunned' },
-- Assault Rifles
[`weapon_assaultrifle`] = { name = 'weapon_assaultrifle', label = 'Assault Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_assaultrifle_mk2`] = { name = 'weapon_assaultrifle_mk2', label = 'Assault Rifle MK2', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_carbinerifle`] = { name = 'weapon_carbinerifle', label = 'Carbine Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_carbinerifle_mk2`] = { name = 'weapon_carbinerifle_mk2', label = 'Carbine Rifle MK2', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_advancedrifle`] = { name = 'weapon_advancedrifle', label = 'Advanced Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_specialcarbine`] = { name = 'weapon_specialcarbine', label = 'Special Carbine', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_specialcarbine_mk2`] = { name = 'weapon_specialcarbine_mk2', label = 'Specialcarbine MK2', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_bullpuprifle`] = { name = 'weapon_bullpuprifle', label = 'Bullpup Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_bullpuprifle_mk2`] = { name = 'weapon_bullpuprifle_mk2', label = 'Bull Puprifle MK2', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_compactrifle`] = { name = 'weapon_compactrifle', label = 'Compact Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_militaryrifle`] = { name = 'weapon_militaryrifle', label = 'Military Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
[`weapon_heavyrifle`] = { name = 'weapon_heavyrifle', label = 'Heavy Rifle', weapontype = 'Assault Rifle', ammotype = 'AMMO_RIFLE', damagereason = 'Ended / Rifled / Shot down / Floored' },
-- Light Machine Guns
[`weapon_mg`] = { name = 'weapon_mg', label = 'Machinegun', weapontype = 'Light Machine Gun', ammotype = 'AMMO_MG', damagereason = 'Machine gunned / Sprayed / Ruined' },
[`weapon_combatmg`] = { name = 'weapon_combatmg', label = 'Combat MG', weapontype = 'Light Machine Gun', ammotype = 'AMMO_MG', damagereason = 'Machine gunned / Sprayed / Ruined' },
[`weapon_combatmg_mk2`] = { name = 'weapon_combatmg_mk2', label = 'Combat MG MK2', weapontype = 'Light Machine Gun', ammotype = 'AMMO_MG', damagereason = 'Machine gunned / Sprayed / Ruined' },
[`weapon_gusenberg`] = { name = 'weapon_gusenberg', label = 'Thompson SMG', weapontype = 'Light Machine Gun', ammotype = 'AMMO_MG', damagereason = 'Machine gunned / Sprayed / Ruined' },
-- Sniper Rifles
[`weapon_sniperrifle`] = { name = 'weapon_sniperrifle', label = 'Sniper Rifle', weapontype = 'Sniper Rifle', ammotype = 'AMMO_SNIPER', damagereason = 'Sniped / Picked off / Scoped' },
[`weapon_heavysniper`] = { name = 'weapon_heavysniper', label = 'Heavy Sniper', weapontype = 'Sniper Rifle', ammotype = 'AMMO_SNIPER', damagereason = 'Sniped / Picked off / Scoped' },
[`weapon_heavysniper_mk2`] = { name = 'weapon_heavysniper_mk2', label = 'Heavysniper MK2', weapontype = 'Sniper Rifle', ammotype = 'AMMO_SNIPER', damagereason = 'Sniped / Picked off / Scoped' },
[`weapon_marksmanrifle`] = { name = 'weapon_marksmanrifle', label = 'Marksman Rifle', weapontype = 'Sniper Rifle', ammotype = 'AMMO_SNIPER', damagereason = 'Sniped / Picked off / Scoped' },
[`weapon_marksmanrifle_mk2`] = { name = 'weapon_marksmanrifle_mk2', label = 'Marksman Rifle MK2', weapontype = 'Sniper Rifle', ammotype = 'AMMO_SNIPER', damagereason = 'Sniped / Picked off / Scoped' },
[`weapon_remotesniper`] = { name = 'weapon_remotesniper', label = 'Remote Sniper', weapontype = 'Sniper Rifle', ammotype = 'AMMO_SNIPER_REMOTE', damagereason = 'Sniped / Picked off / Scoped' },
-- Heavy Weapons
[`weapon_rpg`] = { name = 'weapon_rpg', label = 'RPG', weapontype = 'Heavy Weapons', ammotype = 'AMMO_RPG', damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_grenadelauncher`] = { name = 'weapon_grenadelauncher', label = 'Grenade Launcher', weapontype = 'Heavy Weapons', ammotype = 'AMMO_GRENADELAUNCHER', damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_grenadelauncher_smoke`] = { name = 'weapon_grenadelauncher_smoke', label = 'Smoke Grenade Launcher', weapontype = 'Heavy Weapons', ammotype = 'AMMO_GRENADELAUNCHER', damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_minigun`] = { name = 'weapon_minigun', label = 'Minigun', weapontype = 'Heavy Weapons', ammotype = 'AMMO_MINIGUN', damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_firework`] = { name = 'weapon_firework', label = 'Firework Launcher', weapontype = 'Heavy Weapons', ammotype = nil, damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_railgun`] = { name = 'weapon_railgun', label = 'Railgun', weapontype = 'Heavy Weapons', ammotype = nil, damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_railgunxm3`] = { name = 'weapon_railgunxm3', label = 'Railgun XM3', weapontype = 'Heavy Weapons', ammotype = nil, damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_hominglauncher`] = { name = 'weapon_hominglauncher', label = 'Homing Launcher', weapontype = 'Heavy Weapons', ammotype = 'AMMO_STINGER', damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_compactlauncher`] = { name = 'weapon_compactlauncher', label = 'Compact Launcher', weapontype = 'Heavy Weapons', ammotype = nil, damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_rayminigun`] = { name = 'weapon_rayminigun', label = 'Ray Minigun', weapontype = 'Heavy Weapons', ammotype = 'AMMO_MINIGUN', damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_emplauncher`] = { name = 'weapon_emplauncher', label = 'EMP Launcher', weapontype = 'Heavy Weapons', ammotype = 'AMMO_EMPLAUNCHER', damagereason = 'Died' },
-- Throwables
[`weapon_grenade`] = { name = 'weapon_grenade', label = 'Grenade', weapontype = 'Throwable', ammotype = nil, damagereason = 'Bombed / Exploded / Detonated / Blew up' },
[`weapon_bzgas`] = { name = 'weapon_bzgas', label = 'BZ Gas', weapontype = 'Throwable', ammotype = nil, damagereason = 'Died' },
[`weapon_molotov`] = { name = 'weapon_molotov', label = 'Molotov', weapontype = 'Throwable', ammotype = nil, damagereason = 'Torched / Flambeed / Barbecued' },
[`weapon_stickybomb`] = { name = 'weapon_stickybomb', label = 'C4', weapontype = 'Throwable', ammotype = nil, damagereason = 'Bombed / Exploded / Detonated / Blew up' },
[`weapon_proxmine`] = { name = 'weapon_proxmine', label = 'Proxmine Grenade', weapontype = 'Throwable', ammotype = nil, damagereason = 'Bombed / Exploded / Detonated / Blew up' },
[`weapon_snowball`] = { name = 'weapon_snowball', label = 'Snowball', weapontype = 'Throwable', ammotype = nil, damagereason = 'Died' },
[`weapon_pipebomb`] = { name = 'weapon_pipebomb', label = 'Pipe Bomb', weapontype = 'Throwable', ammotype = nil, damagereason = 'Bombed / Exploded / Detonated / Blew up' },
[`weapon_ball`] = { name = 'weapon_ball', label = 'Ball', weapontype = 'Throwable', ammotype = 'AMMO_BALL', damagereason = 'Died' },
[`weapon_smokegrenade`] = { name = 'weapon_smokegrenade', label = 'Smoke Grenade', weapontype = 'Throwable', ammotype = nil, damagereason = 'Died' },
[`weapon_flare`] = { name = 'weapon_flare', label = 'Flare pistol', weapontype = 'Throwable', ammotype = 'AMMO_FLARE', damagereason = 'Died' },
-- Miscellaneous
[`weapon_petrolcan`] = { name = 'weapon_petrolcan', label = 'Petrol Can', weapontype = 'Miscellaneous', ammotype = 'AMMO_PETROLCAN', damagereason = 'Died' },
[`gadget_parachute`] = { name = 'gadget_parachute', label = 'Parachute', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Died' },
[`weapon_fireextinguisher`] = { name = 'weapon_fireextinguisher', label = 'Fire Extinguisher', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Died' },
[`weapon_hazardcan`] = { name = 'weapon_hazardcan', label = 'Hazardcan', weapontype = 'Miscellaneous', ammotype = 'AMMO_PETROLCAN', damagereason = 'Died' },
[`weapon_fertilizercan`] = { name = 'weapon_fertilizercan', label = 'Fertilizer Can', weapontype = 'Miscellaneous', ammotype = 'AMMO_FERTILIZERCAN', damagereason = 'Died' },
[`weapon_barbed_wire`] = { name = 'weapon_barbed_wire', label = 'Barbed Wire', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Prodded' },
[`weapon_drowning`] = { name = 'weapon_drowning', label = 'Drowning', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Died' },
[`weapon_drowning_in_vehicle`] = { name = 'weapon_drowning_in_vehicle', label = 'Drowning in a Vehicle', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Died' },
[`weapon_bleeding`] = { name = 'weapon_bleeding', label = 'Bleeding', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Bled out' },
[`weapon_electric_fence`] = { name = 'weapon_electric_fence', label = 'Electric Fence', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Fried' },
[`weapon_explosion`] = { name = 'weapon_explosion', label = 'Explosion', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Killed / Exploded / Obliterated / Destroyed / Erased / Annihilated' },
[`weapon_fall`] = { name = 'weapon_fall', label = 'Fall', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Committed suicide' },
[`weapon_exhaustion`] = { name = 'weapon_exhaustion', label = 'Exhaustion', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Died' },
[`weapon_hit_by_water_cannon`] = { name = 'weapon_hit_by_water_cannon', label = 'Water Cannon', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Died' },
[`weapon_rammed_by_car`] = { name = 'weapon_rammed_by_car', label = 'Rammed - Vehicle', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Flattened / Ran over / Ran down' },
[`weapon_run_over_by_car`] = { name = 'weapon_run_over_by_car', label = 'Run Over - Vehicle', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Flattened / Ran over / Ran down' },
[`weapon_heli_crash`] = { name = 'weapon_heli_crash', label = 'Heli Crash', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Helicopter Crash' },
[`weapon_fire`] = { name = 'weapon_fire', label = 'Fire', weapontype = 'Miscellaneous', ammotype = nil, damagereason = 'Torched / Flambeed / Barbecued' },
-- Animals
[`weapon_animal`] = { name = 'weapon_animal', label = 'Animal', weapontype = 'Animals', ammotype = nil, damagereason = 'Mauled' },
[`weapon_cougar`] = { name = 'weapon_cougar', label = 'Cougar', weapontype = 'Animals', ammotype = nil, damagereason = 'Mauled' },
}