# Design: Taverne In-Game-Szene

Datum: 2026-04-14  
Status: Genehmigt

---

## Kontext

Die bestehende `tavern.tscn` ist ein einfacher Gastraum-Blockout der als Lobby-Warteraum dient (vor Spielstart). Nach `game_started` müssen Spieler in eine richtige In-Game-Szene wechseln: die vollständige 2-stöckige Taverne, in der die Geschichte beginnt.

Gleichzeitig wird der Gastraum als wiederverwendbare Sub-Scene extrahiert, damit Lobby und In-Game-Szene dieselbe Geometrie nutzen (keine Duplikation).

---

## Grundriss

**Variante — Offene Küche:** Gastraum und Küche sind offen verbunden. Spieler können die Küche vom Gastraum aus sehen, aber nicht betreten (Tresen als Grenze). Private Gemächer als abgetrennter Bereich mit verschlossener Tür.

```
OBERGESCHOSS
┌─────────────────────────────────────────────┐
│  [Z1][Z2][Z3][Z4][Z5][Z6][Z7][Z8][Annas Z.]│
│  ←——————————— Korridor ——————————————→      │
│                                  [Treppe]   │
└─────────────────────────────────────────────┘

ERDGESCHOSS
┌─────────────────────────────────────────────┐
│ [Eingang] [————— Gastraum —————] [Private]  │
│           [offene Küche hinten]             │
│                                  [Treppe]   │
└─────────────────────────────────────────────┘
```

- 8 Gästezimmer + Annas Zimmer im Obergeschoss (9 Türen total)
- Küche sichtbar vom Gastraum, aber nicht betretbar (Tresen als Grenze)
- Private Gemächer: verschlossene Tür, nicht betretbar

---

## Szenenarchitektur

### Neue Dateistruktur

```
scenes/
  taproom.tscn           ← Gastraum als wiederverwendbare Sub-Scene
                            Geometrie-Only: BoxMesh-Wände, Böden, Decke, Tresen
                            Kein Script, kein CanvasLayer, keine UI

  tavern_lobby.tscn      ← bisherige tavern.tscn UMBENANNT auf Disk
                            instanziert taproom.tscn als Child
                            behält CanvasLayer (JoinPanel, WaitPanel)
                            Script: tavern_lobby.gd (bisherige tavern.gd umbenannt)

  tavern.tscn            ← neue In-Game-Szene: vollständige 2-stöckige Taverne
                            instanziert taproom.tscn als Child
                            + Obergeschoss, Treppe, Private Gemächer (Tür), Ausgang
                            + Myzelspur (Plane-Meshes)
                            Script: tavern.gd (neu)

  dm_view.tscn           ← DM-Szene (Stub; wird in Schritt 7 ausgebaut)
                            Script: dm_view.gd (Stub)
```

**Umbenennung auf Disk:**
- `scenes/tavern.tscn` → `scenes/tavern_lobby.tscn`
- `scripts/tavern.gd` → `scripts/tavern_lobby.gd`

### SceneManager-Änderungen

```gdscript
const SCENES := {
    "lobby":   "res://scenes/tavern_lobby.tscn",  # war: "tavern" → tavern.tscn
    "tavern":  "res://scenes/tavern.tscn",         # neu: volle In-Game-Taverne
    "dm_view": "res://scenes/dm_view.tscn",        # neu: DM-Stub
    # "chamber" entfällt — Zimmer sind Teil von tavern.tscn
    ...
}
```

**`_on_root_ready` muss geändert werden:**
```gdscript
# ALT:
transition_to("tavern")
# NEU:
transition_to("lobby")
```

**`_on_game_started` muss implementiert werden:**
```gdscript
func _on_game_started() -> void:
    var args := OS.get_cmdline_args() + OS.get_cmdline_user_args()
    if OS.has_feature("dedicated_server") or "--server" in args:
        return
    var role: String = NetworkManager.players.get(NetworkManager.my_id, {}).get("role", "player")
    if role == "dm":
        transition_to("dm_view")
    else:
        transition_to("tavern")
```

---

## Szeneninhalt `tavern.tscn`

### Erdgeschoss

| Bereich | Betretbar | Notiz |
|---|---|---|
| Gastraum (`taproom.tscn`) | Ja | Sub-Scene |
| Küche | Nein | Vom Gastraum sichtbar, Tresen als physische Grenze |
| Private Gemächer | Nein | Verschlossene Tür (StaticBody) |
| Ausgang | Ja | Tür → Myzelpfad (nächste Szene) |

Myzelspur am Boden: Treppe ↓ → Gastraum → Ausgang (als farbige Plane-Meshes, grünlich leuchtend).

### Obergeschoss

