From 1bec9f241fae22b7a7cb809a3cb6f96c1d5622a1 Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Thu, 16 Apr 2026 00:52:02 +0200 Subject: [PATCH 1/4] net: add request_update_character RPC + character_updated signal --- ruf-der-pilze/scripts/network_manager.gd | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ruf-der-pilze/scripts/network_manager.gd b/ruf-der-pilze/scripts/network_manager.gd index f4b7de7..fca6e1a 100644 --- a/ruf-der-pilze/scripts/network_manager.gd +++ b/ruf-der-pilze/scripts/network_manager.gd @@ -144,3 +144,22 @@ func _build_room_assignments() -> Dictionary: func _broadcast_player_left(peer_id: int) -> void: players.erase(peer_id) player_left.emit(peer_id) + + +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) From 5d2d97d064702bae8913a1c8de03bd98e6d7a425 Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Thu, 16 Apr 2026 00:52:02 +0200 Subject: [PATCH 2/4] lobby: add character sheet UI nodes (CharSheet, ModGrid, ArchetypeRow) --- ruf-der-pilze/scenes/tavern_lobby.tscn | 100 +++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/ruf-der-pilze/scenes/tavern_lobby.tscn b/ruf-der-pilze/scenes/tavern_lobby.tscn index e4fd68c..be4675b 100644 --- a/ruf-der-pilze/scenes/tavern_lobby.tscn +++ b/ruf-der-pilze/scenes/tavern_lobby.tscn @@ -48,3 +48,103 @@ custom_minimum_size = Vector2(0, 150) [node name="StartButton" type="Button" parent="CanvasLayer/WaitPanel"] text = "Spiel starten" visible = false + +[node name="CharSheet" type="VBoxContainer" parent="CanvasLayer/WaitPanel"] +visible = false + +[node name="LblChar" type="Label" parent="CanvasLayer/WaitPanel/CharSheet"] +text = "Charakter" + +[node name="CharGrid" type="GridContainer" parent="CanvasLayer/WaitPanel/CharSheet"] +columns = 2 + +[node name="LblRace" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/CharGrid"] +text = "Rasse" + +[node name="RaceOption" type="OptionButton" parent="CanvasLayer/WaitPanel/CharSheet/CharGrid"] + +[node name="LblClass" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/CharGrid"] +text = "Klasse" + +[node name="ClassOption" type="OptionButton" parent="CanvasLayer/WaitPanel/CharSheet/CharGrid"] + +[node name="HSeparator" type="HSeparator" parent="CanvasLayer/WaitPanel/CharSheet"] + +[node name="LblModifiers" type="Label" parent="CanvasLayer/WaitPanel/CharSheet"] +text = "Werte" + +[node name="ModGrid" type="GridContainer" parent="CanvasLayer/WaitPanel/CharSheet"] +columns = 6 + +[node name="LblSTR" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +text = "STR" + +[node name="LblDEX" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +text = "DEX" + +[node name="LblCON" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +text = "CON" + +[node name="LblINT" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +text = "INT" + +[node name="LblWIS" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +text = "WIS" + +[node name="LblCHA" type="Label" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +text = "CHA" + +[node name="SpinSTR" type="SpinBox" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +min_value = -5.0 +max_value = 10.0 +value = 0.0 + +[node name="SpinDEX" type="SpinBox" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +min_value = -5.0 +max_value = 10.0 +value = 0.0 + +[node name="SpinCON" type="SpinBox" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +min_value = -5.0 +max_value = 10.0 +value = 0.0 + +[node name="SpinINT" type="SpinBox" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +min_value = -5.0 +max_value = 10.0 +value = 0.0 + +[node name="SpinWIS" type="SpinBox" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +min_value = -5.0 +max_value = 10.0 +value = 0.0 + +[node name="SpinCHA" type="SpinBox" parent="CanvasLayer/WaitPanel/CharSheet/ModGrid"] +min_value = -5.0 +max_value = 10.0 +value = 0.0 + +[node name="HSeparator2" type="HSeparator" parent="CanvasLayer/WaitPanel/CharSheet"] + +[node name="LblArchetype" type="Label" parent="CanvasLayer/WaitPanel/CharSheet"] +text = "Schnell-Auswahl (Dev)" + +[node name="ArchetypeRow" type="HBoxContainer" parent="CanvasLayer/WaitPanel/CharSheet"] + +[node name="BtnKrieger" type="Button" parent="CanvasLayer/WaitPanel/CharSheet/ArchetypeRow"] +text = "Krieger" + +[node name="BtnMagier" type="Button" parent="CanvasLayer/WaitPanel/CharSheet/ArchetypeRow"] +text = "Magier" + +[node name="BtnKleriker" type="Button" parent="CanvasLayer/WaitPanel/CharSheet/ArchetypeRow"] +text = "Kleriker" + +[node name="BtnSchurke" type="Button" parent="CanvasLayer/WaitPanel/CharSheet/ArchetypeRow"] +text = "Schurke" + +[node name="BtnDruide" type="Button" parent="CanvasLayer/WaitPanel/CharSheet/ArchetypeRow"] +text = "Druide" + +[node name="BtnBarbar" type="Button" parent="CanvasLayer/WaitPanel/CharSheet/ArchetypeRow"] +text = "Barbar" From bea3f5867b9f7a1a57292fdb032287bc8ec31c6a Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Thu, 16 Apr 2026 00:52:02 +0200 Subject: [PATCH 3/4] =?UTF-8?q?lobby:=20wire=20character=20sheet=20form=20?= =?UTF-8?q?=E2=80=94=20archetypes,=20modifier=20spinboxes,=20auto-sync=20R?= =?UTF-8?q?PC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruf-der-pilze/scripts/tavern_lobby.gd | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/ruf-der-pilze/scripts/tavern_lobby.gd b/ruf-der-pilze/scripts/tavern_lobby.gd index 6c7c3c4..198afb6 100644 --- a/ruf-der-pilze/scripts/tavern_lobby.gd +++ b/ruf-der-pilze/scripts/tavern_lobby.gd @@ -3,6 +3,63 @@ extends Node3D var _local_role: String = "" var _pending_player_name: String = "" +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 := $CanvasLayer/WaitPanel/CharSheet/CharGrid/RaceOption as OptionButton + var class_opt := $CanvasLayer/WaitPanel/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($CanvasLayer/WaitPanel/CharSheet/ModGrid/SpinSTR.value), + "DEX": int($CanvasLayer/WaitPanel/CharSheet/ModGrid/SpinDEX.value), + "CON": int($CanvasLayer/WaitPanel/CharSheet/ModGrid/SpinCON.value), + "INT": int($CanvasLayer/WaitPanel/CharSheet/ModGrid/SpinINT.value), + "WIS": int($CanvasLayer/WaitPanel/CharSheet/ModGrid/SpinWIS.value), + "CHA": int($CanvasLayer/WaitPanel/CharSheet/ModGrid/SpinCHA.value), + } + +func _send_char_data() -> void: + NetworkManager.request_update_character.rpc_id(1, _collect_char_data()) + +func _setup_char_sheet() -> void: + var race_opt := $CanvasLayer/WaitPanel/CharSheet/CharGrid/RaceOption as OptionButton + var class_opt := $CanvasLayer/WaitPanel/CharSheet/CharGrid/ClassOption as OptionButton + for race in ["Mensch", "Elf", "Zwerg", "Halbork", "Halbelf", "Tiefling"]: + race_opt.add_item(race) + for cls in ["Krieger", "Magier", "Kleriker", "Schurke", "Druide", "Barbar"]: + class_opt.add_item(cls) + for archetype_name: String in ARCHETYPES: + var btn := $CanvasLayer/WaitPanel/CharSheet/ArchetypeRow.get_node( + NodePath("Btn" + archetype_name)) as Button + var preset: Dictionary = ARCHETYPES[archetype_name] + btn.pressed.connect(func(): + var r_opt := $CanvasLayer/WaitPanel/CharSheet/CharGrid/RaceOption as OptionButton + var c_opt := $CanvasLayer/WaitPanel/CharSheet/CharGrid/ClassOption as OptionButton + for i in r_opt.item_count: + if r_opt.get_item_text(i) == preset["race"]: + r_opt.selected = i + for i in c_opt.item_count: + if c_opt.get_item_text(i) == preset["class"]: + c_opt.selected = i + for stat: String in ["STR", "DEX", "CON", "INT", "WIS", "CHA"]: + ($CanvasLayer/WaitPanel/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"]: + ($CanvasLayer/WaitPanel/CharSheet/ModGrid.get_node(NodePath(spin_name)) as SpinBox + ).value_changed.connect(func(_v: float): _send_char_data()) + (race_opt as OptionButton).item_selected.connect(func(_i: int): _send_char_data()) + (class_opt as OptionButton).item_selected.connect(func(_i: int): _send_char_data()) + func _ready() -> void: $CanvasLayer/JoinPanel/RoleOption.add_item("Spieler") @@ -16,6 +73,7 @@ func _ready() -> void: NetworkManager.player_joined.connect(_on_player_joined) NetworkManager.player_left.connect(_on_player_left) NetworkManager.player_list_synced.connect(_rebuild_player_list) + _setup_char_sheet() func _on_join_pressed() -> void: @@ -32,6 +90,7 @@ func _on_connected() -> void: $CanvasLayer/JoinPanel.visible = false $CanvasLayer/WaitPanel.visible = true $CanvasLayer/WaitPanel/StartButton.visible = (_local_role == "dm") + $CanvasLayer/WaitPanel/CharSheet.visible = true func _exit_tree() -> void: From 0a2f814e97b056b47dda11ca170f7536460b127f Mon Sep 17 00:00:00 2001 From: "s0wlz (Matthias Puchstein)" Date: Thu, 16 Apr 2026 00:52:02 +0200 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20mark=20Schritt=2012=20complete=20?= =?UTF-8?q?=E2=80=94=20lite=20Charakterbogen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/STATUS.md | 5 ++++- ruf-der-pilze/CLAUDE.md | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/STATUS.md b/docs/STATUS.md index d3f2449..f90e19f 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,6 +1,6 @@ # Ruf der Pilze — Projektstatus -Zuletzt aktualisiert: 2026-04-14 (Schritt 5 abgeschlossen) +Zuletzt aktualisiert: 2026-04-16 (Schritt 12 abgeschlossen) --- @@ -15,6 +15,8 @@ Zuletzt aktualisiert: 2026-04-14 (Schritt 5 abgeschlossen) - **Tavern Lobby** — 3D-Taverne ersetzt die flache Lobby-UI; SceneManager Autoload eingeführt - **Taverne + Szenen-Wechsel** — 2-stöckige In-Game-Taverne (Blockout), taproom.tscn als Sub-Scene, game_started → Spieler in zugewiesenem Zimmer (SpawnPoint), DM in dm_view.tscn Stub +- **Lite Charakterbogen** — Rasse/Klasse OptionButtons, 6 Ability-Modifier SpinBoxen, 6 Archetype-Quick-Fill-Buttons; + Auto-Sync via `request_update_character` RPC, `character_updated` Signal in NetworkManager ### 🔄 In Arbeit — (nichts aktiv) @@ -37,6 +39,7 @@ Zuletzt aktualisiert: 2026-04-14 (Schritt 5 abgeschlossen) 7. ⏳ Refectorium — asymmetrische Wahrnehmung (erster Raum) 8. ⏳ Alle Räume aufbauen 9. ⏳ Polish — Audio, Nebel, Licht, Würfel-UI +12. ✅ Lite Charakterbogen — Rasse, Klasse, Modifier-Spinboxen, Archetype-Quick-Fill, RPC-Sync --- diff --git a/ruf-der-pilze/CLAUDE.md b/ruf-der-pilze/CLAUDE.md index 69bc51b..3616f4c 100644 --- a/ruf-der-pilze/CLAUDE.md +++ b/ruf-der-pilze/CLAUDE.md @@ -241,6 +241,7 @@ Verzeichnis: `../Anna_Model/` (außerhalb von `ruf-der-pilze/`, im Repo-Root) 7. ⏳ Erster Raum — Refectory mit asymmetrischer Wahrnehmung 8. ⏳ Alle Räume aufbauen 9. ⏳ Polish — Audio, Nebel, Licht, Würfel-UI +12. ✅ Lite Charakterbogen — Rasse, Klasse, Modifier-Spinboxen, Archetype-Quick-Fill, RPC-Sync ---