# Software-Konzept & Spielablauf

Dieses Dokument beschreibt die Software-Architektur, die Rollenverteilung und die Kommunikationsabläufe des Lasertag-Systems.

## Überblick

* Architektur: Leader als BLE/Thread-Brücke, Westen als Spiel-Authority für Health/Regeln, Waffen als Sensor/Aktor.
* Funk: BLE für Provisionierung/App, Thread (CoAP/UDP) für Spielverkehr, IR für Treffer-Übertragung.
* Prinzip: Dezentraler Trefferentscheid (auf der Weste) mit optionalem Live-Ticker zum Leader; Leader hält Spielstatus und sammelt Logs.

## 1. System-Rollen & Hardware-Typen

Das System basiert auf nRF52840-Chips, die über OpenThread (802.15.4) kommunizieren. Am wichtigsten dabei ist die Kommunikation von der Weste zur Waffe, da die Waffe beim Death des Spielers sofort deaktiviert werden muss. Die OpenThread Message Priority soll verwendet werden, um die Weste-Waffe-Kommunikation zusätzlich zu optimieren. Multicasts: Low, Weste -> Waffe enable und disable: High, Rest normal.

### A. Leader Box (Game Controller)
* **Funktion:** Zentrale Spielsteuerung, Zeitgeber, Gateway zur Smartphone-App.
* **Modi (wählbar via DIP-Schalter):**
    * `00` **Leader:** Spielleiter, BLE-Gateway, sammelt Punkte.
    * `01` **Repeater:** Router im Mesh zur Reichweitenverlängerung (z.B. am Baum).
    * `11` **Base:** Interaktives Ziel (z.B. für "Domination"-Modus).
* **Hardware:** IR-Empfänger, RGB-LEDs, BLE aktiv.

### B. Weste (Player Hub)
* **Funktion:** Zentrale Einheit des Spielers. Verwaltet Lebenspunkte, empfängt Treffer, steuert Audio.
* **Sensoren:** Kopf (3x: Stirn, Links, Rechts), Brust/Rücken, Seiten (Schultern/Arme).
* **Kommunikation:** Hält die Verbindung zur Waffe (Pairing) und zum Leader.

### C. Waffe
* **Funktion:** Aussenden der IR-Signale, haptisches Feedback, Muzzle-Flash.
* **Typen:** Pistole, Sniper, Shotgun (unterschiedliche IR-Leistung/Fokussierung).
* **Logik:** Sendet "Schuss"-Events, empfängt "Sperren"-Befehle von der Weste (wenn tot).

---

## 2. Provisionierung & Setup (Lobby-Phase)

Bevor das Spiel startet, müssen Geräte dem Netzwerk beitreten und Spielern zugeordnet werden.

### Schritt 1: Netzwerk-Beitritt (Provisioning)
* **Szenario:** Neue Hardware wird zum ersten Mal verwendet.
* **Ablauf:**
    1. Spielleiter verbindet App via BLE mit **Leader Box**.
    2. Leader Box öffnet das Thread-Netzwerk (Commissioning).
    3. Neue Geräte (Waffe/Weste) werden in den Pairing-Modus versetzt (z.B. Tastenkombination).
    4. Geräte erhalten Netzwerk-Credentials und treten dem Mesh bei.

### Schritt 2: Spieler-Konfiguration (Assignment)
* **Ziel:** Zuordnung von Hardware zu einer logischen `PlayerID` und einem `Team`.
* **Identifikation:** Jedes nRF52-Board hat eine eindeutige **EUI-64** (MAC).
* **Ablauf:**
    1. App scannt **QR-Code** oder **NFC-Tag** an der Weste/Waffe (Payload: EUI-64 Adresse).
    2. App sendet Konfiguration an Leader Box: `EUI-64 -> {PlayerID, Team, Name}`.
    3. Leader Box löst EUI-64 in aktuelle IPv6-Adresse auf (Neighbor Table / Discovery).
    4. Leader sendet Konfigurations-Paket (CoAP Unicast) an das Gerät:
        * **Weste:** Erhält PlayerID, TeamID, MaxHealth, evtl. Rolle.
        * **Waffe:** Erhält Damage-Wert, Nachladezeit, Magazingröße, Waffentyp.
    5. Geräte speichern ID/Team im RAM (optional NVS für Persistenz bei Neustart).

### Provisioning – Sequenzdiagramm

