EndCore Framework

en-clothing

Character creator, barbers, and clothing as wearable inventory items that give warmth, radiation protection and bag storage.

en-clothing handles appearance and clothes. A new survivor goes through a character creator covering model, heritage, 20 face features, overlays, hair and eyes, and starts out wearing a few starter items. After that the face is fixed, except at barbers, where you can pay cash to change hair, facial hair, brows, makeup, blush, lipstick and chest hair.

Clothing isn't a free outfit menu. Every garment is an inventory item that you find, trade or loot, then wear. Wearing an item moves it out of your inventory into an equipment slot. Clothes add up to warmth for the temperature system, and some give radiation protection, like a gas mask. A worn bag gives you its own storage grid in en-inventory.

At a glance

Depends onoxmysql, en-core, en-ui, en-inventory
Start afterAll of the above
Database tablesencore_appearance, encore_worn
Config filesconfig/shared.lua; clothing items live in en-inventory/data/items.lua
Key bindsen_clothing_equipment (O), en_clothing_barber (E, at a barber)
Radial menuen-clothing:equipment under survivor
In-game builderContent kind barber in en-admin

How it works

Creator and barbers

The full creator can only be used once, while a character has no appearance saved. After that, only an admin running /appearance reopens it. Barbers can change hair and the overlays listed in Config.barberOverlays, but never the model. Appearance data larger than 12,000 characters of JSON is rejected.

Equipment slots

SlotLabelGTA part
hatHeadprop 0
maskFacecomponent 1
glassesEyesprop 1
topJacketcomponent 11
undershirtShirtcomponent 8
vestVestcomponent 9
glovesHandscomponent 3
legsLegscomponent 4
shoesFeetcomponent 6
bagBackcomponent 5

An empty slot shows the Config.bare drawable for that model.

Wearing and removing

Every item with a clothing block is registered as usable, and using it means wearing it. If the slot is already taken, the old item has to fit back into your inventory first. If space runs out mid-swap, the old item is put back on. en-inventory's equipment column calls the CanEquip, Equip and Unequip exports.

Warmth and radiation

Worn items add up to two player state bags:

  • warmth is capped at Config.warmthCap. en-weathersync uses it to reduce the pull of cold and rain chill.
  • radProtection is capped at 0.95. en-core's survival system uses it for radiation.

Configuration

KeyDefaultWhat it does
Config.keys.equipment'O'Equipment panel (opens the en-inventory page when en-inventory is started)
Config.modelsmale = 'mp_m_freemode_01', female = 'mp_f_freemode_01'Character models
Config.slots10 slots{ id, label, icon, component = n } or { id, label, icon, prop = n }
Config.bareper modelWhat each component shows when empty: [component] = { drawable, texture }
Config.starter{ 'tshirt', 'jeans', 'sneakers' } for both modelsClothes a new character wears after the creator
Config.warmthCap1.0Maximum total warmth
Config.creatorparents = 46, features (20 labels), overlays (11: id, label, palette)Creator options
Config.barbers3 barbers{ id?, label, coords = vec3, radius, price, blip? }; id defaults to config:<index>
Config.barberOverlays{ [1] = true, [2] = true, [4] = true, [5] = true, [8] = true, [10] = true }Overlay ids a barber may change

Adding a clothing item

Clothing items are defined in en-inventory's data/items.lua with a clothing block:

FieldWhat it does
slotA slot id from the table above
male, female{ drawable, texture } for base-game clothes, or { collection = 'pack_name', drawable = localIndex, texture, fallback = { drawable, texture } } for addon packs. An item with no entry for the character's model doesn't fit
arms{ male = n, female = n }, for tops: the arms drawable (component 3) that fits
warmth0–1, summed across worn items
radProtection0–1, summed across worn items

A bag also needs a container = { width, height, maxWeight } block on the item. en-inventory uses it for the worn bag's storage.

lua
-- en-inventory/data/items.lua
parka = {
    label = 'Arctic Parka', description = 'Fur-lined hood. Nothing gets through.',
    category = 'clothing', size = { 3, 3 }, weight = 1.8, usable = true, consume = 0,
    clothing = { slot = 'top', male = { 58, 1 }, female = { 73, 1 }, arms = { male = 14, female = 14 }, warmth = 0.6 },
},
hazmat_hood = {
    label = 'Hazmat Hood', description = 'Sealed and filtered.',
    category = 'clothing', size = { 2, 2 }, weight = 0.7, usable = true, consume = 0,
    clothing = { slot = 'mask', male = { 46, 0 }, female = { 46, 0 }, warmth = 0.05, radProtection = 0.6 },
},
field_pack = {
    label = 'Field Pack', description = 'Wear it to carry what\'s inside.',
    category = 'equipment', size = { 3, 3 }, weight = 1.2, usable = true, consume = 0,
    clothing = { slot = 'bag', male = { 45, 0 }, female = { 45, 0 } },
    container = { width = 6, height = 5, maxWeight = 20.0 },
},

