docs: add plans for Schritt 12 (Charakterbogen), 13 (Capsules), 14 (Art Pass)

This commit is contained in:
2026-04-16 00:48:43 +02:00
parent 9cf0ea6f6f
commit e46cb10f6a
3 changed files with 763 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
# 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"
```

View File

@@ -0,0 +1,217 @@
# Schritt 13 — Charakter-Platzhalter (Capsule-Avatare)
> **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:** Jeder Spieler wird in der Lobby als farbige Kapsel mit Namens-Label dargestellt. Farbe kommt aus `players[id]["char"]["class"]` (Fallback: Grau wenn char-Daten noch nicht vorhanden). Capsules sind eine eigene Szene — einfach austauschbar gegen echte Modelle später.
**Architecture:** Neue Szene `character_placeholder.tscn` (Node3D + CapsuleMesh + Label3D). `tavern_lobby.gd` instanziiert und entfernt Capsules wenn `player_joined`/`player_left` feuert. Capsules stehen an vordefinierten `Marker3D`-Lobby-Standpunkten (5 Spots in tavern_lobby.tscn). Farbe wird per `CharacterPlaceholder.set_color(class_name)` gesetzt; bei `character_updated`-Signal wird Farbe aktualisiert.
**Tech Stack:** Godot 4.x GDScript, MeshInstance3D, CapsuleMesh, StandardMaterial3D, Label3D, Marker3D
**Parallel-Hinweis:** Dieser Plan liest `players[id].get("char", {}).get("class", "")`. Falls Schritt 12 noch nicht gemergt ist, ist "class" leer → Grau-Fallback. Kein harter Blocker.
---
## Files
| File | Aktion |
|------|--------|
| `ruf-der-pilze/scenes/character_placeholder.tscn` | Create — Capsule + Label3D + Script |
| `ruf-der-pilze/scripts/character_placeholder.gd` | Create — `set_color(class_name)`, `set_label(name)` |
| `ruf-der-pilze/scenes/tavern_lobby.tscn` | Modify — 5 LobbySpot Marker3D-Nodes hinzufügen |
| `ruf-der-pilze/scripts/tavern_lobby.gd` | Modify — Capsule spawn/remove on join/leave |
| `docs/STATUS.md` | Modify — Schritt 13 ✅ |
---
## Task 1: character_placeholder.gd + .tscn erstellen
**Files:**
- Create: `ruf-der-pilze/scripts/character_placeholder.gd`
- Create: `ruf-der-pilze/scenes/character_placeholder.tscn`
- [ ] **Schritt 1: character_placeholder.gd erstellen**
Datei anlegen unter `ruf-der-pilze/scripts/character_placeholder.gd`:
```gdscript
extends Node3D
const CLASS_COLORS: Dictionary = {
"Krieger": Color(0.80, 0.20, 0.20), # Rot
"Magier": Color(0.20, 0.20, 0.90), # Blau
"Kleriker": Color(0.90, 0.85, 0.20), # Gelb
"Schurke": Color(0.20, 0.65, 0.20), # Grün
"Druide": Color(0.40, 0.75, 0.30), # Hellgrün
"Barbar": Color(0.75, 0.40, 0.10), # Orange
}
const DEFAULT_COLOR := Color(0.65, 0.65, 0.65) # Grau
func set_color(class_name: String) -> void:
var mat := StandardMaterial3D.new()
mat.albedo_color = CLASS_COLORS.get(class_name, DEFAULT_COLOR)
($Capsule as MeshInstance3D).material_override = mat
func set_label(player_name: String) -> void:
($NameLabel as Label3D).text = player_name
```
- [ ] **Schritt 2: character_placeholder.tscn via Godot-Editor erstellen**
Neue Szene: `Datei → Neue Szene → Node3D als Root`.
Root umbenennen in `CharacterPlaceholder`, Script `character_placeholder.gd` zuweisen.
Kinder-Nodes hinzufügen:
**MeshInstance3D** mit Name `Capsule`:
- `mesh` = neues `CapsuleMesh`
- CapsuleMesh-Properties: `height = 1.8`, `radius = 0.3`
- `material_override` = neues `StandardMaterial3D`, `albedo_color = Color(0.65, 0.65, 0.65)`
**Label3D** mit Name `NameLabel`:
- `text = ""`
- `position = Vector3(0.0, 1.2, 0.0)`
- `font_size = 48`
- `billboard = BILLBOARD_ENABLED`
- `no_depth_test = true`
Szene als `ruf-der-pilze/scenes/character_placeholder.tscn` speichern.
- [ ] **Schritt 3: Parse-Fehler prüfen**
Godot-Editor: Ausgabe-Panel prüfen, keine Fehler in `character_placeholder.gd`.
- [ ] **Schritt 4: Commit**
```bash
git add ruf-der-pilze/scenes/character_placeholder.tscn ruf-der-pilze/scripts/character_placeholder.gd
git commit -m "feat: add CharacterPlaceholder scene — colored capsule + name label"
```
---
## Task 2: LobbySpot-Marker in tavern_lobby.tscn
**Files:** Modify `ruf-der-pilze/scenes/tavern_lobby.tscn`
`tavern_lobby.tscn` instanziiert `taproom.tscn` als Sub-Scene und hat einen CanvasLayer für UI. Die Marker3D-Nodes kommen in die 3D-Welt (unter der Root-Node, nicht unter CanvasLayer).
- [ ] **Schritt 1: 5 Marker3D-Nodes in tavern_lobby.tscn hinzufügen**
Im Godot-Editor `tavern_lobby.tscn` öffnen. Im Scene-Panel: Root-Node (oder die Node3D direkt unterhalb der Root) → `Marker3D` hinzufügen. Fünfmal wiederholen.
Namen und Positionen:
```
Marker3D "LobbySpot0" — position = Vector3(-2.0, 0.0, 1.0)
Marker3D "LobbySpot1" — position = Vector3(-1.0, 0.0, 1.5)
Marker3D "LobbySpot2" — position = Vector3( 0.0, 0.0, 1.8)
Marker3D "LobbySpot3" — position = Vector3( 1.0, 0.0, 1.5)
Marker3D "LobbySpot4" — position = Vector3( 2.0, 0.0, 1.0)
```
Koordinaten ggf. anpassen damit Kapseln im Gastraum stehen (nicht in Wänden). Orientierung der Marker: `rotation_degrees.y = 180` so dass Kapseln zur Kamera schauen.
- [ ] **Schritt 2: Screenshot via godot-mcp prüfen**
Godot-Editor: Szene kurz ausführen oder Viewport nutzen um zu prüfen ob Positionen im Gastraum liegen. Anpassen falls nötig.
- [ ] **Schritt 3: Commit**
```bash
git add ruf-der-pilze/scenes/tavern_lobby.tscn
git commit -m "lobby: add 5 LobbySpot Marker3D for capsule placement"
```
---
## Task 3: Capsule-Spawn-Logik in tavern_lobby.gd
**Files:** Modify `ruf-der-pilze/scripts/tavern_lobby.gd`
Zuerst das Script lesen um bestehende Struktur zu verstehen. Neue Felder und Methoden hinzufügen — bestehenden Code nicht anfassen.
- [ ] **Schritt 1: Capsule-Tracking-Variablen + Preload hinzufügen**
Am Anfang der Klasse (nach bestehenden `var`-Deklarationen):
```gdscript
const CharacterPlaceholder := preload("res://scenes/character_placeholder.tscn")
var _capsules: Dictionary = {} # peer_id: int → Node3D
var _lobby_spots: Array[Marker3D] = []
```
- [ ] **Schritt 2: Spot-Array + Signal-Verbindungen in `_ready()` ergänzen**
Am Ende von `_ready()` anhängen:
```gdscript
_lobby_spots = [
$LobbySpot0 as Marker3D,
$LobbySpot1 as Marker3D,
$LobbySpot2 as Marker3D,
$LobbySpot3 as Marker3D,
$LobbySpot4 as Marker3D,
]
NetworkManager.player_joined.connect(_on_player_joined_capsule)
NetworkManager.player_left.connect(_on_player_left_capsule)
NetworkManager.character_updated.connect(_on_character_updated_capsule)
```
Pfade `$LobbySpot0` etc. anpassen wenn Marker3D-Nodes unter einer anderen Parent-Node liegen.
- [ ] **Schritt 3: Spawn/Remove/Update-Methoden hinzufügen**
```gdscript
func _on_player_joined_capsule(peer_id: int, _data: Dictionary) -> void:
if _capsules.has(peer_id):
return
var spot_index := _capsules.size() % _lobby_spots.size()
var capsule: Node3D = CharacterPlaceholder.instantiate()
add_child(capsule)
capsule.global_position = _lobby_spots[spot_index].global_position
var player_data: Dictionary = NetworkManager.players.get(peer_id, {})
capsule.set_label(player_data.get("name", "???"))
capsule.set_color(player_data.get("char", {}).get("class", ""))
_capsules[peer_id] = capsule
func _on_player_left_capsule(peer_id: int) -> void:
if not _capsules.has(peer_id):
return
(_capsules[peer_id] as Node3D).queue_free()
_capsules.erase(peer_id)
func _on_character_updated_capsule(peer_id: int, data: Dictionary) -> void:
if not _capsules.has(peer_id):
return
(_capsules[peer_id] as Node3D).set_color(data.get("class", ""))
```
- [ ] **Schritt 4: Godot öffnen, Parse-Fehler prüfen**
- [ ] **Schritt 5: End-to-End-Test**
Server + 2 Spieler-Clients starten.
- Spieler A joined → Kapsel A erscheint an LobbySpot0 (grau, kein char-Daten)
- Spieler B joined → Kapsel B erscheint an LobbySpot1
- Wenn Schritt 12 gemergt: Spieler A klickt "Magier" → Kapsel A wird blau
- Spieler A verlässt → Kapsel A verschwindet
- [ ] **Schritt 6: Commit**
```bash
git add ruf-der-pilze/scripts/tavern_lobby.gd
git commit -m "lobby: spawn colored capsule avatars for each player at LobbySpots"
```
---
## Task 4: STATUS.md updaten
- [ ] **Schritt 1: Schritt 13 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 13 complete — Charakter-Capsules"
```

