This commit is contained in:
Team3
2026-09-16 13:08:59 +02:00
parent 89fad5dc7a
commit e813b5f7aa
17 changed files with 483 additions and 247 deletions

View File

@@ -5,6 +5,16 @@ var fehler := 0
var main: Node3D
const ZEITRAFFER := 8.0 # die Engine läuft im Test achtmal schneller
## Abschnitte, die ganze Tage oder viele Läufe simulieren -- im Schnelllauf übersprungen.
const LANGSAM := ["test_tagesablauf", "test_verfolgung", "test_erster_traum_schaffbar",
"test_huegel", "test_dorf_organisch", "test_welt_waechst"]
var schnell := false
var _uhr_abschnitt := 0
var _dauern: Array = []
var _uebersprungen := 0
func p(ok: bool, text: String) -> void:
print((" OK " if ok else " FEHLT ") + text)
if not ok:
@@ -12,53 +22,91 @@ func p(ok: bool, text: String) -> void:
func abschnitt(t: String) -> void:
_abschnitt_ende()
print("\n== " + t)
_dauern.append([t, 0.0])
_uhr_abschnitt = Time.get_ticks_msec()
func _abschnitt_ende() -> void:
if _dauern.is_empty():
return
_dauern[_dauern.size() - 1][1] = float(Time.get_ticks_msec() - _uhr_abschnitt) / 1000.0
## Wer den Traum mit reinen Game-Aufrufen vorantreibt, tickt keine Momente:
## ein laufender Moment hielte den Fortschritt sonst für immer an.
func _ohne_momente() -> void:
Game.situation = {}
Game.situation_pause = 1.0e6
## Ruft einen Testabschnitt auf -- oder überspringt ihn im Schnelllauf.
func lauf(name: String) -> void:
if schnell and LANGSAM.has(name):
_uebersprungen += 1
return
await Callable(self, name).call()
func _ready() -> void:
schnell = OS.get_cmdline_user_args().has("schnell")
# Zeitraffer: die Uhr läuft achtmal schneller, der Physikschritt bleibt 1/60 s --
# sonst fiele der Spieler bei großen Schritten durch den Boden.
Engine.time_scale = ZEITRAFFER
Engine.physics_ticks_per_second = int(60.0 * ZEITRAFFER)
Engine.max_physics_steps_per_frame = 64
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_dorf_organisch()
await test_huegel()
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_verfolgung()
await test_charakter()
await test_kamera_und_etagen()
await test_gehen_bei_tempo()
await test_diagnose()
await test_notieren_im_spiel()
await test_tagesablauf_anzeige()
await test_ablauf_im_hud()
await test_probleme_und_momente()
await test_eigene_menschen()
await test_systeme()
await test_tempo_des_traums()
await test_cooldown()
await test_chance_verdeckt()
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()
await test_brett_nie_leer()
await test_lesen_lohnt_sich()
await test_tempo()
await lauf("test_start")
await lauf("test_journal")
await lauf("test_traumdorf")
await lauf("test_dorf_organisch")
await lauf("test_huegel")
await lauf("test_haus_betreten")
await lauf("test_hoersaal")
await lauf("test_mission_annehmen")
await lauf("test_reise_zur_erde")
await lauf("test_bezirk")
await lauf("test_tagesablauf")
await lauf("test_wege_machbar")
await lauf("test_geist")
await lauf("test_verfolgung")
await lauf("test_charakter")
await lauf("test_kamera_und_etagen")
await lauf("test_gehen_bei_tempo")
await lauf("test_diagnose")
await lauf("test_notieren_im_spiel")
await lauf("test_tagesablauf_anzeige")
await lauf("test_ablauf_im_hud")
await lauf("test_probleme_und_momente")
await lauf("test_eigene_menschen")
await lauf("test_systeme")
await lauf("test_tempo_des_traums")
await lauf("test_cooldown")
await lauf("test_chance_verdeckt")
await lauf("test_mission_abschliessen")
await lauf("test_welt_waechst")
await lauf("test_techniken_verbessern_sich")
await lauf("test_journal_ueberall")
await lauf("test_steuerung")
await lauf("test_erster_traum_schaffbar")
await lauf("test_brett_nie_leer")
await lauf("test_lesen_lohnt_sich")
await lauf("test_tempo")
_abschnitt_ende()
_dauern.sort_custom(func(x, y): return x[1] > y[1])
var langsam: Array = []
for i in range(mini(5, _dauern.size())):
langsam.append("%s %.0f s" % [_dauern[i][0], _dauern[i][1]])
print("\nLangsamste Abschnitte: " + " · ".join(langsam))
if schnell:
print("Schnelllauf: %d Abschnitte übersprungen" % _uebersprungen)
print("\n=========================================")
print("Fehlende Prüfungen: %d" % fehler)
print("=========================================")
@@ -817,7 +865,6 @@ func test_charakter() -> void:
sp.set_mode(Player.Mode.GEIST)
sp.look_enabled = true
sp.folgt = null
var moment_merker: Dictionary = Game.situation.duplicate(true)
Game.situation = {} # ein laufender Moment ließe die Farbe flackern
p(sp._augen.size() == 2 and sp._augen[0].position.z < 0.0, "Zwei Augen, vorn am Kopf")
p(sp._funken.size() == Player.FUNKEN_MAX, "%d Funken stehen bereit" % Player.FUNKEN_MAX)
@@ -837,7 +884,6 @@ func test_charakter() -> void:
for i in range(90):
await get_tree().physics_frame
p(_farbabstand(sp._farbe, Player.FARBE_FOLGE) < 0.1, "Beim Verfolgen leuchtet er warm")
Game.situation = moment_merker
sp.folgt = null
sp.global_position = Vector3(0, 0.5, 9.0) # freier Rasen im Park
sp.velocity = Vector3.ZERO
@@ -954,6 +1000,18 @@ func test_kamera_und_etagen() -> void:
if Orte.etagen(HumanDB.PLACES, key) > 1:
mehr += 1
p(mehr >= 12, "%d Gebäude haben mehrere Stockwerke" % mehr)
var eng := 0
for key in HumanDB.PLACES:
var sz: Array = HumanDB.PLACES[key]["size"]
if float(sz[0]) > 0.0 and minf(float(sz[0]), float(sz[1])) < Orte.TREPPE_TIEFE + 2.0:
eng += 1
p(eng == 0, "Jedes Gebäude hat Platz für Treppenhaus und Decke (%d zu schmal)" % eng)
for b in welt()._bauten:
for mi in Prims._meshes_von(b):
var bm := (mi as MeshInstance3D).mesh as BoxMesh
if bm != null and (bm.size.x <= 0.0 or bm.size.y <= 0.0 or bm.size.z <= 0.0):
eng += 1
p(eng == 0, "Keine Platte mit negativer Größe")
var oben: Bewohner = null
for m in welt().humans:
if m.etage(str(m.data["heim"])) > 0:
@@ -1064,6 +1122,40 @@ func test_kamera_und_etagen() -> void:
p(sp.folgt == oben, "Ein Klick auf einen Menschen hängt den Geist an ihn")
main._klick(schirm)
p(sp.folgt == null, "Noch ein Klick löst ihn wieder")
# Anlauf: von weit weg zu Fuß über die Straße bis in seine Wohnung
var daheim := false
for i in range(3000):
schritte(1, 0.05, 2.0)
if oben.place == heim and oben._path.is_empty():
daheim = true
break
sp.global_position = Vector3(0, 0.3, 12.0) # im Park, weit weg
sp.velocity = Vector3.ZERO
sp.spruenge = 0
sp.anhaengen(oben)
p(daheim and sp._anlauf.size() >= 4 and (sp._anlauf.back() as Vector3).y > Orte.ETAGE - 0.1,
"Von weit weg plant der Geist den Weg über die Straße in seine Etage (%d Punkte)" %
sp._anlauf.size())
var angekommen := false
for i in range(2400):
await get_tree().physics_frame
if sp.global_position.distance_to(oben.global_position) < 4.0:
angekommen = true
break
p(angekommen and sp.spruenge == 0,
"Und kommt zu Fuß bei ihm an, ohne dass die Leine nachziehen muss (%d Sprünge, Abstand %.1f)" %
[sp.spruenge, sp.global_position.distance_to(oben.global_position)])
# Schilder: nur wer markiert ist
schritte(2)
var fremde_schilder := 0
for m in welt().humans:
if m != oben and (m._label as Label3D).visible and not Game.markiert.has(str(m.data["id"])):
fremde_schilder += 1
p((oben._label as Label3D).visible and fremde_schilder == 0,
"Nur der Verfolgte trägt ein Schild, die anderen nicht (%d fremde)" % fremde_schilder)
sp.folgt = null
Game.time_h = uhr
@@ -1160,7 +1252,7 @@ func test_verfolgung() -> void:
p(main.player.folgt == null, "Wer selbst losgeht, lässt ihn los")
# Die Taste löst ebenfalls
main.player.global_position = mensch.global_position + Vector3(0, 1.5, 2.0)
main.player.global_position = mensch.global_position + Vector3(0, 0.3, 0)
main.player.velocity = Vector3.ZERO
main._process(0.05)
main._verfolgen()
@@ -1963,6 +2055,7 @@ func test_systeme() -> void:
## Je mehr Ursachen abgedeckt sind, desto schneller kommt der Traum voran.
func test_tempo_des_traums() -> void:
abschnitt("Tempo — je besser die Systeme, desto schneller")
_ohne_momente()
var id := "mensch_0"
Game.active_mission = id
Game.systeme.clear()
@@ -2023,6 +2116,7 @@ func test_tempo_des_traums() -> void:
func test_mission_abschliessen() -> void:
abschnitt("Mission abschließen")
_ohne_momente()
var id := "mensch_0"
Game.active_mission = id
Game.progress[id] = 0.0
@@ -2042,6 +2136,7 @@ func test_mission_abschliessen() -> void:
func test_welt_waechst() -> void:
abschnitt("Erfüllte Träume lassen das Dorf wachsen")
_ohne_momente()
var zugeordnet: Dictionary = {}
var doppelt: Array = []
for i in range(TechDB.LIST.size()):
@@ -2153,6 +2248,7 @@ func _dorf_platz_pruefen() -> void:
func test_techniken_verbessern_sich() -> void:
abschnitt("Techniken verbessern sich nur durch Anwendung")
_ohne_momente()
Game.known.clear()
Game.systeme.clear()
Game.cooldown.clear()
@@ -2260,7 +2356,7 @@ func test_steuerung() -> void:
"D schiebt den Spieler nach rechts (x = %.1f)" % main.player.global_position.x)
# Ein geschlossener Klotz hält ihn auf: von der Straße aus gegen die Wand
var war_erde := main.auf_erde
var war_erde: bool = main.auf_erde
if not war_erde:
main._erde()
await get_tree().process_frame
@@ -2289,6 +2385,22 @@ func test_steuerung() -> void:
Input.action_release("vor")
var tiefe_im_klotz: float = halb - (main.player.global_position - kc).dot(richtung)
p(tiefe_im_klotz < 0.6, "Wände halten den Geist auf (%.1f m in der Wand)" % maxf(0.0, tiefe_im_klotz))
# Eingeklemmt zwischen zwei Klötzen: nach zwei Sekunden Drücken steht er wieder auf der Straße
var quer_r := Vector3(-richtung.z, 0, richtung.x)
var breit: float = (kg.x if absf(richtung.z) > 0.0 else kg.y) * 0.5
main.player.global_position = kc + quer_r * (breit + 0.4) + Vector3(0, 0.3, 0)
main.player.velocity = Vector3.ZERO
main.player.spruenge = 0
main.player.yaw = atan2(richtung.x, richtung.z)
Input.action_press("vor")
for i in range(200):
await get_tree().physics_frame
Input.action_release("vor")
var fuge: Vector3 = kc + quer_r * (breit + 0.4)
var weg_von_fuge: float = _xz(main.player.global_position).distance_to(_xz(fuge))
p(main.player.spruenge >= 1 or weg_von_fuge > 3.0,
"In der Fuge zwischen zwei Klötzen bleibt er nicht stecken (%.1f m weg, %d Befreiungen)" %
[weg_von_fuge, main.player.spruenge])
if not war_erde:
main._traumwelt() # die folgenden Messungen erwarten die Traumwelt
await get_tree().process_frame
@@ -2300,6 +2412,7 @@ func test_steuerung() -> void:
## nicht den ganzen Tag um.
func test_cooldown() -> void:
abschnitt("Techniken haben einen Cooldown")
_ohne_momente()
var id := "mensch_0"
Game.active_mission = id
Game.cooldown.clear()
@@ -2387,6 +2500,7 @@ func test_chance_verdeckt() -> void:
## 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")
_ohne_momente()
var merk_c: Array = Game.completed.duplicate()
var merk_k: Dictionary = Game.known.duplicate()
var leer_bei := -1
@@ -2744,6 +2858,7 @@ func _erster_traum_tage(saat: int) -> int:
Game.progress.clear()
Game.completed.clear()
Game.active_mission = ""
_ohne_momente()
Game.welt_erzeugen()
for id in _basiswege():
Game.learn(str(id))
@@ -2794,6 +2909,7 @@ func _bestes_system(id: String) -> int:
func test_erster_traum_schaffbar() -> void:
abschnitt("Der erste Traum ist mit dem Startkapital zu schaffen")
_ohne_momente()
Game.completed.clear()
Game.welt_erzeugen()
p(Game.offene_techniken() == 1,
@@ -2819,6 +2935,7 @@ func test_erster_traum_schaffbar() -> void:
## kommt schneller ans Ziel als wer irgendwo irgendwas einbaut.
func test_lesen_lohnt_sich() -> void:
abschnitt("Die Technik zu lesen zahlt sich aus")
_ohne_momente()
var id := "mensch_0"
Game.known.clear()
for t in TechDB.LIST: