# Points (/docs/es_extended/client/imports/point)

`ESX.Point` is a small class for proximity triggers. You give it coordinates, a radius and callbacks for entering, leaving and staying inside. One shared loop does the distance checks for all points, which is cheaper than one `while true` loop per marker.

## Overview [#overview]

| | |
|---|---|
| Resource | `es_extended` (implemented in `esx_lib` as `xLib.point`) |
| Side | Client |
| Check interval | 500 ms for enter/leave, every frame while inside a point with `inside` |

## Creating a point [#creating-a-point]

### ESX.Point:new [#esxpointnew]

```lua
local point = ESX.Point:new(properties)
```

| Property | Type | Description |
|---|---|---|
| `coords` | `vector3` | Center of the point. |
| `distance` | `number` | Radius in units. |
| `hidden` | `boolean?` | Start hidden (no callbacks fire until shown). |
| `enter` | `function?` | `function(self)` when the player enters the radius. |
| `leave` | `function?` | `function(self)` when the player leaves the radius. |
| `inside` | `function?` | `function(self, distance)` every frame while inside. |

**Returns:** the point object. It keeps `coords`, `hidden`, `enter`, `leave`, `inside` and an internal `handle`.

The callbacks are called as methods, so the first argument is the point itself and the distance is the second argument of `inside`.

```lua title="client/main.lua"
local shop = ESX.Point:new({
    coords = vector3(25.7, -1347.3, 29.5),
    distance = 2.0,
    enter = function(self)
        ESX.TextUI('[E] Open shop')
    end,
    leave = function(self)
        ESX.HideUI()
    end,
    inside = function(self, distance)
        if distance < 1.0 and IsControlJustReleased(0, 38) then
            OpenShopMenu()
        end
    end,
})
```

<Callout type="info">
For key presses, consider [ESX.RegisterInteraction](/docs/es_extended/client/modules/interactions) instead of polling a control inside `inside`. Then you do not need the every-frame callback at all.
</Callout>

## Methods [#methods]

### point:delete [#pointdelete]

```lua
point:delete()
```

Removes the point. `leave` is **not** called, so hide any UI yourself.

```lua
ESX.HideUI()
shop:delete()
```

### point:toggle [#pointtoggle]

```lua
point:toggle(hidden)
```

| Name | Type | Description |
|---|---|---|
| `hidden` | `boolean?` | `true` hides, `false` shows. Without an argument the state is flipped. |

A hidden point is treated as out of range: if the player was inside, `leave` fires on the next check.

```lua
RegisterNetEvent('esx:setJob', function(job)
    shop:toggle(job.name ~= 'shopkeeper')
end)
```

## Low-level functions [#low-level-functions]

`ESX.Point` is built on three functions that are also exposed. You normally do not need them.

| Function | Description |
|---|---|
| `ESX.CreatePointInternal(coords, distance, hidden, enter, leave, inside)` | Creates a raw point. Returns a numeric handle. Callbacks get no `self`; `inside` gets `distance`. |
| `ESX.RemovePointInternal(handle)` | Removes a raw point. |
| `ESX.HidePointInternal(handle, hidden)` | Hides or shows a raw point. |

## Common issues [#common-issues]

- **Errors in callbacks.** An error in `enter` or `leave` is printed as `point X from resource errored` and the loop continues. An error in `inside` stops that point's `inside` calls until the player leaves and enters again.
- **`point.currDistance` is nil.** That field does not exist. Use the second argument of `inside`.
- **Nothing happens.** Check that `coords` is a `vector3` and `distance` is set.

## Related [#related]

- [Interactions](/docs/es_extended/client/modules/interactions)
- [Client UI (TextUI)](/docs/es_extended/client/ui)
- [Require](/docs/es_extended/client/imports/require)
- [Performance guide](/docs/guides/performance)
