# Schritt 12 — Lite Charakterbogen in der Lobby

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Spieler füllen in der Lobby ihren Charakter aus (Name, Rasse, Klasse, 6 Ability-Modifier). Die Daten werden an alle Peers gesynct. Quick-Fill-Buttons für Dev-Archetypen.

**Architecture:** Neue Felder in `tavern_lobby.tscn` unter dem Join-Bereich. `tavern_lobby.gd` sendet bei Änderungen via neuem `request_update_character` RPC an Server; Server speichert unter `players[id]["char"]` und relayed an alle. NetworkManager bekommt Signal `character_updated(peer_id, data)`.

**Tech Stack:** Godot 4.x GDScript, MultiplayerAPI RPC, OptionButton, SpinBox, GridContainer

---

## Files

| File | Aktion |
|------|--------|
| `ruf-der-pilze/scenes/tavern_lobby.tscn` | Modify — Charakterbogen-UI unter Join-Bereich |
| `ruf-der-pilze/scripts/tavern_lobby.gd` | Modify — Formular-Logik, Auto-Send bei Änderung |
| `ruf-der-pilze/scripts/network_manager.gd` | Modify — `request_update_character` + `_relay_character_update` RPC, `character_updated` Signal |
| `docs/STATUS.md` | Modify — Schritt 12 ✅ |

---

## Task 1: Character-RPC in NetworkManager

**Files:** Modify `ruf-der-pilze/scripts/network_manager.gd`

- [ ] **Schritt 1: Signal + zwei RPCs am Ende von network_manager.gd hinzufügen**

  ```gdscript
  signal character_updated(peer_id: int, data: Dictionary)

  # Client → Server
  @rpc("any_peer", "call_remote", "reliable")
  func request_update_character(data: Dictionary) -> void:
      if not multiplayer.is_server(): return
      var peer_id := multiplayer.get_remote_sender_id()
      if not players.has(peer_id): return
      players[peer_id]["char"] = data
      _relay_character_update.rpc(peer_id, data)

  # Server → All
  @rpc("authority", "call_local", "reliable")
  func _relay_character_update(peer_id: int, data: Dictionary) -> void:
      if players.has(peer_id):
          players[peer_id]["char"] = data
      character_updated.emit(peer_id, data)
  ```

- [ ] **Schritt 2: Godot öffnen, Parse-Fehler prüfen**

  Godot-Editor öffnen und prüfen ob das Script fehlerfrei geladen wird (keine roten Fehler im Output-Panel).

- [ ] **Schritt 3: Commit**

  ```bash
  git add ruf-der-pilze/scripts/network_manager.gd
  git commit -m "net: add request_update_character RPC + character_updated signal"
  ```

---

## Task 2: Charakterbogen-UI in tavern_lobby.tscn

**Files:** Modify `ruf-der-pilze/scenes/tavern_lobby.tscn`

Node-Struktur unter dem bestehenden Join-Bereich (nach NameInput/JoinButton, aber vor der StartButton-Zeile). `CharSheet` ist zunächst `visible = false` — wird nach erfolgreichem Join eingeblendet.

```
VBoxContainer "CharSheet"
  visible = false
  Label "LblChar" — text="Charakter"
  GridContainer "CharGrid" — columns=2
    Label — text="Rasse"
    OptionButton "RaceOption"
      items: Mensch, Elf, Zwerg, Halbork, Halbelf, Tiefling
    Label — text="Klasse"
    OptionButton "ClassOption"
      items: Krieger, Magier, Kleriker, Schurke, Druide, Barbar
  HSeparator
  Label "LblModifiers" — text="Werte"
  GridContainer "ModGrid" — columns=6
    Label — text="STR"
    Label — text="DEX"
    Label — text="CON"
    Label — text="INT"
    Label — text="WIS"
    Label — text="CHA"
    SpinBox "SpinSTR" — min=-5, max=10, value=0
    SpinBox "SpinDEX" — min=-5, max=10, value=0
    SpinBox "SpinCON" — min=-5, max=10, value=0
    SpinBox "SpinINT" — min=-5, max=10, value=0
    SpinBox "SpinWIS" — min=-5, max=10, value=0
    SpinBox "SpinCHA" — min=-5, max=10, value=0
  HSeparator
  Label "LblArchetype" — text="Schnell-Auswahl (Dev)"
  HBoxContainer "ArchetypeRow"
    Button "BtnKrieger" — text="Krieger"
    Button "BtnMagier"  — text="Magier"
    Button "BtnKleriker"— text="Kleriker"
    Button "BtnSchurke" — text="Schurke"
    Button "BtnDruide"  — text="Druide"
    Button "BtnBarbar"  — text="Barbar"
```

