feat: merge Schritt 12 — lite Charakterbogen (manual conflict resolution)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Ruf der Pilze — Projektstatus
|
||||
|
||||
Zuletzt aktualisiert: 2026-04-16 (Schritt 11 abgeschlossen — Sporennebel Slider; alle Mechaniken fertig)
|
||||
Zuletzt aktualisiert: 2026-04-16 (Schritt 12 abgeschlossen — Lite Charakterbogen)
|
||||
|
||||
---
|
||||
|
||||
@@ -18,6 +18,8 @@ Zuletzt aktualisiert: 2026-04-16 (Schritt 11 abgeschlossen — Sporennebel Slide
|
||||
game_started → Spieler in zugewiesenem Zimmer (SpawnPoint), DM in dm_view.tscn Stub
|
||||
- **DM Regiepult Basics** — GameState Autoload, Overlay-RPC-Kette, EG+OG Top-Down nebeneinander,
|
||||
Spieler-Positionsmarker (etagen-gefiltert), Live-Cam-Feeds pro Spieler, Overlay-Toggle-Panel
|
||||
- **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
|
||||
|
||||
@@ -47,7 +49,7 @@ Zuletzt aktualisiert: 2026-04-16 (Schritt 11 abgeschlossen — Sporennebel Slide
|
||||
|
||||
### Charaktere + Assets
|
||||
|
||||
12. ⏳ Lite Charakterbogen in Lobby — CharName, Rasse, Klasse, 6 Skill-Modifier; Archetypes für Dev
|
||||
12. ✅ Lite Charakterbogen in Lobby — Rasse/Klasse OptionButtons, 6 Modifier-SpinBoxen, 6 Archetype-Buttons; RPC-Sync
|
||||
13. ⏳ Charakter-Platzhalter — farbige Capsules pro Archetyp, austauschbar
|
||||
14. ⏳ Tavern Art Pass — echte Props (Tische, Stühle, Tresen, Betten, Fässer, Kerzen) via Polyhaven/Sketchfab/Meshy
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ Verzeichnis: `../Anna_Model/` (außerhalb von `ruf-der-pilze/`, im Repo-Root)
|
||||
10. ✅ Spore Overlay — spore_overlay.gdshader, overlay_changed signal, CanvasLayer layer=10
|
||||
11. ✅ Sporennebel Slider — SporeLevel Autoload, request_spore_level RPC, HSlider im DM-Panel
|
||||
--- Charaktere + Assets ---
|
||||
12. ⏳ Lite Charakterbogen — CharName, Rasse, Klasse, 6 Skill-Modifier; Archetypes für Dev
|
||||
12. ✅ Lite Charakterbogen — Rasse/Klasse OptionButtons, 6 Modifier-SpinBoxen, 6 Archetype-Buttons; RPC-Sync
|
||||
13. ⏳ Charakter-Platzhalter — farbige Capsules pro Archetyp
|
||||
14. ⏳ Tavern Art Pass — echte Props via Polyhaven/Sketchfab/Meshy AI
|
||||
--- Klosterwelt ---
|
||||
@@ -271,7 +271,6 @@ Verzeichnis: `../Anna_Model/` (außerhalb von `ruf-der-pilze/`, im Repo-Root)
|
||||
26. ⏳ Aktions-Selektor — klassen-spezifische Zauber/Angriff-Effekte (nur visuell)
|
||||
27. ⏳ Full Spore Effects — Nebel-Partikel, Bewegungsverzerrung
|
||||
28. ⏳ Soundscape + Spieluhr-Melodie — direktionaler Audio, Annas Lied
|
||||
|
||||
---
|
||||
|
||||
## Arbeitsweise
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -16,6 +16,7 @@ signal player_list_synced()
|
||||
signal game_started()
|
||||
signal roll_received(roller_peer_id: int, player_name: String, d20_result: int, modifier: int, total: int)
|
||||
signal overlay_changed(overlay_name: String)
|
||||
signal character_updated(peer_id: int, data: Dictionary)
|
||||
|
||||
|
||||
func start_server(port: int, max_clients: int) -> void:
|
||||
@@ -197,3 +198,21 @@ func _relay_roll(roller_peer_id: int, d20_result: int, modifier: int) -> void:
|
||||
var player_name: String = players.get(roller_peer_id, {}).get("name", "???")
|
||||
var total := d20_result + modifier
|
||||
roll_received.emit(roller_peer_id, player_name, d20_result, modifier, total)
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user