# Usage / Integration

## Usage & Integration

This guide covers all events, exports, and callbacks available in Force Appearance, along with practical examples for integration with your scripts.

### Table of Contents

* Client Events
* Server Events
* Client Exports
* Server Callbacks
* Integration Examples
* Keyboard Shortcuts

### Client Events

#### force-appearance:client:openApp

Opens the appearance customization menu with default settings.

**Usage:**

```lua
TriggerEvent("force-appearance:client:openApp")

-- Or from server:
TriggerClientEvent("force-appearance:client:openApp", source)
```

**Example:**

```lua
-- Open appearance menu when player enters a marker
AddEventHandler('force-appearance:enterMarker', function()
  TriggerEvent("force-appearance:client:openApp")
end)
```

#### force-appearance:client:loadAppearance

Loads and applies a saved appearance/skin to the player.

**Parameters:**

* `skin` (table) - The appearance data to load

**Usage:**

```lua
TriggerEvent("force-appearance:client:loadAppearance", skinData)

-- Or from server:
TriggerClientEvent("force-appearance:client:loadAppearance", source, skinData)
```

**Example:**

```lua
-- Load appearance after character selection
local skinData = {
  model = "mp_m_freemode_01",
  components = {
    face = 0,
    skin = 1,
    hair_1 = 5,
    hair_2 = 0,
    -- ... more components
  }
}

TriggerEvent("force-appearance:client:loadAppearance", skinData)
```

#### force-appearance:client:openTattooShop

Opens the tattoo shop interface.

**Usage:**

```lua
RegisterNetEvent('force-appearance:client:openTattooShop', function()
  -- Tattoo shop opened
end)

-- Trigger from server:
TriggerClientEvent('force-appearance:client:openTattooShop', source)
```

**Example:**

```lua
-- Open tattoo shop at specific location
AddEventHandler('force-appearance:client:openTattooShop', function()
  -- Additional logic before opening
  print("Tattoo shop opened")
end)
```

#### force-appearance:client:saveTattoos

Saves the player's current tattoos.

**Parameters:**

* `tattoos` (table) - Array of tattoo data

**Usage:**

```lua
TriggerEvent("force-appearance:client:saveTattoos", tattoosData)
```

**Example:**

```lua
local tattoos = {
  { collection = "mpbusiness_overlays", name = "MP_Buis_M_Neck_000" },
  { collection = "mpbusiness_overlays", name = "MP_Buis_M_Neck_001" }
}

TriggerEvent("force-appearance:client:saveTattoos", tattoos)
```

### Server Events

#### force-appearance:server:saveAppearance

Saves a player's appearance to the database.

**Parameters:**

* `skin` (table) - The appearance data to save

**Usage:**

```lua
-- Client-side trigger:
TriggerServerEvent("force-appearance:server:saveAppearance", skinData)
```

**Server-side handler:**

```lua
RegisterNetEvent("force-appearance:server:saveAppearance", function(skin)
  local source = source
  -- Validates and saves to database
  -- Shows notification on success/failure
end)
```

**Example:**

```lua
-- After player customizes appearance
local currentSkin = exports["force-appearance"]:saveComponents()
TriggerServerEvent("force-appearance:server:saveAppearance", currentSkin)
```

#### force-appearance:server:saveTattoos

Saves a player's tattoos to the database.

**Parameters:**

* `tattoos` (table) - Array of tattoo data

**Usage:**

```lua
-- Client-side trigger:
TriggerServerEvent('force-appearance:server:saveTattoos', tattoosData)
```

**Example:**

```lua
local tattoos = {
  { collection = "mpbusiness_overlays", name = "MP_Buis_M_Neck_000" }
}
TriggerServerEvent('force-appearance:server:saveTattoos', tattoos)
```

### Client Exports

#### openApp

Opens the appearance menu with custom configuration.

**Parameters:**

* `type` (string) - Menu type ("main", "barber", "clothing", "tattoo")
* `config` (table) - Configuration options

**Usage:**

```lua
exports["force-appearance"]:openApp(type, config)
```

**Configuration Options:**

