Koja Scripts

Configuration

Every option in shared/koja_config.lua and shared/koja_shops.lua: branding, studio, safety, mask fix, prices, hub, camera, capture and shopkeepers.

Configuration

Koja Appearance reads three config files:

FileWhat is in it
shared/koja_config.luaEverything added by the redesign: Config.Locale and Config.Koja.*
shared/koja_shops.luaShopkeepers: Config.Koja.Shops
shared/config.luaThe upstream illenium-appearance options (prices, stores, clothing rooms, outfits, blacklists)

Restart the resource after changing any of them.

General

OptionDefaultWhat 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.AutoMigrateESXSkinstrueESX: 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

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"
}
OptionWhat it does
ServerName, LogoShown at the top of the left rail. ServerName is also the title of notifications
CurrencySymbol in front of every price
PedImageUrlPreview image of each ped model. %s is replaced by the model name
ThemeThe default colours and corner style. Players can change them in the Studio; their choice is saved on their PC

Studio

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) },
    },
}
OptionWhat it does
EnabledShows the studio locations in the Studio panel
PrivateBucketMoves the player to their own routing bucket while at a studio. The bucket is StudioBucketBase + server id
DefaultFallback location (index in Locations), used only when the player's old coords are missing. Opening the menu never teleports on its own
LocationsStudios players can pick. "Your old coords" is always the first entry
Config.Koja.StudioBucketBase7000 — base of the private bucket numbers
Config.Koja.PrefsKvp"koja_appearance_prefs" — client KVP key of the per-player Studio settings

Studio light

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

OptionDefaultWhat it does
Showcase.BackdropColor"#1A1A1A"Default backdrop colour
Showcase.BackdropSize5.0Half size, in metres, of the backdrop box drawn around the ped
Showcase.SpinSpeed30.0Turntable speed in degrees per second
Showcase.KeepArmsjackets, undershirts, vests, armsItem view keeps the arms for these categories, so tops do not float over a bare body
Showcase.KeepHairhair, hats, masks, glasses, ears, beardItem view keeps the hair for these categories
Showcase.Weathers13 weathersWeathers offered in Time & weather. Set one to false to hide it

Safety while customising

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

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.

OptionWhat it does
ThresholdThe fix only kicks in when at least one face feature is further than this from 0
IntervalMilliseconds between checks for masks changed by other resources
IgnoreDrawablesMask drawables that never clip (bandanas, small mouth masks). Flattening is skipped for them

Inheritance faces

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

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 },
            -- ...
        },
    },
}
OptionWhat it does
BasketPay per item in clothing shops. With false, the flat upstream Config.ClothingCost is charged
DefaultPricePrice of an item with no saved price and no auto price. 0 = FREE
PaymentMethods"cash" and / or "bank"
AutoPrices.EnabledGives 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.StepGenerated prices are rounded to this
AutoPrices.FreeFirstDrawable 0 (usually "none" or the basic item) is free
AutoPrices.Categoriescategory = { 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 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

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 for the item definition.

Wardrobe hub

shared/koja_config.lua
Config.Koja.Hub = {
    Enabled = true,
    Prompt = true,
    CloseOnWear = false,
}
OptionWhat it does
EnabledThe 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
PromptA styled key-cap pill instead of lib.showTextUI in zones. Works without the hub too
CloseOnWearCloses the hub after putting on an outfit

Camera and Bones 3D

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

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 ...
}
OptionWhat it does
EnabledShows the Admin Panel button to admins
AdminGroupAce that counts as admin
FrameworkGroupsESX group / QBCore permission that also counts as admin
ScreenshotResourceName of the screenshot-basic resource
CoordsWhere the ped is shot. High in the sky; the green screen is drawn around the ped
GreenScreenColorRGB of the green screen
DefaultOffsetsCamera of a category with no saved preset
ImageSizeOutput size in pixels (square)
SettleMs / TextureSettleMsWait after a new drawable / texture before the shot. Raise them if images come out blurry or half loaded
InvisibleBodyEmpty 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
ModelsThe Select ped list. gender is the image set the shots go to. Any freemode-compatible add-on ped works
PoseAnimations held during shots. The first one that plays is used
GhostGhost shots: clothes only, no body. See below

Ghost shots

OptionDefaultWhat it does
Ghost.DefaulttrueGhost toggle state when the panel opens
Ghost.ReferenceSettleMs150Wait after emptying the slot before the reference shot
Ghost.Threshold24Colour difference (0–255) still treated as the same pixel
Ghost.CacheReferencefalseOne reference per category. Faster, but GTA's auto exposure can drift
Ghost.KeepHeadhair, beardCategories shot with the head
Ghost.ToneTestfalseExtra light / dark skin shot to cut out the body. Off by default; it left outlines
Ghost.SkinLight, SkinDark, ToneThreshold0, 19, 28Settings of the tone test
Ghost.Face"mood_sleeping_1"Facial idle while shooting (closed eyes, no eyeballs in mask holes)
Ghost.DebugfalseSaves the raw frames of every shot to capture_debug/. Tuning only, 1–2 MB per shot

Upload limits

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

OptionDefaultWhat it does
TattooCapture.EnabledtrueTattoo zones in the Capture panel. The editor still shows existing images when false
TattooCapture.SkinTone12Skin tone while shooting (0–45). nil keeps the player's skin
TattooCapture.Layers1How many times the tattoo is applied. More = darker ink
TattooCapture.Nakedper genderComponents of the naked body used while shooting
TattooCapture.BaldHeadtrueNo 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.ViewSettleMs40Wait after moving the camera to the next view
TattooCapture.InkOnlytrue"Flash" images: only the ink, no body
TattooCapture.InkThreshold10Colour difference still treated as bare skin
TattooCapture.InkSettleMs60Wait after removing / re-applying the tattoo
TattooCapture.PapernilBackground colour of flash images, e.g. "#FFFFFF". nil = transparent

Shopkeepers

shared/koja_shops.lua:

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 ...
}
OptionWhat it does
EnabledShopkeepers replace the zones, target boxes and blips of Config.Stores
Interactionauto = 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
PromptKey prompt of marker mode: hub (built-in pill), koja-lib (koja-lib Text UI) or ox_lib (lib.showTextUI)
Key, KeyLabelControl id and label of the interaction key
SpawnDistanceClerks are spawned locally only while the player is this close
InteractDistance, TargetDistanceHow close the player must be in marker / target mode
MarkerType, Size, Color, DrawDistance, Offset (metres in front of the clerk)
TalkThe short greeting before the menu: Enabled, Camera (over the shoulder), DurationMs, Speech
TypesPer 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>).

Upstream options

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

OptionNote
Config.ClothingCostCharged only when Config.Koja.Shop.Basket = false
Config.ChargePerTattoofalse 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.EnableJobOutfitsCommandEnables /joboutfits and /gangoutfits
Config.BossManagedOutfitsBoss managed job / gang outfits, shown in the wardrobe hub
Config.StoresNot used while Config.Koja.Shops.Enabled = true