Konzepte angepasst und erweitert
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 13s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 13s
This commit is contained in:
194
doc/software.md
Normal file
194
doc/software.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user