* `categories` (table) - Array of category names to show
* `useCamera` (boolean) - Enable/disable camera system
* `dialog` (table) - Custom dialog configuration
* `callback` (function) - Function called when menu closes

**Example - Basic Usage:**

```lua
-- Open default appearance menu
exports["force-appearance"]:openApp("main", {
  categories = { "face", "hair", "body" },
  useCamera = true
})
```

**Example - Barber Shop:**

```lua
exports["force-appearance"]:openApp("main", {
  categories = { "hair", "beard", "eyebrows", "makeup" },
  useCamera = true,
  dialog = {
    title = 'Barber Shop',
    text = 'Do you want to save your changes?',
    input = false,
    buttons = {
      {
        text = 'Pay $150',
        color = '#14d7ad',
        action = 'pay'
      },
      {
        text = 'Cancel',
        color = '#e74c3c',
        action = 'cancel'
      }
    }
  },
  callback = function(action)
    if action == "pay" then
      -- Calculate price based on changed components
      local totalPrice = 150

      -- Check if player can afford
      if playerMoney >= totalPrice then
        -- Take money and save appearance
        TriggerServerEvent('force-appearance:server:payBarber', totalPrice)
        exports["force-appearance"]:saveComponents()
      else
        lib.notify({
          title = 'Barber Shop',
          description = 'You cannot afford these services',
          type = 'error'
        })
        exports["force-appearance"]:closeApp()
      end
    else
      -- Cancel without saving
      exports["force-appearance"]:closeApp()
    end
  end
})
```

**Example - Clothing Store:**

```lua
exports["force-appearance"]:openApp("main", {
  categories = { "clothes", "accessories" },
  useCamera = true,
  dialog = {
    title = 'Clothing Store',
    text = 'Purchase your new outfit?',
    input = false,
    buttons = {
      {
        text = 'Buy',
        color = '#3498db',
        action = 'buy'
      }
    }
  },
  callback = function(action)
    if action == "buy" then
      -- Your payment logic
      local clothingPrice = 500
      TriggerServerEvent('force-appearance:server:buyClothing', clothingPrice)
      exports["force-appearance"]:saveComponents()
    end
  end
})
```

**Example - First Time Character Creation:**

```lua
exports["force-appearance"]:openApp("main", {
  categories = { "face", "hair", "body", "clothes" },
  useCamera = true,
  dialog = {
    title = 'Character Creation',
    text = 'Finalize your character?',
    input = true, -- Enable character name input
    inputLabel = 'Character Name',
    buttons = {
      {
        text = 'Create Character',
        color = '#27ae60',
        action = 'create'
      }
    }
  },
  callback = function(action, inputValue)
    if action == "create" and inputValue then
      -- Save character with name
      local characterData = exports["force-appearance"]:saveComponents()
      TriggerServerEvent('force-appearance:server:createCharacter', inputValue, characterData)
    end
  end
})
```

#### openDefault

Opens the appearance menu with all default settings.

**Usage:**

```lua
exports["force-appearance"]:openDefault()
```

**Example:**

```lua
-- Admin command to open full appearance menu
RegisterCommand('appearance', function()
  exports["force-appearance"]:openDefault()
end, false)
```

#### loadAppearance

Loads and applies appearance data to the player's ped.

**Parameters:**

* `skin` (table) - The appearance data to load
* `useCamera` (boolean, optional) - Whether to use camera system

**Usage:**

```lua
exports["force-appearance"]:loadAppearance(skinData, useCamera)
```

**Example:**

```lua
-- Load appearance after spawning
AddEventHandler('playerSpawned', function()
  local skinData = GetPlayerSkinData() -- Your function
  exports["force-appearance"]:loadAppearance(skinData, false)
end)
```

#### saveComponents

Returns the current appearance data without saving to database.

**Returns:**

* `table` - Current appearance components

**Usage:**

```lua
local currentSkin = exports["force-appearance"]:saveComponents()
```

**Example:**

```lua
-- Save to custom location or system
local appearance = exports["force-appearance"]:saveComponents()
TriggerServerEvent('myresource:saveToCustomDB', appearance)

-- Example return value:
-- {
--   model = "mp_m_freemode_01",
--   face = 5,
--   skin = 2,
--   hair_1 = 10,
--   hair_2 = 0,
--   ...
-- }
```

