# Special Slots

## Special Slots Events API

The Special Slots API allows developers to register custom events for special inventory slots (tool, armour, drug, radio, backpack, identification, phone, tablet, key, wallet).

### Available Slots

Available slots are configured via the `inventory:specialslots` convar in your `server.cfg`. By default, they include:

| Slot             | Description              |
| ---------------- | ------------------------ |
| `tool`           | Tools                    |
| `armour`         | Protective vests         |
| `drug`           | Substances / medications |
| `radio`          | Communication radios     |
| `backpack`       | Backpacks                |
| `identification` | Identity documents       |
| `phone`          | Mobile phones            |
| `tablet`         | Electronic tablets       |
| `key`            | Keys                     |
| `wallet`         | Wallets                  |

***

### Registering Events

Use the `RegisterSpecialSlotEvent` export to register a slot with its events:

```lua
exports.lvs_inventory:RegisterSpecialSlotEvent('tool', {
    onEquip = function(slotData)
        print('Tool equipped')
    end,

    onUnequip = function(slotData)
        print('Tool unequipped')
    end,

    onTick = function(slotData)
        -- Logic that runs while the item is equipped
    end,

    tickRate = 1000 -- (optional) Interval in ms. Default: 100
})
```

***

### Available Events

#### onEquip

Triggered when an item is placed in the special slot.

**Parameters:**

| Parameter  | Type  | Description                                             |
| ---------- | ----- | ------------------------------------------------------- |
| `slotData` | table | Data of the equipped item (name, count, metadata, etc.) |

**Example:**

```lua
onEquip = function(slotData)
    print('Item equipped:', slotData.name)
    print('Count:', slotData.count)
    print('Metadata:', json.encode(slotData.metadata))
end
```

#### onUnequip

Triggered when an item is removed from the special slot.

**Parameters:**

| Parameter  | Type  | Description                          |
| ---------- | ----- | ------------------------------------ |
| `slotData` | table | Data of the item that was unequipped |

**Example:**

```lua
onUnequip = function(slotData)
    if slotData then
        print('Item unequipped:', slotData.name)
    else
        print('Slot cleared without previous data')
    end
end
```

#### onTick

Executed periodically while the item remains in the special slot.

**Parameters:**

| Parameter  | Type  | Description                       |
| ---------- | ----- | --------------------------------- |
| `slotData` | table | Updated data of the equipped item |

**Example:**

```lua
onTick = function(slotData)
    -- Check if a tool has durability
    if slotData.metadata.durability then
        if slotData.metadata.durability <= 0 then
            -- Notify the player
            exports['ox_lib']:notify({
                title = 'Broken tool',
                description = 'Your tool has worn out completely',
                type = 'warning'
            })
        end
    end
end
```

### Tick Rate Configuration

You can configure the frequency of `onTick` execution using the `tickRate` property in milliseconds:

```lua
exports.lvs_inventory:RegisterSpecialSlotEvent('phone', {
    onTick = function(slotData)
        -- Runs every 5 seconds
        checkPhoneSignal(slotData.metadata.number)
    end,
    
    tickRate = 5000 -- 5 seconds
})
```

If `tickRate` is not specified, the default value is **100ms**.

> **Performance Note:** Very low values (such as 1ms) can impact server performance. Use the highest value that is functional for your use case.

***

### Complete Examples

#### Tool with Durability

```lua
exports.lvs_inventory:RegisterSpecialSlotEvent('tool', {
    onEquip = function(slotData)
        exports['ox_lib']:notify({
            title = 'Tool equipped',
            description = string.format('%s ready to use', slotData.label),
            type = 'success'
        })
    end,

    onUnequip = function(slotData)
        exports['ox_lib']:notify({
            title = 'Tool stored',
            description = string.format('%s stored away', slotData.label),
            type = 'info'
        })
    end,

    onTick = function(slotData)
        -- Reduce durability over time
        if slotData.metadata.durability and slotData.metadata.durability > 0 then
            -- Wear logic here...
        end
    end,

    tickRate = 1000 -- Every second
})
```

#### Radio with Channel Verification

```lua
exports.lvs_inventory:RegisterSpecialSlotEvent('radio', {
    onEquip = function(slotData)
        local channel = slotData.metadata.channel or 1
        SetRadioChannel(channel)
        exports['ox_lib']:notify({
            title = 'Radio turned on',
            description = string.format('Channel: %d', channel),
            type = 'success'
        })
    end,

    onUnequip = function(slotData)
        SetRadioChannel(0) -- Turn off radio
        exports['ox_lib']:notify({
            title = 'Radio turned off',
            type = 'info'
        })
    end,

    onTick = function(slotData)
        -- Check if there is signal on the current channel
        local channel = slotData.metadata.channel or 1
        if not HasRadioSignal(channel) then
            -- No signal...
        end
    end,

    tickRate = 2000 -- Every 2 seconds
})
```

#### Phone with State Updates

```lua
local phoneActive = false

exports.lvs_inventory:RegisterSpecialSlotEvent('phone', {
    onEquip = function(slotData)
        phoneActive = true
        local number = slotData.metadata.number or 'Unknown'
        exports['ox_lib']:notify({
            title = 'Phone started',
            description = string.format('Number: %s', number),
            type = 'success'
        })
        OpenPhoneUI()
    end,

    onUnequip = function(slotData)
        phoneActive = false
        ClosePhoneUI()
        exports['ox_lib']:notify({
            title = 'Phone turned off',
            type = 'info'
        })
    end,

    onTick = function(slotData)
        if phoneActive then
            -- Check for pending notifications
            CheckPendingNotifications()
        end
    end,

    tickRate = 3000 -- Every 3 seconds
})
```

### Manual Event Triggering

You can manually trigger events using the `TriggerSpecialSlotEvent` export:

```lua
local success, result = exports.lvs_inventory:TriggerSpecialSlotEvent('tool', 'onEquip', mySlotData)

if not success then
    print('Error executing event:', result)
end
```

**Parameters:**

| Parameter   | Type   | Description                             |
| ----------- | ------ | --------------------------------------- |
| `slotName`  | string | Name of the slot (tool, radio, etc.)    |
| `eventType` | string | Event type (onEquip, onUnequip, onTick) |
| `...`       | any    | Additional arguments for the callback   |

**Returns:**

| Value          | Type    | Description                                  |
| -------------- | ------- | -------------------------------------------- |
| `success`      | boolean | Whether the callback executed without errors |
| `result/error` | any     | Callback result or error message             |

### Important Notes

#### Slot Data (slotData)

The `slotData` parameter contains the equipped item's information and is automatically updated. Its general structure includes:

```lua
{
    name = "lvs_wrench",         -- Item name
    label = "Wrench",            -- Display label
    count = 1,                   -- Quantity
    weight = 500,                -- Weight in grams
    slot = 16,                   -- Slot number
    metadata = {                 -- Custom metadata
        durability = 85,
        description = "A robust tool"
        -- ...your own metadata
    }
}
```

#### Avoid Heavy Operations in onTick

If you need to perform expensive operations inside `onTick`, consider using `Citizen.CreateThread` to avoid blocking the cycle:

```lua
onTick = function(slotData)
    if not processing then
        processing = true
        Citizen.CreateThread(function()
            -- Heavy operation here
            processData(slotData)
            processing = false
        end)
    end
end
```
