> 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/telemetry-providers-samples-and-patterns.md).

# Telemetry Providers - Samples & Patterns

## Telemetry Provider (Optional)

**Why it matters**\
Rules can require *Pressure* or *Average Player HP* values. These are **game-specific** and cannot be hardcoded.\
The manager queries a **Telemetry Provider** at runtime:

* If available → values are compared against rule thresholds.
* If missing → the condition automatically fails (safe default, no spawn).

**Supplying Telemetry**

1. **ScriptableObject Provider (Inspector)**
   * Base: `SpecialsTelemetryProvider` (abstract).
   * Sample: `BasicSpecialsTelemetry` (in `Samples~`) – lets you enter static test values.
   * Workflow:
     * Create via *Assets → Create → MegaCrush → Spawner → Specials Telemetry*.
     * Assign to **Telemetry Provider** field.
2. **Code Provider (ISpecialsTelemetry)**
   * Implement the `ISpecialsTelemetry` interface in your game system (e.g. GameState, DifficultyManager).
   * Inject at runtime:

     ```csharp
     encounterManager.SetTelemetryProvider(myCustomProvider);
     ```

**Telemetry Hooks**

* **Pressure** (float, any scale: 0–1 recommended).
  * e.g. `currentEnemies / maxEnemies`
  * e.g. director intensity level
* **Average Player HP** (float 0–1).
  * e.g. sum of all players’ health% / player count

***

#### System Flow (Telemetry Evaluation)

```
 ┌───────────────────────────────┐
 │   SpecialEncounterManager     │
 │   (runs each eval interval)   │
 └───────────────┬───────────────┘
                 │
                 ▼
     ┌─────────────────────┐
     │   Special Rule(s)   │
     │ - Step Range        │
     │ - Alive Cap         │
     │ - Cooldowns         │
     │ - Telemetry Gates?  │
     └─────────┬───────────┘
               │
   Needs Pressure / Avg HP?
               │
        ┌──────┴────────┐
        ▼               ▼
┌────────────────┐  ┌─────────────────┐
│ Telemetry      │  │ No Provider Set │
│ Provider       │  │ (rule fails     │
│ (SO or code)   │  │ quietly, no     │
│                │  │ spawn occurs)   │
└───────┬────────┘  └─────────────────┘
        │
        ▼
┌──────────────────────────────┐
│ Values returned (Pressure,   │
│ Avg Player HP, etc)          │
└───────────┬──────────────────┘
            │
   Compare vs thresholds
            │
            ▼
 ┌─────────────────────────────┐
 │  Rule passes → spawn agent  │
 │  Rule fails  → no spawn     │
 └─────────────────────────────┘
```

### BasicSpecialsTelemetry (included in Samples)

**Purpose**\
`BasicSpecialsTelemetry` is a minimal **ScriptableObject** provider you can use to test Special Encounter rules without wiring any real game systems. It exposes two sliders-**Pressure** and **Average Player HP-**&#x74;hat the `SpecialEncounterManager` will read during rule evaluation.

```csharp
// Samples~/.../BasicSpecialsTelemetry.cs
using UnityEngine;

namespace MegaCrush.Spawner.Samples
{
    [CreateAssetMenu(menuName = "Runtime Spawner/Specials Telemetry/Basic Sample")]
    public sealed class BasicSpecialsTelemetry : SpecialsTelemetryProvider
    {
        [Range(0f, 1f)] public float pressure = 0f;
        [Range(0f, 1f)] public float averagePlayerHP = 1f;

        public override bool TryGetPressure(out float value) { value = pressure; return true; }
        public override bool TryGetAveragePlayerHP(out float value) { value = averagePlayerHP; return true; }
    }
}
```

#### When to use it

* You’re validating **Special Rules** (thresholds, cooldowns, step ranges) before your gameplay systems are ready.
* You want to see how changing **Pressure** or **Avg HP** impacts encounter timing in real time.
* You’re building automated tests around rules that need deterministic values.

#### Create & Assign (Quickstart)

1. **Create the asset**\
   `Assets → Create → Runtime Spawner → Specials Telemetry → Basic Sample`
2. **Assign it on the Manager**\
   In the **Special Encounter Manager** inspector, set **Telemetry Provider** to your new `BasicSpecialsTelemetry` asset.
3. **Play Mode**\
   Tweak the **Pressure** and **Average Player HP** sliders on the asset while the game runs and observe:
   * Rule pass/fail based on thresholds
   * Spawn frequency and tags
   * “Runtime Status” in the Manager inspector (Next/Last spawn times)