#### updateComponent

Updates a specific appearance component in real-time.

**Parameters:**

* `componentName` (string) - Name of the component
* `value` (number) - New value for the component

**Usage:**

```lua
exports["force-appearance"]:updateComponent(componentName, value)
```

**Example:**

```lua
-- Change hair style dynamically
exports["force-appearance"]:updateComponent("hair_1", 5)

-- Change shirt
exports["force-appearance"]:updateComponent("tshirt_1", 15)

-- Change skin color
exports["force-appearance"]:updateComponent("skin", 3)
```

#### closeApp

Closes the appearance menu without saving.

**Usage:**

```lua
exports["force-appearance"]:closeApp()
```

**Example:**

```lua
-- Close menu after timer expires
SetTimeout(60000, function()
  exports["force-appearance"]:closeApp()
  lib.notify({
    title = 'Timeout',
    description = 'Character creation time expired',
    type = 'error'
  })
end)
```

#### FirstTimeCustomization

Opens the first-time character customization flow (usually for new characters).

**Usage:**

```lua
exports["force-appearance"]:FirstTimeCustomization()
```

**Example:**

```lua
-- After selecting new character slot
AddEventHandler('character:created', function()
  exports["force-appearance"]:FirstTimeCustomization()
end)
```

#### OpenOutfitMenu

Opens the outfit management menu.

**Usage:**

```lua
exports["force-appearance"]:OpenOutfitMenu()
```

**Example:**

```lua
-- Command to open outfit menu
RegisterCommand('outfits', function()
  exports["force-appearance"]:OpenOutfitMenu()
end, false)

-- Or from a menu option
{
  title = 'Manage Outfits',
  icon = 'fas fa-tshirt',
  onSelect = function()
    exports["force-appearance"]:OpenOutfitMenu()
  end
}
```

### Server Callbacks

#### force-appearance:callback:getAppearance

Gets a player's saved appearance from the database.

**Returns:**

* `skin` (table) - Appearance data
* `tattoos` (table) - Tattoo data

**Usage:**

```lua
local skin, tattoos = lib.callback.await('force-appearance:callback:getAppearance', false)
```

**Example:**

```lua
-- Load appearance on player spawn
RegisterNetEvent('playerSpawned', function()
  local skin, tattoos = lib.callback.await('force-appearance:callback:getAppearance', false)

  if skin then
    exports["force-appearance"]:loadAppearance(skin)
  end

  if tattoos then
    TriggerEvent("force-appearance:client:saveTattoos", tattoos)
  end
end)
```

#### force-appearance:callback:getPeds

Gets the list of all available ped models.

**Returns:**

* `table` - Categorized ped models

**Usage:**

```lua
local peds = lib.callback.await('force-appearance:callback:getPeds', false)
```

#### force-appearance:callback:getTattoos

Gets the list of all available tattoos.

**Returns:**

* `table` - Categorized tattoos

**Usage:**

```lua
local tattoos = lib.callback.await('force-appearance:callback:getTattoos', false)
```

#### force-appearance:callback:getOutfits

Gets a player's saved outfits.

**Returns:**

* `table` - Array of outfit data

**Usage:**

```lua
local outfits = lib.callback.await('force-appearance:callback:getOutfits', false)
```

### Integration Examples

#### Complete Barber Shop Integration

