extends CanvasLayer class_name UI signal panel_opened signal panel_closed const BREITE := 900 var offen := false var _kind := "" var _hud: RichTextLabel var _hinweis: Label var _log: VBoxContainer var _ablauf: HBoxContainer var _ablauf_titel: Label var _ablauf_jetzt: Label var _ablauf_grund: ColorRect var _probleme: RichTextLabel var _probleme_grund: ColorRect var _zellen: Array = [] var _ticks: Array = [] var _dim: ColorRect var _panel: PanelContainer var _titel: Label var _content: VBoxContainer var _scroll: ScrollContainer var _apply_human: Node3D var _journal_seite := "techniken" var _scroll_wunsch := 0 var _scroll_frames := 0 func _ready() -> void: layer = 10 _baue_hud() _baue_panel() Game.notified.connect(_notiz) Game.state_changed.connect(_hud_aktualisieren) _hud_aktualisieren() func _baue_hud() -> void: _hud = RichTextLabel.new() _hud.bbcode_enabled = true _hud.fit_content = true _hud.scroll_active = false _hud.set_anchors_preset(Control.PRESET_TOP_LEFT) _hud.position = Vector2(18, 14) _hud.custom_minimum_size = Vector2(420, 130) _hud.size = Vector2(420, 130) _hud.mouse_filter = Control.MOUSE_FILTER_IGNORE _hud.add_theme_font_size_override("normal_font_size", 17) _hud.add_theme_font_size_override("bold_font_size", 17) add_child(_hud) _baue_ablauf() _baue_probleme() var steuerung := Label.new() steuerung.text = "WASD Bewegen · Maus Umsehen · E Interagieren · F Folgen · " + \ "R Notieren · J Journal · 1/2/3 Tempo · Esc Maus frei" steuerung.set_anchors_preset(Control.PRESET_TOP_RIGHT) steuerung.position = Vector2(-720, 16) steuerung.size = Vector2(700, 24) steuerung.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT steuerung.add_theme_font_size_override("font_size", 14) steuerung.add_theme_color_override("font_color", Color(1, 1, 1, 0.65)) steuerung.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(steuerung) _hinweis = Label.new() _hinweis.set_anchors_preset(Control.PRESET_CENTER_BOTTOM) _hinweis.position = Vector2(-300, -110) _hinweis.size = Vector2(600, 30) _hinweis.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _hinweis.add_theme_font_size_override("font_size", 19) _hinweis.add_theme_color_override("font_color", Color(1, 0.95, 0.75)) _hinweis.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9)) _hinweis.add_theme_constant_override("outline_size", 6) _hinweis.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_hinweis) _log = VBoxContainer.new() _log.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT) _log.position = Vector2(-520, -230) _log.size = Vector2(500, 200) _log.alignment = BoxContainer.ALIGNMENT_END _log.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_log) ## Der Tagesablauf im HUD: 24 Felder, eines je Stunde, immer sichtbar. const ABLAUF_X := 18.0 const ABLAUF_Y := 126.0 const ZELLE_BREIT := 14.0 const ZELLE_LUECKE := 2.0 func _baue_ablauf() -> void: var breite := 24.0 * (ZELLE_BREIT + ZELLE_LUECKE) - ZELLE_LUECKE _ablauf_grund = ColorRect.new() _ablauf_grund.color = Color(0, 0, 0, 0.42) _ablauf_grund.set_anchors_preset(Control.PRESET_TOP_LEFT) _ablauf_grund.position = Vector2(ABLAUF_X - 8.0, ABLAUF_Y - 6.0) _ablauf_grund.custom_minimum_size = Vector2(breite + 16.0, 88.0) _ablauf_grund.size = _ablauf_grund.custom_minimum_size _ablauf_grund.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_ablauf_grund) _ablauf_titel = Label.new() _ablauf_titel.set_anchors_preset(Control.PRESET_TOP_LEFT) _ablauf_titel.position = Vector2(ABLAUF_X, ABLAUF_Y) _ablauf_titel.add_theme_font_size_override("font_size", 14) _ablauf_titel.add_theme_color_override("font_color", Color(1, 1, 1, 0.7)) _ablauf_titel.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_ablauf_titel) _ablauf = HBoxContainer.new() _ablauf.set_anchors_preset(Control.PRESET_TOP_LEFT) _ablauf.position = Vector2(ABLAUF_X, ABLAUF_Y + 22.0) _ablauf.add_theme_constant_override("separation", int(ZELLE_LUECKE)) _ablauf.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_ablauf) for stunde in range(24): var zelle := ColorRect.new() zelle.custom_minimum_size = Vector2(ZELLE_BREIT, 16) zelle.mouse_filter = Control.MOUSE_FILTER_IGNORE _ablauf.add_child(zelle) _zellen.append(zelle) # Stundenmarken unter dem Streifen for stunde in [0, 6, 12, 18]: var tick := Label.new() tick.text = "%d" % stunde tick.set_anchors_preset(Control.PRESET_TOP_LEFT) tick.position = Vector2(ABLAUF_X + float(stunde) * (ZELLE_BREIT + ZELLE_LUECKE), ABLAUF_Y + 42.0) tick.add_theme_font_size_override("font_size", 11) tick.add_theme_color_override("font_color", Color(1, 1, 1, 0.45)) tick.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(tick) _ticks.append(tick) _ablauf_jetzt = Label.new() _ablauf_jetzt.set_anchors_preset(Control.PRESET_TOP_LEFT) _ablauf_jetzt.position = Vector2(ABLAUF_X, ABLAUF_Y + 60.0) _ablauf_jetzt.add_theme_font_size_override("font_size", 15) _ablauf_jetzt.add_theme_color_override("font_color", Color(1, 0.95, 0.8)) _ablauf_jetzt.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_ablauf_jetzt) ## Farbe je Stunde: unbekannt, gesehen, ungeklärt, benannt, gebaut. func _ablauf_farbe(human_id: String, nr: int) -> Color: match Game.slot_zustand(human_id, nr): Game.Stunde.UNBEKANNT: return Color(0.22, 0.23, 0.30) Game.Stunde.GEBAUT: return Color(0.44, 0.78, 0.50) Game.Stunde.UNGEKLAERT: return Color(0.85, 0.66, 0.32) Game.Stunde.BENANNT: return Color(0.84, 0.42, 0.35) Game.Stunde.SCHLAF: return Color(0.30, 0.33, 0.44) return Color(0.46, 0.50, 0.62) func _ablauf_aktualisieren() -> void: var id := Game.active_mission var an := id != "" _ablauf.visible = an _ablauf_titel.visible = an _ablauf_jetzt.visible = an _ablauf_grund.visible = an for t in _ticks: (t as Label).visible = an if not an: return var jetzt := int(Game.time_h) var offen := 0 for stunde in range(24): var nr := Game.slot_bei_stunde(id, stunde) var f := _ablauf_farbe(id, nr) if not Game.gesehen_slot(id, nr): offen += 1 if stunde == jetzt: f = f.lightened(0.55) var zelle: ColorRect = _zellen[stunde] zelle.color = f zelle.custom_minimum_size.y = 20 if stunde == jetzt else 16 _ablauf_titel.text = "Ihr Tag 0 – 23 Uhr%s" % \ ("" if offen == 0 else " · %d Stunden unbekannt" % offen) var nr_jetzt := Game.slot_bei_stunde(id, jetzt) _ablauf_jetzt.text = "%02d Uhr %s" % [jetzt, _stunden_text(id, nr_jetzt)] ## Die zweite Ebene im HUD: bekannte Probleme und der offene Moment. ## Bewusst anders gezeichnet als der Tagesstreifen -- sie hängt nicht an Stunden. func _baue_probleme() -> void: _probleme_grund = ColorRect.new() _probleme_grund.color = Color(0, 0, 0, 0.42) _probleme_grund.set_anchors_preset(Control.PRESET_TOP_LEFT) _probleme_grund.position = Vector2(ABLAUF_X - 8.0, ABLAUF_Y + 88.0) _probleme_grund.custom_minimum_size = Vector2(414.0, 74.0) _probleme_grund.size = _probleme_grund.custom_minimum_size _probleme_grund.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_probleme_grund) _probleme = RichTextLabel.new() _probleme.bbcode_enabled = true _probleme.fit_content = true _probleme.scroll_active = false _probleme.set_anchors_preset(Control.PRESET_TOP_LEFT) _probleme.position = Vector2(ABLAUF_X, ABLAUF_Y + 94.0) _probleme.custom_minimum_size = Vector2(400, 64) _probleme.size = Vector2(400, 64) _probleme.mouse_filter = Control.MOUSE_FILTER_IGNORE _probleme.add_theme_font_size_override("normal_font_size", 14) _probleme.add_theme_font_size_override("bold_font_size", 14) add_child(_probleme) func _probleme_aktualisieren() -> void: var id := Game.active_mission var an := id != "" _probleme.visible = an _probleme_grund.visible = an if not an: return var zeilen: Array = [] if Game.situation_laeuft() and Game.situation_bei(id): var s: Dictionary = Probleme.SITUATIONEN[Game.situation["key"]] zeilen.append("[color=#ffcc66][b]Jetzt: %s[/b][/color] %s [b]%.0f s[/b]" % [s["name"], s["was"], float(Game.situation["rest"])]) else: zeilen.append("[color=#8e93a8]Ihre Momente[/color]") var bekannt: Array = [] for p in Game.probleme_von(id): var key := str(p) if not Game.problem_bekannt(id, key): bekannt.append("[color=#55596b]?[/color]") elif Game.problem_ueberwunden(id, key): bekannt.append("[color=#88ff99]%s ✓[/color]" % Probleme.name_von(key)) else: bekannt.append("[color=#ffb0a0]%s %d/%d[/color]" % [Probleme.name_von(key), Game.problem_fortschritt(id, key), Game.PROBLEM_UEBERWINDEN]) zeilen.append(" · ".join(bekannt)) _probleme.text = "\n".join(zeilen) func _baue_panel() -> void: _dim = ColorRect.new() _dim.color = Color(0, 0, 0, 0.62) _dim.set_anchors_preset(Control.PRESET_FULL_RECT) _dim.visible = false add_child(_dim) _panel = PanelContainer.new() _panel.set_anchors_preset(Control.PRESET_CENTER) _panel.custom_minimum_size = Vector2(BREITE, 200) _panel.visible = false var sb := StyleBoxFlat.new() sb.bg_color = Color(0.10, 0.09, 0.16, 0.98) sb.border_color = Color(0.55, 0.70, 1.0, 0.8) sb.set_border_width_all(2) sb.set_corner_radius_all(10) sb.set_content_margin_all(22) _panel.add_theme_stylebox_override("panel", sb) add_child(_panel) var vb := VBoxContainer.new() vb.add_theme_constant_override("separation", 10) _panel.add_child(vb) _titel = Label.new() _titel.add_theme_font_size_override("font_size", 26) _titel.add_theme_color_override("font_color", Color(1, 0.93, 0.75)) vb.add_child(_titel) _scroll = ScrollContainer.new() _scroll.custom_minimum_size = Vector2(BREITE - 44, 140) _scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED vb.add_child(_scroll) var scroll := _scroll _content = VBoxContainer.new() _content.custom_minimum_size.x = BREITE - 70 _content.add_theme_constant_override("separation", 10) scroll.add_child(_content) var fuss := Label.new() fuss.text = "Esc schließt" fuss.add_theme_font_size_override("font_size", 14) fuss.add_theme_color_override("font_color", Color(1, 1, 1, 0.5)) vb.add_child(fuss) func _process(_delta: float) -> void: if not offen: _hud_aktualisieren() else: var wunsch := _content.get_combined_minimum_size().y var platz := get_viewport().get_visible_rect().size.y - 220.0 _scroll.custom_minimum_size.y = clampf(wunsch, 120.0, platz) _panel.reset_size() # sonst behält der Rahmen die Höhe des Panels davor if _scroll_frames > 0: _scroll.scroll_vertical = _scroll_wunsch _scroll_frames -= 1 _panel.position = (get_viewport().get_visible_rect().size - _panel.size) * 0.5 # ------------------------------------------------------------------ HUD func _hud_aktualisieren() -> void: var zeilen: Array = [] var takt := ("[color=#aaaaaa]Tempo %d (%d×)[/color]" % [Game.tempo, int(Game.faktor_von(Game.tempo))]) if Game.schlaeft_ziel(): takt = ("[color=#9fb6ff]Tempo 3 (%d×) · Nachtsprung[/color]" % int(Game.faktor_von(3))) zeilen.append("[b]%s[/b] %s" % [Game.time_string(), takt]) zeilen.append("Techniken: [b]%d[/b] gelernt · [b]%d[/b] entdeckt · %d im Spiel" % [Game.known.size(), Game.offene_techniken(), TechDB.LIST.size()]) if Game.active_mission != "": var h := HumanDB.by_id(Game.active_mission) zeilen.append("Mission: [color=#ffdd77]%s[/color] — %d %% · %d %% je Tag" % [h["name"], int(Game.mission_progress(Game.active_mission)), int(round(Game.traum_tempo(Game.active_mission)))]) zeilen.append("[color=#aaaaaa]%s[/color]" % h["traum"]) elif Game.completed.size() >= HumanDB.LIST.size(): zeilen.append("[color=#88ff99]Alle Träume erfüllt.[/color]") else: zeilen.append("[color=#aaaaaa]Keine Mission. Schwarzes Brett am Dorfplatz.[/color]") _hud.text = "\n".join(zeilen) _ablauf_aktualisieren() _probleme_aktualisieren() func setze_hinweis(text: String) -> void: _hinweis.text = text func _notiz(text: String, farbe: Color) -> void: var l := Label.new() l.text = text l.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART l.custom_minimum_size.x = 500 l.add_theme_font_size_override("font_size", 16) l.add_theme_color_override("font_color", farbe) l.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9)) l.add_theme_constant_override("outline_size", 5) _log.add_child(l) while _log.get_child_count() > 6: var alt := _log.get_child(0) _log.remove_child(alt) alt.queue_free() var tw := l.create_tween() tw.tween_interval(6.0) tw.tween_property(l, "modulate:a", 0.0, 1.5) tw.tween_callback(l.queue_free) # ------------------------------------------------------------------ Panel func _leeren(titel: String) -> void: for c in _content.get_children(): _content.remove_child(c) c.queue_free() _titel.text = titel func _text(bb: String, groesse: int = 17) -> RichTextLabel: var r := RichTextLabel.new() r.bbcode_enabled = true r.fit_content = true r.scroll_active = false r.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART r.custom_minimum_size.x = BREITE - 80 r.text = bb r.add_theme_font_size_override("normal_font_size", groesse) r.add_theme_font_size_override("bold_font_size", groesse) _content.add_child(r) return r func _knopf(beschriftung: String, cb: Callable, aktiv: bool = true) -> Button: var b := Button.new() b.text = beschriftung b.disabled = not aktiv b.custom_minimum_size = Vector2(0, 38) b.add_theme_font_size_override("font_size", 17) _content.add_child(b) if aktiv: b.pressed.connect(cb) return b func _trenner() -> void: var s := HSeparator.new() _content.add_child(s) func _zeigen(kind: String) -> void: _kind = kind offen = true _dim.visible = true _panel.visible = true _panel.reset_size() panel_opened.emit() func schliessen() -> void: if not offen: return offen = false _kind = "" _apply_human = null _dim.visible = false _panel.visible = false panel_closed.emit() func _hinweis_block(t: Dictionary) -> String: return ("[b]Was:[/b] %s\n[b]Wann:[/b] %s\n[b]Warum:[/b] %s\n" + "[color=#9fb6ff][b]Zu sehen an:[/b] %s[/color]") % \ [t["was"], t["wann"], t["warum"], t["woran"]] # -------------------------------------------------------- Journal ## Das Journal ist immer dabei und führt Buch: welche Techniken du gelernt hast ## und welche Missionen du angenommen hast. Gelehrt wird im Hörsaal, ## vergeben werden die Missionen am Schwarzen Brett. func journal_panel(seite: String = "") -> void: if seite != "": _journal_seite = seite var scroll_alt := _scroll.scroll_vertical if offen and _kind == "journal" else 0 _leeren("Journal") _reiter() if _journal_seite == "missionen": _journal_missionen() else: _journal_techniken() _zeigen("journal") _scroll_wunsch = scroll_alt _scroll_frames = 4 func _reiter() -> void: var hb := HBoxContainer.new() hb.add_theme_constant_override("separation", 8) _content.add_child(hb) var seiten := [ ["techniken", "Techniken (%d)" % Game.known.size()], ["missionen", "Missionen (%d)" % Game.progress.size()], ] for eintrag in seiten: var b := Button.new() b.text = eintrag[1] b.custom_minimum_size = Vector2(0, 36) b.size_flags_horizontal = Control.SIZE_EXPAND_FILL b.add_theme_font_size_override("font_size", 17) b.disabled = _journal_seite == eintrag[0] if b.disabled: b.add_theme_color_override("font_disabled_color", Color(1, 0.88, 0.55)) hb.add_child(b) if not b.disabled: b.pressed.connect(func() -> void: journal_panel(eintrag[0])) var mission := "keine" if Game.active_mission != "": mission = "%s (%d %%)" % [HumanDB.by_id(Game.active_mission)["name"], int(Game.mission_progress(Game.active_mission))] _text("[color=#aaaaaa]Mission: %s[/color]" % mission, 15) _trenner() func _journal_techniken() -> void: if Game.known.is_empty(): _text("[color=#ffcc66]Du hast noch keine Technik gelernt.[/color]\n" + "Im [b]Hörsaal der Wege[/b] hinter dem Dorfplatz werden sie gelehrt.", 16) return _text("Was du gelernt hast. Besser wird eine Technik nur, indem du sie draußen " + "anwendest — Üben lässt sie sich nicht.", 15) _trenner() for id in TechDB.ids(): if not Game.knows(id): continue var t := TechDB.by_id(id) _text("[b][color=#ffe3a0]%s[/color][/b] · Stufe %d (%s)" % [t["name"], Game.skill_of(id), Game.xp_to_next(id)], 18) _text(_hinweis_block(t), 15) _trenner() func _journal_missionen() -> void: if Game.progress.is_empty(): _text("[color=#ffcc66]Du hast noch keine Mission angenommen.[/color]\n" + "Am [b]Schwarzen Brett[/b] am Dorfplatz hängen die Menschen aus, " + "die nicht weiterkommen.", 16) return _text("Die Menschen, für die du dich entschieden hast.", 15) _trenner() for h in HumanDB.LIST: var id: String = h["id"] if not Game.angenommen(id): continue _text("[b][color=#ffe3a0]%s[/color][/b], %d — %s" % [h["name"], h["alter"], h["beruf"]], 18) _text("[b]Traum:[/b] %s" % h["traum"], 16) _text("[b]Was im Weg steht:[/b] %s" % h["problem"], 15) if Game.completed.has(id): _text("[color=#88ff99]★ Der Traum ist erfüllt.[/color]", 15) elif Game.active_mission == id: _text("[color=#ffdd77]Aktive Mission — %d %%[/color]" % int(Game.mission_progress(id)), 15) else: _text("[color=#aaaaaa]Zurückgestellt — %d %%[/color]" % int(Game.mission_progress(id)), 15) if not Game.completed.has(id): _knopf("Tagesablauf — %d Systeme" % Game.system_anzahl(id), func() -> void: systeme_panel(id)) _trenner() # ---------------------------------------------- Diagnose und Systeme ## Der Traum scheitert an Ursachen. Die stehen nicht im Panel -- man findet sie, ## indem man dabeisteht. Gegen jede gefundene Ursache baut man ein System in ## den Tag. Je mehr abgedeckt ist, desto schneller kommt der Traum voran. func systeme_panel(human_id: String) -> void: var scroll_alt := _scroll.scroll_vertical if offen and _kind == "systeme" else 0 var h := HumanDB.by_id(human_id) _leeren("%s — Tagesablauf" % h["name"]) _text("[b]Traum:[/b] %s" % h["traum"], 16) _text("[b]Fortschritt:[/b] %d %% · [b]Tempo:[/b] %d %% je Tag" % [int(Game.mission_progress(human_id)), int(round(Game.traum_tempo(human_id)))], 16) _trenner() _text("[b]Woran es scheitert[/b]", 17) var offene := 0 for u in Game.ursachen_von(human_id): var name: String = str(u) if not Game.ursache_bekannt(human_id, name): _text("[color=#8e93a8]? — noch nicht erkannt[/color]", 15) offene += 1 continue var g := Game.ursache_geloest(human_id, name) var stand := "[color=#88ff99]gelöst[/color]" if g > 0.0 else \ "[color=#ff9977]offen · bremst auf %d %%[/color]" % \ int(float(Ursachen.LISTE[name]["faktor"]) * 100.0) _text("[b]%s[/b] — %s %s" % [Ursachen.LISTE[name]["name"], Ursachen.LISTE[name]["kurz"], stand], 15) if offene > 0: _text("[color=#aaaaaa]Steh dabei, dann füllt sich ihr Tag. Und drücke [b]R[/b], " + "wenn du meinst, dass eine Stunde den Traum aufhält.[/color]", 14) _trenner() _tagesablauf(human_id) _trenner() _knopf("Zurück zum Journal", func() -> void: journal_panel("missionen")) _zeigen("systeme") _scroll_wunsch = scroll_alt _scroll_frames = 4 ## Der Ablauf von 0 bis 23 Uhr. Jeder Tag ist gleich -- es gibt keine ## Wochentage. Zu Beginn ist jede Stunde leer; wer dabeisteht, füllt sie. func _tagesablauf(human_id: String) -> void: var offen := 0 for stunde in range(24): if not Game.gesehen_slot(human_id, Game.slot_bei_stunde(human_id, stunde)): offen += 1 _text("[b]Ihr Tag[/b] [color=#8e93a8]0 – 23 Uhr, jeden Tag derselbe%s[/color]" % ("" if offen == 0 else " · %d Stunden noch leer" % offen), 17) # eine Zeile je Abschnitt des Tages -- auch wenn noch keiner bekannt ist, # steht der Tag vollständig da var von := 0 while von < 24: var nr := Game.slot_bei_stunde(human_id, von) var bis := von while bis + 1 < 24 and Game.slot_bei_stunde(human_id, bis + 1) == nr: bis += 1 _stunden_zeile(human_id, nr, von, bis, _stunden_text(human_id, nr)) von = bis + 1 func _stunden_text(human_id: String, nr: int) -> String: var grund := Game.slot_grund(human_id, nr) match Game.slot_zustand(human_id, nr): Game.Stunde.UNBEKANNT: return "—" Game.Stunde.GEBAUT: var sys := Game.system_in(human_id, nr) return "%s ← %s" % [Game.slot_aktivitaet(human_id, nr), TechDB.by_id(str(sys["tech"]))["name"]] Game.Stunde.UNGEKLAERT: return "?" Game.Stunde.BENANNT: return "%s (%s)" % [Ursachen.BLOCKER[grund]["name"], Ursachen.LISTE[Ursachen.BLOCKER[grund]["ursache"]]["name"]] return Orte.text(grund) func _stunden_zeile(human_id: String, nr: int, von: int, bis: int, text: String) -> void: var spanne := "%02d – %02d" % [von, bis + 1 if bis < 23 else 24] var offen: bool = Game.gesehen_slot(human_id, nr) and Game.erkannt(human_id, nr) \ and Game.slot_grund(human_id, nr) != "schlaeft" _knopf("%s %s" % [spanne, text], func() -> void: slot_panel(human_id, nr), offen) ## Der offene Moment: hier zählt, welche Technik zu diesem Problem passt. func situation_panel(mensch: Node3D) -> void: _apply_human = mensch var id: String = str(mensch.data["id"]) if not Game.situation_bei(id): anwenden_panel(mensch) return var s: Dictionary = Probleme.SITUATIONEN[Game.situation["key"]] var problem := Game.situation_problem() _leeren("%s — %s" % [mensch.data["name"], s["name"]]) _text("[i]%s[/i]" % s["was"], 16) if Game.problem_bekannt(id, problem): _text("Dahinter steckt [b]%s[/b] — %s." % [Probleme.name_von(problem), Probleme.LISTE[problem]["kurz"]], 15) _text("[color=#aaaaaa]Überwunden nach %d von %d Malen.[/color]" % [Game.problem_fortschritt(id, problem), Game.PROBLEM_UEBERWINDEN], 14) else: _text("[color=#ffcc66]Was dahintersteckt, weißt du noch nicht.[/color]", 15) _text("[color=#aaaaaa]Noch %.0f Sekunden. Solange kommt ihr Traum nicht voran.[/color]" % float(Game.situation["rest"]), 14) _trenner() if Game.known.is_empty(): _text("[color=#ffcc66]Du kennst keine Technik.[/color]", 16) _zeigen("situation") return for tech_id in TechDB.ids(): if not Game.knows(tech_id): continue var t := TechDB.by_id(tech_id) var wofuer: Array = [] for tag in t["tags"]: wofuer.append(Probleme.name_von(str(tag))) _text("[b]%s[/b] · Stufe %d\n[color=#cccccc]%s[/color]\n[color=#9fb6ff]Hilft bei:[/color] %s" % [t["name"], Game.skill_of(tech_id), t["wann"], ", ".join(wofuer)], 15) var frei := Game.bereit(tech_id) var text := "Jetzt anwenden" if frei else "Noch %.0f s" % Game.cooldown_rest(tech_id) _knopf(text, func() -> void: Game.situation_anwenden(tech_id) schliessen(), frei) _trenner() _zeigen("situation") ## Was soll in diesem Slot stehen? func slot_panel(human_id: String, nr: int) -> void: var h := HumanDB.by_id(human_id) var grund := Game.slot_grund(human_id, nr) var stunde := "%02d:00" % int(Game.slots(human_id)[nr][0]) _leeren("%s um %s" % [h["name"], stunde]) if Ursachen.ist_blocker(grund): var b: Dictionary = Ursachen.BLOCKER[grund] _text("Sie %s.\n[b]Auslöser:[/b] %s\n[b]Belohnung:[/b] %s" % [b["name"], b["ausloeser"], Ursachen.belohnung_text(str(b["belohnung"]))], 16) _text("[color=#aaaaaa]Eine Ersatzhandlung muss dieselbe Belohnung liefern, " + "sonst hält sie nicht.[/color]", 15) else: _text("Hier ist Platz. Ein neues System passt in diesen Slot.", 16) _trenner() var sys := Game.system_in(human_id, nr) if not sys.is_empty(): _text("[color=#88ff99]Hier steht: %s[/color]" % TechDB.by_id(str(sys["tech"]))["name"], 16) _knopf("System wieder herausnehmen", func() -> void: Game.system_entfernen(human_id, nr) systeme_panel(human_id)) _trenner() for id in TechDB.ids(): if not Game.knows(id): continue var pruef := Game.system_pruefen(human_id, nr, id) var t := TechDB.by_id(id) var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(id)] if not pruef["geht"]: beschriftung += " — " + str(pruef["grund"]) elif float(pruef["guete"]) > Game.GUETE_NEU: beschriftung += " — passt genau" _knopf(beschriftung, func() -> void: _system_setzen(human_id, nr, id), bool(pruef["geht"])) _trenner() _knopf("Zurück", func() -> void: systeme_panel(human_id)) _zeigen("slot") func _system_setzen(human_id: String, nr: int, tech_id: String) -> void: var res := Game.system_setzen(human_id, nr, tech_id, false) if not res["ok"]: Game.notified.emit(str(res["grund"]), Color(1, 0.6, 0.4)) return if not res["erfolg"]: Game.notified.emit("%s greift diesmal nicht. Später noch einmal." % TechDB.by_id(tech_id)["name"], Color(1, 0.6, 0.5)) systeme_panel(human_id) # -------------------------------------------------------- Lehrorte ## Jeder Lehrort unterrichtet genau einen Weg: der Hörsaal den ersten, jedes ## Haus den, den sein Bewohner mitgebracht hat. func lehre_panel(ort: String = "hoersaal") -> void: if not DreamDB.PLACES.has(ort): return var scroll_alt := _scroll.scroll_vertical if offen and _kind == "lehre" else 0 var pl: Dictionary = DreamDB.PLACES[ort] var ids: Array = DreamDB.techniken_von(ort) _leeren(str(pl["name"])) _text("[color=#aaaaaa]Gelernt: [b]%d[/b] von %d entdeckten Wegen · %d im Spiel[/color]" % [Game.known.size(), Game.offene_techniken(), TechDB.LIST.size()], 15) _trenner() if ids.is_empty(): _text("[color=#ffcc66]Hier wird nichts gelehrt.[/color]", 16) for id in ids: var t := TechDB.by_id(id) if Game.knows(id): _text("[b][color=#ffe3a0]%s[/color][/b] · Stufe %d (%s)" % [t["name"], Game.skill_of(id), Game.xp_to_next(id)], 18) else: _text("[b][color=#9aa0b8]%s[/color][/b] · noch nicht gelernt" % t["name"], 18) _text(_hinweis_block(t), 15) if Game.knows(id): _text("[color=#88ff99]Gelernt. Stufe %d steigt nur, wenn du diesen Weg " % Game.skill_of(id) + "draußen anwendest — hier ist nichts mehr zu holen.[/color]", 15) else: _knopf("Diesen Weg lernen", func() -> void: if Game.learn(id): lehre_panel(ort)) _trenner() _naechster_weg() _zeigen("lehre") _scroll_wunsch = scroll_alt _scroll_frames = 4 ## Was als Nächstes ins Dorf käme. func _naechster_weg() -> void: var offen_zahl := Game.offene_techniken() if offen_zahl >= TechDB.LIST.size(): _text("[color=#88ff99]Alle zwölf Wege sind entdeckt. Das Dorf wächst weiter.[/color]", 15) return var naechste: Dictionary = TechDB.LIST[offen_zahl] _text("[color=#aaaaaa]Noch unentdeckt: [b]%s[/b] und %d weitere. Jeder erfüllte Traum " % [naechste["name"], TechDB.LIST.size() - offen_zahl - 1] + "bringt einen Bewohner ins Dorf, und er lehrt einen neuen Weg.[/color]", 15) # -------------------------------------------------------- Schwarzes Brett ## Hier hängen die Menschen aus, deren Traum feststeckt. func brett_panel() -> void: var scroll_alt := _scroll.scroll_vertical if offen and _kind == "brett" else 0 _leeren("Schwarzes Brett") _text("Ausgehängt wird nur, wem du mit dem, was du kannst, auch helfen kannst. " + "Das Tor zur Erde öffnet sich nur mit einer Mission.", 15) _trenner() var offen := Game.offene_missionen() var gezeigt := 0 var verdeckt := 0 for h in HumanDB.LIST: var id: String = h["id"] if Game.completed.has(id): continue if not offen.has(id): verdeckt += 1 continue gezeigt += 1 _text("[b][color=#ffe3a0]%s[/color][/b], %d — %s" % [h["name"], h["alter"], h["beruf"]], 18) _text("[b]Traum:[/b] %s" % h["traum"], 16) _text("[b]Was im Weg steht:[/b] %s" % h["problem"], 15) if Game.active_mission == id: _text("[color=#ffdd77]Aktive Mission — %d %%[/color]" % int(Game.mission_progress(id)), 15) else: if Game.angenommen(id): _text("[color=#aaaaaa]Schon einmal angenommen — %d %%[/color]" % int(Game.mission_progress(id)), 15) _knopf("Mission annehmen", func() -> void: Game.accept_mission(id) brett_panel()) _trenner() if gezeigt == 0: _text("[color=#ffcc66]Gerade hängt nichts aus, das zu deinen Wegen passt.[/color]\n" + "Lerne im Hörsaal oder in einem Haus einen weiteren Weg.", 16) if verdeckt > 0: _text("[color=#aaaaaa]%d weitere Anliegen bleiben verdeckt. Keiner deiner Wege " % verdeckt + "trifft ihr Thema in einem Moment ihres Tages.[/color]", 15) _zeigen("brett") _scroll_wunsch = scroll_alt _scroll_frames = 4 # -------------------------------------------------------- Vor Ort ## Draußen am Menschen: hier greift man in den Slot ein, der gerade läuft. func anwenden_panel(mensch: Node3D) -> void: _apply_human = mensch var h: Dictionary = mensch.data var id: String = str(h["id"]) var nr: int = int(mensch.slot) _leeren("%s — %s" % [h["name"], Orte.text(mensch.activity)]) _text("[b]Traum:[/b] %s" % h["traum"], 16) if Game.active_mission != id: _text("[color=#ffcc66]Keine aktive Mission für diesen Menschen.[/color]", 15) _zeigen("anwenden") return var grund := Game.slot_grund(id, nr) if not Game.erkannt(id, nr): _text("[color=#ffcc66]Was hier läuft, hast du noch nicht notiert. " + "Sieh ihr zu und drücke [b]R[/b], wenn du meinst, hier läuft etwas " + "schief.[/color]", 16) _zeigen("anwenden") return if Ursachen.ist_blocker(grund): var b: Dictionary = Ursachen.BLOCKER[grund] _text("Sie %s. [b]Auslöser:[/b] %s · [b]Belohnung:[/b] %s" % [b["name"], b["ausloeser"], Ursachen.belohnung_text(str(b["belohnung"]))], 15) _trenner() if Game.known.is_empty(): _text("[color=#ffcc66]Du kennst keine Technik.[/color]") _zeigen("anwenden") return _text("[color=#aaaaaa]Was eine Technik tut, steht im Journal.[/color]", 15) for tid in TechDB.ids(): if not Game.knows(tid): continue var pruef := Game.system_pruefen(id, nr, tid) var t := TechDB.by_id(tid) var beschriftung := "%s · Stufe %d" % [t["name"], Game.skill_of(tid)] if not pruef["geht"]: beschriftung += " — " + str(pruef["grund"]) elif mensch.resist_left > 0.0: beschriftung += " — macht gerade dicht" _knopf(beschriftung, func() -> void: _vor_ort_setzen(tid), bool(pruef["geht"])) _zeigen("anwenden") func _vor_ort_setzen(tech_id: String) -> void: if _apply_human == null or not is_instance_valid(_apply_human): schliessen() return var mensch := _apply_human var id: String = str(mensch.data["id"]) var res := Game.system_setzen(id, int(mensch.slot), tech_id, mensch.resist_left > 0.0) schliessen() if not res["ok"]: Game.notified.emit(str(res["grund"]), Color(1, 0.6, 0.4)) return var t := TechDB.by_id(tech_id) if res["erfolg"]: mensch.reagiere(true, "%s\nsteht" % t["name"]) else: mensch.reagiere(false, "%s greift nicht" % t["name"]) func _unhandled_input(event: InputEvent) -> void: if offen and event.is_action_pressed("ui_cancel"): schliessen() get_viewport().set_input_as_handled()