```mermaid
sequenceDiagram
    actor User as Spielleiter
    participant App
    participant Leader
    participant Weste
    participant Waffe

    User->>App: Verbindung via BLE
    App->>Leader: Thread-Credentials (BLE)
    Leader->>Leader: Öffne Mesh für Provisioning
    
    User->>Weste: Pairing-Modus aktiviert
    User->>Waffe: Pairing-Modus aktiviert
    
    Weste->>Leader: Beitritt ins Thread-Netz
    Waffe->>Leader: Beitritt ins Thread-Netz
    
    User->>App: QR-Code Weste scannen
    App->>Leader: PlayerID 1, Team=Rot, Name="Alice"
    Leader->>Weste: Konfig (CoAP PUT /game/conf)
    Weste->>Weste: Speichere PlayerID=1, Team=Rot
    
    User->>App: QR-Code Waffe scannen
    App->>Leader: PlayerID 1, Waffentyp=Pistol
    Leader->>Waffe: Konfig (CoAP PUT /game/wconf)
    Waffe->>Waffe: Speichere Damage=10, ReloadTime=500ms
    
    App->>Leader: "Spieler bereit?"
    Leader->>App: Ping alle Knoten → OK
```

---

## 3. Spielablauf (Game Loop)

### Phase A: Vorbereitung
* **Leader:** Sendet Multicast `GAME_STATE_LOBBY`.
* **Geräte:** Spielen Idle-Animation ab, warten auf Start.
* **Check:** Leader kann "Ping" an alle senden, um Anwesenheit zu prüfen.

### Phase B: Countdown
* **Leader:** Sendet Multicast `GAME_START_COUNTDOWN` (Payload: 10 sek).
* **Westen:** Zählen laut herunter: "10, 9, 8...".

### Phase C: Spiel läuft (Running)
* **Status:** `GAME_STATE_RUNNING`.
* **Aktion:** Waffen sind entsperrt. Sensoren sind scharf.
* **Treffer-Logik (Dezentral):**
    1. Waffe A schießt (sendet IR-Frame mit `Type=Hit`, `ShooterID`, `Damage`, `CRC8`).
    2. Weste B empfängt IR-Signal über TSOP4838, validiert CRC.
    3. Weste B berechnet Schaden (unter Berücksichtigung von Trefferzone-Multiplikator).
    4. Weste B zieht Lebenspunkte ab.
    5. **Feedback:** Weste B leuchtet/vibriert/spielt Sound ("Ugh!").
    6. **Speicherung:** Weste B speichert den Treffer im internen Flash-Log (`Timestamp, ShooterID, Zone, Damage`).
    7. *(Optional)* Weste B sendet UDP-Paket an Leader für Live-Scoreboard (Best Effort).

!!! info "Warum kein MilesTag2?"
    MilesTag2 wurde als Basis erwogen, ist aber mit ~40 ms Frame-Zeit und starren 8-Bit-IDs zu langsam und unflexibel. Unser Custom-Protokoll bietet:
    
    - **Kürzere Frames:** ~36 ms vs. ~40 ms (weniger anfällig für Zittern/Bewegung)
    - **Flexible Type-Codes:** Hit/Heal/PowerUp/Admin in einem Format
    - **CRC8-Prüfung:** >99.5% Fehlerrate-Erkennung bei Sonnenlicht
    - **Variable Daten:** 13-Bit-Payload anpassbar pro Type
    
    Details siehe [IR-Protokoll-Spezifikation](../specifications/ir_protocol.md).
* **Heilquellen:** Medic/Medipack-IR (breit gestreut, kurze Reichweite, negativer Damage) werden als Heilung interpretiert.
* **Zonen-Effekte:** Bases/Joiner senden `game/zone` (Link-Local, Hop=1); Weste prüft RSSI-Schwelle und addiert HP-Deltas (friend/foe) nach optionalem Warn-Countdown.

### Phase D: Spieler eliminiert
* **Bedingung:** Lebenspunkte <= 0.
* **Weste B:**
    * Spielt "Dead"-Sound.
    * Leuchtet dauerhaft in Teamfarbe (oder aus).
    * Sendet CoAP Unicast an **eigene Waffe**: `CMD_DISABLE`.
* **Respawn (falls aktiv):**
    * Nach Zeitablauf (z.B. 30s) sendet Weste an Waffe: `CMD_ENABLE`.
    * Lebenspunkte werden zurückgesetzt.

