# 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"
  ```
