> For the complete documentation index, see [llms.txt](https://atiysus-organization.gitbook.io/aty-scripts/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://atiysus-organization.gitbook.io/aty-scripts/clothing/integrations.md).

# Integrations

## Integrations

Everything another resource needs to work with ATY Clothing: exports, menu types and category keys, events, and the qb-clothing, esx\_skin and skinchanger events it answers. Exports are called as `exports['aty_clothing']:Name(...)`.

### Client exports

| Export                                                | Returns      | What it does                              |
| ----------------------------------------------------- | ------------ | ----------------------------------------- |
| `OpenCustomMenu(menuType, categories, title, shopId)` | —            | Opens a menu with your own category list  |
| `OpenWardrobeMenu(title)`                             | —            | Saved outfits only                        |
| `OpenClothingShop(title, shopId)`                     | —            | A clothing shop with Apparel              |
| `OpenBarberShop(title, shopId)`                       | —            | A barber with hair, overlays and the face |
| `OpenTattooShop(title, shopId)`                       | —            | A tattoo parlour                          |
| `CloseMenu(restoreSkin)`                              | —            | Closes the menu                           |
| `IsMenuOpen()`                                        | boolean      | Whether the menu is open                  |
| `ResetPedAppearance()`                                | boolean      | Discards unsaved changes                  |
| `GetPedSkinData()`                                    | table or nil | The player's current skin                 |
| `ApplySkinData(skinData)`                             | —            | Applies a skin to the player's ped        |
| `GetNearbyInteraction()`                              | table or nil | The shop the player is standing at        |

#### OpenCustomMenu

| Parameter    | Default                   | Notes                                                                                                                   |
| ------------ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `menuType`   | `"creator"`               | `creator`, `clothing`, `market`, `barber` or `tattoo` — see Menu types                                                  |
| `categories` | The type's default list   | Category keys, `"category:part"` entries or pre-v3 keys                                                                 |
| `title`      | "Character Customization" | Header text. The creator always shows its own heading                                                                   |
| `shopId`     | `"custom_api"`            | Handed to `menuOpened`. Prices and shop balances ignore it — the server works out the shop from where the player stands |

```lua
-- A barber that also does facial features
exports['aty_clothing']:OpenCustomMenu("barber", { "hair", "overlays", "faceAdjust" }, "Downtown Barber", "barber_downtown")

-- A jewellery counter: one Apparel shelf
exports['aty_clothing']:OpenCustomMenu("market", { "apparel:handWrist" }, "Vangelico", "jewellery_1")
```

Calling any open export while a menu is already open does nothing.

#### Shop shortcuts

| Export                            | Type       | Categories                                   | Default title  |
| --------------------------------- | ---------- | -------------------------------------------- | -------------- |
| `OpenWardrobeMenu(title)`         | `clothing` | `wardrobe`                                   | Wardrobe       |
| `OpenClothingShop(title, shopId)` | `clothing` | `apparel`                                    | Clothing Store |
| `OpenBarberShop(title, shopId)`   | `barber`   | `hair`, `overlays`, `faceAdjust`, `faceBody` | Barber Shop    |
| `OpenTattooShop(title, shopId)`   | `tattoo`   | `tattoos`                                    | Tattoo Studio  |

#### CloseMenu and ResetPedAppearance

`CloseMenu(true)` puts the model and look back to how they were when the menu opened. `CloseMenu(false)` closes and leaves the ped as it is, previewed pieces included. Nothing is saved either way — `/refreshskin` or a reconnect brings back the saved look.

`ResetPedAppearance()` puts back the look the menu opened with and reloads the saved skin. It returns `false` when there is nothing to go back to. Use it while the menu is open: the ped is frozen again three seconds later.

#### GetPedSkinData and ApplySkinData

`GetPedSkinData()` returns the skin table plus two things the menu uses: `maxValues` (the highest valid value of each key for this ped) and `tattoo_<zone>_overlay` keys. Face and heritage values come from the character's saved data when the framework holds it, otherwise from the ped.

`ApplySkinData(skinData)` applies a skin to the **current** model — it never switches model — and redraws tattoos when the table has `tattoos`. To switch model as well, use `setPedSkin` below or the server export `LoadPlayerSkin`.

#### GetNearbyInteraction

```lua
local here = exports['aty_clothing']:GetNearbyInteraction()
-- { active = true, kind = "shop", shopType = "clothing", shopId = 3,
--   title = "Clothing Shop", coords = vector3(...) }
```

`kind` is `"shop"` or `"jobshop"`; `nil` when the player is not at a shop. Wardrobes are announced only through the `nearInteraction` event. Neither is tracked with `Config.DisplayMethod = "target"`, where the target resource handles proximity.

#### Lower-level client exports

| Export                                 | Returns      | What it does                                                                                   |
| -------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------- |
| `OpenClothingMenu(menuType, shopData)` | —            | What the open exports call. `shopData = { title, allowedCategories, shopId }`                  |
| `CloseClothingMenu(restoreSkin)`       | —            | Same as `CloseMenu`                                                                            |
| `ResetPed()`                           | boolean      | Same as `ResetPedAppearance`                                                                   |
| `getPedSkin(ped)`                      | table or nil | `GetPedSkinData` for any ped                                                                   |
| `setPedSkin(ped, skin, changeModel)`   | boolean      | Applies a full skin; switches model unless `changeModel` is `false`                            |
| `getMaxValues(ped)`                    | table or nil | Highest valid value of each key for that ped                                                   |
| `getPedClothingOnly(ped)`              | table        | Clothing and prop keys only — what the wardrobe stores                                         |
| `setPedClothingOnly(ped, clothing)`    | boolean      | Applies clothing and prop keys; face, hair and tattoos stay                                    |
| `loadPlayerClothing(skinData, ped)`    | —            | Applies a skin to a ped (the player's by default) without switching model                      |
| `setPedComponent(data)`                | boolean      | Applies one key, e.g. `{ name = "torso_1", value = 4 }`, to the player's ped. Used by the menu |

`ResetCameraFocus`, `RotatePed`, `RotatePedMouse`, `SmoothFocusCamera`, `FocusOnPoint`, `FocusAnchor`, `GetCameraState` and `StartHotspotTracking` drive the menu's preview camera and only have an effect while the menu is open.

### Server exports

| Export                                         | Returns | What it does                                |
| ---------------------------------------------- | ------- | ------------------------------------------- |
| `OpenClothingMenu(source, menuType, shopData)` | —       | Opens a menu on that player                 |
| `CloseClothingMenu(source, restoreSkin)`       | —       | Closes it and restores the look — see below |
| `LoadPlayerSkin(source)`                       | boolean | Applies the saved skin to the player's ped  |
| `GetPlayerSkin(source, cb)`                    | —       | `cb(skinData, model)` from the database     |
| `SavePlayerSkin(source, skinData)`             | boolean | Replaces the saved skin                     |
| `GetPlayerOutfits(source, cb)`                 | —       | `cb(outfits)` from the wardrobe             |

`OpenClothingMenu` takes the same `menuType` (default `"creator"`) and the same `shopData` fields as the client export: `title`, `allowedCategories`, `shopId`.

{% hint style="warning" %}
Call `CloseClothingMenu` with `restoreSkin = true`. The menu then closes about a second later and the ped goes back to how it looked when the menu opened. With `false` or no second argument the menu stays open. To close without restoring, use the client export `CloseMenu(false)`.
{% endhint %}

`LoadPlayerSkin` reads the active `playerskins` row (QBCore) or `users.skin` (ESX) and applies it on the client, switching model when needed. It returns `false` when the player has no loaded character; the database read happens after it returns.

```lua
exports['aty_clothing']:GetPlayerSkin(source, function(skinData, model)
    if not skinData then return end -- nothing saved
    print(model, json.encode(skinData))
end)
```

`SavePlayerSkin` replaces the stored skin — on QBCore it becomes the active `playerskins` row for its model, on ESX it overwrites `users.skin`. The model comes from `model`, then `ped`, then `sex`. It returns `true` once the write is queued, not when it has finished, and it does not touch the ped: call `LoadPlayerSkin` to show the result.

```lua
exports['aty_clothing']:GetPlayerOutfits(source, function(outfits)
    for _, outfit in ipairs(outfits) do
        local clothes = json.decode(outfit.skin) -- clothing and prop keys
        print(outfit.outfitId, outfit.outfitname, outfit.model)
    end
end)
```

Each row is a `player_outfits` record: `id`, `citizenid`, `outfitname`, `model`, `skin` (JSON), `outfitId` and `created_at`.

### Menu types and categories

#### Menu types

| Type       | Default categories                                                                | Checkout                                                             |
| ---------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `creator`  | `pedSelector`, `faceBody`, `faceAdjust`, `overlays`, `hair`, `apparel`, `tattoos` | **Save Character** — free unless the player is within 30 m of a shop |
| `clothing` | `wardrobe`, `apparel`, `hair`, `overlays`                                         | **Purchase**                                                         |
| `market`   | `apparel`                                                                         | **Purchase**                                                         |
| `barber`   | `hair`, `overlays`                                                                | **Purchase**                                                         |
| `tattoo`   | `tattoos`                                                                         | **Purchase**                                                         |

The defaults apply when an export or `/skin` passes no list. Shops placed in `config.lua` use `Config.ShopCategories` for their `shopType`. A purchase charges only for changed clothing slots — see Prices.

#### Category keys and parts

`"category:part"` offers one group of a category. Several parts of the same category add up, and a bare `"category"` in the same list wins over any narrowing.

| Key           | Contains                                                      | Parts                                                                                                                         |
| ------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `pedSelector` | Model list; dropped when `Config.PedSelector.Enabled = false` | —                                                                                                                             |
| `faceBody`    | Sex, character presets, heritage, skin tone, eye colour       | `sex`, `presets`, `heritage`, `skin`, `eyecolor`                                                                              |
| `faceAdjust`  | Face shape                                                    | `nose`, `brow`, `cheeks`, `eyes`, `lips`, `jaw`, `chin`, `neck`                                                               |
| `overlays`    | Skin and makeup overlays                                      | `blemishes`, `ageing`, `makeup`, `blush`, `complexion`, `sundamage`, `lipstick`, `moles`, `bodyblemishes`, `addbodyblemishes` |
| `hair`        | Hair and its colour, eyebrows, beard, chest hair              | `hairs`, `eyebrows`, `beard`, `chest`                                                                                         |
| `apparel`     | Five shelves of clothing and accessories                      | A shelf or a group — see below                                                                                                |
| `tattoos`     | Six zones                                                     | `tattooHead`, `tattooTorso`, `tattooLeftArm`, `tattooRightArm`, `tattooLeftLeg`, `tattooRightLeg`                             |
| `wardrobe`    | Saved outfits                                                 | —                                                                                                                             |

| Apparel shelf | Groups on it                                          |
| ------------- | ----------------------------------------------------- |
| `outfits`     | `outfitsCasual`, `outfitsWork`, `outfitsActivity`     |
| `clothing`    | `torso`, `tshirt`, `decals`, `arms`, `pants`, `shoes` |
| `upperBody`   | `chains`, `bproof`, `bags`                            |
| `handWrist`   | `watches`, `bracelets`                                |
| `head`        | `helmet`, `glasses`, `ears`, `mask`                   |

A `Config.Slots` menu is the group `slot_<key>` (for example `slot_gloves`) on the shelf its `section` names.

```lua
{ "hair:beard" }                                      -- beard trimmer
{ "apparel:handWrist", "apparel:head" }               -- accessories counter
{ "tattoos:tattooLeftArm", "tattoos:tattooRightArm" } -- arm work only
```

#### Pre-v3 keys

Keys from before v3 still work, in config and in exports:

| Old key                  | Opens                                             |
| ------------------------ | ------------------------------------------------- |
| `dna`                    | `faceBody`, `faceAdjust`                          |
| `face`                   | `faceAdjust`                                      |
| `hairs`                  | `hair`, `overlays`                                |
| `clothes`, `accessories` | `apparel`                                         |
| `tattoo`                 | `tattoos`                                         |
| `headTattoo`             | `tattoos:tattooHead`                              |
| `bodyTattoo`             | `tattoos:tattooTorso`                             |
| `armsTattoo`             | `tattoos:tattooLeftArm`, `tattoos:tattooRightArm` |
| `legsTattoo`             | `tattoos:tattooLeftLeg`, `tattoos:tattooRightLeg` |

The old tattoo tabs keep their exact zones, so a parlour set up to sell only head tattoos does not start selling leg tattoos.

### Events and hooks

Local client events, for `AddEventHandler`:

| Event                                 | Arguments            | When                                           |
| ------------------------------------- | -------------------- | ---------------------------------------------- |
| `aty_clothing:client:menuOpened`      | `menuType, shopData` | The menu opens                                 |
| `aty_clothing:client:menuClosed`      | `menuType`           | The menu starts closing                        |
| `aty_clothing:client:nearInteraction` | `data`               | The player enters or leaves a shop or wardrobe |

`nearInteraction` sends `{ active = false }` on leaving. On entering, `data.kind` is `"shop"`, `"jobshop"`, `"wardrobe"` or `"jobwardrobe"`; shops add `shopType` and `shopId`, wardrobes `wardrobeId`, and both carry `title` and `coords`. It fires on changes only, and not with `Config.DisplayMethod = "target"`.

After a creator save is stored, the server also sends `aty_clothing:client:characterSaved` with the saved skin. It is a network event: register it with `RegisterNetEvent` to listen.

Without writing a resource, the same moments are reachable from `config.lua`. A hook that errors is caught and printed, and cannot break the menu:

```lua
Config.Hooks = {
    OnMenuOpen = function(menuType, shopData) --[[ e.g. hide your HUD ]] end,
    OnMenuClose = function(menuType) --[[ e.g. show it again ]] end,
}
```

### Compatibility with other clothing resources

ATY Clothing answers the events below. It does not stand in for another resource's **exports** (the manifest provides none), so code that calls another clothing resource's exports has to move to the exports on this page. Events that are not listed, such as `skinchanger:getSkin` or `qb-clothing:client:openMenu`, are not handled.

#### qb-clothing

Active while `Config.QBClothingEvents.Enabled = true`. Only `Enabled` is read; the `Events` list beside it changes nothing.

| Event                                                     | Side            | What happens                                                             |
| --------------------------------------------------------- | --------------- | ------------------------------------------------------------------------ |
| `qb-clothes:client:CreateFirstCharacter`                  | Client          | Sets the freemode model for the character's gender and opens the creator |
| `qb-clothing:client:loadPlayerClothing` (`data, ped`)     | Client          | Applies a skin table to a ped — multicharacter previews                  |
| `qb-clothes:loadSkin` (`_, model, data`)                  | Client          | Sets the model (a hash) and applies the skin (JSON)                      |
| `qb-clothing:saveSkin` (`model, skin`)                    | Server          | Saves the skin (JSON) as the active `playerskins` row. QBCore only       |
| `qb-clothes:loadPlayerSkin`, `qb-clothing:loadPlayerSkin` | Server          | Loads the saved skin onto the player. QBCore only                        |
| `qb-clothes:saveOutfit` (`name, model, skinData`)         | Server          | Passed to the wardrobe's own save                                        |
| `qb-clothing:server:removeOutfit` (`name, outfitId`)      | Server          | Passed to the wardrobe's own delete                                      |
| `qb-clothing:server:getOutfits`                           | Server callback | Returns the player's outfits                                             |

#### esx\_skin and skinchanger

Active with `Config.Framework = "esx"`.

| Event                                          | What happens                                                                                                       |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `skinchanger:loadSkin` (`skin, cb`)            | Applies the skin, then calls `cb`                                                                                  |
| `esx_skin:openSaveableMenu`                    | Sets the freemode model for the player's sex and opens the creator. It does not call the save callback it is given |
| `esx:playerLoaded` (`playerData, isNew, skin`) | Applies `skin` for a returning character. With `Config.Multicharacter = false` it loads the saved skin itself      |

When the creator saves on ESX, the client fires `skinchanger:loadSkin` and `esx_skin:characterSaved` for freemode models. With `Config.TriggerPlayerLoadedOnCreation = true` the framework's player-loaded events fire as well, on QBCore and ESX; the server-side one only once per connection.

### Examples

#### Multicharacter: a new character

```lua
-- Client
local creating = false

-- Your multicharacter has just created a character
RegisterNetEvent('my_multicharacter:client:newCharacter', function(gender)
    local model = gender == 1 and `mp_f_freemode_01` or `mp_m_freemode_01`
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(0) end
    SetPlayerModel(PlayerId(), model)
    SetModelAsNoLongerNeeded(model)

    creating = true
    exports['aty_clothing']:OpenCustomMenu("creator", {
        "faceBody", "faceAdjust", "overlays", "hair", "apparel", "tattoos",
    })
end)

-- Continue once the save has reached the database
RegisterNetEvent('aty_clothing:client:characterSaved', function(savedSkin)
    if not creating then return end
    creating = false
    -- spawn the player, show your HUD, ...
end)
```

The menu closes on its own right after `characterSaved`. `menuClosed` fires too, but also when the player leaves without saving. A QBCore multicharacter that already triggers `qb-clothes:client:CreateFirstCharacter` needs no change.

#### House or motel wardrobe

```lua
-- Client: inside your closet interaction
if nearCloset and IsControlJustPressed(0, 38) then -- E
    exports['aty_clothing']:OpenWardrobeMenu("House Wardrobe")
end
```

#### Admin edit

```lua
-- Server
RegisterCommand('editlook', function(source, args)
    local target = tonumber(args[1])
    if not target or not GetPlayerName(target) then return end

    exports['aty_clothing']:OpenClothingMenu(target, "creator", {
        allowedCategories = { "faceBody", "faceAdjust", "overlays", "hair", "apparel", "tattoos" },
    })
end, true) -- restricted: needs the command.editlook ACE
```

`creator` is used so the change is saved without charging the player (unless they stand within 30 m of a shop). `/skin [id] creator` does the same without any code.

#### Your own prompt

```lua
-- config.lua: Config.DisplayMethod = "none"
AddEventHandler('aty_clothing:client:nearInteraction', function(data)
    if data.active then
        exports['my_prompt']:show(('[E] %s'):format(data.title or 'Clothing'))
    else
        exports['my_prompt']:hide()
    end
end)
```

With `"none"` only the built-in prompt is hidden: the zones and the **E** key still open the shop or wardrobe.

### Menu presentation

| Setting                         | Default | Effect                                                                                                               |
| ------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `Config.UseScreenFade`          | `true`  | The screen fades out (400 ms) while the ped and camera are set up and back in (600 ms), on opening and on closing    |
| `Config.HideMinimap`            | `true`  | The minimap is hidden while the menu is open, and shown again on close only if it was visible before                 |
| `Config.Camera.FocusOnCategory` | `false` | `true`: picking a category moves the camera to that part of the body. Clicking a marker on the character always does |

While the menu is open the ped is frozen, invincible and holds a still idle pose, so ambient animations and head turns do not move it. Freezing and invincibility end when the menu closes. If the resource stops while a menu is open, the minimap, focus, freeze and pose are all restored, so a restart never leaves a player stuck.
