Body armour in FiveM: values, items, vests and persistence
Armour is a value from 0 up to the player's maximum, 100 by default: SetPedArmour(ped, n) sets it, AddArmourToPed adds to it, GetPedArmour reads it and SetPlayerMaxArmour(PlayerId(), n) changes the cap. ox_inventory ships an armour item that sets 100; QBCore's qb-smallresources has armor (75) and heavyarmor (100, with a visible vest). The visible vest is clothing component 9. Armour is not saved by default everywhere, so store it on disconnect, and check it on the server with GetPedArmour under OneSync.
Armour is the other half of weapon balance. You can tune every gun on the server and still end up with one-sided fights if one player has a full vest and the other has none, or if armour quietly resets on every relog. Armour in GTA is a simple number, but the item that grants it, the vest people can see, and whether it survives a restart are all left to your scripts.

#How armour works
A player ped has two pools: health and armour. Player health tops out at 200 by default, and armour runs from 0 to the player's maximum armour, which is 100 unless a script changes it. Incoming weapon damage is taken from armour first, so a full vest is effectively extra hit points against gunfire.
| Native | Side | What it does |
|---|---|---|
SetPedArmour(ped, amount) | Client and server | Sets armour to a value |
AddArmourToPed(ped, amount) | Client | Adds to the current value |
GetPedArmour(ped) | Client and server | Reads the current value |
SetPlayerMaxArmour(player, value) | Client | Changes the cap (default 100) |
GetPlayerMaxArmour(player) | Client and server | Reads the cap |
Armour natives
Note that SetPlayerMaxArmour takes a player id, not a ped. SetPedArmour and GetPedArmour exist on the server as well, which is what makes server-side armour logic possible with OneSync.
Two exceptions to "armour absorbs gunfire" matter for balance. Melee weapons such as the knife, bat and machete have the ArmourPenetrating flag in their weapons.meta, and headshots are governed by the critical hit rules, not the vest. If a vest does not seem to help against headshots, look at headshot damage settings, where SetPedSuffersCriticalHits lives.
#Giving armour from a script
The simplest use is a one-liner, but do it where it cannot be abused. Setting armour in a client event that any client can trigger is an open door for cheaters, so apply it from the server.
local function giveArmour(src, amount)
local ped = GetPlayerPed(src)
if ped == 0 then return end
SetPedArmour(ped, math.min(amount, 100))
endIf you raise the cap for a special role, the client must call SetPlayerMaxArmour(PlayerId(), 150) first, otherwise values above 100 are clamped. Most roleplay servers keep 100 as the maximum for everyone and vary how much each vest gives.
#The ox_inventory armour item
ox_inventory ships an item called armour, labelled "Bulletproof Vest", weighing 3000 and not stackable. Using it plays a clothing animation for 3.5 seconds; its client handler only runs if the player's armour is below 100, and when the item is used it sets the maximum and the armour to 100.
['armour'] = {
label = 'Bulletproof Vest',
weight = 3000,
stack = false,
client = {
anim = { dict = 'clothingshirt', clip = 'try_shirt_positive_d' },
usetime = 3500
}
},For several vest tiers, add your own items and a client export that follows the same pattern: check the current armour, let ox_inventory consume the item, then apply the value.
['vest_light'] = {
label = 'Light Vest',
weight = 1500,
stack = false,
client = {
anim = { dict = 'clothingshirt', clip = 'try_shirt_positive_d' },
usetime = 2500,
export = 'my_armour.vest',
},
},local VESTS = { vest_light = 50, vest_heavy = 100 }
exports('vest', function(data, slot)
local amount = VESTS[data.name]
if not amount or GetPedArmour(PlayerPedId()) >= amount then return end
exports.ox_inventory:useItem(data, function(used)
if used then
SetPlayerMaxArmour(PlayerId(), 100)
SetPedArmour(PlayerPedId(), amount)
end
end)
end)The pattern of useItem with a callback is the same one ox_inventory uses for its own armour item, so the item is only removed when the use actually completes. Item fields are covered in ox_inventory items.lua.
#QBCore armour items
In QBCore the vests live in qb-smallresources as usable items. armor gives 75 armour and refuses if the player already has 75 or more; heavyarmor gives 100 and, unless disabled in the config, also puts a vest drawable on the player. Both remove the item on the server and set armour there with SetPedArmour(GetPlayerPed(source), value). There is also a police-only /resetarmor command that takes the heavy vest off and returns the item.
QBCore keeps an armor value in player metadata, but how and when it is written depends on your resource versions, so check that armour actually survives a relog on your server before assuming it does.
#The visible vest (component 9)
Armour as a number and a vest you can see are unrelated. The visible vest is ped clothing component 9, listed as Kevlar in the MP freemode component list. Setting armour does not add a vest, and adding a vest does not add armour; scripts that want both do both.
local saved
local function wearVest(drawable, texture)
local ped = PlayerPedId()
saved = { GetPedDrawableVariation(ped, 9), GetPedTextureVariation(ped, 9) }
SetPedComponentVariation(ped, 9, drawable, texture, 0)
end
local function removeVest()
if not saved then return end
SetPedComponentVariation(PlayerPedId(), 9, saved[1], saved[2], 0)
saved = nil
endDrawable numbers differ between the male and female freemode models and between game builds, which is why QBCore's heavy armour code checks the character's gender before picking one. Find the numbers for your server in a clothing menu rather than copying them from another server. Many servers also remove the vest drawable automatically when armour reaches zero, so a "shot-through" vest disappears.
#Saving armour across relogs
If armour resets to zero on reconnect, players lose vests they paid for; if it resets to full, vests become free. When your framework does not save it, store it yourself. This example uses the resource key-value store and the player's license identifier.
local function key(src)
local license = GetPlayerIdentifierByType(src, 'license')
return license and ('armour:' .. license)
end
AddEventHandler('playerDropped', function()
local src = source
local k = key(src)
if not k then return end
SetResourceKvpInt(k, GetPedArmour(GetPlayerPed(src)))
end)
RegisterNetEvent('armour:restore', function()
local src = source
local k = key(src)
if not k then return end
local value = GetResourceKvpInt(k)
if value > 0 then
SetPedArmour(GetPlayerPed(src), math.min(value, 100))
end
end)Trigger armour:restore from the client after your framework has finished loading the character and spawning the ped; armour set on a ped that is about to be replaced is lost. If your server has multiple characters per license, key the value by character id instead.
#Checking armour on the server
Armour is a favourite target for menus because it is a single number. With OneSync the server can read it and compare it with what is possible.
CreateThread(function()
while true do
Wait(10000)
for _, id in ipairs(GetPlayers()) do
local ped = GetPlayerPed(id)
if ped ~= 0 and GetPedArmour(ped) > 100 then
print(('[armour] %s has %d armour'):format(GetPlayerName(id), GetPedArmour(ped)))
end
end
end
end)A value above the maximum is a strong signal. A player who goes from 0 to 100 without using an item is another; you can track that by recording when your own scripts grant armour. As with any detection, log first and act once you trust it; the wider approach is in server-side weapon validation.
#Balancing armour against your weapons
- Vest strength shapes time-to-kill more than small damage tweaks. A full vest is a large block of extra effective health, so test your weapons against armoured and unarmoured targets separately.
- Make vests cost something. Weight in the inventory, a price, a crafting recipe or job-only access keep full armour from being the default state.
- Consider tiers. Light vests for civilians and gangs, heavy vests for police and tactical units, gives each side a reason to engage differently.
- Remember melee. Because melee weapons penetrate armour, a knife fight ignores vests; decide whether that is realistic enough or whether to scale melee damage down, as covered in the melee guide.
Questions
How do I give armour in FiveM?
SetPedArmour(ped, 100). With OneSync it also works on the server with SetPedArmour(GetPlayerPed(source), 100), which is safer than a client event.What is the maximum armour in FiveM?
SetPlayerMaxArmour(PlayerId(), value) on the client before setting a higher value.How do I make the vest visible on the player?
SetPedComponentVariation(ped, 9, drawable, texture, 0). The visible vest and the armour value are separate.Why does armour reset when a player reconnects?
playerDropped and restore it after the character has loaded.Does armour protect against headshots and knives?
ArmourPenetrating flag, so vests help far less against both.Ready to pick a map?
Twelve themes on three base map styles, $8 each, instant download.
Relevant to what you just read
The tool for this
Twenty theme pairs for ox_lib and ox_inventory From $15, built in the browser, exported as a drop-in resource.
Look it up while you work
The reference tables this guide leans on, free and without an account.
- All 860 ped model namesEvery model GetHashKey resolves, with its hash and how many clothing components and props it carries.