### Phase E: Spielende & Auswertung
* **Leader:** Sendet Multicast `GAME_STATE_FINISHED`.
* **Ablauf:**
    1. Alle Spieler kommen zusammen.
    2. Spielleiter drückt in App "Daten abrufen".
    3. Leader Box fragt nacheinander (Unicast) alle bekannten Westen ab: `GET /game/log`.
    4. Westen übertragen ihre Treffer-Historie.
    5. App berechnet Highscores, MVP, Trefferquoten.

### Game Loop – Zustandsautomat (Weste)

```mermaid
stateDiagram-v2
    [*] --> Idle
    
    Idle --> Lobby: GAME_STATE_LOBBY empfangen
    Lobby --> Countdown: GAME_START_COUNTDOWN empfangen
    Countdown --> Running: Countdown = 0
    
    Running --> Running: IR-Hit empfangen → Damage abzug, Log
    Running --> Dead: Health <= 0
    
    Dead --> Dead: Waffe CMD_DISABLE senden
    Dead --> Running: Respawn aktiviert (CMD_ENABLE)
    
    Running --> Finished: GAME_STATE_FINISHED
    Dead --> Finished: GAME_STATE_FINISHED
    
    Finished --> [*]
    
    note right of Running
        - Health-Tracking
        - Treffer-Log
        - LED/Audio-Feedback
    end note
    
    note right of Dead
        - Waffe gesperrt
        - LED dauerhaft an
        - Optional Respawn-Timer
    end note
```

---

## 4. Spielmodi

Die Logik für die Modi wird primär auf den Westen implementiert (Regelwerk), gesteuert durch Flags vom Leader.

### Team Deathmatch
* Klassisch Rot gegen Blau.
* Friendly Fire konfigurierbar (an/aus).
* Siegbedingung: Meiste Kills oder wenigste Tode nach Zeitablauf.

### Last Man Standing (Free-for-all)
* Jeder gegen Jeden.
* Keine Teams (oder jeder hat eigene Team-ID).
* Kein Respawn.

### Zombie (Infected)
* **Start:** 1 Spieler ist "Zombie" (Team Grün), Rest "Mensch" (Team Rot).
* **Regel:**
    * Zombie hat unendlich Leben (oder sehr viel).
    * Mensch hat 1 Leben.
    * Wird Mensch getroffen -> Wechselt Team zu Zombie (Weste leuchtet grün, Waffe sendet ab jetzt Zombie-ID).
    * Wird Zombie getroffen -> "Stunned" (Waffe 5s gesperrt).
* **Ziel:** Überleben bis Zeitablauf.

### Base Domination
* **Hardware:** Leader-Boxen im Modus `11` (Base) verteilt im Gelände.
* **Ablauf:**
    * Spieler schießt auf Base-Box.
    * Base-Box wechselt Farbe zu Teamfarbe des Schützen.
    * Base-Box zählt Zeit für das haltende Team.
    * Am Ende fragt Leader alle Base-Boxen ab: "Wie lange warst du Rot? Wie lange Blau?".

### Medic & Heal Packs
* **Rollen:**
    * **Medic-Spieler:** Rolle `Medic` in der Spieler-Konfiguration; deren Waffe feuert ein "Heal-IR" mit breiter Streuung und sehr kurzer Reichweite.
    * **Medipacks (Objekte):** Aktiv durch Tastendruck; senden erst nach Button-Press ein Heal-IR mit breiter Streuung.
* **Wirkung:**
    * Heal-IR ist als eigene Damage-Class kodiert (negativer Schaden → Heilung).
    * Reichweite absichtlich klein (< 2–3 m) und stark gestreut, damit Heilen ein Positionierungs-Feature bleibt.
    * Treffer-Logik auf der Weste interpretiert diese Pakete als Heilung (+HP, begrenzt durch `health_max`).
* **Balancing:**
    * Heal pro Tick konfigurierbar; zusätzlich wählbar: max. Anzahl Heilungen, Mindest-Pause zwischen Heilungen oder beides.
    * Friendly-only oder auch Self-Heal konfigurierbar.
    * Hardware: Kann identisch zur Waffe sein (Trigger/Taster, Audio). Beispiele: "nääääääään" bei Cooldown, "Sorry, I am empty" wenn Magazin leer ist.

