diff --git a/docs/plans/2026-04-14-parallel-impl-all.md b/docs/plans/2026-04-14-parallel-impl-all.md new file mode 100644 index 0000000..a18eed9 --- /dev/null +++ b/docs/plans/2026-04-14-parallel-impl-all.md @@ -0,0 +1,200 @@ +# Parallel Agent Implementation — Schritt 7–11 + +> **For agentic workers:** Use superpowers:subagent-driven-development to execute this plan task-by-task. + +**Goal:** Implement Schritt 7–11 using parallel agents where file overlap allows, minimizing wall-clock time. + +--- + +## Parallelism Analysis + +| Schritt | Dateien (schreibend) | +|---------|---------------------| +| 7 — Lighting | `taproom.tscn`, `tavern.tscn` | +| 8 — Dice Roller | `network_manager.gd`, `tavern.gd`, `dm_view.tscn`, `dm_view.gd` | +| 9 — Player Interaction | `project.godot`, `player_controller.gd`★, `interactable.gd`★, `test_interactable.gd`★, `tavern.gd`, `tavern.tscn` | +| 10 — Spore Overlay | `spore_overlay.gdshader`★, `network_manager.gd`, `tavern.gd` | +| 11 — Sporennebel Slider | `spore_level.gd`★, `project.godot`, `network_manager.gd`, `dm_view.tscn`, `dm_view.gd`, `tavern.gd` | + +★ = neue Datei, kein Merge-Conflict möglich + +**Conflict-Matrix:** + +| | 7 | 8 | 9 | 10 | 11 | +|---|---|---|---|---|---| +| 7 | — | ✅ | ⚠️ tscn | ✅ | ✅ | +| 8 | ✅ | — | ⚠️ tavern.gd | ⚠️ nm.gd+tavern | ⚠️ nm.gd+dm_view+tavern | +| 9 | ⚠️ tscn | ⚠️ tavern.gd | — | ⚠️ tavern.gd | ⚠️ tavern.gd+proj | +| 10 | ✅ | ⚠️ | ⚠️ | — | ⚠️ nm.gd+tavern | +| 11 | ✅ | ⚠️ | ⚠️ | ⚠️ | — | + +**Fazit:** Nur 7+8 sind konfliktfrei parallel. Alle anderen haben Überschneidungen in tavern.gd oder network_manager.gd → sequential nach Merge. + +--- + +## Ausführungs-Reihenfolge + +``` +Phase 1 (parallel): 7 + 8 +Phase 2 (merge): merge 7 + 8 → main +Phase 3 (sequenz.): 9 +Phase 4 (merge): merge 9 → main +Phase 5 (sequenz.): 10 +Phase 6 (merge): merge 10 → main +Phase 7 (sequenz.): 11 +Phase 8 (merge): merge 11 → main +``` + +--- + +## Phase 1: Agents 7 + 8 parallel dispatchen + +Beide Agents **gleichzeitig** starten (single message, two Agent tool calls). + +### Agent A — Tavern Lighting (`feat/tavern-lighting`) + +**Prompt:** +> Implement the plan at `docs/plans/2026-04-14-tavern-lighting.md` in the Godot 4 project at `ruf-der-pilze/`. Work on branch `feat/tavern-lighting` (create from current `main`). Use superpowers:executing-plans. +> +> Do NOT update STATUS.md — that happens after merge. Push the branch when done and report which commits were created. + +### Agent B — Dice Roller (`feat/dice-roller`) + +**Prompt:** +> Implement the plan at `docs/plans/2026-04-14-dice-roller.md` in the Godot 4 project at `ruf-der-pilze/`. Work on branch `feat/dice-roller` (create from current `main`). Use superpowers:executing-plans. +> +> Do NOT update STATUS.md — that happens after merge. Push the branch when done and report which commits were created. + +--- + +## Phase 2: Review + Merge 7 + 8 + +- [ ] **Agent A output reviewen** — Godot öffnen, Screenshot der beleuchteten Taverne +- [ ] **Agent B output reviewen** — Würfelwurf mit zwei Clients testen, DM DC-Anzeige prüfen +- [ ] **Lighting branch mergen** + ```bash + git checkout main + git merge feat/tavern-lighting --no-ff -m "feat: merge Schritt 7 — tavern lighting" + ``` +- [ ] **Dice branch mergen** + ```bash + git merge feat/dice-roller --no-ff -m "feat: merge Schritt 8 — dice roller" + ``` +- [ ] **STATUS.md updaten** + ```bash + git add docs/STATUS.md ruf-der-pilze/CLAUDE.md + git commit -m "docs: mark Schritt 7 + 8 complete" + ``` + +--- + +## Phase 3: Agent C — Player Interaction (`feat/player-interaction`) + +**Prompt:** +> Implement the plan at `docs/plans/2026-04-14-player-interaction.md` in the Godot 4 project at `ruf-der-pilze/`. Work on branch `feat/player-interaction` (create from current `main`). +> +> Use superpowers:executing-plans. +> +> **Critical context (already merged into main):** +> - `tavern.gd._ready()` endet jetzt mit `_setup_dice_ui()` — das ist der letzte Aufruf (von Schritt 8). Wenn du `_spawn_player()` ersetzt (Task 4), berühre `_ready()` NICHT. +> - `tavern.tscn` hat neue Licht-Nodes von Schritt 7. Beim Hinzufügen des Test-Interactables (Task 5) nur neue Nodes ergänzen, keine vorhandenen Nodes anfassen. +> +> Do NOT update STATUS.md. Push when done. + +--- + +## Phase 4: Review + Merge 9 + +- [ ] **Agent C output reviewen** — WASD Bewegung, Maus-Look, E-Taste Interaktion testen +- [ ] **Interaction branch mergen** + ```bash + git checkout main + git merge feat/player-interaction --no-ff -m "feat: merge Schritt 9 — player interaction" + ``` +- [ ] **STATUS.md updaten** + ```bash + git add docs/STATUS.md ruf-der-pilze/CLAUDE.md + git commit -m "docs: mark Schritt 9 complete" + ``` + +--- + +## Phase 5: Agent D — Spore Overlay (`feat/spore-overlay`) + +**Prompt:** +> Implement the plan at `docs/plans/2026-04-14-spore-overlay.md` in the Godot 4 project at `ruf-der-pilze/`. Work on branch `feat/spore-overlay` (create from current `main`). +> +> Use superpowers:executing-plans. +> +> **Critical context (already in main):** +> - `tavern.gd._ready()` endet mit `_setup_dice_ui()` — nur APPEND, kein Rewrite. +> - `network_manager.gd` hat bereits: `broadcast_roll`, `_relay_roll` (Schritt 8), `sync_player_position` (Schritt 6). Füge `overlay_changed` Signal und die Änderung in `set_overlay` hinzu ohne andere Funktionen zu ändern. +> +> Do NOT update STATUS.md. Push when done. + +--- + +## Phase 6: Review + Merge 10 + +- [ ] **Agent D output reviewen** — DM togglet `spore_active`, Spieler sieht Shader-Effekt +- [ ] **Overlay branch mergen** + ```bash + git checkout main + git merge feat/spore-overlay --no-ff -m "feat: merge Schritt 10 — spore overlay shader" + ``` +- [ ] **STATUS.md updaten** + ```bash + git add docs/STATUS.md ruf-der-pilze/CLAUDE.md + git commit -m "docs: mark Schritt 10 complete" + ``` + +--- + +## Phase 7: Agent E — Sporennebel Slider (`feat/spore-slider`) + +**Prompt:** +> Implement the plan at `docs/plans/2026-04-14-spore-level-slider.md` in the Godot 4 project at `ruf-der-pilze/`. Work on branch `feat/spore-slider` (create from current `main`). +> +> Use superpowers:executing-plans. +> +> **Critical context (already in main from Schritt 10):** +> - `tavern.gd` hat bereits: `var _spore_mat: ShaderMaterial`, `_setup_spore_overlay()`, `_on_overlay_changed(overlay_name: String)`. +> - Task 5 dieses Plans ersetzt `_on_overlay_changed()` — das ist beabsichtigt und notwendig. +> - `network_manager.gd` hat bereits `overlay_changed` Signal (Schritt 10). Füge `request_spore_level` und `_relay_spore_level` am Ende hinzu ohne bestehende Funktionen zu ändern. +> - `dm_view.tscn` SidePanel hat bereits Würfelwurf-Sektion (Schritt 8). Sporennebel-Slider unterhalb davon ergänzen. +> +> Do NOT update STATUS.md. Push when done. + +--- + +## Phase 8: Review + Merge 11 + +- [ ] **Agent E output reviewen** — DM Slider bewegen → Spieler Shader-Intensität ändert sich live +- [ ] **Slider branch mergen** + ```bash + git checkout main + git merge feat/spore-slider --no-ff -m "feat: merge Schritt 11 — sporennebel slider" + ``` +- [ ] **STATUS.md updaten — alle Mechanics complete** + ```bash + git add docs/STATUS.md ruf-der-pilze/CLAUDE.md + git commit -m "docs: mark Schritt 11 complete — all mechanics done, next: Refectorium" + ``` + +--- + +## Zusammenfassung + +``` +main +├── [parallel] feat/tavern-lighting → Agent A +├── [parallel] feat/dice-roller → Agent B +│ [merge beide] +└── feat/player-interaction → Agent C + [merge] + feat/spore-overlay → Agent D + [merge] + feat/spore-slider → Agent E + [merge] + → Alle Mechanics abgeschlossen → weiter mit Refectorium +``` diff --git a/docs/plans/2026-04-14-spore-level-slider.md b/docs/plans/2026-04-14-spore-level-slider.md new file mode 100644 index 0000000..142af26 --- /dev/null +++ b/docs/plans/2026-04-14-spore-level-slider.md @@ -0,0 +1,256 @@ +# Sporennebel Slider Implementation Plan + +> **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:** DM can control spore density (0.0–1.0) via a slider in the Regiepult. The value is broadcast to all clients and scales the spore overlay shader intensity continuously (instead of binary on/off). + +**Architecture:** New `SporeLevel` Autoload holds the current density with a `level_changed` signal. Two new RPCs in NetworkManager sync the value from DM → server → all clients. A slider is added to `dm_view.tscn` SidePanel. In `tavern.gd`, the overlay intensity calculation is updated: intensity = `SporeLevel.current_level` when `spore_active`, 0.0 otherwise. This plan depends on Schritt 10 (spore overlay shader + `_spore_mat` variable must exist in `tavern.gd`). + +**Tech Stack:** Godot 4.x GDScript, MultiplayerAPI RPC, HSlider UI + +--- + +## Context + +After Schritt 10, `tavern.gd` has: +- `var _spore_mat: ShaderMaterial` — the overlay shader material +- `func _on_overlay_changed(overlay_name: String)` — sets intensity to 0.0 or 1.0 + +This plan: +1. Adds `SporeLevel` Autoload (replaces the hardcoded 1.0 with a dynamic level) +2. Modifies `_on_overlay_changed` to use `SporeLevel.current_level` +3. Connects `SporeLevel.level_changed` so intensity updates when slider moves + +**RPC pattern:** Same server-relay as dice roller — DM sends to server via `rpc_id(1)`, server validates DM role and relays to all. + +## Files + +| File | Action | +|------|--------| +| `scripts/spore_level.gd` | Create — SporeLevel Autoload | +| `project.godot` | Add SporeLevel to `[autoload]` section | +| `scripts/network_manager.gd` | Add `request_spore_level` + `_relay_spore_level` RPCs | +| `scenes/dm_view.tscn` | Add slider to SidePanel | +| `scripts/dm_view.gd` | Add `_setup_spore_slider()` | +| `scripts/tavern.gd` | Update `_on_overlay_changed()`, add `_on_spore_level_changed()` | + +--- + +## Task 1: Create SporeLevel Autoload + +**Files:** +- Create: `scripts/spore_level.gd` +- Modify: `project.godot` — add autoload entry + +- [ ] **Step 1: Create scripts/spore_level.gd** + + ```gdscript + extends Node + + signal level_changed(new_level: float) + + var current_level: float = 0.0: + set(value): + current_level = clampf(value, 0.0, 1.0) + level_changed.emit(current_level) + ``` + +- [ ] **Step 2: Add to project.godot autoload section** + + In the `[autoload]` section, add alongside the existing entries: + ```ini + SporeLevel="*res://scripts/spore_level.gd" + ``` + + Alternatively: use Godot editor Project Settings → Autoload → add `SporeLevel` pointing to `res://scripts/spore_level.gd`. + +- [ ] **Step 3: Open Godot, verify SporeLevel appears in Project Settings → Autoload** + +- [ ] **Step 4: Commit** + + ```bash + git add ruf-der-pilze/scripts/spore_level.gd ruf-der-pilze/project.godot + git commit -m "feat: add SporeLevel autoload — spore density with level_changed signal" + ``` + +--- + +## Task 2: Add spore level RPCs to NetworkManager + +**Files:** +- Modify: `scripts/network_manager.gd` — append two RPCs at end of file + +Same server-relay pattern as `broadcast_roll` / `_relay_roll` from Schritt 8. + +- [ ] **Step 1: Add two RPCs at end of network_manager.gd** + + ```gdscript + # DM calls this on server only via rpc_id(1, level) + @rpc("any_peer", "call_remote", "reliable") + func request_spore_level(level: float) -> void: + if not multiplayer.is_server(): return + var requester_id := multiplayer.get_remote_sender_id() + if players.get(requester_id, {}).get("role", "") != "dm": return + _relay_spore_level.rpc(level) + + # Server broadcasts to all clients including itself + @rpc("authority", "call_local", "reliable") + func _relay_spore_level(level: float) -> void: + SporeLevel.current_level = level + ``` + +- [ ] **Step 2: Open Godot, check for parse errors** + +- [ ] **Step 3: Commit** + + ```bash + git add ruf-der-pilze/scripts/network_manager.gd + git commit -m "feat: network_manager — add request_spore_level + _relay_spore_level RPCs" + ``` + +--- + +## Task 3: Add slider to dm_view.tscn + +**Files:** +- Modify: `scenes/dm_view.tscn` — add nodes to SidePanel + +In the SidePanel (`RootLayout/TopSection/SidePanel`), add below `OverlayScroll` (or below `SepDice` if the dice roller plan has already added nodes there): + +``` +HSeparator "SepSpore" +Label "LblSpore" — text = "Sporennebel" +HBoxContainer "SporeLevelRow" + HSlider "SporeSlider" + min_value = 0.0 + max_value = 1.0 + step = 0.05 + value = 0.0 + size_flags_horizontal = SIZE_EXPAND_FILL + Label "SporeValueLabel" — text = "0%" + custom_minimum_size = Vector2(35, 0) +``` + +- [ ] **Step 1: Add the nodes to dm_view.tscn via Godot editor** + + Open `dm_view.tscn`. Navigate to `RootLayout/TopSection/SidePanel`. Add the nodes listed above in order. + +- [ ] **Step 2: Verify layout in Godot editor** + + SidePanel should show: Overlays section → (dice section if Schritt 8 merged) → Sporennebel label → slider + percentage label. + +- [ ] **Step 3: Commit** + + ```bash + git add ruf-der-pilze/scenes/dm_view.tscn + git commit -m "feat: dm_view — add sporennebel slider to SidePanel" + ``` + +--- + +## Task 4: Wire slider in dm_view.gd + +**Files:** +- Modify: `scripts/dm_view.gd` — add `_setup_spore_slider()`, call in `_ready()` + +- [ ] **Step 1: Add _setup_spore_slider() to dm_view.gd** + + ```gdscript + func _setup_spore_slider() -> void: + var slider := $RootLayout/TopSection/SidePanel/SporeLevelRow/SporeSlider as HSlider + var value_label := $RootLayout/TopSection/SidePanel/SporeLevelRow/SporeValueLabel as Label + slider.value_changed.connect(func(val: float) -> void: + value_label.text = "%d%%" % roundi(val * 100.0) + NetworkManager.request_spore_level.rpc_id(1, val) + ) + ``` + +- [ ] **Step 2: Call _setup_spore_slider() in dm_view.gd _ready()** + + Append to end of `_ready()`: + ```gdscript + _setup_spore_slider() + ``` + +- [ ] **Step 3: Open Godot, check for parse errors** + +- [ ] **Step 4: Commit** + + ```bash + git add ruf-der-pilze/scripts/dm_view.gd + git commit -m "feat: dm_view — wire sporennebel slider to request_spore_level RPC" + ``` + +--- + +## Task 5: Update tavern.gd to use SporeLevel + +**Files:** +- Modify: `scripts/tavern.gd` — update `_on_overlay_changed()`, add `_on_spore_level_changed()`, connect in `_setup_spore_overlay()` + +**Prerequisite:** This task requires Schritt 10 to be merged — `_spore_mat`, `_setup_spore_overlay()`, and `_on_overlay_changed()` must already exist in `tavern.gd`. + +The intensity should now be: `SporeLevel.current_level` when overlay is `spore_active`, else `0.0`. Both the overlay toggle and the slider need to update the shader. + +- [ ] **Step 1: Replace _on_overlay_changed() in tavern.gd** + + Find the existing method: + ```gdscript + func _on_overlay_changed(overlay_name: String) -> void: + var intensity := 1.0 if overlay_name == "spore_active" else 0.0 + _spore_mat.set_shader_parameter("intensity", intensity) + ``` + + Replace with: + ```gdscript + func _on_overlay_changed(overlay_name: String) -> void: + _apply_spore_intensity() + + + func _on_spore_level_changed(_level: float) -> void: + _apply_spore_intensity() + + + func _apply_spore_intensity() -> void: + var overlay_name := GameState.get_overlay(NetworkManager.my_id) + var intensity := SporeLevel.current_level if overlay_name == "spore_active" else 0.0 + _spore_mat.set_shader_parameter("intensity", intensity) + ``` + +- [ ] **Step 2: Connect SporeLevel.level_changed in _setup_spore_overlay()** + + In `_setup_spore_overlay()`, after `NetworkManager.overlay_changed.connect(_on_overlay_changed)`, add: + ```gdscript + SporeLevel.level_changed.connect(_on_spore_level_changed) + ``` + +- [ ] **Step 3: Open Godot, check for parse errors** + +- [ ] **Step 4: Run end-to-end test** + + Start server + DM client + player client. + - DM: toggle player overlay to `spore_active` + - DM: move sporennebel slider from 0 → 1.0 + - Player: shader intensity should increase smoothly + - DM: slider back to 0.3 → player sees reduced effect + - DM: toggle overlay back to `default` → player effect disappears even at 0.3 level + +- [ ] **Step 5: Commit** + + ```bash + git add ruf-der-pilze/scripts/tavern.gd + git commit -m "feat: tavern — spore overlay intensity driven by SporeLevel (continuous slider control)" + ``` + +--- + +## Task 6: Update STATUS.md + +- [ ] **Step 1: Mark Schritt 11 ✅ in docs/STATUS.md and ruf-der-pilze/CLAUDE.md** + +- [ ] **Step 2: Commit** + + ```bash + git add docs/STATUS.md ruf-der-pilze/CLAUDE.md + git commit -m "docs: mark Sporennebel Slider complete" + ``` diff --git a/docs/plans/2026-04-14-spore-overlay.md b/docs/plans/2026-04-14-spore-overlay.md new file mode 100644 index 0000000..417ec4a --- /dev/null +++ b/docs/plans/2026-04-14-spore-overlay.md @@ -0,0 +1,213 @@ +# Spore Overlay Implementation Plan + +> **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:** When the DM toggles a player to `spore_active`, that player sees a fullscreen screen-space shader effect (green tint, vignette, subtle distortion). Toggling back to `default` removes it. + +**Architecture:** A new `shaders/spore_overlay.gdshader` (CanvasItem shader sampling the screen texture) is applied to a fullscreen ColorRect on a CanvasLayer. `NetworkManager` emits a new `overlay_changed` signal from the existing `set_overlay` RPC. `tavern.gd._setup_spore_overlay()` creates the overlay UI and connects to that signal. DM clients skip this (early return in `_ready()`). Intensity is binary (0.0 / 1.0) in this plan — Schritt 11 adds continuous slider control. + +**Tech Stack:** Godot 4.x GDScript, GLSL (Godot canvas_item shader), CanvasLayer, ShaderMaterial + +--- + +## Context + +Existing RPC chain (already implemented): +``` +DM: request_set_overlay.rpc_id(1, peer_id, "spore_active") + → Server validates DM role, calls set_overlay.rpc_id(peer_id, "spore_active") + → Player client: GameState.set_overlay_local(my_id, "spore_active") +``` + +`set_overlay` in `network_manager.gd` currently only calls `GameState.set_overlay_local` — no visual effect fires. This plan adds: +1. `signal overlay_changed(overlay_name: String)` to NetworkManager, emitted from `set_overlay` +2. The actual shader + CanvasLayer in `tavern.gd` + +The overlay intensity in this plan is binary: 1.0 when `spore_active`, 0.0 for `default`. Schritt 11 extends this to use `SporeLevel.current_level` as the multiplier. + +## Files + +| File | Action | +|------|--------| +| `shaders/spore_overlay.gdshader` | Create — screen-space spore effect shader | +| `scripts/network_manager.gd` | Add `overlay_changed` signal + emit in `set_overlay` | +| `scripts/tavern.gd` | Add `_setup_spore_overlay()`, call in `_ready()` | + +--- + +## Task 1: Create the spore overlay shader + +**Files:** +- Create: `shaders/spore_overlay.gdshader` + +The shader samples the screen texture and applies: green tint, edge vignette, subtle time-based distortion. The `intensity` uniform drives all effects (0.0 = invisible, 1.0 = full effect). + +- [ ] **Step 1: Create ruf-der-pilze/shaders/ directory and spore_overlay.gdshader** + + ```glsl + shader_type canvas_item; + + uniform float intensity : hint_range(0.0, 1.0) = 0.0; + uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; + + void fragment() { + vec2 uv = SCREEN_UV; + + // Subtle distortion — wavy lines, stronger at higher intensity + float distort_x = sin(uv.y * 35.0 + TIME * 1.8) * 0.003 * intensity; + float distort_y = cos(uv.x * 28.0 + TIME * 1.2) * 0.002 * intensity; + + // Sample screen with slight RGB split for chromatic aberration feel + float split = 0.003 * intensity; + vec4 r = texture(screen_texture, uv + vec2(distort_x + split, distort_y)); + vec4 g = texture(screen_texture, uv + vec2(distort_x, distort_y)); + vec4 b = texture(screen_texture, uv + vec2(distort_x - split, distort_y)); + vec4 screen = vec4(r.r, g.g, b.b, 1.0); + + // Vignette — darken edges + vec2 center = uv - 0.5; + float vignette = 1.0 - smoothstep(0.25, 0.75, length(center) * 1.6); + screen.rgb *= mix(1.0, vignette, intensity * 0.6); + + // Green spore tint + vec3 spore_tint = vec3(0.05, 0.45, 0.12); + screen.rgb = mix(screen.rgb, screen.rgb * spore_tint * 2.2, intensity * 0.35); + + // Slight brightness pulse + float pulse = 1.0 + sin(TIME * 1.5) * 0.04 * intensity; + screen.rgb *= pulse; + + COLOR = screen; + } + ``` + + The shader uses `hint_screen_texture` to capture the rendered scene. The ColorRect must cover the full screen and use this shader — Godot automatically provides the screen texture. + +- [ ] **Step 2: Open Godot, verify shader parses without errors** + + Open Project → any scene → check Output for shader compilation errors. + +- [ ] **Step 3: Commit** + + ```bash + git add ruf-der-pilze/shaders/spore_overlay.gdshader + git commit -m "feat: add spore_overlay.gdshader — screen-space tint, vignette, distortion" + ``` + +--- + +## Task 2: Add overlay_changed signal to NetworkManager + +**Files:** +- Modify: `scripts/network_manager.gd` — add signal declaration + emit in `set_overlay` + +- [ ] **Step 1: Add signal declaration near top of network_manager.gd** + + Add alongside the other signal declarations: + ```gdscript + signal overlay_changed(overlay_name: String) + ``` + +- [ ] **Step 2: Emit the signal in set_overlay RPC** + + Find the `set_overlay` function (currently at end of file): + ```gdscript + @rpc("authority", "call_remote", "reliable") + func set_overlay(overlay_name: String) -> void: + GameState.set_overlay_local(my_id, overlay_name) + ``` + + Add one line: + ```gdscript + @rpc("authority", "call_remote", "reliable") + func set_overlay(overlay_name: String) -> void: + GameState.set_overlay_local(my_id, overlay_name) + overlay_changed.emit(overlay_name) + ``` + +- [ ] **Step 3: Open Godot, check for parse errors** + +- [ ] **Step 4: Commit** + + ```bash + git add ruf-der-pilze/scripts/network_manager.gd + git commit -m "feat: network_manager — emit overlay_changed signal from set_overlay RPC" + ``` + +--- + +## Task 3: Add spore overlay CanvasLayer to tavern.gd + +**Files:** +- Modify: `scripts/tavern.gd` — add member variable, `_setup_spore_overlay()`, `_on_overlay_changed()` + +The DM client already returns early in `_ready()` before reaching the player setup block, so no DM guard is needed here. + +- [ ] **Step 1: Add member variable to tavern.gd** + + Near the top of the script (alongside other member variables): + ```gdscript + var _spore_mat: ShaderMaterial + ``` + +- [ ] **Step 2: Add _setup_spore_overlay() and _on_overlay_changed() to tavern.gd** + + ```gdscript + func _setup_spore_overlay() -> void: + var canvas := CanvasLayer.new() + canvas.name = "SporeOverlay" + canvas.layer = 10 # renders above dice UI (layer 0) + add_child(canvas) + + var rect := ColorRect.new() + rect.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + rect.mouse_filter = Control.MOUSE_FILTER_IGNORE + _spore_mat = ShaderMaterial.new() + _spore_mat.shader = load("res://shaders/spore_overlay.gdshader") as Shader + rect.material = _spore_mat + canvas.add_child(rect) + + NetworkManager.overlay_changed.connect(_on_overlay_changed) + + + func _on_overlay_changed(overlay_name: String) -> void: + var intensity := 1.0 if overlay_name == "spore_active" else 0.0 + _spore_mat.set_shader_parameter("intensity", intensity) + ``` + +- [ ] **Step 3: Call _setup_spore_overlay() in _ready()** + + Add at the very end of `_ready()`, after the existing `_setup_dice_ui()` call: + ```gdscript + _setup_spore_overlay() + ``` + + **Important:** do NOT modify any other part of `_ready()`. The DM early-return at the top already skips this. Only append — do not rewrite the method. + +- [ ] **Step 4: Open Godot, check for parse errors** + +- [ ] **Step 5: Run as player client** + + Log in as Spieler. Have DM toggle overlay to `spore_active` in the Regiepult. + Expected: green tinted, slightly distorted, vignette'd screen visible to the player. + Expected: toggling back to `default` clears the effect. + +- [ ] **Step 6: Commit** + + ```bash + git add ruf-der-pilze/scripts/tavern.gd + git commit -m "feat: tavern — add spore overlay CanvasLayer + shader driven by overlay_changed signal" + ``` + +--- + +## Task 4: Update STATUS.md + +- [ ] **Step 1: Mark Schritt 10 ✅ in docs/STATUS.md and ruf-der-pilze/CLAUDE.md** + +- [ ] **Step 2: Commit** + + ```bash + git add docs/STATUS.md ruf-der-pilze/CLAUDE.md + git commit -m "docs: mark Spore Overlay complete" + ```