docs: add plans for Schritt 10-11 (spore overlay, slider) + full parallel execution plan
This commit is contained in:
200
docs/plans/2026-04-14-parallel-impl-all.md
Normal file
200
docs/plans/2026-04-14-parallel-impl-all.md
Normal file
@@ -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
|
||||
```
|
||||
256
docs/plans/2026-04-14-spore-level-slider.md
Normal file
256
docs/plans/2026-04-14-spore-level-slider.md
Normal file
@@ -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"
|
||||
```
|
||||
213
docs/plans/2026-04-14-spore-overlay.md
Normal file
213
docs/plans/2026-04-14-spore-overlay.md
Normal file
@@ -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"
|
||||
```
|
||||
Reference in New Issue
Block a user