merge: feat/tavern-scene into main

This commit is contained in:
2026-04-14 03:14:56 +02:00
11 changed files with 685 additions and 426 deletions

View File

@@ -1,6 +1,6 @@
# Ruf der Pilze — Projektstatus
Zuletzt aktualisiert: 2026-04-14 (Plan Schritt 6 fertig)
Zuletzt aktualisiert: 2026-04-14 (Schritt 5 abgeschlossen)
---
@@ -13,14 +13,13 @@ Zuletzt aktualisiert: 2026-04-14 (Plan Schritt 6 fertig)
- **Multiplayer Grundgerüst** — ENet Server/Client, NetworkManager Autoload, welcome RPC verifiziert
- **Lobby + Rollen** — Join-UI, Warteraum, Spieler/DM-Rollen, Sync, Kick, Spiel-Start-Signal
- **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
### 🔄 In Arbeit
— (nichts aktiv)
### ⏳ Als nächstes
- **Taverne + Szenen-Wechsel nach Spielstart** — 2-stöckige In-Game-Taverne (Blockout), taproom als Sub-Scene, game_started → Spieler in Zimmer, DM in Stub-Szene
Spec: `docs/superpowers/specs/2026-04-14-tavern-scene-design.md`
Plan: `docs/plans/2026-04-14-tavern-scene-plan.md`
- **DM Regiepult Basics** — Top-Down-Karte (EG/OG), Spieler-Positionsmarker, Live-Cam-Feeds pro Spieler, Overlay-Toggle
Plan: `docs/plans/2026-04-14-dm-regiepult-basics.md`
@@ -32,7 +31,7 @@ Zuletzt aktualisiert: 2026-04-14 (Plan Schritt 6 fertig)
2. ✅ Multiplayer Grundgerüst (Server, Clients verbinden, rpc testen)
3. ✅ Lobby + Rollen (Spieler registrieren sich, DM kriegt Sonderrechte)
4. ✅ Tavern Lobby — 3D-Taverne als Warteraum, SceneManager Autoload
5. Szenen-Wechsel nach Spielstart — Chamber/DM-Szene nach game_started
5. Szenen-Wechsel nach Spielstart — taproom Sub-Scene, 2 Etagen, SpawnPoints, dm_view Stub
6. ⏳ DM Regiepult Basics — Overlay-Toggle, Top-Down pro Etage, Player-Cams
Plan: `docs/plans/2026-04-14-dm-regiepult-basics.md`
7. ⏳ Refectorium — asymmetrische Wahrnehmung (erster Raum)

View File

@@ -233,10 +233,9 @@ Verzeichnis: `../Anna_Model/` (außerhalb von `ruf-der-pilze/`, im Repo-Root)
2. ✅ Multiplayer Grundgerüst (Server, Clients verbinden, rpc testen)
3. ✅ Lobby + Rollen (Spieler mit Name + Rolle, DM-Sonderrechte)
4. ✅ Tavern Lobby — 3D-Taverne als Warteraum, SceneManager Autoload
5. Taverne + Szenen-Wechsel — 2-stöckige In-Game-Taverne (Blockout), taproom.tscn
5. Taverne + Szenen-Wechsel — 2-stöckige In-Game-Taverne (Blockout), taproom.tscn
als Sub-Scene, game_started → Spieler in Zimmer (SpawnPoint), DM → dm_view.tscn
Kein separates "chamber" — Zimmer sind Teil von tavern.tscn
Plan: ../docs/plans/2026-04-14-tavern-scene-plan.md
6. ⏳ DM Regiepult Basics — Overlay-Toggle, Top-Down pro Etage, Player-Cams
Plan: `../docs/plans/2026-04-14-dm-regiepult-basics.md`
7. ⏳ Erster Raum — Refectory mit asymmetrischer Wahrnehmung

View File

