> For the complete documentation index, see [llms.txt](https://malbersanimations.gitbook.io/animal-controller/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://malbersanimations.gitbook.io/animal-controller/malbers-questforge/points-of-interest/custom-waypoint.md).

# Custom Waypoint

**Custom Waypoint** is the lightweight runtime marker used for player-placed navigation pins. Where `POIMarker` is a persistent scene component backed by a `PointOfInterest` asset, `CustomWaypoint` is created and destroyed entirely at runtime — the player places one on the world map, it appears on the minimap and compass immediately, and it removes itself automatically when reached or after a configurable lifetime.

Because `CustomWaypoint` implements the same `IPOITarget` interface as `POIMarker`, every navigation surface (minimap, world map, compass) handles it identically without special-case code. From the map's perspective, a custom waypoint is just another icon with a position.

Custom waypoints are almost always created through `POIManager.CreateWaypoint()` rather than placed manually in the scene.

***

### How Waypoints Are Created

The standard creation flow is:

1. The player double-clicks a location on the **World Map**.
2. `WorldMapUI` calls `POIManager.CreateWaypoint(worldPosition, name)`.
3. `POIManager` instantiates a new GameObject, adds a `CustomWaypoint` component, calls `Initialize()` on it, and optionally spawns the configured **Marker Prefab** as a child.
4. The new waypoint is registered in POIManager's waypoint list and `OnPOIListChanged` fires, causing all map surfaces to refresh.

The waypoint then persists — visible on the minimap, compass, and world map — until it is removed via `RemoveSelf()`, by `POIManager.RemoveWaypoint()`, or by `POIManager.ClearAllWaypoints()`.

***

### Inspector Sections

#### Waypoint Data

The identity and icon of this waypoint.

| Property          | Type   | Default             | Description                                                                                                 |
| ----------------- | ------ | ------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Waypoint Name** | string | `"Custom Waypoint"` | Display name shown on map tooltips and compass labels. Writable at runtime via the `WaypointName` property. |
| **Icon**          | Sprite | —                   | A single sprite used for both the minimap/world-map icon and the compass icon.                              |
| **Color**         | Color  | Yellow              | Tint applied to the icon on all navigation surfaces. Also used to colour the scene gizmos.                  |
| **Is Active**     | bool   | true                | When false, the waypoint is excluded from all navigation displays.                                          |

A live 32 × 32 icon preview (tinted by Color) is drawn beneath the icon slot when a sprite is assigned. The inspector warns if the name is empty or the icon is not assigned, as the waypoint will be invisible without an icon.

***

#### Display Settings

| Property          | Type  | Default | Description                                                                                                                           |
| ----------------- | ----- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Show Distance** | bool  | true    | Whether to append a distance readout to the waypoint label on the compass bar.                                                        |
| **Fade Distance** | float | 200 m   | The distance at which the waypoint icon begins to fade. The inspector confirms the active value (e.g. *Waypoint will fade at 200 m*). |

***

#### Lifetime Settings

Controls how long the waypoint persists before removing itself.

| Property                 | Type  | Default | Description                                                                                                                |
| ------------------------ | ----- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
| **Has Lifetime**         | bool  | false   | When enabled, the waypoint automatically calls `RemoveSelf()` after the specified duration.                                |
| **Lifetime**             | float | 60 s    | Duration in seconds before auto-removal. Only shown when Has Lifetime is enabled.                                          |
| **Auto Remove on Reach** | bool  | true    | When the player comes within the reach radius (5 m by default), `OnWaypointReached` fires and the waypoint removes itself. |

> **Reach vs. Lifetime** — `autoRemoveOnReach` and `hasLifetime` are independent. If either condition is met while the player is within range, `RemoveSelf()` is called. A waypoint with Has Lifetime enabled will also remove itself at the lifetime boundary even if the player never approached it.

In Play mode with Has Lifetime enabled, a progress bar is drawn showing the time remaining as it counts down in real time.

***

#### Events

| Event                   | Signature           | Fires When                                                                                                                                                          |
| ----------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **On Waypoint Reached** | `UnityEvent`        | The player's distance drops to or below the reach radius (5 m). Fires once per waypoint lifetime — subsequent distance checks are skipped after `isReached` is set. |
| **On Distance Updated** | `UnityEvent<float>` | Every time `UpdateDistance()` is called (at `POIManager.updateInterval`, typically 0.1 s). Passes the current distance in world units.                              |

***

#### Status & Testing

**In edit mode**: Validates that the waypoint name is set and an icon is assigned.

**In Play mode**, the section shows:

* `IsActive` state
* Current distance to the player
* World-space position
* Time remaining (if Has Lifetime is enabled)

**Testing buttons** (Play mode):

| Button               | Effect                                                                              |
| -------------------- | ----------------------------------------------------------------------------------- |
| **Remove**           | Calls `RemoveSelf()`, cleanly removing the waypoint through `POIManager`.           |
| **\[Waypoint Name]** | Selects the waypoint's GameObject in the Hierarchy and frames it in the Scene view. |

***

### `IPOITarget` Implementation

`CustomWaypoint` implements the `IPOITarget` interface as follows:

| Member            | Behaviour                                                                                                |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| `WorldPosition`   | `transform.position` — no vertical offset (unlike `POIMarker`).                                          |
| `POIName`         | Returns `waypointName`.                                                                                  |
| `MapIcon`         | Returns `icon`.                                                                                          |
| `CompassIcon`     | Returns `icon` — the same sprite is used for both surfaces.                                              |
| `IconColor`       | Returns `color`.                                                                                         |
| `IsActive`        | `isActive && gameObject.activeInHierarchy` — both the serialized flag and the GameObject must be active. |
| `CurrentDistance` | Cached distance to the player, updated by `POIManager` each interval.                                    |
| `ShowOnMinimap`   | Always `true` — custom waypoints always appear on the minimap.                                           |
| `ShowOnCompass`   | Always `true` — custom waypoints always appear on the compass.                                           |

***

### Scene Gizmos

Unlike most components, `CustomWaypoint` draws gizmos via `OnDrawGizmos` (always visible, not just when selected) so placed waypoints are easy to locate in the Scene view.

| Gizmo                   | Description                                                                                                                      |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **Wire sphere** (0.5 m) | Drawn at the waypoint's position in the icon color.                                                                              |
| **Vertical line**       | 10 units tall, acting as a scene beacon.                                                                                         |
| **Reach radius sphere** | Semi-transparent wire sphere at the reach distance (5 m by default). Represents the proximity that triggers `OnWaypointReached`. |

The `OnSceneGUI` handler (selected only) draws the same beacon shapes via Handles, adds a semi-transparent disc at the **Fade Distance** radius, and renders the waypoint name as a coloured bold label at the top of the vertical line.

***

### Public API

#### Lifecycle

| Method                                                                    | Description                                                                                                                                                                                                                         |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Initialize(string name, Sprite icon, Color color, float reachDist = 5f)` | Sets the waypoint's name, icon, colour, and reach radius. Called automatically by `POIManager.CreateWaypoint()` immediately after the component is added. Also resets the creation time, so a lifetime clock starts from this call. |
| `RemoveSelf()`                                                            | Cleanly removes this waypoint. Calls `POIManager.RemoveWaypoint(this)` if the manager is available (which fires `OnWaypointRemoved` and `OnPOIListChanged`), or falls back to `Destroy(gameObject)`.                                |

#### Runtime Updates

| Method                                   | Description                                                                                                                                                      |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UpdateDistance(Vector3 playerPosition)` | Recalculates `CurrentDistance`, fires `OnDistanceUpdated`, and checks whether the reach threshold has been crossed. Called by `POIManager` each update interval. |

#### Post-Creation Configuration

| Method                                            | Description                                                                                                                                                                                                                           |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SetIconAndColor(Sprite newIcon, Color newColor)` | Updates the icon and colour after the waypoint has been created (e.g. after the player confirms a selection in a `WaypointPickerUI`). Fires `POIManager.OnPOIListChanged` so all navigation surfaces refresh their icons immediately. |
| `SetLifetime(float seconds)`                      | Enables the lifetime system and sets the duration. Resets the creation time to now, so the countdown begins from the moment this method is called rather than from when the component started.                                        |

#### Properties

| Property        | Type              | Description                                                                                                                                                                                                           |
| --------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `WaypointName`  | string (get/set)  | Read or write the display name at runtime. Setting the name does not automatically notify POIManager — call `POIManager.OnPOIListChanged?.Invoke()` manually if the name change needs to propagate to UI immediately. |
| `TimeRemaining` | float (read-only) | Seconds until auto-removal. Returns `float.MaxValue` when Has Lifetime is disabled. Never returns a negative value (`Mathf.Max(0, …)` is applied).                                                                    |

***

### Workflow: Full Player-Placed Waypoint Flow

1. **Player interaction** — Player opens the World Map and double-clicks a location.
2. **`WorldMapUI`** — Calls `POIManager.CreateWaypoint(worldPosition, "Custom Waypoint")`.
3. **`POIManager`** — Creates a new GameObject, adds `CustomWaypoint`, calls `Initialize()` with the default icon and colour from its **Default Waypoint Icon** and **Waypoint Color** settings, and optionally spawns the **Marker Prefab** in the world.
4. **Icon refresh** — `OnPOIListChanged` fires; minimap, compass, and world map rebuild their icon pools. The new pin appears on all surfaces.
5. *(Optional)* **`WaypointPickerUI`** — If a picker panel is configured, the player selects a custom icon and name before confirming. The panel calls `SetIconAndColor()` on the created waypoint, triggering another icon refresh.
6. **`POIManager.SetActiveWaypoint(waypoint)`** — Marks this as the navigation target; minimap and world map draw a dotted path to it.
7. **Player navigates** — `UpdateDistance()` fires every 0.1 s. When the player reaches within 5 m, `OnWaypointReached` fires and (if `autoRemoveOnReach` is true) `RemoveSelf()` is called.
8. **Cleanup** — `POIManager.RemoveWaypoint()` destroys the GameObject and fires `OnPOIListChanged`. The icon disappears from all navigation surfaces.

***

### Other Tips

* **Always create via `POIManager`** — Calling `new GameObject().AddComponent<CustomWaypoint>()` directly bypasses the waypoint cap check, the marker prefab spawning, and the `OnWaypointCreated` event. Always use `POIManager.CreateWaypoint()`.
* **Same icon for map and compass** — Unlike `POIMarker`, `CustomWaypoint` uses a single sprite for both surfaces. If you need different icons per surface on a waypoint, use a `POIMarker` with `useCustomSettings = true` and `customCategory = Waypoint` instead.
* **`WorldPosition` has no offset** — The icon is placed at exactly `transform.position`. There is no `worldMarkerOffset` equivalent. The world marker prefab (if any) is parented to the waypoint's GameObject with local position zero.
* **`SetLifetime()` restarts the clock** — Calling `SetLifetime()` on an existing waypoint does not add time; it resets the countdown to start from the moment of the call. This is intentional for scripted "refresh" scenarios (e.g. a timed event extending a waypoint's life).
* **`TimeRemaining` is not zero when lifetime expires** — The Update loop calls `RemoveSelf()` the frame the condition is met; `TimeRemaining` is clamped to zero but the GameObject may persist until the end of that frame. Do not rely on `TimeRemaining == 0` as a reliable destroy signal; use `OnWaypointRemoved` on `POIManager` instead.
* **Reach detection requires `POIManager` to call `UpdateDistance()`** — The reach check runs inside `UpdateDistance()`, not in the waypoint's own Update loop. If the waypoint is not registered with `POIManager` (i.e. created manually), `OnWaypointReached` will never fire.
