10 — Raum-Prefab-Format #11

Closed
opened 2026-08-11 13:13:28 +00:00 by RDietermann · 1 comment
Owner

Teil der Map #1 · Type: wayfinder:grilling · Blocked by: #4

Wie sieht ein Raum-Prefab in TOML aus, und wie fuegt der Generator daraus eine Etage zusammen?

Volle Fragestellung (kanonisch): .scratch/rustkeep-1.0/issues/10-raum-prefab-format.md

Teil der Map #1 · **Type: `wayfinder:grilling`** · **Blocked by: #4** Wie sieht ein Raum-Prefab in TOML aus, und wie fuegt der Generator daraus eine Etage zusammen? Volle Fragestellung (kanonisch): `.scratch/rustkeep-1.0/issues/10-raum-prefab-format.md`
RDietermann added the wayfinder:grilling label 2026-08-11 13:13:28 +00:00
Author
Owner

Aufgeloest in 6442e10. Kanonische Fassung: .scratch/rustkeep-1.0/issues/10-raum-prefab-format.md

Etagenaufbau: freie Platzierung mit Korridoren

Prefabs nacheinander an zufaelliger Position, Ueberlappung (plus ein Feld Rand) verworfen. Verbunden wird Raum i mit Raum i−1 in Platzierungsreihenfolge, danach ein paar Zusatzkorridore fuer Schleifen; L-Form.

Die Kette statt eines Spannbaums, weil sie Erreichbarkeit strukturell garantiert: haengt jeder Raum am vorigen, ist jeder von jedem erreichbar — ohne Flutfuellung, ohne Reparaturschleife. Das beantwortet die Garantiefrage aus dem Ticketkoerper nebenbei.

Rotation und Spiegelung erlaubt (acht Varianten, Drehen eines ASCII-Blocks ist eine Transposition). Kein Opt-out-Feld: 1.0 hat kein Prefab, dessen Ausrichtung etwas bedeutet.

Ein Prefab

[[prefabs]]
id   = "core:kreuzung"
tags = ["core:normal"]
layout = """
####+####
#.......#
#..e...e#
+.......+
#..l....#
#.......#
####+####
"""

# Wand · . Boden · + Tuer · e Gegner-Platz · l Beute-Platz · > Treppe · < Eingang · n NPC-Platz.

Marker stehen im Block, nicht als Koordinatenliste daneben — der Sinn von ASCII ist, dass der Modder den Raum sieht. Tueren explizit, sonst stanzt der Generator Loecher in eine von Hand gezeichnete Wand.

Kein eigener Haendler-Marker: ein Haendler ist ein NPC. Ein zweites Zeichen fuer denselben Platz waere dieselbe Vermischung, die #3 beim ItemType abgeraeumt hat.

Ein e sagt „hier steht ein Gegner", nicht welcher — das entscheidet die Etage, sonst waere jedes Prefab an eine Schwierigkeitsstufe gebunden. Bei n ist es umgekehrt: den NPC nennt das Prefab, denn ein Schmied ist auf Etage 5 derselbe wie auf Etage 1.

Die Etage

[[floors]]
id       = "core:floor_3"
number   = 3
size     = [80, 40]
rooms    = 9
required = ["core:entrance", "core:stairs", "core:merchant"]   # Tags
prefabs  = [ { tag = "core:normal", weight = 10 } ]
creatures = [ { id = "core:skeleton", weight = 5 } ]
loot     = "core:floor_3_table"