### Zonen-Effekte (Bases/Joiner)
* **Joiner/Base-Knoten** können zusätzlich periodisch CoAP-Pakete auf Ressource `zone` senden (Link-Local, Hop-Limit=1).
* **Anwendungsfälle:**
    * **Heal-Zonen:** In Team-Homebase oder dominierten Bases → Team-Mitglieder werden geheilt, Gegner ggf. geschwächt.
    * **Kill-/Radiation-Zonen:** Schadenszonen mit Warn-Countdown; Gegner müssen den Bereich verlassen.
    * **Respawn-Zonen:** Spielmodi, in denen tote Spieler nur in der eigenen Home-Zone oder einer dominierten Base respawnen dürfen; die Zone validiert Präsenz (RSSI) und erlaubt dann Respawn.
* **Paketfelder (Beispiel):** `team: 2, friend: +20, foe: -10, rssi: -70, warn: 5`
    * `team`: Besitzer der Zone (z.B. 2 = Blau)
    * `friend`: HP-Delta für eigenes Team (+20 Heal)
    * `foe`: HP-Delta für andere Teams (-10 Schaden)
    * `rssi`: Mindest-RSSI (dBm) für Wirksamkeit (z.B. -70 → nur nahe dran)
    * `warn`: Anzahl Aussendungen als Warnung, bevor der Effekt scharf wird
* **Instant-Death Beispiel:** `team: 0, friend: -128, foe: -128, warn: 0, rssi: -60` → Jeder Empfänger mit RSSI besser als -60 dBm fällt sofort auf 0 HP.

---

## 5. Technische Spezifikation (API & Datenstrukturen)

Die Kommunikation erfolgt über CoAP (UDP). Alle Payloads sind binär (`__packed` C-Structs, Little Endian für nRF52, Network Byte Order für Interop optional).

### 5.1 CoAP Endpunkte

| Ressource | Methode | Typ | Beschreibung | Payload |
| :--- | :--- | :--- | :--- | :--- |
| `game/state` | PUT | Multicast | Globaler Spielstatus (Start/Stop/Zeit) | `struct game_state_packet` |
| `game/conf` | PUT | Unicast | Konfiguration für einen Spieler | `struct player_config_packet` |
| `game/wconf` | PUT | Unicast | Konfiguration für eine Waffe | `struct weapon_config_packet` |
| `game/hit` | POST | Unicast | Treffer-Meldung (Live-Ticker) | `struct hit_report_packet` |
| `game/log` | GET | Unicast | Abruf der gespeicherten Trefferdaten | `struct flash_log_entry[]` |
| `game/zone` | PUT | Multicast (Link-Local, Hop=1) | Zonen-Effekte (Heal/Kill) aus Bases/Joinern | `struct zone_effect_packet` |

| `game/powerup` | PUT | Unicast | Power-Up-Event von Weste zu Waffe | `struct powerup_event_packet` |
| `game/pup_log` | GET | Unicast | Abruf aller Power-Up-Aktivierungen | `struct powerup_log_entry[]` |

### 5.2 Datenstrukturen

#### Spielstatus (Multicast)
```c
struct game_state_packet {
    uint8_t state;          // 0=Idle, 1=Lobby, 2=Running, 3=Paused, 4=Finished
    uint8_t game_mode;      // 0=TeamDeathmatch, 1=Zombie, 2=Base
    uint16_t game_id;       // Rolling Counter zur Deduplizierung
    uint16_t remaining_sec; // Restzeit in Sekunden
    uint8_t flags;          // Bitmaske (z.B. FriendlyFire)
} __packed;
```

#### Spieler-Konfiguration (Provisioning)
```c
struct player_config_packet {
    uint16_t player_id;     // Logische ID (1-65535)
    uint8_t team_id;        // 0=Rot, 1=Blau, 2=Grün (Zombie), ...
    uint8_t role;           // 0=Soldier, 1=Medic, 2=Sniper
    uint8_t damage_out;     // Basis-Schaden der Waffe
    uint8_t health_max;     // Maximale Lebenspunkte
    char name[16];          // Anzeigename (null-terminated)
} __packed;
```

#### Waffen-Konfiguration
```c
struct weapon_config_packet {
    uint8_t base_damage;    // Schaden pro Schuss
    uint16_t reload_time_ms;// Zeit für Nachladen
    uint8_t magazine_size;  // Schuss pro Magazin
} __packed;
```

