extends Node ## Prüft die tatsächliche Eingabekette: Events -> GUI -> _unhandled_input. var main var step := 0 var wait := 0.0 var ok := 0 var fail := 0 var y0 := 0.0 var p0 := 0.0 var s0 := 0.0 var x0 := 0.0 var m0 := 0 var n0 := 0 func chk(name: String, cond: bool, info := "") -> void: if cond: ok += 1 print(" OK %s %s" % [name, info]) else: fail += 1 print(" FAIL %s %s" % [name, info]) 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 _mouse(dx: float, dy: float) -> void: var e := InputEventMouseMotion.new() e.relative = Vector2(dx, dy) e.position = get_viewport().get_visible_rect().size * 0.5 e.global_position = e.position Input.parse_input_event(e) ## Stellt den Spieler vor einen Hotspot und laesst ihn hinsehen. func goto(car_index: int, kind: String) -> bool: var car = main.train.cars[car_index] for h in car.hotspots: if String(h["kind"]) != kind: continue var pos: Vector3 = h["pos"] var hs: float = car.center_s + pos.z var p = main.player for dz in [-1.0, 1.0, -1.4, 1.4, -0.7, 0.7]: for px in [pos.x * 0.35, 0.0, pos.x * 0.6, -pos.x * 0.35]: if p._blocked(px, hs + dz): continue p.x = px p.s = hs + dz p.yaw = atan2(pos.x - px, hs - p.s) p.pitch = clampf(atan2(pos.y - (2.72), Vector2(pos.x - px, hs - p.s).length()), -1.2, 1.2) return true return false func _process(d): if main == null: main = get_tree().current_scene return if not main.started: main._start_solo() return if wait > 0.0: wait -= d return var p = main.player match step: 0: chk("Maus gefangen", Input.mouse_mode == Input.MOUSE_MODE_CAPTURED, "mode=%d" % Input.mouse_mode) y0 = p.yaw p0 = p.pitch _mouse(120.0, 60.0) wait = 0.3 1: chk("Mausblick horizontal", absf(p.yaw - y0) > 0.05, "yaw %.3f -> %.3f" % [y0, p.yaw]) chk("Mausblick vertikal", absf(p.pitch - p0) > 0.05, "pitch %.3f -> %.3f" % [p0, p.pitch]) p.yaw = 0.0 p.pitch = 0.0 s0 = p.s _key(KEY_W, true) wait = 1.0 2: _key(KEY_W, false) chk("Vorwärts laufen", p.s - s0 > 0.5, "ds=%.2f" % (p.s - s0)) x0 = p.x _key(KEY_D, true) wait = 0.6 3: _key(KEY_D, false) chk("Seitwärts laufen", absf(p.x - x0) > 0.2, "dx=%.2f" % (p.x - x0)) # Ans Fahrpult stellen und E drücken var loco = main.train.cars[0] p.s = loco.center_s + loco.inner_z1 - 1.7 p.x = 0.0 p.yaw = 0.0 p.pitch = 0.0 wait = 0.3 4: chk("Hotspot erkannt", String(p.hotspot.get("kind", "")) == "drive", "kind=%s" % String(p.hotspot.get("kind", "-"))) _key(KEY_E, true) wait = 0.3 5: _key(KEY_E, false) chk("E übernimmt Steuerung", main.driver_id == 1, "driver=%d" % main.driver_id) chk("Spieler im Fahrmodus", p.mode == 1, "mode=%d" % p.mode) _key(KEY_W, true) wait = 1.5 6: _key(KEY_W, false) chk("W gibt Gas", main.train.speed > 1.0, "v=%.1f" % main.train.speed) _key(KEY_S, true) wait = 3.0 7: _key(KEY_S, false) chk("S bremst", main.train.speed < 0.05, "v=%.2f" % main.train.speed) _key(KEY_E, true) wait = 0.3 8: _key(KEY_E, false) chk("E gibt Steuerung frei", main.driver_id == 0) _key(KEY_V, true) _key(KEY_V, false) wait = 0.3 9: chk("V schaltet auf Außenkamera", main.cam_rig.is_active()) _key(KEY_V, true) _key(KEY_V, false) wait = 0.3 10: chk("V schaltet zurück", p.camera.current) _key(KEY_ESCAPE, true) _key(KEY_ESCAPE, false) wait = 0.3 11: chk("Esc gibt Maus frei", Input.mouse_mode != Input.MOUSE_MODE_CAPTURED) p.capture_mouse(true) # Zweiter Spieler darf nicht dazwischenfunken main._srv_toggle_drive(1) main._srv_toggle_drive(2) chk("Führerstand exklusiv", main.driver_id == 1, "driver=%d" % main.driver_id) main._srv_toggle_drive(1) # Zug mit Speisewagen und Gästen ausstatten main.train.consist = ["coach", "dining", "freight"] as Array[String] main._rebuild_train() main._park_at(main.stations[0]) for i in 4: main.spawn_passenger(0, 2) wait = 7.0 12: chk("Gäste eingestiegen", _seated() >= 4, "seated=%d" % _seated()) chk("Zum Depot-Terminal gelaufen", goto(0, "shop")) wait = 0.3 13: chk("Terminal im Blick", String(p.hotspot.get("kind", "")) == "shop", "kind=%s" % String(p.hotspot.get("kind", "-"))) _key(KEY_E, true); _key(KEY_E, false) wait = 0.4 14: chk("Terminal offen", main.shop.is_open) main.shop._tabs.current_tab = 1 wait = 0.3 15: var btn := _find_button(main.shop, "Kaufen") chk("Kaufen-Knopf sichtbar", btn != null) m0 = main.money n0 = main.train.consist.size() if btn != null: _click(btn.get_global_rect().get_center()) wait = 0.5 16: chk("Klick im Laden kauft", main.train.consist.size() == n0 + 1 and main.money < m0, "consist %d->%d, geld %d->%d" % [n0, main.train.consist.size(), m0, main.money]) _key(KEY_ESCAPE, true); _key(KEY_ESCAPE, false) wait = 0.4 17: chk("Terminal geschlossen", not main.shop.is_open) chk("Gäste überleben den Umbau", _seated() >= 4, "seated=%d" % _seated()) chk("Maus wieder gefangen", Input.mouse_mode == Input.MOUSE_MODE_CAPTURED) chk("Zur Theke gelaufen", goto(_car_of("tap"), "tap")) wait = 0.3 18: chk("Theke im Blick", String(p.hotspot.get("kind", "")) == "tap", "kind=%s" % String(p.hotspot.get("kind", "-"))) _key(KEY_E, true); _key(KEY_E, false) wait = 0.3 19: chk("Getränk aufgenommen", p.carrying > 0, "carrying=%d" % p.carrying) chk("Zum Gast gelaufen", _goto_occupied_seat()) wait = 0.3 20: chk("Gast im Blick", String(p.hotspot.get("kind", "")) == "seat", "kind=%s" % String(p.hotspot.get("kind", "-"))) m0 = main.money _key(KEY_E, true); _key(KEY_E, false) wait = 0.3 21: chk("Ticket bringt Geld", main.money > m0, "+%d €" % (main.money - m0)) var occ = _seat_occupant() chk("Gast erreichbar", occ != null) if occ != null: occ.wants_drink = true occ.served = false m0 = main.money _key(KEY_E, true); _key(KEY_E, false) wait = 0.3 22: var occ2 = _seat_occupant() chk("Serviert", occ2 != null and occ2.served) chk("Trinkgeld", main.money > m0, "+%d €" % (main.money - m0)) chk("Getränk verbraucht", p.carrying == 0, "carrying=%d" % p.carrying) chk("Zur Ladekonsole gelaufen", goto(_car_of("load"), "load")) wait = 0.3 23: chk("Konsole im Blick", String(p.hotspot.get("kind", "")) == "load", "kind=%s" % String(p.hotspot.get("kind", "-"))) main.stations[0].stock = 10.0 _key(KEY_E, true) wait = 2.5 24: _key(KEY_E, false) chk("E halten lädt Ware", main.train.total_cargo() > 0, "cargo=%d" % main.train.total_cargo()) print("== %d ok, %d fail ==" % [ok, fail]) get_tree().quit(1 if fail > 0 else 0) step += 1 func _seated() -> int: var n := 0 for pa in main.passengers: if pa.state == 2: n += 1 return n func _seat_occupant(): var h = main.player.hotspot if String(h.get("kind", "")) != "seat": return null return main.train.cars[int(h["car"])].seats[int(h["seat"])]["occupant"] func _click(pos: Vector2) -> void: for pressed in [true, false]: var e := InputEventMouseButton.new() e.button_index = MOUSE_BUTTON_LEFT e.pressed = pressed e.position = pos e.global_position = pos Input.parse_input_event(e) func _find_button(n: Node, text: String) -> Button: if n is Button and (n as Button).text == text and not (n as Button).disabled: return n for c in n.get_children(): var r := _find_button(c, text) if r != null: return r return null func _car_of(kind: String) -> int: for ci in main.train.cars.size(): for h in main.train.cars[ci].hotspots: if String(h["kind"]) == kind: return ci return -1 func _goto_occupied_seat() -> bool: var pl = main.player for ci in main.train.cars.size(): var car = main.train.cars[ci] for h in car.hotspots: if String(h["kind"]) != "seat": continue if car.seats[int(h["seat"])]["occupant"] == null: continue var pos: Vector3 = h["pos"] var hs: float = car.center_s + pos.z for dz in [0.0, -0.8, 0.8, -1.2, 1.2]: if pl._blocked(0.0, hs + dz): continue pl.x = 0.0 pl.s = hs + dz pl.yaw = atan2(pos.x, hs - pl.s) pl.pitch = 0.0 return true return false