extends Node3D ## Wurzelknoten: hält Spieler und Oberfläche, wechselt zwischen den Welten ## und wickelt Interaktionen ab. const REICHWEITE_OBJEKT := 4.2 const REICHWEITE_MENSCH := 3.6 var player: Player var ui: UI var welt: Node3D var auf_erde := false var _fokus: Node3D = null var _fokus_mensch: Node3D = null func _ready() -> void: ui = UI.new() add_child(ui) ui.panel_opened.connect(func() -> void: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE player.look_enabled = false) ui.panel_closed.connect(func() -> void: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED player.look_enabled = true) player = Player.new() add_child(player) Game.mission_done.connect(func(_id: String) -> void: Game.notified.emit("Kehre zur Traumwelt zurück — die Säule im Park bringt dich hin.", Color(0.8, 0.95, 1))) _traumwelt() Input.mouse_mode = Input.MOUSE_MODE_CAPTURED func _traumwelt() -> void: auf_erde = false _setze_welt(DreamWorld.new()) player.set_mode(Player.Mode.TRAUM) player.global_position = (welt as DreamWorld).spawn player.yaw = 0.0 player.pitch = -0.18 func _erde() -> void: auf_erde = true _setze_welt(EarthWorld.new()) player.set_mode(Player.Mode.GEIST) player.global_position = (welt as EarthWorld).spawn player.yaw = 0.0 player.pitch = -0.42 func _setze_welt(neu: Node3D) -> void: if welt != null: remove_child(welt) welt.queue_free() welt = neu add_child(welt) move_child(welt, 0) Game.teleport_lock = 1.0 _fokus = null _fokus_mensch = null func _process(delta: float) -> void: var dt := delta * Game.zeit_faktor() Game.advance_time(dt) Game.traum_voranbringen(dt * 24.0 / Game.DAY_SECONDS) Game.teleport_lock = maxf(0.0, Game.teleport_lock - delta) if ui.offen: ui.setze_hinweis("") return Game.notiz_sperre = maxf(0.0, Game.notiz_sperre - delta) _fokus_suchen() _dabeisein() _hinweis_setzen() func _fokus_suchen() -> void: _fokus = null _fokus_mensch = null var p := player.global_position var best := REICHWEITE_OBJEKT for n in get_tree().get_nodes_in_group("interactable"): var d: float = p.distance_to((n as Node3D).global_position) if d < best: best = d _fokus = n if auf_erde: var bestm := REICHWEITE_MENSCH for m in (welt as EarthWorld).humans: var d: float = p.distance_to((m as Node3D).global_position + Vector3(0, 1.0, 0)) if d < bestm: bestm = d _fokus_mensch = m if _fokus_mensch != null and (_fokus == null or bestm < best): _fokus = null elif _fokus != null: _fokus_mensch = null func _hinweis_setzen() -> void: if player.folgt != null and is_instance_valid(player.folgt): ui.setze_hinweis("[F] loslassen · %s — %s" % [player.folgt.data["name"], _notiz_hinweis(player.folgt)]) elif _fokus_mensch != null: ui.setze_hinweis("[F] folgen · %s — %s" % [_fokus_mensch.data["name"], _notiz_hinweis(_fokus_mensch)]) elif _fokus != null: var kind: String = _fokus.get_meta("kind") var titel: String = _fokus.get_meta("titel") match kind: "erde": ui.setze_hinweis("[E] Zur Erde reisen") "traum": ui.setze_hinweis("[E] Zurück in die Traumwelt") "lehre": ui.setze_hinweis("[E] %s — Techniken lernen" % titel) "brett": ui.setze_hinweis("[E] Schwarzes Brett — Mission annehmen") _: ui.setze_hinweis("[E] %s" % titel) else: ui.setze_hinweis("") ## Was am Menschen gerade dransteht: notieren, ansprechen oder nur zusehen. func _notiz_hinweis(mensch: Bewohner) -> String: var tat := Orte.text(mensch.activity) if str(mensch.data["id"]) != Game.active_mission: return tat if Game.notiz_sperre > 0.0: return "%s (%.0f s)" % [tat, Game.notiz_sperre] if Game.erkannt(str(mensch.data["id"]), int(mensch.slot)): return "%s · [E] ansprechen" % tat return "%s · [R] notieren, wenn hier etwas schiefläuft" % tat func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel") and not ui.offen: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE if \ Input.mouse_mode == Input.MOUSE_MODE_CAPTURED else Input.MOUSE_MODE_CAPTURED player.look_enabled = Input.mouse_mode == Input.MOUSE_MODE_CAPTURED get_viewport().set_input_as_handled() return if ui.offen: return if event is InputEventMouseButton and event.pressed and Input.mouse_mode == Input.MOUSE_MODE_VISIBLE: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED player.look_enabled = true return for stufe in [1, 2, 3]: if event.is_action_pressed("tempo_%d" % stufe): Game.tempo_setzen(stufe) get_viewport().set_input_as_handled() return if event.is_action_pressed("journal"): ui.journal_panel() get_viewport().set_input_as_handled() return if event.is_action_pressed("interagieren"): _interagieren() get_viewport().set_input_as_handled() return if event.is_action_pressed("folgen"): _verfolgen() get_viewport().set_input_as_handled() return if event.is_action_pressed("notieren"): _notieren() get_viewport().set_input_as_handled() ## Wer dabeisteht, füllt die Stunde im Ablauf -- mehr nicht. Was dort ## schiefläuft, muss er selbst benennen. func _dabeisein() -> void: if _fokus_mensch == null or not auf_erde: return var id: String = str(_fokus_mensch.data["id"]) if id == Game.active_mission: Game.sehen(id, int(_fokus_mensch.slot)) ## Den Moment benennen: hier läuft etwas schief. Das ist der Kern des ## Beobachtens -- der Spieler entscheidet, wann er hinschaut. func _notieren() -> void: if not auf_erde: return var ziel: Bewohner = _fokus_mensch if ziel == null and player.folgt != null and is_instance_valid(player.folgt): ziel = player.folgt if ziel == null: Game.notified.emit("Geh näher an den Menschen heran, dem du hilfst.", Color(1, 0.85, 0.5)) return var id: String = str(ziel.data["id"]) if id != Game.active_mission: Game.notified.emit("Für %s hast du keine Mission angenommen." % ziel.data["name"], Color(1, 0.85, 0.5)) return if Game.notiz_sperre > 0.0: Game.notified.emit("Du bist noch beim letzten Fehlgriff (%.0f s)." % Game.notiz_sperre, Color(1, 0.75, 0.55)) return Game.notieren(id, int(ziel.slot)) ## Als Geist an einen Menschen hängen und ihn durch seinen Tag begleiten. func _verfolgen() -> void: if not auf_erde: return if player.folgt != null: var name: String = player.folgt.data["name"] player.folgt = null Game.notified.emit("Du lässt %s wieder los." % name, Color(0.8, 0.85, 1)) return if _fokus_mensch == null: Game.notified.emit("Geh näher an einen Menschen heran, um ihm zu folgen.", Color(1, 0.85, 0.5)) return player.folgt = _fokus_mensch Game.notified.emit("Du hängst dich an %s." % _fokus_mensch.data["name"], Color(0.7, 0.9, 1)) func _interagieren() -> void: if _fokus_mensch != null: ui.anwenden_panel(_fokus_mensch) return if _fokus == null: return var kind: String = _fokus.get_meta("kind") match kind: "erde": if Game.active_mission == "" and Game.completed.size() < HumanDB.LIST.size(): Game.notified.emit( "Das Tor öffnet sich nur mit einer Mission. Sieh am Schwarzen Brett nach.", Color(1, 0.8, 0.5)) else: _erde() "traum": _traumwelt() "lehre": ui.lehre_panel(str(_fokus.get_meta("arg"))) "brett": ui.brett_panel()