This commit is contained in:
team3
2026-09-16 17:55:38 +02:00
parent c9416ed3c6
commit f012dd7cd5
17 changed files with 878 additions and 160 deletions

View File

@@ -7,6 +7,7 @@ var ok := 0
var fail := 0
var worst := 0.0
var elapsed := 0.0
var sfx_first := false
func chk(n: String, c: bool, i := "") -> void:
if c: ok += 1; print(" OK %s %s" % [n, i])
@@ -23,11 +24,14 @@ func _process(d):
if not main.started:
main._start_solo()
return
if not main.audio.loaded:
if not main.audio.music_loaded:
elapsed += d
worst = maxf(worst, d)
if main.audio.loaded:
sfx_first = true
return
if step == 0:
chk("Klänge vor der Musik verfügbar", sfx_first)
chk("Kein Ruckler beim Erzeugen", worst < 0.2,
"längster Frame %.0f ms in %.1f s Ladezeit" % [worst * 1000.0, elapsed])
if wait > 0.0:
@@ -56,7 +60,7 @@ func _process(d):
chk("Thread beendet", a._thread == null)
var m: AudioStreamWAV = a._music.stream
chk("Musik ist eine Schleife", m.loop_mode == AudioStreamWAV.LOOP_FORWARD)
chk("Musik lang genug", m.data.size() / 2 > 20 * 22050,
chk("Musik hat drei Teile", m.data.size() / 2 > 75 * 22050,
"%.1f s" % (m.data.size() / 2.0 / 22050.0))
chk("Musik nicht stumm", _peak(m) > 3000, "peak=%d" % _peak(m))
chk("Rumble läuft leise", a._rumble.playing and a._rumble.volume_db < -40.0,
@@ -134,7 +138,7 @@ func _peak(w: AudioStreamWAV) -> int:
func _check_harmony() -> void:
var Sfx = load("res://scripts/sfx.gd")
var bad: Array[String] = []
for stage in Sfx.PROGRESSION:
for stage in Sfx.PROGRESSION + Sfx.PROGRESSION_B:
var voices: Array[float] = []
for f in stage["pad"]:
voices.append(float(f))

View File

@@ -31,6 +31,7 @@ func _process(d):
if role == "host":
main._start_host(7788)
elif role == "client" and step == 0:
main.menu._name.text = "Tester"
main._start_join("127.0.0.1", 7788)
step = 1
return
@@ -42,14 +43,24 @@ func _process(d):
else:
_client_step()
func _key(c: int, d: bool) -> void:
var e := InputEventKey.new(); e.physical_keycode = c; e.keycode = c; e.pressed = d
Input.parse_input_event(e)
func _host_step() -> void:
match step:
0:
print("[host] Sitzung gestartet")
for i in 4:
main.spawn_passenger(0, 2)
wait = 30.0
wait = 16.0
1:
# Host faehrt los, der Client soll das weich sehen
main._srv_toggle_drive(1)
_key(KEY_W, true)
wait = 20.0
2:
_key(KEY_W, false)
print("[host] Ende. money=%d driver=%d" % [main.money, main.driver_id])
get_tree().quit()
step += 1
@@ -59,7 +70,7 @@ func _client_step() -> void:
1:
wait = 6.0
2:
chk("Welt gebaut", main.stations.size() == 5)
chk("Welt gebaut", main.stations.size() == 6)
chk("Zug übernommen", main.train.cars.size() == 3, "cars=%d" % main.train.cars.size())
chk("Eigene Peer-ID", main.my_id > 1)
chk("Fahrgäste sichtbar", main.passengers.size() >= 4, "n=%d" % main.passengers.size())
@@ -69,6 +80,11 @@ func _client_step() -> void:
seated += 1
chk("Gäste sitzen", seated >= 4, "seated=%d" % seated)
chk("Geld synchron", main.money == 1500)
chk("Eigener Name beim Host angekommen",
String(main.players[main.my_id]["name"]) == "Tester",
"%s" % main.players[main.my_id]["name"])
var host_av = main.players.get(1, {}).get("avatar")
chk("Avatar des Hosts sichtbar", host_av != null)
main.request("drive")
wait = 1.5
3:
@@ -96,6 +112,36 @@ func _client_step() -> void:
wait = 2.0
6:
chk("Kein Selbstlauf ohne Gas", main.train.speed < 0.01)
main.request("drive") # Steuerung abgeben, der Host faehrt gleich
_measuring = true
wait = 12.0
7:
_measuring = false
chk("Zug beim Client in Fahrt", _top_speed > 3.0, "v=%.1f" % _top_speed)
chk("Zug beim Client ruckelfrei", _frames > 100 and _jumps * 20 < _frames,
"%d Sprünge in %d Frames, größter %.2f m" % [_jumps, _frames, _worst])
print("[client] == %d ok, %d fail ==" % [ok, fail])
get_tree().quit(1 if fail > 0 else 0)
step += 1
var _measuring := false
var _last := Vector3.ZERO
var _frames := 0
var _jumps := 0
var _worst := 0.0
var _top_speed := 0.0
func _physics_process(d: float) -> void:
if not _measuring or main == null or not main.started:
return
var t = main.train
var p: Vector3 = t.cars[0].global_position
if _last != Vector3.ZERO and t.speed > 1.0:
_frames += 1
var moved := p.distance_to(_last)
var expected: float = t.speed * d
_worst = maxf(_worst, absf(moved - expected))
if absf(moved - expected) > 0.6:
_jumps += 1
_top_speed = maxf(_top_speed, t.speed)
_last = p

View File

@@ -27,6 +27,12 @@ install_test tests/input_test.gd
"$GODOT" --path . --resolution 1280x720 2>&1 | grep -E "OK|FAIL|=="
INPUT=${PIPESTATUS[0]}
echo
echo "=== Weichen-Tests ==="
install_test tests/switch_test.gd
"$GODOT" --path . --resolution 1280x720 2>&1 | grep -E "OK|FAIL|=="
SWITCH=${PIPESTATUS[0]}
echo
echo "=== Klang-Tests ==="
install_test tests/audio_test.gd
@@ -50,5 +56,5 @@ NET=${PIPESTATUS[0]}
wait $HOST 2>/dev/null
echo
[ "$SOLO" -eq 0 ] && [ "$INPUT" -eq 0 ] && [ "$DIR" -eq 0 ] && [ "$AUDIO" -eq 0 ] && [ "$NET" -eq 0 ] && echo "Alle Tests bestanden." || echo "Es sind Tests fehlgeschlagen."
exit $(( SOLO || INPUT || DIR || AUDIO || NET ))
[ "$SOLO" -eq 0 ] && [ "$SWITCH" -eq 0 ] && [ "$INPUT" -eq 0 ] && [ "$DIR" -eq 0 ] && [ "$AUDIO" -eq 0 ] && [ "$NET" -eq 0 ] && echo "Alle Tests bestanden." || echo "Es sind Tests fehlgeschlagen."
exit $(( SOLO || SWITCH || INPUT || DIR || AUDIO || NET ))

View File

@@ -112,6 +112,36 @@ func _process(d):
var before = main.train.consist.size()
main.sell_car(0)
chk("Wagen verkauft", main.train.consist.size() == before - 1)
# Lok verkaufen: Dampf-Veteran springt ein, Geld kommt zurueck
var m_before = main.money
main.request("sell_loco")
chk("Lok verkauft", main.train.loco_id == "steam", "loco=%s" % main.train.loco_id)
chk("Rücknahme gutgeschrieben", main.money - m_before == 6600, "+%d €" % (main.money - m_before))
main.buy_loco("express")
# Speichern und Laden
main.save_game()
var saved_money = main.money
var saved_consist = main.train.consist.duplicate()
var saved_seg = main.train.seg
var saved_prog = main.train.progress
main.money = 1
main.train.consist = ["coach"] as Array[String]
main.train.loco_id = "steam"
main._rebuild_train()
chk("Spielstand geladen", main.load_game())
chk("Geld wiederhergestellt", main.money == saved_money, "%d vs %d" % [main.money, saved_money])
chk("Zug wiederhergestellt", main.train.consist == saved_consist and main.train.loco_id == "express",
"%s %s" % [main.train.loco_id, main.train.consist])
chk("Position wiederhergestellt", main.train.seg == saved_seg and absf(main.train.progress - saved_prog) < 0.5)
# Ziele
main.tickets_checked = 10
var m_goal = main.money
main._check_goals()
chk("Ziel erreicht", main.goals_done.has("tickets10"))
chk("Belohnung gezahlt", main.money - m_goal == 250, "+%d €" % (main.money - m_goal))
chk("Ziele der Reihe nach", not main.goals_done.has("cargo30") and not main.won)
var g = main.current_goal()
chk("Nächstes Ziel im HUD", String(g.get("text", "")).begins_with("30 Einheiten"), "%s" % g)
wait = 0.3
7:
chk("Spieler im Zug", main.train.locate(main.player.s)["index"] == 0)
@@ -121,6 +151,9 @@ func _process(d):
wait = 3.0
8:
chk("Zug fährt", main.train.speed > 3.0, "v=%.1f m/s" % main.train.speed)
var e: Vector3 = main.train.cars[0].global_transform.basis.get_euler()
chk("Wagen wackelt bei Fahrt", absf(e.z) > 0.0005 or absf(e.x) > 0.0005,
"roll=%.4f pitch=%.4f" % [e.z, e.x])
_key(KEY_W, false)
_key(KEY_S, true)
wait = 4.0

175
tests/switch_test.gd Normal file
View File

@@ -0,0 +1,175 @@
extends Node
## Weichen: Route wählen, Übergang ohne Sprung, Bergstrecke erreichbar.
var main
var step := 0
var wait := 0.0
var ok := 0
var fail := 0
var last_pos := Vector3.ZERO
var worst_jump := 0.0
var jump_frames := 0
var _dbg := 0.0
var _cam_hits := 0
var _cam_pulled := 0
var _cam_yaw := 0
func chk(n: String, c: bool, i := "") -> void:
if c: ok += 1; print(" OK %s %s" % [n, i])
else: fail += 1; print(" FAIL %s %s" % [n, i])
func _key(c: int, d: bool) -> void:
var e := InputEventKey.new(); e.physical_keycode = c; e.keycode = c; e.pressed = d
Input.parse_input_event(e)
func _watch(d: float) -> void:
var t = main.train
var p: Vector3 = t.cars[t.cars.size() - 1].global_position
if last_pos != Vector3.ZERO and d > 0.0:
var jump := p.distance_to(last_pos)
var allowed: float = t.speed * d * 1.5 + 0.05
if jump > allowed:
jump_frames += 1
worst_jump = maxf(worst_jump, jump - t.speed * d)
last_pos = p
## Im Physik-Takt messen: dort bewegt sich der Zug genau einmal pro Aufruf.
func _physics_process(d: float) -> void:
if main != null and main.started and step >= 1 and step <= 8:
_watch(d)
func _process(d):
if main == null:
main = get_tree().current_scene
return
if not main.started:
main._start_solo()
return
var t = main.train
if wait > 0.0:
wait -= d
return
match step:
0:
chk("Drei Abschnitte", main.track.segments.size() == 3)
chk("Heimstrecke hat Weiche", main.track.is_switch(2))
chk("Sechs Bahnhöfe", main.stations.size() == 6)
main._park_at(main.stations[4]) # Ahornstadt, Heimstrecke
chk("Start auf Heimstrecke", t.seg == 2, "seg=%d" % t.seg)
chk("Weiche voraus erkannt", t.upcoming_switch() == 2)
var nxt: Array = t.next_station()
chk("Nächster Halt Waldkirch (links)", nxt[0].station_name == "Waldkirch",
"%s" % nxt[0].station_name)
t.switch_choice = 1
var nxt2: Array = t.next_station()
chk("Nächster Halt Bergblick (rechts)", nxt2[0].station_name == "Bergblick",
"%s" % nxt2[0].station_name)
t.switch_choice = 0
main.driver_id = 1
_key(KEY_W, true)
last_pos = Vector3.ZERO
wait = 1.0
1:
wait = 0.5
2:
if t.seg != 0:
step -= 1
wait = 0.3
_dbg += 0.3
if _dbg >= 2.0:
_dbg = 0.0
print("[dbg] seg=%d prog=%.1f/%.1f v=%.1f thr=%.1f driver=%d mode=%d" % [t.seg, t.progress,
main.track.seg_length(t.seg), t.speed, t.throttle, main.driver_id, main.player.mode])
else:
_key(KEY_W, false)
_key(KEY_S, true)
chk("Links -> Talstrecke", t.seg == 0, "seg=%d" % t.seg)
chk("Historie geführt", t.history.size() > 0 and t.history[t.history.size() - 1] == 2,
"%s" % [t.history])
chk("Letzter Wagen ohne Sprung", jump_frames == 0,
"Sprungframes=%d, größter Sprung %.2f m" % [jump_frames, worst_jump])
wait = 4.0
3:
_key(KEY_S, false)
main._park_at(main.stations[4])
t.switch_choice = 1
last_pos = Vector3.ZERO
jump_frames = 0
worst_jump = 0.0
_key(KEY_D, true)
_key(KEY_W, true)
wait = 1.0
4:
wait = 0.5
5:
if t.seg != 1:
step -= 1
wait = 0.3
else:
chk("Rechts -> Bergstrecke", t.seg == 1, "seg=%d" % t.seg)
chk("D hält die Weiche rechts", t.switch_choice == 1)
chk("Übergang rechts ohne Sprung", jump_frames == 0,
"Sprungframes=%d" % jump_frames)
_key(KEY_D, false)
wait = 0.5
6:
var nxt: Array = t.next_station()
chk("Bergblick voraus", nxt[0].station_name == "Bergblick", "%s" % nxt[0].station_name)
wait = 0.5
7:
# bis kurz vor Bergblick fahren, dann bremsen
var berg = main.stations[5]
var ahead: float = t.distance_ahead(berg)
if ahead > t.speed * t.speed / 16.0 + 8.0:
step -= 1
wait = 0.2
else:
_key(KEY_W, false)
_key(KEY_S, true)
wait = 6.0
8:
_key(KEY_S, false)
chk("Zug steht", t.speed < 0.05, "v=%.2f" % t.speed)
chk("Am Bergblick angekommen", t.docked_at != null and t.docked_at.station_name == "Bergblick",
"dock=%s" % ("-" if t.docked_at == null else t.docked_at.station_name))
# Sichtkontrolle: Kreuzung von oben
main._park_at(main.stations[4])
t.place_head(2, main.track.seg_length(2) - 6.0)
main._toggle_camera()
main.cam_rig.distance = 95.0
main.cam_rig.pitch = deg_to_rad(-70.0)
main.cam_rig.yaw = 0.4
wait = 1.0
9:
get_viewport().get_texture().get_image().save_png("user://junction.png")
t.place_head(0, main.track.seg_length(0) - 20.0)
main.cam_rig.snap_to(t.cars[0].global_position)
wait = 1.0
10:
get_viewport().get_texture().get_image().save_png("user://junction2.png")
# Kamera-Kollision: Fokus auf den Bahnsteig, rundherum drehen
var st = main.stations[0]
main.cam_rig.target = null
main.cam_rig.snap_to(st.global_position + Vector3(0, 0, 0))
main.cam_rig.distance = 30.0
main.cam_rig.pitch = deg_to_rad(-10.0)
_cam_hits = 0
_cam_yaw = 0
wait = 0.2
11:
if _cam_yaw < 12:
main.cam_rig.yaw = _cam_yaw * TAU / 12.0
_cam_yaw += 1
step -= 1
wait = 0.15
if main.cam_rig.blocked:
_cam_hits += 1
var dist: float = main.cam_rig._cam.global_position.distance_to(main.cam_rig.global_position)
if dist < 29.0:
_cam_pulled += 1
else:
chk("Kamera stößt am Gebäude an", _cam_hits > 0, "%d von 12 Richtungen" % _cam_hits)
chk("Kamera rückt davor", _cam_pulled == _cam_hits, "%d/%d" % [_cam_pulled, _cam_hits])
12:
print("== %d ok, %d fail ==" % [ok, fail])
get_tree().quit(1 if fail > 0 else 0)
step += 1