# Common Issues
Transferring Scripts [#transferring-scripts]
1. Log in to your [Cfx.re Portal](https://portal.cfx.re/) account.
2. Go to the "[Granted Assets](https://portal.cfx.re/assets/granted-assets)" page to view your purchased resources.
3. Click "Transfer to another account" next to the script you want to transfer.
4. Enter the username of the receiving account and finalize the transfer.
You lack the required entitlement [#you-lack-the-required-entitlement]
Common causes:
1. **Script purchased with a different FiveM account:** If the script was bought on a different account than the one your server uses, it won't work. See the Transfer Resource Guide above.
2. **Server restart required:** Restart your server after purchasing the script.
3. **Incorrect system time:** Your OS clock may be out of sync. Check against [Time.is](https://time.is/) for your server's location; if it's off by more than a minute, correct the system time.
Error: syntax error near <\1> [#error-syntax-error-near-1]
Two common causes:
1. **File transfer method:**
* **FileZilla:** Transfers can corrupt the script unless done in binary mode. Either enable binary mode and re-upload the entire resource, or use WinSCP instead.
* **Drag-and-drop via Remote Desktop:** Moving files individually can damage the script. Upload the zip to the server and unzip it there instead.
2. **Outdated server version:** Your server must run artifact 4752 or higher. Run the `version` command in your server console to check.
Outdated Artifacts [#outdated-artifacts]
**Linux:**
1. Download the latest recommended artifact from [this page](https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/).
2. Stop your server.
3. Delete the `cache` and `alpine` folders.
4. Unzip the `fx.tar.xz` file using the command: `tar -xf fx.tar.xz`.
5. Start the server and use the `version` command to verify that the update was successful.
**Windows:**
1. Download the latest recommended FXServer artifact from [this page](https://runtime.fivem.net/artifacts/fivem/build_server_windows/master/).
2. Stop your server.
3. Remove the old server folder.
4. Replace it with the newly downloaded version.
5. Start the server and use the `version` command to check that the update was applied.
# Introduction
This page contains in-depth documentation for our script, offering tips, tutorials, and instructions on how to use the scripting API, configure settings, and more.
Why are the scripts encrypted? [#why-are-the-scripts-encrypted]
To safeguard our intellectual property and ensure the ongoing development of high-quality resources, we've implemented encryption for most of our scripts.
How does encryption work? [#how-does-encryption-work]
1. Upon purchasing one of our paid scripts, it is automatically associated with your **FiveM account**. This account is then linked to your server through **Cfx keys**. You will need to use this specific account and one of the three provided keys to access the script.
2. You can check your [Cfx.re Portal](https://portal.cfx.re/) under *Purchased Assets* to find your script, which should appear within moments of purchase.
3. Ensure that your server is running server artifacts version **17000** or later. You can update your artifacts [here for Windows](https://runtime.fivem.net/artifacts/fivem/build_server_windows/master/) or [here for Linux](https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/).
If you need any support, please feel free to open a ticket on our Discord
server. We’ll be more than happy to assist you with anything you need.
# Config
blacklist [#blacklist]
```lua title="blacklist.lua"
Config.Blacklist = {
male = {
hair = {},
components = {
masks = {},
upperBody = {},
lowerBody = {},
bags = {},
shoes = {},
scarfAndChains = {},
shirts = {},
bodyArmor = {},
decals = {},
jackets = {},
},
props = {
hats = {},
glasses = {},
ear = {},
watches = {},
bracelets = {},
},
},
female = {
hair = {},
components = {
masks = {},
upperBody = {},
lowerBody = {},
bags = {},
shoes = {},
scarfAndChains = {},
shirts = {},
bodyArmor = {},
decals = {},
jackets = {},
},
props = {
hats = {},
glasses = {},
ear = {},
watches = {},
bracelets = {},
},
},
}
```
config [#config]
```lua title="config.lua"
Config = {}
Config.Debug = false
Config.ClothingCost = 100
Config.BarberCost = 100
Config.TattooCost = 100
Config.SurgeonCost = 100
Config.DisableDataPanel = false -- You can completely disable the export panel & hide it.
Config.UseNumericSystemForTattoos = false -- Instead of using the dropdown to select the tattoo, you use a number input just like the other values.
Config.ChargePerTattoo = true -- Charge players per tattoo. Config.TattooCost will become the cost of 1 tattoo. The cost can be overridden by adding `cost` key in shared/tattoos.lua for specific tattoos
-- Set this to true if you preferr using RCore Tattoos
Config.RCoreTattoosCompatibility = false
Config.UseTarget = false
Config.TextUIOptions = {
position = "right-center",
}
Config.NotifyOptions = {
position = "top-right",
}
Config.OutfitCodeLength = 10
Config.UseRadialMenu = false
Config.UseOxRadial = false -- Set to true to use ox_lib radial menu, both this and UseRadialMenu must be true
Config.EnablePedsForShops = true
Config.EnablePedsForClothingRooms = true
Config.EnablePedsForPlayerOutfitRooms = true
Config.EnablePedMenu = true
Config.PedMenuGroup = "group.admin"
Config.EnableJobOutfitsCommand = false -- Enables /joboutfits and /gangoutfits commands
Config.ShowNearestShopOnly = false
Config.HideRadar = false -- Hides the minimap while the appearance menu is open
Config.NearestShopBlipUpdateDelay = 10000
Config.InvincibleDuringCustomization = true
Config.PreventTrackerRemoval = false -- Disables "Scarf and Chains" section if the player has tracker
Config.TrackerClothingOptions = {
drawable = 13,
texture = 0,
}
Config.NewCharacterSections = {
Ped = true,
HeadBlend = true,
FaceFeatures = true,
HeadOverlays = true,
Components = true,
Props = true,
Tattoos = true,
}
Config.GenderBasedOnPed = true
Config.AlwaysKeepProps = false
Config.PersistUniforms = false -- Keeps Job / Gang Outfits on player reconnects / logout
Config.OnDutyOnlyClothingRooms = false -- Set to `true` to make the clothing rooms accessible only to players who are On Duty
Config.BossManagedOutfits = false -- Allows Job / Gang bosses to manage their own job / gang outfits
Config.ReloadSkinCooldown = 5000
Config.AutomaticFade = false -- Enables automatic fading and disabled the Fade section from Hair
Config.DisableComponents = {
Masks = false,
UpperBody = false,
LowerBody = false,
Bags = false,
Shoes = false,
ScarfAndChains = false,
BodyArmor = false,
Shirts = false,
Decals = false,
Jackets = false,
}
Config.DisableProps = {
Hats = false,
Glasses = false,
Ear = false,
Watches = false,
Bracelets = false,
}
---@type string[]
Config.Aces = {} -- list of ace permissions used for blacklisting
Config.Blips = {
["clothing"] = {
Show = true,
Sprite = 366,
Color = 47,
Scale = 0.7,
Name = "Clothing Store",
},
["barber"] = {
Show = true,
Sprite = 71,
Color = 0,
Scale = 0.7,
Name = "Barber",
},
["tattoo"] = {
Show = true,
Sprite = 75,
Color = 4,
Scale = 0.7,
Name = "Tattoo Shop",
},
["surgeon"] = {
Show = true,
Sprite = 102,
Color = 4,
Scale = 0.7,
Name = "Plastic Surgeon",
},
}
Config.TargetConfig = {
["clothing"] = {
model = "s_f_m_shop_high",
scenario = "WORLD_HUMAN_STAND_MOBILE",
icon = "fas fa-tshirt",
label = "Open Clothing Store",
distance = 3,
},
["barber"] = {
model = "s_m_m_hairdress_01",
scenario = "WORLD_HUMAN_STAND_MOBILE",
icon = "fas fa-scissors",
label = "Open Barber Shop",
distance = 3,
},
["tattoo"] = {
model = "u_m_y_tattoo_01",
scenario = "WORLD_HUMAN_STAND_MOBILE",
icon = "fas fa-pen",
label = "Open Tattoo Shop",
distance = 3,
},
["surgeon"] = {
model = "s_m_m_doctor_01",
scenario = "WORLD_HUMAN_STAND_MOBILE",
icon = "fas fa-scalpel",
label = "Open Surgeon",
distance = 3,
},
["clothingroom"] = {
model = "mp_g_m_pros_01",
scenario = "WORLD_HUMAN_STAND_MOBILE",
icon = "fas fa-sign-in-alt",
label = "Open Job / Gang Clothes Menu",
distance = 3,
},
["playeroutfitroom"] = {
model = "mp_g_m_pros_01",
scenario = "WORLD_HUMAN_STAND_MOBILE",
icon = "fas fa-sign-in-alt",
label = "Open Outfits Menu",
distance = 3,
},
}
Config.Stores = {
{
type = "clothing",
coords = vector4(1693.2, 4828.11, 42.07, 188.66),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false, -- false => uses the size + rotation to create the zone | true => uses points to create the zone
showBlip = true, -- overrides the blip visibilty configured above for the group
--targetModel = "s_m_m_doctor_01", -- overrides the target ped configured for the group
--targetScenario = "" -- overrides the target scenario configure for the group
points = {
vector3(1686.9018554688, 4829.8330078125, 42.07),
vector3(1698.8566894531, 4831.4604492188, 42.07),
vector3(1700.2448730469, 4817.7734375, 42.07),
vector3(1688.3682861328, 4816.2954101562, 42.07),
},
},
{
type = "clothing",
coords = vector4(-705.5, -149.22, 37.42, 122),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-719.86212158203, -147.83151245117, 37.42),
vector3(-709.10491943359, -141.53076171875, 37.42),
vector3(-699.94342041016, -157.44494628906, 37.42),
vector3(-710.68774414062, -163.64665222168, 37.42),
},
},
{
type = "clothing",
coords = vector4(-1192.61, -768.4, 17.32, 216.6),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-1206.9552001953, -775.06304931641, 17.32),
vector3(-1190.6080322266, -764.03198242188, 17.32),
vector3(-1184.5672607422, -772.16949462891, 17.32),
vector3(-1199.24609375, -783.07928466797, 17.32),
},
},
{
type = "clothing",
coords = vector4(425.91, -801.03, 29.49, 177.79),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(419.55020141602, -798.36547851562, 29.49),
vector3(431.61773681641, -798.31909179688, 29.49),
vector3(431.19784545898, -812.07122802734, 29.49),
vector3(419.140625, -812.03594970703, 29.49),
},
},
{
type = "clothing",
coords = vector4(-168.73, -301.41, 39.73, 238.67),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-160.82145690918, -313.85919189453, 39.73),
vector3(-172.56513977051, -309.82858276367, 39.73),
vector3(-166.5775604248, -292.48077392578, 39.73),
vector3(-154.84906005859, -296.51647949219, 39.73),
},
},
{
type = "clothing",
coords = vector4(75.39, -1398.28, 29.38, 6.73),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(81.406135559082, -1400.7791748047, 29.38),
vector3(69.335029602051, -1400.8251953125, 29.38),
vector3(69.754981994629, -1387.078125, 29.38),
vector3(81.500122070312, -1387.3002929688, 29.38),
},
},
{
type = "clothing",
coords = vector4(-827.39, -1075.93, 11.33, 294.31),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-826.26251220703, -1082.6293945312, 11.33),
vector3(-832.27856445312, -1072.2819824219, 11.33),
vector3(-820.16442871094, -1065.7727050781, 11.33),
vector3(-814.08953857422, -1076.1878662109, 11.33),
},
},
{
type = "clothing",
coords = vector4(-1445.86, -240.78, 49.82, 36.17),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-1448.4829101562, -226.39401245117, 49.82),
vector3(-1439.2475585938, -234.70428466797, 49.82),
vector3(-1451.5389404297, -248.33193969727, 49.82),
vector3(-1460.7554931641, -240.02815246582, 49.82),
},
},
{
type = "clothing",
coords = vector4(9.22, 6515.74, 31.88, 131.27),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(6.4955291748047, 6522.205078125, 31.88),
vector3(14.737417221069, 6513.3872070312, 31.88),
vector3(4.3691010475159, 6504.3452148438, 31.88),
vector3(-3.5187695026398, 6513.1538085938, 31.88),
},
},
{
type = "clothing",
coords = vector4(615.35, 2762.72, 42.09, 170.51),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(612.58312988281, 2747.2814941406, 42.09),
vector3(612.26214599609, 2767.0520019531, 42.09),
vector3(622.37548828125, 2767.7614746094, 42.09),
vector3(623.66833496094, 2749.5180664062, 42.09),
},
},
{
type = "clothing",
coords = vector4(1191.61, 2710.91, 38.22, 269.96),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(1188.7923583984, 2704.2021484375, 38.22),
vector3(1188.7498779297, 2716.2661132812, 38.22),
vector3(1202.4979248047, 2715.8479003906, 38.22),
vector3(1202.3558349609, 2703.9294433594, 38.22),
},
},
{
type = "clothing",
coords = vector4(-3171.32, 1043.56, 20.86, 334.3),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-3162.0075683594, 1056.7303466797, 20.86),
vector3(-3170.8247070312, 1039.0412597656, 20.86),
vector3(-3180.0979003906, 1043.1201171875, 20.86),
vector3(-3172.7292480469, 1059.8623046875, 20.86),
},
},
{
type = "clothing",
coords = vector4(-1105.52, 2707.79, 19.11, 317.19),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-1103.3004150391, 2700.8195800781, 19.11),
vector3(-1111.3771972656, 2709.884765625, 19.11),
vector3(-1100.8548583984, 2718.638671875, 19.11),
vector3(-1093.1976318359, 2709.7365722656, 19.11),
},
},
{
type = "clothing",
coords = vector4(-1119.24, -1440.6, 5.23, 300.5),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-1124.5535888672, -1444.5367431641, 5.23),
vector3(-1118.7023925781, -1441.0450439453, 5.23),
vector3(-1121.2891845703, -1434.8474121094, 5.23),
vector3(-1128.4727783203, -1439.8254394531, 5.23),
},
},
{
type = "clothing",
coords = vector4(124.82, -224.36, 54.56, 335.41),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(133.60948181152, -210.31390380859, 54.56),
vector3(125.8349609375, -228.48097229004, 54.56),
vector3(116.3140335083, -225.02020263672, 54.56),
vector3(122.56930541992, -207.83396911621, 54.56),
},
},
{
type = "barber",
coords = vector4(-814.22, -183.7, 37.57, 116.91),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-825.06127929688, -182.67497253418, 37.57),
vector3(-808.82415771484, -179.19134521484, 37.57),
vector3(-808.55261230469, -184.9720916748, 37.57),
vector3(-819.77899169922, -191.81831359863, 37.57),
},
},
{
type = "barber",
coords = vector4(136.78, -1708.4, 29.29, 144.19),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(132.57008361816, -1710.5053710938, 29.29),
vector3(138.77899169922, -1702.6778564453, 29.29),
vector3(142.73052978516, -1705.6853027344, 29.29),
vector3(135.49719238281, -1712.9750976562, 29.29),
},
},
{
type = "barber",
coords = vector4(-1282.57, -1116.84, 6.99, 89.25),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-1287.4735107422, -1115.4364013672, 6.99),
vector3(-1277.5638427734, -1115.1229248047, 6.99),
vector3(-1277.2469482422, -1120.1147460938, 6.99),
vector3(-1287.4561767578, -1119.2506103516, 6.99),
},
},
{
type = "barber",
coords = vector4(1931.41, 3729.73, 32.84, 212.08),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(1932.4931640625, 3725.3374023438, 32.84),
vector3(1927.2720947266, 3733.7663574219, 32.84),
vector3(1931.4379882812, 3736.5327148438, 32.84),
vector3(1936.0697021484, 3727.2839355469, 32.84),
},
},
{
type = "barber",
coords = vector4(1212.8, -472.9, 65.2, 60.94),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(1208.3327636719, -469.84338378906, 65.2),
vector3(1217.9066162109, -472.40216064453, 65.2),
vector3(1216.9870605469, -477.00939941406, 65.2),
vector3(1206.1077880859, -473.83499145508, 65.2),
},
},
{
type = "barber",
coords = vector4(-32.9, -152.3, 56.1, 335.22),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-29.730783462524, -148.67495727539, 56.1),
vector3(-32.919719696045, -158.04254150391, 56.1),
vector3(-37.612594604492, -156.62759399414, 56.1),
vector3(-33.30192565918, -147.31649780273, 56.1),
},
},
{
type = "barber",
coords = vector4(-278.1, 6228.5, 30.7, 49.32),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-280.29818725586, 6232.7265625, 30.7),
vector3(-273.06427001953, 6225.9692382812, 30.7),
vector3(-276.25280761719, 6222.4013671875, 30.7),
vector3(-282.98211669922, 6230.015625, 30.7),
},
},
{
type = "tattoo",
coords = vector4(1322.6, -1651.9, 51.2, 42.47),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(1323.9360351562, -1649.2370605469, 51.2),
vector3(1328.0186767578, -1654.3087158203, 51.2),
vector3(1322.5780029297, -1657.7045898438, 51.2),
vector3(1319.2043457031, -1653.0885009766, 51.2),
},
},
{
type = "tattoo",
coords = vector4(-1154.01, -1425.31, 4.95, 23.21),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-1152.7110595703, -1422.8382568359, 4.95),
vector3(-1149.0043945312, -1428.1975097656, 4.95),
vector3(-1154.6730957031, -1431.1898193359, 4.95),
vector3(-1157.7064208984, -1426.3433837891, 4.95),
},
},
{
type = "tattoo",
coords = vector4(322.62, 180.34, 103.59, 156.2),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(319.28741455078, 179.9383392334, 103.59),
vector3(321.537109375, 186.04516601562, 103.59),
vector3(327.24526977539, 183.12303161621, 103.59),
vector3(325.01351928711, 177.8542175293, 103.59),
},
},
{
type = "tattoo",
coords = vector4(-3169.52, 1074.86, 20.83, 253.29),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-3169.5861816406, 1072.3740234375, 20.83),
vector3(-3175.4802246094, 1075.0703125, 20.83),
vector3(-3172.2041015625, 1080.5860595703, 20.83),
vector3(-3167.076171875, 1078.0391845703, 20.83),
},
},
{
type = "tattoo",
coords = vector4(1864.1, 3747.91, 33.03, 17.23),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(1860.2752685547, 3750.1608886719, 33.03),
vector3(1866.390625, 3752.8081054688, 33.03),
vector3(1868.6164550781, 3747.3562011719, 33.03),
vector3(1863.65234375, 3744.5034179688, 33.03),
},
},
{
type = "tattoo",
coords = vector4(-294.24, 6200.12, 31.49, 195.72),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(-289.42239379883, 6198.68359375, 31.49),
vector3(-294.69515991211, 6194.5366210938, 31.49),
vector3(-298.23013305664, 6199.2451171875, 31.49),
vector3(-294.1501159668, 6203.2700195312, 31.49),
},
},
{
type = "surgeon",
coords = vector4(298.78, -572.81, 43.26, 114.27),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(298.84417724609, -572.92205810547, 43.26),
vector3(296.39556884766, -575.65942382812, 43.26),
vector3(293.56317138672, -572.60675048828, 43.26),
vector3(296.28656005859, -570.330078125, 43.26),
},
},
}
Config.ClothingRooms = {
{
job = "police",
coords = vector4(454.91, -990.89, 30.69, 193.4),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(460.41918945312, -993.11444091797, 30.69),
vector3(449.39508056641, -993.60614013672, 30.69),
vector3(449.88696289062, -990.23779296875, 30.69),
vector3(450.97882080078, -989.71411132812, 30.69),
vector3(451.0325012207, -987.89904785156, 30.69),
vector3(453.47863769531, -987.76928710938, 30.69),
vector3(454.35513305664, -988.46459960938, 30.69),
vector3(460.4231262207, -987.94573974609, 30.69),
},
},
}
Config.PlayerOutfitRooms = {
-- Sample outfit room config
--[[{
job = "police",
coords = vector4(287.28, -573.41, 43.16, 79.61),
size = vector3(4, 4, 4),
rotation = 45,
usePoly = false,
points = {
vector3(284.83, -574.01, 43.16),
vector3(286.33, -570.03, 43.16),
vector3(290.33, -571.74, 43.16),
vector3(289.0, -574.75, 43.16)
},
citizenIDs = {
"BHH65156"
}
}]]
--
}
Config.Outfits = {
["police"] = {
["Male"] = {
{
name = "Short Sleeve",
outfitData = {
["pants"] = { item = 24, texture = 0 }, -- Pants
["arms"] = { item = 19, texture = 0 }, -- Arms
["t-shirt"] = { item = 58, texture = 0 }, -- T Shirt
["vest"] = { item = 0, texture = 0 }, -- Body Vest
["torso2"] = { item = 55, texture = 0 }, -- Jacket
["shoes"] = { item = 51, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = -1, texture = -1 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 0, 1, 2, 3, 4 },
},
{
name = "Trooper Tan",
outfitData = {
["pants"] = { item = 24, texture = 0 }, -- Pants
["arms"] = { item = 20, texture = 0 }, -- Arms
["t-shirt"] = { item = 58, texture = 0 }, -- T Shirt
["vest"] = { item = 0, texture = 0 }, -- Body Vest
["torso2"] = { item = 317, texture = 3 }, -- Jacket
["shoes"] = { item = 51, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 58, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 0, 1, 2, 3, 4 },
},
{
name = "Long Sleeve",
outfitData = {
["pants"] = { item = 24, texture = 0 }, -- Pants
["arms"] = { item = 20, texture = 0 }, -- Arms
["t-shirt"] = { item = 58, texture = 0 }, -- T Shirt
["vest"] = { item = 0, texture = 0 }, -- Body Vest
["torso2"] = { item = 317, texture = 0 }, -- Jacket
["shoes"] = { item = 51, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = -1, texture = -1 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 1, 2, 3, 4 },
},
{
name = "Trooper Black",
outfitData = {
["pants"] = { item = 24, texture = 0 }, -- Pants
["arms"] = { item = 20, texture = 0 }, -- Arms
["t-shirt"] = { item = 58, texture = 0 }, -- T Shirt
["vest"] = { item = 0, texture = 0 }, -- Body Vest
["torso2"] = { item = 317, texture = 8 }, -- Jacket
["shoes"] = { item = 51, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 58, texture = 3 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 2, 3, 4 },
},
{
name = "SWAT",
outfitData = {
["pants"] = { item = 130, texture = 1 }, -- Pants
["arms"] = { item = 172, texture = 0 }, -- Arms
["t-shirt"] = { item = 15, texture = 0 }, -- T Shirt
["vest"] = { item = 15, texture = 2 }, -- Body Vest
["torso2"] = { item = 336, texture = 3 }, -- Jacket
["shoes"] = { item = 24, texture = 0 }, -- Shoes
["accessory"] = { item = 133, texture = 0 }, -- Neck Accessory
["hat"] = { item = 150, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 52, texture = 0 }, -- Mask
},
grades = { 3, 4 },
},
},
["Female"] = {
{
name = "Short Sleeve",
outfitData = {
["pants"] = { item = 133, texture = 0 }, -- Pants
["arms"] = { item = 31, texture = 0 }, -- Arms
["t-shirt"] = { item = 35, texture = 0 }, -- T Shirt
["vest"] = { item = 34, texture = 0 }, -- Body Vest
["torso2"] = { item = 48, texture = 0 }, -- Jacket
["shoes"] = { item = 52, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 0, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 0, 1, 2, 3, 4 },
},
{
name = "Trooper Tan",
outfitData = {
["pants"] = { item = 133, texture = 0 }, -- Pants
["arms"] = { item = 31, texture = 0 }, -- Arms
["t-shirt"] = { item = 35, texture = 0 }, -- T Shirt
["vest"] = { item = 34, texture = 0 }, -- Body Vest
["torso2"] = { item = 327, texture = 3 }, -- Jacket
["shoes"] = { item = 52, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 0, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 0, 1, 2, 3, 4 },
},
{
name = "Long Sleeve",
outfitData = {
["pants"] = { item = 133, texture = 0 }, -- Pants
["arms"] = { item = 31, texture = 0 }, -- Arms
["t-shirt"] = { item = 35, texture = 0 }, -- T Shirt
["vest"] = { item = 34, texture = 0 }, -- Body Vest
["torso2"] = { item = 327, texture = 0 }, -- Jacket
["shoes"] = { item = 52, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 0, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 1, 2, 3, 4 },
},
{
name = "Trooper Black",
outfitData = {
["pants"] = { item = 133, texture = 0 }, -- Pants
["arms"] = { item = 31, texture = 0 }, -- Arms
["t-shirt"] = { item = 35, texture = 0 }, -- T Shirt
["vest"] = { item = 34, texture = 0 }, -- Body Vest
["torso2"] = { item = 327, texture = 8 }, -- Jacket
["shoes"] = { item = 52, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 0, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 2, 3, 4 },
},
{
name = "SWAT",
outfitData = {
["pants"] = { item = 135, texture = 1 }, -- Pants
["arms"] = { item = 213, texture = 0 }, -- Arms
["t-shirt"] = { item = 0, texture = 0 }, -- T Shirt
["vest"] = { item = 17, texture = 2 }, -- Body Vest
["torso2"] = { item = 327, texture = 8 }, -- Jacket
["shoes"] = { item = 52, texture = 0 }, -- Shoes
["accessory"] = { item = 102, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 149, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 35, texture = 0 }, -- Mask
},
grades = { 3, 4 },
},
},
},
["realestate"] = {
["Male"] = {
{
-- Outfits
name = "Worker",
outfitData = {
["pants"] = { item = 28, texture = 0 }, -- Pants
["arms"] = { item = 1, texture = 0 }, -- Arms
["t-shirt"] = { item = 31, texture = 0 }, -- T Shirt
["vest"] = { item = 0, texture = 0 }, -- Body Vest
["torso2"] = { item = 294, texture = 0 }, -- Jacket
["shoes"] = { item = 10, texture = 0 }, -- Shoes
["accessory"] = { item = 0, texture = 0 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = 12, texture = -1 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 0, 1, 2, 3, 4 },
},
},
["Female"] = {
{
name = "Worker",
outfitData = {
["pants"] = { item = 57, texture = 2 }, -- Pants
["arms"] = { item = 0, texture = 0 }, -- Arms
["t-shirt"] = { item = 34, texture = 0 }, -- T Shirt
["vest"] = { item = 0, texture = 0 }, -- Body Vest
["torso2"] = { item = 105, texture = 7 }, -- Jacket
["shoes"] = { item = 8, texture = 5 }, -- Shoes
["accessory"] = { item = 11, texture = 3 }, -- Neck Accessory
["bag"] = { item = 0, texture = 0 }, -- Bag
["hat"] = { item = -1, texture = -1 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["mask"] = { item = 0, texture = 0 }, -- Mask
},
grades = { 0, 1, 2, 3, 4 },
},
},
},
["ambulance"] = {
["Male"] = {
{
name = "T-Shirt",
outfitData = {
["arms"] = { item = 85, texture = 0 }, -- Arms
["t-shirt"] = { item = 129, texture = 0 }, -- T-Shirt
["torso2"] = { item = 250, texture = 0 }, -- Jackets
["vest"] = { item = 0, texture = 0 }, -- Vest
["decals"] = { item = 58, texture = 0 }, -- Decals
["accessory"] = { item = 127, texture = 0 }, -- Neck
["bag"] = { item = 0, texture = 0 }, -- Bag
["pants"] = { item = 96, texture = 0 }, -- Pants
["shoes"] = { item = 54, texture = 0 }, -- Shoes
["mask"] = { item = 121, texture = 0 }, -- Mask
["hat"] = { item = 122, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["ear"] = { item = 0, texture = 0 }, -- Ear accessories
},
grades = { 0, 1, 2, 3, 4 },
},
{
name = "Polo",
outfitData = {
["arms"] = { item = 90, texture = 0 }, -- Arms
["t-shirt"] = { item = 15, texture = 0 }, -- T-Shirt
["torso2"] = { item = 249, texture = 0 }, -- Jackets
["vest"] = { item = 0, texture = 0 }, -- Vest
["decals"] = { item = 57, texture = 0 }, -- Decals
["accessory"] = { item = 126, texture = 0 }, -- Neck
["bag"] = { item = 0, texture = 0 }, -- Bag
["pants"] = { item = 96, texture = 0 }, -- Pants
["shoes"] = { item = 54, texture = 0 }, -- Shoes
["mask"] = { item = 121, texture = 0 }, -- Mask
["hat"] = { item = 122, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["ear"] = { item = 0, texture = 0 }, -- Ear accessories
},
grades = { 2, 3, 4 },
},
{
name = "Doctor",
outfitData = {
["arms"] = { item = 93, texture = 0 }, -- Arms
["t-shirt"] = { item = 32, texture = 3 }, -- T-Shirt
["torso2"] = { item = 31, texture = 7 }, -- Jackets
["vest"] = { item = 0, texture = 0 }, -- Vest
["decals"] = { item = 0, texture = 0 }, -- Decals
["accessory"] = { item = 126, texture = 0 }, -- Neck
["bag"] = { item = 0, texture = 0 }, -- Bag
["pants"] = { item = 28, texture = 0 }, -- Pants
["shoes"] = { item = 10, texture = 0 }, -- Shoes
["mask"] = { item = 0, texture = 0 }, -- Mask
["hat"] = { item = -1, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["ear"] = { item = 0, texture = 0 }, -- Ear accessories
},
grades = { 3, 4 },
},
},
["Female"] = {
{
name = "T-Shirt",
outfitData = {
["arms"] = { item = 109, texture = 0 }, -- Arms
["t-shirt"] = { item = 159, texture = 0 }, -- T-Shirt
["torso2"] = { item = 258, texture = 0 }, -- Jackets
["vest"] = { item = 0, texture = 0 }, -- Vest
["decals"] = { item = 66, texture = 0 }, -- Decals
["accessory"] = { item = 97, texture = 0 }, -- Neck
["bag"] = { item = 0, texture = 0 }, -- Bag
["pants"] = { item = 99, texture = 0 }, -- Pants
["shoes"] = { item = 55, texture = 0 }, -- Shoes
["mask"] = { item = 121, texture = 0 }, -- Mask
["hat"] = { item = 121, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["ear"] = { item = 0, texture = 0 }, -- Ear accessories
},
grades = { 0, 1, 2, 3, 4 },
},
{
name = "Polo",
outfitData = {
["arms"] = { item = 105, texture = 0 }, -- Arms
["t-shirt"] = { item = 13, texture = 0 }, -- T-Shirt
["torso2"] = { item = 257, texture = 0 }, -- Jackets
["vest"] = { item = 0, texture = 0 }, -- Vest
["decals"] = { item = 65, texture = 0 }, -- Decals
["accessory"] = { item = 96, texture = 0 }, -- Neck
["bag"] = { item = 0, texture = 0 }, -- Bag
["pants"] = { item = 99, texture = 0 }, -- Pants
["shoes"] = { item = 55, texture = 0 }, -- Shoes
["mask"] = { item = 121, texture = 0 }, -- Mask
["hat"] = { item = 121, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["ear"] = { item = 0, texture = 0 }, -- Ear accessories
},
grades = { 2, 3, 4 },
},
{
name = "Doctor",
outfitData = {
["arms"] = { item = 105, texture = 0 }, -- Arms
["t-shirt"] = { item = 39, texture = 3 }, -- T-Shirt
["torso2"] = { item = 7, texture = 1 }, -- Jackets
["vest"] = { item = 0, texture = 0 }, -- Vest
["decals"] = { item = 0, texture = 0 }, -- Decals
["accessory"] = { item = 96, texture = 0 }, -- Neck
["bag"] = { item = 0, texture = 0 }, -- Bag
["pants"] = { item = 34, texture = 0 }, -- Pants
["shoes"] = { item = 29, texture = 0 }, -- Shoes
["mask"] = { item = 0, texture = 0 }, -- Mask
["hat"] = { item = -1, texture = 0 }, -- Hat
["glass"] = { item = 0, texture = 0 }, -- Glasses
["ear"] = { item = 0, texture = 0 }, -- Ear accessories
},
grades = { 3, 4 },
},
},
},
}
Config.InitialPlayerClothes = {
Male = {
Model = "mp_m_freemode_01",
Components = {
{
component_id = 0, -- Face
drawable = 0,
texture = 0,
},
{
component_id = 1, -- Mask
drawable = 0,
texture = 0,
},
{
component_id = 2, -- Hair
drawable = 0,
texture = 0,
},
{
component_id = 3, -- Upper Body
drawable = 0,
texture = 0,
},
{
component_id = 4, -- Lower Body
drawable = 0,
texture = 0,
},
{
component_id = 5, -- Bag
drawable = 0,
texture = 0,
},
{
component_id = 6, -- Shoes
drawable = 0,
texture = 0,
},
{
component_id = 7, -- Scarf & Chains
drawable = 0,
texture = 0,
},
{
component_id = 8, -- Shirt
drawable = 0,
texture = 0,
},
{
component_id = 9, -- Body Armor
drawable = 0,
texture = 0,
},
{
component_id = 10, -- Decals
drawable = 0,
texture = 0,
},
{
component_id = 11, -- Jacket
drawable = 0,
texture = 0,
},
},
Props = {
{
prop_id = 0, -- Hat
drawable = -1,
texture = -1,
},
{
prop_id = 1, -- Glasses
drawable = -1,
texture = -1,
},
{
prop_id = 2, -- Ear
drawable = -1,
texture = -1,
},
{
prop_id = 6, -- Watch
drawable = -1,
texture = -1,
},
{
prop_id = 7, -- Bracelet
drawable = -1,
texture = -1,
},
},
Hair = {
color = 0,
highlight = 0,
style = 0,
texture = 0,
},
},
Female = {
Model = "mp_f_freemode_01",
Components = {
{
component_id = 0, -- Face
drawable = 0,
texture = 0,
},
{
component_id = 1, -- Mask
drawable = 0,
texture = 0,
},
{
component_id = 2, -- Hair
drawable = 0,
texture = 0,
},
{
component_id = 3, -- Upper Body
drawable = 0,
texture = 0,
},
{
component_id = 4, -- Lower Body
drawable = 0,
texture = 0,
},
{
component_id = 5, -- Bag
drawable = 0,
texture = 0,
},
{
component_id = 6, -- Shoes
drawable = 0,
texture = 0,
},
{
component_id = 7, -- Scarf & Chains
drawable = 0,
texture = 0,
},
{
component_id = 8, -- Shirt
drawable = 0,
texture = 0,
},
{
component_id = 9, -- Body Armor
drawable = 0,
texture = 0,
},
{
component_id = 10, -- Decals
drawable = 0,
texture = 0,
},
{
component_id = 11, -- Jacket
drawable = 0,
texture = 0,
},
},
Props = {
{
prop_id = 0, -- Hat
drawable = -1,
texture = -1,
},
{
prop_id = 1, -- Glasses
drawable = -1,
texture = -1,
},
{
prop_id = 2, -- Ear
drawable = -1,
texture = -1,
},
{
prop_id = 6, -- Watch
drawable = -1,
texture = -1,
},
{
prop_id = 7, -- Bracelet
drawable = -1,
texture = -1,
},
},
Hair = {
color = 0,
highlight = 0,
style = 0,
texture = 0,
},
},
}
```
peds [#peds]
```lua title="peds.lua"
Config.Peds = {
pedConfig = {
{
peds = {
-- First 2 values are the default female/male peds, if you wanna remove custom peds just delete the rest and keep these 2.
"mp_f_freemode_01",
"mp_m_freemode_01",
"a_c_boar",
"a_c_boar_02",
"a_c_cat_01",
"a_c_chickenhawk",
"a_c_chimp",
"a_c_chimp_02",
"a_c_chop",
"a_c_chop_02",
"a_c_cormorant",
"a_c_cow",
"a_c_coyote",
"a_c_coyote_02",
"a_c_crow",
"a_c_deer",
"a_c_deer_02",
"a_c_dolphin",
"a_c_fish",
"a_c_hen",
"a_c_humpback",
"a_c_husky",
"a_c_killerwhale",
"a_c_mtlion",
"a_c_mtlion_02",
"a_c_panther",
"a_c_pig",
"a_c_pigeon",
"a_c_poodle",
"a_c_pug",
"a_c_pug_02",
"a_c_rabbit_01",
"a_c_rabbit_02",
"a_c_rat",
"a_c_retriever",
"a_c_rhesus",
"a_c_rottweiler",
"a_c_seagull",
"a_c_sharkhammer",
"a_c_sharktiger",
"a_c_shepherd",
"a_c_stingray",
"a_c_westy",
"a_f_m_beach_01",
"a_f_m_bevhills_01",
"a_f_m_bevhills_02",
"a_f_m_bodybuild_01",
"a_f_m_business_02",
"a_f_m_downtown_01",
"a_f_m_eastsa_01",
"a_f_m_eastsa_02",
"a_f_m_fatbla_01",
"a_f_m_fatcult_01",
"a_f_m_fatwhite_01",
"a_f_m_genbiker_01",
"a_f_m_genstreet_01",
"a_f_m_ktown_01",
"a_f_m_ktown_02",
"a_f_m_prolhost_01",
"a_f_m_salton_01",
"a_f_m_skidrow_01",
"a_f_m_soucent_01",
"a_f_m_soucent_02",
"a_f_m_soucentmc_01",
"a_f_m_tourist_01",
"a_f_m_tramp_01",
"a_f_m_trampbeac_01",
"a_f_o_genstreet_01",
"a_f_o_indian_01",
"a_f_o_ktown_01",
"a_f_o_salton_01",
"a_f_o_soucent_01",
"a_f_o_soucent_02",
"a_f_y_beach_01",
"a_f_y_beach_02",
"a_f_y_bevhills_01",
"a_f_y_bevhills_02",
"a_f_y_bevhills_03",
"a_f_y_bevhills_04",
"a_f_y_bevhills_05",
"a_f_y_business_01",
"a_f_y_business_02",
"a_f_y_business_03",
"a_f_y_business_04",
"a_f_y_carclub_01",
"a_f_y_clubcust_01",
"a_f_y_clubcust_02",
"a_f_y_clubcust_03",
"a_f_y_clubcust_04",
"a_f_y_eastsa_01",
"a_f_y_eastsa_02",
"a_f_y_eastsa_03",
"a_f_y_epsilon_01",
"a_f_y_femaleagent",
"a_f_y_fitness_01",
"a_f_y_fitness_02",
"a_f_y_gencaspat_01",
"a_f_y_genhot_01",
"a_f_y_golfer_01",
"a_f_y_hiker_01",
"a_f_y_hippie_01",
"a_f_y_hipster_01",
"a_f_y_hipster_02",
"a_f_y_hipster_03",
"a_f_y_hipster_04",
"a_f_y_indian_01",
"a_f_y_juggalo_01",
"a_f_y_runner_01",
"a_f_y_rurmeth_01",
"a_f_y_scdressy_01",
"a_f_y_skater_01",
"a_f_y_smartcaspat_01",
"a_f_y_soucent_01",
"a_f_y_soucent_02",
"a_f_y_soucent_03",
"a_f_y_studioparty_01",
"a_f_y_studioparty_02",
"a_f_y_tennis_01",
"a_f_y_topless_01",
"a_f_y_tourist_01",
"a_f_y_tourist_02",
"a_f_y_vinewood_01",
"a_f_y_vinewood_02",
"a_f_y_vinewood_03",
"a_f_y_vinewood_04",
"a_f_y_yoga_01",
"a_m_m_acult_01",
"a_m_m_afriamer_01",
"a_m_m_bankrobber_01",
"a_m_m_beach_01",
"a_m_m_beach_02",
"a_m_m_bevhills_01",
"a_m_m_bevhills_02",
"a_m_m_business_01",
"a_m_m_eastsa_01",
"a_m_m_eastsa_02",
"a_m_m_farmer_01",
"a_m_m_fatlatin_01",
"a_m_m_genbiker_01",
"a_m_m_genfat_01",
"a_m_m_genfat_02",
"a_m_m_golfer_01",
"a_m_m_hasjew_01",
"a_m_m_hillbilly_01",
"a_m_m_hillbilly_02",
"a_m_m_indian_01",
"a_m_m_ktown_01",
"a_m_m_malibu_01",
"a_m_m_mexcntry_01",
"a_m_m_mexlabor_01",
"a_m_m_mlcrisis_01",
"a_m_m_og_boss_01",
"a_m_m_paparazzi_01",
"a_m_m_polynesian_01",
"a_m_m_prolhost_01",
"a_m_m_rurmeth_01",
"a_m_m_salton_01",
"a_m_m_salton_02",
"a_m_m_salton_03",
"a_m_m_salton_04",
"a_m_m_skater_01",
"a_m_m_skidrow_01",
"a_m_m_socenlat_01",
"a_m_m_soucent_01",
"a_m_m_soucent_02",
"a_m_m_soucent_03",
"a_m_m_soucent_04",
"a_m_m_stlat_02",
"a_m_m_studioparty_01",
"a_m_m_tennis_01",
"a_m_m_tourist_01",
"a_m_m_tramp_01",
"a_m_m_trampbeac_01",
"a_m_m_tranvest_01",
"a_m_m_tranvest_02",
"a_m_o_acult_01",
"a_m_o_acult_02",
"a_m_o_beach_01",
"a_m_o_beach_02",
"a_m_o_genstreet_01",
"a_m_o_ktown_01",
"a_m_o_salton_01",
"a_m_o_soucent_01",
"a_m_o_soucent_02",
"a_m_o_soucent_03",
"a_m_o_tramp_01",
"a_m_y_acult_01",
"a_m_y_acult_02",
"a_m_y_beach_01",
"a_m_y_beach_02",
"a_m_y_beach_03",
"a_m_y_beach_04",
"a_m_y_beachvesp_01",
"a_m_y_beachvesp_02",
"a_m_y_bevhills_01",
"a_m_y_bevhills_02",
"a_m_y_breakdance_01",
"a_m_y_busicas_01",
"a_m_y_business_01",
"a_m_y_business_02",
"a_m_y_business_03",
"a_m_y_carclub_01",
"a_m_y_clubcust_01",
"a_m_y_clubcust_02",
"a_m_y_clubcust_03",
"a_m_y_clubcust_04",
"a_m_y_cyclist_01",
"a_m_y_dhill_01",
"a_m_y_downtown_01",
"a_m_y_eastsa_01",
"a_m_y_eastsa_02",
"a_m_y_epsilon_01",
"a_m_y_epsilon_02",
"a_m_y_gay_01",
"a_m_y_gay_02",
"a_m_y_gencaspat_01",
"a_m_y_genstreet_01",
"a_m_y_genstreet_02",
"a_m_y_golfer_01",
"a_m_y_hasjew_01",
"a_m_y_hiker_01",
"a_m_y_hippy_01",
"a_m_y_hipster_01",
"a_m_y_hipster_02",
"a_m_y_hipster_03",
"a_m_y_indian_01",
"a_m_y_jetski_01",
"a_m_y_juggalo_01",
"a_m_y_ktown_01",
"a_m_y_ktown_02",
"a_m_y_latino_01",
"a_m_y_methhead_01",
"a_m_y_mexthug_01",
"a_m_y_motox_01",
"a_m_y_motox_02",
"a_m_y_musclbeac_01",
"a_m_y_musclbeac_02",
"a_m_y_polynesian_01",
"a_m_y_roadcyc_01",
"a_m_y_runner_01",
"a_m_y_runner_02",
"a_m_y_salton_01",
"a_m_y_skater_01",
"a_m_y_skater_02",
"a_m_y_smartcaspat_01",
"a_m_y_soucent_01",
"a_m_y_soucent_02",
"a_m_y_soucent_03",
"a_m_y_soucent_04",
"a_m_y_stbla_01",
"a_m_y_stbla_02",
"a_m_y_stlat_01",
"a_m_y_studioparty_01",
"a_m_y_stwhi_01",
"a_m_y_stwhi_02",
"a_m_y_sunbathe_01",
"a_m_y_surfer_01",
"a_m_y_tattoocust_01",
"a_m_y_vindouche_01",
"a_m_y_vinewood_01",
"a_m_y_vinewood_02",
"a_m_y_vinewood_03",
"a_m_y_vinewood_04",
"a_m_y_yoga_01",
"cs_amandatownley",
"cs_andreas",
"cs_ashley",
"cs_bankman",
"cs_barry",
"cs_beverly",
"cs_brad",
"cs_bradcadaver",
"cs_carbuyer",
"cs_casey",
"cs_chengsr",
"cs_chrisformage",
"cs_clay",
"cs_dale",
"cs_davenorton",
"cs_debra",
"cs_denise",
"cs_devin",
"cs_dom",
"cs_dreyfuss",
"cs_drfriedlander",
"cs_drfriedlander_02",
"cs_fabien",
"cs_fbisuit_01",
"cs_floyd",
"cs_guadalope",
"cs_gurk",
"cs_hunter",
"cs_janet",
"cs_jewelass",
"cs_jimmyboston",
"cs_jimmydisanto",
"cs_jimmydisanto2",
"cs_joeminuteman",
"cs_johnnyklebitz",
"cs_josef",
"cs_josh",
"cs_karen_daniels",
"cs_lamardavis",
"cs_lamardavis_02",
"cs_lazlow",
"cs_lazlow_2",
"cs_lestercrest",
"cs_lestercrest_2",
"cs_lestercrest_3",
"cs_lifeinvad_01",
"cs_magenta",
"cs_manuel",
"cs_marnie",
"cs_martinmadrazo",
"cs_maryann",
"cs_michelle",
"cs_milton",
"cs_molly",
"cs_movpremf_01",
"cs_movpremmale",
"cs_mrk",
"cs_mrs_thornhill",
"cs_mrsphillips",
"cs_natalia",
"cs_nervousron",
"cs_nervousron_02",
"cs_nigel",
"cs_old_man1a",
"cs_old_man2",
"cs_omega",
"cs_orleans",
"cs_paper",
"cs_patricia",
"cs_patricia_02",
"cs_priest",
"cs_prolsec_02",
"cs_russiandrunk",
"cs_siemonyetarian",
"cs_solomon",
"cs_stevehains",
"cs_stretch",
"cs_tanisha",
"cs_taocheng",
"cs_taocheng2",
"cs_taostranslator",
"cs_taostranslator2",
"cs_tenniscoach",
"cs_terry",
"cs_tom",
"cs_tomepsilon",
"cs_tracydisanto",
"cs_wade",
"cs_zimbor",
"csb_abigail",
"csb_agatha",
"csb_agent",
"csb_alan",
"csb_anita",
"csb_anton",
"csb_ary",
"csb_ary_02",
"csb_avery",
"csb_avischwartzman_02",
"csb_avischwartzman_03",
"csb_avon",
"csb_ballas_leader",
"csb_ballasog",
"csb_billionaire",
"csb_bogdan",
"csb_bride",
"csb_brucie2",
"csb_bryony",
"csb_burgerdrug",
"csb_callgirl_01",
"csb_callgirl_02",
"csb_car3guy1",
"csb_car3guy2",
"csb_celeb_01",
"csb_charlie_reed",
"csb_chef",
"csb_chef_03",
"csb_chef2",
"csb_chin_goon",
"csb_cletus",
"csb_cop",
"csb_customer",
"csb_dax",
"csb_denise_friend",
"csb_dix",
"csb_djblamadon",
"csb_drugdealer",
"csb_englishdave",
"csb_englishdave_02",
"csb_fos_rep",
"csb_g",
"csb_georginacheng",
"csb_golfer_a",
"csb_golfer_b",
"csb_groom",
"csb_grove_str_dlr",
"csb_gustavo",
"csb_hao",
"csb_hao_02",
"csb_helmsmanpavel",
"csb_huang",
"csb_hugh",
"csb_imani",
"csb_imran",
"csb_isldj_00",
"csb_isldj_01",
"csb_isldj_02",
"csb_isldj_03",
"csb_isldj_04",
"csb_jackhowitzer",
"csb_jamalamir",
"csb_janitor",
"csb_jio",
"csb_jio_02",
"csb_johnny_guns",
"csb_juanstrickler",
"csb_labrat",
"csb_luchadora",
"csb_maude",
"csb_miguelmadrazo",
"csb_mimi",
"csb_mjo",
"csb_mjo_02",
"csb_money",
"csb_moodyman_02",
"csb_mp_agent14",
"csb_mrs_r",
"csb_musician_00",
"csb_mweather",
"csb_ortega",
"csb_oscar",
"csb_paige",
"csb_party_promo",
"csb_popov",
"csb_porndudes",
"csb_prologuedriver",
"csb_prolsec",
"csb_ramp_gang",
"csb_ramp_hic",
"csb_ramp_hipster",
"csb_ramp_marine",
"csb_ramp_mex",
"csb_rashcosvki",
"csb_reporter",
"csb_req_officer",
"csb_roccopelosi",
"csb_screen_writer",
"csb_security_a",
"csb_sessanta",
"csb_sol",
"csb_soundeng_00",
"csb_sss",
"csb_stripper_01",
"csb_stripper_02",
"csb_talcc",
"csb_talmm",
"csb_thornton",
"csb_tomcasino",
"csb_tonya",
"csb_tonyprince",
"csb_trafficwarden",
"csb_undercover",
"csb_vagos_leader",
"csb_vagspeak",
"csb_vernon",
"csb_vincent",
"csb_vincent_2",
"csb_vincent_4",
"csb_wendy",
"csb_yusufamir",
"g_f_importexport_01",
"g_f_m_fooliganz_01",
"g_f_y_ballas_01",
"g_f_y_families_01",
"g_f_y_lost_01",
"g_f_y_vagos_01",
"g_m_importexport_01",
"g_m_m_armboss_01",
"g_m_m_armgoon_01",
"g_m_m_armlieut_01",
"g_m_m_cartelgoons_01",
"g_m_m_cartelguards_01",
"g_m_m_cartelguards_02",
"g_m_m_casrn_01",
"g_m_m_chemwork_01",
"g_m_m_chiboss_01",
"g_m_m_chicold_01",
"g_m_m_chigoon_01",
"g_m_m_chigoon_02",
"g_m_m_fooliganz_01",
"g_m_m_friedlandergoons_01",
"g_m_m_genthug_01",
"g_m_m_goons_01",
"g_m_m_korboss_01",
"g_m_m_maragrande_01",
"g_m_m_mexboss_01",
"g_m_m_mexboss_02",
"g_m_m_prisoners_01",
"g_m_m_slasher_01",
"g_m_y_armgoon_02",
"g_m_y_azteca_01",
"g_m_y_ballaeast_01",
"g_m_y_ballaorig_01",
"g_m_y_ballasout_01",
"g_m_y_famca_01",
"g_m_y_famdnf_01",
"g_m_y_famfor_01",
"g_m_y_korean_01",
"g_m_y_korean_02",
"g_m_y_korlieut_01",
"g_m_y_lost_01",
"g_m_y_lost_02",
"g_m_y_lost_03",
"g_m_y_mexgang_01",
"g_m_y_mexgoon_01",
"g_m_y_mexgoon_02",
"g_m_y_mexgoon_03",
"g_m_y_pologoon_01",
"g_m_y_pologoon_02",
"g_m_y_salvaboss_01",
"g_m_y_salvagoon_01",
"g_m_y_salvagoon_02",
"g_m_y_salvagoon_03",
"g_m_y_strpunk_01",
"g_m_y_strpunk_02",
"hc_driver",
"hc_gunman",
"hc_hacker",
"ig_abigail",
"ig_acidlabcook",
"ig_agatha",
"ig_agent",
"ig_agent_02",
"ig_ahronward",
"ig_amandatownley",
"ig_andreas",
"ig_ary",
"ig_ary_02",
"ig_ashley",
"ig_avery",
"ig_avischwartzman_02",
"ig_avischwartzman_03",
"ig_avon",
"ig_ballas_leader",
"ig_ballasog",
"ig_bankman",
"ig_barry",
"ig_benny",
"ig_benny_02",
"ig_bestmen",
"ig_beverly",
"ig_billionaire",
"ig_brad",
"ig_bride",
"ig_brucie2",
"ig_callgirl_01",
"ig_callgirl_02",
"ig_car3guy1",
"ig_car3guy2",
"ig_casey",
"ig_celeb_01",
"ig_charlie_reed",
"ig_chef",
"ig_chef_03",
"ig_chef2",
"ig_chengsr",
"ig_chrisformage",
"ig_clay",
"ig_claypain",
"ig_cletus",
"ig_dale",
"ig_davenorton",
"ig_dax",
"ig_denise",
"ig_devin",
"ig_dix",
"ig_djblamadon",
"ig_djblamrupert",
"ig_djblamryanh",
"ig_djblamryans",
"ig_djdixmanager",
"ig_djgeneric_01",
"ig_djsolfotios",
"ig_djsoljakob",
"ig_djsolmanager",
"ig_djsolmike",
"ig_djsolrobt",
"ig_djtalaurelia",
"ig_djtalignazio",
"ig_dom",
"ig_dreyfuss",
"ig_drfriedlander",
"ig_drfriedlander_02",
"ig_drugdealer",
"ig_englishdave",
"ig_englishdave_02",
"ig_entourage_a",
"ig_entourage_b",
"ig_fabien",
"ig_fbisuit_01",
"ig_floyd",
"ig_fooliganz_01",
"ig_fooliganz_02",
"ig_furry",
"ig_g",
"ig_georginacheng",
"ig_golfer_a",
"ig_golfer_b",
"ig_groom",
"ig_gunvanseller",
"ig_gustavo",
"ig_hao",
"ig_hao_02",
"ig_helmsmanpavel",
"ig_hippyleader",
"ig_huang",
"ig_hunter",
"ig_imani",
"ig_isldj_00",
"ig_isldj_01",
"ig_isldj_02",
"ig_isldj_03",
"ig_isldj_04",
"ig_isldj_04_d_01",
"ig_isldj_04_d_02",
"ig_isldj_04_e_01",
"ig_jackie",
"ig_jamalamir",
"ig_janet",
"ig_jay_norris",
"ig_jaywalker",
"ig_jewelass",
"ig_jimmyboston",
"ig_jimmyboston_02",
"ig_jimmydisanto",
"ig_jimmydisanto2",
"ig_jio",
"ig_jio_02",
"ig_joeminuteman",
"ig_johnny_guns",
"ig_johnnyklebitz",
"ig_josef",
"ig_josh",
"ig_juanstrickler",
"ig_karen_daniels",
"ig_kaylee",
"ig_kerrymcintosh",
"ig_kerrymcintosh_02",
"ig_labrat",
"ig_lacey_jones_02",
"ig_lamardavis",
"ig_lamardavis_02",
"ig_lazlow",
"ig_lazlow_2",
"ig_lestercrest",
"ig_lestercrest_2",
"ig_lestercrest_3",
"ig_lifeinvad_01",
"ig_lifeinvad_02",
"ig_lildee",
"ig_luchadora",
"ig_magenta",
"ig_malc",
"ig_manuel",
"ig_marnie",
"ig_maryann",
"ig_mason_duggan",
"ig_maude",
"ig_mechanic_01",
"ig_mechanic_02",
"ig_michelle",
"ig_miguelmadrazo",
"ig_milton",
"ig_mimi",
"ig_mjo",
"ig_mjo_02",
"ig_molly",
"ig_money",
"ig_moodyman_02",
"ig_mp_agent14",
"ig_mrk",
"ig_mrs_thornhill",
"ig_mrsphillips",
"ig_musician_00",
"ig_natalia",
"ig_nervousron",
"ig_nervousron_02",
"ig_nigel",
"ig_old_man1a",
"ig_old_man2",
"ig_oldrichguy",
"ig_omega",
"ig_oneil",
"ig_orleans",
"ig_ortega",
"ig_paige",
"ig_paper",
"ig_party_promo",
"ig_patricia",
"ig_patricia_02",
"ig_pernell_moss",
"ig_pilot",
"ig_pilot_02",
"ig_popov",
"ig_priest",
"ig_prolsec_02",
"ig_ramp_gang",
"ig_ramp_hic",
"ig_ramp_hipster",
"ig_ramp_mex",
"ig_rashcosvki",
"ig_req_officer",
"ig_roccopelosi",
"ig_roostermccraw",
"ig_russiandrunk",
"ig_sacha",
"ig_screen_writer",
"ig_security_a",
"ig_sessanta",
"ig_siemonyetarian",
"ig_sol",
"ig_solomon",
"ig_soundeng_00",
"ig_sss",
"ig_stevehains",
"ig_stretch",
"ig_subcrewhead",
"ig_talcc",
"ig_talina",
"ig_talmm",
"ig_tanisha",
"ig_taocheng",
"ig_taocheng2",
"ig_taostranslator",
"ig_taostranslator2",
"ig_tenniscoach",
"ig_terry",
"ig_thornton",
"ig_tomcasino",
"ig_tomepsilon",
"ig_tonya",
"ig_tonyprince",
"ig_tracydisanto",
"ig_trafficwarden",
"ig_tylerdix",
"ig_tylerdix_02",
"ig_vagos_leader",
"ig_vagspeak",
"ig_vernon",
"ig_vincent",
"ig_vincent_2",
"ig_vincent_3",
"ig_vincent_4",
"ig_wade",
"ig_warehouseboss",
"ig_wendy",
"ig_yusufamir",
"ig_zimbor",
"mp_f_bennymech_01",
"mp_f_boatstaff_01",
"mp_f_cardesign_01",
"mp_f_chbar_01",
"mp_f_cocaine_01",
"mp_f_counterfeit_01",
"mp_f_deadhooker",
"mp_f_execpa_01",
"mp_f_execpa_02",
"mp_f_forgery_01",
"mp_f_helistaff_01",
"mp_f_meth_01",
"mp_f_misty_01",
"mp_f_stripperlite",
"mp_f_weed_01",
"mp_g_m_pros_01",
"mp_headtargets",
"mp_m_avongoon",
"mp_m_boatstaff_01",
"mp_m_bogdangoon",
"mp_m_claude_01",
"mp_m_cocaine_01",
"mp_m_counterfeit_01",
"mp_m_exarmy_01",
"mp_m_execpa_01",
"mp_m_famdd_01",
"mp_m_fibsec_01",
"mp_m_forgery_01",
"mp_m_g_vagfun_01",
"mp_m_marston_01",
"mp_m_meth_01",
"mp_m_niko_01",
"mp_m_securoguard_01",
"mp_m_shopkeep_01",
"mp_m_waremech_01",
"mp_m_weapexp_01",
"mp_m_weapwork_01",
"mp_m_weed_01",
"mp_s_m_armoured_01",
"p_franklin_02",
"player_one",
"player_two",
"player_zero",
"s_f_m_autoshop_01",
"s_f_m_fembarber",
"s_f_m_maid_01",
"s_f_m_retailstaff_01",
"s_f_m_shop_high",
"s_f_m_studioassist_01",
"s_f_m_sweatshop_01",
"s_f_m_warehouse_01",
"s_f_y_airhostess_01",
"s_f_y_bartender_01",
"s_f_y_baywatch_01",
"s_f_y_beachbarstaff_01",
"s_f_y_casino_01",
"s_f_y_clubbar_01",
"s_f_y_clubbar_02",
"s_f_y_cop_01",
"s_f_y_factory_01",
"s_f_y_hooker_01",
"s_f_y_hooker_02",
"s_f_y_hooker_03",
"s_f_y_migrant_01",
"s_f_y_movprem_01",
"s_f_y_ranger_01",
"s_f_y_scrubs_01",
"s_f_y_sheriff_01",
"s_f_y_shop_low",
"s_f_y_shop_mid",
"s_f_y_stripper_01",
"s_f_y_stripper_02",
"s_f_y_stripperlite",
"s_f_y_sweatshop_01",
"s_m_m_ammucountry",
"s_m_m_armoured_01",
"s_m_m_armoured_02",
"s_m_m_autoshop_01",
"s_m_m_autoshop_02",
"s_m_m_autoshop_03",
"s_m_m_bouncer_01",
"s_m_m_bouncer_02",
"s_m_m_ccrew_01",
"s_m_m_ccrew_02",
"s_m_m_ccrew_03",
"s_m_m_chemsec_01",
"s_m_m_ciasec_01",
"s_m_m_cntrybar_01",
"s_m_m_cop_01",
"s_m_m_dockwork_01",
"s_m_m_doctor_01",
"s_m_m_drugprocess_01",
"s_m_m_fiboffice_01",
"s_m_m_fiboffice_02",
"s_m_m_fibsec_01",
"s_m_m_fieldworker_01",
"s_m_m_gaffer_01",
"s_m_m_gardener_01",
"s_m_m_gentransport",
"s_m_m_hairdress_01",
"s_m_m_hazmatworker_01",
"s_m_m_highsec_01",
"s_m_m_highsec_02",
"s_m_m_highsec_03",
"s_m_m_highsec_04",
"s_m_m_highsec_05",
"s_m_m_janitor",
"s_m_m_lathandy_01",
"s_m_m_lifeinvad_01",
"s_m_m_linecook",
"s_m_m_lsmetro_01",
"s_m_m_mariachi_01",
"s_m_m_marine_01",
"s_m_m_marine_02",
"s_m_m_migrant_01",
"s_m_m_movalien_01",
"s_m_m_movprem_01",
"s_m_m_movspace_01",
"s_m_m_paramedic_01",
"s_m_m_pilot_01",
"s_m_m_pilot_02",
"s_m_m_postal_01",
"s_m_m_postal_02",
"s_m_m_prisguard_01",
"s_m_m_raceorg_01",
"s_m_m_scientist_01",
"s_m_m_security_01",
"s_m_m_snowcop_01",
"s_m_m_strperf_01",
"s_m_m_strpreach_01",
"s_m_m_strvend_01",
"s_m_m_studioassist_02",
"s_m_m_studioprod_01",
"s_m_m_studiosoueng_02",
"s_m_m_subcrew_01",
"s_m_m_tattoo_01",
"s_m_m_trucker_01",
"s_m_m_ups_01",
"s_m_m_ups_02",
"s_m_m_warehouse_01",
"s_m_o_busker_01",
"s_m_y_airworker",
"s_m_y_ammucity_01",
"s_m_y_armymech_01",
"s_m_y_autopsy_01",
"s_m_y_barman_01",
"s_m_y_baywatch_01",
"s_m_y_blackops_01",
"s_m_y_blackops_02",
"s_m_y_blackops_03",
"s_m_y_busboy_01",
"s_m_y_casino_01",
"s_m_y_chef_01",
"s_m_y_clown_01",
"s_m_y_clubbar_01",
"s_m_y_construct_01",
"s_m_y_construct_02",
"s_m_y_cop_01",
"s_m_y_dealer_01",
"s_m_y_devinsec_01",
"s_m_y_dockwork_01",
"s_m_y_doorman_01",
"s_m_y_dwservice_01",
"s_m_y_dwservice_02",
"s_m_y_factory_01",
"s_m_y_fireman_01",
"s_m_y_garbage",
"s_m_y_grip_01",
"s_m_y_hwaycop_01",
"s_m_y_marine_01",
"s_m_y_marine_02",
"s_m_y_marine_03",
"s_m_y_mime",
"s_m_y_pestcont_01",
"s_m_y_pilot_01",
"s_m_y_prismuscl_01",
"s_m_y_prisoner_01",
"s_m_y_ranger_01",
"s_m_y_robber_01",
"s_m_y_sheriff_01",
"s_m_y_shop_mask",
"s_m_y_strvend_01",
"s_m_y_swat_01",
"s_m_y_uscg_01",
"s_m_y_valet_01",
"s_m_y_waiter_01",
"s_m_y_waretech_01",
"s_m_y_westsec_01",
"s_m_y_westsec_02",
"s_m_y_winclean_01",
"s_m_y_xmech_01",
"s_m_y_xmech_02",
"s_m_y_xmech_02_mp",
"u_f_m_casinocash_01",
"u_f_m_casinoshop_01",
"u_f_m_corpse_01",
"u_f_m_debbie_01",
"u_f_m_drowned_01",
"u_f_m_miranda",
"u_f_m_miranda_02",
"u_f_m_promourn_01",
"u_f_o_carol",
"u_f_o_eileen",
"u_f_o_moviestar",
"u_f_o_prolhost_01",
"u_f_y_beth",
"u_f_y_bikerchic",
"u_f_y_comjane",
"u_f_y_corpse_01",
"u_f_y_corpse_02",
"u_f_y_danceburl_01",
"u_f_y_dancelthr_01",
"u_f_y_dancerave_01",
"u_f_y_hotposh_01",
"u_f_y_jewelass_01",
"u_f_y_lauren",
"u_f_y_mistress",
"u_f_y_poppymich",
"u_f_y_poppymich_02",
"u_f_y_princess",
"u_f_y_spyactress",
"u_f_y_taylor",
"u_m_m_aldinapoli",
"u_m_m_bankman",
"u_m_m_bikehire_01",
"u_m_m_blane",
"u_m_m_curtis",
"u_m_m_doa_01",
"u_m_m_edtoh",
"u_m_m_fibarchitect",
"u_m_m_filmdirector",
"u_m_m_glenstank_01",
"u_m_m_griff_01",
"u_m_m_jesus_01",
"u_m_m_jewelsec_01",
"u_m_m_jewelthief",
"u_m_m_juggernaut_03",
"u_m_m_markfost",
"u_m_m_partytarget",
"u_m_m_prolsec_01",
"u_m_m_promourn_01",
"u_m_m_rivalpap",
"u_m_m_spyactor",
"u_m_m_streetart_01",
"u_m_m_vince",
"u_m_m_willyfist",
"u_m_m_yeti",
"u_m_m_yulemonster",
"u_m_o_dean",
"u_m_o_filmnoir",
"u_m_o_finguru_01",
"u_m_o_taphillbilly",
"u_m_o_tramp_01",
"u_m_y_abner",
"u_m_y_antonb",
"u_m_y_babyd",
"u_m_y_baygor",
"u_m_y_burgerdrug_01",
"u_m_y_caleb",
"u_m_y_chip",
"u_m_y_corpse_01",
"u_m_y_croupthief_01",
"u_m_y_cyclist_01",
"u_m_y_danceburl_01",
"u_m_y_dancelthr_01",
"u_m_y_dancerave_01",
"u_m_y_fibmugger_01",
"u_m_y_gabriel",
"u_m_y_guido_01",
"u_m_y_gunvend_01",
"u_m_y_hippie_01",
"u_m_y_imporage",
"u_m_y_juggernaut_01",
"u_m_y_juggernaut_02",
"u_m_y_justin",
"u_m_y_mani",
"u_m_y_militarybum",
"u_m_y_paparazzi",
"u_m_y_party_01",
"u_m_y_pogo_01",
"u_m_y_prisoner_01",
"u_m_y_proldriver_01",
"u_m_y_rsranger_01",
"u_m_y_sbike",
"u_m_y_smugmech_01",
"u_m_y_staggrm_01",
"u_m_y_tattoo_01",
"u_m_y_ushi",
"u_m_y_zombie_01",
},
},
},
}
```
theme [#theme]
```lua title="theme.lua"
-- Make sure to follow the required format on colors for each section.
return {
-- Colors (HSL)
["background"] = "0 0% 6.7%",
["background-alt"] = "0 0% 11%",
["foreground"] = "0 0% 94.9%",
["foreground-alt"] = "0 0% 70.2%",
["muted"] = "240 5.9% 16.7%",
["muted-foreground"] = "0 0% 100% / 0.4",
["dark"] = "0 0% 96.1%",
["dark-04"] = "0 0% 96% / 0.4",
["dark-10"] = "0 0% 96% / 0.1",
["dark-40"] = "0 0% 96% / 0.04",
["accent"] = "201 88% 87%",
["destructive"] = "347 78% 60%",
["border"] = "0 0% 16.5%",
["border-alt"] = "0 0% 8.2%",
-- Colors (HEX)
["text"] = "#dadada",
["text-alt"] = "#dadada95",
["icon"] = "#ffffff",
-- Background Noise Config
["noise-deg"] = "0deg",
["noise-from-color"] = "#FFFFFF1A",
["noise-from-percentage"] = "0%",
["noise-to-color"] = "#FFFFFF1A",
["noise-to-percentage"] = "100%",
--Gradient Values
["menu-gradient"] = "#111111",
["header-radial-gradient"] = "#262626, #0d0d0f",
--Shadows
["shadow-mini"] = "0 0.25vmin 0 0.25vmin #0000004D",
["shadow-popover"] = "0 1.75vmin 3vmin 0.75vmin #0000004D",
-- Border Radius
["none"] = "0",
["sm"] = "0.125vmin",
["DEFAULT"] = "0.25vmin",
["md"] = "0.675vmin",
["lg"] = "1vmin",
["xl"] = "1.55vmin",
["2xl"] = "2vmin",
["3xl"] = "2.5vmin",
["card"] = "1.6vmin",
["card-sm"] = "1.0vmin",
["card-lg"] = "2.0vmin",
["input"] = "0.9vmin",
["button"] = "1vmin",
}
```
```
```
# Exports
Clientside Exports [#clientside-exports]
startPlayerCustomization(callback, config) [#startplayercustomizationcallback-config]
Starts the player customization process, allowing players to modify their appearance. The callback is triggered once customization is complete, returning appearance data or nil if canceled.
```lua
---@param callback function Receives `appearance` data if saved or `nil` if canceled
---@param config table Configuration for customization options
exports['illenium-appearance']:startPlayerCustomization(function(appearance)
if appearance then
print('Customization saved!')
else
print('Customization canceled.')
end
end, {
ped = true,
headBlend = true,
faceFeatures = true,
headOverlays = true,
components = true,
props = true,
allowExit = true,
tattoos = true
})
```
getPedModel(ped) [#getpedmodelped]
Retrieves the model name associated with a player's entity.
```lua
---@param ped number Entity ID of the pedestrian
---@return string Model name
local pedModel = exports['illenium-appearance']:getPedModel(ped)
print(pedModel) -- e.g. "mp_m_freemode_01"
```
getPedComponents(ped) [#getpedcomponentsped]
Retrieves the clothing and accessory components of a pedestrian's model.
```lua
---@param ped number Entity ID
---@return table Components data
local components = exports['illenium-appearance']:getPedComponents(ped)
print(components[1].drawable)
```
getPedProps(ped) [#getpedpropsped]
Retrieves the props (hats, glasses, etc.) for a pedestrian.
```lua
---@param ped number Entity ID
---@return table Props data
local props = exports['illenium-appearance']:getPedProps(ped)
print(props[1].prop_id)
```
getPedHeadBlend(ped) [#getpedheadblendped]
Retrieves the head blend data (shape, skin mix, etc.) for a pedestrian.
```lua
---@param ped number Entity ID
---@return table Head blend data
local headBlend = exports['illenium-appearance']:getPedHeadBlend(ped)
print(headBlend.shapeFirst)
```
getPedFaceFeatures(ped) [#getpedfacefeaturesped]
Retrieves the face feature data (facial traits) for a pedestrian.
```lua
---@param ped number Entity ID
---@return table Face features
local faceFeatures = exports['illenium-appearance']:getPedFaceFeatures(ped)
print(faceFeatures['noseWidth'])
```
getPedHeadOverlays(ped) [#getpedheadoverlaysped]
Retrieves the head overlays (makeup, scars, etc.) for a pedestrian.
```lua
---@param ped number Entity ID
---@return table Head overlays
local headOverlays = exports['illenium-appearance']:getPedHeadOverlays(ped)
print(headOverlays['blush'].opacity)
```
getPedHair(ped) [#getpedhairped]
Retrieves the hair data for a pedestrian, including style and color.
```lua
---@param ped number Entity ID
---@return table Hair data
local hair = exports['illenium-appearance']:getPedHair(ped)
print(hair.style)
```
getPedAppearance(ped) [#getpedappearanceped]
Retrieves the full appearance object for a pedestrian.
```lua
---@param ped number Entity ID
---@return table Full appearance
local appearance = exports['illenium-appearance']:getPedAppearance(ped)
print(appearance.model)
```
setPlayerModel(model) [#setplayermodelmodel]
Sets the player’s model to the specified model.
```lua
---@param model string|number Model name or hash
---@return number Entity ID
local success = exports['illenium-appearance']:setPlayerModel("mp_f_freemode_01")
print(success)
```
setPedHeadBlend(ped, headBlend) [#setpedheadblendped-headblend]
Sets the head blend data (shape, skin mix, etc.) for a pedestrian.
```lua
---@param ped number Entity ID
---@param headBlend table Head blend data
exports['illenium-appearance']:setPedHeadBlend(ped, {
shapeFirst = 0, shapeSecond = 1, shapeThird = 2,
skinFirst = 0, skinSecond = 1, skinThird = 2,
shapeMix = 0.5, skinMix = 0.5, thirdMix = 0.5
})
```
setPedFaceFeatures(ped, faceFeatures) [#setpedfacefeaturesped-facefeatures]
Sets the face features for a pedestrian.
```lua
---@param ped number Entity ID
---@param faceFeatures table Face features data
exports['illenium-appearance']:setPedFaceFeatures(ped, {
noseWidth = 0.8, cheekboneWidth = 0.5, chinLength = 0.7
})
```
setPedHeadOverlays(ped, headOverlays) [#setpedheadoverlaysped-headoverlays]
Sets the head overlays (makeup, scars, etc.) for a pedestrian.
```lua
---@param ped number Entity ID
---@param headOverlays table Head overlays data
exports['illenium-appearance']:setPedHeadOverlays(ped, {
blush = {opacity = 0.5, color = 1},
freckles = {opacity = 0.3, color = 2}
})
```
setPedHair(ped, hair) [#setpedhairped-hair]
Sets the hair data for a pedestrian, including style and color.
```lua
---@param ped number Entity ID
---@param hair table Hair data
exports['illenium-appearance']:setPedHair(ped, {
style = 1, color = 0, highlight = 1, texture = 0
})
```
setPedAppearance(ped, appearance) [#setpedappearanceped-appearance]
Sets the full appearance for a pedestrian, including model, head blend, and features.
```lua
---@param ped number Entity ID
---@param appearance table Full appearance data
exports['illenium-appearance']:setPedAppearance(ped, {
model = "mp_f_freemode_01",
headBlend = {
shapeFirst = 0, shapeSecond = 1, shapeThird = 2,
skinFirst = 0, skinSecond = 1, skinThird = 2,
shapeMix = 0.5, skinMix = 0.5, thirdMix = 0.5
},
faceFeatures = {
noseWidth = 0.8,
cheekboneWidth = 0.5,
chinLength = 0.7
}
})
```
# Illenium Appearance Rework & Redesign
Information [#information]
* **Frameworks:** ESX, QB/Qbox, Overextended
* **Tech Stack:** Svelte 5, Tailwind CSS, LUA, Vite
* **Escrow:** 95% open-source; 3 files escrowed (1 new, 2 rewritten). Access logic on request.
Features [#features]
* Rebuilt in Svelte 5
* Redesigned, modern UI
* Backend refinements for stability & scalability
* Ability to export/import appearances to other servers.
* Ability to export/import appearances to Menyoo.
* Auto detection for onx-faces.
Preview [#preview]
# Config
functions.lua [#functionslua]
```lua title="functions.lua"
return {
isSeatbeltOn = function(ped, currentVehicle) -- Repalce this with your own seatbelt logic if your not using the built in seatbelt logic.
return false
end,
getVehicleFuel = function(currentVehicle) -- Replace this with your own logic to grab the fuel level of the vehicle.
return 75 -- For ox_fuel Entity(currentVehicle).state.fuel
end,
}
```
shared.lua [#sharedlua]
```lua title="shared.lua"
return {
debug = false,
useBuiltInSeatbeltLogic = true, -- Cannot be used with useBuiltInFrameworkSeatbeltLogic, Whether to enable the custom seatbelt logic in this script, or use your own one, keep in mind that if you decide to use your own seatbelt logic, you will need to edit the source code to tailor your needs.
useBuiltInFrameworkSeatbeltLogic = false, -- Currently only supports ESX, QB, Qbox, please disable the useBuiltInSeatbeltLogic if you wanna use this, as it supports your current framework seatbelt logic, or you can just use a completely custom one by disabling both seatbelt related bools and modifying your shared/functions.lua
framework = "none", -- none, esx, qb, qbox, ox, custom. If you wanna enable the hunger/thirst/stress stats you need to use a framework, or you can edit the source code to tailor your own custom framework
speedometer_unit = "mph", -- MPH or KMH
defaultSettings = {
colors = {
heart = "#b22530",
shield = "#004ADE",
apple = "#E68800",
water = "#00D0D0",
brain = "#792AEC",
},
},
}
```
# Exports
Clientside Exports [#clientside-exports]
toggleAppFrame(state) [#toggleappframestate]
Export to toggle the app frame, essentially toggling the whole HUD on/off.
```lua
---@param state boolean
exports["vipex-hudt"]:toggleAppFrame(state)
```
# Introduction
Information [#information]
* **Tech Stack:** Svelte 5, Tailwind, Vite, LUA
* **Dependencies**: None
* **Framework Compatibility**: ESX, QB/Qbox, OX, ND, Custom & Standalone
Features [#features]
* Modern, minimal UI
* Customizable via an in-game settings panel
* Lightweight and performance-friendly
* Scales to all resolutions, aspect ratios, and safe zones
* Built-in seatbelt logic
Preview [#preview]
# Installation
Download the Package [#download-the-package]
Download from the Cfx.re Portal upon purchasing - [https://portal.cfx.re/](https://portal.cfx.re/).
Add to your server config: [#add-to-your-server-config]
Add `ensure vipex-hudt` to your `server.cfg`.
```
ensure vipex-hudt
```
Extract the file & place it. [#extract-the-file--place-it]
Extract the downloaded file and place it inside of your server's resources folder.
Configuration [#configuration]
Edit the `functions.lua` or `shared.lua` to your liking.
Restart [#restart]
Restart your server and the heads-up display should be present.
# Config
functions.lua [#functionslua]
```lua title="functions.lua"
return {
---Grabs the fuel level of the current vehicle.
---@param entVehicleId number
---@return number
getFuel = function(entVehicleId)
assert(entVehicleId, "[getFuel] invalid arg passed.")
-- Logic Here.
return 75
end
}
```
shared.lua [#sharedlua]
```lua title="shared.lua"
return {
language = "en", -- You have to setup your own locale in `locales/**` and make sure the file name matches this string,
framework = "none", -- esx, qb, qbox, ox, nd, custom, none
disableAutoDetection = false, -- Set to true if you wanna disable the auto framework & fuel resource detection.
displayMinimapOnFoot = false, -- This could also be an option in the settings panel, although i'm unsure if people would want that since some servers want it forced off.
hiddenUIComponents = {
3, -- Cash
6, -- Vehicle Name
7, -- Area Name
8, -- Vehicle Class
9 -- Street Name
},
useMapCustomZoomLevels = false, -- Enable if you wanna use your own custom zoom levels, the ones defined in `mapZoomLevels`
mapZoomLevels = {
{
index = 1,
zoomScale = 2.7999999523163,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 2,
zoomScale = 8.0,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 3,
zoomScale = 11.0,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 4,
zoomScale = 16.0,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 5,
zoomScale = 55.0,
zoomSpeed = 0.0,
scrollSpeed = 0.10000000149012,
tilesX = 2.0,
tilesY = 1.0
},
},
seatbeltEjectionValues = {
ejectVelocity = (1 / 2.236936),
unknownEjectVelocity = (2 / 2.236936),
unknownModifier = 17.0,
minDamage = 0.0,
}
}
```
# Exports
Clientside Exports [#clientside-exports]
toggleHud(state) [#togglehudstate]
Export to toggle the app frame, essentially toggling the whole HUD on/off.
```lua
---@param state boolean
exports["vipex-hudw"]:toggleHud(state)
```
# Introduction
Information [#information]
* **Tech Stack:** Preact/Tailwind CSS, and back-end is built using LUA.
* **Dependencies**: None
* **Framework Compatibility**: ESX, QB/Qbox, OX, ND, Custom & Standalone
Features [#features]
* Settings page to customize the colors of most status icons.
* Clean, minimal design.
* Organized codebase with good performance.
Preview [#preview]
# Installation
Download the Package [#download-the-package]
Download from the Cfx.re Portal upon purchasing - [https://portal.cfx.re/](https://portal.cfx.re/).
Add to your server config: [#add-to-your-server-config]
Add `ensure vipex_hudt` to your `server.cfg`.
```
ensure vipex_hudt
```
Extract the file & place it. [#extract-the-file--place-it]
Extract the downloaded file and place it inside of your server's resources folder.
Configuration [#configuration]
Edit the `functions.lua` or `shared.lua` to your liking.
Restart [#restart]
Restart your server and the heads-up display should be present.
# Config
functions.lua [#functionslua]
```lua title="functions.lua"
return {
---Grabs the fuel level of the current vehicle.
---@param entVehicleId number
---@return number
getFuel = function(entVehicleId)
assert(entVehicleId, "[getFuel] invalid arg passed.")
-- Logic Here.
return 75
end,
---Returns the proximity level of the player, 0-100 (Auto detected if auto detection isn't disabled or if your using a resource that isn't automatically detected.)
---@param player_id string|string
---@return number|nil
getPlayerProximityLevel = function(player_id)
-- Logic Here
return nil
end
}
```
shared.lua [#sharedlua]
```lua title="shared.lua"
---@alias IFrameworkTypes "qb" | "esx" | "ox" | "qbox" | "nd" | "custom" | "none"
---@alias THudConditionType "always_visible" | "always_hidden" | "visible_under_condition"
---@class MapDataZoomLevel
---@field index integer
---@field zoomScale integer
---@field zoomSpeed integer
---@field scrollSpeed integer
---@field tilesX integer
---@field tilesY integer
---@class IHudColors
---@field health string
---@field armor string
---@field hunger string
---@field thirst string
---@field stress string
---@field stamina string
---@field minimapBorder string
---@class IHudVisibility
---@field health THudConditionType
---@field armor THudConditionType
---@field hunger THudConditionType
---@field thirst THudConditionType
---@field stress THudConditionType
---@field stamina THudConditionType
---@class IHudConditions
---@field health? number
---@field armor? number
---@field hunger? number
---@field thirst? number
---@field stress? number
---@field stamina? number
---@class IHudSettings
---@field rectangularMap boolean
---@field theme "modern" | "classic"
---@field iconStyle "square" | "hexagon" | "star" | "circle" | "no_shape"
---@field useImperialUnits boolean
---@field displayMinimapOnFoot boolean
---@field borderedMinimap boolean
---@field modernizedCompass boolean
---@field hideCompass boolean
---@field colors IHudColors
---@field visibility IHudVisibility
---@field conditions IHudConditions
---@field cinematicMode boolean
---@field statusOverCinematicMode boolean
---@field blindfoldedDisplay boolean
---@field hideGearCounter boolean
---@field anchorControl boolean
---@field hoverControl boolean
---@field statusAlerts boolean
---@class IDisabledIcons
---@field micProximityIndicator boolean
---@class IConfig
---@field framework IFrameworkTypes
---@field disableAutoDetection boolean
---@field forceDisableStressDisplay boolean
---@field language string
---@field hiddenUIComponents integer[]
---@field defaultHudSettings IHudSettings
---@field seatbeltEjectionValues {ejectVelocity: integer, unknownEjectVelocity: integer, unknownModifier: integer, minDamage: integer}
---@field disabledIcons IDisabledIcons
---@field useMapCustomZoomLevels boolean?
---@field mapZoomLevels MapDataZoomLevel[]
---@type IConfig
return {
language = "en", -- You have to setup your own locale in `locales/**` and make sure the file name matches this string,
framework = "none", -- esx, qb, qbox, ox, nd, custom, none
disableAutoDetection = false, -- Set to true if you wanna disable the auto framework & fuel resource detection.
forceDisableStressDisplay = false, -- Stress is only displayed if the logic is there for your framework of choice, but you can fully disable it.
forceDisableStaminaDisplay = false, -- Optionally turn off the stamina display.
hiddenUIComponents = {
3, -- Cash
6, -- Vehicle Name
7, -- Area Name
8, -- Vehicle Class
9 -- Street Name
},
seatbeltEjectionValues = {
ejectVelocity = (1 / 2.236936),
unknownEjectVelocity = (2 / 2.236936),
unknownModifier = 17.0,
minDamage = 0.0,
},
disabledIcons = {
micProximityIndicator = true
},
useMapCustomZoomLevels = false, -- Enable if you wanna use your own custom zoom levels, the ones defined in `mapZoomLevels`
mapZoomLevels = {
{
index = 1,
zoomScale = 2.7999999523163,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 2,
zoomScale = 8.0,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 3,
zoomScale = 11.0,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 4,
zoomScale = 16.0,
zoomSpeed = 0.89999997615814,
scrollSpeed = 0.079999998211861,
tilesX = 0.0,
tilesY = 0.0
},
{
index = 5,
zoomScale = 55.0,
zoomSpeed = 0.0,
scrollSpeed = 0.10000000149012,
tilesX = 2.0,
tilesY = 1.0
},
},
defaultHudSettings = {
rectangularMap = true,
theme = "modern",
iconStyle = "square",
useImperialUnits = true,
borderedMinimap = true,
modernizedCompass = false,
displayMinimapOnFoot = false,
hideGearCounter = false,
hideCompass = false,
colors = {
health = "#11f601",
armor = "#2980B9",
hunger = "#ffba00",
thirst = "#b4fefe",
stress = "#c2b4fb",
stamina = "#00e676",
mic = "#808080",
minimapBorder = "#ffffff",
},
visibility = {
health = "always_visible",
armor = "always_visible",
hunger = "always_visible",
thirst = "always_visible",
stress = "always_visible",
mic = "always_visible",
stamina = "visible_under_condition",
},
conditions = {
stamina = 90 -- Visible only when under 100.
},
cinematicMode = false,
statusOverCinematicMode = false,
blindfoldedDisplay = false,
anchorControl = true,
hoverControl = true,
statusAlerts = true,
}
}
```
# Exports
Clientside Exports [#clientside-exports]
toggleAppFrame(bool) [#toggleappframebool]
Export to toggle the app frame, essentially toggling the whole HUD on/off.
```lua
---@param state boolean
exports.vipex_hudls:toggleAppFrame(state)
```
# Introduction
Information [#information]
* **Tech Stack:** Svelte 5, Tailwind, Vite, LUA
* **Dependencies**: None
* **Framework Compatibility**: ESX, QB/Qbox, OX, ND, Custom & Standalone
Features [#features]
* Modern, adaptive UI.
* Lightweight and performance-friendly.
* Scales across resolutions, aspect ratios, and safe zones.
* Built-in seatbelt logic.
* In-game settings panel for customization.
* Locale support for translation into any language.
Preview [#preview]
# Installation
Download the Package [#download-the-package]
Download from the Cfx.re Portal upon purchasing - [https://portal.cfx.re/](https://portal.cfx.re/).
Add to your server config: [#add-to-your-server-config]
Add `ensure vipex_hudls` to your `server.cfg`.
```
ensure vipex_hudls
```
Extract the file & place it. [#extract-the-file--place-it]
Extract the downloaded file and place it inside of your server's resources folder.
Configuration [#configuration]
Edit the `functions.lua` or `shared.lua` to your liking.
Restart [#restart]
Restart your server and the heads-up display should be present.
# Admin Tools
mdt_setadmin [#mdt_setadmin]
Promotes a player to department admin from the **server console**. Handles missing departments, missing ranks, inactive staff, and offline players without any manual SQL.
```
mdt_setadmin
```
| Parameter | Description |
| --------- | ------------------------------------------------------------------------------------------------------ |
| `target` | An **online player's server id** (e.g. `1`) or a framework identifier (e.g. citizen ID, license, etc.) |
| `job` | Job name matching a key in your `departments` config (e.g. `police`, `bcso`, `ambulance`) |
A purely numeric target is treated as a server id and resolved to that player's identifier; they must be online with a character loaded. Anything else is treated as a raw identifier, which also works for offline players.
This command only works from the **server console** (source 0). It cannot be run by players in-game.
What it does [#what-it-does]
1. **Resolves the department**: looks up the job in the database. If it doesn't exist yet but is defined in your config, the department is auto-created (including its default admin rank, templates, and licenses).
2. **Finds or creates an admin rank**: searches for a rank with the wildcard (`*`) permission. If none exists, creates an "Admin" rank at the highest priority.
3. **Resolves the staff record**: if the player already has a staff record that is fired, suspended, on leave, or inactive, it reactivates them. If no staff record exists, it creates one (using the player's real name if they're online, or a placeholder "Admin Recovery" if offline).
4. **Promotes to admin**: sets the staff member's rank to the admin rank.
5. **Syncs live state**: if the player is online, updates their rank via state bags immediately. Invalidates all rank and permission caches.
Examples [#examples]
```bash title="Server Console"
mdt_setadmin 1 police
mdt_setadmin license:abc123 police
mdt_setadmin AB12CD34 bcso
```
Edge cases [#edge-cases]
| Scenario | Behavior |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Numeric target with no matching online player | Prints error, no changes made |
| Job not in config or database | Prints error, no changes made |
| Job in config but not in database | Auto-creates department with defaults |
| No rank with `*` permission exists | Creates "Admin" rank at top priority |
| Staff record doesn't exist + player offline | Creates staff with placeholder name "Admin Recovery" |
| Staff record doesn't exist + player online | Creates staff with player's real character name |
| Staff is fired / suspended / on leave / inactive | Reactivates to "active" then promotes |
| Player is online | Live rank sync via state bags |
| Player is offline | Database updated, sync applies on next join |
| [Grade sync](/paid-resources/mdt/config#grade-sync) is enabled | Wildcard ranks are exempt from inbound sync, so the grant is not demoted on login or grade change |
***
mdt_runmigration [#mdt_runmigration]
Re-runs the full database migration pipeline from the **server console**. Useful for recovery scenarios or after manual database changes.
```
mdt_runmigration
```
This command only works from the **server console** (source 0). It cannot be run by players in-game.
What it does [#what-it-does-1]
1. **Schema transaction**: runs all `CREATE TABLE IF NOT EXISTS` statements for every core table, preserving existing data.
2. **Planner queries**: executes post-schema changes: column additions (`ADD COLUMN IF NOT EXISTS`), index creation, ENUM modifications, and framework-specific generated columns.
3. **Seed**: re-runs the default charges seed (`INSERT IGNORE`), adding any missing charge entries without duplicating existing ones.
4. **Updates KVP**: sets the migration version key so the next server boot knows the migration is current.
All steps are idempotent. Running this on a healthy database changes nothing.
When to use [#when-to-use]
| Scenario | Why |
| ------------------------------------------------- | -------------------------------------------------------- |
| Tables were accidentally dropped or corrupted | Recreates missing tables without affecting existing ones |
| Updated to a new version and migration didn't run | Forces the full pipeline regardless of KVP state |
| Manual schema changes caused inconsistencies | Planner re-applies column/index fixes |
***
mdt_fixweaponmodels [#mdt_fixweaponmodels]
Repairs existing weapon rows where the `model` column stores a human-readable **label** (e.g. `AP Pistol`) instead of the weapon **hash name** (e.g. `weapon_appistol`). Older inventory bridge hooks wrote labels by mistake, which prevented the MDT from resolving weapon images and display names from `data/weapons.json`.
```
mdt_fixweaponmodels
```
This command only works from the **server console** (source 0). It cannot be run by players in-game.
What it does [#what-it-does-2]
1. **Loads `data/weapons.json`**, the canonical map of hash name to label shipped with the resource.
2. **Builds a reverse lookup** (label to hash) so rows whose `model` is a known label can be remapped.
3. **Scans `vx_mdt_weapons`** for every row with a non-empty `model`.
4. **Classifies each row:**
* **Already valid**: `model` is a known hash name (case-insensitive). Skipped.
* **Remappable**: `model` matches a known label. Queued for update to the correct hash.
* **Unmatched**: `model` matches neither. Left untouched and listed at the end of the run.
5. **Runs one transaction**: all remaps are applied in a single `MySQL.transaction.await` call.
Example output [#example-output]
```text title="Server Console"
[mdt_fixweaponmodels] Scanned 184 rows: 12 already valid, 169 to remap, 3 unmatched
[mdt_fixweaponmodels] Updated 169 weapon row(s).
[mdt_fixweaponmodels] 3 unmatched row(s) left untouched:
- XJ829S (model: Custom Relic)
- QQ3341 (model: RP Hammer)
- MM22C9 (model: Lost MC Blade)
```
When to use [#when-to-use-1]
| Scenario | Why |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Upgrading from a pre-fix version where the ox/qb/jaksam/origen/tgiann inventory bridge wrote labels | Remaps stale rows so weapon images and labels resolve correctly in the MDT |
| Weapon records show "AP Pistol" instead of a proper weapon icon | The `model` column holds a label; this command converts it |
| Custom weapons are listed as unmatched | Expected. Unmatched rows are left untouched. Add entries to `data/weapons.json` (hash → label) and re-run to pick them up |
The command is idempotent: running it on a healthy database does nothing. Rows whose `model` is already a valid hash (or lowercased hash) are skipped.
***
mdt_refresh_vehicle_cache [#mdt_refresh_vehicle_cache]
Forces a refresh of the vehicle label cache (`data/vehicles.json`) from a connected client. The cache maps vehicle hashes to display labels and is used to resolve vehicle model names throughout the MDT without needing a connected client on every lookup.
```
mdt_refresh_vehicle_cache
```
This command only works from the **server console** (source 0). At least one client must be connected; the server picks the first online player and enumerates the full vehicle table from their GTA client.
What it does [#what-it-does-3]
1. **Guards against re-entry**: aborts if a refresh is already in progress (boot, manual, or another concurrent call).
2. **Picks the first connected client**: issues a server-to-client callback that enumerates every loaded vehicle model and its `GetLabelText`-resolved display name.
3. **Applies sanity threshold**: rejects results below `vehicleCache.minExpectedEntries` (default `100`) to guard against partial client-side failures.
4. **Atomic swap + persist**: rebinds `GLOBAL.STORAGE.VEHICLES` (never mutates in place) and writes `data/vehicles.json` with schema version, count, timestamp, and `sv_enforceGameBuild` value.
When to use [#when-to-use-2]
| Scenario | Why |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| GTA game build changed after a DLC update | New vehicles won't resolve until the cache is refreshed |
| `data/vehicles.json` was deleted or corrupted | Auto-refresh will eventually pick it up on next player connect, but manual refresh is faster |
| `vehicleCache.ttlSeconds = 0` (auto-refresh disabled) | Manual refresh is the only way to update the cache |
| Boot refresh gave up after 5 failed attempts | The warning in the console instructs to run this command to retry |
The cache is populated automatically on the first player connect after a resource start/restart if it's empty or stale. This command is only needed for recovery scenarios or when auto-refresh is disabled.
# Bridges
Bridges are optional integrations that extend MDT functionality. They are **auto-detected**, with no manual configuration needed. Just ensure the supported resource is started before vx\_mdt.
Supported Bridges [#supported-bridges]
Framework [#framework]
| Resource | Notes |
| ------------- | ------ |
| `es_extended` | ESX |
| `qb-core` | QBCore |
| `qbx_core` | Qbox |
Inventory [#inventory]
| Resource |
| ------------------ |
| `ox_inventory` |
| `qb-inventory` |
| `origen_inventory` |
| `tgiann-inventory` |
| `jaksam_inventory` |
Billing [#billing]
| Resource |
| ----------------- |
| `esx_billing` |
| `qbx_core` |
| `tgg-banking` |
| `snipe-banking` |
| `kartik-banking` |
| `fd_banking` |
| `wasabi_billing` |
| `okokBilling` |
| `Renewed-Banking` |
| `RxBilling` |
Prison [#prison]
| Resource |
| ---------------- |
| `qbx_police` |
| `rcore_prison` |
| `tk_jail` |
| `stevo_prison` |
| `xt-prison` |
| `dhs-prisonsim` |
| `pickle_prisons` |
| `r_prison` |
| `p_policejob` |
| `dynyx_prison` |
Garages [#garages]
| Resource |
| -------------------- |
| `jg-advancedgarages` |
| `qbx_core` |
| `es_extended` |
Properties [#properties]
| Resource |
| ------------------ |
| `qbx_properties` |
| `nolag_properties` |
| `vms_housing` |
| `rtx_housing` |
Camera [#camera]
| Resource | Notes |
| ------------------ | ----------------------------------------- |
| `fmsdk` | FiveManage SDK (handles capture + upload) |
| `screencapture` | Standalone capture, uploads via API |
| `screenshot-basic` | CFX built-in capture, uploads via API |
See the [Camera](/paid-resources/mdt/camera) page for setup instructions.
Voice [#voice]
| Resource |
| ----------- |
| `pma-voice` |
Radar [#radar]
| Resource |
| ------------- |
| `cd_radar` |
| `fd_dispatch` |
License [#license]
| Resource | Notes |
| ------------- | ---------------------------------------------------------------------- |
| `qbx_core` | Reads/writes `metadata.licences` via player object (dot-notation) |
| `qb-core` | Reads/writes `metadata.licences` via player object (read-modify-write) |
| `esx_license` | Uses `user_licenses` table |
Forensics [#forensics]
| Resource | Notes |
| -------------- | ---------------------------------- |
| `r14-evidence` | Reads biometrics from r14-evidence |
| `fw_metadata` | Framework metadata fallback (ESX) |
| `qb_metadata` | QBCore metadata |
| `qbx_metadata` | QBox metadata |
Interaction [#interaction]
| Resource |
| ----------- |
| `ox_target` |
Dispatch Compatibility [#dispatch-compatibility]
Set `dispatch.dispatchCompat` to one of the following resource names to intercept its events.
| Resource |
| ---------------- |
| `cd_dispatch` |
| `cd_dispatch3d` |
| `ps-dispatch` |
| `rcore_dispatch` |
| `fd_dispatch` |
# Camera
The camera system lets officers capture in-game screenshots as evidence, which can be attached to profiles, vehicles, incidents, reports, weapons, and properties. It requires a media provider ([FiveManage](https://fivemanage.com)) and one of the supported capture resources.
Setup [#setup]
Get a FiveManage API Key [#get-a-fivemanage-api-key]
Sign up at [fivemanage.com](https://fivemanage.com) and generate an API key.
Add the Convar to server.cfg [#add-the-convar-to-servercfg]
```
set vx_mdt:media "your-api-key-here"
```
Install a Capture Resource [#install-a-capture-resource]
Install one of the supported capture resources below and ensure it starts **before** vx\_mdt in your `server.cfg`.
Done [#done]
The bridge is auto-detected; no additional configuration needed.
Supported Bridges [#supported-bridges]
| Resource | Description |
| ------------------ | ---------------------------------------------------------------- |
| `fmsdk` | FiveManage SDK. Handles capture and upload internally. |
| `screencapture` | Standalone capture resource. Uploads to FiveManage via API. |
| `screenshot-basic` | CFX built-in screenshot resource. Uploads to FiveManage via API. |
Only one capture resource is needed. If multiple are installed, auto-detection picks the first available (priority: fmsdk → screencapture → screenshot-basic). You can override this with `bridges.camera` in config.
Client Configuration [#client-configuration]
Customize camera behavior in `config/client/camera.lua`.
| Field | Type | Default | Description |
| -------------------------------- | --------------- | --------------------------------------- | --------------------------------------------------------------- |
| `enabled` | boolean | `true` | Master toggle for camera prop/animation |
| `anim.dict` | string | `"amb@world_human_paparazzi@male@base"` | Animation dictionary |
| `anim.clip` | string | `"base"` | Animation clip name |
| `anim.blend_in` | number | `2.0` | Blend in speed (seconds) |
| `anim.blend_out` | number | `2.0` | Blend out speed (seconds) |
| `anim.duration` | number | `-1` | Animation duration in ms (`-1` = loop until stopped) |
| `anim.flag` | number | `49` | Animation flags (`49` = upper body + loop) |
| `prop.model` | string | `"prop_pap_camera_01"` | Prop model name |
| `prop.bone` | number | `28422` | Bone ID to attach prop to (`28422` = right hand) |
| `prop.offset` | vec3 | `0.0, 0.0, 0.0` | Position offset for the prop |
| `prop.rotation` | vec3 | `0.0, 0.0, 0.0` | Rotation offset for the prop |
| `scripted_camera.fov_default` | number | `60.0` | Default field of view |
| `scripted_camera.fov_min` | number | `20.0` | Maximum zoom in |
| `scripted_camera.fov_max` | number | `90.0` | Maximum zoom out |
| `scripted_camera.fov_step` | number | `3.0` | FOV change per scroll step |
| `scripted_camera.sensitivity_x` | number | `4.0` | Horizontal look sensitivity |
| `scripted_camera.sensitivity_y` | number | `4.0` | Vertical look sensitivity |
| `scripted_camera.pitch_min` | number | `-89.0` | Minimum vertical look angle (degrees) |
| `scripted_camera.pitch_max` | number | `89.0` | Maximum vertical look angle (degrees) |
| `scripted_camera.head_bone` | number | `31086` | Bone ID the camera attaches to (`31086` = head) |
| `scripted_camera.forward_offset` | number | `0.6` | Camera distance from the head bone |
| `hideHud` / `showHud` | function \| nil | `nil` | Callbacks to hide/show your HUD resource while camera is active |
Controls [#controls]
| Action | Key |
| ----------- | ---------------- |
| Look around | Mouse |
| Capture | Left Click (LMB) |
| Zoom in/out | Scroll Wheel |
| Exit | ESC |
# Configuration
All configuration lives in `config/shared/global.lua`.
Full Config [#full-config]
```lua title="config/shared/global.lua"
return {
exportAllowlist = {},
bridges = {
debug = false,
framework = "",
license = "",
interaction = "",
inventory = "",
properties = "",
billing = "",
prison = "",
garages = "",
radar = "",
camera = "",
voice = "",
},
forensics = {
providerManaged = false,
providers = { "r14-evidence", "framework" },
},
evidence = {
allowStashIdEdit = true,
},
vehicles = {
autoLinkOwner = true,
substringSearch = false,
},
gradeSync = {
enabled = false,
syncOnLogin = true,
syncLive = true,
},
vehicleCache = {
ttlSeconds = 5 * 24 * 60 * 60,
minExpectedEntries = 100,
},
weapons = {
autoRegister = true,
autoLinkOwner = true,
},
bolos = {
autoFlagRadar = true,
syncOnStartup = false,
},
terminal = {
command = true,
keybind = true,
defaultKey = "K",
},
dispatch = {
globalOverlay = true,
overlayKeybind = "F5",
newCallNotifications = true,
panicButton = true,
panicKeybind = "F9",
panicCooldown = 30,
notificationDuration = 8000,
exportAllowlist = {},
maxCallAge = 1800,
maxCalls = 100,
dispatchCompat = "",
dispatchCompatExports = true,
autoExpireAge = 86400,
autoExpireCheckInterval = 60,
autoDetection = {
enabled = true,
events = {
gunshot = {
enabled = true,
cooldown = 15,
priority = 2,
weaponBlacklist = {
"weapon_stungun",
"weapon_flaregun",
"weapon_flare",
"weapon_snowball",
"weapon_ball",
"weapon_firework",
"weapon_fireextinguisher",
"weapon_petrolcan",
"weapon_hazardcan",
},
},
fight = { enabled = true, cooldown = 30, priority = 3 },
carjacking = { enabled = true, cooldown = 20, priority = 1 },
vehicleTheft = { enabled = true, cooldown = 30, priority = 2 },
explosion = { enabled = true, cooldown = 20, priority = 1 },
speeding = { enabled = true, cooldown = 45, speedThreshold = 130, priority = 3 },
},
},
fields = {
plate = true,
vehicleColor = true,
speed = true,
heading = true,
suspect = true,
owner = true,
},
blips = {
enabled = true,
defaultSprite = 161,
defaultColour = 1,
defaultScale = 0.8,
defaultOffset = 0,
defaultRadius = 0,
defaultRadiusColour = nil,
defaultRadiusAlpha = 128,
},
},
radio = {
enabled = true,
canUseRadio = function(srcNetId)
return true
end,
},
jobMasks = {
-- ["scammer"] = "Unemployed",
},
departments = {
["police"] = {
type = "law",
title = "Los Santos Police Department",
shortTitle = "LSPD",
symbol = "#",
accentColor = "#3b82f6",
allowAccessOffDuty = false,
defaultRadioFrequency = 1,
defaultTags = GLOBAL.DEFAULTS.TAGS,
defaultLicenses = GLOBAL.DEFAULTS.LICENSES,
defaultCertificates = GLOBAL.DEFAULTS.CERTIFICATES,
defaultTemplates = GLOBAL.DEFAULTS.TEMPLATES,
defaultUnits = GLOBAL.DEFAULTS.UNITS,
},
["bcso"] = {
type = "law",
title = "Blaine County Sheriff's Office",
shortTitle = "BCSO",
symbol = "$",
accentColor = "#F97316",
allowAccessOffDuty = false,
defaultRadioFrequency = 2,
defaultTags = GLOBAL.DEFAULTS.TAGS,
defaultLicenses = GLOBAL.DEFAULTS.LICENSES,
defaultCertificates = GLOBAL.DEFAULTS.CERTIFICATES,
defaultTemplates = GLOBAL.DEFAULTS.TEMPLATES,
defaultUnits = GLOBAL.DEFAULTS.UNITS,
},
["sahp"] = {
type = "law",
title = "San Andreas Highway Patrol",
shortTitle = "SAHP",
symbol = "%",
accentColor = "#f97316",
allowAccessOffDuty = false,
defaultRadioFrequency = 3,
defaultTags = GLOBAL.DEFAULTS.TAGS,
defaultLicenses = GLOBAL.DEFAULTS.LICENSES,
defaultCertificates = GLOBAL.DEFAULTS.CERTIFICATES,
defaultTemplates = GLOBAL.DEFAULTS.TEMPLATES,
defaultUnits = GLOBAL.DEFAULTS.UNITS,
},
["ambulance"] = {
type = "ems",
title = "Los Santos Medical Services",
shortTitle = "LSMS",
symbol = "+",
accentColor = "#22c55e",
allowAccessOffDuty = false,
defaultRadioFrequency = 4,
defaultTags = GLOBAL.DEFAULTS.TAGS_EMS,
defaultLicenses = GLOBAL.DEFAULTS.LICENSES,
defaultCertificates = GLOBAL.DEFAULTS.CERTIFICATES,
defaultTemplates = GLOBAL.DEFAULTS.TEMPLATES_EMS,
defaultUnits = GLOBAL.DEFAULTS.UNITS_EMS,
defaultMedicalConditionTypes = GLOBAL.DEFAULTS.MEDICAL_CONDITION_TYPES,
},
["judge"] = {
type = "judge",
title = "Department of Justice",
shortTitle = "DOJ",
symbol = "§",
accentColor = "#a855f7",
allowAccessOffDuty = false,
defaultRadioFrequency = 5,
defaultTags = GLOBAL.DEFAULTS.TAGS_JUDICIAL,
defaultLicenses = GLOBAL.DEFAULTS.LICENSES,
defaultCertificates = GLOBAL.DEFAULTS.CERTIFICATES,
defaultTemplates = GLOBAL.DEFAULTS.TEMPLATES_JUDICIAL,
defaultUnits = GLOBAL.DEFAULTS.UNITS_JUDICIAL,
},
},
}
```
Bridges [#bridges]
| Field | Type | Default | Description |
| ------------- | ------- | ------- | -------------------------------------------------------------------------------- |
| `debug` | boolean | `false` | Print resolver diagnostics for every bridge candidate during startup |
| `framework` | string | `""` | Force a specific framework bridge, or `"none"` to disable. Empty = auto-detect |
| `license` | string | `""` | Force a specific license bridge, or `"none"` to disable. Empty = auto-detect |
| `interaction` | string | `""` | Force a specific interaction bridge, or `"none"` to disable. Empty = auto-detect |
| `inventory` | string | `""` | Force a specific inventory bridge, or `"none"` to disable. Empty = auto-detect |
| `properties` | string | `""` | Force a specific properties bridge, or `"none"` to disable. Empty = auto-detect |
| `billing` | string | `""` | Force a specific billing bridge, or `"none"` to disable. Empty = auto-detect |
| `prison` | string | `""` | Force a specific prison bridge, or `"none"` to disable. Empty = auto-detect |
| `garages` | string | `""` | Force a specific garages bridge, or `"none"` to disable. Empty = auto-detect |
| `radar` | string | `""` | Force a specific radar bridge, or `"none"` to disable. Empty = auto-detect |
| `camera` | string | `""` | Force a specific camera bridge, or `"none"` to disable. Empty = auto-detect |
| `voice` | string | `""` | Force a specific voice bridge, or `"none"` to disable. Empty = auto-detect |
Bridges are auto-detected by default. You only need to set these if you want to force a specific bridge or disable a category with `"none"`. See the [Bridges](/paid-resources/mdt/bridges) page for supported resources.
Forensics [#forensics]
| Field | Type | Default | Description |
| ----------------- | --------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `providerManaged` | boolean | `false` | When `true`, fingerprint and blood type are read-only in the MDT and fetched from external providers instead |
| `providers` | string\[] | `{ "r14-evidence", "framework" }` | Ordered provider list; first provider that returns a value wins |
Available provider values:
* `"r14-evidence"`: reads from r14-evidence `ev_identifiers`
* `"framework"`: uses QB/QBox metadata fallback (aliases: `"qb"`, `"qbx"`, `"qbox"`)
ESX has no built-in metadata provider for biometrics. ESX servers should only list explicit providers like `"r14-evidence"`.
Export Allowlist [#export-allowlist]
`exportAllowlist` restricts which resources can call vx\_mdt server exports. An empty table means all resources are allowed.
```lua
exportAllowlist = { "my-resource", "another-resource" },
```
Evidence [#evidence]
| Field | Type | Default | Description |
| ------------------ | ------- | ------- | ------------------------------------------------------------ |
| `allowStashIdEdit` | boolean | `true` | Allow editing the stash ID field on evidence items in the UI |
Vehicles [#vehicles]
| Field | Type | Default | Description |
| ----------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `autoLinkOwner` | boolean | `true` | Automatically link the vehicle owner's profile when registering a vehicle |
| `substringSearch` | boolean | `false` | Match vehicle plate/VIN/model searches anywhere in the string instead of only from the start (e.g. `"JJJ"` matches plate `"BM94 JJJ"`). Disables index-accelerated prefix matching and may be slower on large databases. |
Grade Sync [#grade-sync]
Bidirectional synchronization between a player's framework job grade and their MDT rank. Off by default.
| Field | Type | Default | Description |
| ------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled` | boolean | `false` | Master toggle. When `false`, framework grades and MDT ranks stay completely independent. When `true`, framework grade ↔ MDT rank stay in sync bidirectionally. |
| `syncOnLogin` | boolean | `true` | Apply inbound sync on player load and every time the MDT terminal is opened. |
| `syncLive` | boolean | `true` | Apply inbound sync live when the framework grade changes mid-session (boss menu, `/setjob`, etc.). |
When enabled, ranks gain a **Framework Grade** field in the rank create/edit modal. Linking a grade activates these behaviours:
* **Inbound**: when a player's framework grade changes, their MDT rank is updated to match the rank linked to that grade. If the new grade has no linked rank, they fall back to the department's default (lowest priority) rank. Staff whose current rank holds the wildcard (`*`) permission are exempt from inbound sync, so admin grants (e.g. via `mdt_setadmin`) are never demoted by it.
* **Outbound**: when an admin changes someone's rank in the Roster, the player's framework grade is pushed to match the linked grade. Ranks without a `framework_grade` mapping skip the push silently.
* **Job loss**: if the framework job changes to unemployed or a non-MDT job, the staff record is set to `inactive` and the terminal is torn down. Existing rehire flow re-onboards them with the default rank if they regain the job.
A 2-second per-source loop guard prevents the outbound push from triggering its own inbound listener. The framework grade dropdown in the rank modal is hidden entirely when `enabled = false`.
Vehicle Cache [#vehicle-cache]
The vehicle label cache stores `hash → display label` pairs on disk (`data/vehicles.json`) so the MDT can resolve vehicle model labels without a connected client. The cache auto-populates on the first player connect after boot and persists across restarts.
| Field | Type | Default | Description |
| -------------------- | ------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ttlSeconds` | number | `5 * 24 * 60 * 60` | Seconds before the cache is considered stale and auto-refreshed on next player connect. Set to `0` to disable auto-refresh (manual command only). |
| `minExpectedEntries` | number | `100` | Reject refresh results below this many entries as likely client-side failures. GTA V has \~780 vehicles including DLC, so `100` is a safe floor. |
Use the `mdt_refresh_vehicle_cache` console command to trigger a manual refresh. See the [Admin Tools](/paid-resources/mdt/admin-tools) page for details.
Weapons [#weapons]
| Field | Type | Default | Description |
| --------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `autoRegister` | boolean | `true` | Automatically register weapons in the MDT when picked up/equipped via the inventory bridge. Disable to require weapons to be created manually (or via the `createWeapon` export) |
| `autoLinkOwner` | boolean | `true` | Automatically link the weapon owner's profile when registering a weapon |
BOLOs [#bolos]
| Field | Type | Default | Description |
| --------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| `autoFlagRadar` | boolean | `true` | Automatically flag/unflag vehicle plates in the radar bridge when BOLOs are created, updated, or cancelled |
| `syncOnStartup` | boolean | `false` | Re-flag all active BOLO plates in radar on resource start. Disable if your radar script persists flags in its own database |
When `autoFlagRadar` is enabled and a radar bridge is detected, vehicle plates linked to active BOLOs are automatically flagged in the radar system. Plates are unflagged when a BOLO is cancelled, expired, or deleted. If the same plate appears in multiple BOLOs, it is only unflagged when no active BOLO references it.
Terminal [#terminal]
| Field | Type | Default | Description |
| ------------ | ------- | ------- | --------------------------------------------------------------------------------------------- |
| `command` | boolean | `true` | Enable the `/mdt` chat command (set `false` to open only via keybind, item script, or export) |
| `keybind` | boolean | `true` | Enable the keybind to toggle the MDT (set `false` to use `/mdt` command only) |
| `defaultKey` | string | `"K"` | Default key to toggle the MDT |
Dispatch [#dispatch]
| Field | Type | Default | Description |
| ---------------------------------------------- | ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `globalOverlay` | boolean | `true` | Enable the global dispatch overlay panel (toggled via keybind) |
| `overlayKeybind` | string | `"F5"` | Keybind to toggle the dispatch overlay |
| `newCallNotifications` | boolean | `true` | Show popup notifications for new dispatch calls |
| `panicButton` | boolean | `true` | Enable the panic button keybind |
| `panicKeybind` | string | `"F9"` | Keybind to send a panic signal |
| `panicCooldown` | number | `30` | Seconds between panic signals |
| `notificationDuration` | number | `8000` | How long dispatch notifications stay visible (ms) |
| `exportAllowlist` | table | `{}` | Restrict which resources can create dispatch calls via exports (empty = all) |
| `maxCallAge` | number | `1800` | Seconds before inactive calls are eligible for eviction |
| `maxCalls` | number | `100` | Max active calls before oldest are evicted |
| `dispatchCompat` | string | `""` | Name of third-party dispatch resource to intercept (`"cd_dispatch"`, `"cd_dispatch3d"`, `"ps-dispatch"`, `"rcore_dispatch"`, `"fd_dispatch"`) |
| `dispatchCompatExports` | boolean | `true` | Override the third-party dispatch resource's exports so external scripts route into vx\_mdt. Disable if the third-party dispatch is still running to avoid duplicate notifications. |
| `autoExpireAge` | number | `86400` | Seconds before completed/expired calls are auto-deleted |
| `autoExpireCheckInterval` | number | `60` | How often (seconds) to check for expired calls |
| `autoDetection.enabled` | boolean | `true` | Master toggle for auto-detection of game events (shots fired, carjacking, etc.) |
| `autoDetection.events..enabled` | boolean | `true` | Enable/disable a specific event type |
| `autoDetection.events..cooldown` | number | varies | Seconds before the same event can trigger again |
| `autoDetection.events..priority` | number | varies | Dispatch call priority (1 = highest) |
| `autoDetection.events.speeding.speedThreshold` | number | `130` | Speed in km/h above which a speeding event triggers |
| `autoDetection.events.gunshot.weaponBlacklist` | string\[] | see default | Weapon hash names (keys from `data/weapons.json`) that should NOT trigger a shots-fired call (e.g. stun gun, flare gun, fireworks). Applies to both on-foot and drive-by variants |
| `autoDetection.events..suppress` | `fun(ctx) -> boolean?` | `nil` | Optional per-event callback. Return `true` to drop the event before dispatch (also skips cooldown). `ctx` carries event-specific data; see [Auto-Detection Suppression Hooks](/paid-resources/mdt/dispatch#suppression-hooks) |
| `fields.plate` | boolean | `true` | Attach vehicle plate as a structured field |
| `fields.vehicleColor` | boolean | `true` | Attach vehicle color with hex swatch |
| `fields.speed` | boolean | `true` | Attach vehicle speed (speeding events) |
| `fields.heading` | boolean | `true` | Attach compass heading |
| `fields.suspect` | boolean | `true` | Attach suspect sex description |
| `fields.owner` | boolean | `true` | Look up registered vehicle owner by plate (server-side, cached) |
| `blips.enabled` | boolean | `true` | Show GTA map blips for active dispatch calls |
| `blips.defaultSprite` | number | `161` | Default GTA blip sprite ID |
| `blips.defaultColour` | number | `1` | Default GTA blip colour ID |
| `blips.defaultScale` | number | `0.8` | Default blip scale on the map |
| `blips.defaultOffset` | number | `0` | Random offset applied to blip position (GTA world units). `0` = exact location. |
| `blips.defaultRadius` | number | `0` | Radius circle overlay size (GTA world units). `0` = no circle. |
| `blips.defaultRadiusColour` | number \| nil | `nil` | Colour for the radius circle. `nil` falls back to `defaultColour`. |
| `blips.defaultRadiusAlpha` | number | `128` | Alpha transparency for radius circle (0–255). |
Radio [#radio]
| Field | Type | Default | Description |
| ------------- | --------------- | -------------- | --------------------------------------------------------------------------------------- |
| `enabled` | boolean | `true` | Master toggle for all radio integration (auto-tuning, channel guarding) |
| `canUseRadio` | function \| nil | `returns true` | Called before auto-tuning a player. Return `true` to allow, or `false, reason` to deny. |
canUseRadio Example [#canuseradio-example]
```lua
canUseRadio = function(srcNetId)
if not sv_inventory.hasItem(srcNetId, "radio") then
return false, "Please equip a radio."
end
return true
end,
```
Job Masks [#job-masks]
Hide a player's real job from other MDT viewers by replacing it with a display label. Useful for illegal or internal jobs you don't want police to discover passively.
| Field | Type | Default | Description |
| ---------- | ----------------------- | ------- | --------------------------------------------------------------- |
| `jobMasks` | `table` | `{}` | Map of real framework job name → display label shown in the MDT |
```lua title="config/shared/global.lua"
jobMasks = {
["scammer"] = "Unemployed",
["cartel"] = "Unemployed",
["lost_mc"] = "Unemployed",
},
```
Masking is applied server-side to search results, profile views, and involved persons in records (reports, incidents, warrants, evidence, vehicles, weapons, BOLOs, properties). Grade is hidden on masked rows: a masked job renders as just the label (e.g. `Unemployed`), never `Unemployed (Boss)`.
The player's actual framework job is unchanged; only how other MDT users see them is substituted. On-duty checks, speed dial, authorization, and terminal login continue to use the real job.
Departments [#departments]
Each key in the `departments` table is a **job name** that maps to a department definition. Add or remove departments as needed.
| Field | Type | Description |
| ------------------------------ | ------------------------------- | ------------------------------------------------------------------- |
| `type` | `"law"` \| `"ems"` \| `"judge"` | Determines routing, UI layout, and available features |
| `title` | string | Full display name |
| `shortTitle` | string | Abbreviated name shown in compact UI elements |
| `symbol` | string | Single character shown in UI badges |
| `accentColor` | string | Hex color for department theming |
| `allowAccessOffDuty` | boolean | Allow opening MDT while off duty |
| `defaultRadioFrequency` | number | Radio frequency auto-assigned to officers |
| `defaultTags` | table | Tags seeded on first boot |
| `defaultLicenses` | table | Licenses seeded on first boot |
| `defaultCertificates` | table | Certificates seeded on first boot |
| `defaultTemplates` | table | Report/incident templates seeded on first boot |
| `defaultUnits` | table | Units seeded on first boot |
| `defaultMedicalConditionTypes` | table | Medical condition types seeded on first boot (EMS departments only) |
Defaults (tags, licenses, certificates, templates, units) are only seeded the first time a department is created. Changing them in config after the initial boot has no effect; manage them through the MDT UI instead.
# Dispatch
The dispatch system is a built-in subsystem of vx\_mdt with its own set of server exports, event hooks, and a compatibility layer for third-party dispatch resources.
Dispatch exports are controlled by a **separate** `dispatch.exportAllowlist` in config (independent from the core `exportAllowlist`).
Lifecycle [#lifecycle]
createDispatchCall(data) [#createdispatchcalldata]
Create a new dispatch call. When coords are provided, a map blip is automatically created for on-duty officers.
```lua
---@param data { dep_type: string, call_type: string, title: string, description?: string, location_text?: string, priority?: number, metadata?: table, coords?: vector3, radio_frequency?: number, author_name?: string, author_identifier?: string, blip?: table|false, fields?: table[] }
---@return { id: number }|{ error: string }
local result = exports.vx_mdt:createDispatchCall({
dep_type = "law",
call_type = "10-31",
title = "Robbery in Progress",
description = "Armed robbery at Fleeca Bank",
location_text = "Fleeca Bank, Route 68",
priority = 1,
coords = vector3(311.6, -278.6, 54.2),
blip = {
sprite = 161, -- GTA blip sprite (default from config)
colour = 1, -- GTA blip colour (default from config)
scale = 0.8, -- blip scale (default from config)
duration = 60, -- seconds before auto-removing (0 = until call ends)
flash = true, -- flash the blip (default: auto for P0/P1)
text = "Robbery", -- blip label (default: call title)
offset = 50, -- random offset in world units (0 = exact, default from config)
radius = 100, -- radius circle overlay in world units (0 = none, default from config)
radius_colour = 1, -- radius circle colour (default: blip colour)
radius_alpha = 128, -- radius circle transparency 0-255 (default from config)
},
fields = {
{ type = "plate", value = "ABC123" },
{ type = "vehicle", value = "Sultan", data = "ABC123" },
{ type = "color", value = "Red", data = "27" },
{ type = "speed", value = "142" },
{ type = "person", value = "John Smith", data = "ABC12345" },
{ label = "WEAPON", value = "AK-47" },
},
})
```
The `blip` parameter is optional:
* **Omit**: uses config defaults, blip appears at call coords
* **Table**: override specific blip properties (all fields optional)
* **`false`**: suppress the blip for this call entirely
The `fields` parameter adds structured data fields visible on the dispatch notification and in the MDT call detail. Each field entry can use a pre-defined `type` (with auto-populated label/icon and interactive behavior) or a custom `label`+`value` pair.
**Pre-defined types:**
| Type | Label | Behavior |
| --------- | ------- | -------------------------------------------------------------- |
| `plate` | PLATE | Clickable in MDT, opens vehicle record |
| `vehicle` | VEHICLE | Clickable if `data` contains the plate |
| `color` | COLOR | Renders a hex color swatch (set `data` to the GTA color index) |
| `person` | SUSPECT | Clickable if `data` contains the citizen identifier |
| `speed` | SPEED | Color-coded by threshold, appends "km/h" |
Custom fields use `label` + `value` directly: `{ label = "WEAPON", value = "AK-47" }`
All fields support an optional `icon` (Iconify name) and `data` (navigation target or color index).
updateDispatchCall(callId, data) [#updatedispatchcallcallid-data]
Update fields on an active dispatch call.
```lua
---@param callId number Dispatch call ID
---@param data table Fields to update
exports.vx_mdt:updateDispatchCall(callId, {
priority = 2,
description = "Suspects fleeing on foot",
})
```
completeDispatchCall(callId) [#completedispatchcallcallid]
Mark a dispatch call as completed.
```lua
---@param callId number Dispatch call ID
exports.vx_mdt:completeDispatchCall(callId)
```
cancelDispatchCall(callId) [#canceldispatchcallcallid]
Cancel a dispatch call.
```lua
---@param callId number Dispatch call ID
exports.vx_mdt:cancelDispatchCall(callId)
```
Assignment [#assignment]
attachDispatchCall(callId, source) [#attachdispatchcallcallid-source]
Attach an officer to a dispatch call.
```lua
---@param callId number Dispatch call ID
---@param source number Player server ID
exports.vx_mdt:attachDispatchCall(callId, source)
```
detachDispatchCall(callId, source) [#detachdispatchcallcallid-source]
Detach an officer from a dispatch call.
```lua
---@param callId number Dispatch call ID
---@param source number Player server ID
exports.vx_mdt:detachDispatchCall(callId, source)
```
addDispatchCallUpdate(callId, content, author) [#adddispatchcallupdatecallid-content-author]
Add a status update to a dispatch call.
```lua
---@param callId number Dispatch call ID
---@param content string Update text
---@param author string Author name
exports.vx_mdt:addDispatchCallUpdate(callId, "Suspects in custody", "Officer Smith")
```
Query [#query]
getDispatchCall(callId) [#getdispatchcallcallid]
Get a dispatch call by ID.
```lua
---@param callId number Dispatch call ID
---@return table|nil Dispatch call or nil
local call = exports.vx_mdt:getDispatchCall(callId)
```
getActiveDispatchCalls(depType?) [#getactivedispatchcallsdeptype]
Get all active dispatch calls, optionally filtered by department type.
```lua
---@param depType? string Optional: "law", "ems", or "judge"
---@return table[] Array of active dispatch calls
local calls = exports.vx_mdt:getActiveDispatchCalls("law")
```
Hooks [#hooks]
Subscribe to dispatch events for real-time integrations.
onDispatch(event, handler) [#ondispatchevent-handler]
Register a listener for dispatch events. Returns a table with the listener ID for cleanup.
```lua
---@param event "created"|"updated"|"deleted"|"attached"|"detached"|"completed"|"cancelled"
---@param handler function Callback receiving event data
---@return { id: number }|{ error: string }
local result = exports.vx_mdt:onDispatch("created", function(call)
print("New dispatch call:", call.title)
end)
local listenerId = result.id
```
offDispatch(listenerId) [#offdispatchlistenerid]
Unregister a dispatch event listener.
```lua
---@param listenerId number Listener ID from onDispatch
---@return {}|{ error: string }
exports.vx_mdt:offDispatch(listenerId)
```
Auto-Detection [#auto-detection]
Auto-detection listens for GTA game events and automatically creates dispatch calls; no third-party dispatch resource required. Enabled by default; set `dispatch.autoDetection.enabled = false` to opt out.
```lua title="config/shared/global.lua"
dispatch = {
autoDetection = {
enabled = true,
events = {
gunshot = {
enabled = true,
cooldown = 15,
priority = 2,
weaponBlacklist = {
"weapon_stungun",
"weapon_flaregun",
"weapon_flare",
"weapon_snowball",
"weapon_ball",
"weapon_firework",
"weapon_fireextinguisher",
"weapon_petrolcan",
"weapon_hazardcan",
},
},
fight = { enabled = true, cooldown = 30, priority = 3 },
carjacking = { enabled = true, cooldown = 20, priority = 1 },
vehicleTheft = { enabled = true, cooldown = 30, priority = 2 },
explosion = { enabled = true, cooldown = 20, priority = 1 },
speeding = { enabled = true, cooldown = 45, speedThreshold = 130, priority = 3 },
},
},
fields = {
plate = true,
vehicleColor = true,
speed = true,
heading = true,
suspect = true,
owner = true,
},
},
```
Structured Fields [#structured-fields]
Auto-detected calls attach structured data fields to the notification card and MDT detail view. Each field can be individually toggled via `autoDetection.fields`:
| Field | Description |
| -------------- | -------------------------------------------------------------------------------------------- |
| `plate` | Vehicle plate number |
| `vehicleColor` | Vehicle color with hex swatch (uses GTA color index) |
| `speed` | Vehicle speed in km/h (speeding events only) |
| `heading` | Compass heading (N, NE, E, SE, etc.) |
| `suspect` | Suspect sex description |
| `owner` | Registered vehicle owner name (looked up from the database by plate, cached for 120 seconds) |
When `owner` is enabled and the vehicle plate matches a registered vehicle, the owner's name is automatically resolved and displayed as a clickable link to their profile in the MDT.
Detected Events [#detected-events]
| Event | Game Trigger | Title | Default Priority |
| -------------- | ----------------------------- | ------------------------------- | :--------------: |
| `gunshot` | CEventGunShot | Shots Fired / Drive-By Shooting | 2 |
| `fight` | CEventShockingSeenMeleeAction | Fight In Progress | 3 |
| `carjacking` | CEventPedJackingMyVehicle | Carjacking In Progress | 1 |
| `vehicleTheft` | CEventShockingCarAlarm | Vehicle Theft | 2 |
| `explosion` | CEventExplosionHeard | Explosion Reported | 1 |
| `speeding` | Multiple driving events | Speeding Vehicle | 3 |
Each event can be individually enabled/disabled and has its own cooldown. The `speeding` event has an additional `speedThreshold` (km/h): only vehicles exceeding this speed trigger a call.
On-duty LEO/EMS players are automatically exempt and will never trigger auto-detected calls. Silenced weapons, aircraft, unarmed hits, and any weapons listed in `autoDetection.events.gunshot.weaponBlacklist` (stun gun, flare gun, fireworks, etc. by default) are filtered out. Server-side rate limiting prevents abuse (max 5 events per 15 seconds per player).
Suppression Hooks [#suppression-hooks]
Each auto-detection event can be silenced under custom conditions, for example when a player is inside a paintball minigame or a scripted cutscene. Suppressors run **before** the cooldown timer, so suppressed events don't burn the cooldown window.
Two registration paths feed the same internal list:
1. Config callback (server owner) [#1-config-callback-server-owner]
Add a `suppress` function to any event under `dispatch.autoDetection.events`. Return `true` to drop the event, return `false`/`nil` to allow it.
```lua title="config/shared/global.lua"
dispatch = {
autoDetection = {
events = {
gunshot = {
enabled = true,
cooldown = 15,
priority = 2,
weaponBlacklist = { ... },
suppress = function(ctx)
if exports["pug-paintball"]:IsInPaintball() then
return true
end
end,
},
},
},
},
```
2. Runtime export (third-party resource) [#2-runtime-export-third-party-resource]
Resources can self-register a suppressor without editing vx\_mdt files, useful for closed-source minigame scripts or custom integrations:
```lua title="my-resource/client.lua"
CreateThread(function()
exports.vx_mdt:registerAutoDetectSuppressor("gunshot", function(ctx)
return exports["pug-paintball"]:IsInPaintball()
end)
end)
```
Returns `true` on success, `false` if the event key is unknown or arguments are invalid. Registrations are cleared when vx\_mdt restarts; re-register on resource start.
Context payload [#context-payload]
The `ctx` argument shape varies per event:
| Event | `ctx` fields |
| -------------- | ----------------------------------------------- |
| `gunshot` | `ped`, `coords`, `weapon`, `vehicle?` |
| `fight` | `ped`, `coords` |
| `carjacking` | `ped`, `coords`, `vehicle` |
| `vehicleTheft` | `ped`, `coords`, `vehicle` |
| `explosion` | `ped`, `coords` |
| `speeding` | `ped`, `coords`, `vehicle`, `speed` (km/h, int) |
Suppressors are wrapped in `pcall`: a broken hook logs a warning via `lib.print.warn` instead of crashing the handler. Multiple runtime suppressors per event stack: if **any** returns `true`, the event is dropped.
Compatibility Mode [#compatibility-mode]
Set `dispatch.dispatchCompat` in config to the name of your third-party dispatch resource to intercept its calls and route them into vx\_mdt automatically.
Supported values: `"cd_dispatch"`, `"cd_dispatch3d"`, `"ps-dispatch"`, `"rcore_dispatch"`, `"fd_dispatch"`, or `""` (disabled).
```lua
dispatch = {
dispatchCompat = "cd_dispatch",
},
```
When compatibility mode is enabled, you do not need to remove the third-party dispatch resource. vx\_mdt intercepts its events and exports transparently.
# Mobile Data Terminal
Information [#information]
* **Frameworks:** ESX, QB, Qbox
* **Tech Stack:** Svelte 5, Tailwind CSS, Lua, Vite
* **Dependencies:** ox\_lib, oxmysql, OneSync
* **Escrow:** Bridges and config are open-source. Core logic is escrowed.
Features [#features]
* Multi-department support (law, EMS, judicial) with per-department theming
* Integrated dispatch system with overlay, panic button, and compatibility mode
* Citizen profiles with mugshots, DNA, fingerprints, and linked records
* Incident and report management with rich-text editors and templates
* Evidence tracking with stash integration
* Warrant and BOLO system with real-time in-memory lookups
* Weapon and vehicle registry with automatic linking from bridges
* Medical records system for EMS with condition tracking, prescriptions, and severity levels
* Codex knowledge base with categories and certificate-gated entries
* Charge management with categories and fine calculation
* Granular rank-based permissions per department
* Optional bidirectional sync between framework job grades and MDT ranks
* Configurable job masking to hide illegal or internal jobs from other MDT viewers
* Real-time officer presence and status tracking
* Radio integration (pma-voice) with auto-tuning and channel guarding
* Record sharing across departments
* Extensive server export API for third-party integration
Preview [#preview]
# Installation
Download the Resource [#download-the-resource]
Download the resource from the [Cfx.re Portal](https://portal.cfx.re/) after purchasing.
Extract and Place into Resources [#extract-and-place-into-resources]
Extract the downloaded file and place it in your server's resource folder.
Update server.cfg [#update-servercfg]
Add the following to your `server.cfg`. The resource should be started after your framework and dependencies.
```
ensure ox_lib
ensure oxmysql
ensure vx_mdt
```
Server Requirements [#server-requirements]
Your server must be running **artifacts 17000+** with **OneSync** enabled.
Configure Departments [#configure-departments]
Edit `config/shared/global.lua` to set up your departments, dispatch settings, and other options.
See the [Configuration](/paid-resources/mdt/config) page for a full reference.
The framework is auto-detected; no manual framework configuration is needed. Bridges for inventory, billing, prison, and other integrations are also auto-detected. Just ensure the supported resource is started.
Restart Server [#restart-server]
Restart your server. The MDT will automatically create all required database tables on first boot and seed default data for each department.
# Keybinds
All keybinds are registered through `ox_lib` and can be rebound in **FiveM Settings > Key Bindings > FiveM** at any time. The notification UI dynamically reflects the player's current bindings.
Terminal [#terminal]
| Default Key | Action | Config |
| ----------- | ---------- | --------------------- |
| `K` | Toggle MDT | `terminal.defaultKey` |
Set `terminal.keybind` to `false` to disable the keybind entirely. Players can still use the `/mdt` chat command.
Dispatch Notifications [#dispatch-notifications]
These keybinds are only active while a dispatch notification popup is visible on screen.
| Default Key | Action |
| ----------- | ------------------------------------------- |
| `O` | Dismiss the active notification |
| `G` | Open MDT and navigate to the dispatch call |
| `Z` | Respond (attach to or detach from the call) |
| `J` | Expand or collapse notification details |
Dispatch Panel [#dispatch-panel]
| Default Key | Action | Config |
| ----------- | ----------------------------- | ------------------------- |
| `F5` | Toggle dispatch overlay panel | `dispatch.overlayKeybind` |
Only registered when `dispatch.globalOverlay` is `true`.
Panic [#panic]
| Default Key | Action | Config |
| ----------- | ----------------- | ----------------------- |
| `F9` | Send panic signal | `dispatch.panicKeybind` |
Only registered when `dispatch.panicButton` is `true`. Cooldown between signals is controlled by `dispatch.panicCooldown` (default 30 seconds).
Dispatch notification keys (`O`, `G`, `Z`, `J`) are not configurable in `config/shared/global.lua`; they use fixed defaults registered through ox\_lib. Players can rebind them individually in FiveM's key binding settings.
# Config
Framework Related [#framework-related]
server/esx.lua [#serveresxlua]
```lua title="server/esx.lua"
return {
identifier_prefix = "char", -- Make sure it matches whatever you had before, although the resource will attempt to auto detect this.
database = {
-- Toggle the deletion (this will wipe any table listed below for any deleted character)
---@type boolean
cleanup = false,
-- A list of tables that you wish to clean when they are deleted.
---@type table
tables = {
{ name = 'users', column = 'identifier' },
{ name = 'owned_vehicles', column = 'owner' },
-- { name = 'playerskins', column = 'citizenid' },
-- { name = 'player_outfits', column = 'citizenid' },
},
}
}
```
server/qbox.lua [#serverqboxlua]
```lua title="server/qbox.lua"
--#region Modules
local log = require("modules.utility.shared.logger")
--#endregion
---@class IQboxCoreStarterItems
---@field name string
---@field amount integer
---@field metadata fun(client: integer): table
---@class IQboxSharedConfigResponse
---@field serverName string
---@field defaultSpawn vector4
---@field notifyPosition string
---@field starterItems IQboxCoreStarterItems
return {
--- Gets the starter items.
---@return IQboxCoreStarterItems[]
get_starter_items = function()
if GetResourceState("qbx_core") == "missing" then
log.warningf("[qbox::get_starter_items] `qbx_core` is missing, returning nothing.")
return {}
end
local data = LoadResourceFile("qbx_core", "config/shared.lua")
if not data then return {} end
local success, result = pcall(load, data)
if not success then
log.warningf("[qbox::get_starter_items] Failed to load Lua code (Got - %s)", result)
return {}
end
---@type boolean, IQboxSharedConfigResponse?
local execute_success, config_table = pcall(result --[[@as fun()]])
if not execute_success then
log.warningf("[qbox::get_starter_items] Failed to execute Lua code (Got - %s)", config_table)
return {}
end
if not config_table or not config_table.starterItems then
log.warningf("[qbox::get_starter_items] No starter items found in config")
return {}
end
return config_table.starterItems
end,
}
```
shared/qb.lua [#sharedqblua]
```lua title="shared/qb.lua"
return {
support_starting_apartments = GetResourceState("qb-apartments") ~= "missing"
}
```
shared/qbox.lua [#sharedqboxlua]
```lua title="shared/qbox.lua"
return {
support_starting_apartments = GetResourceState("qbx_properties") ~= "missing"
}
```
# Exports
Clientside Exports [#clientside-exports]
Logout() [#logout]
Logs the source/client out of the current character and goes back to the character select.
```lua
---@return nil
exports.vx_multicharacter:Logout()
```
Server Exports [#server-exports]
Logout(client) [#logoutclient]
Logs the targeted/specified client out of their character and goes back to the character selection screen..
```lua
---@param client integer
---@return nil
exports.vx_multicharacter:Logout(client)
```
# Introduction
Information [#information]
* Modern, cinematic UI.
* Lightweight and performance-friendly.
* Scales across all resolutions.
* Locale support for translation into any language.
* Customizable camera paths, character positions, and animations.
Features [#features]
* **Framework Compatibility**: ESX, QB, Qbox, Custom
* **Dependencies**: oxmysql
Preview [#preview]
# Clothing & Hair
The **illenium-appearance** resource provides functionality for blacklisting specific clothing items and hairstyles based on certain criteria. This feature allows you to restrict certain items based on:
* **Jobs**
* **Gangs**
* **ACEs (Access Control Entries)**
* **CitizenIDs (Player Identifiers)**
How It Works [#how-it-works]
To blacklist specific items, you need to modify the configuration in the `shared/blacklist.lua` file. This file contains a list of component names and drawable IDs that you can blacklist for male and female players.
By using the following format, you can restrict specific clothes or hairstyles based on certain conditions.
Example Use Case [#example-use-case]
For example, if you wanted to blacklist the following items:
* **Jackets:**
* 10, 12, 13, 18 (All Textures)
* 11 (Textures: 1, 2, 3)
* 25, 30, 35 (Accessible only to "police" job)
* **Masks:**
* 10, 11, 12, 13 (All Textures)
* 14 (Textures: 5, 7, 10, 12, 13)
* **Hats:**
* 41, 42, 45 (Accessible only to the "ballas" gang)
* **Scarfs & Chains:**
* 5, 6, 7 (Accessible only to "vip" ACE)
* **Shirts:**
* 16 (Accessible only to CitizenID "ZUM10723")
* **Hair:**
* 15 (All Textures)
Configuration Example [#configuration-example]
```lua title="blacklist.lua"
Config.Blacklist = {
male = {
hair = {
{
drawables = {15} -- All textures of hair ID 15 are blacklisted
}
},
components = {
masks = {
{
drawables = {10, 11, 12, 13} -- Blacklist masks with drawables 10, 11, 12, 13
},
{
drawables = {14},
textures = {5, 7, 10, 11, 12, 13} -- Blacklist mask 14 with specific textures
}
},
upperBody = {},
lowerBody = {},
bags = {},
shoes = {},
scarfAndChains = {
{
drawables = {5, 6, 7},
aces = {"vip"} -- Only available to players with the "vip" ACE
}
},
shirts = {
{
drawables = {16},
citizenids = {"ZUM10723"} -- Only accessible to player with CitizenID "ZUM10723"
}
},
bodyArmor = {},
decals = {},
jackets = {
{
drawables = {11},
textures = {1, 2, 3} -- Only textures 1, 2, and 3 for jacket 11 are blacklisted
},
{
drawables = {10, 12, 13, 18} -- Blacklist these jackets for all players
},
{
drawables = {25, 30, 35},
jobs = {"police"} -- Accessible only to players with the "police" job
}
}
},
props = {
hats = {
{
drawables = {41, 42, 45},
gangs = {"ballas"} -- Only accessible to the "ballas" gang
}
},
glasses = {},
ear = {},
watches = {},
bracelets = {}
}
},
female = {
components = {
masks = {},
upperBody = {},
lowerBody = {},
bags = {},
shoes = {},
scarfAndChains = {},
shirts = {},
bodyArmor = {},
decals = {},
jackets = {}
},
props = {
hats = {},
glasses = {},
ear = {},
watches = {},
bracelets = {}
}
}
}
```
Config Breakdown [#config-breakdown]
Male / Female [#male--female]
The blacklist configuration can be applied separately to male and female players. Ensure you modify the right section of the config for the desired player model.
Components [#components]
You can blacklist clothing components like jackets, masks, and shirts using `drawables` and `textures`. These are specified by their component IDs.
* **drawables**: The drawable ID of the item.
* **textures**: Specific texture IDs for items that have multiple textures (e.g., jackets).
* **jobs**: Restrict items to specific jobs (e.g., `police`).
* **gangs**: Restrict items to specific gangs (e.g., `ballas`).
* **aces**: Restrict items to users with a specific ACE (e.g., `vip`).
* **citizenids**: Restrict items to players with a specific CitizenID (e.g., `ZUM10723`).
Props [#props]
This section handles items like hats, glasses, and accessories. The logic is the same as with clothing components, where you can use `drawables` and restrict based on gangs, ACEs, or jobs.
Customizing the Blacklist [#customizing-the-blacklist]
You can easily modify the blacklist to suit your server's needs:
1. **Add new restrictions** by including the desired components (e.g., jackets, masks) and specifying which `drawables` or `textures` you want to blacklist.
2. **Assign roles**: Use the `jobs`, `gangs`, `aces`, and `citizenids` tables to ensure specific clothes are accessible only to players with the appropriate role or ID.
3. **Separate male and female configurations**: If you want different blacklisting rules for male and female players, ensure the configuration is placed under the respective sections (`male` or `female`).
Notes [#notes]
* Ensure that all blacklisted `drawables` and `textures` are correct and correspond to the correct clothing items in your resource.
* Adjust the roles, ACEs, and CitizenIDs as per your server's requirements for more granular control over who can wear what.
# Blacklisting
Before you can apply ACE-based restrictions in the **illenium-appearance** blacklist configuration, you must first enable ACE permissions on your server. Follow the steps below to set up ACE permissions properly.
Steps to Enable ACE Permissions [#steps-to-enable-ace-permissions]
1. **Add the ACE Command to `server.cfg`**\
To allow the server to recognize ACE permissions, you need to add the following line to your `server.cfg`:
```lua
add_ace resource.illenium-appearance command.list_aces allow
```
2. **Enable ACE Permissions in `shared/config.lua`**\
Next, you need to enable ACE permissions in the `shared/config.lua` configuration file. Locate the `Config.EnableACEPermissions` setting and set it to `true`:
```lua
Config.EnableACEPermissions = true
```
Example of Creating ACEs [#example-of-creating-aces]
Once ACE permissions are enabled, you can create and assign ACE roles to players.
Creating an ACE for VIP Players [#creating-an-ace-for-vip-players]
1. **Create the ACE Role**\
In your `server.cfg`, add the following line to create an ACE role called `vip`:
```lua
add_ace group.vip vip allow
```
2. **Assign Players to the VIP Role**\
To assign a specific player to the VIP role, use the `add_principal` command, specifying their unique **license** identifier. For example:
```lua
add_principal identifier.license:xxxxxxxxxxxxxxxxxx group.vip
```
Replace `xxxxxxxxxxxxxxxxxx` with the actual **license** of the player. You need to add the above `add_principal` line for each player you wish to assign the VIP role to.
Example for Multiple Players [#example-for-multiple-players]
If you want to assign the `vip` role to multiple players, just repeat the `add_principal` line with each player's unique license. For example:
```lua
add_principal identifier.license:abc123 group.vip
add_principal identifier.license:def456 group.vip
add_principal identifier.license:ghi789 group.vip
```
This assigns the **vip** ACE to the players with those specific license identifiers.
Final Steps [#final-steps]
Once you have enabled ACE permissions and created the necessary roles, you can use the ACE-based restrictions in the **illenium-appearance** blacklist configuration.
For example, you can blacklist certain clothing or ped models to only be accessible by players with the `vip` ACE by using the `aces` key in the blacklist configuration.
```lua title="blacklist.lua"
Config.Blacklist = {
male = {
components = {
scarfAndChains = {
{
drawables = {5, 6, 7},
aces = {"vip"} -- Accessible only to VIP players
}
}
}
}
}
```
Notes [#notes]
* Ensure that ACE permissions are properly set up before trying to use them in the blacklist configuration.
* If you have multiple ACE roles, you can assign them to players in the same way and use them to apply more granular access control.
# Peds
The **illenium-appearance** resource allows you to blacklist specific ped models based on various criteria. This feature helps restrict access to certain peds depending on the player's role. The supported criteria for blacklisting peds include:
* **Jobs**
* **Gangs**
* **ACEs (Access Control Entries)**
How It Works [#how-it-works]
To blacklist specific ped models, you need to modify the configuration in the `shared/peds.lua` file. The default file contains a list of all ped models without any restrictions applied.
Example Use Case [#example-use-case]
Let's say you want to limit access to the following ped models based on specific conditions:
* **Peds Available to "police" job**:\
`a_f_m_beach_01`, `a_f_m_bevhills_01`, `a_f_m_bevhills_02`
* **Peds Available to "vagos" gang**:\
`a_f_m_bodybuild_01`
* **Peds Available to players with the "admin" ACE**:\
`a_f_m_downtown_01`
* **Peds Available to both "police" job and "ballas" gang**:\
`a_f_m_skidrow_01`
To apply these restrictions, you need to remove any existing peds from the default list that do not have specific filters or limits defined.
Configuration Example [#configuration-example]
```lua title="peds.lua"
Config.Peds = {
pedConfig = {
-- Default peds list, no restrictions
{
peds = {...} -- List of peds that are accessible to everyone
},
-- Police Job restriction
{
peds = {"a_f_m_beach_01", "a_f_m_bevhills_01", "a_f_m_bevhills_02"},
jobs = {"police"} -- Accessible only to players with the "police" job
},
-- Vagos Gang restriction
{
peds = {"a_f_m_bodybuild_01"},
gangs = {"vagos"} -- Accessible only to players in the "vagos" gang
},
-- Admin ACE restriction
{
peds = {"a_f_m_downtown_01"},
aces = {"admin"} -- Accessible only to players with the "admin" ACE
},
-- Police Job and Ballas Gang restriction
{
peds = {"a_f_m_skidrow_01"},
jobs = {"police"}, -- Accessible only to players with the "police" job
gangs = {"ballas"} -- Accessible only to players in the "ballas" gang
}
}
}
```
Config Breakdown [#config-breakdown]
* **pedConfig**\
This section contains a list of configurations for various peds. Each configuration can specify one or more conditions based on jobs, gangs, and ACEs.
* **peds**\
The list of ped models that you want to apply restrictions to. You can specify multiple peds in an array.
* **jobs**\
Restricts a ped to players with a specific job. For example, if a ped is only available to players with the "police" job, add:
```lua
jobs = { "police" }
```
* **gangs**\
Restricts a ped to players in a specific gang. For example:
```lua
gangs = { "vagos" }
```
limits the ped to players in the "vagos" gang.
* **aces**\
Restricts a ped to players with a specific ACE (Access Control Entry). For example:
```lua
aces = { "admin" }
```
makes the ped available only to players with the "admin" ACE.
Customizing the Ped Blacklist [#customizing-the-ped-blacklist]
You can modify the blacklist to fit your server's needs:
1. **Adding more peds**: Simply add the name of the ped(s) you want to restrict in the `peds` list.
2. **Assigning roles**: Use the `jobs`, `gangs`, or `aces` fields to specify which roles should have access to the ped. You can combine these fields to apply multiple restrictions simultaneously.
3. **Ensure accurate ped names**: Double-check that the ped names you use are correct and match the available models.
Notes [#notes]
* Make sure that the ped names are accurate and correspond to the correct ped models in your resource.
* Ensure the roles (jobs, gangs, ACEs) are correctly set up in your server's configuration to avoid conflicts or unwanted access.
# ESX
Download the Resource [#download-the-resource]
Download the resource from the [Cfx.re Portal](https://portal.cfx.re/) after purchasing.
Extract and Place into Resources [#extract-and-place-into-resources]
Download the file, extract it, and place it in your server's resource folder.
Update Config [#update-config]
Make sure to include the resource in your `server.cfg` if you haven't already.\
The resource should be started after your framework and `ox_lib`.
```
ensure es_extended
ensure ox_lib
ensure illenium-appearance
```
Conflicting Resources [#conflicting-resources]
You must remove any other appearance editor from your server, as this one overrides them.\
Below is a list of resources:
* esx\_skin
* skinchanger
* fivem-appearance
* esx\_barbershop
* esx\_clotheshop
Update the fxmanifest [#update-the-fxmanifest]
Make sure to update the `fxmanifest.lua` to add a new "provides" section that prevents any errors from being thrown from other ESX resources.
```lua
provides({ "esx_skin", "skinchanger" })
```
Install SQL to Database [#install-sql-to-database]
```sql title="sql.sql"
CREATE TABLE IF NOT EXISTS `management_outfits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_name` varchar(50) NOT NULL,
`type` varchar(50) NOT NULL,
`minrank` int(11) NOT NULL DEFAULT 0,
`name` varchar(50) NOT NULL DEFAULT 'Cool Outfit',
`gender` varchar(50) NOT NULL DEFAULT 'male',
`model` varchar(50) DEFAULT NULL,
`props` varchar(1000) DEFAULT NULL,
`components` varchar(1500) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `playerskins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(255) NOT NULL,
`model` varchar(255) NOT NULL,
`skin` text NOT NULL,
`active` tinyint(4) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `citizenid` (`citizenid`),
KEY `active` (`active`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `player_outfits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) DEFAULT NULL,
`outfitname` varchar(50) NOT NULL DEFAULT '0',
`model` varchar(50) DEFAULT NULL,
`props` varchar(1000) DEFAULT NULL,
`components` varchar(1500) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `citizenid_outfitname_model` (`citizenid`,`outfitname`,`model`),
KEY `citizenid` (`citizenid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `player_outfit_codes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`outfitid` int(11) NOT NULL,
`code` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `FK_player_outfit_codes_player_outfits` (`outfitid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
```
Configure the Resource [#configure-the-resource]
You can now configure the resource; all configs are in the `shared` folder.
Restart Server [#restart-server]
Once you've completed all the steps above, you should be set.
# Install
import { Box } from "lucide-react";
} href="/paid-resources/appearance/install/esx" title="ESX">
Installation for the framework **ESX**.
} href="/paid-resources/appearance/install/qbcore" title="QB">
Installation for the framework **QB**.
# QB
Download the Resource [#download-the-resource]
Download the resource from the [Cfx.re Portal](https://portal.cfx.re/) after purchasing.
Extract and Place into Resources [#extract-and-place-into-resources]
Download the file, extract it, and place it in your server's resource folder.
Update Config [#update-config]
Make sure to include the resource in your `server.cfg` if you haven't already.\
The resource should be started after your framework and `ox_lib`.
```
ensure qb-core
ensure ox_lib
ensure illenium-appearance
```
Conflicting Resources [#conflicting-resources]
You must remove any other appearance editor from your server, as this one overrides them.\
Below is a list of resources:
* qb-clothing
* fivem-appearance
* qb-tattooshop
Install SQL to Database [#install-sql-to-database]
```sql title="sql.sql"
CREATE TABLE IF NOT EXISTS `management_outfits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_name` varchar(50) NOT NULL,
`type` varchar(50) NOT NULL,
`minrank` int(11) NOT NULL DEFAULT 0,
`name` varchar(50) NOT NULL DEFAULT 'Cool Outfit',
`gender` varchar(50) NOT NULL DEFAULT 'male',
`model` varchar(50) DEFAULT NULL,
`props` varchar(1000) DEFAULT NULL,
`components` varchar(1500) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `playerskins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(255) NOT NULL,
`model` varchar(255) NOT NULL,
`skin` text NOT NULL,
`active` tinyint(4) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `citizenid` (`citizenid`),
KEY `active` (`active`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `player_outfits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) DEFAULT NULL,
`outfitname` varchar(50) NOT NULL DEFAULT '0',
`model` varchar(50) DEFAULT NULL,
`props` varchar(1000) DEFAULT NULL,
`components` varchar(1500) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `citizenid_outfitname_model` (`citizenid`,`outfitname`,`model`),
KEY `citizenid` (`citizenid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `player_outfit_codes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`outfitid` int(11) NOT NULL,
`code` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `FK_player_outfit_codes_player_outfits` (`outfitid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
```
Adjust your Resources [#adjust-your-resources]
Remove the `qb-clothing` dependency from the `fxmanifest.lua` file in both the `qb-houses` and `qb-apartments` projects.
Steps [#steps]
1. **Navigate to your projects:**
* Go to the project directory of `qb-houses`.
* Locate the `fxmanifest.lua` file.
2. **Modify the `fxmanifest.lua` file:**
* Open the `fxmanifest.lua` file using a text editor.
* Find the `dependencies` section.
* Remove or comment out the line containing `qb-clothing`.
3. **Repeat for `qb-apartments`:**
* Navigate to the `qb-apartments` project directory.
* Follow steps 2a to 2c.
Example [#example]
```diff
dependencies {
'qb-core',
'qb-vehicles',
- 'qb-clothing',
'qb-interior'
}
```
Verification [#verification]
* Ensure the `qb-clothing` entry is removed from both projects' `fxmanifest.lua`.
* Double-check functionality by running a test to ensure other components work without `qb-clothing`.
Configure the Resource [#configure-the-resource]
You can now configure the resource; all configs are in the `shared` folder.
Restart Server [#restart-server]
Once you've completed all the steps above, you should be set.
# esx_core
esx_skin [#esx_skin]
Install Resource [#install-resource]
Follow the installation guide provided on the installation guide.
Connect [#connect]
Connect to your server and log in to any character.
Command Execution [#command-execution]
Execute the command `/migrateskins` and wait for it to complete.
Restart Server [#restart-server]
After completing the migration, quit the game and restart your server. You should then be good to go.
fivem-appearance [#fivem-appearance]
Install Resource [#install-resource-1]
Follow the installation guide provided on the installation guide.
Migrate Outfits [#migrate-outfits]
Rename the `outfits` table to `player_outfits`. Change the column names in the `player_outfits` table as follows:
* `identifier` → `citizenid`
* `name` → `outfitname`
* `ped` → `model`
Restart Server [#restart-server-1]
After completing the migration, quit the game and restart your server. You should then be good to go.
# Migration
import { Box } from "lucide-react";
} href="/paid-resources/appearance/migration/esx" title="ESX">
Migrating from **ESX**.
} href="/paid-resources/appearance/migration/qbcore" title="QB">
Migrating from **QB**.
# qb_core
fivem-appearance [#fivem-appearance]
Install Resource [#install-resource]
Follow the installation guide provided on the installation guide.
Remove Outfits [#remove-outfits]
Delete the `player_outfits` table from the database.
Connect to Server [#connect-to-server]
Connect to your server and log in to any character.
Command Execution [#command-execution]
Execute the command `/migrateskins fivem-appearance` and wait for it to complete.
Restart Server [#restart-server]
After completing the migration, quit the game and restart your server. You should then be good to go.
qb-clothing [#qb-clothing]
Install Resource [#install-resource-1]
Follow the installation guide provided above.
Connect [#connect]
Connect to your server and log in to any character.
Command Execution [#command-execution-1]
Execute the command `/migrateskins qb-clothing` and wait for it to complete.
Restart Server [#restart-server-1]
After completing the migration, quit the game and restart your server. You should then be good to go.
# Client
Client exports are intended for player-side integrations: item scripts, keybinds, or other client-only flows that need to interact with the local MDT.
They are **not** gated by the `exportAllowlist` (that setting only applies to server exports). Open requests are routed to the server internally, where `setupOfficer` enforces job, duty, and roster-status checks, so a client calling `openTerminal()` cannot bypass the same authorization the `/mdt` command runs.
For server-side exports see [Server](/paid-resources/mdt/exports/server).
Terminal [#terminal]
openTerminal() [#openterminal]
Open the MDT for the local player. Fires a server event that runs the full authorization flow (same as
/mdt
). Useful for item scripts where the player uses an item to open their tablet.
```lua
exports.vx_mdt:openTerminal()
```
closeTerminal() [#closeterminal]
Close the MDT for the local player. Pure client-side, no server round-trip. Safe to call when the MDT is already closed.
```lua
exports.vx_mdt:closeTerminal()
```
isTerminalOpen() [#isterminalopen]
Check whether the local player has the MDT open. Reads the local
terminal_subscribed
state bag.
```lua
---@return boolean
local open = exports.vx_mdt:isTerminalOpen()
```
Dispatch [#dispatch]
triggerPanic() [#triggerpanic]
Send a panic signal as the local player. Mirrors the F9 keybind / dispatch overlay button: captures current coords and street name, then creates a priority-0 panic call broadcast to all departments.
Useful for custom keybind scripts, smartwatch items, or down-detection scripts that need to trigger panic without going through the MDT UI.
All existing guards still apply: the player must be on duty (have
mdt_dep_id
set), the server enforces
jobCheck
, source-based rate limiting, and per-identifier cooldown (
dispatch.panicCooldown
, default 30s).
```lua
---@return { id?: number, error?: string }
local result = exports.vx_mdt:triggerPanic()
if result and result.id then
print("Panic call created with ID:", result.id)
end
```
Returns
{'{ id }'}
on success,
{'{ error }'}
on rejection (off duty, server cooldown, or job check failed). If the local cooldown is active, a toast is shown to the player and no server request is sent.
registerAutoDetectSuppressor(eventKey, fn) [#registerautodetectsuppressoreventkey-fn]
Register a callback that can drop a specific auto-detection event before it generates a dispatch call. Useful for paintball minigames, arena events, scripted cutscenes, or any context where the player's actions shouldn't page LEOs.
eventKey
must be one of
gunshot
,
fight
,
carjacking
,
vehicleTheft
,
explosion
,
speeding
.
fn
receives an event-specific context table and should return
true
to suppress,
false
/
nil
to allow.
Suppressors stack: multiple resources can each register their own. If any returns
true
, the event is dropped. Calls run before the cooldown timer, so suppressed events don't burn the cooldown window. See
Suppression Hooks
for the full
ctx
shape per event.
```lua
---@param eventKey "gunshot"|"fight"|"carjacking"|"vehicleTheft"|"explosion"|"speeding"
---@param fn fun(ctx: table): boolean?
---@return boolean ok
local ok = exports.vx_mdt:registerAutoDetectSuppressor("gunshot", function(ctx)
return exports["pug-paintball"]:IsInPaintball()
end)
```
Returns
true
on success,
false
if the event key is unknown or arguments are invalid. Registrations are cleared on vx_mdt restart; re-register from your resource's start hook.
# Server
Server exports are controlled by the `exportAllowlist` in config; an empty table allows all resources to call them. Failed calls return `{ error = "..." }`.
For client-side exports see [Client](/paid-resources/mdt/exports/client).
Terminal [#terminal]
openTerminal(source) [#openterminalsource]
Open the MDT for a player. Runs the same setup as the
/mdt
command (job check, duty check, roster status, department/staff fetch). Shows the same access-denied toasts on failure.
```lua
---@param source number Player server ID
---@return { ok: true }|{ error: string }
local result = exports.vx_mdt:openTerminal(source)
if result.error then
-- Denied: wrong job, off-duty, suspended/fired/on-leave, etc.
end
```
closeTerminal(source) [#closeterminalsource]
Close the MDT for a player. Safe to call when the MDT is already closed.
```lua
---@param source number Player server ID
---@return { ok: true }|{ error: string }
exports.vx_mdt:closeTerminal(source)
```
isTerminalOpen(source) [#isterminalopensource]
Check whether a player currently has the MDT open. Reads the replicated
terminal_subscribed
state bag.
```lua
---@param source number Player server ID
---@return boolean
local open = exports.vx_mdt:isTerminalOpen(source)
```
Officers [#officers]
createOfficer(identifier, depId, data) [#createofficeridentifier-depid-data]
Create a staff record for a department.
```lua
---@param identifier string Citizen/state ID
---@param depId number Department ID
---@param data { first_name: string, last_name: string, callsign?: string, badge_number?: string, rank_id?: number, source?: number }
---@return table Staff record or { error: string }
local officer = exports.vx_mdt:createOfficer(identifier, depId, {
first_name = "John",
last_name = "Doe",
callsign = "1-A",
badge_number = "1234",
})
```
deleteOfficer(identifier, depId) [#deleteofficeridentifier-depid]
Soft-delete an officer (sets status to "terminated").
```lua
---@param identifier string Citizen/state ID
---@param depId number Department ID
---@return boolean
local success = exports.vx_mdt:deleteOfficer(identifier, depId)
```
getOfficer(identifier, depId) [#getofficeridentifier-depid]
Get officer data including permissions.
```lua
---@param identifier string Citizen/state ID
---@param depId number Department ID
---@return table|nil Staff record or nil
local officer = exports.vx_mdt:getOfficer(identifier, depId)
```
updateOfficer(identifier, depId, data) [#updateofficeridentifier-depid-data]
Update officer fields.
```lua
---@param identifier string Citizen/state ID
---@param depId number Department ID
---@param data { callsign?: string, badge_number?: string, rank_id?: number, status?: string, notes?: string, certificates?: table }
---@return boolean
local success = exports.vx_mdt:updateOfficer(identifier, depId, {
callsign = "2-B",
badge_number = "5678",
})
```
isOfficer(source) [#isofficersource]
Check if an online player is set up as an officer.
```lua
---@param source number Player server ID
---@return boolean
local isOfficer = exports.vx_mdt:isOfficer(source)
```
getOfficerBySource(source) [#getofficerbysourcesource]
Get officer data for an online player.
```lua
---@param source number Player server ID
---@return table|nil Staff record or nil
local officer = exports.vx_mdt:getOfficerBySource(source)
```
Profiles [#profiles]
getProfile(identifier) [#getprofileidentifier]
Get a full citizen profile.
```lua
---@param identifier string Citizen/state ID
---@return table|nil Profile record or nil
local profile = exports.vx_mdt:getProfile(identifier)
```
updateProfileField(identifier, field, value) [#updateprofilefieldidentifier-field-value]
Update a specific field on a citizen profile.
```lua
---@param identifier string Citizen/state ID
---@param field "dna"|"fingerprint"|"bloodtype"|"profile_image_url"
---@param value string New value
---@return boolean
local success = exports.vx_mdt:updateProfileField(identifier, "dna", "ABC-1234")
```
searchProfiles(query) [#searchprofilesquery]
Search citizen profiles by name or identifier.
```lua
---@param query string Search query
---@return table[] Array of profile search results
local results = exports.vx_mdt:searchProfiles("John")
```
Vehicles [#vehicles]
getVehicle(plate) [#getvehicleplate]
Get a full vehicle record by plate.
```lua
---@param plate string Vehicle plate
---@return table|nil Vehicle record or nil
local vehicle = exports.vx_mdt:getVehicle("ABC123")
```
setVehicleStatus(plate, status, notes?) [#setvehiclestatusplate-status-notes]
Set the status flag on a vehicle. When
notes
is provided, it overwrites the vehicle's notes field; when omitted, existing notes are preserved.
```lua
---@param plate string Vehicle plate
---@param status "none"|"stolen"|"seized"|"impounded"|"wanted"|"flagged"|"parked"|"out"
---@param notes? string Optional notes to set on the vehicle
---@return boolean
local success = exports.vx_mdt:setVehicleStatus("ABC123", "stolen")
local success = exports.vx_mdt:setVehicleStatus("ABC123", "parked", "Garage: Legion Square")
```
searchVehicles(query) [#searchvehiclesquery]
Search vehicles by plate or owner.
```lua
---@param query string Search query
---@return table[] Array of vehicle search results
local results = exports.vx_mdt:searchVehicles("ABC")
```
registerVehicle(source, data) [#registervehiclesource-data]
Fire-and-forget auto-register from garage bridges.
```lua
---@param source number Player server ID
---@param data { plate: string, model_name?: string, color?: string, vehicle_class?: string, autoLinkOwner?: boolean, owner?: string }
exports.vx_mdt:registerVehicle(source, {
plate = "ABC123",
model_name = "sultan",
color = "Red",
})
```
Weapons [#weapons]
createWeapon(serial, data) [#createweaponserial-data]
Create a weapon record if it doesn't already exist.
```lua
---@param serial string Weapon serial number
---@param data { model?: string, owner?: string, source?: number }
---@return boolean
local success = exports.vx_mdt:createWeapon("WPN-001", {
model = "WEAPON_PISTOL",
owner = "ABC12345",
})
```
getWeapon(serial) [#getweaponserial]
Get a full weapon record by serial number.
```lua
---@param serial string Weapon serial number
---@return table|nil Weapon record or nil
local weapon = exports.vx_mdt:getWeapon("WPN-001")
```
setWeaponStatus(serial, status) [#setweaponstatusserial-status]
Set the status flag on a weapon.
```lua
---@param serial string Weapon serial number
---@param status "none"|"stolen"|"seized"|"destroyed"|"missing"
---@return boolean
local success = exports.vx_mdt:setWeaponStatus("WPN-001", "stolen")
```
registerWeapon(source, data) [#registerweaponsource-data]
Fire-and-forget auto-register from inventory bridges.
```lua
---@param source number Player server ID
---@param data { serial: string, model?: string, owner?: string }
exports.vx_mdt:registerWeapon(source, {
serial = "WPN-001",
model = "WEAPON_PISTOL",
})
```
Properties [#properties]
getProperty(id) [#getpropertyid]
Get a property record by ID.
```lua
---@param id number Property ID
---@return table|nil Property record or nil
local property = exports.vx_mdt:getProperty(1)
```
searchProperties(query) [#searchpropertiesquery]
Search properties by address.
```lua
---@param query string Search query
---@return table[] Array of property search results
local results = exports.vx_mdt:searchProperties("Alta St")
```
BOLOs [#bolos]
createBolo(depId, data) [#createbolodepid-data]
Create a Be On the Lookout notice.
```lua
---@param depId number Department ID
---@param data { title: string, bolo_type?: "person"|"vehicle"|"other", author?: string }
---@return { id: number }|{ error: string }
local result = exports.vx_mdt:createBolo(1, {
title = "Suspect in white sedan",
bolo_type = "vehicle",
author = "Officer Smith",
})
```
deleteBolo(boloId, depId) [#deleteboloboloid-depid]
Delete a BOLO by ID.
```lua
---@param boloId number BOLO ID
---@param depId number Department ID
---@return {}|{ error: string }
local result = exports.vx_mdt:deleteBolo(1, 1)
```
hasActiveBolo(identifier) [#hasactiveboloidentifier]
Check if an identifier has any active BOLOs. Uses in-memory lookup for fast performance.
```lua
---@param identifier string Citizen/state ID
---@return boolean
local hasBolo = exports.vx_mdt:hasActiveBolo(identifier)
```
Warrants [#warrants]
createWarrant(depId, data) [#createwarrantdepid-data]
Create a warrant.
```lua
---@param depId number Department ID
---@param data { title: string, warrant_type?: "arrest"|"search", author?: string }
---@return { id: number }|{ error: string }
local result = exports.vx_mdt:createWarrant(1, {
title = "Arrest warrant for John Doe",
warrant_type = "arrest",
author = "Judge Williams",
})
```
deleteWarrant(warrantId, depId) [#deletewarrantwarrantid-depid]
Delete a warrant by ID.
```lua
---@param warrantId number Warrant ID
---@param depId number Department ID
---@return {}|{ error: string }
local result = exports.vx_mdt:deleteWarrant(1, 1)
```
hasActiveWarrant(identifier) [#hasactivewarrantidentifier]
Check if an identifier has any active warrants. Uses in-memory lookup for fast performance.
```lua
---@param identifier string Citizen/state ID
---@return boolean
local hasWarrant = exports.vx_mdt:hasActiveWarrant(identifier)
```
Incidents [#incidents]
createIncident(depId, data) [#createincidentdepid-data]
Create a new incident.
```lua
---@param depId number Department ID
---@param data { title: string, author?: string, dep_type?: "law"|"ems"|"judge" }
---@return { id: number }|{ error: string }
local result = exports.vx_mdt:createIncident(1, {
title = "Traffic stop on Route 68",
author = "Officer Smith",
})
```
Reports [#reports]
createReport(depId, data) [#createreportdepid-data]
Create a new report.
```lua
---@param depId number Department ID
---@param data { title: string, author?: string, dep_type?: "law"|"ems"|"judge" }
---@return { id: number }|{ error: string }
local result = exports.vx_mdt:createReport(1, {
title = "Daily patrol report",
author = "Officer Smith",
})
```
Toaster [#toaster]
toastPlayer(source, data) [#toastplayersource-data]
Send a toast notification to a single player.
```lua
---@param source number Player server ID
---@param data { title: string, message: string, close_delay?: number, type?: "default"|"assertive"|"polite"|"warn", sound?: string }
---@return boolean|{ error: string }
local result = exports.vx_mdt:toastPlayer(source, {
title = "Dispatch",
message = "You have been assigned to call #42",
})
```
toastPlayers(sources, data) [#toastplayerssources-data]
Send a toast notification to multiple players.
```lua
---@param sources number[] Array of player server IDs
---@param data { title: string, message: string, close_delay?: number, type?: "default"|"assertive"|"polite"|"warn", sound?: string }
---@return boolean|{ error: string }
local result = exports.vx_mdt:toastPlayers({1, 2, 3}, {
title = "Alert",
message = "Shift change in 5 minutes",
})
```
toastDepartment(depId, data, excludeSource?) [#toastdepartmentdepid-data-excludesource]
Send a toast notification to all online members of a department.
```lua
---@param depId number Department ID
---@param data { title: string, message: string, close_delay?: number, type?: "default"|"assertive"|"polite"|"warn", sound?: string }
---@param excludeSource? number Player server ID to exclude
---@return boolean|{ error: string }
local result = exports.vx_mdt:toastDepartment(1, {
title = "BOLO Update",
message = "New BOLO issued for white sedan",
})
```
Lookups [#lookups]
getCharges() [#getcharges]
Returns all charges with categories (cached).
```lua
---@return table[] Array of charges
local charges = exports.vx_mdt:getCharges()
```
getDepartments() [#getdepartments]
Returns a lightweight department list.
```lua
---@return table[] Array of departments
local departments = exports.vx_mdt:getDepartments()
```
# ESX
Download the Resource [#download-the-resource]
Download the resource from the [Cfx.re Portal](https://portal.cfx.re/) after purchasing.
Extract and Place into Resources [#extract-and-place-into-resources]
Download the file, extract it, and place it in your server's resource folder.
Update Config [#update-config]
Make sure to include the resource in your `server.cfg` if you haven't already.\
The resource should be started after your framework and `oxmysql`.
```
ensure oxmysql
ensure es_extended
ensure vx_multicharacter
```
Conflicting Resources [#conflicting-resources]
You must remove any other multicharacter/identity resource from your server, as this one overrides them.\
Below is a list of resources:
* esx\_multicharacter
* esx\_identity
Update the fxmanifest [#update-the-fxmanifest]
Make sure to update the `fxmanifest.lua` to add a new "provides" section if it's not there already\
that prevents any errors from being thrown from other ESX resources.
```lua
provides({ "esx_identity", "esx_multicharacter" })
```
Configure the Resource [#configure-the-resource]
You can now configure the resource; all configs are in the `config` folder.
Restart Server [#restart-server]
Once you've completed all the steps above, you should be set.
# Install
import { Box } from "lucide-react";
} href="/paid-resources/multicharacter/install/esx" title="ESX">
Installation for the framework **ESX**.
} href="/paid-resources/multicharacter/install/qbcore" title="QB">
Installation for the framework **QB**.
} href="/paid-resources/multicharacter/install/qbox" title="QBX">
Installation for the framework **QBX**.
# QB
Download the Resource [#download-the-resource]
Download the resource from the [Cfx.re Portal](https://portal.cfx.re/) after purchasing.
Extract and Place into Resources [#extract-and-place-into-resources]
Download the file, extract it, and place it in your server's resource folder.
Update Config [#update-config]
Make sure to include the resource in your `server.cfg` if you haven't already.\
The resource should be started after your framework and `oxmysql`.
```
ensure oxmysql
ensure qb-core
ensure vx_multicharacter
```
Conflicting Resources [#conflicting-resources]
You must remove any other appearance editor from your server, as this one overrides them.\
Below is a list of resources:
* qb-multicharacter
Configure the Resource [#configure-the-resource]
You can now configure the resource; all configs are in the `config` folder.
Restart Server [#restart-server]
Once you've completed all the steps above, you should be set.
# QBX
Download the Resource [#download-the-resource]
Download the resource from the [Cfx.re Portal](https://portal.cfx.re/) after purchasing.
Extract and Place into Resources [#extract-and-place-into-resources]
Download the file, extract it, and place it in your server's resource folder.
Update Config [#update-config]
Make sure to include the resource in your `server.cfg` if you haven't already.\
The resource should be started after your framework and `oxmysql`.
```
ensure oxmysql
ensure qbx_core
ensure vx_multicharacter
```
Configuration Instructions for QBX Core [#configuration-instructions-for-qbx-core]
To properly set up the `qbx_core`, you will need to modify the configuration file and adjust some settings. Follow these steps to ensure the correct setup:
1. **Access the Configuration File:**\
Navigate to the `qbx_core/config/client.lua` file on your server. This file contains all the necessary settings to customize your core functionality.
2. **Update Configuration Values:**\
Locate the settings within this file and modify the following values to ensure they match your server's requirements:
```lua
useExternalCharacters = true
startingApartment = false
```
* Setting `useExternalCharacters` to `true` allows integration with external character systems, providing more flexibility in character management.
* Setting `startingApartment` to `false` disables any starting apartments, which is necessary due to the fact that the multicharacter has its own logic that supports `qbx_properties`.
Configure the Resource [#configure-the-resource]
You can now configure the resource; all configs are in the `config` folder.
Restart Server [#restart-server]
Once you've completed all the steps above, you should be set.