# Configuration

Koja Appearance reads three config files:

| File | What is in it |
| --- | --- |
| `shared/koja_config.lua` | Everything added by the redesign: `Config.Locale` and `Config.Koja.*` |
| `shared/koja_shops.lua` | Shopkeepers: `Config.Koja.Shops` |
| `shared/config.lua` | The upstream illenium-appearance options (prices, stores, clothing rooms, outfits, blacklists) |

Restart the resource after changing any of them.

## General

| Option | Default | What it does |
| --- | --- | --- |
| `Config.Locale` | `"en"` | Language: `ar`, `bg`, `cs`, `de`, `en`, `es-ES`, `fr`, `hu`, `id`, `it`, `nl`, `pl`, `pt-BR`, `ro-RO`, `zh-CN`, `zh-TW`. The convar `illenium-appearance:locale` wins when set |
| `Config.Koja.AutoMigrateESXSkins` | `true` | ESX: converts old `esx_skin` / `skinchanger` skins in `users.skin` on start. Originals are kept in `koja_appearance_skin_backup` |
| `Config.Koja.SkinCommands` | `{ "skin" }` | Extra admin commands that open the full character creator, like `/pedmenu` |

## Branding and theme

```lua title="shared/koja_config.lua"
Config.Koja.ServerName = "YOUR SERVER NAME"
Config.Koja.Logo = nil -- url or nil
Config.Koja.Currency = "$"

Config.Koja.PedImageUrl = "https://docs.fivem.net/peds/%s.webp"

Config.Koja.Theme = {
    accent = "#FF9B00",
    background = "#121212",
    corners = "rounded", -- "rounded" | "sharp"
}
```

| Option | What it does |
| --- | --- |
| `ServerName`, `Logo` | Shown at the top of the left rail. `ServerName` is also the title of notifications |
| `Currency` | Symbol in front of every price |
| `PedImageUrl` | Preview image of each ped model. `%s` is replaced by the model name |
| `Theme` | The default colours and corner style. Players can change them in the Studio; their choice is saved on their PC |

## Studio

```lua title="shared/koja_config.lua"
Config.Koja.Studio = {
    Enabled = true,
    PrivateBucket = true,
    Default = 1,
    Locations = {
        { label = "Civil", coords = vec4(-797.6995, 328.4894, 190.7158, 2.3068) },
        { label = "Gangster", coords = vec4(71.1171, -1399.3535, 29.3761, 315.9135) },
        { label = "Exclusive", coords = vec4(-703.8611, -151.9658, 37.4151, 159.8186) },
    },
}
```

| Option | What it does |
| --- | --- |
| `Enabled` | Shows the studio locations in the Studio panel |
| `PrivateBucket` | Moves the player to their own routing bucket while at a studio. The bucket is `StudioBucketBase` + server id |
| `Default` | Fallback location (index in `Locations`), used only when the player's old coords are missing. Opening the menu never teleports on its own |
| `Locations` | Studios players can pick. "Your old coords" is always the first entry |
| `Config.Koja.StudioBucketBase` | `7000` — base of the private bucket numbers |
| `Config.Koja.PrefsKvp` | `"koja_appearance_prefs"` — client KVP key of the per-player Studio settings |

:::hint{type="info" title="No ghosting"}
The private bucket is kept only while the player stays within 60 m of a studio location.
Leaving the studio, disconnecting or stopping the resource restores the previous bucket.
:::

### Studio light

```lua title="shared/koja_config.lua"
Config.Koja.Light = {
    enabled = false,
    power = 4.0,
    x = 0.0, y = 1.6, z = 0.6, -- offset from the ped
    color = "#FFFFFF",
}
```

The default light. Players change it in the Studio panel.

### Showcase tools

| Option | Default | What it does |
| --- | --- | --- |
| `Showcase.BackdropColor` | `"#1A1A1A"` | Default backdrop colour |
| `Showcase.BackdropSize` | `5.0` | Half size, in metres, of the backdrop box drawn around the ped |
| `Showcase.SpinSpeed` | `30.0` | Turntable speed in degrees per second |
| `Showcase.KeepArms` | jackets, undershirts, vests, arms | Item view keeps the arms for these categories, so tops do not float over a bare body |
| `Showcase.KeepHair` | hair, hats, masks, glasses, ears, beard | Item view keeps the hair for these categories |
| `Showcase.Weathers` | 13 weathers | Weathers offered in **Time & weather**. Set one to `false` to hide it |

