> 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/poi-manager.md).

# POI Manager

### Overview

**POI Manager** is the central singleton that orchestrates every Point of Interest in the game. It maintains the authoritative list of all active `POIMarker` components and `CustomWaypoint` objects, runs a timed distance-update loop, and exposes query, creation, and navigation APIs consumed by the Minimap, World Map, and Compass systems.

Every `POIMarker` in the scene self-registers with POI Manager on Awake and unregisters when disabled or destroyed — no manual wiring is required. POI Manager then notifies the map systems of the change so icons appear or disappear immediately.

A single POI Manager instance persists for the lifetime of the game session (`DontDestroyOnLoad`). If none exists in a scene, one is created automatically at runtime. Only one scene in the project needs to contain a configured POI Manager GameObject.

<figure><img src="https://963537199-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lzhr1XSMzMqNXjRnNlb%2Fuploads%2F2hyZO6ItgN04lKkneVJu%2Fimage.png?alt=media&amp;token=f2132073-cdd2-49e4-89d9-c32de87be131" alt=""><figcaption></figcaption></figure>

***

### Scene Setup (ONLY IF NOT USING THE ONE-CLICK SETUP!)

Add a single **POI Manager** component to a persistent GameObject (e.g. your `_Managers` object). Mark it **DontDestroyOnLoad** implicitly — the component handles this itself. Do not place additional POI Manager components in other scenes; duplicate instances are destroyed on Awake.

***

### Inspector Sections

#### Settings

Core runtime behaviour of the manager.

| Property             | Type               | Default | Description                                                                                                                                                                                                                                           |
| -------------------- | ------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Auto Find Player** | bool               | true    | On Start, searches for a GameObject tagged `Player` and assigns its transform automatically. Disable to use a manual reference instead.                                                                                                               |
| **Player Transform** | TransformReference | —       | The transform used for all distance and elevation calculations. Shown only when **Auto Find Player** is off, or when a constant value is already assigned. Accepts either a direct scene reference or a Malbers `TransformVariable` ScriptableObject. |
| **Update Interval**  | float              | 0.1 s   | How often (in seconds) distances are recalculated for all registered POIs and custom waypoints. Lower values increase accuracy at the cost of CPU. The inspector warns if the value drops below 0.05 s.                                               |

> **Auto Find Player** uses `GameObject.FindGameObjectWithTag("Player")` during Start. If the player spawns after Start (e.g. from a loading system), disable Auto Find Player and assign the reference manually or via script once the player exists.

***

#### Default Icons

Fallback sprites used when a POI or waypoint has no icon assigned to its asset.

| Property                  | Type   | Description                                                                                                                  |
| ------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **Default Map Icon**      | Sprite | Fallback icon displayed on the minimap and world map for any POI whose `PointOfInterest` asset has no **Map Icon** assigned. |
| **Default Compass Icon**  | Sprite | Fallback icon for the compass bar.                                                                                           |
| **Default Waypoint Icon** | Sprite | Icon used for all player-created custom waypoints. If not assigned, falls back to **Default Map Icon**.                      |

The inspector warns in edit mode if Default Map Icon or Default Compass Icon is empty, as this will cause POIs to be invisible.

***

#### Custom Waypoints

Controls the runtime waypoint system that lets players (or systems) place navigation markers in the world.

| Property           | Type       | Default | Description                                                                                                                                                                          |
| ------------------ | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Max Waypoints**  | int        | 10      | The hard cap on how many custom waypoints can exist simultaneously. `CreateWaypoint()` returns null and logs a warning once this limit is reached.                                   |
| **Marker Prefab**  | GameObject | —       | An optional 3D prefab instantiated as a child of each waypoint's GameObject at the world-space position. Use for visible in-world pins or flags. Leave empty for map-only waypoints. |
| **Waypoint Color** | Color      | Yellow  | The default `IconColor` assigned to newly created waypoints.                                                                                                                         |

In Play mode the inspector shows a live **Current Waypoints: X / N** counter and confirms how many more can be created.

***

#### POI Elevation Tracking

POI Manager maintains an elevation system that calculates the vertical height difference between the player and each POI. The compass bar can display an up/down indicator and a distance label coloured by severity.

| Property                      | Type   | Default | Description                                                                                                                                                 |
| ----------------------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Enable Elevation Tracking** | bool   | true    | Master toggle. When off, `GetElevationData()` always returns `ElevationData.None`.                                                                          |
| **Min Elevation Threshold**   | float  | 3 m     | Height differences smaller than this are treated as "same level" — no indicator is shown. Prevents jitter from minor terrain undulation.                    |
| **Show Elevation on Compass** | bool   | true    | Whether the elevation indicator is shown on the compass bar.                                                                                                |
| **Elevation Format**          | string | `"0"`   | C# number format string controlling how the elevation distance is displayed. `"0"` = whole metres (e.g. `15m`); `"0.0"` = one decimal place (e.g. `15.5m`). |

