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

# POI Filter Manager

### Overview

**POI Filter Manager** is the singleton gatekeeper that decides which Points of Interest are visible on the minimap, compass, and world map. Rather than having each navigation surface manage its own visibility logic, every surface calls `ShouldDisplayPOI()` and defers the decision entirely to this component.

Filters are organised into four independent layers evaluated in sequence: quick filters (broad overrides), category filters (per-type toggles), and a distance filter. When `Remember Filters` is enabled, the player's last-used state is persisted between sessions via `PlayerPrefs`, so the map remembers which icon types they had toggled off.

<figure><img src="https://963537199-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lzhr1XSMzMqNXjRnNlb%2Fuploads%2FkKHn1cM1lOxCoomI0ncf%2Fimage.png?alt=media&amp;token=ecffd222-3360-4b7b-a935-b44eacf3e3c2" alt=""><figcaption></figcaption></figure>

***

### Scene Setup

Add a single **POI Filter Manager** component to any persistent GameObject (typically alongside `POIManager`). The component is a singleton — duplicate instances are silently destroyed in Awake. The component does not call `DontDestroyOnLoad`, so place it in your persistent scene.

`POIFilterManager` is optional. If no instance is present in the scene, navigation UIs treat all POIs as visible (no filtering is applied).

***

### Inspector Sections

#### Filter Settings

Top-level controls for the entire system.

| Property             | Type | Default | Description                                                                                                                                                                    |
| -------------------- | ---- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Enable Filtering** | bool | true    | Master toggle. When off, `ShouldDisplayPOI()` returns `true` for every POI unconditionally. All other settings are ignored.                                                    |
| **Remember Filters** | bool | true    | When enabled, every filter change is immediately written to `PlayerPrefs` and the saved state is loaded in Start. Preserves the player's map preferences across play sessions. |

The inspector confirms filtering is active, or warns when it has been disabled.

***

#### Category Filters

One toggle per `POICategory`. When a category is disabled, every POI of that type is hidden on all navigation surfaces.

| Property             | Category Hidden When Off                                       |
| -------------------- | -------------------------------------------------------------- |
| **Quest Objectives** | `POICategory.QuestObjective`                                   |
| **Quest Givers**     | `POICategory.QuestGiver`                                       |
| **Waypoints**        | `POICategory.Waypoint` (includes all `CustomWaypoint` objects) |
| **Locations**        | `POICategory.Location`                                         |
| **Enemies**          | `POICategory.Enemy`                                            |
| **NPCs**             | `POICategory.NPC`                                              |
| **Items**            | `POICategory.Item`                                             |
| **Merchants**        | `POICategory.Merchant`                                         |
| **Fast Travel**      | `POICategory.FastTravel`                                       |
| **Custom**           | `POICategory.Custom`                                           |

The inspector shows a live count confirming how many of the 10 categories are currently enabled (e.g. *✓ 7 of 10 categories visible*).

***

#### Quick Filters

Broad override rules that operate independently of the category toggles. Each quick filter is applied before category filters are checked.

| Property                      | Type | Default | Description                                                                                                                                           |
| ----------------------------- | ---- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Only Quest Markers**        | bool | false   | When on, only POIs with category `QuestObjective` pass through. All other categories — including ones enabled in Category Filters — are hidden.       |
| **Hide Completed Objectives** | bool | true    | Suppresses any `POIMarker` whose linked objective is currently marked as completed in `QuestManager`. POIs without a linked objective are unaffected. |
| **Only Tracked Quests**       | bool | false   | Only shows POIs whose linked quest has `isTracked = true` on its `QuestInstance`. POIs without a linked objective are hidden when this is on.         |

Active quick filters are confirmed with an info message in the inspector.

***

#### Distance Filter

An optional global distance cap applied on top of the per-POI `MaxDisplayDistance` setting.

