> For the complete documentation index, see [llms.txt](https://5scripts-1.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://5scripts-1.gitbook.io/docs/assets/5s_skinmenu/editable.md).

# Editable

### Overview

Files in `editable` folders are designed to be modified without breaking the core functionality. These files handle:

* Compatibility with other scripts
* Custom behaviors and checks
* Database operations
* UI interactions
* Development tools

### Client Editable Files

Located in `client/editable/`

#### compatibility.lua

**Purpose:** Ensures compatibility with other clothing/skin scripts like skinchanger.

**Key Functions**

```lua
function getClothingOptions(excludedComponents, excludedProps)
    -- Gets clothing options excluding specified components/props
    -- Parameters:
    --   excludedComponents (table): Component IDs to exclude
    --   excludedProps (table): Prop IDs to exclude
    -- Returns: table with components and props
end
```

```lua
function setClothingOptions(clothingOptions)
    -- Sets clothing options (excluding face components)
    -- Parameters:
    --   clothingOptions (table): Clothing data to apply
end
```

```lua
function getSkinOptionsDb()
    -- Retrieves skin data from database
    -- Returns: skin data table or nil
end
```

```lua
function saveSkinOptions(skinOptions)
    -- Saves skin options to database
    -- Parameters:
    --   skinOptions (table, optional): Data to save (defaults to current)
    -- Returns: boolean success status
end
```

**Registered Events**

```lua
RegisterNetEvent('skinchanger:loadClothes', setClothingOptions)
RegisterNetEvent('skinchanger:change', setComponentOption)
```

**Exports**

```lua
exports('getClothingOptions', getClothingOptions)
exports('setClothingOptions', setClothingOptions)
exports('getSkinOptionsDb', getSkinOptionsDb)
exports('getSkinOptions', getSkinOptions)
exports('saveSkinOptions', saveSkinOptions)
```

#### const/constants.lua

**Purpose:** Defines all constants and mappings used throughout the script.

**Component IDs**

```lua
constants.PED_COMPONENTS_IDS = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }
constants.PED_PROPS_IDS = { 0, 1, 2, 3, 4, 5, 6, 7 }
```

**Component Name Mapping**

```lua
constants.componentsId = {
    ["face"] = 0,      -- Face structure
    ["masks"] = 1,     -- Masks
    ["tshirts"] = 8,   -- T-shirts/tops
    ["pants"] = 4,     -- Pants/legs
    ["shoes"] = 6,     -- Footwear
    ["hoodies"] = 11,  -- Hoodies/torso
    ["jewelry"] = 7,   -- Necklaces/accessories
    ["bags"] = 5,      -- Bags/backpacks
    ["decals"] = 10,   -- Decals
    ["vests"] = 9,     -- Vests/armor
    ["arms"] = 3,      -- Arms/gloves
}
```

**Props Name Mapping**

```lua
constants.propsId = {
    ["hats"] = 0,      -- Hats/helmets
    ["glasses"] = 1,   -- Glasses/eyewear
    ["ear"] = 2,       -- Earpieces
    ["watch"] = 6,     -- Watches
    ["bracelet"] = 7,  -- Bracelets
}
```

**Face Features**

```lua
constants.FACE_FEATURES = {
    "noseWidth", "nosePeakHigh", "nosePeakSize", "noseBoneHigh",
    "nosePeakLowering", "noseBoneTwist", "eyeBrownHigh", "eyeBrownForward",
    "cheeksBoneHigh", "cheeksBoneWidth", "cheeksWidth", "eyesOpening",
    "lipsThickness", "jawBoneWidth", "jawBoneBackSize", "chinBoneLowering",
    "chinBoneLenght", "chinBoneSize", "chinHole", "neckThickness",
}
```

**Head Overlays**

```lua
constants.HEAD_OVERLAYS = {
    "blemishes", "beard", "eyebrows", "ageing", "makeUp", "blush",
    "complexion", "sunDamage", "lipstick", "moleAndFreckles",
    "chestHair", "bodyBlemishes",
}
```

**Hair Decorations**

Contains extensive mappings for male and female hair decorations with texture dictionaries.

#### devCommands.lua

**Purpose:** Development commands for testing and debugging (only active when debug mode is enabled).

```lua
if not fs.config.debug then
    return  -- Skip loading if not in debug mode
end

RegisterCommand('openskin', function()
    openSkinMenu(false)
end, false)

RegisterCommand('closeskin', function()
    closeSkinMenu()
end, false)
```

**Default Available Commands (only in debug mode)**

* `/openskin` - Opens the skin menu
* `/closeskin` - Closes the skin menu

#### disabledClothings.lua

**Purpose:** Configuration for default clothing when items are toggled off.

**Structure**

```lua
defaultDisabledClothings = {
    hats = {
        enabled = true,        -- Whether this option is available
        propIndex = 0,         -- Prop index (for accessories)
        componentIndex = nil,  -- Component index (for clothing)
        drawable = 0,          -- Default drawable ID
        variant = 0           -- Default texture variant
    },
    -- ... other items
}
```

**Configurable Items**

| Item      | Type      | Description                     |
| --------- | --------- | ------------------------------- |
| `hats`    | Prop      | Hats and helmets                |
| `masks`   | Component | Face masks                      |
| `glasses` | Prop      | Eyewear                         |
| `hoodies` | Component | Hoodies/torso (multi-component) |
| `bags`    | Component | Backpacks and bags              |
| `pants`   | Component | Pants and legs                  |
| `shoes`   | Component | Footwear                        |

**Multi-Component Items**

Some items affect multiple components:

```lua
hoodies = {
    enabled = true,
    propIndex = nil,
    componentIndex = { 11, 8, 3 },  -- Multiple components
    default = { 91, 15, 15 },       -- Default values for males
    defaultF = { 15, 15, 15 },      -- Default values for females
    drawable = { 0, 0, 0 },         -- Drawable when disabled
    variant = { 0, 0, 0 }           -- Variants when disabled
}
```

#### handlers.lua

**Purpose:** Handles menu opening/closing events and player loading.

**Event Handlers**

```lua
function onSkinMenuOpened()
    -- Called when skin menu opens
    -- Default: Shows key hints for camera controls
    showKeyhintsPermanent({
        {
            keyIcon = { "ph-fill ph-mouse-left-click", "ph-fill ph-arrows-horizontal" },
            keyClass = "flex flex-col items-center align-center keyhint_iconsVertical",
            info = locale('keyhint_rotateCharacter'),
        },
        -- ... more hints
    }, 'bottom-left')
end
```

```lua
function onSkinMenuClosed()
    -- Called when skin menu closes
    clearKeyhints()
end
```

```lua
AddEventHandler('5s_lib:onPlayerLoaded', function()
    -- Automatically load saved skin when player joins
    local skin = lib.callback.await('skinMenu/getSkin', false)
    if skin ~= nil and skin ~= {} then
        setSkinOptions(skin)
    end
end)
```

**Customization Examples**

**Custom opening behavior:**

```lua
function onSkinMenuOpened()
    -- Your custom logic here
    TriggerEvent('myserver:logSkinMenuOpen')
    
    -- Still show keyhints
    showKeyhintsPermanent(yourCustomHints, 'top-right')
end
```

**Integration with other systems:**

```lua
function onSkinMenuClosed()
    clearKeyhints()
    
    -- Example: Trigger outfit save in custom system
    if isInClothingStore then
        TriggerServerEvent('clothingstore:purchaseOutfit')
    end
end
```

#### maskFix.lua

**Purpose:** Automatically adjusts facial features when wearing masks for better compatibility.

**Key Features**

* Automatically saves face blend data when mask is equipped
* Shrinks facial features to prevent clipping
* Restores original features when mask is removed

**Main Functions**

```lua
function forceRestoreFace()
    -- Manually restore saved facial features
    -- Use when mask removal doesn't trigger automatically
end

function fixClothing()
    -- Fix clothing-related issues
    -- Calls mask fix if needed
end
```

**Exports**

```lua
exports("forceRestoreFace", forceRestoreFace)
exports("fixClothing", fixClothing)
```

#### prechecks.lua

**Purpose:** Validation checks before allowing menu access.

**Main Function**

```lua
function canOpenSkinMenu()
    -- Add your custom checks here
    
    -- Example: Check if player is morphed
    -- if exports['other_script']:isPlayerMorphed() then
    --     fs.utils.notify('Cannot change appearance right now', 'error')
    --     return false
    -- end
    
    -- Example: Check player permissions
    -- if not exports['permission_system']:hasPermission('use.skinmenu') then
    --     return false
    -- end
    
    -- Example: Check if player is in jail
    -- if exports['esx_jail']:isPlayerInJail() then
    --     return false
    -- end
    
    return true
end
```

**Common Use Cases**

**Permission-based access:**

```lua
function canOpenSkinMenu()
    local playerGroup = ESX.GetPlayerData().group
    if playerGroup == 'user' and not isInClothingStore then
        fs.utils.notify('You need to be in a clothing store', 'error')
        return false
    end
    return true
end
```

**Location-based restrictions:**

```lua
function canOpenSkinMenu()
    local playerCoords = GetEntityCoords(PlayerPedId())
    local inRestrictedZone = IsPlayerInRestrictedZone(playerCoords)
    
    if inRestrictedZone then
        fs.utils.notify('Cannot change appearance in this area', 'error')
        return false
    end
    return true
end
```

**State-based checks:**

```lua
function canOpenSkinMenu()
    if IsPedInAnyVehicle(PlayerPedId()) then
        fs.utils.notify('Exit vehicle first', 'error')
        return false
    end
    
    if GetEntityHealth(PlayerPedId()) <= 0 then
        return false
    end
    
    return true
end
```

### Server Editable Files

Located in `server/editable/`

#### database.lua

**Purpose:** Database operations for saving and loading skin data.

**Core Functions**

```lua
function saveSkinData(source, skinOptions)
    -- Saves player skin data to database
    -- Parameters:
    --   source (number): Player server ID
    --   skinOptions (table): Skin data to save
    -- Returns: boolean success status
    
    local identifier = fs.framework.getIdentifier(source)
    
    local existingData = retrieveSkinData(source)
    if not existingData then
        -- Insert new record
        MySQL.update.await('INSERT INTO skin_data(identifier, skinData) VALUES (?, ?)', {
            identifier,
            json.encode(skinOptions)
        })
    else
        -- Update existing record
        success = MySQL.update.await('UPDATE skin_data SET skinData = ? WHERE identifier = ?', {
            json.encode(skinOptions),
            identifier
        }) ~= 0
    end
    
    return success
end
```

```lua
function retrieveSkinData(source)
    -- Retrieves player skin data from database
    -- Parameters:
    --   source (number): Player server ID
    -- Returns: table with skin data or nil
    
    local identifier = fs.framework.getIdentifier(source)
    
    local result = MySQL.single.await('select skinData from skin_data where identifier = ?', { identifier })
    return result?.skinData and json.decode(result.skinData) or nil
end
```