```lua
-- client/barbershop.lua
local BarbershopClient = {}

function BarbershopClient:OpenBarber(shopData)
  -- Start animation
  TaskStartScenarioInPlace(PlayerPedId(), "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER", 0, true)

  -- Track original components for price calculation
  self.originalComponents = exports["force-appearance"]:saveComponents()

  exports["force-appearance"]:openApp("main", {
    categories = { "hair", "beard", "eyebrows", "makeup", "lipstick", "blush", "chest" },
    useCamera = true,
    dialog = {
      title = shopData.name or 'Barber Shop',
      text = 'Do you want to save your changes?',
      input = false,
      buttons = {
        {
          text = 'Pay & Apply',
          color = '#14d7ad',
          action = 'pay'
        },
        {
          text = 'Cancel',
          color = '#e74c3c',
          action = 'cancel'
        }
      }
    },
    callback = function(action)
      -- Clear animation
      ClearPedTasksImmediately(PlayerPedId())

      if action == "pay" then
        -- Calculate price
        local totalPrice = self:CalculateTotalPrice()

        -- Check if can afford
        local canAfford = lib.callback.await('force-appearance:canAffordBarber', false, totalPrice)

        if canAfford then
          exports["force-appearance"]:saveComponents()
          lib.notify({
            title = 'Barber Shop',
            description = 'Changes saved ($' .. totalPrice .. ')',
            type = 'success'
          })
        else
          lib.notify({
            title = 'Barber Shop',
            description = 'You cannot afford these services',
            type = 'error'
          })
          -- Restore original appearance
          exports["force-appearance"]:loadAppearance(self.originalComponents)
        end
      else
        -- Restore original appearance
        exports["force-appearance"]:loadAppearance(self.originalComponents)
      end
    end
  })
end

function BarbershopClient:CalculateTotalPrice()
  local current = exports["force-appearance"]:saveComponents()
  local original = self.originalComponents
  local total = 0

  local prices = {
    hair = 100,
    beard = 50,
    eyebrows = 40,
    chest = 50,
    makeup = 80
  }

  -- Check each component category
  if current.hair_1 ~= original.hair_1 or current.hair_2 ~= original.hair_2 then
    total = total + prices.hair
  end

  if current.beard_1 ~= original.beard_1 or current.beard_2 ~= original.beard_2 then
    total = total + prices.beard
  end

  -- ... check other components

  return total
end

-- Register interaction
AddEventHandler('barbershop:interact', function(shopData)
  BarbershopClient:OpenBarber(shopData)
end)
```

#### Character Creation Flow

```lua
-- client/character_creation.lua
local CharacterCreation = {}

function CharacterCreation:Start()
  -- Disable controls during creation
  self:DisableControls(true)

  -- Open appearance menu
  exports["force-appearance"]:openApp("main", {
    categories = {
      "face", "hair", "body", "clothes", "accessories"
    },
    useCamera = true,
    dialog = {
      title = 'Create Your Character',
      text = 'Choose your character name',
      input = true,
      inputLabel = 'First Name',
      buttons = {
        {
          text = 'Continue',
          color = '#27ae60',
          action = 'continue'
        }
      }
    },
    callback = function(action, firstName)
      self:DisableControls(false)

      if action == "continue" and firstName then
        -- Get the created appearance
        local appearance = exports["force-appearance"]:saveComponents()

        -- Ask for last name
        local input = lib.inputDialog('Character Creation', {
          { label = 'Last Name', type = 'input', required = true },
          { label = 'Date of Birth', type = 'date', required = true },
          { label = 'Gender', type = 'select', options = {
            { value = 'male', label = 'Male' },
            { value = 'female', label = 'Female' }
          }}
        })

        if input then
          -- Send character data to server
          TriggerServerEvent('character:create', {
            firstName = firstName,
            lastName = input[1],
            dob = input[2],
            gender = input[3],
            appearance = appearance
          })
        end
      end
    end
  })
end

function CharacterCreation:DisableControls(disable)
  CreateThread(function()
    while disable and self.disabling do
      DisableAllControlActions(0)
      EnableControlAction(0, 1, true) -- Mouse look
      EnableControlAction(0, 2, true) -- Mouse look
      Wait(0)
    end
  end)
end

-- server/character.lua
RegisterNetEvent('character:create', function(data)
  local source = source

  -- Save to database
  MySQL.insert('INSERT INTO characters (identifier, firstname, lastname, dob, gender, skin) VALUES (?, ?, ?, ?, ?, ?)', {
    GetPlayerIdentifier(source),
    data.firstName,
    data.lastName,
    data.dob,
    data.gender,
    json.encode(data.appearance)
  }, function(id)
    if id then
      TriggerClientEvent('character:created', source, id)
    end
  end)
end)
```