| Property                 | Type  | Default | Description                                                                                                                                                       |
| ------------------------ | ----- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Use Distance Filter**  | bool  | false   | Activates the global distance cap.                                                                                                                                |
| **Max Display Distance** | float | 200 m   | Any POI whose `CurrentDistance` exceeds this value is hidden, regardless of its own `MaxDisplayDistance` setting. Only shown when Use Distance Filter is enabled. |

> **Distance filter vs. POI asset range** — The per-POI `MinDisplayDistance`/`MaxDisplayDistance` settings on the `PointOfInterest` asset are enforced by the minimap and compass rendering code, not here. POI Filter Manager's distance filter is an additional global cap; both limits must be satisfied for a POI to appear.

***

#### Events

| Event                  | Signature              | Description                                                                                                                                                              |
| ---------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **On Filters Changed** | `UnityEvent` (no args) | Fired after any filter is modified via the public API. All navigation surfaces (minimap, compass, world map) subscribe to this and refresh their icon pools in response. |

***

#### Validation & Testing

**In edit mode**: Warns if filtering is disabled; confirms configuration is valid otherwise.

**In Play mode**, the section shows live runtime status and provides testing buttons:

| Button                  | Effect                                                          |
| ----------------------- | --------------------------------------------------------------- |
| **Show All Categories** | Calls `ShowAllCategories()` — enables all 10 category filters.  |
| **Hide All Categories** | Calls `HideAllCategories()` — disables all 10 category filters. |
| **Preset: Quests Only** | Calls `PresetQuestsOnly()`.                                     |
| **Preset: Exploration** | Calls `PresetExplorationOnly()`.                                |

***

### Filter Evaluation Order

`ShouldDisplayPOI(IPOITarget target)` evaluates gates in this sequence and returns `false` at the first failing gate:

| Step | Gate                          | Fails When                                                                     |
| ---- | ----------------------------- | ------------------------------------------------------------------------------ |
| 0    | **Filtering disabled**        | `enableFiltering` is false → returns `true` immediately (no filtering).        |
| 1    | **Null check**                | `target` is null.                                                              |
| 2    | **Only Quest Markers**        | `onlyShowQuestMarkers` is true and the POI's category is not `QuestObjective`. |
| 3    | **Only Tracked Quests**       | `onlyShowTrackedQuests` is true and the POI is not linked to a tracked quest.  |
| 4    | **Hide Completed Objectives** | `hideCompletedObjectives` is true and the linked objective is completed.       |
| 5    | **Category filter**           | The POI's category has its category filter set to false.                       |
| 6    | **Distance filter**           | `useDistanceFilter` is true and `target.CurrentDistance > maxDisplayDistance`. |
| —    | **Pass**                      | All gates passed → returns `true`.                                             |

***

### Built-In Presets

Three preset methods provide one-call configuration for common map modes:

| Preset Method             | What It Enables                                                           |
| ------------------------- | ------------------------------------------------------------------------- |
| `PresetQuestsOnly()`      | Hides all categories, then enables: QuestObjective, QuestGiver, Waypoint. |
| `PresetExplorationOnly()` | Hides all categories, then enables: Location, FastTravel, Waypoint.       |
| `PresetCombatOnly()`      | Hides all categories, then enables: Enemy only.                           |

All three call `HideAllCategories()` first and then selectively re-enable categories, so they produce a clean slate regardless of prior state. If `rememberFilters` is true, the preset state is saved immediately.

***

### PlayerPrefs Keys

When **Remember Filters** is enabled, the following keys are written on every filter change and read on Start:

| Key                        | Stores                                                                     |
| -------------------------- | -------------------------------------------------------------------------- |
| `POIFilter_{CategoryName}` | `1` or `0` for each of the 10 categories (e.g. `POIFilter_QuestObjective`) |
| `POIFilter_OnlyQuests`     | `1` / `0`                                                                  |
| `POIFilter_HideCompleted`  | `1` / `0`                                                                  |
| `POIFilter_OnlyTracked`    | `1` / `0`                                                                  |
| `POIFilter_UseDistance`    | `1` / `0`                                                                  |
| `POIFilter_MaxDistance`    | float                                                                      |

