# 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
        }
    }
})
```


---

# 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.menanak47.com/plugins/ak47_lib/interface/3d-text-ui.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.
