1094 lines
40 KiB
GDScript
1094 lines
40 KiB
GDScript
extends Node
|
||
## Fährt das echte Spiel headless und prüft die Implementierungen.
|
||
|
||
var fehler := 0
|
||
var main: Node3D
|
||
|
||
|
||
func p(ok: bool, text: String) -> void:
|
||
print((" OK " if ok else " FEHLT ") + text)
|
||
if not ok:
|
||
fehler += 1
|
||
|
||
|
||
func abschnitt(t: String) -> void:
|
||
print("\n== " + t)
|
||
|
||
|
||
func _ready() -> void:
|
||
seed(20240)
|
||
main = load("res://scenes/main.tscn").instantiate()
|
||
add_child(main)
|
||
await get_tree().process_frame
|
||
await get_tree().process_frame
|
||
|
||
await test_start()
|
||
await test_journal()
|
||
await test_traumdorf()
|
||
await test_haus_betreten()
|
||
await test_hoersaal()
|
||
await test_mission_annehmen()
|
||
await test_reise_zur_erde()
|
||
await test_tagesablauf()
|
||
await test_wege_machbar()
|
||
await test_geist()
|
||
await test_technik_anwenden()
|
||
await test_pipeline()
|
||
await test_mission_abschliessen()
|
||
await test_welt_waechst()
|
||
await test_techniken_verbessern_sich()
|
||
await test_journal_ueberall()
|
||
await test_steuerung()
|
||
await test_erster_traum_schaffbar()
|
||
|
||
print("\n=========================================")
|
||
print("Fehlende Prüfungen: %d" % fehler)
|
||
print("=========================================")
|
||
get_tree().quit(1 if fehler > 0 else 0)
|
||
|
||
|
||
func ui() -> UI:
|
||
return main.ui
|
||
|
||
|
||
func welt() -> Node3D:
|
||
return main.welt
|
||
|
||
|
||
func schritte(n: int, dt: float = 0.05, zeit: float = -1.0) -> void:
|
||
for i in range(n):
|
||
if zeit >= 0.0:
|
||
Game.time_h = zeit
|
||
main._process(dt)
|
||
if welt().has_method("_process"):
|
||
welt()._process(dt)
|
||
for kind in welt().get_children():
|
||
if kind is Bewohner:
|
||
kind._process(dt)
|
||
|
||
|
||
func _panel_text() -> String:
|
||
var out: Array = []
|
||
for c in ui()._content.get_children():
|
||
if c is RichTextLabel:
|
||
out.append((c as RichTextLabel).get_parsed_text())
|
||
return "\n".join(out)
|
||
|
||
|
||
func _journal_text() -> String:
|
||
return _panel_text()
|
||
|
||
|
||
## Welche Gebäude stehen wirklich in der Traumwelt? Erkennbar an ihren Schildern.
|
||
## Was der Hörsaal von Anfang an lehrt.
|
||
func _basiswege() -> Array:
|
||
return DreamDB.techniken_von("hoersaal")
|
||
|
||
|
||
func _gebaute_orte() -> Array:
|
||
var namen: Array = []
|
||
for key in DreamDB.PLACES:
|
||
if float(DreamDB.PLACES[key]["size"][0]) <= 0.0:
|
||
continue
|
||
namen.append(str(DreamDB.PLACES[key]["name"]))
|
||
var da: Array = []
|
||
for n in welt().get_children():
|
||
if n is Label3D and namen.has((n as Label3D).text):
|
||
da.append((n as Label3D).text)
|
||
return da
|
||
|
||
|
||
func _marke(kind: String) -> Node3D:
|
||
for n in get_tree().get_nodes_in_group("interactable"):
|
||
if n.get_meta("kind") == kind:
|
||
return n
|
||
return null
|
||
|
||
|
||
## Stellt den Spieler vor eine Marke und lässt den Fokus neu suchen.
|
||
func _hingehen(ziel: Node3D) -> void:
|
||
main.player.set_mode(Player.Mode.TRAUM)
|
||
main.player.global_position = ziel.global_position + Vector3(0, 0, 1.2)
|
||
main.player.velocity = Vector3.ZERO
|
||
ui().schliessen()
|
||
main._process(0.05)
|
||
|
||
|
||
## Drückt den ersten aktiven Knopf, dessen Beschriftung so beginnt.
|
||
func _knopf_texte() -> String:
|
||
var out: Array = []
|
||
for c in ui()._content.get_children():
|
||
if c is Button:
|
||
out.append((c as Button).text)
|
||
return "\n".join(out)
|
||
|
||
|
||
func _knopf_da(anfang: String) -> bool:
|
||
for c in ui()._content.get_children():
|
||
if c is Button and (c as Button).text.begins_with(anfang):
|
||
return true
|
||
return false
|
||
|
||
|
||
func _druecke(anfang: String) -> bool:
|
||
for c in ui()._content.get_children():
|
||
if c is Button and not (c as Button).disabled and (c as Button).text.begins_with(anfang):
|
||
(c as Button).pressed.emit()
|
||
return true
|
||
return false
|
||
|
||
|
||
func laufe(von: Vector3, yaw: float, frames: int) -> Vector3:
|
||
main.player.set_mode(Player.Mode.TRAUM)
|
||
main.player.look_enabled = true
|
||
main.player.global_position = von
|
||
main.player.velocity = Vector3.ZERO
|
||
main.player.yaw = yaw
|
||
Input.action_press("vor")
|
||
for i in range(frames):
|
||
await get_tree().physics_frame
|
||
Input.action_release("vor")
|
||
return main.player.global_position
|
||
|
||
|
||
# --------------------------------------------------------------------
|
||
|
||
func test_start() -> void:
|
||
abschnitt("Start")
|
||
p(not ui().offen, "Kein Startbildschirm — das Spiel läuft sofort")
|
||
p(welt() is DreamWorld, "Startwelt ist die Traumwelt")
|
||
p(main.player != null and main.player.mode == Player.Mode.TRAUM, "Spieler läuft (kein Geist)")
|
||
var arten: Dictionary = {}
|
||
for n in get_tree().get_nodes_in_group("interactable"):
|
||
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((welt() as DreamWorld).bewohner.is_empty(),
|
||
"Das Traumdorf steht am Anfang leer (%d Wesen)" %
|
||
(welt() as DreamWorld).bewohner.size())
|
||
var lebt := false
|
||
for n in welt().get_children():
|
||
if n is Bewohner:
|
||
lebt = true
|
||
p(not lebt, "Auch kein Wesen im Szenenbaum")
|
||
|
||
var gebaut := _gebaute_orte()
|
||
p(gebaut.size() == 1 and gebaut.has("Hörsaal der Wege"),
|
||
"Nur der Hörsaal steht als Gebäude (%s)" % ", ".join(gebaut))
|
||
|
||
|
||
func test_journal() -> void:
|
||
abschnitt("Journal")
|
||
for t in TechDB.LIST:
|
||
var vollstaendig: bool = t["was"] != "" and t["wann"] != "" and t["warum"] != ""
|
||
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())
|
||
|
||
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")
|
||
ui().journal_panel("missionen")
|
||
p(_journal_text().contains("Schwarzen Brett"),
|
||
"Ohne Mission verweist das Journal auf das Schwarze Brett")
|
||
p(not _knopf_da("Mission annehmen"), "Das Journal vergibt keine Missionen")
|
||
ui().schliessen()
|
||
|
||
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(not Game.learn(str(TechDB.LIST[1]["id"])),
|
||
"Der zweite Weg ist trotz Einsicht nirgends zu lernen")
|
||
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")
|
||
Game.add_xp(erster, 24)
|
||
p(Game.skill_of(erster) == 5, "28 EP erreichen Stufe 5")
|
||
Game.known[erster] = 0
|
||
|
||
ui().journal_panel("techniken")
|
||
var buch := _journal_text()
|
||
var alle_gelernten := true
|
||
for id in Game.known:
|
||
if not buch.contains(str(TechDB.by_id(id)["name"])):
|
||
alle_gelernten = false
|
||
p(alle_gelernten, "Das Journal führt alle %d gelernten Techniken" % Game.known.size())
|
||
var fremde := 0
|
||
for t in TechDB.LIST:
|
||
if not Game.knows(t["id"]) and buch.contains(str(t["name"])):
|
||
fremde += 1
|
||
p(fremde == 0, "Ungelernte Techniken stehen nicht im Journal (%d)" % fremde)
|
||
ui().schliessen()
|
||
|
||
|
||
func test_traumdorf() -> void:
|
||
abschnitt("Traumwesen ziehen ein und leben ihren Tag")
|
||
p(welt().bewohner.is_empty(), "Vorher wohnt niemand hier")
|
||
for i in range(4):
|
||
Game.completed.append("probe_%d" % i)
|
||
Game.welt_erzeugen()
|
||
main._traumwelt()
|
||
await get_tree().process_frame
|
||
if welt().bewohner.size() != i + 1:
|
||
p(false, "Nach %d Träumen wohnen %d Wesen hier" %
|
||
[i + 1, welt().bewohner.size()])
|
||
p(welt().bewohner.size() == 4,
|
||
"Je erfülltem Traum zieht eines ein, hier sind es vier (%d)" %
|
||
welt().bewohner.size())
|
||
var wesen: Array = welt().bewohner
|
||
|
||
var haeuser := 0
|
||
for key in DreamDB.PLACES:
|
||
if key.begins_with("haus_"):
|
||
haeuser += 1
|
||
p(haeuser == 4, "Vier Häuser sind erzeugt (%d)" % haeuser)
|
||
p(float(DreamDB.PLACES["hoersaal"]["size"][0]) > 0.0, "Dazu steht der Hörsaal")
|
||
|
||
var eigene: Dictionary = {}
|
||
for w in wesen:
|
||
var haus: String = str(w.data["haus"])
|
||
p(DreamDB.PLACES.has(haus), "%s hat ein eigenes Haus (%s)" % [w.data["name"], haus])
|
||
eigene[haus] = true
|
||
var kommt_vor := false
|
||
for e in w.data["routine"]:
|
||
if e[1] == haus:
|
||
kommt_vor = true
|
||
p(kommt_vor, "Die Routine von %s führt ins eigene Haus" % w.data["name"])
|
||
p(eigene.size() == 4, "Jedes Wesen hat ein anderes Haus")
|
||
|
||
var luecken := 0
|
||
for w in wesen:
|
||
for h in range(24):
|
||
var r: Array = w._routine_at(float(h) + 0.5)
|
||
if not DreamDB.PLACES.has(r[0]) or not Orte.ACTIVITY_TEXT.has(r[1]):
|
||
luecken += 1
|
||
p(luecken == 0, "Alle vier haben rund um die Uhr Ort und Tätigkeit")
|
||
|
||
# Ein voller Tag am Stück: niemand bleibt hängen
|
||
Game.time_h = 0.0
|
||
Game.day = 1
|
||
var besucht: Dictionary = {}
|
||
for w in wesen:
|
||
besucht[w.data["id"]] = {}
|
||
for i in range(9000):
|
||
schritte(1)
|
||
for w in wesen:
|
||
if w._path.is_empty():
|
||
besucht[w.data["id"]][w.place] = true
|
||
if Game.time_h >= 23.5:
|
||
break
|
||
var alle_ok := true
|
||
for w in wesen:
|
||
var soll: Dictionary = {}
|
||
for e in w.data["routine"]:
|
||
soll[e[1]] = true
|
||
var ist: int = besucht[w.data["id"]].size()
|
||
if ist < soll.size():
|
||
alle_ok = false
|
||
p(false, "%s erreichte nur %d von %d Stationen" % [w.data["name"], ist, soll.size()])
|
||
p(alle_ok, "Über einen ganzen Tag erreicht jedes Wesen jede Station seiner Routine")
|
||
|
||
var zuhause := 0
|
||
schritte(20, 0.05, 1.0)
|
||
for w in wesen:
|
||
var r: Array = w._routine_at(1.0)
|
||
if w.place == r[0]:
|
||
zuhause += 1
|
||
p(zuhause == 4, "Um 01:00 sind alle vier an ihrem Nachtplatz (%d)" % zuhause)
|
||
|
||
# Ausgangslage wiederherstellen: das Dorf ist wieder leer
|
||
Game.completed.clear()
|
||
Game.welt_erzeugen()
|
||
main._traumwelt()
|
||
await get_tree().process_frame
|
||
|
||
|
||
func test_haus_betreten() -> void:
|
||
abschnitt("Orte betreten")
|
||
p(not Game.ort_offen(DreamDB.haus_key(0)), "Ohne Bewohner steht kein Haus")
|
||
Game.completed.append("probe_haus")
|
||
Game.welt_erzeugen()
|
||
main._traumwelt()
|
||
await get_tree().process_frame
|
||
var haus_name := str(DreamDB.PLACES[DreamDB.haus_key(0)]["name"])
|
||
p(_gebaute_orte().has(haus_name), "Mit dem ersten Bewohner steht sein Haus: %s" % haus_name)
|
||
var haus := Orte.center(DreamDB.PLACES, DreamDB.haus_key(0))
|
||
var tuer := Orte.door(DreamDB.PLACES, DreamDB.haus_key(0))
|
||
var hinein: Vector3 = (haus - tuer).normalized()
|
||
var yaw_hinein := atan2(-hinein.x, -hinein.z)
|
||
|
||
var ziel: Vector3 = await laufe(tuer - hinein * 1.5 + Vector3(0, 1.0, 0), yaw_hinein, 80)
|
||
var drin: bool = absf(ziel.x - haus.x) < 6.0 and absf(ziel.z - haus.z) < 5.0
|
||
p(drin, "Durch die Tür läuft man ins Haus (Ziel %.1f / %.1f, Haus %.1f / %.1f)" %
|
||
[ziel.x, ziel.z, haus.x, haus.z])
|
||
|
||
# Die Wand hält: von der türabgewandten Seite kommt niemand hinein
|
||
var rueck := haus + hinein * 11.0 + Vector3(0, 1.0, 0)
|
||
var gestoppt: Vector3 = await laufe(rueck, atan2(hinein.x, hinein.z), 80)
|
||
var drin2: bool = absf(gestoppt.x - haus.x) < 6.0 and absf(gestoppt.z - haus.z) < 5.0
|
||
p(not drin2, "Die Rückwand hält den Spieler auf (%.1f / %.1f)" % [gestoppt.x, gestoppt.z])
|
||
|
||
main.player.set_mode(Player.Mode.TRAUM)
|
||
main.player.global_position = (welt() as DreamWorld).spawn
|
||
Game.completed.clear()
|
||
Game.welt_erzeugen()
|
||
main._traumwelt()
|
||
await get_tree().process_frame
|
||
|
||
|
||
func test_hoersaal() -> void:
|
||
abschnitt("Techniken werden im Hörsaal gelehrt")
|
||
var pult := _marke("lehre")
|
||
p(pult != null, "Der Hörsaal hat ein Lehrpult")
|
||
var raum := Orte.center(DreamDB.PLACES, "hoersaal")
|
||
p(absf(pult.global_position.x - raum.x) < 8.0 and absf(pult.global_position.z - raum.z) < 6.0,
|
||
"Das Pult steht im Hörsaal (%.1f / %.1f)" % [pult.global_position.x, pult.global_position.z])
|
||
|
||
var im_saal: Vector3 = await laufe(Vector3(0, 1.0, 24.0), PI, 150)
|
||
p(im_saal.z > 33.0,
|
||
"Vom Eingang führt ein freier Mittelgang zum Pult (z = %.1f, Pult bei %.1f)" %
|
||
[im_saal.z, raum.z + 3.4])
|
||
p(absf(im_saal.x) < 2.0, "Der Gang hält den Spieler in der Mitte (x = %.1f)" % im_saal.x)
|
||
|
||
_hingehen(pult)
|
||
p(main._fokus == pult, "Das Pult wird als Fokus erkannt")
|
||
main._interagieren()
|
||
p(ui().offen and ui()._kind == "lehre", "Der Hörsaal öffnet sich")
|
||
var seite := _panel_text()
|
||
var basis_da := true
|
||
for id in _basiswege():
|
||
if not seite.contains(str(TechDB.by_id(id)["name"])):
|
||
basis_da = false
|
||
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"):
|
||
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(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
|
||
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")
|
||
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")
|
||
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:
|
||
abschnitt("Mission am Schwarzen Brett annehmen")
|
||
var brett := _marke("brett")
|
||
p(brett != null, "Das Schwarze Brett steht in der Traumwelt")
|
||
var spawn: Vector3 = (welt() as DreamWorld).spawn
|
||
p(absf(brett.global_position.x) > 2.5,
|
||
"Es steht neben der Achse Start–Tor, versperrt sie also nicht (x = %.1f)" %
|
||
brett.global_position.x)
|
||
p(brett.global_position.distance_to(spawn) < 14.0,
|
||
"Vom Start aus in Sichtweite (%.1f)" % brett.global_position.distance_to(spawn))
|
||
var hin: Vector3 = await laufe(Vector3(brett.global_position.x, 1.0, spawn.z), 0.0, 150)
|
||
p(hin.z < 14.0, "Man läuft vom Start ungehindert bis davor (z = %.1f)" % hin.z)
|
||
main._process(0.05)
|
||
p(main._fokus == brett, "Davor steht man in Reichweite")
|
||
ui().schliessen()
|
||
main._interagieren()
|
||
p(ui().offen and ui()._kind == "brett", "Das Brett öffnet sich")
|
||
var alle_aus := true
|
||
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())
|
||
|
||
p(Game.active_mission == "", "Vorher keine Mission")
|
||
var gedrueckt := _druecke("Mission annehmen")
|
||
p(gedrueckt, "Ein Aushang lässt sich annehmen")
|
||
p(Game.active_mission == "mensch_0", "Mission Lena ist aktiv (%s)" % Game.active_mission)
|
||
p(Game.angenommen("mensch_0"), "Lena steht als angenommen im Buch")
|
||
ui().schliessen()
|
||
|
||
ui().journal_panel("missionen")
|
||
var buch := _panel_text()
|
||
p(buch.contains(str(HumanDB.by_id("mensch_0")["name"])),
|
||
"Das Journal führt die angenommene Mission")
|
||
p(not buch.contains(str(HumanDB.by_id("mensch_1")["name"])),
|
||
"Nicht angenommene Menschen fehlen im Journal")
|
||
p(not _knopf_da("Mission annehmen"), "Im Journal lässt sich keine Mission annehmen")
|
||
ui().schliessen()
|
||
|
||
|
||
func test_reise_zur_erde() -> void:
|
||
abschnitt("Reise zur Erde")
|
||
var tor: Node3D = null
|
||
for n in get_tree().get_nodes_in_group("interactable"):
|
||
if n.get_meta("kind") == "erde":
|
||
tor = n
|
||
p(tor != null, "Tor zur Erde existiert")
|
||
|
||
# Zu Fuß hin: die Torplattform darf keine unüberwindliche Stufe sein
|
||
ui().schliessen()
|
||
main.player.set_mode(Player.Mode.TRAUM)
|
||
main.player.look_enabled = true
|
||
main.player.global_position = Vector3(0, 1.0, -20.0)
|
||
main.player.velocity = Vector3.ZERO
|
||
main.player.yaw = 0.0
|
||
Input.action_press("vor")
|
||
var erreicht := false
|
||
var weit: float = main.player.global_position.z
|
||
for i in range(200):
|
||
await get_tree().physics_frame
|
||
main._process(0.02)
|
||
weit = main.player.global_position.z
|
||
if main._fokus == tor:
|
||
erreicht = true
|
||
break
|
||
Input.action_release("vor")
|
||
p(erreicht, "Zu Fuß kommt man ans Tor — keine Stufe im Weg (z = %.1f, Tor bei %.1f)" %
|
||
[weit, tor.global_position.z])
|
||
|
||
Game.teleport_lock = 1.0
|
||
main.player.global_position = tor.global_position + Vector3(0, 0, 1.5)
|
||
main._process(0.05)
|
||
p(main._fokus == tor, "Tor wird als Fokus erkannt")
|
||
main._interagieren()
|
||
await get_tree().process_frame
|
||
p(main.auf_erde and welt() is EarthWorld, "Spieler ist auf der Erde")
|
||
p(welt().humans.size() == HumanDB.LIST.size() and welt().humans.size() >= 3,
|
||
"%d Menschen leben in der Stadt" % welt().humans.size())
|
||
|
||
|
||
func test_tagesablauf() -> void:
|
||
abschnitt("Tagesablauf der Menschen")
|
||
var mensch: Bewohner = null
|
||
for m in welt().humans:
|
||
if m.data["id"] == "mensch_0":
|
||
mensch = m
|
||
p(mensch != null, "Der erste Mensch lebt auf der Erde: %s" % mensch.data["name"])
|
||
|
||
schritte(30, 0.05, 2.0)
|
||
p(mensch.activity == "schlaeft", "Um 02:00 schläft er (%s)" % mensch.activity)
|
||
p(mensch.global_position.distance_to(
|
||
Orte.center(HumanDB.PLACES, str(mensch.data["heim"]))) < 5.0,
|
||
"Und zwar zu Hause")
|
||
|
||
# Jede Station des eigenen Tagesablaufs wird pünktlich erreicht
|
||
var verfehlt: Array = []
|
||
for e in mensch.data["routine"]:
|
||
var stunde: float = float(e[0]) + 0.5
|
||
var da := false
|
||
for i in range(1200):
|
||
schritte(1, 0.05, stunde)
|
||
if mensch.place == e[1] and mensch._path.is_empty():
|
||
da = true
|
||
break
|
||
if not da or mensch.activity != e[2]:
|
||
verfehlt.append("%s um %d Uhr" % [e[1], int(e[0])])
|
||
p(verfehlt.is_empty(), "Alle %d Stationen seines Tages werden erreicht (%s)" %
|
||
[(mensch.data["routine"] as Array).size(),
|
||
"lückenlos" if verfehlt.is_empty() else ", ".join(verfehlt)])
|
||
|
||
var luecken := 0
|
||
for m in welt().humans:
|
||
for h in range(24):
|
||
var r: Array = m._routine_at(float(h) + 0.5)
|
||
if not HumanDB.PLACES.has(r[0]) or not Orte.ACTIVITY_TEXT.has(r[1]):
|
||
luecken += 1
|
||
p(luecken == 0, "Alle %d Menschen haben rund um die Uhr Ort und Tätigkeit" %
|
||
welt().humans.size())
|
||
|
||
Game.time_h = 0.0
|
||
Game.day = 1
|
||
var besucht: Dictionary = {}
|
||
for m in welt().humans:
|
||
besucht[m.data["id"]] = {}
|
||
for i in range(9000):
|
||
schritte(1)
|
||
for m in welt().humans:
|
||
if m._path.is_empty():
|
||
besucht[m.data["id"]][m.place] = true
|
||
if Game.time_h >= 23.5:
|
||
break
|
||
var alle_ok := true
|
||
for m in welt().humans:
|
||
var soll: Dictionary = {}
|
||
for e in m.data["routine"]:
|
||
soll[e[1]] = true
|
||
if besucht[m.data["id"]].size() < soll.size():
|
||
alle_ok = false
|
||
p(false, "%s erreichte nur %d von %d Stationen" %
|
||
[m.data["name"], besucht[m.data["id"]].size(), soll.size()])
|
||
p(alle_ok, "Über einen ganzen Tag erreicht jeder Mensch jede Station seiner Routine")
|
||
|
||
|
||
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)
|
||
|
||
|
||
func _wege_pruefen(welt_name: String, places: Dictionary, liste: Array) -> void:
|
||
var sek_pro_stunde := Game.DAY_SECONDS / 24.0
|
||
var alles_ok := true
|
||
var engste := 999.0
|
||
var engste_text := ""
|
||
for m in liste:
|
||
var r: Array = m["routine"]
|
||
for i in range(r.size()):
|
||
var von: String = String(r[i - 1][1]) if i > 0 else String(r[r.size() - 1][1])
|
||
var nach: String = String(r[i][1])
|
||
if von == nach:
|
||
continue
|
||
var dauer := _weg_laenge(places, von, nach) / 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
|
||
if dauer >= fenster:
|
||
alles_ok = false
|
||
p(false, "%s: %s -> %s braucht %.0f s, Fenster nur %.0f s" %
|
||
[m["name"], von, nach, dauer, fenster])
|
||
elif fenster - dauer < engste:
|
||
engste = fenster - dauer
|
||
engste_text = ("%s: %s -> %s (%.0f s Weg, %.0f s Fenster)"
|
||
% [m["name"], von, nach, dauer, fenster])
|
||
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)
|
||
var l := 0.0
|
||
var pos := Orte.center(places, von)
|
||
for pt in pts:
|
||
l += pos.distance_to(pt)
|
||
pos = pt
|
||
return l
|
||
|
||
|
||
func test_geist() -> void:
|
||
abschnitt("Geist-Bewegung")
|
||
p(main.player.mode == Player.Mode.GEIST, "Spieler ist auf der Erde ein Geist")
|
||
p(main.player.collision_layer == 0 and main.player.collision_mask == 0,
|
||
"Der Geist kollidiert mit nichts — er schwebt durch Wände")
|
||
var vor: Vector3 = main.player.global_position
|
||
main.player.global_position = Vector3(0, 8.0, 0)
|
||
main.player.velocity = Vector3(6, 0, 0)
|
||
for i in range(30):
|
||
await get_tree().physics_frame
|
||
p(main.player.global_position.x > 0.5, "Der Geist bewegt sich frei durch den Raum")
|
||
p(absf(main.player.global_position.y - 8.0) < 0.5, "Ohne Schwerkraft fällt er nicht")
|
||
|
||
var lena: Bewohner = welt().humans[0]
|
||
var davor: Vector3 = lena.global_position
|
||
main.player.global_position = lena.global_position + Vector3(0, 1, 0.5)
|
||
schritte(10)
|
||
p(lena.global_position.distance_to(davor) < 2.0, "Menschen nehmen den Geist nicht wahr")
|
||
|
||
|
||
func test_technik_anwenden() -> void:
|
||
abschnitt("Techniken anwenden — Erfolgschance")
|
||
var lena: Bewohner = null
|
||
for m in welt().humans:
|
||
if m.data["id"] == "mensch_0":
|
||
lena = m
|
||
|
||
# Die Stunde suchen, in der dieser Mensch zögert — sie steht in seiner Routine
|
||
var zoegert_um := -1.0
|
||
for e in lena.data["routine"]:
|
||
if e[2] == "zoegert":
|
||
zoegert_um = float(e[0]) + 0.5
|
||
for i in range(1200):
|
||
schritte(1, 0.05, zoegert_um)
|
||
if lena.activity == "zoegert" and lena._path.is_empty():
|
||
break
|
||
p(lena.activity == "zoegert", "%s zögert um %.0f Uhr (%s)" %
|
||
[lena.data["name"], zoegert_um, lena.activity])
|
||
|
||
main.player.set_mode(Player.Mode.GEIST)
|
||
main.player.global_position = lena.global_position + Vector3(0, 1.0, 1.5)
|
||
main._process(0.05)
|
||
p(main._fokus_mensch == lena, "Der Geist erfasst Lena in Reichweite")
|
||
main._interagieren()
|
||
p(ui().offen and ui()._kind == "anwenden", "Anwenden-Panel öffnet sich")
|
||
ui().schliessen()
|
||
|
||
Game.known["pomodoro"] = 0
|
||
var s1: float = Game.evaluate("pomodoro", "mensch_0", "zoegert", false)["chance"]
|
||
Game.known["pomodoro"] = 28
|
||
var s5: float = Game.evaluate("pomodoro", "mensch_0", "zoegert", false)["chance"]
|
||
p(s5 > s1, "Höherer Skill = höhere Erfolgschance (%.0f%% -> %.0f%%)" % [s1 * 100, s5 * 100])
|
||
Game.known["pomodoro"] = 0
|
||
|
||
var passend: Dictionary = Game.evaluate("pomodoro", "mensch_0", "zoegert", false)
|
||
var unpassend: Dictionary = Game.evaluate("pomodoro", "mensch_0", "essen", false)
|
||
p(passend["moment"] and not unpassend["moment"], "Moment-Erkennung: zögert ja, isst nein")
|
||
p(passend["chance"] > unpassend["chance"], "Passender Moment erhöht die Chance")
|
||
p(passend["tag"], "Pomodoro trifft Lenas Thema Aufschieben")
|
||
var falsches_thema: Dictionary = Game.evaluate("pomodoro", "mensch_2", "zoegert", false)
|
||
p(not falsches_thema["tag"], "Bei Mira trifft Pomodoro das Thema nicht")
|
||
p(passend["chance"] > falsches_thema["chance"], "Passendes Thema erhöht die Chance")
|
||
p(Game.evaluate("pomodoro", "mensch_0", "schlaeft", false)["blocked"],
|
||
"Schlafende Menschen sind nicht erreichbar")
|
||
p(Game.evaluate("pomodoro", "mensch_0", "zoegert", true)["chance"] < passend["chance"],
|
||
"Wer abblockt, ist schwerer zu erreichen")
|
||
p(Game.evaluate("nein_sagen", "mensch_0", "zoegert", false)["blocked"],
|
||
"Ungelernte Techniken lassen sich nicht anwenden")
|
||
|
||
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.known["pomodoro"] > 0, "Jede Anwendung bringt Erfahrung")
|
||
|
||
Game.progress["mensch_0"] = 0.0
|
||
Game.used.clear()
|
||
Game.known["pomodoro"] = 28
|
||
var ertraege: Array = []
|
||
for i in range(6):
|
||
var r := Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
|
||
if r["success"]:
|
||
ertraege.append(r["gain"])
|
||
p(ertraege.size() >= 2 and ertraege[ertraege.size() - 1] < ertraege[0],
|
||
"Dieselbe Technik immer wieder wirkt schwächer")
|
||
|
||
|
||
func test_pipeline() -> void:
|
||
abschnitt("Regelwerk — Missionsfortschritt automatisieren")
|
||
Game.active_mission = "mensch_0"
|
||
Game.progress["mensch_0"] = 0.0
|
||
Game.completed.clear()
|
||
Game.used.clear()
|
||
Game.pipeline.clear()
|
||
Game.known = {"pomodoro": 28, "zerlegen": 28, "umgebung": 28}
|
||
|
||
var taten := HumanDB.aktivitaeten("mensch_0")
|
||
p(taten.size() >= 5, "Lenas Tag hat %d verschiedene Tätigkeiten" % taten.size())
|
||
p(taten.has("zoegert") and taten.has("schlaeft"),
|
||
"Darunter zögert und schläft: %s" % ", ".join(taten))
|
||
|
||
# Regelwerk über das Journal aufbauen
|
||
ui().journal_panel("missionen")
|
||
p(_knopf_da("Regelwerk"), "Die angenommene Mission trägt ein Regelwerk")
|
||
p(_druecke("Regelwerk"), "Das Regelwerk öffnet sich")
|
||
p(ui()._kind == "pipeline", "Panel »pipeline« ist offen")
|
||
var zeilen := _knopf_texte()
|
||
var fehlt: Array = []
|
||
for a in taten:
|
||
if not zeilen.contains(Orte.text(a)):
|
||
fehlt.append(a)
|
||
p(fehlt.is_empty(), "Jede der %d Tätigkeiten hat eine Zeile (fehlt: %s)" %
|
||
[taten.size(), ", ".join(fehlt)])
|
||
p(zeilen.contains("schläft → unerreichbar"),
|
||
"Schlaf ist als unerreichbar ausgewiesen, statt eine Regel anzubieten")
|
||
var schlaf_aktiv := false
|
||
for c in ui()._content.get_children():
|
||
if c is Button and (c as Button).text.begins_with("schläft") and not (c as Button).disabled:
|
||
schlaf_aktiv = true
|
||
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(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")
|
||
ui().schliessen()
|
||
|
||
Game.regel_setzen("mensch_0", "gruebelt", "zerlegen")
|
||
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
|
||
Game.regel_setzen("mensch_0", "arbeitet", "zerlegen")
|
||
p(Game.regel_anzahl("mensch_0") == 3,
|
||
"Dieselbe Technik anderswo einzusetzen fügt keine Regel 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")
|
||
Game.regel_setzen("mensch_0", "arbeitet", "")
|
||
Game.regel_setzen("mensch_0", "gruebelt", "zerlegen")
|
||
|
||
# Ohne Zutun einen Tag laufen lassen
|
||
var lena: Bewohner = null
|
||
for m in welt().humans:
|
||
if m.data["id"] == "mensch_0":
|
||
lena = m
|
||
p(lena != null, "Lena lebt in der Stadt")
|
||
var vorher: float = Game.mission_progress("mensch_0")
|
||
var vor_xp: int = int(Game.known["pomodoro"]) + int(Game.known["zerlegen"])
|
||
Game.time_h = 0.0
|
||
main.player.global_position = Vector3(0, 30.0, 60.0) # weit weg, kein Zutun
|
||
for i in range(9000):
|
||
schritte(1)
|
||
if Game.time_h >= 23.5:
|
||
break
|
||
var nachher: float = Game.mission_progress("mensch_0")
|
||
p(nachher > vorher, "Der Traum wächst von allein: %d %% -> %d %%" % [int(vorher), int(nachher)])
|
||
p(int(Game.known["pomodoro"]) + int(Game.known["zerlegen"]) > vor_xp,
|
||
"Die Regeln sammeln nebenbei Erfahrung")
|
||
|
||
# Ohne Regeln passiert nichts
|
||
Game.pipeline.clear()
|
||
Game.progress["mensch_0"] = 0.0
|
||
var still: float = Game.mission_progress("mensch_0")
|
||
Game.time_h = 0.0
|
||
for i in range(9000):
|
||
schritte(1)
|
||
if Game.time_h >= 23.5:
|
||
break
|
||
p(is_equal_approx(Game.mission_progress("mensch_0"), still),
|
||
"Ohne Regeln bewegt sich nichts (%d %%)" % int(Game.mission_progress("mensch_0")))
|
||
|
||
# Regeln greifen nur für die aktive Mission
|
||
Game.regel_setzen("mensch_1", "zoegert", "zerlegen")
|
||
Game.progress["mensch_1"] = 0.0
|
||
Game.time_h = 0.0
|
||
for i in range(9000):
|
||
schritte(1)
|
||
if Game.time_h >= 23.5:
|
||
break
|
||
p(is_equal_approx(Game.mission_progress("mensch_1"), 0.0),
|
||
"Regeln fremder Missionen bleiben still (%d %%)" % int(Game.mission_progress("mensch_1")))
|
||
|
||
# Ausgangslage für die folgenden Prüfungen
|
||
Game.pipeline.clear()
|
||
Game.progress["mensch_0"] = 0.0
|
||
Game.used.clear()
|
||
Game.known = {"pomodoro": 0, "zerlegen": 0, "umgebung": 0}
|
||
|
||
|
||
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:
|
||
Game.apply_technique("pomodoro", "mensch_0", "zoegert", false)
|
||
Game.apply_technique("zerlegen", "mensch_0", "gruebelt", false)
|
||
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)
|
||
var lena: Bewohner = welt().humans[0]
|
||
p(lena.beschriftung().contains("erfüllt"), "Lena zeigt sichtbar den erfüllten Traum")
|
||
|
||
|
||
func test_welt_waechst() -> void:
|
||
abschnitt("Erfüllte Träume lassen das Dorf wachsen")
|
||
var zugeordnet: Dictionary = {}
|
||
var doppelt: Array = []
|
||
for i in range(TechDB.LIST.size()):
|
||
var ort := DreamDB.ort_von_technik(str(TechDB.LIST[i]["id"]))
|
||
if zugeordnet.has(ort):
|
||
doppelt.append(ort)
|
||
zugeordnet[ort] = TechDB.LIST[i]["id"]
|
||
p(zugeordnet.size() == TechDB.LIST.size() and doppelt.is_empty(),
|
||
"Jede der %d Techniken hat einen eigenen Lehrort (%d Orte, %d doppelt)" %
|
||
[TechDB.LIST.size(), zugeordnet.size(), doppelt.size()])
|
||
p(DreamDB.ort_von_technik(str(TechDB.LIST[0]["id"])) == "hoersaal",
|
||
"Den ersten Weg lehrt der Hörsaal")
|
||
p(DreamDB.ort_von_technik(str(TechDB.LIST[1]["id"])) == DreamDB.haus_key(0),
|
||
"Den zweiten das erste Haus")
|
||
|
||
p(Game.completed.size() == 1, "Ein Traum ist erfüllt (%d)" % Game.completed.size())
|
||
main._traumwelt()
|
||
await get_tree().process_frame
|
||
var lehrorte: Dictionary = {}
|
||
for n in get_tree().get_nodes_in_group("interactable"):
|
||
if n.get_meta("kind") == "lehre":
|
||
lehrorte[n.get_meta("arg")] = n
|
||
p(lehrorte.has("hoersaal") and lehrorte.has(DreamDB.haus_key(0)),
|
||
"Hörsaal und das erste Haus lehren (%s)" % ", ".join(lehrorte.keys()))
|
||
p(not lehrorte.has(DreamDB.haus_key(1)), "Ein zweites Haus gibt es noch nicht")
|
||
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(Game.knows(neu_id), "Gelernt: %s" % TechDB.by_id(neu_id)["name"])
|
||
ui().schliessen()
|
||
p(not Game.technik_offen(str(TechDB.LIST[2]["id"])),
|
||
"%s bleibt unentdeckt" % TechDB.by_id(str(TechDB.LIST[2]["id"]))["name"])
|
||
p(Game.offene_techniken() == 2, "Entdeckt sind zwei Wege (%d)" % Game.offene_techniken())
|
||
|
||
await _dorf_platz_pruefen()
|
||
|
||
# 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
|
||
|
||
|
||
## Das Dorf wird erzeugt — also muss über viele Ausbaustufen gelten:
|
||
## nichts überlappt, nichts ragt über den Rand, jeder Weg passt ins Zeitfenster.
|
||
func _dorf_platz_pruefen() -> void:
|
||
var merker: Array = Game.completed.duplicate()
|
||
var stufen := 24
|
||
var engster := 999.0
|
||
var engster_text := ""
|
||
var ueberlappung := ""
|
||
var rand_fehler := ""
|
||
for stufe in range(stufen + 1):
|
||
Game.completed.clear()
|
||
for i in range(stufe):
|
||
Game.completed.append("stufe_%d" % i)
|
||
Game.welt_erzeugen()
|
||
var radius := Game.insel_radius()
|
||
var keys: Array = []
|
||
for key in DreamDB.PLACES:
|
||
if float(DreamDB.PLACES[key]["size"][0]) > 0.0:
|
||
keys.append(key)
|
||
for a in range(keys.size()):
|
||
var ka: String = keys[a]
|
||
var ca := Orte.center(DreamDB.PLACES, ka)
|
||
var sa: Array = DreamDB.PLACES[ka]["size"]
|
||
var ecke: float = Vector3(absf(ca.x) + float(sa[0]) * 0.5, 0,
|
||
absf(ca.z) + float(sa[1]) * 0.5).length()
|
||
if radius - ecke < engster:
|
||
engster = radius - ecke
|
||
engster_text = ("%s bei %d Häusern (%.1f von %.0f)"
|
||
% [DreamDB.PLACES[ka]["name"], stufe, ecke, radius])
|
||
if ecke >= radius and rand_fehler == "":
|
||
rand_fehler = "%s ragt bei %d Häusern über den Rand (%.1f >= %.0f)" % \
|
||
[DreamDB.PLACES[ka]["name"], stufe, ecke, radius]
|
||
for b in range(a + 1, keys.size()):
|
||
var kb: String = keys[b]
|
||
var cb := Orte.center(DreamDB.PLACES, kb)
|
||
var sb: Array = DreamDB.PLACES[kb]["size"]
|
||
var dx: float = absf(ca.x - cb.x) - (float(sa[0]) + float(sb[0])) * 0.5
|
||
var dz: float = absf(ca.z - cb.z) - (float(sa[1]) + float(sb[1])) * 0.5
|
||
if dx < 2.0 and dz < 2.0 and ueberlappung == "":
|
||
ueberlappung = "%s und %s stehen ineinander (%.1f / %.1f)" % \
|
||
[DreamDB.PLACES[ka]["name"], DreamDB.PLACES[kb]["name"], dx, dz]
|
||
p(ueberlappung == "", "Über %d Ausbaustufen berührt sich kein Gebäude (%s)" %
|
||
[stufen, "keins" if ueberlappung == "" else ueberlappung])
|
||
p(rand_fehler == "" and engster >= 4.0,
|
||
"Und keins ragt über den Rand — enger Fall: %s%s" %
|
||
[engster_text, "" if rand_fehler == "" else " — " + rand_fehler])
|
||
|
||
Game.completed.clear()
|
||
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)
|
||
Game.completed.assign(merker)
|
||
Game.welt_erzeugen()
|
||
|
||
|
||
func test_techniken_verbessern_sich() -> void:
|
||
abschnitt("Techniken verbessern sich durch Anwendung")
|
||
Game.einsicht = 2
|
||
p(Game.learn("zwei_minuten"), "Technik gelernt")
|
||
p(Game.skill_of("zwei_minuten") == 1, "Start auf Stufe 1")
|
||
var einsicht_vorher := Game.einsicht
|
||
var anwendungen := 0
|
||
while Game.skill_of("zwei_minuten") < 3 and anwendungen < 60:
|
||
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"]
|
||
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")
|
||
|
||
|
||
func test_journal_ueberall() -> void:
|
||
abschnitt("Journal ist immer dabei")
|
||
p(InputMap.has_action("journal"), "Eigene Aktion für das Journal")
|
||
var tasten: Array = []
|
||
for ev in InputMap.action_get_events("journal"):
|
||
tasten.append(ev.physical_keycode)
|
||
p(tasten.has(KEY_J) and tasten.has(KEY_TAB), "Auf J und Tab gelegt")
|
||
|
||
p(main.auf_erde, "Test läuft auf der Erde")
|
||
ui().journal_panel()
|
||
p(ui().offen, "Journal öffnet auf der Erde")
|
||
ui().schliessen()
|
||
|
||
main._traumwelt()
|
||
await get_tree().process_frame
|
||
ui().journal_panel()
|
||
p(ui().offen, "Journal öffnet auch in der Traumwelt")
|
||
ui().schliessen()
|
||
|
||
# Traumwesen sind keine Missionsziele
|
||
var wesen: Bewohner = welt().bewohner[0]
|
||
main.player.set_mode(Player.Mode.TRAUM)
|
||
main.player.global_position = wesen.global_position + Vector3(0, 1.0, 1.2)
|
||
main._process(0.05)
|
||
p(main._fokus_mensch == null, "Traumwesen lassen sich nicht mit Techniken bearbeiten")
|
||
p(not wesen.mit_mission, "Sie tragen keine Missionsmarkierung")
|
||
p(wesen.beschriftung().contains(str(wesen.data["rolle"])),
|
||
"Ihre Beschriftung nennt Name und Rolle: %s" % wesen.beschriftung().replace("\n", " / "))
|
||
|
||
|
||
func test_steuerung() -> void:
|
||
abschnitt("Steuerung")
|
||
var aktionen := ["vor", "zurueck", "links", "rechts", "steigen", "sinken",
|
||
"interagieren", "journal"]
|
||
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")
|
||
|
||
ui().schliessen()
|
||
main.player.look_enabled = true
|
||
main.player.set_mode(Player.Mode.GEIST)
|
||
main.player.global_position = Vector3(0, 8.0, 0)
|
||
main.player.velocity = Vector3.ZERO
|
||
main.player.yaw = 0.0
|
||
var start: Vector3 = main.player.global_position
|
||
|
||
Input.action_press("vor")
|
||
for i in range(40):
|
||
await get_tree().physics_frame
|
||
Input.action_release("vor")
|
||
p(main.player.global_position.z < start.z - 1.0,
|
||
"W schiebt den Spieler nach vorn (dz = %.1f)" % (main.player.global_position.z - start.z))
|
||
|
||
main.player.global_position = Vector3(0, 8.0, 0)
|
||
main.player.velocity = Vector3.ZERO
|
||
Input.action_press("rechts")
|
||
for i in range(40):
|
||
await get_tree().physics_frame
|
||
Input.action_release("rechts")
|
||
p(main.player.global_position.x > 1.0,
|
||
"D schiebt den Spieler nach rechts (x = %.1f)" % main.player.global_position.x)
|
||
|
||
main.player.global_position = Vector3(0, 8.0, 0)
|
||
main.player.velocity = Vector3.ZERO
|
||
Input.action_press("steigen")
|
||
for i in range(40):
|
||
await get_tree().physics_frame
|
||
Input.action_release("steigen")
|
||
p(main.player.global_position.y > 9.0,
|
||
"Leertaste lässt den Geist steigen (y = %.1f)" % main.player.global_position.y)
|
||
|
||
main.player.velocity = Vector3.ZERO
|
||
var hoch: float = main.player.global_position.y
|
||
Input.action_press("sinken")
|
||
for i in range(40):
|
||
await get_tree().physics_frame
|
||
Input.action_release("sinken")
|
||
p(main.player.global_position.y < hoch - 1.0,
|
||
"Umschalt lässt den Geist sinken (y = %.1f)" % main.player.global_position.y)
|
||
|
||
|
||
## Die Probe auf Loop 2: ohne einen erfüllten Traum gibt es keine neuen Wege.
|
||
## Also muss der erste Traum mit dem Startkapital allein zu schaffen sein —
|
||
## und zwar zuverlässig, nicht nur bei gutem Würfelglück.
|
||
func _erster_traum_tage(saat: int) -> int:
|
||
seed(saat)
|
||
Game.einsicht = 3
|
||
Game.known.clear()
|
||
Game.used.clear()
|
||
Game.pipeline.clear()
|
||
Game.progress.clear()
|
||
Game.completed.clear()
|
||
Game.active_mission = ""
|
||
Game.welt_erzeugen()
|
||
for id in _basiswege():
|
||
Game.learn(id)
|
||
Game.accept_mission("mensch_0")
|
||
# So ordnet ein Spieler zu: jede Technik an den Moment, wo sie am besten wirkt.
|
||
for id in Game.known:
|
||
var bester := ""
|
||
var beste_chance := 0.0
|
||
for activity in HumanDB.aktivitaeten("mensch_0"):
|
||
if not Game.erreichbar(activity) or Game.regel_fuer("mensch_0", activity) != "":
|
||
continue
|
||
var ev := Game.evaluate(id, "mensch_0", activity, false)
|
||
if float(ev["chance"]) > beste_chance:
|
||
beste_chance = float(ev["chance"])
|
||
bester = activity
|
||
if bester != "":
|
||
Game.regel_setzen("mensch_0", bester, id)
|
||
|
||
main._erde()
|
||
await get_tree().process_frame
|
||
main.player.global_position = Vector3(0, 30.0, 60.0) # der Spieler tut nichts
|
||
Game.time_h = 7.0
|
||
Game.day = 1
|
||
for i in range(140000):
|
||
schritte(1)
|
||
if Game.completed.has("mensch_0") or Game.day > 14:
|
||
break
|
||
return Game.day
|
||
|
||
|
||
func test_erster_traum_schaffbar() -> void:
|
||
abschnitt("Der erste Traum ist mit dem Startkapital zu schaffen")
|
||
Game.completed.clear()
|
||
Game.welt_erzeugen()
|
||
p(Game.offene_techniken() == 1,
|
||
"Am Anfang ist genau ein Weg entdeckt (%d)" % Game.offene_techniken())
|
||
|
||
var tage: Array = []
|
||
for saat in [1, 7, 555, 20240, 90210]:
|
||
tage.append(await _erster_traum_tage(saat))
|
||
p(Game.known.size() == 1, "Man startet mit einem Weg (%d)" % Game.known.size())
|
||
p(Game.regel_anzahl("mensch_0") == 1,
|
||
"Der trägt genau eine Regel (%d)" % Game.regel_anzahl("mensch_0"))
|
||
|
||
var schlimmster: int = tage.max()
|
||
var bester: int = tage.min()
|
||
p(schlimmster <= 14, "Der Traum erfüllt sich in allen %d Läufen ohne Zutun (%s)" %
|
||
[tage.size(), str(tage)])
|
||
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())
|