- [ ] **Schritt 1: Nodes in tavern_lobby.tscn via Godot-Editor anlegen**

  `tavern_lobby.tscn` öffnen. Im Scene-Panel die bestehende UI-Hierarchie finden (CanvasLayer → Panel o.ä.). Den neuen `VBoxContainer "CharSheet"` nach den Join-Controls einfügen.

- [ ] **Schritt 2: Layout in Godot-Editor prüfen**

  CharSheet sollte initial unsichtbar sein (`visible = false`). Kurz auf `visible = true` setzen, Layout im Viewport prüfen, dann wieder auf `false`.

- [ ] **Schritt 3: Commit**

  ```bash
  git add ruf-der-pilze/scenes/tavern_lobby.tscn
  git commit -m "lobby: add character sheet UI nodes (CharSheet, ModGrid, ArchetypeRow)"
  ```

---

## Task 3: Charakterbogen-Logik in tavern_lobby.gd

**Files:** Modify `ruf-der-pilze/scripts/tavern_lobby.gd`

Zuerst das Script lesen um die genauen Node-Pfade und bestehende Struktur zu verstehen. Die Pfade in den Code-Beispielen unten ggf. anpassen.

- [ ] **Schritt 1: Konstante Archetyp-Presets + Helper `_collect_char_data()` hinzufügen**

  Am Anfang der Klasse einfügen (nach `extends` und bestehenden `var`-Deklarationen):

  ```gdscript
  const ARCHETYPES := {
      "Krieger":  {"race": "Mensch",  "class": "Krieger",  "STR": 3, "DEX": 1, "CON": 2, "INT": 0, "WIS": 0, "CHA": 0},
      "Magier":   {"race": "Elf",     "class": "Magier",   "STR":-1, "DEX": 2, "CON": 0, "INT": 4, "WIS": 2, "CHA": 1},
      "Kleriker": {"race": "Zwerg",   "class": "Kleriker", "STR": 1, "DEX": 0, "CON": 2, "INT": 1, "WIS": 3, "CHA": 0},
      "Schurke":  {"race": "Halbelf", "class": "Schurke",  "STR": 0, "DEX": 4, "CON": 1, "INT": 1, "WIS": 1, "CHA": 2},
      "Druide":   {"race": "Mensch",  "class": "Druide",   "STR": 0, "DEX": 1, "CON": 1, "INT": 1, "WIS": 3, "CHA": 0},
      "Barbar":   {"race": "Halbork", "class": "Barbar",   "STR": 4, "DEX": 2, "CON": 3, "INT":-1, "WIS": 0, "CHA":-1},
  }

  func _collect_char_data() -> Dictionary:
      var race_opt := $<UI_PATH>/CharSheet/CharGrid/RaceOption as OptionButton
      var class_opt := $<UI_PATH>/CharSheet/CharGrid/ClassOption as OptionButton
      return {
          "race":  race_opt.get_item_text(race_opt.selected),
          "class": class_opt.get_item_text(class_opt.selected),
          "STR": int($<UI_PATH>/CharSheet/ModGrid/SpinSTR.value),
          "DEX": int($<UI_PATH>/CharSheet/ModGrid/SpinDEX.value),
          "CON": int($<UI_PATH>/CharSheet/ModGrid/SpinCON.value),
          "INT": int($<UI_PATH>/CharSheet/ModGrid/SpinINT.value),
          "WIS": int($<UI_PATH>/CharSheet/ModGrid/SpinWIS.value),
          "CHA": int($<UI_PATH>/CharSheet/ModGrid/SpinCHA.value),
      }
  ```

  `<UI_PATH>` durch den tatsächlichen Pfad zur UI-Ebene ersetzen (aus dem Script lesen).