The drawable numbers above are only examples. Use /clothingdev in game to find the right ones. See Add an item for the rest of the item fields.

Existing clothing includes tshirt (warmth 0.05), hoodie (0.2), winter_jacket (0.45), jeans, cargo_pants, sneakers, boots, beanie, bandana, gas_mask (radProtection 0.35) and work_gloves. Bags include backpack (6x4, 15 kg), duffel_bag (8x4, 25 kg) and military_rucksack (8x6, 32 kg), plus satchel, daypack, scav_pack, scav_pack_heavy, hiking_rucksack, wasteland_rucksack, tactical_pack and assault_pack from the bundled clothing pack.

Note

Addon pack drawables only stream when the server has an Element Club Argentum (or higher) key. Without one, the item's fallback drawable is used.

Exports

Server

ExportArgumentsReturns
GetWornsource{ [slot] = itemName }
GetWornEntriessource{ [slot] = { item, metadata } }
GetAppearancesourceAppearance table or nil
GetCharacterLookcitizenid{ appearance (or false), worn = { [slot] = item } }; works for offline characters
Wearsource, itemName, metadata?ok, err. Takes the item from the player's inventory and wears it; the previous item goes back to the inventory
Removesource, slotok, err. Takes the item off into the inventory
CanEquipsource, itemNameok, err, slot, with no side effects
Equipsource, itemName, metadataok, previous ({ item, metadata }) or false, err. The caller has already removed the item from wherever it was
Unequipsource, slot{ item, metadata } or nil. The caller decides where it goes
SetWornMetadatasource, slot, fieldsboolean. Merges fields into the worn item's metadata
IsLoadedsourceboolean
GetSlotsnone{ id, label, icon }[] in display order
BuilderList, BuilderGet, BuilderValidate, BuilderSchema, BuilderTemplatekind, ...For 'barber': fields label, coords, radius (0.5–25), price (cash per visit), blip

Client

ExportArgumentsReturns
ApplyLookped, lookApplies { appearance, worn } to any ped, such as a character select preview

Commands

CommandRestrictedWhat it does
/appearance [target]group.adminLets the player redo their look in the full creator
/clothingdevClient commandDeveloper tool for finding drawables (see below)

/clothingdev usage:

  • /clothingdev lists drawable ranges per slot.
  • /clothingdev packs lists clothing packs.
  • /clothingdev <slot> <drawable> [texture] previews a base-game drawable.
  • /clothingdev <slot> <pack> <drawable> [texture] previews a pack drawable and prints the { collection, drawable, texture } snippet to paste into an item.

Events

Emitted, server-local:

EventPayload
en-clothing:server:loadedsource
en-clothing:server:wornsource, slot, itemName
en-clothing:server:removedsource, slot, itemName
en-clothing:server:appearanceSavedsource, isFirstTime

Client events: en-clothing:client:dress ({ appearance, worn }) and en-clothing:client:openCreator.

State bags

KeyScopeMeaning
warmthPlayer0 to warmthCap
radProtectionPlayer0 to 0.95

Database

TableColumns
encore_appearancecitizenid (primary key), appearance (LONGTEXT JSON: model, heritage, features, overlays, hair, eyes), updated_at
encore_wornPrimary key (citizenid, slot), item, metadata (JSON)

Examples

Hand out a hazmat outfit and put it straight on:

lua
-- server
if encore.inventory.addItem(source, 'gas_mask', 1) then
    local ok, err = exports['en-clothing']:Wear(source, 'gas_mask')
    if not ok then print('Could not wear gas mask: ' .. tostring(err)) end
end

Only let players into a radiation zone with a mask on:

lua
-- server
local worn = exports['en-clothing']:GetWorn(source)

if worn.mask ~= 'gas_mask' then
    return false, 'You need a gas mask to go in there.'
end

Preview a character's look on a ped in your own selector:

lua
-- server: fetch the look for an offline character
local look = exports['en-clothing']:GetCharacterLook(citizenid)

-- client: apply it to a preview ped
exports['en-clothing']:ApplyLook(previewPed, look)