| Bereich | Betretbar | Notiz |
|---|---|---|
| Korridor | Ja | Verbindet alle Zimmer |
| Gästezimmer 1–8 | Ja | Je ein `Marker3D` SpawnPoint im Zimmer |
| Annas Zimmer | Ja | Tür offen, Raum myzelüberzogen, Myzelspur führt zum Korridor |

Annas Zimmer: Myzelspur → Korridor → Treppe ↓ → Gastraum → Ausgang (durchgehende Spur).

---

## Spawn-Logik

### Zimmer-Zuweisung

Der Server weist jedem Spieler-Peer deterministisch ein Zimmer zu, sobald `game_started` gesendet wird. Die Zuweisung erfolgt über einen neuen RPC direkt nach `game_started`:

```gdscript
# Server-Seite (in NetworkManager oder GameState):
func _assign_rooms() -> void:
    var player_peers := NetworkManager.players.keys().filter(
        func(id): return NetworkManager.players[id].role == "player"
    )
    for i in player_peers.size():
        var room_index := i % 8  # 8 Gästezimmer (Zimmer 1–8)
        rpc_id(player_peers[i], "receive_room_assignment", room_index)

@rpc("authority", "call_remote", "reliable")
func receive_room_assignment(room_index: int) -> void:
    SceneManager.pending_room_index = room_index
```

`SceneManager.pending_room_index` wird gesetzt **bevor** `transition_to("tavern")` aufgerufen wird, damit `tavern.gd` den Wert in `_ready()` auslesen kann.

### Spawn im Zimmer

`tavern.gd` liest `SceneManager.pending_room_index` in `_ready()` und positioniert den Spieler am zugehörigen SpawnPoint:

```gdscript
# tavern.gd
func _ready() -> void:
    var room_index := SceneManager.pending_room_index
    var spawn := get_node("UpperFloor/Room%d/SpawnPoint" % (room_index + 1)) as Marker3D
    # Spieler-Controller an spawn.global_position platzieren
    _spawn_player(spawn.global_transform)
```

### Spieler-Controller (Stub)

Für den Blockout wird ein minimaler `PlayerController` instanziert:
- Node-Typ: `Node3D` mit `Camera3D` als Child
- Kein Movement noch (kommt in einem späteren Schritt)
- Die Kamera zeigt die Zimmer-Perspektive (First Person)
- **Keine** feste atmosphärische Kamera in `tavern.tscn` — nur der PlayerController besitzt die aktive Camera3D

### Spawn-Sequenz

Für den **Blockout**: Spieler erscheint sofort stehend am SpawnPoint (kein Fade, keine Animation).

Für **später** (eigenes Feature):
1. Fade in from black — "Augen öffnen sich"
2. Spieler liegt im Bett — kurze Aufsteh-Animation (AnimationPlayer)
3. DM hat Zeit für Narration
4. Spieler übernimmt Kontrolle (First Person)

---

## Detailgrad

### Jetzt (Blockout)

Alles als BoxMesh-Placeholder, analog zur bestehenden `tavern.tscn`:

- Wände, Böden, Decken aller Räume
- Treppen (Rampe oder Stufen-Boxen)
- Türen als Lücken in der Wand (keine Tür-Meshes)
- Betten als flache Box + `Marker3D` SpawnPoint darüber
- Tresen als Box (Küchen-Grenze)
- Collision shapes (`StaticBody3D`) für alle Geometrie
- Myzelspur: farbige Plane-Meshes am Boden (grünlich)
- Minimaler PlayerController (Node3D + Camera3D, kein Movement)
- `dm_view.tscn`: leere Szene mit Label "DM View — Stub"

### Später (eigene Features)

- Echte 3D-Modelle via Meshy AI / Blender (Möbel, Architekturdetails)
- Myzel-Shader und Partikeleffekte
- Kerzen- und Fackel-Beleuchtung
- Fade-in + Aufsteh-Animation (AnimationPlayer)
- Tür-Interaktionen (öffnen/schließen)
- Audio: Gastraum-Atmosphäre, Schritte
- Vollständige PlayerController-Bewegung (First Person)

---

## Abhängigkeiten

- `tavern.tscn` wird auf Disk umbenannt zu `tavern_lobby.tscn`; `tavern.gd` → `tavern_lobby.gd`
- `scene_manager.gd`: SCENES-Dict und `_on_root_ready` / `_on_game_started` werden angepasst
- `network_manager.gd`: `game_started` Signal bereits vorhanden; neuer `receive_room_assignment` RPC kommt hinzu (oder wird in `GameState` Autoload angelegt)
- `taproom.tscn` ist geometrie-only — kein Script, kein CanvasLayer
