> For the complete documentation index, see [llms.txt](https://megacrush.gitbook.io/megacrush-unity-assets/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://megacrush.gitbook.io/megacrush-unity-assets/runtime-spawner/runtime-spawner-user-manual/getting-started/specials-telemetry-system/how-to-using-specials-telemetry.md).

# How to: Using Specials Telemetry

## Prerequisites

* Runtime Spawner is in your scene and calls `Init(...)` → `Begin()` on startup.
* A `SpecialEncounterManager` is present and assigned a `SpecialProfile` with at least one `SpecialRule`.
* Your project has TextMeshPro and the Diagnostics HUD in the scene (or you’ve added it).

## 1) Choose how you’ll provide telemetry

You can feed “Pressure” and “Average Player HP” into specials via either:

* **Option A (Inspector / ScriptableObject):** subclass `SpecialsTelemetryProvider` and assign the asset on the manager.
* **Option B (Code / Interface):** implement `ISpecialsTelemetry` and inject the instance at runtime.

Both options can coexist; if you call `SetTelemetryProvider(...)` at runtime, that overrides the SO field.

***

## 2) Option A - ScriptableObject provider (Inspector setup)

1. Create a provider asset:
   * Right‑click in Project: **Create → MegaCrush → Spawner → Specials Telemetry** (use your subclass or the `BasicSpecialsTelemetry` sample under `Samples~` for testing).
2. Assign it:
   * Select your `SpecialEncounterManager` in the scene.
   * Drag the provider asset into the **Telemetry Provider** field.
3. Hook your real data (recommended):
   * In your subclass of `SpecialsTelemetryProvider`, implement:

     ```csharp
     public override bool TryGetPressure(out float value) { /* set value */ }
     public override bool TryGetAveragePlayerHP(out float value) { /* set value */ }
     ```
   * Pull values from your game systems (difficulty/AI director, health system, etc.).

Notes:

* The sample asset exposes static sliders - useful to prove the pipeline works, then replace with a real subclass.

***

## 3) Option B - Code provider (runtime injection)

1. Implement the interface anywhere in your game:

   ```csharp
   using MegaCrush.Spawner;

   public sealed class MyTelemetry : ISpecialsTelemetry
   {
       public bool TryGetPressure(out float value)
       {
           // Example: normalized by your max enemy budget
           value = CurrentEnemyCount / (float)MaxEnemyBudget;
           return true;
       }

       public bool TryGetAveragePlayerHP(out float value)
       {
           // Example: average over all active players (0..1)
           value = ComputeAveragePlayerHealthNormalized();
           return true;
       }
   }
   ```
2. Inject it before specials begin:

   ```csharp
   // wherever you bootstrap spawner systems after SpecialEncounterManager.Init(...)
   var specials = FindAnyObjectByType<SpecialEncounterManager>();
   specials.SetTelemetryProvider(new MyTelemetry());
   ```

***

## 4) Configure rules that use telemetry

Open your `SpecialProfile` and for any `SpecialRule` you want gated:

* Set **minPressure** to a threshold (e.g., `0.75`).
* Set **minAvgPlayerHP** (0..1) if you want to avoid spawning when players are very low.
* Ensure **stepRange**, **maxAlive**, **evalEverySeconds**, **cooldownSeconds**, **distanceRange**, and **spawnTag** are configured sensibly.

Behavior:

* If a rule requires telemetry but no provider is available, the rule’s condition fails (special won’t spawn). Everything else continues to work.

***

## 5) Enable the Diagnostics HUD timing (optional but recommended)

If you’ve updated the HUD with the added fields:

* Assign in the HUD inspector:
  * **SpecialsTimerText**
  * **SpecialsLastSpawnText**
  * **SpecialsNextTagText**
* In play mode, you’ll see:
  * “Specials: next in Xs (at Y.s)” - the next evaluation ETA.
  * “Last special: Ns ago” - last successful special time.
  * “Next Tag: …” - the planned tag (best effort debug hint).

The Population section’s **Total Active** shows the live count, and the per‑source breakdown helps confirm sources (Global/Region/Wave/Special).

***

## 6) Quick test workflow

1. Put your player into the scene and ensure `RuntimeSpawner` knows the **PlayerTransform**.
2. Use the sample `BasicSpecialsTelemetry` asset (Option A) and set:
   * `pressure = 0.9`
   * `averagePlayerHP = 1.0`
3. In your `SpecialProfile`, set one rule with:
   * `minPressure = 0.8`
   * `minAvgPlayerHP = 0.5`
   * Reasonable `evalEverySeconds` (e.g., 1–3s) and `cooldownSeconds` (e.g., 10–20s).
4. Enter Play:
   * Watch the HUD: ETA should count down; last spawn updates when a special appears.
   * If you’re step‑gated, advance your Director step and verify the HUD updates “Intensity Step”.

Tip (Editor only): you can use the context menu **Force Specials Roll Now** on `SpecialEncounterManager` to arm all rules immediately. This is wrapped in `#if UNITY_EDITOR` and excluded from builds.

***

## 7) Troubleshooting

* **“Specials: (unscheduled)”**\
  Likely all rules are ineligible (step gating, no rules, or evaluation times in the future). Advance the Director step or reduce thresholds.
* **No spawns despite positive thresholds**\
  Check:
  * `minGapSeconds` on the profile (global cooldown).
  * `maxAlive` for the rule (you may be at cap).
  * Anchor availability: ensure `WaveSpawnPoint`s exist with matching `spawnTag`, correct `distanceRange`, and LOS setting (if `requireNoLOS`, confirm an occluder actually blocks).
  * Telemetry provider returns `true` and supplies values that meet thresholds.
* **HUD “Total Active” climbs forever**\
  Ensure you’re on the updated HUD that uses `_pop.Current` for the live count.
* **Nothing happens after scene load**\
  Confirm your flow calls: `RuntimeSpawner.Init(...)` → `SpecialEncounterManager.Init(...)` (done by spawner) → `RuntimeSpawner.StartSpawners()` → `SpecialEncounterManager.Begin()` (called by spawner).
