# Software-Konzept & Spielablauf Dieses Dokument beschreibt die Software-Architektur, die Rollenverteilung und die Kommunikationsabläufe des Lasertag-Systems. ## 1. System-Rollen & Hardware-Typen Das System basiert auf nRF52840-Chips, die über OpenThread (802.15.4) kommunizieren. ### 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). ### B. Weste (Player Hub) * **Funktion:** Zentrale Einheit des Spielers. Verwaltet Lebenspunkte, empfängt Treffer, steuert Audio. * **Kommunikation:** Hält die Verbindung zur Waffe (Pairing) und zum Leader. ### C. Waffe * **Funktion:** Aussenden der IR-Signale, haptisches Feedback, Muzzle-Flash. * **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. 2. Payload: Enthält die EUI-64 Adresse. 3. App sendet Konfiguration an Leader Box: `EUI-64 -> {PlayerID: 5, Team: Rot, Name: "Rambo"}`. 4. **Waffen-Setup:** Waffe meldet ihren Typ (z.B. "Sniper") an Leader. 5. Leader sendet Konfigurations-Paket (CoAP Unicast) an das Gerät: * **Weste:** Erhält PlayerID, TeamID, MaxHealth. * **Waffe:** Erhält Damage-Wert, Nachladezeit, Magazingröße. --- ## 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-Code mit `ShooterID` + `Damage` + `TeamID`). 2. Weste B empfängt IR-Signal. 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). ### 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. --- ## 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?". --- ## 5. Technische Spezifikation (API & Datenstrukturen) Die Kommunikation erfolgt über CoAP (UDP). Alle Payloads sind binär (`__packed` C-Structs, Little Endian für nRF52, aber Network Byte Order Big Endian empfohlen für Portabilität - hier vereinfacht Little Endian da homogene Hardware). ### 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/hit` | POST | Unicast | Treffer-Meldung (Live-Ticker) | `struct hit_report_packet` | | `game/wconf` | PUT | Unicast | Konfiguration für eine Waffe | `struct weapon_config_packet` | | `game/log` | GET | Unicast | Abruf der gespeicherten Trefferdaten | `struct flash_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 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; ``` --- ## 6. IR-Protokoll (Physical Layer) Das IR-Signal nutzt eine 38kHz Trägerfrequenz (NEC-ähnlich). Payload (32-bit): * `8 bit` Protokoll-ID (Magic Byte zur Unterscheidung von Fernbedienungen) * `16 bit` Shooter ID * `8 bit` Info (4 bit Team, 4 bit Damage Class)