# :necktie: Premade Outfits
Documentation relating to the **jo_clothingstore_outfits** add-on for [Clothing store script](clothing-store).

:::: tabs
::: tab BUY
[Buy the add-on](https://jumpon-studios.com/redm/premade-outfits)
:::

::: tab PREVIEW
Preview coming soon.
:::
::::

## 1. Installation
::: warning
[Clothing store script](clothing-store) is required to use this add-on
:::

To install jo_clothingstore_outfits:
- Drag and drop the resource into your resources folder
  - jo_clothingstore_outfits 
- Add this ensure in your server.cfg after `ensure jo_clothingstore`
  - `ensure jo_clothingstore_outfits`

Congratulation, the **Premade outfits** add-on is ready to be used!

## 2. Usage

This add-on adds a **Premade outfits** menu in the clothing store/wardrobe.
- Open the clothing store, go to **Outfits** and select **Premade outfits**.
- Choose the premade outfit sex when the sex selection menu opens.
- Outfits are split into sections of 100 (Outfits 1 to 100, 101 to 200, ...).
- Select an outfit, use the variation slider to preview, then press the buy prompt to purchase.
- Purchased outfits are saved to the player's outfits list in jo_clothingstore.

If multiple payment options are configured for an outfit, use the **Switch price** prompt to cycle through them.

Premade outfits are separated into `male` and `female` catalogues. If the selected premade outfit sex is different from the player's current character, the preview is displayed on the opposite-sex mannequin.

### Fixing body parts
:::warning Fixing body parts

Some premade outfits may not perfectly fit your character and can cause body clipping. If that happens, press the **Fix body parts prompt** to open the adjustment menu.

This menu lets you cycle through different upper and lower body variations to find the best fit for the selected outfit.

Once you purchase the outfit, the chosen body settings are saved with it, so you only need to adjust it once.
:::

## 3. Configuration

The configuration files are located in the `config` folder within the resource. This folder contains two subfolders:

- **`_default.lock/`** - Contains the default configuration files. **Do not edit these files directly** as they may be overwritten during updates.
- **`custom/`** - This is where you place your custom configuration files to override the defaults.

### How to customize the configuration

1. Navigate to `jo_clothingstore_outfits/config/_default.lock/`
2. Copy the file you want to modify (e.g., `prices.lua`)
3. Paste it into `jo_clothingstore_outfits/config/custom/`
4. Edit the copied file in the `custom` folder

The script automatically loads files from `custom/` first, and only uses `_default.lock/` as a fallback. This system ensures your customizations are preserved when updating the script.

### Default configuration files

`jo_clothingstore_outfits/config/_default.lock/customOutfits.lua`

```lua
-- ================================================================
-- CUSTOM OUTFITS CONFIGURATION GUIDE
-- ================================================================
-- CustomOutfits must be a list of outfits.
-- Leave it empty to disable custom outfits by default:
--
--   CustomOutfits = {}
--
-- Outfit fields:
--   label      Display/group label. Outfits with the same label become variations.
--   category   Section name displayed in the custom outfits menu.
--   isMale     true for male outfits, false for female outfits.
--   components Table of clothing components to apply.
--
-- Component format:
--   Each component key must match a supported clothing category, such as pants or hats.
--   A component can be either:
--     { hash = 1234567890 }
--   Or the full object accepted by jo.component.apply(ped, category, data):
--     {
--       drawable = "...",
--       material = "...",
--       normale = "...",
--       albedo = "...",
--       palette = "...",
--       t0 = 0,
--       t1 = 0,
--       t2 = 0
--     }
--
-- Complete example:
--
-- CustomOutfits = {
--     {
--         label = "police_01",
--         category = "police",
--         isMale = true,
--         components = {
--             pants = {
--                 drawable = "tuxlower_mr1_000",
--                 material = "tuxlower_mr1_000_c1_000_m",
--                 normale = "tuxlower_mr1_000_c1_000_nm",
--                 albedo = "tuxlower_mr1_000_c1_999_ab",
--                 palette = "metaped_tint_combined_leather",
--                 t0 = 66,
--                 t1 = 14,
--                 t2 = 14
--             },
--             hats = { hash = 2647571465 }
--         }
--     },
--     {
--         label = "police_01",
--         category = "police",
--         isMale = true,
--         components = {
--             pants = {
--                 drawable = "pantcombo_mr1_010",
--                 material = "pantcombo_mr1_010_c1_000_m",
--                 normale = "pantcombo_mr1_010_c1_000_nm",
--                 albedo = "pantcombo_mr1_010_c1_999_ab",
--                 palette = "metaped_tint_generic_clean",
--                 t0 = 17,
--                 t1 = 254,
--                 t2 = 254
--             },
--             hats = {
--                 drawable = "hat_mr1_079",
--                 material = "hat_mr1_079_c1_000_m",
--                 normale = "hat_mr1_079_c1_000_nm",
--                 albedo = "hat_mr1_079_c1_999_ab",
--                 palette = "metaped_tint_combined_leather",
--                 t0 = 31,
--                 t1 = 1,
--                 t2 = 43
--             }
--         }
--     },
--     {
--         label = "Lady",
--         category = "city",
--         isMale = false,
--         components = {
--             pants = {
--                 drawable = "pantcombo_mr1_002",
--                 material = "pantcombo_mr1_002_c3_000_m",
--                 normale = "pantcombo_mr1_002_c3_000_nm",
--                 albedo = "pantcombo_mr1_002_c3_999_ab",
--                 palette = "metaped_tint_combined_leather",
--                 t0 = 65,
--                 t1 = 11,
--                 t2 = 64
--             },
--             hats = {
--                 drawable = "hat_mr1_007_alt02",
--                 material = "hat_mr1_007_c3_000_m",
--                 normale = "hat_mr1_007_c3_000_nm",
--                 albedo = "hat_mr1_007_c3_999_ab",
--                 palette = "metaped_tint_hat",
--                 t0 = 19,
--                 t1 = 0,
--                 t2 = 0
--             }
--         }
--     }
-- }
-- ================================================================

CustomOutfits = {}
```

`jo_clothingstore_outfits/config/_default.lock/global.lua`

```lua
Config.usePaletteSprites = false -- Enable palette sprites preview for variations, can make the menu slower if there is many outfits with many variations
```

`jo_clothingstore_outfits/config/_default.lock/lang.lua`

```lua
-- * ======================================
-- *  GENERAL
-- * ======================================

Lang.premadeOutfits = "Premade outfits"
Lang.premadeOutfitsDesc = "Browse and purchase premade outfits."
Lang.customOutfits = "Custom"
Lang.classicOutfits = "Classic"
Lang.outfitSection = "Outfits %d to %d"
Lang.outfitItem = "Outfit %d"
Lang.premadeOutfitBought = "Outfit %s purchased !"
Lang.noOutfitsForYou = "No outfits available for you"
Lang.notAllowed = "You are not allowed to do this"
Lang.priceNotFound = "Price not found"
```

`jo_clothingstore_outfits/config/_default.lock/prices.lua`

```lua
-- ================================================================
-- PRICE CONFIGURATION GUIDE
-- ================================================================
-- Need to build or adjust a price table quickly?
-- Use the official Price Generator here:
--   https://docs.jumpon-studios.com/DeveloperResources/price-generator
--
-- The generator creates ready-to-paste Lua price tables for this config.
-- Copy the generated Lua code and paste it as the value of the relevant
-- price entry or model override.
-- ================================================================

Config.outfitDefaultPrice = 2 -- Default price for each outfit

Config.specificOutfitPrices = {}
Config.specificOutfitPrices["male"] = {}
Config.specificOutfitPrices["female"] = {}

Config.specificOutfitPrices["male"]["13"] = 10   -- Specific price for outfit 13
Config.specificOutfitPrices["male"]["13:3"] = 15 -- Specific price for outfit 13, variation 3

-- Example of complex pricing with multiple payment options using "or" operator
Config.specificOutfitPrices["male"]["200:9"] = {
    operator = "or",                               -- Player can choose between these payment options
    { money = 5, item = "acid" },                  -- Option 1: $5 AND 1 acid item
    { gold = 5 },                                  -- Option 2: 5 gold
    { money = 2, { item = "acid", quantity = 3 } } -- Option 3: $2 AND 3 acid items
}

-- You can also disable an outfit by setting its price to -1:
-- Config.specificOutfitPrices["male"]["3"] = -1

-- Or disable a specific variation:
-- Config.specificOutfitPrices["male"]["13:3"] = -1
```

### Language Configuration

The script supports full translation through the language system. To override any text:

1. Find the key you want to change in `config/_default.lock/lang.lua`
2. Copy `lang.lua` to `config/custom/` and add only the keys you want to override

Example:

```lua
Lang.premadeOutfits = "Premade outfits"
Lang.outfitSection = "Outfits %d to %d"
Lang.premadeOutfitBought = "Outfit %s purchased!"
```

Available translation keys include:
- General UI text (`premadeOutfits`, `premadeOutfitsDesc`)
- Menu labels (`outfitSection`, `outfitItem`)
- Notifications (`premadeOutfitBought`)

## 4. For developers

### Actions

Actions are one of the two types of Hooks. They provide a way for running a function at a specific point in the execution of scripts. Callback functions for an Action do not return anything back to the calling Action hook. They are the counterpart to Filters.

Below is a complete list of all available actions in the jo_clothingstore_outfits script.

#### `[Client]` switchPrice
Triggered when user switch the outfit price

```lua
exports.jo_clothingstore_outfits:registerAction('switchPrice', function(priceIndex)
    -- Your code here
end)
```

### Filters

[Filters](/DeveloperResources/filters) allow you to modify data or permissions synchronously at specific points in the script.

#### `[Server]` canBuyOutfit
Control whether a player can buy a premade outfit. This filter is called after the selected outfit has been cleaned and before the payment is charged.

```lua
-- @param canBuy - boolean (default true)
-- @param source - integer server ID of the player
-- @param name - string outfit name entered by the player
-- @param outfit - table cleaned outfit data that will be saved/applied
-- @param price - table selected price for the outfit
-- @param sexe - string outfit sex category
-- @param outfitIndex - integer premade outfit index
-- @param variationIndex - integer premade outfit variation index
exports.jo_clothingstore_outfits:registerFilter("canBuyOutfit", function(canBuy, source, name, outfit, price, sexe, outfitIndex, variationIndex)
    return canBuy
end)
```

#### `[Client]` canOpenPremadeOutfitsMenu
Control whether the player can open the premade outfits menu.

```lua
-- @param canOpen - boolean (default true)
exports.jo_clothingstore_outfits:registerFilter("canOpenPremadeOutfitsMenu", function(canOpen)
    return canOpen
end)
```

#### `[Server]` outfitSectionsAccess
Control which premade outfit types and sections are visible to a player. This server filter is called when the add-on opens the premade outfits menu after the player selects a sex in the main `jo_clothingstore` menu.

The filter receives the full access table and must return it. You can hide a complete type with `access.types`, or hide individual sections with `access.sections`.

```lua
-- @param access - table access configuration for the selected outfit sex
-- @param source - integer server ID of the player
-- @param sexe - string selected outfit sex: "male" or "female"
exports.jo_clothingstore_outfits:registerFilter("outfitSectionsAccess", function(access, source, sexe)
    return access
end)
```

Default contract:

```lua
local access = {
    types = {
        custom = true,  -- Custom outfits type is visible
        classic = true, -- Classic outfits type is visible
    },
    sections = {
        custom = {
            ["Your custom category label 1"] = true, -- Custom category label
            ["Your custom category label 2"] = true,
        },
        classic = {
            [1] = true, -- Outfits 1 to 100
            [2] = true, -- Outfits 101 to 200
        }
    }
}
```

If every type and section is denied, the menu is not opened and the player receives the `noOutfitsForYou` notification.

Example: allow only custom outfits:

```lua
exports.jo_clothingstore_outfits:registerFilter("outfitSectionsAccess", function(access, source, sexe)
    access.types.classic = false
    access.sections.classic = {}

    return access
end)
```

Example: allow only one custom category:

```lua
exports.jo_clothingstore_outfits:registerFilter("outfitSectionsAccess", function(access, source, sexe)
    access.types.custom = true
    access.types.classic = false
    access.sections.custom = {
        ["Your custom category label 1"] = true,
    }
    access.sections.classic = {}

    return access
end)
```

Example: allow only the first classic section:

```lua
exports.jo_clothingstore_outfits:registerFilter("outfitSectionsAccess", function(access, source, sexe)
    access.types.custom = false
    access.types.classic = true
    access.sections.custom = {}
    access.sections.classic = {
        [1] = true,
    }

    return access
end)
```

Example: deny everything:

```lua
exports.jo_clothingstore_outfits:registerFilter("outfitSectionsAccess", function(access, source, sexe)
    access.types.custom = false
    access.types.classic = false
    access.sections.custom = {}
    access.sections.classic = {}

    return access
end)
```

### Related jo_clothingstore filter

The premade outfits menu uses the outfit sex selection flow from the main `jo_clothingstore` resource. To disable opposite-sex premade outfit browsing and purchases, register this server filter from the main script `jo_clothingstore`:

#### `[Server]` canCreateOppositeSexOutfit
```lua
-- @param canCreate - boolean (default true)
-- @param source - serverID of the player
exports.jo_clothingstore:registerFilter("canCreateOppositeSexOutfit", function(canCreate, source)
    return false
end)
```