Auswahl durchgehend ueber Tags, auch bei Pflichtraeumen — IDs hiessen, dass ein Mod keinen eigenen Eingangsraum beisteuern kann, ohne die Tabelle zu ueberschreiben. Pflichtraeume zuerst auf die leere Flaeche; das ist die Garantie. Optionale Raeume, die nicht mehr passen, entfallen — Normalfall, kein Fehler. Passen die Pflicht-Tags grundsaetzlich nicht in size, ist das ein Datenfehler und beim Laden erkennbar (Warnzeile ueber #5); zur Laufzeit kein Wiederholungszaehler, kein Rueckfallpfad.

core:merchant steht unter required, nicht unter prefabs: #8 hat Backtracking ausgeschlossen, der Haendler muss garantiert auftauchen.

Damit ist die offene Frage aus der #4-Vorgabe beantwortet: Generator-Parameter sind Daten. [[prefabs]] und [[floors]] sind die neunte und zehnte Registry.

Kampf beginnt beim Betreten eines Raums

Die Bestellung aus #15: Gegner gehoeren zum Raum, nicht zur Etage. room_entered in einen Raum mit lebenden Gegnern verschiebt sie nach Game.enemies; nach dem Sieg gilt der Raum als geraeumt.

Der Grund ist, was dadurch nicht gebaut wird: frei umherlaufende Gegner braeuchten Pfadsuche, Sichtlinien, Verfolgung und ein Bewegungsbudget pro Zug — ein eigenes System, das niemand bestellt hat.

Floor — der Innenbau aus #8

struct Floor { number: u8, tiles: Vec<Tile>, width: u16, height: u16,
               rooms: Vec<Room>, player_room: Option<usize> }
struct Room  { prefab: PrefabId, rect: Rect, enemies: Vec<Creature>,
               loot: Vec<(Pos, ItemInstance)>, cleared: bool }
enum Tile    { Wall, Floor, Door, Stairs, Entrance }

Korridore gehoeren zu keinem Raum — schlicht Tile::Floor ausserhalb aller rect. Dadurch feuert room_entered sauber beim Uebergang, und player_room: None ist ein echter Zustand statt eines Fehlers. Gegner werden bei Kampfbeginn verschoben, nicht kopiert. Ladepruefung: layout-Zeilen muessen gleich lang sein.

Siegbedingung — bisher nirgends definiert

Kein Ticket hatte je festgelegt, was beim Abstieg von der letzten Etage passiert.

Descend auf der letzten definierten Etage gewinnt den Lauf. Es gibt keine Zahl "5" im Code: die Engine sucht [[floors]] mit number = aktuell + 1, findet sie keine, ist Schluss. Ein Mod, der Etage 6 ergaenzt, verlaengert das Spiel ohne eine Zeile Engine-Code — dieselbe Bewegung wie core:attack in #12.

Zwei Nachtraege

  • #9 (08): room_entered { prefab_id } als zwoelftes Event. Dazu eine Beobachtung: die Liste wurde in #9 als geschlossen festgelegt, und seither hat jedes Ticket mit echten Spielsituationen eines gebraucht (#15 zwei, #11 eines). Wer #16 oder #17 bearbeitet, sollte damit rechnen.
  • #15 (14): GameOver { outcome: Died | Won } statt nur GameOver.

Auflagen

  • #10 (09 Save): Layout oder Seed? #15 hat einen seedbaren Generator geliefert — aber der Raumzustand (cleared, gefallene Gegner, genommene Beute) laesst sich nicht regenerieren und muss ohnehin mit. #10 entscheidet, ob sich der Seed dann noch lohnt.
  • #17 (16 Loot): loot an der Etage verweist auf eine Tabelle, die l-Plaetze ziehen daraus.
**Aufgeloest** in 6442e10. Kanonische Fassung: `.scratch/rustkeep-1.0/issues/10-raum-prefab-format.md` ### Etagenaufbau: freie Platzierung mit Korridoren Prefabs nacheinander an zufaelliger Position, Ueberlappung (plus ein Feld Rand) verworfen. Verbunden wird **Raum `i` mit Raum `i−1`** in Platzierungsreihenfolge, danach ein paar Zusatzkorridore fuer Schleifen; L-Form. Die Kette statt eines Spannbaums, weil sie **Erreichbarkeit strukturell garantiert**: haengt jeder Raum am vorigen, ist jeder von jedem erreichbar — ohne Flutfuellung, ohne Reparaturschleife. Das beantwortet die Garantiefrage aus dem Ticketkoerper nebenbei. Rotation und Spiegelung erlaubt (acht Varianten, Drehen eines ASCII-Blocks ist eine Transposition). Kein Opt-out-Feld: 1.0 hat kein Prefab, dessen Ausrichtung etwas bedeutet. ### Ein Prefab ```toml [[prefabs]] id = "core:kreuzung" tags = ["core:normal"] layout = """ ####+#### #.......# #..e...e# +.......+ #..l....# #.......# ####+#### """ ``` `#` Wand · `.` Boden · `+` Tuer · `e` Gegner-Platz · `l` Beute-Platz · `>` Treppe · `<` Eingang · `n` NPC-Platz. Marker stehen **im Block**, nicht als Koordinatenliste daneben — der Sinn von ASCII ist, dass der Modder den Raum sieht. **Tueren explizit**, sonst stanzt der Generator Loecher in eine von Hand gezeichnete Wand. Kein eigener Haendler-Marker: ein Haendler ist ein NPC. Ein zweites Zeichen fuer denselben Platz waere dieselbe Vermischung, die #3 beim `ItemType` abgeraeumt hat. **Ein `e` sagt „hier steht ein Gegner", nicht welcher** — das entscheidet die Etage, sonst waere jedes Prefab an eine Schwierigkeitsstufe gebunden. Bei `n` ist es **umgekehrt**: den NPC nennt das Prefab, denn ein Schmied ist auf Etage 5 derselbe wie auf Etage 1. ### Die Etage ```toml [[floors]] id = "core:floor_3" number = 3 size = [80, 40] rooms = 9 required = ["core:entrance", "core:stairs", "core:merchant"] # Tags prefabs = [ { tag = "core:normal", weight = 10 } ] creatures = [ { id = "core:skeleton", weight = 5 } ] loot = "core:floor_3_table" ``` Auswahl durchgehend ueber **Tags**, auch bei Pflichtraeumen — IDs hiessen, dass ein Mod keinen eigenen Eingangsraum beisteuern kann, ohne die Tabelle zu ueberschreiben. Pflichtraeume zuerst auf die leere Flaeche; das *ist* die Garantie. Optionale Raeume, die nicht mehr passen, entfallen — Normalfall, kein Fehler. Passen die Pflicht-Tags grundsaetzlich nicht in `size`, ist das ein Datenfehler und **beim Laden erkennbar** (Warnzeile ueber #5); zur Laufzeit kein Wiederholungszaehler, kein Rueckfallpfad. `core:merchant` steht unter `required`, nicht unter `prefabs`: #8 hat Backtracking ausgeschlossen, der Haendler muss garantiert auftauchen. Damit ist die offene Frage aus der #4-Vorgabe beantwortet: **Generator-Parameter sind Daten.** `[[prefabs]]` und `[[floors]]` sind die neunte und zehnte Registry. ### Kampf beginnt beim Betreten eines Raums Die Bestellung aus #15: **Gegner gehoeren zum Raum, nicht zur Etage.** `room_entered` in einen Raum mit lebenden Gegnern verschiebt sie nach `Game.enemies`; nach dem Sieg gilt der Raum als geraeumt. Der Grund ist, was dadurch **nicht** gebaut wird: frei umherlaufende Gegner braeuchten Pfadsuche, Sichtlinien, Verfolgung und ein Bewegungsbudget pro Zug — ein eigenes System, das niemand bestellt hat. ### `Floor` — der Innenbau aus #8 ```rust struct Floor { number: u8, tiles: Vec<Tile>, width: u16, height: u16, rooms: Vec<Room>, player_room: Option<usize> } struct Room { prefab: PrefabId, rect: Rect, enemies: Vec<Creature>, loot: Vec<(Pos, ItemInstance)>, cleared: bool } enum Tile { Wall, Floor, Door, Stairs, Entrance } ``` Korridore gehoeren zu **keinem** Raum — schlicht `Tile::Floor` ausserhalb aller `rect`. Dadurch feuert `room_entered` sauber beim Uebergang, und `player_room: None` ist ein echter Zustand statt eines Fehlers. Gegner werden bei Kampfbeginn **verschoben, nicht kopiert**. Ladepruefung: `layout`-Zeilen muessen gleich lang sein. ### Siegbedingung — bisher nirgends definiert Kein Ticket hatte je festgelegt, was beim Abstieg von der letzten Etage passiert. **`Descend` auf der letzten definierten Etage gewinnt den Lauf.** Es gibt keine Zahl "5" im Code: die Engine sucht `[[floors]]` mit `number = aktuell + 1`, findet sie keine, ist Schluss. Ein Mod, der Etage 6 ergaenzt, verlaengert das Spiel ohne eine Zeile Engine-Code — dieselbe Bewegung wie `core:attack` in #12. ### Zwei Nachtraege - **#9 (08)**: `room_entered { prefab_id }` als **zwoelftes** Event. Dazu eine Beobachtung: die Liste wurde in #9 als geschlossen festgelegt, und seither hat jedes Ticket mit echten Spielsituationen eines gebraucht (#15 zwei, #11 eines). Wer #16 oder #17 bearbeitet, sollte damit rechnen. - **#15 (14)**: `GameOver { outcome: Died | Won }` statt nur `GameOver`. ### Auflagen - **#10** (09 Save): Layout oder Seed? #15 hat einen seedbaren Generator geliefert — aber der Raumzustand (`cleared`, gefallene Gegner, genommene Beute) laesst sich nicht regenerieren und muss ohnehin mit. #10 entscheidet, ob sich der Seed dann noch lohnt. - **#17** (16 Loot): `loot` an der Etage verweist auf eine Tabelle, die `l`-Plaetze ziehen daraus.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RDietermann/rustkeep#11