feat: m2 etagengenerierung — prefabs, floors, korridore, abstieg und sieg

Der handgebaute Testraum ist weg; Etagen entstehen aus Daten.

- game/rng.rs: xorshift64*, Seed im Game (Ticket 14)
- game/generate.rs: freie Platzierung, Pflichtraeume zuerst, Kette
  i->i-1 fuer strukturelle Erreichbarkeit, acht Orientierungen
- data/: [[prefabs]] und [[floors]] als neunte und zehnte Registry,
  Ladepruefungen fuer rechteckige Layouts und Pflichtraum-Flaeche
- Descend plus Siegbedingung: keine [[floors]] mit der naechsten Nummer
  heisst gewonnen — die Zahl 5 steht nirgends im Code
- GameOver-Screen, mitlaufender Bildausschnitt im Grid

Befund: Korridore stanzten 2340 Loecher in gezeichnete Waende, weil sie
AUF der Tuer starteten. Erst Schritt nach aussen, dann L-Form, sonst
Wegsuche ueber den freien Raum — die kann eine Raumwand nicht beruehren.
Der Test zaehlt jetzt null.

21 Tests.
This commit is contained in:
2026-08-12 16:14:23 +02:00
parent 3714bad313
commit f998e55210
14 changed files with 1121 additions and 82 deletions

53
mods/core/floors.toml Normal file
View File

@@ -0,0 +1,53 @@
# Etagen sind Registry-Eintraege (Ticket 10). Ausgewaehlt wird durchgehend
# ueber TAGS, nie ueber IDs — sonst kann ein Mod keinen eigenen Eingangsraum
# beisteuern, ohne die ganze Tabelle zu ueberschreiben.
#
# `core:merchant` steht unter `required`, nicht unter `prefabs`: Ticket 07 hat
# Backtracking ausgeschlossen, der Haendler muss also garantiert auftauchen.
#
# Es gibt keine Zahl "5" im Code — findet die Engine keine Etage mit der
# naechsten Nummer, ist der Lauf gewonnen.
[[floors]]
id = "floor_1"
number = 1
size = [72, 34]
rooms = 7
required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 } ]
[[floors]]
id = "floor_2"
number = 2
size = [72, 34]
rooms = 8
required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 2 } ]
[[floors]]
id = "floor_3"
number = 3
size = [80, 38]
rooms = 9
required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 3 } ]
[[floors]]
id = "floor_4"
number = 4
size = [80, 38]
rooms = 10
required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 3 } ]
[[floors]]
id = "floor_5"
number = 5
size = [88, 40]
rooms = 11
required = ["core:entrance", "core:stairs", "core:merchant"]
prefabs = [ { tag = "core:normal", weight = 10 },
{ tag = "core:treasure", weight = 4 } ]

95
mods/core/prefabs.toml Normal file
View File

@@ -0,0 +1,95 @@
# Handgezeichnete Raeume (Ticket 10). Marker stehen IM Block — der Sinn von
# ASCII ist, dass der Modder den Raum sieht.
#
# # Wand e Gegner-Platz > Treppe n NPC-Platz
# . Boden l Beute-Platz < Eingang
# + Tuer (Korridore duerfen nur hier andocken)
#
# Ein `e` sagt "hier steht ein Gegner", NICHT welcher — das entscheidet die
# Etage. Nur so laesst sich derselbe Raum auf Etage 1 und Etage 5 verwenden.
[[prefabs]]
id = "entrance_hall"
tags = ["core:entrance"]
layout = """
#####+#####
#.........#
#....<....#
#.........#
+.........+
#.........#
#####+#####
"""
[[prefabs]]
id = "stair_room"
tags = ["core:stairs"]
layout = """
#######
#.....#
#..>..#
+.....+
#..l..#
#######
"""
[[prefabs]]
id = "market"
tags = ["core:merchant"]
layout = """
####+####
#.......#
#...n...#
+.......+
#..l.l..#
####+####
"""
[[prefabs]]
id = "crossing"
tags = ["core:normal"]
layout = """
####+####
#.......#
#..e...e#
+.......+
#..l....#
#.......#
####+####
"""
[[prefabs]]
id = "pillars"
tags = ["core:normal"]
layout = """
#####+#####
#.........#
#.##...##.#
+.##.e.##.+
#.........#
#..l...l..#
#####+#####
"""
[[prefabs]]
id = "closet"
tags = ["core:normal"]
layout = """
#####
#...#
+.l.+
#.e.#
#####
"""
[[prefabs]]
id = "vault"
tags = ["core:treasure"]
layout = """
#######
#.....#
#.lll.#
+..e..#
#.....#
#######
"""