#### Treffer-Bericht (Live & Log)
```c
struct hit_report_packet {
    uint32_t timestamp;     // ms seit Spielstart
    uint16_t shooter_id;    // ID des Schützen (aus IR)
    uint16_t victim_id;     // Eigene ID
    uint8_t damage;         // Erlittener Schaden
    uint8_t hit_location;   // 0=Unbekannt, 1=Kopf, 2=Brust, 3=Rücken
} __packed;

// Zonen-Effekte (link-local, Hop-Limit=1)
struct zone_effect_packet {
    uint8_t team_id;        // Besitzer der Zone (0=neutral)
    int8_t friend_delta;    // HP-Delta für eigenes Team (z.B. +20 Heal)
    int8_t foe_delta;       // HP-Delta für andere Teams (z.B. -10 Schaden)
    int8_t rssi_thresh_dbm; // Mindest-RSSI für Wirksamkeit (z.B. -70 dBm)
    uint8_t warn_count;     // Anzahl Warn-Pakete vor scharfem Effekt
} __packed;

// Power-Up Event (von Weste zu Waffe nach IR-Empfang)
struct powerup_event_packet {
    uint16_t player_id;        // Betroffener Spieler
    uint8_t powerup_type;      // 0=SHIELD, 1=DAMAGE_BOOST, 2=HEAL, 3=SPEED, etc.
    uint16_t duration_sec;     // Dauer des Power-Ups in Sekunden
    uint8_t persist_on_death;  // 0=verfällt bei Death, 1=bleibt auch nach Respawn
    uint16_t damage_multiplier;// Für Damage-Boost: z.B. 150 = 1.5x
    int8_t health_delta;       // Für Heal-Pack: z.B. +50
} __packed;

// Power-Up Log Entry (für Auswertung nach Spielende)
struct powerup_log_entry {
    uint32_t timestamp;        // ms seit Spielstart
    uint16_t player_id;        // Spieler, der Powerup aktiviert hat
    uint8_t powerup_type;      // Type des PU
    uint8_t source;            // 0=IR-Station, 1=Buzzer-Box, 2=Leader-Kommando, 3=Zone
} __packed;
```

---

## 6. IR-Protokoll (Physical Layer)

Das IR-Signal nutzt eine 38kHz Trägerfrequenz (NEC-ähnlich).

### 6.1 Standard-Shooting-Payload (32-bit)

Payloads von Waffen und Medics (Protokoll-ID `0xAA`):

* Bits 24-31: `Protokoll-ID` (`0xAA` für Standard-Spiel-IRs)
* Bits 8-23: `Shooter ID` (16-bit, eindeutig für jeden Spieler)
* Bits 4-7: `Team-Code` (4-bit, 0=Rot, 1=Blau, 2=Grün, 3=Neutral)
* Bits 0-3: `Damage Class` (4-bit, 0-15, definiert Basis-Schaden; 0xFF = Healing)

**Beispiel:** ShooterID=42, Team=Rot, Damage=10 → `0xAA002A0A`

### 6.2 Power-Up-Payload (32-bit)

Payloads von Power-Up-Stationen und Buzzer-Boxen (Protokoll-ID `0xBB`):

* Bits 24-31: `Protokoll-ID` (`0xBB` für Power-Up-Signale)
* Bits 16-23: `Power-Up-Type` (0=SHIELD, 1=DAMAGE_BOOST, 2=HEAL_PACK, 3=SPEED, 4=WEAKNESS, 5=AMMO, etc.)
* Bits 0-15: `Power-Up-Flags` (optional: Duration-Encoding, Persist-Flag, etc.)

**Beispiel (Shield):** `0xBB000000` (Duration wird in CoAP-Paket übertragen)

---

## 7. Power-Ups & Items (erweitert)

### Hardw are-Seite
* **Power-Up-Stationen (Deckel-Boxen):** nRF52840 + Magnetschalter + IR-Sender + Status-LED; Cooldown intern gesteuert (z.B. 30s).
* **Buzzer-Power-Ups:** nRF52840 + Druckschalter + schwacher IR-Sender (2-3m) + LED; optional Thread-Logging.
* **Beide Typen können zentral über Leader konfiguriert werden.**