## Safety while customising

```lua title="shared/koja_config.lua"
Config.Koja.Safety = {
    Enabled = true,
    FreezeNeeds = true,
    Statuses = { "hunger", "thirst", "stress" },
}
```

While the menu or studio is open the player takes no damage (no drowning or falling) and the statuses in `Statuses` do not drop.
On ESX they are frozen through `esx_status` on the client; on QBCore and QBox through player metadata on the server.

## Mask clipping fix

```lua title="shared/koja_config.lua"
Config.Koja.MaskFix = {
    Enabled = true,
    Threshold = 0.1,
    Interval = 500,
    IgnoreDrawables = { male = {}, female = {} }, -- e.g. male = { [51] = true }
}
```

Face features (nose, brows, cheeks, jaw) push the face through masks.
While a mask is worn the face shape is flattened to neutral and it comes back when the mask comes off — also for masks put on by other resources.
The saved appearance always keeps the real face.

| Option | What it does |
| --- | --- |
| `Threshold` | The fix only kicks in when at least one face feature is further than this from 0 |
| `Interval` | Milliseconds between checks for masks changed by other resources |
| `IgnoreDrawables` | Mask drawables that never clip (bandanas, small mouth masks). Flattening is skipped for them |

## Inheritance faces

```lua title="shared/koja_config.lua"
Config.Koja.ExtraFaces = {
    -- { gender = "female", id = 46, label = "Anna", image = "https://..." },
}
```

Extra parent faces streamed by another resource. They are shown after the GTA faces.

## Shop basket and prices

```lua title="shared/koja_config.lua"
Config.Koja.Shop = {
    Basket = true,
    DefaultPrice = 0,
    PaymentMethods = { "cash", "bank" },
    AutoPrices = {
        Enabled = true,
        Step = 5,
        FreeFirst = true,
        Categories = {
            masks = { 80, 600 },
            jackets = { 150, 1200 },
            -- ...
        },
    },
}
```

| Option | What it does |
| --- | --- |
| `Basket` | Pay per item in clothing shops. With `false`, the flat upstream `Config.ClothingCost` is charged |
| `DefaultPrice` | Price of an item with no saved price and no auto price. `0` = FREE |
| `PaymentMethods` | `"cash"` and / or `"bank"` |
| `AutoPrices.Enabled` | Gives every item without a saved price a fixed price between `min` and `max` of its category. The same item always gets the same price |
| `AutoPrices.Step` | Generated prices are rounded to this |
| `AutoPrices.FreeFirst` | Drawable 0 (usually "none" or the basic item) is free |
| `AutoPrices.Categories` | `category = { min, max }`. A category not listed uses `DefaultPrice` |

The price of an item is, in this order:

1. The price saved in the [Capture panel](/new/appearance/capture-panel) for that texture (`drawable_texture`).
2. The price saved for the drawable.
3. The auto price of the category.
4. `DefaultPrice`.

Barber, tattoo and surgeon shops keep the upstream `Config.BarberCost`, `Config.TattooCost` and `Config.SurgeonCost`.

## Clothing as items

```lua title="shared/koja_config.lua"
Config.Koja.ClothingItems = {
    Enabled = false,
    Item = "clothing",
}
```

