Files
lasertag/doc/software.md
Eduard Iten ce4d0d1a44
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 22s
Added device types and vest mini app
2026-01-10 09:08:45 +01:00

7.8 KiB
Raw Blame History

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)

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)

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

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)

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):

  • 8bit Protokoll-ID (Magic Byte zur Unterscheidung von Fernbedienungen)
  • 16bit Shooter ID
  • 8bit Info (4bit Team, 4bit Damage Class)