#### Outfit Management System

```lua
-- client/outfit_commands.lua

-- Command to open outfit menu
RegisterCommand('outfits', function()
  exports["force-appearance"]:OpenOutfitMenu()
end, false)

-- Quick outfit save command
RegisterCommand('saveoutfit', function(source, args)
  local outfitName = table.concat(args, " ")

  if not outfitName or outfitName == "" then
    lib.notify({
      title = 'Outfits',
      description = 'Usage: /saveoutfit [name]',
      type = 'error'
    })
    return
  end

  local appearance = exports["force-appearance"]:saveComponents()
  TriggerServerEvent('force-appearance:server:saveOutfit', outfitName, appearance)
end, false)
```

#### Admin Appearance Tool

```lua
-- client/admin_tools.lua

RegisterCommand('setappearance', function(source, args)
  local targetId = tonumber(args[1])

  if not targetId then
    lib.notify({
      title = 'Admin',
      description = 'Usage: /setappearance [id]',
      type = 'error'
    })
    return
  end

  exports["force-appearance"]:openApp("main", {
    categories = { "face", "hair", "body", "clothes", "accessories" },
    useCamera = false,
    dialog = {
      title = 'Set Player Appearance',
      text = 'Apply to player ' .. targetId .. '?',
      buttons = {
        {
          text = 'Apply',
          color = '#3498db',
          action = 'apply'
        }
      }
    },
    callback = function(action)
      if action == "apply" then
        local appearance = exports["force-appearance"]:saveComponents()
        TriggerServerEvent('admin:setPlayerAppearance', targetId, appearance)
      end
    end
  })
end, true) -- Admin only
```

### Keyboard Shortcuts

When the appearance menu is open, users can use these keyboard shortcuts:

| Key                  | Action                |
| -------------------- | --------------------- |
| `Arrow Keys` / `Tab` | Navigate between tabs |
| `Ctrl/Cmd + Enter`   | Save changes          |
| `Escape`             | Cancel and close      |
| `Home`               | Jump to first tab     |
| `End`                | Jump to last tab      |

### Best Practices

#### 1. Always Handle Callbacks

```lua
-- ❌ Bad
exports["force-appearance"]:openApp("main", {})

-- ✅ Good
exports["force-appearance"]:openApp("main", {
  callback = function(action)
    if action == "save" then
      -- Handle save
    else
      -- Handle cancel
    end
  end
})
```

#### 2. Cache Appearance Data

```lua
-- Cache to avoid repeated database calls
local cachedAppearance = nil

function LoadAppearance()
  if not cachedAppearance then
    cachedAppearance = lib.callback.await('force-appearance:callback:getAppearance', false)
  end
  return cachedAppearance
end
```

#### 3. Validate Data Server-Side

```lua
-- server.lua
RegisterNetEvent('myresource:saveAppearance', function(data)
  local source = source

  -- Always validate data
  if type(data) ~= "table" or not data.model then
    print("Invalid appearance data from player " .. source)
    return
  end

  -- Proceed with save
end)
```

#### 4. Provide Visual Feedback

```lua
-- Always notify users of actions
if success then
  lib.notify({
    title = 'Success',
    description = 'Changes saved',
    type = 'success'
  })
else
  lib.notify({
    title = 'Error',
    description = 'Failed to save',
    type = 'error'
  })
end
```

### Troubleshooting

#### Appearance Not Saving

* Check database connection
* Verify table/column names in framework config
* Check server console for errors
* Enable `Config.Debug = true`

#### Menu Not Opening

* Ensure ox\_lib is started before force-appearance
* Check for JavaScript errors in F8 console
* Verify NUI focus with `Config.KeepFocus = true`

#### Performance Issues

* Increase `Config.UpdateDelay` for large servers
* Reduce cache expiry times
* Check for conflicting resources

#### Components Not Updating

* Clear cache with `/skin` command
* Verify component names match game version
* Check for conflicting skinchanger resources

***

**Need Help?** Contact Force Developments on Discord (@force3883) for support.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forcedevelopments.com/resources/force-appearance/usage-integration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