To reset a player's saved filter preferences, call `PlayerPrefs.DeleteAll()` or target the specific keys above.

***

### Public API

#### Core Query

| Method                                | Returns | Description                                                                                                   |
| ------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------- |
| `ShouldDisplayPOI(IPOITarget target)` | bool    | The primary query used by all navigation surfaces. Returns true if the target passes all active filter gates. |

#### Category Filters

| Method                                                    | Description                                                                                                       |
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `SetCategoryFilter(POICategory category, bool isVisible)` | Sets a specific category to shown or hidden. Fires `OnFiltersChanged` and saves if `rememberFilters` is on.       |
| `GetCategoryFilter(POICategory category)`                 | Returns the current visibility state for a category. Defaults to `true` if the category is not in the dictionary. |
| `ToggleCategoryFilter(POICategory category)`              | Flips the current state of a category. Fires `OnFiltersChanged` and saves.                                        |
| `ShowAllCategories()`                                     | Enables all 10 category filters. Fires `OnFiltersChanged`.                                                        |
| `HideAllCategories()`                                     | Disables all 10 category filters. Fires `OnFiltersChanged`.                                                       |

#### Quick Filters

| Method                                   | Description                                      |
| ---------------------------------------- | ------------------------------------------------ |
| `SetOnlyQuestMarkers(bool value)`        | Sets the Only Quest Markers quick filter.        |
| `SetHideCompletedObjectives(bool value)` | Sets the Hide Completed Objectives quick filter. |
| `SetOnlyTrackedQuests(bool value)`       | Sets the Only Tracked Quests quick filter.       |

#### Distance Filter

| Method                                                      | Description                                                                |
| ----------------------------------------------------------- | -------------------------------------------------------------------------- |
| `SetDistanceFilter(bool enabled, float maxDistance = 200f)` | Enables or disables the global distance cap and sets the maximum distance. |

#### Presets

| Method                    | Description                          |
| ------------------------- | ------------------------------------ |
| `PresetQuestsOnly()`      | Applies the Quests Only preset.      |
| `PresetExplorationOnly()` | Applies the Exploration Only preset. |
| `PresetCombatOnly()`      | Applies the Combat Only preset.      |

***

### Connecting the Filter UI

`POIFilterManager` provides the runtime state; the in-game filter panel that the player interacts with is a separate `POIFilterUI` component. That UI component calls these API methods in response to player button presses, and the `OnFiltersChanged` event propagates the change to all navigation surfaces.

***

### Other Tips

* **`OnFiltersChanged` is the refresh signal** — Every map, minimap, and compass surface subscribes to this event and rebuilds its visible icon set when it fires. If you set filter state by modifying serialized fields directly (e.g. in a save/load system restoring a game state), call `OnFiltersChanged.Invoke()` manually afterwards to ensure navigation UIs update.
* **Only Tracked Quests hides unlinked POIs** — When `onlyShowTrackedQuests` is true, any POI with no `linkedObjectiveId` is considered "not tracked" and hidden. This means merchants, ambient NPCs, and other non-quest markers all disappear. Combine with `PresetQuestsOnly()` if that's the intended behaviour, but be aware of it if only some markers have quest links.
* **Hide Completed Objectives needs QuestManager** — If `QuestManager` is not in the scene, `IsCompletedObjective()` always returns false, so this quick filter has no effect even when enabled.
* **Distance filter stacks, not replaces** — `maxDisplayDistance` here is a secondary hard cap. A POI with `MaxDisplayDistance = 500m` in its asset will still be hidden by this filter if the player sets it to 200m. Design the per-POI distance settings as "intended range" and the filter distance as "player preference".
* **Clearing saved preferences** — During development, filter state saved by `PlayerPrefs` can cause unexpected map behaviour after changing default inspector values. Use **Edit → Clear All PlayerPrefs** or the `PlayerPrefs.DeleteAll()` console command to reset to inspector defaults.