#### What the sliders mean

* **Pressure**: Any scalar you choose (commonly 0–1). Higher = more stress/intensity.
* **Average Player HP**: Mean health across players (0–1). Lower = team is hurt.

> Note: Rule checks read these values via the provider on each evaluation. If a rule requires telemetry and **no provider** is assigned, that condition **fails safely** (no spawn).

***

### Extending Beyond the Sample

#### Base Class: `SpecialsTelemetryProvider`

Use this when you want an **inspector-driven** provider (SO asset) that pulls values from your game.

```csharp
public abstract class SpecialsTelemetryProvider : ScriptableObject, ISpecialsTelemetry
{
    public abstract bool TryGetPressure(out float value);
    public abstract bool TryGetAveragePlayerHP(out float value);
}
```

**Pattern: SO provider that reads from your systems**

```csharp
using UnityEngine;

public sealed class GameLinkedTelemetry : SpecialsTelemetryProvider
{
    public override bool TryGetPressure(out float value)
    {
        // Example: read from a singleton/service
        value = MyGame.Difficulty.CurrentPressure01;
        return true;
    }

    public override bool TryGetAveragePlayerHP(out float value)
    {
        value = MyGame.Players.AverageHealth01;
        return true;
    }
}
```

Pros

* Easy to **swap** per scene/profile.
* Editable references in the inspector.
* Works with addressables/asset workflows.

Cons

* Slightly more setup than a pure code approach.

#### Interface: `ISpecialsTelemetry`

Use this when you want to inject a **pure code** provider at runtime.

```csharp
public interface ISpecialsTelemetry
{
    bool TryGetPressure(out float value);
    bool TryGetAveragePlayerHP(out float value);
}
```

**Pattern: Code provider injected at runtime**

```csharp
public sealed class RuntimeTelemetry : ISpecialsTelemetry
{
    private readonly DifficultyManager _dm;
    private readonly PlayerService _players;

    public RuntimeTelemetry(DifficultyManager dm, PlayerService players)
    { _dm = dm; _players = players; }

    public bool TryGetPressure(out float value)
    {
        value = _dm.Pressure01;
        return true;
    }

    public bool TryGetAveragePlayerHP(out float value)
    {
        value = _players.GetAverageHealth01();
        return true;
    }
}

// Injection
var mgr = FindObjectOfType<SpecialEncounterManager>();
mgr.SetTelemetryProvider(new RuntimeTelemetry(difficulty, players));
```

Pros

* No assets required.
* Easy to **unit test**.
* Perfect for service/DI architectures.

Cons

* Not directly swappable via inspector.

***

### Choosing a Scale

* **Recommended**: Normalize to **0–1** for both Pressure and Avg HP so thresholds are easy to reason about.
* If you use a different scale, keep it **consistent** across rules and providers.

***

### Common Rule Examples (With Telemetry)

* **Miniboss under pressure**
  * `Min Pressure ≥ 0.75`, `Step Range 3–99`, `Cooldown 180s`
* **Flankers only when team is healthy**
  * `Min Avg HP ≥ 0.6`, `Require No LOS = true`, `Distance 18–32m`
* **Snipers when players are low**
  * Use *reverse* gate by creating two variants:
    * Rule A (sniper): `Min Pressure ≥ 0.5`, `Min Avg HP ≥ 0.3`
    * Baseline population reduces to make room (via director/other rules)

***

### Troubleshooting

* **Nothing spawns despite rules valid**
  * Check **Max Simultaneous Specials** and **Min Gap Seconds** on the Profile.
  * Ensure **alive cap** for the rule isn’t already reached.
  * Verify **Step Range** matches the Director’s current step.
  * Confirm **Telemetry Provider** is assigned (or injected) if the rule uses telemetry.
* **Require No LOS never passes**
  * Set **Occluder Mask** to include geometry layers that should block line‑of‑sight.
  * Verify anchors are placed behind occluders relative to the player.
* **Provider values aren’t changing**
  * If using an SO provider, you can tweak asset fields **at runtime**.
  * If using a code provider, ensure you call `SetTelemetryProvider(...)` **after** your systems are initialized and that your getters return updated values each frame.

***

### Quick Checklist

1. Create a **Special Profile**, add rules with telemetry thresholds.
2. Assign **BasicSpecialsTelemetry** to the Manager to validate behavior.
3. Replace the sample with a **Game‑linked SO provider** or **code provider** implementing `ISpecialsTelemetry`.
4. Use the Manager’s **Runtime Status** and **Force Roll Now** to iterate quickly.
