docs: spec for light & darkness (model B, blocking)
Room.dark + Item.light flags, a Light helper (isLit/carryingLight), and command gating: can't enter a dark room without a lit light source; look/take/examine obscured in the dark; HUD light field wired. Entry-gate prevents soft-locks. Demo: dark dungeon needs the lit lamp. Fuel/timed light out of scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
|||||||
|
# Spec: Licht & Dunkelheit (Mechanik-Erweiterung)
|
||||||
|
|
||||||
|
Stand: 2026-05-31. Kleine Mechanik aus dem Spine (#3 der Mechanik-Liste), die das
|
||||||
|
Fundament (Flags/Items) nutzt. Strenge: **Modell B** (Dunkelheit blockiert).
|
||||||
|
|
||||||
|
## 1. Kontext & Ziel
|
||||||
|
|
||||||
|
Dunkle Räume sollen den schaltbaren `lamp` sinnvoll machen: Ohne aktive
|
||||||
|
Lichtquelle kann man einen dunklen Raum **nicht betreten**, nichts sehen
|
||||||
|
(`look`), und nichts aufnehmen/untersuchen. Die HUD-Zeile `light: on/off` wird
|
||||||
|
endlich verdrahtet.
|
||||||
|
|
||||||
|
## 2. Scope
|
||||||
|
|
||||||
|
**In Scope:**
|
||||||
|
- `Room.dark` (Bool, optional), `Item.light` (Bool, optional).
|
||||||
|
- `Light`-Helper: `isLit`, `carryingLight`.
|
||||||
|
- Command-Gating: `go` (Eintritt), `look`, `take`, `examine`; HUD-`lightOn`.
|
||||||
|
- Demo: Dungeon dunkel → Lampe nötig.
|
||||||
|
|
||||||
|
**Out of Scope:** Lampen-Brennstoff/zeitlich begrenztes Licht; Lichtradius über
|
||||||
|
Räume hinweg; Zufall.
|
||||||
|
|
||||||
|
## 3. Daten (rückwärtskompatibel)
|
||||||
|
|
||||||
|
- **`Room.dark`**: `@Setter`-Feld (default `false`), **kein** Konstruktor-Argument →
|
||||||
|
keine Test-Brüche. Vom `RoomFactory` aus `RoomDto.dark` gesetzt. YAML: `dark: true`.
|
||||||
|
- **`Item.light`**: `boolean` am abstrakten `Item` (via `@SuperBuilder`), default
|
||||||
|
`false`. Vom `ItemFactory` aus `ItemDto.light` gesetzt. YAML: `light: true`.
|
||||||
|
- **Aktive Lichtquelle** = `item.light` **und** (kein Switchable **oder**
|
||||||
|
`SwitchableItem.isOn()`). Die `lamp` leuchtet also nur eingeschaltet.
|
||||||
|
|
||||||
|
## 4. Helper `Light` (game)
|
||||||
|
|
||||||
|
```java
|
||||||
|
static boolean isLit(GameContext ctx, Room room) {
|
||||||
|
if (!room.isDark()) return true;
|
||||||
|
return hasActiveLight(player-inventory) || hasActiveLight(room-items);
|
||||||
|
}
|
||||||
|
static boolean carryingLight(GameContext ctx); // aktive Lichtquelle im Inventar
|
||||||
|
```
|
||||||
|
`hasActiveLight(items)` iteriert; `isActiveLight(item)` = `item.isLight()` &&
|
||||||
|
(`!(item instanceof SwitchableItem s)` || `s.isOn()`).
|
||||||
|
|
||||||
|
Räume gelten als beleuchtet, wenn sie nicht dunkel sind **oder** eine aktive
|
||||||
|
Lichtquelle getragen wird **oder** im Raum liegt.
|
||||||
|
|
||||||
|
## 5. Command-Gating (Modell B)
|
||||||
|
|
||||||
|
| Command | Verhalten bei dunklem, unbeleuchtetem Zielraum/aktuellem Raum |
|
||||||
|
|---|---|
|
||||||
|
| `GoCommand` | **Eintritt blockiert**, wenn `!isLit(ctx, target)`: Hinweis-Text, kein Wechsel. (Lichträume bleiben immer betretbar → **kein Soft-Lock**, Rückzug stets möglich.) |
|
||||||
|
| `LookCommand` | `!isLit(ctx, current)` → „It's pitch black; you can't make anything out." statt `RoomView` (Fall: Lampe ging im Raum aus). |
|
||||||
|
| `TakeCommand` | `!isLit(ctx, current)` → „It's too dark to see that." |
|
||||||
|
| `ExamineCommand` | `!isLit(ctx, current)` → „It's too dark to see that." |
|
||||||
|
| HUD | `lightOn` = `Light.carryingLight(ctx)` (in `Game.publishHud`). |
|
||||||
|
|
||||||
|
Erlaubt im Dunkeln bleiben: `inventory`, `drop`, **`use`** (z. B. Lampe anzünden),
|
||||||
|
`quests`, `map`, `help`. So kann man eine getragene Lampe im Dunkeln anzünden.
|
||||||
|
|
||||||
|
## 6. Fehlerbehandlung / Soft-Lock
|
||||||
|
|
||||||
|
Eintrittssperre statt Innen-Sperre: Man betritt einen dunklen Raum nur **mit**
|
||||||
|
Licht. Geht das Licht drinnen aus, sind `look`/`take`/`examine` blockiert, aber der
|
||||||
|
Rückweg in einen hellen Raum bleibt (Zielraum hell → `isLit` true). Kein Soft-Lock.
|
||||||
|
|
||||||
|
## 7. Demo
|
||||||
|
|
||||||
|
- `lamp`: `light: true` (bereits schaltbar; `use lamp` → an).
|
||||||
|
- `dungeon`: `dark: true`; der Generator steht dort. Ablauf:
|
||||||
|
`take lamp` → `use lamp` (an) → `go south` (jetzt betretbar) → `use generator`.
|
||||||
|
HUD zeigt `light: on`, sobald die Lampe brennt. Gibt man die Lampe später dem
|
||||||
|
alten Mann, verliert man das Licht (thematisch).
|
||||||
|
|
||||||
|
## 8. Testing
|
||||||
|
|
||||||
|
- `Light.isLit`: nicht dunkel → hell; dunkel ohne Licht → dunkel; dunkel + getragene
|
||||||
|
brennende Lampe → hell; dunkel + Lampe **aus** → dunkel; dunkel + brennende Lampe
|
||||||
|
**im Raum** → hell. `carryingLight`.
|
||||||
|
- Command-Gates: `go` in dunklen Raum ohne Licht blockt, mit brennender Lampe ok;
|
||||||
|
`look`/`take` im dunklen Raum blockt.
|
||||||
|
- End-to-End (Konsole): `go south` ohne Licht blockt; `take lamp`/`use lamp`/`go south`
|
||||||
|
betritt; HUD `light: on`.
|
||||||
|
|
||||||
|
## 9. Offene Detailfragen (in Implementierung)
|
||||||
|
|
||||||
|
- Genauer Hinweis-Text der Eintrittssperre (nennt `use lamp`).
|
||||||
Reference in New Issue
Block a user