This commit is contained in:
team3
2026-08-31 11:07:01 +02:00
parent f936dd2b2a
commit ef2ecc08f9
19 changed files with 2094 additions and 438 deletions

View File

@@ -193,7 +193,6 @@ func _ready() -> void:
if not Game.completed.has("mensch_0"):
Game.completed.append("mensch_0")
Game.active_mission = ""
Game.einsicht = 3
Game.welt_erzeugen()
main._traumwelt()
await warte(20)

View File

@@ -25,14 +25,18 @@ func _ready() -> void:
await test_start()
await test_journal()
await test_traumdorf()
await test_dorf_organisch()
await test_haus_betreten()
await test_hoersaal()
await test_mission_annehmen()
await test_reise_zur_erde()
await test_bezirk()
await test_tagesablauf()
await test_wege_machbar()
await test_geist()
await test_technik_anwenden()
await test_cooldown()
await test_chance_verdeckt()
await test_pipeline()
await test_mission_abschliessen()
await test_welt_waechst()
@@ -40,6 +44,9 @@ func _ready() -> void:
await test_journal_ueberall()
await test_steuerung()
await test_erster_traum_schaffbar()
await test_brett_nie_leer()
await test_lesen_lohnt_sich()
await test_tempo()
print("\n=========================================")
print("Fehlende Prüfungen: %d" % fehler)
@@ -79,6 +86,16 @@ func _journal_text() -> String:
return _panel_text()
func _lehre_text() -> String:
return _panel_text()
## Hebt alle Technik-Sperren auf. Nur für Messschleifen, die viele Anwendungen
## in Folge brauchen — der Cooldown selbst wird eigens geprüft.
func _frei() -> void:
Game.cooldown.clear()
## Welche Gebäude stehen wirklich in der Traumwelt? Erkennbar an ihren Schildern.
## Was der Hörsaal von Anfang an lehrt.
func _basiswege() -> Array:
@@ -163,8 +180,13 @@ func test_start() -> void:
arten[n.get_meta("kind")] = true
p(arten.has("erde") and arten.has("lehre") and arten.has("brett"),
"Traumwelt bietet Tor, Hörsaal und Schwarzes Brett (%s)" % ", ".join(arten.keys()))
p(Game.einsicht == 3, "Startkapital 3 Einsicht")
p(Game.known.is_empty(), "Noch keine Technik gelernt")
p(Game.known.size() == 1, "Ein Weg ist von Anfang an gelernt (%d)" % Game.known.size())
p(Game.knows(str(_basiswege()[0])),
"Und zwar der des Hörsaals: %s" % TechDB.by_id(str(_basiswege()[0]))["name"])
p(Game.skill_of(str(_basiswege()[0])) == 1, "Auf Stufe 1, wie frisch gelernt")
p(not Game.offene_missionen().is_empty(),
"Damit hängt vom ersten Moment an eine Mission aus (%d)" %
Game.offene_missionen().size())
p((welt() as DreamWorld).bewohner.is_empty(),
"Das Traumdorf steht am Anfang leer (%d Wesen)" %
(welt() as DreamWorld).bewohner.size())
@@ -182,17 +204,49 @@ func test_start() -> void:
func test_journal() -> void:
abschnitt("Journal")
for t in TechDB.LIST:
var vollstaendig: bool = t["was"] != "" and t["wann"] != "" and t["warum"] != ""
var vollstaendig: bool = t["was"] != "" and t["wann"] != "" and t["warum"] != "" \
and t["woran"] != ""
if not vollstaendig:
p(false, "Technik %s hat alle drei Hinweise" % t["id"])
p(true, "Alle %d Techniken tragen Was/Wann/Warum" % TechDB.LIST.size())
p(false, "Technik %s hat alle vier Hinweise" % t["id"])
p(true, "Alle %d Techniken tragen Was/Wann/Warum/Woran" % TechDB.LIST.size())
# Der Woran-Hinweis muss jeden Moment der Technik wirklich benennen —
# sonst ist die Technik im Spiel nicht lesbar.
var stamm := {
"schlaeft": "schläf", "morgenroutine": "morgenroutine", "arbeitet": "arbeit",
"pause": "pause", "essen": "essen", "gruebelt": "grübel", "ruht": "ausruh",
"angespannt": "angespannt", "abendroutine": "abendroutine",
"aufraeumen": "aufräum", "wartet": "wartet", "muede": "müde",
"unterwegs": "unterwegs", "zoegert": "zöger",
}
var luecken: Array = []
for t in TechDB.LIST:
var text: String = str(t["woran"]).to_lower()
for m in t["momente"]:
if not stamm.has(m):
luecken.append("%s: %s unbekannt" % [t["id"], m])
elif not text.contains(str(stamm[m])):
luecken.append("%s nennt »%s« nicht" % [t["id"], m])
p(luecken.is_empty(), "Jeder Woran-Hinweis benennt alle Momente seiner Technik%s" %
("" if luecken.is_empty() else " — " + ", ".join(luecken)))
# Aber er verrät nicht mehr als die Momente
var zuviel: Array = []
for t in TechDB.LIST:
var text: String = str(t["woran"]).to_lower()
for m in stamm:
if (t["momente"] as Array).has(m):
continue
if text.contains(str(stamm[m])):
zuviel.append("%s nennt fremdes »%s«" % [t["id"], m])
p(zuviel.is_empty(), "Und keinen Moment, der nicht dazugehört%s" %
("" if zuviel.is_empty() else " — " + ", ".join(zuviel)))
ui().journal_panel("techniken")
p(ui().offen and ui()._kind == "journal", "Journal öffnet sich")
p(_journal_text().contains("Hörsaal"),
"Ohne gelernte Technik verweist das Journal auf den Hörsaal")
p(not _knopf_da("Lernen") and not _knopf_da("Vertiefen"),
"Das Journal lehrt und vertieft nicht selbst")
p(_journal_text().contains(str(TechDB.by_id(str(_basiswege()[0]))["name"])),
"Das Journal führt den mitgebrachten Weg")
p(not _knopf_da("Diesen Weg"), "Das Journal lehrt nicht selbst")
ui().journal_panel("missionen")
p(_journal_text().contains("Schwarzen Brett"),
"Ohne Mission verweist das Journal auf das Schwarze Brett")
@@ -202,10 +256,10 @@ func test_journal() -> void:
var erster := str(_basiswege()[0])
p(Game.offene_techniken() == 1,
"Am Anfang ist genau ein Weg entdeckt (%d)" % Game.offene_techniken())
p(Game.learn(erster), "Der erste Weg ist gelernt: %s" % TechDB.by_id(erster)["name"])
p(Game.einsicht == 2, "Das kostet 1 von 3 Einsicht (%d)" % Game.einsicht)
p(Game.knows(erster), "Er ist schon gelernt: %s" % TechDB.by_id(erster)["name"])
p(not Game.learn(erster), "Zweimal lernen geht nicht")
p(not Game.learn(str(TechDB.LIST[1]["id"])),
"Der zweite Weg ist trotz Einsicht nirgends zu lernen")
"Der zweite Weg ist nirgends zu lernen — er ist noch nicht entdeckt")
p(Game.skill_of(erster) == 1, "Frisch gelernte Technik ist Stufe 1")
Game.add_xp(erster, 4)
p(Game.skill_of(erster) == 2, "4 EP heben auf Stufe 2")
@@ -369,37 +423,37 @@ func test_hoersaal() -> void:
p(basis_da, "Die %d Basiswege werden gelehrt" % 1)
var lernbar := 0
for c in ui()._content.get_children():
if c is Button and (c as Button).text.begins_with("Lernen"):
if c is Button and (c as Button).text.begins_with("Diesen Weg"):
lernbar += 1
p(lernbar <= 1, "Und höchstens einer davon ist zu lernen (%d)" % lernbar)
p(seite.contains(str(TechDB.LIST[1]["name"])),
"Der Hörsaal nennt den nächsten noch unentdeckten Weg")
p(not Game.learn("box_atmung"), "Eine unentdeckte Technik lässt sich nicht lernen")
Game.einsicht = 1
Game.known.clear()
ui().lehre_panel()
var vorher := Game.known.size()
p(_druecke("Lernen"), "Ein Lernen-Knopf steht bereit")
p(_druecke("Diesen Weg"), "Ein Lern-Knopf steht bereit")
p(Game.known.size() == vorher + 1, "Im Hörsaal gelernt (%d -> %d)" % [vorher, Game.known.size()])
p(Game.einsicht == 0, "Lernen kostet 1 Einsicht")
Game.einsicht = 1
# Gelernt heißt fertig: der Lehrort bietet danach nichts mehr an
ui().lehre_panel()
var xp_summe := 0
for id in Game.known:
xp_summe += int(Game.known[id])
p(_druecke("Vertiefen"), "Ein Vertiefen-Knopf steht bereit")
p(not _knopf_da("Vertiefen") and not _knopf_da("Üben") and not _knopf_da("Trainieren"),
"Kein Knopf trainiert eine gelernte Technik")
p(not _druecke("Diesen Weg"), "Auch der Lern-Knopf ist verschwunden")
var xp_neu := 0
for id in Game.known:
xp_neu += int(Game.known[id])
p(xp_neu == xp_summe + 3, "Vertiefen bringt 3 EP (%d -> %d)" % [xp_summe, xp_neu])
p(Game.einsicht == 0, "Vertiefen kostet 1 Einsicht")
p(xp_neu == xp_summe, "Der Besuch im Hörsaal bringt keine Erfahrung (%d)" % xp_neu)
p(_lehre_text().contains("anwendest"),
"Stattdessen verweist der Hörsaal aufs Anwenden")
ui().schliessen()
# Ausgangslage für die folgenden Prüfungen wiederherstellen
Game.known = {"pomodoro": 0, "zerlegen": 0, "umgebung": 0}
Game.einsicht = 0
func test_mission_annehmen() -> void:
@@ -419,11 +473,39 @@ func test_mission_annehmen() -> void:
ui().schliessen()
main._interagieren()
p(ui().offen and ui()._kind == "brett", "Das Brett öffnet sich")
var alle_aus := true
# Nur wer mit dem Gelernten zu schaffen ist, hängt aus.
var alle_wege: Dictionary = Game.known.duplicate()
Game.known = {str(_basiswege()[0]): 0}
ui().brett_panel()
var aushang := _panel_text()
var gezeigt := 0
var fehlend := 0
for h in HumanDB.LIST:
if not _panel_text().contains(str(h["name"])):
alle_aus = false
p(alle_aus, "Alle %d Menschen hängen aus" % HumanDB.LIST.size())
var id: String = h["id"]
if aushang.contains(str(h["name"])):
gezeigt += 1
if not Game.machbar(id):
p(false, "%s hängt aus, obwohl kein Weg greift" % h["name"])
elif Game.machbar(id):
p(false, "%s wäre lösbar, hängt aber nicht aus" % h["name"])
else:
fehlend += 1
p(gezeigt > 0, "Mit einem Weg hängt mindestens ein Aushang (%d von %d)" %
[gezeigt, HumanDB.LIST.size()])
p(fehlend > 0, "Und mindestens einer bleibt verdeckt (%d)" % fehlend)
p(aushang.contains("verdeckt"), "Das Brett sagt, dass etwas verdeckt bleibt")
p(Game.offene_missionen().size() == gezeigt,
"Brett und offene_missionen() sind sich einig (%d)" % gezeigt)
# Ohne jede Technik bleibt das Brett leer, mit Hinweis auf den Hörsaal
Game.known.clear()
ui().brett_panel()
p(Game.offene_missionen().is_empty(), "Ohne Technik ist nichts zu holen")
p(_panel_text().contains("Hörsaal"), "Und das Brett schickt in den Hörsaal")
Game.known = alle_wege
ui().brett_panel()
p(Game.offene_missionen().size() >= gezeigt,
"Mehr Wege heißt mehr Aushänge (%d)" % Game.offene_missionen().size())
p(Game.active_mission == "", "Vorher keine Mission")
var gedrueckt := _druecke("Mission annehmen")
@@ -547,11 +629,12 @@ func test_tagesablauf() -> void:
func test_wege_machbar() -> void:
abschnitt("Wege passen in die Zeitfenster")
_wege_pruefen("Erde", HumanDB.PLACES, HumanDB.LIST)
_wege_pruefen("Traumwelt", DreamDB.PLACES, DreamDB.LIST)
_wege_pruefen("Erde", HumanDB.PLACES, HumanDB.LIST, HumanDB.NETZ, HumanDB.ANKER)
_wege_pruefen("Traumwelt", DreamDB.PLACES, DreamDB.LIST, DreamDB.NETZ, DreamDB.ANKER)
func _wege_pruefen(welt_name: String, places: Dictionary, liste: Array) -> void:
func _wege_pruefen(welt_name: String, places: Dictionary, liste: Array,
netz: Netz = null, anker: Dictionary = {}) -> void:
var sek_pro_stunde := Game.DAY_SECONDS / 24.0
var alles_ok := true
var engste := 999.0
@@ -563,7 +646,7 @@ func _wege_pruefen(welt_name: String, places: Dictionary, liste: Array) -> void:
var nach: String = String(r[i][1])
if von == nach:
continue
var dauer := _weg_laenge(places, von, nach) / Bewohner.GEHTEMPO
var dauer := _weg_laenge(places, von, nach, netz, anker) / Bewohner.GEHTEMPO
var slot_h: float = (float(r[i + 1][0]) - float(r[i][0])) if i + 1 < r.size() \
else (24.0 - float(r[i][0]))
var fenster := slot_h * sek_pro_stunde
@@ -578,8 +661,9 @@ func _wege_pruefen(welt_name: String, places: Dictionary, liste: Array) -> void:
p(alles_ok, "%s — jeder Weg passt, engster Fall: %s" % [welt_name, engste_text])
func _weg_laenge(places: Dictionary, von: String, nach: String) -> float:
var pts: Array = Orte.route(places, von, nach, Vector3.ZERO)
func _weg_laenge(places: Dictionary, von: String, nach: String,
netz: Netz = null, anker: Dictionary = {}) -> float:
var pts: Array = Orte.route(places, von, nach, Vector3.ZERO, netz, anker)
var l := 0.0
var pos := Orte.center(places, von)
for pt in pts:
@@ -657,20 +741,26 @@ func test_technik_anwenden() -> void:
p(Game.evaluate("nein_sagen", "mensch_0", "zoegert", false)["blocked"],
"Ungelernte Techniken lassen sich nicht anwenden")
_frei()
Game.progress["mensch_0"] = 50.0
var vorher_p: float = Game.mission_progress("mensch_0")
var res := Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
p(res["ok"], "Anwendung wird ausgeführt")
if res["success"]:
p(Game.mission_progress("mensch_0") > vorher_p, "Erfolg bringt Missionsfortschritt")
else:
p(Game.mission_progress("mensch_0") == vorher_p, "Misserfolg bringt keinen Fortschritt")
p(Game.mission_progress("mensch_0") < vorher_p,
"Fehlversuch kostet Fortschritt (%.0f -> %.0f)" %
[vorher_p, Game.mission_progress("mensch_0")])
p(Game.known["pomodoro"] > 0, "Jede Anwendung bringt Erfahrung")
_frei()
Game.progress["mensch_0"] = 0.0
Game.used.clear()
Game.known["pomodoro"] = 28
var ertraege: Array = []
for i in range(6):
_frei()
var r := Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
if r["success"]:
ertraege.append(r["gain"])
@@ -713,8 +803,8 @@ func test_pipeline() -> void:
p(not schlaf_aktiv, "Und lässt sich gar nicht erst anklicken")
ui().regel_panel("mensch_0", "zoegert")
p(ui()._kind == "regel", "Die Technikwahl für »zögert« öffnet sich")
p(_panel_text().contains("Pomodoro-Technik"), "Gelernte Techniken stehen zur Wahl")
p(_druecke("Diese Regel"), "Eine Technik lässt sich zuordnen")
p(_knopf_texte().contains("Pomodoro-Technik"), "Gelernte Techniken stehen zur Wahl")
p(_druecke("Pomodoro-Technik"), "Eine Technik lässt sich zuordnen")
p(Game.regel_fuer("mensch_0", "zoegert") != "",
"Regel steht: zögert -> %s" % Game.regel_fuer("mensch_0", "zoegert"))
p(ui()._kind == "pipeline", "Danach ist wieder das Regelwerk offen")
@@ -724,15 +814,20 @@ func test_pipeline() -> void:
Game.regel_setzen("mensch_0", "aufraeumen", "umgebung")
p(Game.regel_anzahl("mensch_0") == 3, "Drei Regeln im Werk (%d)" % Game.regel_anzahl("mensch_0"))
# Eine Technik hält nur einen Posten
# Eine Technik darf an mehreren Tätigkeiten hängen
Game.regel_setzen("mensch_0", "arbeitet", "zerlegen")
p(Game.regel_anzahl("mensch_0") == 3,
"Dieselbe Technik anderswo einzusetzen fügt keine Regel hinzu (%d)" %
p(Game.regel_anzahl("mensch_0") == 4,
"Dieselbe Technik anderswo einzusetzen kommt hinzu (%d)" %
Game.regel_anzahl("mensch_0"))
p(Game.regel_fuer("mensch_0", "gruebelt") == "" and Game.regel_fuer("mensch_0", "arbeitet") == "zerlegen",
"Sie ist umgezogen statt sich zu verdoppeln")
p(Game.regel_fuer("mensch_0", "gruebelt") == "zerlegen"
and Game.regel_fuer("mensch_0", "arbeitet") == "zerlegen",
"Sie hängt jetzt an beiden Tätigkeiten")
p(Game.regel_momente("mensch_0", "zerlegen").size() == 2,
"Und das Spiel weiß von beiden Posten (%d)" %
Game.regel_momente("mensch_0", "zerlegen").size())
Game.regel_setzen("mensch_0", "arbeitet", "")
Game.regel_setzen("mensch_0", "gruebelt", "zerlegen")
p(Game.regel_momente("mensch_0", "zerlegen").size() == 1,
"Einzeln löschen lässt den anderen Posten stehen")
# Ohne Zutun einen Tag laufen lassen
var lena: Bewohner = null
@@ -787,18 +882,19 @@ func test_mission_abschliessen() -> void:
abschnitt("Mission abschließen")
Game.progress["mensch_0"] = 0.0
Game.used.clear()
var einsicht_vorher := Game.einsicht
var runden := 0
while Game.mission_progress("mensch_0") < Game.MISSION_ZIEL and runden < 400:
_frei()
Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
_frei()
Game.apply_technique("zerlegen", "mensch_0", "gruebelt", false)
_frei()
Game.apply_technique("umgebung", "mensch_0", "aufraeumen", false)
runden += 1
p(Game.mission_progress("mensch_0") >= Game.MISSION_ZIEL,
"Lenas Traum erreicht 100 %% (nach %d Runden)" % runden)
p(Game.completed.has("mensch_0"), "Mission gilt als abgeschlossen")
p(Game.active_mission == "", "Aktive Mission wurde freigegeben")
p(Game.einsicht == einsicht_vorher + 3, "Abschluss bringt 3 Einsicht")
p(DreamDB.LIST.size() == Game.completed.size(),
"Der Abschluss lässt das Dorf wachsen (%d Wesen)" % DreamDB.LIST.size())
schritte(3)
@@ -836,13 +932,12 @@ func test_welt_waechst() -> void:
p(Game.insel_radius() >= Game.INSEL_GRUND,
"Die Insel trägt das Dorf (%.0f)" % Game.insel_radius())
Game.einsicht = 3
var neu_id := str(TechDB.LIST[1]["id"])
ui().lehre_panel(DreamDB.haus_key(0))
p(ui()._kind == "lehre", "Das neue Haus lehrt")
p(_panel_text().contains(str(TechDB.by_id(neu_id)["name"])),
"Und zwar den Weg, den sein Bewohner mitbrachte")
p(_druecke("Lernen"), "Er lässt sich lernen")
p(_druecke("Diesen Weg"), "Er lässt sich lernen")
p(Game.knows(neu_id), "Gelernt: %s" % TechDB.by_id(neu_id)["name"])
ui().schliessen()
p(not Game.technik_offen(str(TechDB.LIST[2]["id"])),
@@ -853,7 +948,6 @@ func test_welt_waechst() -> void:
# Ausgangslage für die folgenden Prüfungen
Game.known.erase(neu_id)
Game.einsicht = 2
Game.welt_erzeugen()
main._erde()
await get_tree().process_frame
@@ -910,35 +1004,49 @@ func _dorf_platz_pruefen() -> void:
for i in range(stufen):
Game.completed.append("stufe_%d" % i)
Game.welt_erzeugen()
_wege_pruefen("Traumdorf mit %d Häusern" % stufen, DreamDB.PLACES, DreamDB.LIST)
_wege_pruefen("Stadt mit %d Menschen" % HumanDB.LIST.size(), HumanDB.PLACES, HumanDB.LIST)
_wege_pruefen("Traumdorf mit %d Häusern" % stufen, DreamDB.PLACES, DreamDB.LIST,
DreamDB.NETZ, DreamDB.ANKER)
_wege_pruefen("Stadt mit %d Menschen" % HumanDB.LIST.size(), HumanDB.PLACES,
HumanDB.LIST, HumanDB.NETZ, HumanDB.ANKER)
Game.completed.assign(merker)
Game.welt_erzeugen()
func test_techniken_verbessern_sich() -> void:
abschnitt("Techniken verbessern sich durch Anwendung")
Game.einsicht = 2
abschnitt("Techniken verbessern sich nur durch Anwendung")
p(Game.learn("zwei_minuten"), "Technik gelernt")
p(Game.skill_of("zwei_minuten") == 1, "Start auf Stufe 1")
var einsicht_vorher := Game.einsicht
p(Game.skill_of("zwei_minuten") == 1, "Frisch gelernt heißt Stufe 1")
var anwendungen := 0
while Game.skill_of("zwei_minuten") < 3 and anwendungen < 60:
_frei()
Game.apply_technique("zwei_minuten", "mensch_2", "gruebelt", false)
anwendungen += 1
p(Game.skill_of("zwei_minuten") >= 3,
"Nach %d Anwendungen von allein auf Stufe %d" % [anwendungen, Game.skill_of("zwei_minuten")])
p(Game.einsicht == einsicht_vorher, "Das kostet keine Einsicht")
var vor_xp: int = Game.known["zwei_minuten"]
_frei()
var res := Game.apply_technique("zwei_minuten", "mensch_2", "essen", false)
p(Game.known["zwei_minuten"] > vor_xp,
"Auch ein %s bringt Erfahrung" % ("Erfolg" if res["success"] else "Fehlversuch"))
var xp_vor: int = Game.known["zwei_minuten"]
p(Game.deepen("zwei_minuten"), "Vertiefen funktioniert")
p(Game.known["zwei_minuten"] == xp_vor + 3, "Vertiefen bringt 3 EP")
p(Game.einsicht == einsicht_vorher - 1, "Vertiefen kostet 1 Einsicht")
# Es gibt keinen zweiten Weg nach oben
p(not Game.has_method("deepen"), "Kein gezieltes Vertiefen mehr im Spiel")
p(not ("einsicht" in Game), "Und keine Währung, die man dafür ausgeben könnte")
var stand: int = Game.known["zwei_minuten"]
Game.learn("zwei_minuten")
ui().lehre_panel(DreamDB.ort_von_technik("zwei_minuten"))
var geknoepft := _druecke("Diesen Weg") or _druecke("Vertiefen") or _druecke("Üben")
ui().schliessen()
p(not geknoepft and Game.known["zwei_minuten"] == stand,
"Ein zweiter Besuch am Lehrort ändert nichts (%d EP)" % stand)
# Zeitablauf allein bringt auch nichts
var vorher: int = Game.known["zwei_minuten"]
Game.advance_time(Game.DAY_SECONDS)
p(Game.known["zwei_minuten"] == vorher, "Und auch ein ganzer Tag bringt keine Erfahrung")
func test_journal_ueberall() -> void:
@@ -974,13 +1082,13 @@ func test_journal_ueberall() -> void:
func test_steuerung() -> void:
abschnitt("Steuerung")
var aktionen := ["vor", "zurueck", "links", "rechts", "steigen", "sinken",
"interagieren", "journal"]
"interagieren", "journal", "tempo_1", "tempo_2", "tempo_3"]
var alle := true
for a in aktionen:
if not InputMap.has_action(a) or InputMap.action_get_events(a).is_empty():
alle = false
p(false, "Aktion '%s' ist belegt" % a)
p(alle, "Alle acht Steuerungsaktionen sind mit Tasten belegt")
p(alle, "Alle %d Steuerungsaktionen sind mit Tasten belegt" % aktionen.size())
ui().schliessen()
main.player.look_enabled = true
@@ -1030,7 +1138,7 @@ func test_steuerung() -> void:
## und zwar zuverlässig, nicht nur bei gutem Würfelglück.
func _erster_traum_tage(saat: int) -> int:
seed(saat)
Game.einsicht = 3
Game.cooldown.clear()
Game.known.clear()
Game.used.clear()
Game.pipeline.clear()
@@ -1088,6 +1196,548 @@ func test_erster_traum_schaffbar() -> void:
p(bester >= 2, "Nie geschenkt — mindestens %d Tage" % bester)
p(schlimmster <= 8, "Nie zäh — höchstens %d Tage (von Hand geht es schneller)" %
schlimmster)
p(Game.einsicht >= 3, "Der erfüllte Traum bringt 3 Einsicht (%d)" % Game.einsicht)
p(Game.offene_techniken() > 1,
"Und öffnet neue Wege: %d entdeckt" % Game.offene_techniken())
## Nach jedem Einsatz braucht eine Technik Ruhe. Ein Weg allein deckt daher
## keinen ganzen Tag ab — das ist der Grund, mehrere zu lernen.
func test_cooldown() -> void:
abschnitt("Techniken haben einen Cooldown")
_frei()
Game.known = {"pomodoro": 28, "zerlegen": 28}
Game.active_mission = "mensch_0"
Game.progress["mensch_0"] = 50.0
p(Game.bereit("pomodoro"), "Frisch ist eine Technik einsatzbereit")
var erste := Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
p(erste["ok"], "Der erste Einsatz geht durch")
p(not Game.bereit("pomodoro"), "Danach ist sie gesperrt")
p(is_equal_approx(Game.cooldown_rest("pomodoro"), Game.TECHNIK_COOLDOWN),
"Und zwar %.0f Sekunden lang" % Game.TECHNIK_COOLDOWN)
var zweite := Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
p(not zweite["ok"], "Ein zweiter Einsatz prallt an der Sperre ab")
p(str(zweite["grund"]).contains("einsatzbereit"), "Mit klarer Begründung")
p(Game.bereit("zerlegen"), "Die Sperre gilt nur für diese eine Technik")
var andere := Game.apply_technique("zerlegen", "mensch_0", "gruebelt", false)
p(andere["ok"], "Eine andere Technik greift weiter")
# Eine Regel bleibt still, solange die Technik gesperrt ist
Game.pipeline.clear()
Game.regel_setzen("mensch_0", "zoegert", "pomodoro")
var still := Game.regel_ausloesen("mensch_0", "zoegert", false)
p(still.is_empty(), "Auch eine Regel läuft nicht gegen die Sperre an")
Game.advance_time(Game.TECHNIK_COOLDOWN * 0.5)
p(not Game.bereit("pomodoro"), "Nach der halben Zeit noch immer gesperrt")
Game.advance_time(Game.TECHNIK_COOLDOWN * 0.6)
p(Game.bereit("pomodoro"), "Nach Ablauf wieder frei")
p(Game.cooldown_rest("pomodoro") == 0.0, "Und der Zähler steht auf null")
var wieder := Game.regel_ausloesen("mensch_0", "zoegert", false)
p(not wieder.is_empty(), "Jetzt greift die Regel wieder")
# Im Anwenden-Panel ist die Sperre sichtbar und der Knopf tot
var lena: Bewohner = null
for m in welt().humans:
if m.data["id"] == "mensch_0":
lena = m
ui().anwenden_panel(lena)
var gesperrt_sichtbar := _knopf_texte().contains("gesperrt")
var knopf_tot := true
for c in ui()._content.get_children():
if c is Button and (c as Button).text.contains("gesperrt") \
and not (c as Button).disabled:
knopf_tot = false
p(gesperrt_sichtbar, "Der Anwenden-Knopf weist die Sperre aus")
p(knopf_tot, "Und lässt sich nicht drücken")
ui().schliessen()
Game.pipeline.clear()
_frei()
## Wie gut eine Technik im Moment steht, sagt das Spiel nicht. Es nennt nur,
## was zum Thema und was zum Moment passt — entscheiden muss der Spieler.
func test_chance_verdeckt() -> void:
abschnitt("Die Erfolgschance bleibt verdeckt")
_frei()
Game.known = {"pomodoro": 28, "zerlegen": 28}
Game.active_mission = "mensch_0"
Game.pipeline.clear()
Game.regel_setzen("mensch_0", "zoegert", "pomodoro")
ui().regel_panel("mensch_0", "zoegert")
var knoepfe := _knopf_texte()
p(not knoepfe.contains("%"), "Kein Knopf der Technikwahl nennt eine Prozentzahl")
p(not _panel_text().contains("Erfolgschance"),
"Und das Wort Erfolgschance kommt nicht vor")
var wahl := _panel_text()
p(not wahl.contains("passt zum Thema") and not wahl.contains("passender Moment")
and not wahl.contains("weder Thema"),
"Auch kein Hinweis, ob Thema oder Moment passen")
p(not wahl.contains("Wann:") and not wahl.contains("Zu sehen an:"),
"Die Auswahl zeigt nur die Namen, keine Beschreibung")
p(_knopf_texte().contains("Pomodoro-Technik · Stufe"),
"Jede Wahl steht als Name mit Stufe da")
ui().schliessen()
ui().pipeline_panel("mensch_0")
var zeilen := _knopf_texte()
p(not zeilen.contains("%"), "Auch die Regelzeilen nennen keine Chance")
p(zeilen.contains("Pomodoro-Technik"), "Sie nennen nur die eingehängte Technik")
ui().schliessen()
var lena: Bewohner = null
for m in welt().humans:
if m.data["id"] == "mensch_0":
lena = m
ui().anwenden_panel(lena)
var anwenden := _panel_text()
p(not _knopf_texte().contains("%"), "Und der Anwenden-Knopf nennt keine Chance")
p(_knopf_texte().contains("Pomodoro-Technik · Stufe"),
"Er trägt nur Name und Stufe")
p(not anwenden.contains("passt zum Thema") and not anwenden.contains("passender Moment"),
"Auch beim Anwenden steht nicht da, ob es passt")
p(not anwenden.contains("Hilft bei") and not anwenden.contains("Momente:"),
"Und keine Liste der passenden Themen oder Momente")
p(not anwenden.contains("Wann:") and not anwenden.contains("Zu sehen an:"),
"Auch dort nur Namen — die Beschreibung steht im Journal")
p(anwenden.contains("Journal"), "Und das Panel sagt, wo sie steht")
ui().schliessen()
ui().journal_panel("techniken")
var buch := _panel_text()
p(not buch.contains("Hilft bei") and not buch.contains("Momente:"),
"Auch das Journal verrät die Passung nicht")
p(buch.contains("Wann:") and buch.contains("Zu sehen an:"),
"Das Journal dagegen führt Was, Wann, Warum und Zu sehen an")
ui().schliessen()
ui().brett_panel()
p(not _panel_text().contains("Themen:"),
"Und das Brett listet die Themen des Menschen nicht auf")
ui().schliessen()
Game.pipeline.clear()
## Wenn nur lösbare Missionen aushängen, darf das Brett nie ganz leer werden —
## sonst hinge Loop 2 fest: ohne Traum kein neuer Weg, ohne Weg kein Aushang.
func test_brett_nie_leer() -> void:
abschnitt("Das Brett läuft nie leer")
var merk_c: Array = Game.completed.duplicate()
var merk_k: Dictionary = Game.known.duplicate()
var leer_bei := -1
var kleinste := 99
var stufen := 14
for stufe in range(stufen):
# Ein Spieler, der alles lernt, was das Dorf hergibt
for t in TechDB.LIST:
if Game.technik_offen(str(t["id"])) and not Game.knows(str(t["id"])):
Game.known[str(t["id"])] = 0
var offen: Array = Game.offene_missionen()
kleinste = mini(kleinste, offen.size())
if offen.is_empty():
leer_bei = stufe
break
Game.completed.append(str(offen[0]))
Game.welt_erzeugen()
p(leer_bei < 0, "Über %d Ausbaustufen hängt immer etwas aus%s" %
[stufen, "" if leer_bei < 0 else " — leer bei Stufe %d" % leer_bei])
p(kleinste >= 1, "Im knappsten Fall bleibt %d Aushang übrig" % kleinste)
# Und der jeweils neueste Mensch ist mit dem neuesten Weg zu schaffen
Game.completed.clear()
Game.known.clear()
Game.welt_erzeugen()
var passt := 0
for i in range(TechDB.LIST.size()):
Game.completed.clear()
for k in range(i):
Game.completed.append("stufe_%d" % k)
Game.welt_erzeugen()
Game.known = {str(TechDB.LIST[i]["id"]): 0}
if Game.machbar("mensch_%d" % i):
passt += 1
p(passt == TechDB.LIST.size(),
"Zu jedem der %d Wege gibt es den passenden Menschen (%d)" %
[TechDB.LIST.size(), passt])
Game.completed.assign(merk_c)
Game.known = merk_k
Game.welt_erzeugen()
## Die Probe auf den Kern: wer die Technik liest und sie nur dort einhängt, wo
## ihr "Wann" trifft, kommt schneller ans Ziel als wer sie überall hinhängt.
## Ohne diesen Abstand wäre das Lesen umsonst.
func _traum_tage(saat: int, alles: bool) -> int:
seed(saat)
Game.cooldown.clear()
Game.known.clear()
Game.used.clear()
Game.pipeline.clear()
Game.progress.clear()
Game.completed.clear()
Game.active_mission = ""
Game.welt_erzeugen()
var weg := str(_basiswege()[0])
Game.learn(weg)
Game.accept_mission("mensch_0")
var t := TechDB.by_id(weg)
for a in HumanDB.aktivitaeten("mensch_0"):
if not Game.erreichbar(a):
continue
if alles or (t["momente"] as Array).has(a):
Game.regel_setzen("mensch_0", a, weg)
main._erde()
await get_tree().process_frame
main.player.global_position = Vector3(0, 30.0, 60.0)
Game.time_h = 7.0
Game.day = 1
for i in range(200000):
schritte(1)
if Game.completed.has("mensch_0") or Game.day > 14:
break
return Game.day if Game.completed.has("mensch_0") else 99
func test_lesen_lohnt_sich() -> void:
abschnitt("Die Technik zu lesen zahlt sich aus")
var gezielt: Array = []
var blind: Array = []
for saat in [7, 555, 90210]:
gezielt.append(await _traum_tage(saat, false))
blind.append(await _traum_tage(saat, true))
var g := 0.0
var b := 0.0
for i in range(gezielt.size()):
g += float(gezielt[i])
b += float(blind[i])
g /= float(gezielt.size())
b /= float(blind.size())
p(b >= g * 1.25,
"Gezielt gesetzt geht es deutlich schneller: %.1f statt %.1f Tage (gezielt %s, blind %s)" %
[g, b, str(gezielt), str(blind)])
var schlechter := 0
for i in range(gezielt.size()):
if int(blind[i]) < int(gezielt[i]):
schlechter += 1
p(schlechter == 0,
"Und blind gesetzt ist in keinem der %d Läufe besser (%d)" % [gezielt.size(), schlechter])
# Der Grund: ein falscher Moment ist unterm Strich ein Verlustgeschäft
Game.known = {"pomodoro": 0}
var gut := Game.evaluate("pomodoro", "mensch_0", "zoegert", false)
var falsch := Game.evaluate("pomodoro", "mensch_0", "gruebelt", false)
p(gut["moment"] and not falsch["moment"], "»zögert« ist Pomodoros Moment, »grübelt« nicht")
var ertrag_gut := (8.0 + 2.0) + 5.0 + 5.0
var ertrag_falsch := (8.0 + 2.0) + 5.0
var ev_gut: float = float(gut["chance"]) * ertrag_gut \
- (1.0 - float(gut["chance"])) * Game.FEHLVERSUCH_VERLUST
var ev_falsch: float = float(falsch["chance"]) * ertrag_falsch \
- (1.0 - float(falsch["chance"])) * Game.FEHLVERSUCH_VERLUST
p(ev_gut > 0.0, "Im richtigen Moment trägt sie (%.1f je Einsatz)" % ev_gut)
p(ev_falsch < 0.0, "Im falschen kostet sie (%.1f je Einsatz)" % ev_falsch)
## Warten soll nicht wehtun: 1, 2, 3 stellen das Tempo. Über der Nacht des
## Menschen springt es von allein auf drei — dort ist ohnehin nichts zu holen.
func test_tempo() -> void:
abschnitt("Tempo lässt sich stellen")
Game.completed.clear()
Game.known = {"pomodoro": 0}
Game.welt_erzeugen()
Game.active_mission = ""
Game.tempo = 1
p(is_equal_approx(Game.zeit_faktor(), 1.0), "Ohne Zutun läuft die Welt einfach")
# Wie in Die Sims: die Stufe ist nicht der Faktor
var erwartet := {1: 1.0, 2: 3.0, 3: 7.0}
for stufe in [2, 3, 1]:
Game.tempo_setzen(stufe)
p(Game.tempo == stufe and is_equal_approx(Game.zeit_faktor(), float(erwartet[stufe])),
"Stufe %d läuft %.0f-fach" % [stufe, Game.zeit_faktor()])
Game.tempo_setzen(3)
p(not is_equal_approx(Game.zeit_faktor(), 3.0),
"Stufe 3 heißt gerade nicht dreifach (%.0f×)" % Game.zeit_faktor())
Game.tempo_setzen(1)
Game.tempo_setzen(9)
p(Game.tempo == 3, "Höher als drei geht nicht (%d)" % Game.tempo)
Game.tempo_setzen(0)
p(Game.tempo == 1, "Und tiefer als eins auch nicht (%d)" % Game.tempo)
# Über der Nacht des Menschen springt es von allein
Game.accept_mission("mensch_0")
var wach := -1.0
var nacht := -1.0
for stunde in range(24):
Game.time_h = float(stunde) + 0.5
if Game.schlaeft_ziel():
nacht = Game.zeit_faktor()
else:
wach = Game.zeit_faktor()
p(nacht > 0.0 and wach > 0.0, "Lenas Tag hat wache und schlafende Stunden")
p(is_equal_approx(nacht, Game.faktor_von(3)),
"Im Schlaf läuft die Welt auf der höchsten Stufe (%.0f×)" % nacht)
p(is_equal_approx(wach, 1.0), "Wach wieder auf der gewählten Stufe (%.0f)" % wach)
Game.tempo_setzen(1)
Game.time_h = 2.0
p(Game.schlaeft_ziel(), "Um 02:00 schläft sie")
p(is_equal_approx(Game.zeit_faktor(), Game.faktor_von(3)),
"Die eigene Wahl Stufe 1 gilt dann nicht — die Nacht springt")
# Ohne Mission gibt es nichts zu überspringen
Game.active_mission = ""
p(not Game.schlaeft_ziel(), "Ohne Mission kein Nachtsprung")
p(is_equal_approx(Game.zeit_faktor(), 1.0), "Dann zählt nur die Wahl")
# Der Faktor beschleunigt die Uhr wirklich
Game.tempo_setzen(3)
Game.time_h = 8.0
Game.day = 1
var vorher := Game.time_h
Game.advance_time(1.0 * Game.zeit_faktor())
var schnell := Game.time_h - vorher
Game.tempo_setzen(1)
Game.time_h = 8.0
vorher = Game.time_h
Game.advance_time(1.0 * Game.zeit_faktor())
var langsam := Game.time_h - vorher
p(is_equal_approx(schnell, langsam * Game.faktor_von(3)),
"Auf Stufe 3 vergeht %.0f-mal so viel Zeit je Sekunde (%.4f statt %.4f h)" %
[Game.faktor_von(3), schnell, langsam])
# Das HUD sagt, woran man ist
Game.tempo_setzen(2)
ui().journal_panel()
ui().schliessen()
ui()._hud_aktualisieren()
p(ui()._hud.text.contains("Tempo 2 (3×)"),
"Das HUD nennt Stufe und Faktor getrennt")
Game.accept_mission("mensch_0")
Game.time_h = 2.0
ui()._hud_aktualisieren()
p(ui()._hud.text.contains("Nachtsprung"), "Und weist den Nachtsprung aus")
Game.tempo_setzen(1)
Game.active_mission = ""
## Das Dorf wird nicht gesetzt, sondern wächst. Also muss über viele
## Ausbaustufen gelten: nichts überlappt, kein Weg führt durch ein Haus, alles
## hängt zusammen, und was einmal steht, bleibt stehen.
func test_dorf_organisch() -> void:
abschnitt("Das Dorf wächst organisch")
var merker: Array = Game.completed.duplicate()
var stufen := 24
var ueberlappung := ""
var wegtreffer := ""
var lose := ""
var ohne_anker := ""
var unerreichbar := ""
var sektoren_12 := 0
var dichte := ""
for stufe in range(stufen + 1):
Game.completed.clear()
for i in range(stufe):
Game.completed.append("stufe_%d" % i)
Game.welt_erzeugen()
var netz: Netz = DreamDB.NETZ
var keys: Array = []
for key in DreamDB.PLACES:
if float(DreamDB.PLACES[key]["size"][0]) > 0.0:
keys.append(key)
# nichts steht ineinander
for a in range(keys.size()):
for b in range(a + 1, keys.size()):
var ca := Orte.center(DreamDB.PLACES, keys[a])
var cb := Orte.center(DreamDB.PLACES, keys[b])
var sa: Array = DreamDB.PLACES[keys[a]]["size"]
var sb: Array = DreamDB.PLACES[keys[b]]["size"]
if absf(ca.x - cb.x) < (float(sa[0]) + float(sb[0])) * 0.5 \
and absf(ca.z - cb.z) < (float(sa[1]) + float(sb[1])) * 0.5 \
and ueberlappung == "":
ueberlappung = ("%s und %s bei %d Häusern"
% [keys[a], keys[b], stufe])
# kein Weg führt durch ein Gebäude
for k in netz.kanten:
for key in keys:
var s2: Array = DreamDB.PLACES[key]["size"]
if Netz.trifft_rechteck(netz.knoten[k[0]], netz.knoten[k[1]],
Orte.center(DreamDB.PLACES, key),
Vector2(float(s2[0]), float(s2[1])), 0.0) and wegtreffer == "":
wegtreffer = "%s bei %d Häusern" % [key, stufe]
if not netz.zusammenhaengend() and lose == "":
lose = "bei %d Häusern" % stufe
# jedes Haus hängt am Netz und ist vom Dorfplatz aus zu erreichen
for i in range(stufe):
var key3 := DreamDB.haus_key(i)
if not DreamDB.ANKER.has(key3):
if ohne_anker == "":
ohne_anker = "%s bei %d Häusern" % [key3, stufe]
continue
if netz.weglaenge(int(DreamDB.ANKER["lichtung"]), int(DreamDB.ANKER[key3])) == INF \
and unerreichbar == "":
unerreichbar = "%s bei %d Häusern" % [key3, stufe]
if stufe == 12:
var sekt: Dictionary = {}
for i in range(stufe):
var c := Orte.center(DreamDB.PLACES, DreamDB.haus_key(i))
sekt[int(floor((atan2(c.z, c.x) + PI) / (TAU / 8.0))) % 8] = true
sektoren_12 = sekt.size()
if stufe == stufens_probe():
var flaeche := PI * pow(Game.insel_radius(), 2.0)
if flaeche / float(stufe) > 2200.0:
dichte = "%.0f m² je Haus" % (flaeche / float(stufe))
p(ueberlappung == "", "Über %d Ausbaustufen steht kein Gebäude im anderen%s" %
[stufen, "" if ueberlappung == "" else " — " + ueberlappung])
p(wegtreffer == "", "Und kein Weg führt durch ein Gebäude%s" %
["" if wegtreffer == "" else " — " + wegtreffer])
p(lose == "", "Das Wegenetz hängt immer zusammen%s" %
["" if lose == "" else " — " + lose])
p(ohne_anker == "", "Jedes Haus hängt am Netz%s" %
["" if ohne_anker == "" else " — " + ohne_anker])
p(unerreichbar == "", "Und ist vom Dorfplatz aus erreichbar%s" %
["" if unerreichbar == "" else " — " + unerreichbar])
p(sektoren_12 >= 6,
"Bei 12 Häusern ist das Dorf rundum gewachsen (%d von 8 Richtungen)" % sektoren_12)
p(dichte == "", "Die Insel bleibt dicht besiedelt%s" %
["" if dichte == "" else " — " + dichte])
# Verwinkelt heißt: Gabeln und Schleifen, nicht Speichen zur Mitte
Game.completed.clear()
for i in range(18):
Game.completed.append("stufe_%d" % i)
Game.welt_erzeugen()
var netz2: Netz = DreamDB.NETZ
var gabeln := 0
for i in range(netz2.knoten.size()):
if netz2.nachbarn(i).size() >= 3:
gabeln += 1
var schleifen: int = netz2.kanten.size() - netz2.knoten.size() + 1
p(gabeln >= 6, "Bei 18 Häusern gabelt sich das Netz %d-mal" % gabeln)
p(schleifen >= 2, "Und trägt %d Schleifen statt lauter Sackgassen" % schleifen)
# Was einmal steht, bleibt stehen
var vorher: Array = []
for i in range(18):
vorher.append(Orte.center(DreamDB.PLACES, DreamDB.haus_key(i)))
Game.completed.append("stufe_18")
Game.welt_erzeugen()
var verrutscht := 0
for i in range(18):
if vorher[i].distance_to(Orte.center(DreamDB.PLACES, DreamDB.haus_key(i))) > 0.01:
verrutscht += 1
p(verrutscht == 0, "Das 19. Haus verschiebt keines der 18 alten (%d)" % verrutscht)
Game.completed.assign(merker)
Game.welt_erzeugen()
func stufens_probe() -> int:
return 24
## Die Erde ist ein erzeugter Bezirk. Also muss gelten: Parzellen liegen
## nebeneinander statt ineinander, jede hat Straßenanschluss, das Netz hängt
## zusammen -- und anders als im Dorf wächst hier nichts.
func test_bezirk() -> void:
abschnitt("Die Erde ist ein Bezirk")
var netz: Netz = HumanDB.NETZ
p(HumanDB.PARZELLEN.size() >= 60,
"Der Bezirk ist in %d Parzellen geteilt" % HumanDB.PARZELLEN.size())
var ueber := 0
for i in range(HumanDB.PARZELLEN.size()):
for j in range(i + 1, HumanDB.PARZELLEN.size()):
var a: Dictionary = HumanDB.PARZELLEN[i]
var b: Dictionary = HumanDB.PARZELLEN[j]
if absf(a["pos"].x - b["pos"].x) < (a["size"].x + b["size"].x) * 0.5 - 0.05 \
and absf(a["pos"].z - b["pos"].z) < (a["size"].y + b["size"].y) * 0.5 - 0.05:
ueber += 1
p(ueber == 0, "Keine Parzelle steht in einer anderen (%d)" % ueber)
var ohne := 0
var weiteste := 0.0
for pz in HumanDB.PARZELLEN:
var d := INF
for st in HumanDB.STRASSEN:
var f := Geometry3D.get_closest_point_to_segment(pz["pos"], st["a"], st["b"])
d = minf(d, f.distance_to(pz["pos"]) - maxf(pz["size"].x, pz["size"].y) * 0.5
- float(st["breite"]) * 0.5)
weiteste = maxf(weiteste, d)
if d > 2.0:
ohne += 1
p(ohne == 0, "Jede Parzelle liegt an einer Straße (weiteste Lücke %.1f m)" % weiteste)
p(netz.zusammenhaengend(),
"Das Straßennetz hängt zusammen (%d Knoten, %d Kanten)" %
[netz.knoten.size(), netz.kanten.size()])
var unerreichbar := 0
var keys: Array = HumanDB.PLACES.keys()
for i in range(keys.size()):
for j in range(i + 1, keys.size()):
if netz.weglaenge(int(HumanDB.ANKER[keys[i]]), int(HumanDB.ANKER[keys[j]])) == INF:
unerreichbar += 1
p(unerreichbar == 0, "Jeder Ort ist von jedem anderen aus zu erreichen (%d Lücken)" %
unerreichbar)
# Arterien im Raster, roji schmal und als Sackgasse ins Blockinnere
var arterien := 0
var gassen := 0
for st in HumanDB.STRASSEN:
if float(st["breite"]) >= HumanDB.ARTERIE:
arterien += 1
else:
gassen += 1
p(arterien == HumanDB.ACHSEN.size() * 2,
"%d Arterien spannen das Raster auf" % arterien)
p(gassen >= 4, "Und %d roji führen ins Blockinnere" % gassen)
p(HumanDB.ROJI < HumanDB.ARTERIE * 0.6,
"Eine roji ist mit %.1f m deutlich schmaler als eine Arterie (%.1f m)" %
[HumanDB.ROJI, HumanDB.ARTERIE])
var sackgassen := 0
for st in HumanDB.STRASSEN:
if float(st["breite"]) < HumanDB.ARTERIE:
var ende: Vector3 = st["b"]
if netz.nachbarn(netz.fuege_knoten(ende)).size() <= 1:
sackgassen += 1
p(sackgassen >= gassen / 2, "Die meisten roji enden blind (%d von %d)" %
[sackgassen, gassen])
# Alle Menschen sind da, und sie werden nicht mehr
var vorher := HumanDB.LIST.size()
p(vorher == HumanDB.EINWOHNER, "Alle %d Menschen sind von Anfang an da" % vorher)
var merker: Array = Game.completed.duplicate()
for i in range(6):
Game.completed.append("stufe_%d" % i)
Game.welt_erzeugen()
p(HumanDB.LIST.size() == vorher,
"Sechs erfüllte Träume ändern daran nichts (%d)" % HumanDB.LIST.size())
Game.completed.assign(merker)
Game.welt_erzeugen()
# Jeder wohnt in einer eigenen Wohnung
var heime: Dictionary = {}
for m in HumanDB.LIST:
heime[str(m["heim"])] = true
p(heime.size() == HumanDB.LIST.size(),
"Jeder Mensch hat seine eigene Wohnung (%d)" % heime.size())
# Der mittlere Block bleibt frei
var im_park := 0
var halb: float = (float(HumanDB.ACHSEN[2]) - float(HumanDB.ACHSEN[1])) * 0.5 \
- HumanDB.ARTERIE
for pz in HumanDB.PARZELLEN:
var q: Vector3 = pz["pos"]
if absf(q.x) < halb and absf(q.z) < halb:
im_park += 1
p(im_park == 0, "Im mittleren Block steht kein Haus — er ist der Park (%d)" % im_park)