View File

@@ -0,0 +1,313 @@
# Schritt 14 — Tavern Art Pass
> **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:** `taproom.tscn` (Gastraum EG, geteilt von Lobby + In-Game-Taverne) und die Obergeschoss-Zimmer in `tavern.tscn` bekommen echte 3D-Props statt Placeholder-Geometrie. Quellen: Polyhaven (CC0, kostenlos) via `blender-mcp`, optional Hyper3D/Meshy für fehlende Assets.
**Architecture:** Assets via Blender-MCP downloaden/generieren → Skala normalisieren → als `.glb` nach `ruf-der-pilze/assets/props/` exportieren → via Godot-Editor in Szenen platzieren. `taproom.tscn` hat kein Script — Props als reine MeshInstance3D-Nodes einfügen. Keine Logic-Änderungen an Scripts.
**Tech Stack:** Godot 4.x, Blender (blender-mcp MCP-Server), Polyhaven (CC0 Models), godot-mcp Screenshot-Loop
**Asset-Liste:**
| Asset | Anzahl | Szene | Polyhaven-Suche |
|-------|--------|-------|-----------------|
| Holztisch | 45 | taproom | `"wooden table"` / `"pub table"` |
| Holzstuhl/Hocker | 812 | taproom | `"wooden chair"` / `"stool"` |
| Bar-Counter/Tresen | 1 | taproom | `"bar counter"` (evtl. Hyper3D) |
| Fässer (Barrel) | 35 | taproom | `"barrel"` / `"wooden barrel"` |
| Kerzenhalter | 46 | taproom | `"candle"` / `"candlestick"` |
| Bett (einfach) | 1 Template | tavern OG | `"bed"` / `"medieval bed"` |
| Kleiner Tisch | 1 Template | tavern OG | (Wiederverwendung Tisch) |
---
## Files
| File | Aktion |
|------|--------|
| `ruf-der-pilze/assets/props/` | Verzeichnis anlegen, .glb Dateien ablegen |
| `ruf-der-pilze/scenes/taproom.tscn` | Modify — Props platzieren (Tische, Stühle, Tresen, Fässer, Kerzen) |
| `ruf-der-pilze/scenes/tavern.tscn` | Modify — Zimmer-Props (Bett, Tisch, Stuhl, Kerze) |
| `docs/STATUS.md` | Modify — Schritt 14 ✅ |
---
## Task 1: Assets via Polyhaven-MCP suchen und downloaden
**Vorbedingung:** Blender muss offen sein und der blender-mcp-Server muss laufen.
- [ ] **Schritt 1: Polyhaven-Kategorien abrufen**
```
Tool: mcp__blender__get_polyhaven_categories
```
Kategorien für Models notieren (sollte "models" o.ä. enthalten).
- [ ] **Schritt 2: Tisch-Assets suchen**
```
Tool: mcp__blender__search_polyhaven_assets
Params: type="models", query="wooden table"
```
Und:
```
Tool: mcp__blender__search_polyhaven_assets
Params: type="models", query="pub table"
```
Passenden Asset-ID notieren (z.B. `pub_table`, `round_wooden_table`).
- [ ] **Schritt 3: Stuhl/Hocker-Assets suchen**
```
Tool: mcp__blender__search_polyhaven_assets
Params: type="models", query="wooden stool"
```
- [ ] **Schritt 4: Fass-Assets suchen**
```
Tool: mcp__blender__search_polyhaven_assets
Params: type="models", query="barrel"
```
- [ ] **Schritt 5: Kerzen-Assets suchen**
```
Tool: mcp__blender__search_polyhaven_assets
Params: type="models", query="candle"
```
- [ ] **Schritt 6: Bett-Assets suchen**
```
Tool: mcp__blender__search_polyhaven_assets
Params: type="models", query="bed"
```
- [ ] **Schritt 7: Gefundene Assets herunterladen (nacheinander)**
Für jeden Asset:
```
Tool: mcp__blender__download_polyhaven_asset
Params: asset_id="<id>", asset_type="models", resolution="1k"
```
Status pollen:
```
Tool: mcp__blender__get_polyhaven_status
```
Warten bis `status = "completed"`.
- [ ] **Schritt 8: Falls Bar-Counter nicht auf Polyhaven: Hyper3D nutzen**
```
Tool: mcp__blender__generate_hyper3d_model_via_text
Params: prompt="medieval tavern bar counter, dark wood, worn surface, low poly game asset, simple geometry"
```
Status pollen:
```
Tool: mcp__blender__get_hyper3d_status
```
Asset importieren:
```
Tool: mcp__blender__import_generated_asset
```
---
## Task 2: Assets in Blender normalisieren und als .glb exportieren
- [ ] **Schritt 1: Blender-Szene prüfen**
```
Tool: mcp__blender__get_scene_info
Tool: mcp__blender__get_viewport_screenshot
```
Alle heruntergeladenen Assets sollten in der Szene sichtbar sein.
- [ ] **Schritt 2: Assets-Verzeichnis anlegen**
```bash
mkdir -p /home/mpuchstein/Dev/Godot/DnD_Anna_OneShot/ruf-der-pilze/assets/props
```
- [ ] **Schritt 3: Skala prüfen und normalisieren**
Referenz-Maße für mittelalterliche Taverne:
- Tisch: ca. 0.75m hoch, 1.2m × 0.8m Fläche
- Stuhl/Hocker: ca. 0.45m hoch
- Barrel: ca. 0.8m hoch
- Kerzenhalter: ca. 0.3m hoch
- Bett: ca. 0.5m hoch, 2.0m × 1.0m
Skala per Blender-Code anpassen (für jeden Asset einzeln ausführen):
```python
# Beispiel: Tisch auf 0.75m Höhe skalieren
import bpy
obj = bpy.data.objects["<asset_object_name>"] # Namen aus get_scene_info
dims = obj.dimensions
if dims.z > 0:
scale_factor = 0.75 / dims.z
obj.scale = (scale_factor, scale_factor, scale_factor)
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.transform_apply(scale=True)
```
```
Tool: mcp__blender__execute_blender_code
Params: code="<python_code>"
```
- [ ] **Schritt 4: Assets einzeln als .glb exportieren**
Für jeden Asset (Objekt selektieren, dann exportieren):
```python
import bpy
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects["<object_name>"].select_set(True)
bpy.ops.export_scene.gltf(
filepath="/home/mpuchstein/Dev/Godot/DnD_Anna_OneShot/ruf-der-pilze/assets/props/table.glb",
export_format='GLB',
use_selection=True,
export_materials='EXPORT'
)
```
Zieldateien:
- `assets/props/table.glb`
- `assets/props/chair.glb`
- `assets/props/barrel.glb`
- `assets/props/candle.glb`
- `assets/props/bed.glb`
- `assets/props/bar_counter.glb`
- [ ] **Schritt 5: Commit**
```bash
git add ruf-der-pilze/assets/props/
git commit -m "assets: import tavern props — table, chair, barrel, candle, bed, bar_counter"
```
---
## Task 3: taproom.tscn mit Props bestücken
**Files:** Modify `ruf-der-pilze/scenes/taproom.tscn`
`taproom.tscn` ist die Gastraum-Geometrie ohne Script. Hier werden Props als Kinder-Nodes eingefügt. Die Props werden als instanziierte .glb-Szenen oder MeshInstance3D-Nodes platziert.
- [ ] **Schritt 1: Aktuellen Stand per Screenshot prüfen**
In Godot: `taproom.tscn` öffnen. Godot-MCP Screenshot machen um die Raumgröße und Geometry zu sehen:
```
Tool: mcp__godot-mcp__scene
Params: action="get_node_properties", scene_path="res://scenes/taproom.tscn", node_path="."
```
- [ ] **Schritt 2: Bar-Counter an Wand platzieren**
Im Godot-Editor: `bar_counter.glb` (aus `assets/props/`) per Drag & Drop in `taproom.tscn` ziehen, oder Node3D hinzufügen und Mesh zuweisen. Position entlang einer Wand:
- Ungefähre Position: `Vector3(-4.0, 0.0, 0.0)` — an der linken Wand
- Rotation anpassen damit Counter zur Mitte zeigt
- [ ] **Schritt 3: 4 Tische im Raum verteilen**
Vier Tisch-Nodes platzieren, gleichmäßig verteilt:
```
Tisch 1: Vector3(-1.5, 0.0, -2.0)
Tisch 2: Vector3( 1.5, 0.0, -2.0)
Tisch 3: Vector3(-1.5, 0.0, 1.0)
Tisch 4: Vector3( 1.5, 0.0, 1.0)
```
- [ ] **Schritt 4: Stühle/Hocker um jeden Tisch**
Je 24 Stühle um jeden Tisch. Versatz vom Tisch-Zentrum: ca. 0.7m in jede Richtung.
- [ ] **Schritt 5: 34 Fässer nahe Tresen**
Fässer geclustert nahe dem Bar-Counter. Position: entlang der Tresen-Wand.
- [ ] **Schritt 6: Kerzenhalter auf Tischen + Wandbefestigung**
Je 1 Kerzenhalter auf jedem Tisch (Y-Position = Tischhöhe ~0.75). 2 Kerzenhalter an Wänden.
- [ ] **Schritt 7: Screenshot via godot-mcp, Iterationen bis es gut aussieht**
Mindestens 23 Korrekturrunden. Ziel: bewohnter, atmosphärischer Gastraum.
- [ ] **Schritt 8: Commit**
```bash
git add ruf-der-pilze/scenes/taproom.tscn
git commit -m "art: taproom props — tables, chairs, bar counter, barrels, candles"
```
---
## Task 4: Zimmer in tavern.tscn bestücken
**Files:** Modify `ruf-der-pilze/scenes/tavern.tscn`
Die Taverne hat 9 Zimmer im OG (SpawnPoint0SpawnPoint8). Zuerst **Zimmer 1 (SpawnPoint0)** vollständig einrichten, dann das Muster für alle weiteren Zimmer wiederholen.
- [ ] **Schritt 1: tavern.tscn öffnen und SpawnPoint0-Zimmer lokalisieren**
Godot-Editor: `tavern.tscn` öffnen. Im Scene-Panel die SpawnPoint0-Node finden und deren Position notieren.
- [ ] **Schritt 2: Zimmer 1 mit Props bestücken**
Props relativ zu SpawnPoint0-Position (Offsets anpassen nach Zimmergeometrie):
- **Bett:** SpawnPoint.position + Vector3(1.2, 0.0, 0.5), an Wand
- **Kleiner Tisch:** SpawnPoint.position + Vector3(-1.0, 0.0, -0.8)
- **Stuhl:** neben Tisch, Offset ~0.5m
- **Kerzenhalter:** auf Tisch, Y = Tischhöhe
- [ ] **Schritt 3: Screenshot prüfen**
Godot-Editor Viewport oder godot-mcp Screenshot für Zimmer 1.
- [ ] **Schritt 4: Zimmer 29 analog bestücken**
Für SpawnPoint1SpawnPoint8 dieselben Props einfügen. Positionen relativ zum jeweiligen SpawnPoint.
- [ ] **Schritt 5: Commit**
```bash
git add ruf-der-pilze/scenes/tavern.tscn
git commit -m "art: tavern OG rooms — bed, table, chair, candle per room (9 rooms)"
```
---
## Task 5: Gesamt-Test + STATUS.md
- [ ] **Schritt 1: Spiel starten und Taverne visuell prüfen**
Server + 2 Clients starten (oder Godot-Editor ausführen):
- `tavern_lobby.tscn`: Gastraum sieht bewohnt aus
- `tavern.tscn` nach game_started: EG-Gastraum mit Props, OG-Zimmer mit Bett/Tisch/Kerze
- Performance: FPS sollte bei >30 bleiben (Godot-Editor Statistiken)
- [ ] **Schritt 2: Schritt 14 als ✅ markieren**
In `docs/STATUS.md` und `ruf-der-pilze/CLAUDE.md`.
- [ ] **Schritt 3: Commit**
```bash
git add docs/STATUS.md ruf-der-pilze/CLAUDE.md
git commit -m "docs: mark Schritt 14 complete — Tavern Art Pass"
```