With `Enabled = true`, every paid piece bought in the basket is also given as one ox_inventory item. See [Features](/new/appearance/features#clothing-as-items) for the item definition.

## Wardrobe hub

```lua title="shared/koja_config.lua"
Config.Koja.Hub = {
    Enabled = true,
    Prompt = true,
    CloseOnWear = false,
}
```

| Option | What it does |
| --- | --- |
| `Enabled` | The NUI wardrobe hub replaces the ox_lib menus and dialogs of clothing shops, `/pedmenu`, clothing rooms, outfit rooms and boss managed outfits. `false` brings the ox_lib menus back |
| `Prompt` | A styled key-cap pill instead of `lib.showTextUI` in zones. Works without the hub too |
| `CloseOnWear` | Closes the hub after putting on an outfit |

## Camera and Bones 3D

```lua title="shared/koja_config.lua"
Config.Koja.CameraLimits = {
    minDistance = 0.35,
    maxDistance = 3.5,
    yawSpeed = 0.35,     -- degrees per dragged pixel
    pitchSpeed = 0.004,  -- metres of camera height per dragged pixel
    minHeight = -1.0,
    maxHeight = 1.0,
    smoothing = 12.0,    -- higher = snappier
}
```

`Config.Koja.BoneMarkers.Interval` (ms) sets how often the Bones 3D markers update.
`Config.Koja.BoneMarkers.Offsets` moves the marker of a category away from its bone (`vec3`, x right, y forward, z up), so categories that share a bone get separate markers.

## Capture panel

```lua title="shared/koja_config.lua"
Config.Koja.Capture = {
    Enabled = true,
    AdminGroup = "group.admin",
    FrameworkGroups = { "admin", "superadmin", "owner", "god", "dev" },
    ScreenshotResource = "screenshot-basic",
    Coords = vec4(-1000.0, -1000.0, 1000.0, 0.0),
    GreenScreenColor = { 0, 255, 0 },
    DefaultOffsets = { fov = 30.0, height = 0.0, rotation = 0.0, distance = 1.2 },
    ImageSize = 256,
    SettleMs = 150,
    TextureSettleMs = 60,
    -- InvisibleBody, Models, Pose, Ghost ...
}
```

| Option | What it does |
| --- | --- |
| `Enabled` | Shows the **Admin Panel** button to admins |
| `AdminGroup` | Ace that counts as admin |
| `FrameworkGroups` | ESX group / QBCore permission that also counts as admin |
| `ScreenshotResource` | Name of the screenshot-basic resource |
| `Coords` | Where the ped is shot. High in the sky; the green screen is drawn around the ped |
| `GreenScreenColor` | RGB of the green screen |
| `DefaultOffsets` | Camera of a category with no saved preset |
| `ImageSize` | Output size in pixels (square) |
| `SettleMs` / `TextureSettleMs` | Wait after a new drawable / texture before the shot. Raise them if images come out blurry or half loaded |
| `InvisibleBody` | Empty slots (drawable `-1`) during shots, so a shirt shows no arms, legs or feet around it. `head = { drawable, texture }` if you stream an invisible head |
| `Models` | The **Select ped** list. `gender` is the image set the shots go to. Any freemode-compatible add-on ped works |
| `Pose` | Animations held during shots. The first one that plays is used |
| `Ghost` | Ghost shots: clothes only, no body. See below |

### Ghost shots

| Option | Default | What it does |
| --- | --- | --- |
| `Ghost.Default` | `true` | Ghost toggle state when the panel opens |
| `Ghost.ReferenceSettleMs` | `150` | Wait after emptying the slot before the reference shot |
| `Ghost.Threshold` | `24` | Colour difference (0–255) still treated as the same pixel |
| `Ghost.CacheReference` | `false` | One reference per category. Faster, but GTA's auto exposure can drift |
| `Ghost.KeepHead` | hair, beard | Categories shot with the head |
| `Ghost.ToneTest` | `false` | Extra light / dark skin shot to cut out the body. Off by default; it left outlines |
| `Ghost.SkinLight`, `SkinDark`, `ToneThreshold` | `0`, `19`, `28` | Settings of the tone test |
| `Ghost.Face` | `"mood_sleeping_1"` | Facial idle while shooting (closed eyes, no eyeballs in mask holes) |
| `Ghost.Debug` | `false` | Saves the raw frames of every shot to `capture_debug/`. Tuning only, 1–2 MB per shot |

### Upload limits

```lua title="shared/koja_config.lua"
Config.Koja.CaptureExtra = {
    FivemanageUploadUrl = "https://api.fivemanage.com/api/v3/file/base64",
    FivemanageDeleteUrl = "https://api.fivemanage.com/api/v3/file/",
    MaxPngBytes = 1500000,
    HttpTimeoutMs = 20000,
    StoresPerSecond = 20,
}
```

`MaxPngBytes` rejects bigger images, `StoresPerSecond` limits stored images per admin per second.

### Tattoo capture

| Option | Default | What it does |
| --- | --- | --- |
| `TattooCapture.Enabled` | `true` | Tattoo zones in the Capture panel. The editor still shows existing images when `false` |
| `TattooCapture.SkinTone` | `12` | Skin tone while shooting (0–45). `nil` keeps the player's skin |
| `TattooCapture.Layers` | `1` | How many times the tattoo is applied. More = darker ink |
| `TattooCapture.Naked` | per gender | Components of the naked body used while shooting |
| `TattooCapture.BaldHead` | `true` | No hair while shooting, so head and neck tattoos are visible |
| `TattooCapture.Views` | `{}` | Camera views per zone, e.g. `ZONE_TORSO = { 0 }` for the front only |
| `TattooCapture.ViewSettleMs` | `40` | Wait after moving the camera to the next view |
| `TattooCapture.InkOnly` | `true` | "Flash" images: only the ink, no body |
| `TattooCapture.InkThreshold` | `10` | Colour difference still treated as bare skin |
| `TattooCapture.InkSettleMs` | `60` | Wait after removing / re-applying the tattoo |
| `TattooCapture.Paper` | `nil` | Background colour of flash images, e.g. `"#FFFFFF"`. `nil` = transparent |

## Shopkeepers

`shared/koja_shops.lua`:

```lua title="shared/koja_shops.lua"
Config.Koja.Shops = {
    Enabled = true,
    Interaction = "auto", -- "auto" | "target" | "marker"
    Prompt = "hub",       -- "hub" | "koja-lib" | "ox_lib"
    Key = 38,
    KeyLabel = "E",
    SpawnDistance = 45.0,
    InteractDistance = 1.6,
    TargetDistance = 2.5,
    -- Marker, Talk, Types, Locations ...
}
```

| Option | What it does |
| --- | --- |
| `Enabled` | Shopkeepers replace the zones, target boxes and blips of `Config.Stores` |
| `Interaction` | `auto` = ox_target / qb-target on the ped when one runs, otherwise a marker. `target` = always target (marker when no target resource runs). `marker` = floor marker and key prompt |
| `Prompt` | Key prompt of marker mode: `hub` (built-in pill), `koja-lib` (koja-lib Text UI) or `ox_lib` (`lib.showTextUI`) |
| `Key`, `KeyLabel` | Control id and label of the interaction key |
| `SpawnDistance` | Clerks are spawned locally only while the player is this close |
| `InteractDistance`, `TargetDistance` | How close the player must be in marker / target mode |
| `Marker` | `Type`, `Size`, `Color`, `DrawDistance`, `Offset` (metres in front of the clerk) |
| `Talk` | The short greeting before the menu: `Enabled`, `Camera` (over the shoulder), `DurationMs`, `Speech` |
| `Types` | Per shop type (`clothing`, `barber`, `tattoo`, `surgeon`): clerk `model`, idle `scenario`, target `icon`, optional `label` |
| `Locations` | `{ type, label, ped = vec4(...) }`. Optional per location: `model`, `scenario`, `blip = false`, `interact = vec3` |

Prompt texts and blip names come from the locale (`UI.koja.shops.talk.<type>`, `UI.koja.shops.blip.<type>`).

:::hint{type="success" title="Placing a clerk"}
Stand where the clerk should stand — behind the counter, facing the customers — and type `/shopped clothing` (or `barber`, `tattoo`, `surgeon`) as admin.
The ready config line is copied to your clipboard; paste it into `Locations`.
:::

## Upstream options

The options of `shared/config.lua` work as in illenium-appearance. A few the redesign relies on:

| Option | Note |
| --- | --- |
| `Config.ClothingCost` | Charged only when `Config.Koja.Shop.Basket = false` |
| `Config.ChargePerTattoo` | `false` by default in this fork: applying tattoos in the menu is free. `true` = `Config.TattooCost` per tattoo, or `cost` in `shared/tattoos.lua` |
| `Config.EnablePedMenu`, `Config.PedMenuGroup` | `/pedmenu`. Access is checked with the admin rights above |
| `Config.EnableJobOutfitsCommand` | Enables `/joboutfits` and `/gangoutfits` |
| `Config.BossManagedOutfits` | Boss managed job / gang outfits, shown in the wardrobe hub |
| `Config.Stores` | Not used while `Config.Koja.Shops.Enabled = true` |