### Software-Seite
* **IR-Dekodierung:** Weste prüft Protokoll-ID (`0xAA` vs. `0xBB`) und ruft Handler auf.
* **Weste:** Speichert aktive Power-Ups lokal (Typ, Duration, Multiplier); sendet via `game/powerup` Unicast zu Waffe; prüft `persist_on_death` bei Respawn.
* **Waffe:** Empfängt `game/powerup`, appliziert Damage-Multiplier auf nächste Schüsse.
* **Power-Up-Typen:**
    - SHIELD: Blockiert 1 Treffer
    - DAMAGE_BOOST: Multipliziert ausgehenden Schaden (z.B. 1.5x)
    - HEAL_PACK: Instant +HP
    - SPEED: Schneller schießen (Cooldown-Reducer)
    - WEAKNESS: Negativ, mehr Schaden empfangen
    - AMMO: Medipack-Magazine aufladen
* **Leader-Kommandos:** Kann zentrale Power-Ups auslösen (Underdog-Bonus, Base-Schuss-Trigger, Turbo-Round) → Multicast/Unicast.
* **Logging:** Alle Power-Up-Aktivierungen in Flash-Log speichern, abrufbar via `game/pup_log` nach Spielende.

## 8. Weste – Zustandsautomat (detailliert)

### Übergangstabelle

| Von | Nach | Auslöser | Aktion | Bedingung |
| :--- | :--- | :--- | :--- | :--- |
| Idle | Lobby | CoAP `GAME_STATE_LOBBY` | LED idle-Animation, warten auf Start | Multicast vom Leader |
| Lobby | Countdown | CoAP `GAME_START_COUNTDOWN` | Audio-Countdown 10→1 sec, Countdown-Timer init | Payload: 10 sek |
| Countdown | Running | Countdown = 0 | Health reset, Treffer-Sensor aktivieren, Waffe unlock | Timer lokal abgelaufen |
| Running | Dead | Health <= 0 | LED rot/aus, Dead-Sound, CoAP CMD_DISABLE an Waffe | Nach IR-Hit-Verarbeitung |
| Dead | Running | Respawn-Timer = 0 | Health reset, CoAP CMD_ENABLE an Waffe, Sensor on | Optional; Config-abhängig |
| Running | Finished | CoAP `GAME_STATE_FINISHED` | Alle Daten sichern, Logs speichern, LED Idle | Multicast vom Leader |
| Dead | Finished | CoAP `GAME_STATE_FINISHED` | Logs speichern (optional), LED Idle | Multicast vom Leader |

### Treffer-Verarbeitung im Running-State (Flowchart)

```mermaid
flowchart TD
    A["IR-Signal empfangen"] --> B{"ShooterID prüfen"}
    B -->|Friendly Fire off + eigenes Team| C["Signal verwerfen"]
    B -->|Valid| D["Hit Location extrahieren"]
    D --> E["Zone-Multiplikator anwenden"]
    E --> F["Effektiven Schaden berechnen"]
    F --> G["Health -= Damage"]
    G --> H{"Health > 0?"}
    H -->|Ja| I["LED blinken + Sound"]
    I --> J["Hit ins Flash-Log schreiben"]
    J --> K["Optional: CoAP Hit-Report an Leader"]
    H -->|Nein| L["Health = 0"]
    L --> M["LED rot, Dead-Sound"]
    M --> N["CoAP CMD_DISABLE an Waffe"]
    N --> O["Zustand → Dead"]
```

### Übergangsbeschreibungen

- **Idle → Lobby:** App oder Spielleiter triggert über BLE. Leader sendet `GAME_STATE_LOBBY` Multicast. Weste wechselt in Warte-Modus.
- **Lobby → Countdown:** Leader sendet `GAME_START_COUNTDOWN` mit Sekunden-Payload (z.B. 10). Weste zählt laut herunter.
- **Countdown → Running:** Timer lokal = 0 → Weste aktiviert Sensoren, Health voll, Waffe freigegeben.
- **Running → Dead:** Health <= 0 nach Treffer-Verarbeitung → Waffe gesperrt, LED rot/aus, Respawn-Timer optional starten.
- **Dead → Running:** Optional nur wenn Respawn aktiv. Leader schickt evtl. Kommando oder Timer läuft ab.
- **{Running|Dead} → Finished:** Leader sendet `GAME_STATE_FINISHED` → Weste speichert finale Logs, bereit für Auswertung.

## 9. Offene Punkte & Annahmen

* Sicherheitsmodell: Keine Auth auf IR, minimale Auth/ACL auf BLE/Thread? (noch zu klären).
* Anti-Cheat: Debounce IR, Rate-Limit pro Waffe, Rolling Codes möglich.
* Telemetrie: Sampling-Intervall für Live-Ticker und Limit pro Sekunde definieren.