- [ ] **Schritt 2: `_setup_char_sheet()` hinzufügen und in `_ready()` aufrufen**

  ```gdscript
  func _setup_char_sheet() -> void:
      for archetype_name: String in ARCHETYPES:
          var btn := $<UI_PATH>/CharSheet/ArchetypeRow.get_node(
              NodePath("Btn" + archetype_name)) as Button
          var preset: Dictionary = ARCHETYPES[archetype_name]
          btn.pressed.connect(func():
              var race_opt := $<UI_PATH>/CharSheet/CharGrid/RaceOption as OptionButton
              var class_opt := $<UI_PATH>/CharSheet/CharGrid/ClassOption as OptionButton
              for i in race_opt.item_count:
                  if race_opt.get_item_text(i) == preset["race"]:
                      race_opt.selected = i
              for i in class_opt.item_count:
                  if class_opt.get_item_text(i) == preset["class"]:
                      class_opt.selected = i
              for stat: String in ["STR", "DEX", "CON", "INT", "WIS", "CHA"]:
                  ($<UI_PATH>/CharSheet/ModGrid.get_node(
                      NodePath("Spin" + stat)) as SpinBox).value = preset[stat]
              _send_char_data()
          )
      for spin_name: String in ["SpinSTR", "SpinDEX", "SpinCON", "SpinINT", "SpinWIS", "SpinCHA"]:
          ($<UI_PATH>/CharSheet/ModGrid.get_node(NodePath(spin_name)) as SpinBox
              ).value_changed.connect(func(_v: float): _send_char_data())
      ($<UI_PATH>/CharSheet/CharGrid/RaceOption as OptionButton
          ).item_selected.connect(func(_i: int): _send_char_data())
      ($<UI_PATH>/CharSheet/CharGrid/ClassOption as OptionButton
          ).item_selected.connect(func(_i: int): _send_char_data())

  func _send_char_data() -> void:
      NetworkManager.request_update_character.rpc_id(1, _collect_char_data())
  ```

  `_setup_char_sheet()` am Ende von `_ready()` aufrufen.

- [ ] **Schritt 3: CharSheet nach erfolgreichem Join einblenden**

  In der bestehenden Funktion die nach erfolgreichem Join aufgerufen wird (per NetworkManager-Signal oder direkt in `_on_join_button_pressed`), am Ende hinzufügen:

  ```gdscript
  $<UI_PATH>/CharSheet.visible = true
  ```

- [ ] **Schritt 4: Godot öffnen, Parse-Fehler prüfen**

- [ ] **Schritt 5: End-to-End-Test**

  Server + DM-Client + Spieler-Client starten.
  - Spieler joined → CharSheet erscheint
  - Spieler klickt "Magier" → Race=Elf, Class=Magier, Modifier werden gesetzt
  - Spieler ändert STR manuell → sendet sofort
  - Im GDScript-Debugger: `NetworkManager.players[peer_id]["char"]` zeigt die Daten
  - Zweiter Client: erhält `character_updated` Signal

- [ ] **Schritt 6: Commit**

  ```bash
  git add ruf-der-pilze/scripts/tavern_lobby.gd
  git commit -m "lobby: wire character sheet form — archetypes, modifier spinboxes, auto-sync RPC"
  ```

---

## Task 4: STATUS.md updaten

- [ ] **Schritt 1: Schritt 12 als ✅ markieren in `docs/STATUS.md` und `ruf-der-pilze/CLAUDE.md`**

- [ ] **Schritt 2: Commit**

  ```bash
  git add docs/STATUS.md ruf-der-pilze/CLAUDE.md
  git commit -m "docs: mark Schritt 12 complete — lite Charakterbogen"
  ```
