update
This commit is contained in:
770
tests/verify.gd
770
tests/verify.gd
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user