@@ -5,55 +5,27 @@
[node name="DmView" type="Node"]
script = ExtResource("1")
[node name="RootLayout" type="VBoxContainer" parent="."]
[node name="ViewportContainer" type="SubViewportContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="TopSection" type="HSplitContainer" parent="RootLayout"]
size_flags_vertical = 3
[node name="SubViewport" type="SubViewport" parent="ViewportContainer"]
[node name="MapContainer" type="SubViewportContainer" parent="RootLayout/TopSection"]
custom_minimum_size = Vector2(700, 500)
size_flags_horizontal = 3
stretch = true
[node name="MapViewport" type="SubViewport" parent="RootLayout/TopSection/MapContainer"]
size = Vector2i(700, 500)
[node name="MapRoot" type="Node3D" parent="RootLayout/TopSection/MapContainer/MapViewport"]
[node name="PlayerMarkers" type="Node3D" parent="RootLayout/TopSection/MapContainer/MapViewport/MapRoot"]
[node name="TopDownCam" type="Camera3D" parent="RootLayout/TopSection/MapContainer/MapViewport/MapRoot"]
transform = Transform3D(1, 0, 0, 0, 0, -1, 0, 1, 0, 2, 25, 0)
[node name="Camera3D" type="Camera3D" parent="ViewportContainer/SubViewport"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 20, 0)
current = true
[node name="SidePanel" type="VBoxContainer" parent="RootLayout/TopSection"]
custom_minimum_size = Vector2(220, 0)
[node name="UI" type="CanvasLayer" parent="."]
[node name="LblFloor" type="Label" parent="RootLayout/TopSection/SidePanel"]
text = "Etage"
[node name="FloorButtons" type="HBoxContainer" parent="RootLayout/TopSection/SidePanel"]
[node name="BtnEG" type="Button" parent="RootLayout/TopSection/SidePanel/FloorButtons"]
text = "Erdgeschoss"
toggle_mode = true
button_pressed = true
[node name="BtnOG" type="Button" parent="RootLayout/TopSection/SidePanel/FloorButtons"]
text = "Obergeschoss"
toggle_mode = true
[node name="HSeparator" type="HSeparator" parent="RootLayout/TopSection/SidePanel"]
[node name="LblOverlay" type="Label" parent="RootLayout/TopSection/SidePanel"]
text = "Overlays"
[node name="OverlayScroll" type="ScrollContainer" parent="RootLayout/TopSection/SidePanel"]
size_flags_vertical = 3
[node name="PlayerList" type="VBoxContainer" parent="RootLayout/TopSection/SidePanel/OverlayScroll"]
[node name="HSeparator2" type="HSeparator" parent="RootLayout"]
[node name="PlayerCamsRow" type="HBoxContainer" parent="RootLayout"]
custom_minimum_size = Vector2(0, 200)
[node name="StubLabel" type="Label" parent="UI"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -150.0
offset_top = -30.0
offset_right = 150.0
offset_bottom = 30.0
text = "DM View — Stub
(wird in Schritt 7 ausgebaut)"
horizontal_alignment = 1

View File

@@ -0,0 +1,120 @@
[gd_scene load_steps=22 format=3]
[sub_resource type="StandardMaterial3D" id="Material_wood"]
albedo_color = Color(0.169, 0.102, 0.051, 1)
[sub_resource type="BoxMesh" id="BoxMesh_floor"]
size = Vector3(12, 0.1, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_floor"]
size = Vector3(12, 0.1, 8)
[sub_resource type="BoxMesh" id="BoxMesh_ceiling"]
size = Vector3(12, 0.1, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_ceiling"]
size = Vector3(12, 0.1, 8)
[sub_resource type="BoxMesh" id="BoxMesh_wallback"]
size = Vector3(12, 4, 0.1)
[sub_resource type="BoxShape3D" id="BoxShape3D_wallback"]
size = Vector3(12, 4, 0.1)
[sub_resource type="BoxMesh" id="BoxMesh_wallleft"]
size = Vector3(0.1, 4, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_wallleft"]
size = Vector3(0.1, 4, 8)
[sub_resource type="BoxMesh" id="BoxMesh_wallright"]
size = Vector3(0.1, 4, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_wallright"]
size = Vector3(0.1, 4, 8)
[sub_resource type="BoxMesh" id="BoxMesh_bar"]
size = Vector3(4, 1.1, 0.3)
[sub_resource type="BoxShape3D" id="BoxShape3D_bar"]
size = Vector3(4, 1.1, 0.3)
[sub_resource type="Environment" id="Environment_1"]
background_mode = 1
background_color = Color(0.102, 0.059, 0, 1)
ambient_light_color = Color(0.6, 0.35, 0.1, 1)
ambient_light_energy = 0.4
fog_enabled = true
fog_light_color = Color(0.102, 0.059, 0, 1)
fog_density = 0.05
[node name="Taproom" type="Node3D"]
[node name="Floor" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Floor"]
mesh = SubResource("BoxMesh_floor")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Floor"]
shape = SubResource("BoxShape3D_floor")
[node name="Ceiling" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.05, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ceiling"]
mesh = SubResource("BoxMesh_ceiling")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ceiling"]
shape = SubResource("BoxShape3D_ceiling")
[node name="WallBack" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, -4)
[node name="MeshInstance3D" type="MeshInstance3D" parent="WallBack"]
mesh = SubResource("BoxMesh_wallback")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="WallBack"]
shape = SubResource("BoxShape3D_wallback")
[node name="WallLeft" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 2, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="WallLeft"]
mesh = SubResource("BoxMesh_wallleft")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="WallLeft"]
shape = SubResource("BoxShape3D_wallleft")
[node name="WallRight" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 2, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="WallRight"]
mesh = SubResource("BoxMesh_wallright")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="WallRight"]
shape = SubResource("BoxShape3D_wallright")
[node name="Bar" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4, 0.55, -3.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Bar"]
mesh = SubResource("BoxMesh_bar")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Bar"]
shape = SubResource("BoxShape3D_bar")
[node name="Light" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0)
light_color = Color(0.91, 0.643, 0.29, 1)
light_energy = 1.2
omni_range = 10.0
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_1")

View File

@@ -1,108 +1,402 @@
[gd_scene load_steps=8 format=3]
[gd_scene load_steps=28 format=3]
[ext_resource type="Script" path="res://scripts/tavern.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/taproom.tscn" id="2"]
[sub_resource type="Environment" id="Environment_1"]
background_mode = 1
background_color = Color(0.102, 0.059, 0, 1)
ambient_light_color = Color(0.6, 0.35, 0.1, 1)
ambient_light_energy = 0.4
fog_enabled = true
fog_light_color = Color(0.102, 0.059, 0, 1)
fog_density = 0.05
[sub_resource type="StandardMaterial3D" id="Material_1"]
[sub_resource type="StandardMaterial3D" id="Material_wood"]
albedo_color = Color(0.169, 0.102, 0.051, 1)
[sub_resource type="BoxMesh" id="BoxMesh_floor"]
size = Vector3(12, 0.1, 8)
[sub_resource type="StandardMaterial3D" id="Material_myzel"]
albedo_color = Color(0.2, 0.8, 0.3, 1)
emission_enabled = true
emission = Color(0.1, 0.4, 0.1, 1)
[sub_resource type="BoxMesh" id="BoxMesh_ceiling"]
size = Vector3(12, 0.1, 8)
[sub_resource type="BoxMesh" id="BoxMesh_kitchen_back"]
size = Vector3(4, 3, 0.1)
[sub_resource type="BoxMesh" id="BoxMesh_back"]
size = Vector3(12, 4, 0.1)
[sub_resource type="BoxShape3D" id="BoxShape3D_kitchen_back"]
size = Vector3(4, 3, 0.1)
[sub_resource type="BoxMesh" id="BoxMesh_left"]
size = Vector3(0.1, 4, 8)
[sub_resource type="BoxMesh" id="BoxMesh_kitchen_side"]
size = Vector3(0.1, 3, 2.3)
[sub_resource type="BoxMesh" id="BoxMesh_right"]
size = Vector3(0.1, 4, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_kitchen_side"]
size = Vector3(0.1, 3, 2.3)
[sub_resource type="BoxMesh" id="BoxMesh_door"]
size = Vector3(0.1, 4, 2)
[sub_resource type="BoxShape3D" id="BoxShape3D_door"]
size = Vector3(0.1, 4, 2)
[sub_resource type="BoxMesh" id="BoxMesh_trail_long"]
size = Vector3(0.5, 0.01, 3)
[sub_resource type="BoxMesh" id="BoxMesh_trail_medium"]
size = Vector3(0.5, 0.01, 4)
[sub_resource type="BoxMesh" id="BoxMesh_stairs"]
size = Vector3(1.5, 0.2, 4)
[sub_resource type="BoxShape3D" id="BoxShape3D_stairs"]
size = Vector3(1.5, 0.2, 4)
[sub_resource type="BoxMesh" id="BoxMesh_upper_floor"]
size = Vector3(18, 0.1, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_upper_floor"]
size = Vector3(18, 0.1, 8)
[sub_resource type="BoxMesh" id="BoxMesh_upper_wall_long"]
size = Vector3(18, 3, 0.1)
[sub_resource type="BoxShape3D" id="BoxShape3D_upper_wall_long"]
size = Vector3(18, 3, 0.1)
[sub_resource type="BoxMesh" id="BoxMesh_upper_wall_short"]
size = Vector3(0.1, 3, 8)
[sub_resource type="BoxShape3D" id="BoxShape3D_upper_wall_short"]
size = Vector3(0.1, 3, 8)
[sub_resource type="BoxMesh" id="BoxMesh_bed"]
size = Vector3(1.8, 0.5, 0.9)
[sub_resource type="BoxShape3D" id="BoxShape3D_bed"]
size = Vector3(1.8, 0.5, 0.9)
[sub_resource type="BoxMesh" id="BoxMesh_anna_door"]
size = Vector3(0.05, 2.5, 1.2)
[sub_resource type="BoxShape3D" id="BoxShape3D_anna_door"]
size = Vector3(0.05, 2.5, 1.2)
[sub_resource type="BoxMesh" id="BoxMesh_myzel_patch"]
size = Vector3(0.8, 0.01, 0.8)
[sub_resource type="BoxMesh" id="BoxMesh_myzel_trail_anna"]
size = Vector3(0.4, 0.01, 1.5)
[node name="Tavern" type="Node3D"]
script = ExtResource("1")
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.951, 0.309, 0, -0.309, 0.951, 0, 3, 6)
fov = 70.0
current = true
[node name="Taproom" parent="." instance=ExtResource("2")]
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_1")
[node name="GroundFloor" type="Node3D" parent="."]
[node name="OmniLight3D" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0)
light_color = Color(0.91, 0.643, 0.29, 1)
light_energy = 1.2
omni_range = 10.0
[node name="KitchenBack" type="StaticBody3D" parent="GroundFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4, 1.5, -5.9)
[node name="Floor" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0)
mesh = SubResource("BoxMesh_floor")
surface_material_override/0 = SubResource("Material_1")
[node name="MeshInstance3D" type="MeshInstance3D" parent="GroundFloor/KitchenBack"]
mesh = SubResource("BoxMesh_kitchen_back")
surface_material_override/0 = SubResource("Material_wood")
[node name="Ceiling" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.05, 0)
mesh = SubResource("BoxMesh_ceiling")
surface_material_override/0 = SubResource("Material_1")
[node name="CollisionShape3D" type="CollisionShape3D" parent="GroundFloor/KitchenBack"]
shape = SubResource("BoxShape3D_kitchen_back")
[node name="WallBack" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, -4)
mesh = SubResource("BoxMesh_back")
surface_material_override/0 = SubResource("Material_1")
[node name="KitchenLeft" type="StaticBody3D" parent="GroundFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.05, 1.5, -4.85)
[node name="WallLeft" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 2, 0)
mesh = SubResource("BoxMesh_left")
surface_material_override/0 = SubResource("Material_1")
[node name="MeshInstance3D" type="MeshInstance3D" parent="GroundFloor/KitchenLeft"]
mesh = SubResource("BoxMesh_kitchen_side")
surface_material_override/0 = SubResource("Material_wood")
[node name="WallRight" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 2, 0)
mesh = SubResource("BoxMesh_right")
surface_material_override/0 = SubResource("Material_1")
[node name="CollisionShape3D" type="CollisionShape3D" parent="GroundFloor/KitchenLeft"]
shape = SubResource("BoxShape3D_kitchen_side")
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="KitchenRight" type="StaticBody3D" parent="GroundFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.95, 1.5, -4.85)
[node name="JoinPanel" type="VBoxContainer" parent="CanvasLayer"]
offset_left = 300.0
offset_top = 200.0
offset_right = 700.0
offset_bottom = 500.0
[node name="MeshInstance3D" type="MeshInstance3D" parent="GroundFloor/KitchenRight"]
mesh = SubResource("BoxMesh_kitchen_side")
surface_material_override/0 = SubResource("Material_wood")
[node name="Title" type="Label" parent="CanvasLayer/JoinPanel"]
text = "Ruf der Pilze"
[node name="CollisionShape3D" type="CollisionShape3D" parent="GroundFloor/KitchenRight"]
shape = SubResource("BoxShape3D_kitchen_side")
[node name="NameInput" type="LineEdit" parent="CanvasLayer/JoinPanel"]
placeholder_text = "Dein Name"
[node name="PrivateRoomDoor" type="StaticBody3D" parent="GroundFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 2, 2)
[node name="RoleOption" type="OptionButton" parent="CanvasLayer/JoinPanel"]
[node name="MeshInstance3D" type="MeshInstance3D" parent="GroundFloor/PrivateRoomDoor"]
mesh = SubResource("BoxMesh_door")
surface_material_override/0 = SubResource("Material_wood")
[node name="JoinButton" type="Button" parent="CanvasLayer/JoinPanel"]
text = "Beitreten"
[node name="CollisionShape3D" type="CollisionShape3D" parent="GroundFloor/PrivateRoomDoor"]
shape = SubResource("BoxShape3D_door")
[node name="WaitPanel" type="VBoxContainer" parent="CanvasLayer"]
visible = false
offset_left = 300.0
offset_top = 200.0
offset_right = 700.0
offset_bottom = 500.0
[node name="ExitDoor" type="StaticBody3D" parent="GroundFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 2, 4)
[node name="WaitLabel" type="Label" parent="CanvasLayer/WaitPanel"]
text = "Warte auf Spieler..."
[node name="MeshInstance3D" type="MeshInstance3D" parent="GroundFloor/ExitDoor"]
mesh = SubResource("BoxMesh_door")
surface_material_override/0 = SubResource("Material_wood")
[node name="PlayerList" type="ItemList" parent="CanvasLayer/WaitPanel"]
custom_minimum_size = Vector2(0, 150)
[node name="CollisionShape3D" type="CollisionShape3D" parent="GroundFloor/ExitDoor"]
shape = SubResource("BoxShape3D_door")
[node name="StartButton" type="Button" parent="CanvasLayer/WaitPanel"]
text = "Spiel starten"
visible = false
[node name="MyzelTrailGround" type="Node3D" parent="GroundFloor"]
[node name="TrailSegment1" type="MeshInstance3D" parent="GroundFloor/MyzelTrailGround"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.06, 1.5)
mesh = SubResource("BoxMesh_trail_long")
surface_material_override/0 = SubResource("Material_myzel")
[node name="TrailSegment2" type="MeshInstance3D" parent="GroundFloor/MyzelTrailGround"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0.06, 0)
mesh = SubResource("BoxMesh_trail_medium")
surface_material_override/0 = SubResource("Material_myzel")
[node name="Stairs" type="StaticBody3D" parent="GroundFloor"]
transform = Transform3D(1, 0, 0, 0, 0.906, -0.423, 0, 0.423, 0.906, 0, 1, 3)
[node name="MeshInstance3D" type="MeshInstance3D" parent="GroundFloor/Stairs"]
mesh = SubResource("BoxMesh_stairs")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="GroundFloor/Stairs"]
shape = SubResource("BoxShape3D_stairs")
[node name="UpperFloor" type="Node3D" parent="."]
[node name="Floor" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 4, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Floor"]
mesh = SubResource("BoxMesh_upper_floor")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Floor"]
shape = SubResource("BoxShape3D_upper_floor")
[node name="Ceiling" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 7, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Ceiling"]
mesh = SubResource("BoxMesh_upper_floor")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Ceiling"]
shape = SubResource("BoxShape3D_upper_floor")
[node name="WallBack" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 5.5, -4)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/WallBack"]
mesh = SubResource("BoxMesh_upper_wall_long")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/WallBack"]
shape = SubResource("BoxShape3D_upper_wall_long")
[node name="WallFront" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 5.5, 4)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/WallFront"]
mesh = SubResource("BoxMesh_upper_wall_long")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/WallFront"]
shape = SubResource("BoxShape3D_upper_wall_long")
[node name="WallLeft" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 5.5, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/WallLeft"]
mesh = SubResource("BoxMesh_upper_wall_short")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/WallLeft"]
shape = SubResource("BoxShape3D_upper_wall_short")
[node name="WallRight" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 5.5, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/WallRight"]
mesh = SubResource("BoxMesh_upper_wall_short")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/WallRight"]
shape = SubResource("BoxShape3D_upper_wall_short")
[node name="CorridorWall" type="StaticBody3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 5.5, 2)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/CorridorWall"]
mesh = SubResource("BoxMesh_upper_wall_long")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/CorridorWall"]
shape = SubResource("BoxShape3D_upper_wall_long")
[node name="Room1" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room1"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room1/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room1/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room1"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room2" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room2"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room2/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room2/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room2"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room3" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room3"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room3/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room3/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room3"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room4" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room4"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room4/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room4/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room4"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room5" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room5"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room5/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room5/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room5"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room6" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room6"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room6/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room6/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room6"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room7" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room7"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room7/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room7/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room7"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room8" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room8"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room8/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room8/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room8"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="Room9" type="Node3D" parent="UpperFloor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 11, 4.1, 2.8)
[node name="Bed" type="StaticBody3D" parent="UpperFloor/Room9"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, -2.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room9/Bed"]
mesh = SubResource("BoxMesh_bed")
surface_material_override/0 = SubResource("Material_myzel")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room9/Bed"]
shape = SubResource("BoxShape3D_bed")
[node name="SpawnPoint" type="Marker3D" parent="UpperFloor/Room9"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0, -1)
[node name="OpenDoor" type="StaticBody3D" parent="UpperFloor/Room9"]
transform = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0.6, 1.25, 0.2)
[node name="MeshInstance3D" type="MeshInstance3D" parent="UpperFloor/Room9/OpenDoor"]
mesh = SubResource("BoxMesh_anna_door")
surface_material_override/0 = SubResource("Material_wood")
[node name="CollisionShape3D" type="CollisionShape3D" parent="UpperFloor/Room9/OpenDoor"]
shape = SubResource("BoxShape3D_anna_door")
[node name="MyzelDecor" type="Node3D" parent="UpperFloor/Room9"]
[node name="MyzelPatch1" type="MeshInstance3D" parent="UpperFloor/Room9/MyzelDecor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.3, 0.01, -2)
mesh = SubResource("BoxMesh_myzel_patch")
surface_material_override/0 = SubResource("Material_myzel")
[node name="MyzelPatch2" type="MeshInstance3D" parent="UpperFloor/Room9/MyzelDecor"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 0.8, -3.8)
mesh = SubResource("BoxMesh_myzel_patch")
surface_material_override/0 = SubResource("Material_myzel")
[node name="MyzelTrailAnna" type="Node3D" parent="UpperFloor/Room9"]
[node name="TrailOut" type="MeshInstance3D" parent="UpperFloor/Room9/MyzelTrailAnna"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.06, 1.2)
mesh = SubResource("BoxMesh_myzel_trail_anna")
surface_material_override/0 = SubResource("Material_myzel")

View File

@@ -0,0 +1,50 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/tavern_lobby.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/taproom.tscn" id="2"]
[node name="TavernLobby" type="Node3D"]
script = ExtResource("1")
[node name="Taproom" parent="." instance=ExtResource("2")]
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.951, 0.309, 0, -0.309, 0.951, 0, 3, 6)
fov = 70.0
current = true
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="JoinPanel" type="VBoxContainer" parent="CanvasLayer"]
offset_left = 300.0
offset_top = 200.0
offset_right = 700.0
offset_bottom = 500.0
[node name="Title" type="Label" parent="CanvasLayer/JoinPanel"]
text = "Ruf der Pilze"
[node name="NameInput" type="LineEdit" parent="CanvasLayer/JoinPanel"]
placeholder_text = "Dein Name"
[node name="RoleOption" type="OptionButton" parent="CanvasLayer/JoinPanel"]
[node name="JoinButton" type="Button" parent="CanvasLayer/JoinPanel"]
text = "Beitreten"
[node name="WaitPanel" type="VBoxContainer" parent="CanvasLayer"]
visible = false
offset_left = 300.0
offset_top = 200.0
offset_right = 700.0
offset_bottom = 500.0
[node name="WaitLabel" type="Label" parent="CanvasLayer/WaitPanel"]
text = "Warte auf Spieler..."
[node name="PlayerList" type="ItemList" parent="CanvasLayer/WaitPanel"]
custom_minimum_size = Vector2(0, 150)
[node name="StartButton" type="Button" parent="CanvasLayer/WaitPanel"]
text = "Spiel starten"
visible = false

View File

@@ -1,216 +1,5 @@
extends Node
const TAVERN_SCENE := "res://scenes/tavern.tscn"
const CAM_EG_Y := 25.0
const CAM_OG_Y := 30.0
const OVERLAY_CYCLE := ["default", "spore_active"]
var _top_down_cam: Camera3D
var _player_markers: Node3D
var _markers_by_id: Dictionary = {} # peer_id → MeshInstance3D
var _player_cam_cams: Dictionary = {} # peer_id → Camera3D (in SubViewport)
var _showing_upper_floor: bool = false
func _ready() -> void:
$RootLayout.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_load_tavern_into_viewport()
_setup_floor_buttons()
_setup_player_cams()
_setup_overlay_panel()
func _load_tavern_into_viewport() -> void:
var packed := ResourceLoader.load(TAVERN_SCENE, "PackedScene") as PackedScene
if packed == null:
push_error("[DmView] tavern.tscn nicht gefunden — wird nach Schritt 5 verfügbar sein")
return
var map_root := $RootLayout/TopSection/MapContainer/MapViewport/MapRoot as Node3D
_top_down_cam = map_root.get_node("TopDownCam") as Camera3D
_player_markers = map_root.get_node("PlayerMarkers") as Node3D
var tavern := packed.instantiate()
tavern.name = "Tavern"
map_root.add_child(tavern)
print("[DmView] Tavern in SubViewport geladen")
func _setup_floor_buttons() -> void:
var btn_eg := $RootLayout/TopSection/SidePanel/FloorButtons/BtnEG as Button
var btn_og := $RootLayout/TopSection/SidePanel/FloorButtons/BtnOG as Button
btn_eg.pressed.connect(func() -> void: _switch_floor(false))
btn_og.pressed.connect(func() -> void: _switch_floor(true))
func _switch_floor(upper: bool) -> void:
_showing_upper_floor = upper
if _top_down_cam == null:
return
_top_down_cam.global_position.y = CAM_OG_Y if upper else CAM_EG_Y
print("[DmView] Etage gewechselt → %s" % ("OG" if upper else "EG"))
# --- Player Cam Feeds (Task 7) ---
func _setup_player_cams() -> void:
NetworkManager.player_joined.connect(_on_player_joined_cam)
NetworkManager.player_left.connect(_on_player_left_cam)
for peer_id in NetworkManager.players.keys():
var info: Dictionary = NetworkManager.players[peer_id]
if info.get("role", "") == "player":
_create_player_cam(peer_id, info.get("name", "???"))
func _on_player_joined_cam(peer_id: int, player_name: String, role: String) -> void:
if role == "player":
_create_player_cam(peer_id, player_name)
func _on_player_left_cam(peer_id: int) -> void:
var row := $RootLayout/PlayerCamsRow
var panel := row.get_node_or_null("CamPanel_%d" % peer_id)
if panel != null:
panel.queue_free()
_player_cam_cams.erase(peer_id)
func _create_player_cam(peer_id: int, player_name: String) -> void:
var packed := ResourceLoader.load(TAVERN_SCENE, "PackedScene") as PackedScene
if packed == null:
push_error("[DmView] tavern.tscn nicht gefunden für Player-Cam")
return
var viewport := SubViewport.new()
viewport.size = Vector2i(320, 180)
var scene_root := Node3D.new()
var tavern := packed.instantiate()
scene_root.add_child(tavern)
var cam := Camera3D.new()
cam.name = "PlayerCam"
cam.current = true
scene_root.add_child(cam)
viewport.add_child(scene_root)
var container := SubViewportContainer.new()
container.name = "CamPanel_%d" % peer_id
container.custom_minimum_size = Vector2(320, 180)
container.stretch = true
container.add_child(viewport)
var wrapper := VBoxContainer.new()
var label := Label.new()
label.text = player_name
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
wrapper.add_child(label)
wrapper.add_child(container)
$RootLayout/PlayerCamsRow.add_child(wrapper)
_player_cam_cams[peer_id] = cam
print("[DmView] Player-Cam erstellt für %s (Peer %d)" % [player_name, peer_id])
# --- Overlay Panel (Task 8) ---
func _setup_overlay_panel() -> void:
NetworkManager.player_joined.connect(_on_player_joined)
NetworkManager.player_left.connect(_on_player_left)
for peer_id in NetworkManager.players.keys():
var info: Dictionary = NetworkManager.players[peer_id]
if info.get("role", "") == "player":
_add_overlay_row(peer_id, info.get("name", "???"))
func _on_player_joined(peer_id: int, player_name: String, role: String) -> void:
if role == "player":
_add_overlay_row(peer_id, player_name)
func _on_player_left(peer_id: int) -> void:
var list := $RootLayout/TopSection/SidePanel/OverlayScroll/PlayerList as VBoxContainer
var row := list.get_node_or_null("Row_%d" % peer_id)
if row != null:
row.queue_free()
func _add_overlay_row(peer_id: int, player_name: String) -> void:
var list := $RootLayout/TopSection/SidePanel/OverlayScroll/PlayerList as VBoxContainer
var row := HBoxContainer.new()
row.name = "Row_%d" % peer_id
var label := Label.new()
label.text = player_name
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
var btn := Button.new()
btn.text = "● default"
btn.pressed.connect(func() -> void: _toggle_overlay(peer_id, btn))
row.add_child(label)
row.add_child(btn)
list.add_child(row)
func _toggle_overlay(peer_id: int, btn: Button) -> void:
var current := GameState.get_overlay(peer_id)
var idx := OVERLAY_CYCLE.find(current)
var next: String = OVERLAY_CYCLE[(idx + 1) % OVERLAY_CYCLE.size()]
NetworkManager.request_set_overlay.rpc_id(1, peer_id, next)
GameState.set_overlay_local(peer_id, next)
btn.text = "%s" % next
print("[DmView] Overlay-Toggle → Spieler %d: %s" % [peer_id, next])
# --- _process: Marker + Cam updates ---
func _process(_delta: float) -> void:
_update_player_markers()
_update_player_cams()
func _update_player_markers() -> void:
if _player_markers == null:
return
for peer_id in GameState.player_positions.keys():
var data: Dictionary = GameState.player_positions[peer_id]
var pos: Vector3 = data.get("position", Vector3.ZERO)
var rot: Vector3 = data.get("rotation", Vector3.ZERO)
var marker := _get_or_create_marker(peer_id)
marker.global_position = pos + Vector3(0, 0.2, 0)
marker.rotation.y = rot.y
var is_upper := pos.y > 3.5
marker.visible = (is_upper == _showing_upper_floor)
func _get_or_create_marker(peer_id: int) -> MeshInstance3D:
if peer_id in _markers_by_id:
return _markers_by_id[peer_id]
var mesh := MeshInstance3D.new()
var sphere := SphereMesh.new()
sphere.radius = 0.25
sphere.height = 0.5
mesh.mesh = sphere
var mat := StandardMaterial3D.new()
mat.albedo_color = _peer_color(peer_id)
mat.emission_enabled = true
mat.emission = mat.albedo_color
mat.emission_energy_multiplier = 0.5
mesh.material_override = mat
_player_markers.add_child(mesh)
_markers_by_id[peer_id] = mesh
print("[DmView] Marker erstellt für Peer %d" % peer_id)
return mesh
func _peer_color(peer_id: int) -> Color:
var colors := [Color.RED, Color.CYAN, Color.YELLOW, Color.MAGENTA, Color.GREEN]
return colors[peer_id % colors.size()]
func _update_player_cams() -> void:
for peer_id in _player_cam_cams.keys():
if not GameState.player_positions.has(peer_id):
continue
var data: Dictionary = GameState.player_positions[peer_id]
var pos: Vector3 = data.get("position", Vector3.ZERO)
var rot: Vector3 = data.get("rotation", Vector3.ZERO)
var cam: Camera3D = _player_cam_cams[peer_id]
cam.global_position = pos
cam.global_rotation = rot
print("[DmView] DM-Szene geladen (Stub)")

View File

@@ -118,14 +118,27 @@ func request_start_game() -> void:
var requester_id := multiplayer.get_remote_sender_id()
if players.get(requester_id, {}).get("role", "") != "dm":
return
start_game.rpc()
var assignments := _build_room_assignments()
start_game.rpc(assignments)
@rpc("authority", "call_local", "reliable")
func start_game() -> void:
func start_game(room_assignments: Dictionary) -> void:
if my_id in room_assignments:
SceneManager.pending_room_index = room_assignments[my_id]
game_started.emit()
func _build_room_assignments() -> Dictionary:
var result := {}
var player_peers: Array = players.keys().filter(
func(id: int) -> bool: return players[id].role == "player"
)
for i in player_peers.size():
result[player_peers[i]] = i % 8 # 8 Gästezimmer (Index 07)
return result
# call_remote: Server hat lokal bereits gelöscht; nur Clients führen aus
@rpc("authority", "call_remote", "reliable")
func _broadcast_player_left(peer_id: int) -> void:

View File

@@ -1,24 +1,23 @@
extends Node
const SCENES := {
"tavern": "res://scenes/tavern.tscn",
"chamber": "res://scenes/chamber.tscn",
"lobby": "res://scenes/tavern_lobby.tscn",
"tavern": "res://scenes/tavern.tscn",
"dm_view": "res://scenes/dm_view.tscn",
"entrance_hall": "res://scenes/entrance_hall.tscn",
"refectory": "res://scenes/refectory.tscn",
"library": "res://scenes/library.tscn",
"chapel": "res://scenes/chapel.tscn",
"cloister": "res://scenes/cloister.tscn",
"sanctum": "res://scenes/sanctum.tscn",
"refectory": "res://scenes/refectory.tscn",
"library": "res://scenes/library.tscn",
"chapel": "res://scenes/chapel.tscn",
"cloister": "res://scenes/cloister.tscn",
"sanctum": "res://scenes/sanctum.tscn",
}
var pending_room_index: int = 0
var _current_scene_node: Node = null
func _ready() -> void:
NetworkManager.game_started.connect(_on_game_started)
# Defer until main.tscn and its CurrentScene node are in the tree.
# call_deferred is not sufficient here — autoloads init before the main
# scene. Connect to root.ready (fires once, after main scene is ready).
get_tree().root.ready.connect(_on_root_ready, CONNECT_ONE_SHOT)
@@ -26,7 +25,7 @@ func _on_root_ready() -> void:
var args := OS.get_cmdline_args() + OS.get_cmdline_user_args()
var is_server := OS.has_feature("dedicated_server") or "--server" in args
if not is_server:
transition_to("tavern")
transition_to("lobby")
func transition_to(scene_name: String) -> void:
@@ -51,13 +50,8 @@ 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
# TODO next feature: call _load_for_role("chamber") here once role routing is implemented
print("[SceneManager] Spiel gestartet — chamber transition kommt im nächsten Feature")
func _load_for_role(scene_name: String) -> void:
# my_id is only valid after NetworkManager._on_connected_to_server fires.
# Do not call before game_started — my_id will be 0 before that.
var role: String = NetworkManager.players.get(NetworkManager.my_id, {}).get("role", "player")
# TODO next feature: load dm variant when role == "dm"
print("[SceneManager] Role %s%s (stub, not switching yet)" % [role, scene_name])
if role == "dm":
transition_to("dm_view")
else:
transition_to("tavern")

View File

@@ -1,68 +1,29 @@
extends Node3D
var _local_role: String = ""
var _pending_player_name: String = ""
const ROOM_COUNT := 8
func _ready() -> void:
$CanvasLayer/JoinPanel/RoleOption.add_item("Spieler")
$CanvasLayer/JoinPanel/RoleOption.add_item("DM")
$CanvasLayer/JoinPanel/JoinButton.pressed.connect(_on_join_pressed)
$CanvasLayer/WaitPanel/StartButton.pressed.connect(_on_start_pressed)
NetworkManager.connected_to_server.connect(_on_connected)
NetworkManager.connection_failed.connect(_on_connection_failed)
NetworkManager.player_joined.connect(_on_player_joined)
NetworkManager.player_left.connect(_on_player_left)
NetworkManager.player_list_synced.connect(_rebuild_player_list)
func _on_join_pressed() -> void:
var player_name: String = ($CanvasLayer/JoinPanel/NameInput as LineEdit).text.strip_edges()
if player_name.is_empty():
var args := OS.get_cmdline_args() + OS.get_cmdline_user_args()
if OS.has_feature("dedicated_server") or "--server" in args:
return
_local_role = "dm" if $CanvasLayer/JoinPanel/RoleOption.selected == 1 else "player"
_pending_player_name = player_name
NetworkManager.join_server("127.0.0.1", 4242)
var room_index := SceneManager.pending_room_index
_spawn_player(room_index)
func _on_connected() -> void:
NetworkManager.register.rpc_id(1, _pending_player_name, _local_role)
$CanvasLayer/JoinPanel.visible = false
$CanvasLayer/WaitPanel.visible = true
$CanvasLayer/WaitPanel/StartButton.visible = (_local_role == "dm")
func _exit_tree() -> void:
NetworkManager.connected_to_server.disconnect(_on_connected)
NetworkManager.connection_failed.disconnect(_on_connection_failed)
NetworkManager.player_joined.disconnect(_on_player_joined)
NetworkManager.player_left.disconnect(_on_player_left)
NetworkManager.player_list_synced.disconnect(_rebuild_player_list)
func _on_connection_failed() -> void:
push_error("[Tavern] Verbindung fehlgeschlagen")
_local_role = ""
_pending_player_name = ""
$CanvasLayer/JoinPanel.visible = true
$CanvasLayer/WaitPanel.visible = false
func _on_player_joined(_peer_id: int, _player_name: String, _role: String) -> void:
_rebuild_player_list()
func _on_player_left(_peer_id: int) -> void:
_rebuild_player_list()
func _rebuild_player_list() -> void:
$CanvasLayer/WaitPanel/PlayerList.clear()
for p in NetworkManager.players.values():
$CanvasLayer/WaitPanel/PlayerList.add_item("%s (%s)" % [p.name, p.role])
func _on_start_pressed() -> void:
NetworkManager.request_start_game.rpc_id(1)
func _spawn_player(room_index: int) -> void:
var room_num := (room_index % ROOM_COUNT) + 1 # Zimmer heißen Room1..Room8
var spawn_path := "UpperFloor/Room%d/SpawnPoint" % room_num
var spawn := get_node_or_null(spawn_path) as Marker3D
if spawn == null:
push_error("[Tavern] SpawnPoint nicht gefunden: %s" % spawn_path)
return
var controller := Node3D.new()
controller.name = "PlayerController"
var camera := Camera3D.new()
camera.name = "Camera3D"
camera.current = true
controller.add_child(camera)
add_child(controller)
controller.global_transform = spawn.global_transform
print("[Tavern] Spieler gespawnt in %s" % spawn_path)

View File

@@ -0,0 +1,68 @@
extends Node3D
var _local_role: String = ""
var _pending_player_name: String = ""
func _ready() -> void:
$CanvasLayer/JoinPanel/RoleOption.add_item("Spieler")
$CanvasLayer/JoinPanel/RoleOption.add_item("DM")
$CanvasLayer/JoinPanel/JoinButton.pressed.connect(_on_join_pressed)
$CanvasLayer/WaitPanel/StartButton.pressed.connect(_on_start_pressed)
NetworkManager.connected_to_server.connect(_on_connected)
NetworkManager.connection_failed.connect(_on_connection_failed)
NetworkManager.player_joined.connect(_on_player_joined)
NetworkManager.player_left.connect(_on_player_left)
NetworkManager.player_list_synced.connect(_rebuild_player_list)
func _on_join_pressed() -> void:
var player_name: String = ($CanvasLayer/JoinPanel/NameInput as LineEdit).text.strip_edges()
if player_name.is_empty():
return
_local_role = "dm" if $CanvasLayer/JoinPanel/RoleOption.selected == 1 else "player"
_pending_player_name = player_name
NetworkManager.join_server("127.0.0.1", 4242)
func _on_connected() -> void:
NetworkManager.register.rpc_id(1, _pending_player_name, _local_role)
$CanvasLayer/JoinPanel.visible = false
$CanvasLayer/WaitPanel.visible = true
$CanvasLayer/WaitPanel/StartButton.visible = (_local_role == "dm")
func _exit_tree() -> void:
NetworkManager.connected_to_server.disconnect(_on_connected)
NetworkManager.connection_failed.disconnect(_on_connection_failed)
NetworkManager.player_joined.disconnect(_on_player_joined)
NetworkManager.player_left.disconnect(_on_player_left)
NetworkManager.player_list_synced.disconnect(_rebuild_player_list)
func _on_connection_failed() -> void:
push_error("[Tavern] Verbindung fehlgeschlagen")
_local_role = ""
_pending_player_name = ""
$CanvasLayer/JoinPanel.visible = true
$CanvasLayer/WaitPanel.visible = false
func _on_player_joined(_peer_id: int, _player_name: String, _role: String) -> void:
_rebuild_player_list()
func _on_player_left(_peer_id: int) -> void:
_rebuild_player_list()
func _rebuild_player_list() -> void:
$CanvasLayer/WaitPanel/PlayerList.clear()
for p in NetworkManager.players.values():
$CanvasLayer/WaitPanel/PlayerList.add_item("%s (%s)" % [p.name, p.role])
func _on_start_pressed() -> void:
NetworkManager.request_start_game.rpc_id(1)