Exports, events & hooks
The Lua code is encrypted by escrow, but these exports, events and config hooks let your other scripts work with ZAIDERS Appearance.
Client exports
openShop
Opens a menu wherever the player is: a housing wardrobe, a job locker, an ox_target zone on a shop.
exports.zaiders_appearance:openShop('clothing') -- the clothing store
exports.zaiders_appearance:openShop('barber') -- the barber shop
exports.zaiders_appearance:openShop('tattoo') -- the tattoo shop
exports.zaiders_appearance:openShop('wardrobe') -- outfits and uniforms
exports.zaiders_appearance:openShop('wardrobe', { tab = 'job' }) -- straight to the job's uniforms
exports.zaiders_appearance:openShop('wardrobe', { tab = 'gang' }) -- straight to the gang's uniformsIt returns true when the kind is valid and the menu is on its way, and false for an unknown kind. The usual rules still apply: the player can't be dead, downed or cuffed, Config.CanOpenMenu is asked, and the server checks every payment and save, wherever the menu was opened.
-- An ox_target zone that opens the wardrobe of a police station
exports.ox_target:addBoxZone({
coords = vec3(461.5, -998.1, 30.6),
size = vec3(2, 2, 2),
options = {
{
label = 'Locker room',
icon = 'fa-solid fa-shirt',
groups = 'police',
onSelect = function()
exports.zaiders_appearance:openShop('wardrobe', { tab = 'job' })
end,
},
},
})isOpen
if exports.zaiders_appearance:isOpen() then
-- a menu is open, or opening
endsyncsTattoos
Whether ZAIDERS Appearance is showing every player's tattoos to the others (see What other players see). A tattoo pack with its own sync script can stand aside while it says true, so the two don't put tattoos on the same players:
local ok, on = pcall(function() return exports.zaiders_appearance:syncsTattoos() end)
if ok and on then
-- ZAIDERS Appearance shows everyone's tattoos, with their layers: leave other players' peds alone
endsetTattooBusy
For another resource's tattoo shop, while this menu doesn't keep the tattoos: while it says true (60 seconds at most), what the player tries on there isn't sent to the others. Menus with NUI focus or a scripted camera are already detected; call it if yours has neither.
exports.zaiders_appearance:setTattooBusy(true) -- your tattoo shop opened
exports.zaiders_appearance:setTattooBusy(false) -- and closedgetAppearance, setAppearance and reloadSkin
A menu of saved characters or outfits can read and put on the whole look (face, hair, makeup and clothes) in the format the server saves. What goes on this way isn't saved: what a player keeps goes through the shops.
local look = exports.zaiders_appearance:getAppearance() -- the player's look (or getAppearance(ped))
local ok = exports.zaiders_appearance:setAppearance(look) -- with its model, and the saved tattoos back on
local ok2 = exports.zaiders_appearance:reloadSkin() -- the player's saved look back onsetAppearance and reloadSkin return true if the look went on. While this menu (or the photo studio) is open, both answer false: the player's ped is the menu's then.
Character height
The character height comes off out of the box. Then getHeight returns nil, pauseHeight does nothing and zaiders_appearance:heightChanged never fires.
getHeight
A player's height in cm, as the server set it, for the ped this game sees (nil: not around, not the freemode man or woman, or height off):
local cm = exports.zaiders_appearance:getHeight(GetPlayerServerId(PlayerId())) -- 172pauseHeight
Your character at the game's height on every screen while any resource has it paused: for a sitting emote, so nobody sinks into a chair. See Character height.
exports.zaiders_appearance:pauseHeight(true) -- sitting down
exports.zaiders_appearance:pauseHeight(false) -- standing upbaseCoords
Where a ped stands without the height's lowering. A short character is drawn lower, so use this instead of GetEntityCoords to save a position:
local coords = exports.zaiders_appearance:baseCoords(PlayerPedId())Server exports
ownsPed
Whether a player's current character owns a ped of the Peds shop (bought, given or redeemed with a code):
local owns = exports.zaiders_appearance:ownsPed(source, 'a_c_chop') -- true or falseisInStudio
Whether a player is in the photo studio now, as the server knows it. Use it to exempt that admin from anti-cheat and AFK checks: see The photo studio.
local inStudio = exports.zaiders_appearance:isInStudio(source) -- true or falsewearOutfit
For another wardrobe or outfits menu (its server side): one of its outfits that the player keeps goes on the way one of this wardrobe does. Its clothes go over the saved look (the face and the hair stay) and are saved, with the same check for garments only for a job, a gang or VIP. Your script vouches that the outfit is the player's: read it from your database, not from the player's game.
-- outfit: { model, components, props }, as a table or JSON text; the name is for the log
local result = exports.zaiders_appearance:wearOutfit(source, outfit, 'Party')
if result.ok then
-- result.skin: the saved look (JSON). Put it on in the player's game with setAppearance
else
-- result.message: why not, in the server's language (result.reason says which case)
endgetHeight (server)
A player's character height, for the sex of their ped now (nil without a character or with height off):
local h = exports.zaiders_appearance:getHeight(source) -- { cm = 172, scale = 0.95, sex = 'f' }Client events
HUD
Fired when a menu or the panel opens and closes. Your HUD can listen to it to hide itself:
-- hidden: true when a menu opens, false when it closes
-- menu: 'clothing', 'barber', 'tattoo', 'wardrobe', 'creator' or 'panel'
AddEventHandler('zaiders_appearance:hud', function(hidden, menu)
-- your HUD here
end)If your HUD has an export to show and hide it, you don't need code: add it to Config.HideHud.resources. See Config reference.
Creator closed
Fired when the character creator closes, normally right after the new character is saved:
AddEventHandler('zaiders_appearance:creator:closed', function()
-- for example, show your spawn selector
end)Model changed
Fired when this menu changes the player's model for good: a ped or the other sex kept in a shop, a look loaded with another model, a new character's sex. Not while a model is only tried on. The new ped has no tattoos, so another tattoo resource can put its own back:
AddEventHandler('zaiders_appearance:modelChanged', function(ped, model)
TriggerEvent('my_tattoos:applyOwnedTattoos') -- your tattoo resource's own event
end)On ESX, while another resource keeps the tattoos and skinchanger isn't running, it also fires skinchanger's own skinchanger:modelLoaded, which ESX tattoo shops already listen to (not with Config.Compat.exports = false).
Height changed
Only with the character height on. Fired when a player's height changes (the creator, an accepted request), on every game that hears it:
AddEventHandler('zaiders_appearance:heightChanged', function(serverId, value)
-- value = { m = cm or nil, f = cm or nil }: nil is that sex's usual height
end)The photo studio
While an admin takes the catalog photos, their character is invisible, invincible and still, far away and high over the sea, often for hours. Anti-cheats, AFK kicks and other scripts can tell, and leave that player alone.
To exempt that player, ask the server. isInStudio is true while the server has that player in the studio, and only the server decides it:
-- server
if exports.zaiders_appearance:isInStudio(source) then
return -- in the photo studio: skip the teleport, godmode, invisibility and AFK checks
endOr keep your own list with the server event, which fires when the admin enters (true) and when they leave or drop (false):
-- server
AddEventHandler('zaiders_appearance:studio', function(src, inside)
-- src: the player's server ID
end)The admin's own game gets a client event too, for things that only happen in that game (a HUD, a weather script):
-- client, in the admin's own game
AddEventHandler('zaiders_appearance:studio', function(inside)
end)The player's state bag Player(source).state.zaidersStudio also says true while they are in the studio, but it's only for information: a player's game can write its own state bag, so never use it to exempt anyone from an anti-cheat or AFK check.
Weather and time scripts
The studio keeps its own noon light. To pause your weather or time script meanwhile, add its client events to Config.Studio.syncPause: enter fires when the admin enters the studio and leave when they leave.
Config.Studio = {
-- ...
syncPause = {
{ enter = 'qb-weathersync:client:DisableSync', leave = 'qb-weathersync:client:EnableSync' }, -- the default
{ enter = 'my-weather:pause', leave = 'my-weather:resume' },
},
}A script with no such event, such as vSync, has to be stopped while you take photos, or made to leave the admin alone (with the client event above, for example). F8 warns when another script fights the studio's light.
Config hooks
shared/config.lua is open, so you can write small functions there that run in the player's game.
Your notifications
Config.Notify = function(text, kind)
-- kind: 'inform', 'success' or 'error'
exports['my_notify']:Notify(text, kind)
endYour own rule to open the menus
Config.CanOpenMenu = function()
-- return false to keep the menu closed, for example during a police chase
return not LocalPlayer.state.inChase
endYour HUD
Config.HideHud = {
enabled = true,
resources = {
{ resource = 'my-hud', export = 'SetHudVisible' }, -- gets false on open, true on close
{ resource = 'other-hud', event = 'other-hud:toggle', hide = false, show = true },
{ resource = 'third-hud', export = 'SetVisible', changed = 'third-hud:changed' },
},
custom = function(hidden, menu)
-- anything else
end,
}Exports of other clothing menus
ZAIDERS Appearance also answers the exports and events of illenium-appearance, fivem-appearance, qb-clothing and ESX's skinchanger and esx_skin, while those resources aren't running. Scripts made for them work without changes. See Frameworks & compatibility.
The appearance format
Looks are saved in QBCore's appearance format, the same JSON illenium-appearance and qb-clothing use, in playerskins.skin (QBCore and Qbox) or in skinchanger's format in users.skin (ESX). In the menu, Peds → Character JSON copies a character's look in that format. Admins can also paste one there.