**Elevation Ranges** define the colour-coding thresholds:

| Property                   | Default | Meaning                                                                       |
| -------------------------- | ------- | ----------------------------------------------------------------------------- |
| **Close Elevation Range**  | 10 m    | Differences up to this value use **Close Elevation Color** (default green).   |
| **Medium Elevation Range** | 30 m    | Differences up to this value use **Medium Elevation Color** (default yellow). |
| *(above medium)*           | —       | Uses **Far Elevation Color** (default red).                                   |

The elevation cache is keyed by `IPOITarget` reference and capped at 200 entries. Dead or inactive entries are pruned automatically when the cap is reached.

***

#### Events

Fired at runtime as the POI and waypoint lists change. Wire these to UI, audio, or quest systems that need to react to navigation changes.

**POI Events**

| Event                   | Signature               | Fires When                                                                                                                                                                                 |
| ----------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **On POI Registered**   | `UnityEvent<POIMarker>` | A `POIMarker` calls `RegisterPOI()` on Awake or re-enable.                                                                                                                                 |
| **On POI Unregistered** | `UnityEvent<POIMarker>` | A `POIMarker` calls `UnregisterPOI()` on disable or destroy.                                                                                                                               |
| **On POI Updated**      | `UnityEvent<POIMarker>` | Any system calls `UpdatePOI()` for a state change on a specific marker.                                                                                                                    |
| **On POI List Changed** | `MEvent` (no args)      | The full list changes — registration, unregistration, waypoint creation/removal, or active waypoint selection. MinimapUI and WorldMapUI subscribe to this to trigger an icon pool refresh. |

**Waypoint Events**

| Event                   | Signature                    | Fires When                                              |
| ----------------------- | ---------------------------- | ------------------------------------------------------- |
| **On Waypoint Created** | `UnityEvent<CustomWaypoint>` | `CreateWaypoint()` successfully creates a new waypoint. |
| **On Waypoint Removed** | `UnityEvent<CustomWaypoint>` | `RemoveWaypoint()` destroys a waypoint.                 |

***

#### Status & Debug *(Play mode only)*

The bottom section of the inspector is a live dashboard visible during Play mode:

* **Registered POIs** — total count of active POIMarkers
* **Custom Waypoints** — current count vs. the max cap
* **Total Targets** — combined POI + waypoint count
* **POIs by Category** — a breakdown of registered POIs grouped by their `POICategory`
* **Player found** — confirms which GameObject is being used as the player reference, or warns if none is assigned

**Testing Buttons** (Play mode):

| Button                             | Behaviour                                                                                                                                                |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Clear All Waypoints**            | Opens a confirmation dialog, then calls `ClearAllWaypoints()`.                                                                                           |
| **Create Test Waypoint at Player** | Calls `CreateWaypoint()` 10 world units in front of the player. Only shown when a player reference is present and the waypoint cap has not been reached. |

***

### How POI Registration Works

`POIMarker` components self-manage their own registration lifecycle:

1. **OnEnable / Awake** → `POIManager.Instance.RegisterPOI(this)` — the marker is added to the flat list and the category index. `OnPOIRegistered` and `OnPOIListChanged` fire.
2. **OnDisable / OnDestroy** → `POIManager.Instance.UnregisterPOI(this)` — the marker is removed from both collections. `OnPOIUnregistered` and `OnPOIListChanged` fire.

Map and compass UIs subscribe to `OnPOIListChanged` and rebuild their icon pools whenever it fires. This means enabling or disabling a POIMarker at runtime automatically updates every navigation UI with no additional scripting.

***

### Active Navigation Waypoint

POI Manager tracks a single **active navigation waypoint** — the destination the player is currently navigating toward. Both `MinimapUI` and `WorldMapUI` draw a distinct dotted path to this target.

Only one waypoint can be active at a time. Setting a new one automatically replaces the previous selection.

***

### The `IPOITarget` Interface

Defined alongside POI Manager, this interface is the shared contract implemented by both `POIMarker` and `CustomWaypoint`. Map and compass systems operate exclusively on `IPOITarget` so they are agnostic to whether a target is a scene-placed POI or a player-created waypoint.

| Member            | Description                                            |
| ----------------- | ------------------------------------------------------ |
| `WorldPosition`   | The 3D world-space position of the target.             |
| `POIName`         | Display name.                                          |
| `MapIcon`         | Sprite shown on minimap and world map.                 |
| `CompassIcon`     | Sprite shown on the compass bar.                       |
| `IconColor`       | Tint colour.                                           |
| `IsActive`        | Whether the target should currently be visible.        |
| `CurrentDistance` | Cached distance to the player (updated each interval). |
| `ShowOnMinimap`   | Whether to display on the minimap.                     |
| `ShowOnCompass`   | Whether to display on the compass.                     |

***

### Public API

#### POI Registration

