# 3D Text UI

A lightweight, optimized standalone script for rendering 3D interactive text labels in the world. It features a dual-loop automatic culling system for high performance, proximity scaling, and support for "tap" and "hold" interactions with visual progress tracking.

#### ⚙️ Data Structures

Before using the functions, understand the data tables used to configure an interaction point.

**The Main Configuration Table**

This table defines *where* the text is and *how* it behaves.

| **Property** | **Type**  | **Default** | **Description**                                                             |
| ------------ | --------- | ----------- | --------------------------------------------------------------------------- |
| coords       | `vector3` | Required    | The world coordinates where the text should appear.                         |
| options      | `table`   | Required    | A list of interaction options (see below).                                  |
| distance     | `float`   | `2.5`       | The distance at which the UI switches to "Full" mode (interaction enabled). |
| maxDistance  | `float`   | `5.0`       | The max distance at which the text is visible at all.                       |
| scale        | `float`   | `1.0`       | Scaling factor for the UI element.                                          |
| arc          | `boolean` | `false`     | Visual flag passed to UI (optional style preference).                       |

**The Options Table**

The `options` list contains the specific inputs available to the player. Note: Callbacks are now defined directly inside each option via the `action` property.

```lua
options = {
    { 
        label = "PICK UP",          -- The action text displayed
        key = 38,                   -- The FiveM Control Index (38 is E)
        keyName = "E",              -- (Optional) The visual key name. Falls back to Lib47.Keys if omitted.
        action = function()         -- The function triggered upon interaction
            print("Item picked up!")
        end,
        isVisible = function()      -- (Optional) Determines if the option shows up at all
            return true 
        end,
        canInteract = function()    -- (Optional) If false, the option is visible but disabled/grayed out
            return true 
        end
    },
    { 
        label = "INSPECT", 
        key = 74,                   -- 74 is H
        keyName = "H",
        hold = 3,                   -- (Optional) Seconds button must be held to trigger
        action = function()
            print("Inspected!")
        end
    }
}
```

***

#### 🛠️ API Reference

This script exposes functions and assigns them to the global `Lib47` table. It utilizes an automatic slow culling loop (checking within 30.0 units) and a fast interaction loop, meaning you no longer need to check distances manually in your own threads.

**1. RegisterTextUi3d (Static Mode)**

Best for static, permanent locations (e.g., Shops, Duty points, ATMs). You register it once, and the script's internal culling handles the rest.

* Usage: `Lib47.RegisterTextUi3d(data)`
* Returns: `string` (The Interaction ID, derived from coordinates).

**2. ShowTextUi3d (Dynamic/Immediate Mode)**

Best for dynamic entities (like dropping an item on the ground). This registers the point temporarily. If you walk away, it will eventually be culled out unless updated.

* Usage: `Lib47.ShowTextUi3d(data)`
* Returns: `string` (Interaction ID).

**3. HideTextUi3d & RemoveTextUi3d**

Hides or completely removes an interaction point.

* Usage: `Lib47.HideTextUi3d(id)` or `Lib47.RemoveTextUi3d(id)`

***

#### 📝 Code Examples

**Example A: Static Location (Register Mode)**

Use this for permanent locations. Run this code once (e.g., at resource start). You do not need a loop.

```lua
CreateThread(function()
    local position = vector3(373.59, 328.58, 103.68)

    -- Register the point once, the script handles distance and culling automatically
    Lib47.RegisterTextUi3d({
        coords = position,
        distance = 2.5,
        maxDistance = 5.0,
        options = {
            { 
                label = "PICK UP", 
                key = 38,
                keyName = "E",
                action = function()
                    print("Player picked up item") 
                end
            },
            { 
                label = "INSPECT", 
                key = 74,
                keyName = "H",
                hold = 3, -- 3 Second Hold
                action = function()
                    print("Player inspected item") 
                end
            }
        },
    })
end)
```

**Example B: Dynamic State & Conditional Logic**

Use the `isVisible` and `canInteract` functions to dynamically change how the text behaves based on the player's state (e.g., job, money, or items).

```lua
CreateThread(function()
    local atmCoords = vector3(100.0, 100.0, 20.0)

    Lib47.RegisterTextUi3d({
        coords = atmCoords,
        distance = 2.0,
        options = {
            { 
                label = "USE ATM", 
                key = 38, 
                keyName = "E",
                isVisible = function()
                    -- Only show if the player is not in a vehicle
                    return not IsPedInAnyVehicle(PlayerPedId(), false)
                end,
                canInteract = function()
                    -- Visible, but grayed out if the player is dead
                    return not IsEntityDead(PlayerPedId())
                end,
                action = function()
                    print("Accessing ATM...")
                end
            }
        }
    })
end)
```

**Example C: Using Exports directly**

If you do not use the `Lib47` global wrapper, you can use the resource exports directly via the `Interface` wrapper.

```lua
exports['ak47_lib']:RegisterTextUi3d({
    coords = vector3(100.0, 100.0, 20.0),
    options = {
        { 
            label = "TEST", 
            key = 38,
            keyName = "E",
            action = function()
                print("Clicked")
            end
        }
    }
})
```