| Method                         | Description                                                                                                       |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `RegisterPOI(POIMarker poi)`   | Adds a marker to the managed list and fires events. Called automatically by `POIMarker` — rarely needed manually. |
| `UnregisterPOI(POIMarker poi)` | Removes a marker and fires events. Called automatically by `POIMarker`.                                           |
| `UpdatePOI(POIMarker poi)`     | Fires `OnPOIUpdated` for the given marker. Call when the marker's state changes and you need listeners to react.  |

#### Query Methods

| Method                                    | Returns            | Description                                                                                                 |
| ----------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------- |
| `GetPOIsInRange(float maxDistance)`       | `List<POIMarker>`  | All active POIs within `maxDistance` world units of the player, sorted nearest-first.                       |
| `GetPOIsByCategory(POICategory category)` | `List<POIMarker>`  | All active POIs matching the given category, using the pre-built category index for O(1) lookup.            |
| `GetNearestPOI(POICategory? category)`    | `POIMarker`        | The closest active POI overall, or within a specific category if provided. Returns null if none found.      |
| `GetAllTargets()`                         | `List<IPOITarget>` | Combined list of all active `POIMarker` and `CustomWaypoint` objects via the shared `IPOITarget` interface. |

#### Custom Waypoints

| Method                                          | Returns          | Description                                                                                                                                                          |
| ----------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CreateWaypoint(Vector3 position, string name)` | `CustomWaypoint` | Creates a new waypoint at `position` with the given `name`. Spawns `waypointMarkerPrefab` as a child if assigned. Returns null if the waypoint cap has been reached. |
| `RemoveWaypoint(CustomWaypoint waypoint)`       | void             | Removes the waypoint from the list, destroys its GameObject, and clears the active waypoint if it was the removed one.                                               |
| `ClearAllWaypoints()`                           | void             | Removes and destroys all custom waypoints and clears the active navigation target.                                                                                   |

#### Active Navigation

| Method / Property                            | Description                                                                                           |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `ActiveWaypoint`                             | Read-only. The currently selected navigation target, or null.                                         |
| `SetActiveWaypoint(CustomWaypoint waypoint)` | Sets the active navigation target and fires `OnPOIListChanged` to trigger a map/minimap path refresh. |
| `ClearActiveWaypoint()`                      | Clears the active target and fires `OnPOIListChanged`.                                                |

#### Elevation

| Method                                   | Returns         | Description                                                                                                                                                                     |
| ---------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GetElevationData(IPOITarget target)`    | `ElevationData` | Returns cached elevation data for the target, calculating and caching it on first access.                                                                                       |
| `UpdateAllElevationData()`               | void            | Clears the entire elevation cache, forcing recalculation on the next `GetElevationData()` call. Call after a significant player position change (e.g. teleport or fast travel). |
| `ShouldShowElevation(IPOITarget target)` | bool            | Convenience method; returns `GetElevationData(target).showIndicator`.                                                                                                           |

#### Properties

| Property              | Type                   | Description                                                                 |
| --------------------- | ---------------------- | --------------------------------------------------------------------------- |
| `RegisteredPOIs`      | `List<POIMarker>`      | A copy of the current POI list (safe to iterate without modification risk). |
| `CustomWaypoints`     | `List<CustomWaypoint>` | A copy of the current waypoint list.                                        |
| `PlayerTransform`     | `Transform`            | The resolved player transform, or null if not yet found.                    |
| `CustomWaypointCount` | int                    | Current number of active custom waypoints.                                  |
| `CanCreateWaypoint`   | bool                   | True when `CustomWaypointCount < maxCustomWaypoints`.                       |

***

### Other Tips

* **One POI Manager per project, one scene** — Because the component calls `DontDestroyOnLoad`, placing it in multiple scenes will result in the duplicate being silently destroyed. Place it in your initial loading or persistent scene only.
* **Auto-create fallback** — If no POI Manager exists in the scene at runtime, one is created automatically with default settings. This is safe for quick tests but provides no default icons or configured prefabs. Always have a properly configured instance in your persistent scene for a shipping game.
* **Update Interval vs. accuracy** — The 0.1 s default means distance values are up to 100 ms stale. For fast-moving targets or tight distance checks, lower the interval. For ambient decoration POIs, 0.5 s is perfectly sufficient.
* **Elevation cache lifetime** — The elevation cache is only cleared by `UpdateAllElevationData()` or when it exceeds 200 entries. Call `UpdateAllElevationData()` after any fast travel or scene teleport where the player moves a large vertical distance instantly, otherwise compass indicators will show stale elevation data until the cache naturally refreshes.
* **`GetAllTargets()` returns copies** — Both `RegisteredPOIs` and `CustomWaypoints` return new list copies. Storing the result across frames is safe, but the values won't auto-update when new POIs register. Re-call as needed.
* **Category index is O(1)** — `GetPOIsByCategory()` queries a pre-built `Dictionary<string, List<POIMarker>>` rather than iterating all POIs. Use this instead of `RegisteredPOIs.Where(…)` in performance-sensitive code.
