diff --git a/.godot/editor/filesystem_cache10 b/.godot/editor/filesystem_cache10 index 5d0f1ba..71c2bfb 100644 --- a/.godot/editor/filesystem_cache10 +++ b/.godot/editor/filesystem_cache10 @@ -1,29 +1,29 @@ 78098b89c88a74ebe8c20117b8754620 ::res://::1788260833 anforderungen.md::TextFile::-1::1788584851::0::1::::<><><>0<>0<><>:: -README.md::TextFile::-1::1788598652::0::1::::<><><>0<>0<><>:: +README.md::TextFile::-1::1789506499::0::1::::<><><>0<>0<><>:: ::res://scenes/::1788012512 main.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://scripts/main.gd -::res://scripts/::1789505734 -aushaenge.gd::GDScript::4842442143352950821::1789505730::0::1::::Aushaenge<>RefCounted<><>0<>0<><>:: +::res://scripts/::1789506182 +aushaenge.gd::GDScript::4842442143352950821::1789506182::0::1::::Aushaenge<>RefCounted<><>0<>0<><>:: bewohner.gd::GDScript::2125278938812678811::1789505829::0::1::::Bewohner<>Node3D<><>0<>0<><>:: dream_db.gd::GDScript::4217518896355437099::1788260794::0::1::::DreamDB<>RefCounted<><>0<>0<><>:: dream_world.gd::GDScript::8279158278942867258::1788260794::0::1::::DreamWorld<>Node3D<><>0<>0<><>:: earth_world.gd::GDScript::6086818627272428934::1788260794::0::1::::EarthWorld<>Node3D<><>0<>0<><>:: game.gd::GDScript::3600922541500868814::1789505829::0::1::::<>Node<><>0<>0<><>:: -human_db.gd::GDScript::412604892032291296::1789505730::0::1::::HumanDB<>RefCounted<><>0<>0<><>:: +human_db.gd::GDScript::412604892032291296::1789506499::0::1::::HumanDB<>RefCounted<><>0<>0<><>:: main.gd::GDScript::1504009331368977934::1788598457::0::1::::<>Node3D<><>0<>0<><>:: netz.gd::GDScript::8422943321868648339::1788260794::0::1::::Netz<>RefCounted<><>0<>0<><>:: orte.gd::GDScript::5536340784294498671::1788260833::0::1::::Orte<>RefCounted<><>0<>0<><>:: player.gd::GDScript::8724108548430371398::1788459499::0::1::::Player<>CharacterBody3D<><>0<>0<><>:: prims.gd::GDScript::322632462553361584::1788088940::0::1::::Prims<>RefCounted<><>0<>0<><>:: probleme.gd::GDScript::4928319609961045127::1789505398::0::1::::Probleme<>RefCounted<><>0<>0<><>:: -technique_db.gd::GDScript::4543286561661639701::1789505829::0::1::::TechDB<>RefCounted<><>0<>0<><>:: -ui.gd::GDScript::843471304695966286::1789506056::0::1::::UI<>CanvasLayer<><>0<>0<><>:: +technique_db.gd::GDScript::4543286561661639701::1789507527::0::1::::TechDB<>RefCounted<><>0<>0<><>:: +ui.gd::GDScript::843471304695966286::1789507751::0::1::::UI<>CanvasLayer<><>0<>0<><>:: ursachen.gd::GDScript::7371116734724270900::1789505398::0::1::::Ursachen<>RefCounted<><>0<>0<><>:: woerter.gd::GDScript::1848212517770692820::1789505563::0::1::::Woerter<>RefCounted<><>0<>0<><>:: -::res://tests/::1788598610 +::res://tests/::1789506609 shots.gd::GDScript::5999651346126871724::1788260833::0::1::::<>Node<><>0<>0<><>:: shots.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://tests/shots.gd -verify.gd::GDScript::4392606218392155269::1789505952::0::1::::<>Node<><>0<>0<><>:: +verify.gd::GDScript::4392606218392155269::1789507588::0::1::::<>Node<><>0<>0<><>:: verify.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://tests/verify.gd diff --git a/README.md b/README.md index 733b1fe..93b0cde 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,22 @@ Steuerung: **WASD** bewegen · **Maus** umsehen · **E** interagieren · **F** folgen · **R** notieren · **J** Journal · **1/2/3** Tempo · **Esc** Maus freigeben. -Das **HUD** zeigt vier Zeilen: Zeit, die laufende Mission, den Tagesstreifen — -24 Felder, eines je Stunde, die laufende hervorgehoben — und darunter, was -gerade läuft. Grau heißt unbekannt, gelb „hier stimmt etwas nicht, aber du hast -es noch nicht benannt", rot ein benannter Blocker, grün ein gebautes System. -Bricht ein Moment auf, verdrängt er die Stundenzeile: er ist gleich vorbei. -Alles Weitere steht im Journal, nichts davon auf dem Schirm. +**Die Oberfläche** ist um den Tag gebaut. Unten über die ganze Breite liegt +die **Zeitleiste**: 24 Felder, eines je Stunde, die laufende steht heraus. +Grau heißt unbekannt, gelb „hier stimmt etwas nicht, noch nicht benannt", rot +ein benannter Blocker, grün ein gebautes System. Links darüber die Ursachen, +rechts die Momente; bricht einer auf, pulsiert die laufende Stunde und rechts +läuft die Uhr. Ein Klick auf eine Stunde (Maus mit **Esc** freigeben) öffnet +ihre Karte direkt darüber — was passiert, Auslöser, Belohnung, welche Technik +passt. + +Das **Journal** (J) ist eine Seitenleiste rechts, die Welt bleibt sichtbar: +Techniken und Missionen als Karten mit Farbkante, Details auf Klick. Karten +sind Daten, Knöpfe sind Aktionen — es gibt je Panel höchstens eine +hervorgehobene. Fenster in der Mitte gibt es nur an Orten: Brett, Hörsaal, +Aushang schreiben. **Esc** geht eine Ebene zurück, nicht gleich ganz zu. + +Oben links stehen nur Zeit und Mission. Alles Weitere steht im Journal. In der Traumwelt springt **Leertaste**; auf der Erde steigt sie und **Umschalt** lässt sinken. diff --git a/scripts/technique_db.gd b/scripts/technique_db.gd index afc27be..e55ebd1 100644 --- a/scripts/technique_db.gd +++ b/scripts/technique_db.gd @@ -191,7 +191,7 @@ static var LIST: Array = [ "was": "Ab einer festen Uhrzeit bleiben Handy und Fernseher aus. Stattdessen etwas, das die Hände beschäftigt.", "wann": "Wenn der Abend im Scrollen oder in Serien versickert und morgens nichts übrig ist.", "warum": "Bildschirme versprechen Abschalten und liefern Reizung. Der Kopf braucht das Gegenteil.", - "woran": "jemandem, der abends eine Serie schaut oder endlos scrollt.", + "woran": "jemandem, der abends eine Serie schaut und danach wach bleibt.", "ursache": "kraft", "belohnung": "abschalten", "ersatz": "lässt den Bildschirm aus", @@ -230,7 +230,7 @@ static var LIST: Array = [ "was": "Sich mit jemandem zu einer festen Zeit verabreden, um gemeinsam oder nebeneinander zu arbeiten.", "wann": "Wenn man allein immer wieder aussteigt.", "warum": "Vor einem anderen fällt Aufhören schwerer als vor sich selbst.", - "woran": "jemandem, der allein festhängt oder Verabredungen absagt.", + "woran": "jemandem, der allein immer wieder auf etwas anderes umschaltet.", "ursache": "halt", "belohnung": "verbundenheit", "ersatz": "meldet sich zum Dranbleiben", @@ -256,7 +256,7 @@ static var LIST: Array = [ "was": "Die gefürchtete Sache in Stufen teilen, von harmlos bis schwer, und jede Woche eine Sprosse höher.", "wann": "Wenn etwas so viel Angst macht, dass es immer wieder abgesagt wird.", "warum": "Angst schrumpft durch Gewöhnung, nicht durch Mut. Kleine Schritte gewöhnen.", - "woran": "jemandem, der Termine absagt oder vor dem Berg erstarrt.", + "woran": "jemandem, der recherchiert und Listen schreibt, statt den ersten Schritt zu tun.", "ursache": "anfang", "belohnung": "erfolg", "ersatz": "nimmt die nächste Sprosse", @@ -269,7 +269,7 @@ static var LIST: Array = [ "was": "Sorgen auf eine feste Viertelstunde am Tag vertagen. Was vorher kommt, wird notiert und wartet.", "wann": "Wenn Sorgen den ganzen Tag dazwischenfunken.", "warum": "Sorgen lassen sich nicht abstellen, aber verschieben. Verschoben verlieren sie an Gewicht.", - "woran": "jemandem, der drei Dinge gleichzeitig anfängt oder nachts wach liegt.", + "woran": "jemandem, der die Sache nachts im Bett wälzt.", "ursache": "grund", "belohnung": "einordnung", "ersatz": "parkt die Sorge bis fünf", diff --git a/scripts/ui.gd b/scripts/ui.gd index 686efb7..7a5548c 100644 --- a/scripts/ui.gd +++ b/scripts/ui.gd @@ -1,27 +1,59 @@ extends CanvasLayer class_name UI +## Die Oberfläche. Ein Baukasten (_text, _karte, _knopf) füllt immer denselben +## Inhalt (_content); drei Wirte zeigen ihn: die Seitenleiste rechts für das +## Journal, die Karte über der Zeitleiste für alles am Menschen, das Fenster in +## der Mitte für Orte. Die Zeitleiste unten ist der Tag des Menschen -- alles +## im Spiel hängt daran. signal panel_opened signal panel_closed const BREITE := 900 +const BREITE_FORMULAR := 1080 +const DRAWER_BREITE := 470.0 +const KARTE_BREITE := 780.0 +const LEISTE_H := 116.0 + +const C_PANEL := Color(0.09, 0.09, 0.16, 0.97) +const C_ROW := Color(0.14, 0.15, 0.24) +const C_ROW_HOVER := Color(0.20, 0.22, 0.34) +const C_AKZENT := Color(0.50, 0.84, 1.0) +const C_TEXT := Color(0.92, 0.93, 0.97) +const C_MUTED := Color(0.56, 0.58, 0.68) +const C_UNBEKANNT := Color(0.22, 0.23, 0.30) +const C_GESEHEN := Color(0.46, 0.50, 0.62) +const C_SCHLAF := Color(0.30, 0.33, 0.44) +const C_WARN := Color(0.85, 0.66, 0.32) +const C_BLOCKER := Color(0.84, 0.42, 0.35) +const C_SYSTEM := Color(0.44, 0.78, 0.50) var offen := false var _kind := "" +var _root: Control var _hud: RichTextLabel var _hinweis: Label var _log: VBoxContainer -var _ablauf: HBoxContainer -var _ablauf_jetzt: RichTextLabel -var _ablauf_grund: ColorRect +# Zeitleiste +var _ablauf: PanelContainer +var _ablauf_titel: Label +var _ursachen: RichTextLabel +var _probleme: RichTextLabel var _zellen: Array = [] var _ticks: Array = [] +var _ablauf_jetzt: RichTextLabel +# Wirte und Inhalt +var _drawer: PanelContainer +var _karte_host: PanelContainer +var _modal: PanelContainer var _dim: ColorRect -var _panel: PanelContainer +var _box: VBoxContainer var _titel: Label -var _content: VBoxContainer var _scroll: ScrollContainer +var _content: VBoxContainer +var _ziel: Container = null # wohin die Helfer gerade bauen +var _verlauf: Array = [] # wohin Esc zurückführt var _apply_human: Node3D var _journal_seite := "techniken" var _scroll_wunsch := 0 @@ -30,13 +62,64 @@ var _scroll_frames := 0 func _ready() -> void: layer = 10 + _root = Control.new() + _root.set_anchors_preset(Control.PRESET_FULL_RECT) + _root.mouse_filter = Control.MOUSE_FILTER_IGNORE + _root.theme = _theme() + add_child(_root) _baue_hud() - _baue_panel() + _baue_leiste() + _baue_wirte() Game.notified.connect(_notiz) Game.state_changed.connect(_hud_aktualisieren) _hud_aktualisieren() +# ------------------------------------------------------------------ Theme + +func _sb(bg: Color, radius: int = 6, rand: Color = Color(0, 0, 0, 0), rand_breite: int = 0, + links: int = 0) -> StyleBoxFlat: + var s := StyleBoxFlat.new() + s.bg_color = bg + s.set_corner_radius_all(radius) + s.border_color = rand + s.set_border_width_all(rand_breite) + if links > 0: + s.border_width_left = links + s.content_margin_left = 12 + s.content_margin_right = 12 + s.content_margin_top = 6 + s.content_margin_bottom = 6 + return s + + +## Dunkel, flach, eine Akzentfarbe. Einmal gesetzt, gilt es überall darunter. +func _theme() -> Theme: + var th := Theme.new() + th.set_stylebox("normal", "Button", _sb(C_ROW)) + th.set_stylebox("hover", "Button", _sb(C_ROW_HOVER)) + th.set_stylebox("pressed", "Button", _sb(C_ROW_HOVER.lightened(0.1))) + th.set_stylebox("disabled", "Button", _sb(C_ROW.darkened(0.25))) + th.set_stylebox("focus", "Button", StyleBoxEmpty.new()) + th.set_color("font_color", "Button", C_TEXT) + th.set_color("font_hover_color", "Button", Color.WHITE) + th.set_color("font_disabled_color", "Button", C_MUTED) + for typ in ["LineEdit", "TextEdit", "SpinBox", "OptionButton"]: + th.set_stylebox("normal", typ, _sb(Color(0.06, 0.06, 0.11), 5, C_MUTED, 1)) + th.set_stylebox("focus", typ, _sb(Color(0.06, 0.06, 0.11), 5, C_AKZENT, 1)) + th.set_stylebox("hover", "OptionButton", _sb(C_ROW_HOVER, 5, C_MUTED, 1)) + th.set_color("font_color", "LineEdit", C_TEXT) + th.set_color("font_color", "TextEdit", C_TEXT) + th.set_color("font_color", "CheckBox", C_TEXT) + th.set_color("font_color", "Label", C_TEXT) + th.set_color("default_color", "RichTextLabel", C_TEXT) + th.set_stylebox("panel", "PanelContainer", _sb(C_PANEL, 10, C_AKZENT * Color(1, 1, 1, 0.45), 1)) + th.set_stylebox("panel", "ScrollContainer", StyleBoxEmpty.new()) + return th + + +# ------------------------------------------------------------------ HUD + func _baue_hud() -> void: _hud = RichTextLabel.new() _hud.bbcode_enabled = true @@ -44,14 +127,12 @@ func _baue_hud() -> void: _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.custom_minimum_size = Vector2(420, 50) + _hud.size = Vector2(420, 50) _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() + _root.add_child(_hud) var steuerung := Label.new() steuerung.text = "J Journal · 1-3 Tempo · Esc" @@ -62,203 +143,29 @@ func _baue_hud() -> void: steuerung.add_theme_font_size_override("font_size", 14) steuerung.add_theme_color_override("font_color", Color(1, 1, 1, 0.4)) steuerung.mouse_filter = Control.MOUSE_FILTER_IGNORE - add_child(steuerung) + _root.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.position = Vector2(-320, -(LEISTE_H + 44)) + _hinweis.size = Vector2(640, 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) + _root.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.position = Vector2(-520, -(LEISTE_H + 110)) + _log.size = Vector2(500, 90) _log.alignment = BoxContainer.ALIGNMENT_END _log.mouse_filter = Control.MOUSE_FILTER_IGNORE - add_child(_log) + _root.add_child(_log) -## Der Tagesablauf im HUD: 24 Felder, eines je Stunde. Kein Titel, keine -## Erklärung -- der Streifen erklärt sich, sobald man eine Stunde gefüllt hat. -const ABLAUF_X := 18.0 -const ABLAUF_Y := 74.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.38) - _ablauf_grund.set_anchors_preset(Control.PRESET_TOP_LEFT) - _ablauf_grund.position = Vector2(ABLAUF_X - 8.0, ABLAUF_Y - 6.0) - _ablauf_grund.mouse_filter = Control.MOUSE_FILTER_IGNORE - add_child(_ablauf_grund) - - _ablauf = HBoxContainer.new() - _ablauf.set_anchors_preset(Control.PRESET_TOP_LEFT) - _ablauf.position = Vector2(ABLAUF_X, ABLAUF_Y) - _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, 14) - zelle.mouse_filter = Control.MOUSE_FILTER_IGNORE - _ablauf.add_child(zelle) - _zellen.append(zelle) - - 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) - 2.0, - ABLAUF_Y + 18.0) - tick.add_theme_font_size_override("font_size", 10) - tick.add_theme_color_override("font_color", Color(1, 1, 1, 0.35)) - tick.mouse_filter = Control.MOUSE_FILTER_IGNORE - add_child(tick) - _ticks.append(tick) - - _ablauf_jetzt = RichTextLabel.new() - _ablauf_jetzt.bbcode_enabled = true - _ablauf_jetzt.fit_content = true - _ablauf_jetzt.scroll_active = false - _ablauf_jetzt.set_anchors_preset(Control.PRESET_TOP_LEFT) - _ablauf_jetzt.position = Vector2(ABLAUF_X, ABLAUF_Y + 34.0) - _ablauf_jetzt.custom_minimum_size = Vector2(breite, 20) - _ablauf_jetzt.size = Vector2(breite, 20) - _ablauf_jetzt.mouse_filter = Control.MOUSE_FILTER_IGNORE - _ablauf_jetzt.add_theme_font_size_override("normal_font_size", 14) - _ablauf_jetzt.add_theme_font_size_override("bold_font_size", 14) - 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_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) - for stunde in range(24): - var f := _ablauf_farbe(id, Game.slot_bei_stunde(id, stunde)) - var zelle: ColorRect = _zellen[stunde] - zelle.color = f.lightened(0.55) if stunde == jetzt else f - zelle.custom_minimum_size.y = 18 if stunde == jetzt else 14 - - # Der offene Moment verdrängt die Stundenzeile -- er ist gleich vorbei - var zeilen: Array = [] - if Game.situation_bei(id): - var s: Dictionary = Probleme.SITUATIONEN[Game.situation["key"]] - zeilen.append("[color=#ffcc66][b]» %s «[/b][/color] [b]%.0f s[/b]" % - [s["name"], float(Game.situation["rest"])]) - else: - zeilen.append("[color=#cfd6ff]%02d[/color] %s" % - [jetzt, _stunden_text(id, Game.slot_bei_stunde(id, jetzt))]) - var offen: Array = [] - for p in Game.probleme_von(id): - var key := str(p) - if not Game.problem_bekannt(id, key): - continue - if Game.problem_ueberwunden(id, key): - offen.append("[color=#88ff99]%s ✓[/color]" % Probleme.name_von(key)) - else: - offen.append("[color=#ffb0a0]%s %d/%d[/color]" % [Probleme.name_von(key), - Game.problem_fortschritt(id, key), Game.PROBLEM_UEBERWINDEN]) - if not offen.is_empty(): - zeilen.append(" ".join(offen)) - _ablauf_jetzt.text = "\n".join(zeilen) - var hoehe := 62.0 + (20.0 if zeilen.size() > 1 else 0.0) - _ablauf_grund.size = Vector2(24.0 * (ZELLE_BREIT + ZELLE_LUECKE) + 14.0, hoehe) - - -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) - - - - -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 kopf := "[b]%s[/b]" % Game.time_string() if Game.schlaeft_ziel(): @@ -303,53 +210,356 @@ func _notiz(text: String, farbe: Color) -> void: tw.tween_callback(l.queue_free) -# ------------------------------------------------------------------ Panel +# ------------------------------------------------------------ Zeitleiste +# Der Tag des Menschen, unten über die ganze Breite. 24 Felder, die laufende +# Stunde steht heraus, links die Ursachen, rechts die Momente. Ein Klick auf +# eine Stunde öffnet ihre Karte darüber. + +func _baue_leiste() -> void: + _ablauf = PanelContainer.new() + _ablauf.set_anchors_preset(Control.PRESET_BOTTOM_WIDE) + _ablauf.offset_left = 12 + _ablauf.offset_right = -12 + _ablauf.offset_top = -LEISTE_H + _ablauf.offset_bottom = -10 + _ablauf.add_theme_stylebox_override("panel", _sb(Color(0.06, 0.06, 0.11, 0.92), 10, + C_AKZENT * Color(1, 1, 1, 0.3), 1)) + _root.add_child(_ablauf) + + var vb := VBoxContainer.new() + vb.add_theme_constant_override("separation", 4) + _ablauf.add_child(vb) + + var kopf := HBoxContainer.new() + kopf.add_theme_constant_override("separation", 16) + vb.add_child(kopf) + _ablauf_titel = Label.new() + _ablauf_titel.add_theme_font_size_override("font_size", 13) + _ablauf_titel.add_theme_color_override("font_color", C_MUTED) + kopf.add_child(_ablauf_titel) + _ursachen = _hud_zeile(14) + _ursachen.size_flags_horizontal = Control.SIZE_EXPAND_FILL + kopf.add_child(_ursachen) + _probleme = _hud_zeile(14) + _probleme.size_flags_horizontal = Control.SIZE_EXPAND_FILL + kopf.add_child(_probleme) + + var reihe := HBoxContainer.new() + reihe.add_theme_constant_override("separation", 3) + vb.add_child(reihe) + for stunde in range(24): + var zelle := ColorRect.new() + zelle.custom_minimum_size = Vector2(10, 16) + zelle.size_flags_horizontal = Control.SIZE_EXPAND_FILL + zelle.size_flags_vertical = Control.SIZE_SHRINK_END + zelle.mouse_filter = Control.MOUSE_FILTER_STOP + zelle.gui_input.connect(func(ev: InputEvent) -> void: + if ev is InputEventMouseButton and ev.pressed and ev.button_index == MOUSE_BUTTON_LEFT: + _stunde_klick(stunde)) + reihe.add_child(zelle) + _zellen.append(zelle) + reihe.custom_minimum_size.y = 24 + + var ticks := HBoxContainer.new() + ticks.add_theme_constant_override("separation", 3) + vb.add_child(ticks) + for stunde in range(24): + var t := Label.new() + t.text = "%d" % stunde if stunde % 3 == 0 else "" + t.size_flags_horizontal = Control.SIZE_EXPAND_FILL + t.add_theme_font_size_override("font_size", 10) + t.add_theme_color_override("font_color", Color(1, 1, 1, 0.35)) + ticks.add_child(t) + _ticks.append(t) + + _ablauf_jetzt = _hud_zeile(15) + vb.add_child(_ablauf_jetzt) + + +func _hud_zeile(groesse: int) -> RichTextLabel: + var r := RichTextLabel.new() + r.bbcode_enabled = true + r.fit_content = true + r.scroll_active = false + r.mouse_filter = Control.MOUSE_FILTER_IGNORE + r.add_theme_font_size_override("normal_font_size", groesse) + r.add_theme_font_size_override("bold_font_size", groesse) + return r + + +func _ablauf_farbe(human_id: String, nr: int) -> Color: + match Game.slot_zustand(human_id, nr): + Game.Stunde.UNBEKANNT: + return C_UNBEKANNT + Game.Stunde.GEBAUT: + return C_SYSTEM + Game.Stunde.UNGEKLAERT: + return C_WARN + Game.Stunde.BENANNT: + return C_BLOCKER + Game.Stunde.SCHLAF: + return C_SCHLAF + return C_GESEHEN + + +func _ablauf_aktualisieren() -> void: + var id := Game.active_mission + var an := id != "" + _ablauf.visible = an + if not an: + return + var jetzt := int(Game.time_h) + var offen_std := 0 + var puls := 0.5 + 0.5 * sin(Time.get_ticks_msec() * 0.008) + 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_std += 1 + var zelle: ColorRect = _zellen[stunde] + if stunde == jetzt: + f = f.lightened(0.55 if not Game.situation_bei(id) else 0.3 + 0.4 * puls) + zelle.color = f + zelle.custom_minimum_size.y = 24 if stunde == jetzt else 16 + zelle.tooltip_text = "%02d Uhr — %s" % [stunde, _stunden_text(id, nr)] + _ablauf_titel.text = "Ihr Tag 0 – 23 Uhr%s" % \ + ("" if offen_std == 0 else " · %d Stunden unbekannt" % offen_std) + + var urs: Array = [] + for u in Game.ursachen_von(id): + var name := str(Ursachen.LISTE[u]["name"]) + if Game.ursache_geloest(id, str(u)) > 0.0: + urs.append("[color=#7bea8f]%s[/color]" % name) + elif Game.ursache_bekannt(id, str(u)): + urs.append("[color=#ff9a8a]%s[/color]" % name) + else: + urs.append("[color=#55596b]?[/color]") + _ursachen.text = "[center]" + " · ".join(urs) + "[/center]" + + var zeilen: Array = [] + if Game.situation_bei(id): + var s: Dictionary = Probleme.SITUATIONEN[Game.situation["key"]] + zeilen.append("[right][color=#ffcc66][b]Jetzt: %s[/b][/color] %s [b]%.0f s[/b][/right]" % + [s["name"], s["was"], float(Game.situation["rest"])]) + else: + var chips: Array = [] + for pr in Game.probleme_von(id): + var key := str(pr) + if not Game.problem_bekannt(id, key): + chips.append("[color=#55596b]?[/color]") + elif Game.problem_ueberwunden(id, key): + chips.append("[color=#7bea8f]%s ✓[/color]" % Probleme.name_von(key)) + else: + chips.append("[color=#ffb0a0]%s %d/%d[/color]" % [Probleme.name_von(key), + Game.problem_fortschritt(id, key), Game.PROBLEM_UEBERWINDEN]) + zeilen.append("[right]" + " ".join(chips) + "[/right]") + _probleme.text = "\n".join(zeilen) + _ablauf_jetzt.text = "%02d Uhr [color=#cfd6ff]%s[/color]" % \ + [jetzt, _stunden_text(id, Game.slot_bei_stunde(id, jetzt))] + + +## Klick auf eine Stunde: ihre Karte über der Leiste. +func _stunde_klick(stunde: int) -> void: + var id := Game.active_mission + if id == "": + return + slot_panel(id, Game.slot_bei_stunde(id, stunde)) + + +# ------------------------------------------------------------------ Wirte + +func _baue_wirte() -> void: + _dim = ColorRect.new() + _dim.color = Color(0, 0, 0, 0.55) + _dim.set_anchors_preset(Control.PRESET_FULL_RECT) + _dim.visible = false + _dim.mouse_filter = Control.MOUSE_FILTER_IGNORE + _root.add_child(_dim) + + _box = VBoxContainer.new() + _box.add_theme_constant_override("separation", 10) + _titel = Label.new() + _titel.add_theme_font_size_override("font_size", 24) + _titel.add_theme_color_override("font_color", Color(1, 0.93, 0.75)) + _box.add_child(_titel) + _scroll = ScrollContainer.new() + _scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + _scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL + _box.add_child(_scroll) + _content = VBoxContainer.new() + _content.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _content.add_theme_constant_override("separation", 8) + _scroll.add_child(_content) + + _drawer = PanelContainer.new() + _drawer.set_anchors_preset(Control.PRESET_RIGHT_WIDE) + _drawer.offset_left = -DRAWER_BREITE - 12 + _drawer.offset_right = -12 + _drawer.offset_top = 56 + _drawer.offset_bottom = -(LEISTE_H + 22) + _drawer.visible = false + _root.add_child(_drawer) + + _karte_host = PanelContainer.new() + _karte_host.set_anchors_preset(Control.PRESET_TOP_LEFT) + _karte_host.custom_minimum_size.x = KARTE_BREITE + _karte_host.visible = false + _root.add_child(_karte_host) + + _modal = PanelContainer.new() + _modal.set_anchors_preset(Control.PRESET_CENTER) + _modal.visible = false + _root.add_child(_modal) + + +func _wirt_fuer(kind: String) -> PanelContainer: + match kind: + "journal", "technik", "systeme": + return _drawer + "slot", "situation", "anwenden": + return _karte_host + return _modal + + +func _process(_delta: float) -> void: + if not offen: + _hud_aktualisieren() + return + var wirt := _wirt_fuer(_kind) + var wunsch := _content.get_combined_minimum_size().y + var vp := get_viewport().get_visible_rect().size + if wirt == _drawer: + _scroll.custom_minimum_size.y = maxf(120.0, _drawer.size.y - 90.0) + else: + var platz: float = vp.y - LEISTE_H - 120.0 if wirt == _karte_host else vp.y - 200.0 + _scroll.custom_minimum_size.y = clampf(wunsch, 60.0, platz) + if _scroll_frames > 0: + _scroll.scroll_vertical = _scroll_wunsch + _scroll_frames -= 1 + if wirt == _modal: + var breite: float = BREITE_FORMULAR if _kind == "schreiben" else BREITE + _modal.custom_minimum_size.x = breite + _scroll.custom_minimum_size.x = breite - 48 + _modal.reset_size() + _modal.position = (vp - _modal.size) * 0.5 + elif wirt == _karte_host: + _scroll.custom_minimum_size.x = KARTE_BREITE - 48 + _karte_host.reset_size() + _karte_host.position = Vector2((vp.x - _karte_host.size.x) * 0.5, + vp.y - LEISTE_H - 22.0 - _karte_host.size.y) + _ablauf_aktualisieren() + + +# ------------------------------------------------------------------ Baukasten + +func _wo() -> Container: + return _ziel if _ziel != null else _content + func _leeren(titel: String) -> void: + _ziel = null for c in _content.get_children(): _content.remove_child(c) c.queue_free() _titel.text = titel -func _text(bb: String, groesse: int = 17) -> RichTextLabel: +func _text(bb: String, groesse: int = 17, in_: Container = null) -> 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.size_flags_horizontal = Control.SIZE_EXPAND_FILL 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) + (in_ if in_ != null else _wo()).add_child(r) return r -func _knopf(beschriftung: String, cb: Callable, aktiv: bool = true) -> Button: +## Eine Aktion: farbig, mittig. Nur für Dinge, die etwas tun. +func _knopf(beschriftung: String, cb: Callable, aktiv: bool = true, in_: Container = null) -> Button: var b := Button.new() b.text = beschriftung b.disabled = not aktiv - b.custom_minimum_size = Vector2(0, 32) + b.custom_minimum_size = Vector2(0, 34) b.add_theme_font_size_override("font_size", 16) - _content.add_child(b) + b.add_theme_stylebox_override("normal", _sb(C_AKZENT.darkened(0.62), 6, C_AKZENT, 1)) + b.add_theme_stylebox_override("hover", _sb(C_AKZENT.darkened(0.48), 6, C_AKZENT, 1)) + b.add_theme_stylebox_override("pressed", _sb(C_AKZENT.darkened(0.35), 6, C_AKZENT, 1)) + (in_ if in_ != null else _wo()).add_child(b) if aktiv: b.pressed.connect(cb) return b +## Eine Datenzeile: linksbündig, Farbkante für den Zustand. Klick öffnet Details. +func _karte(beschriftung: String, farbe: Color, cb: Callable, aktiv: bool = true, + in_: Container = null) -> Button: + var b := Button.new() + b.text = beschriftung + b.alignment = HORIZONTAL_ALIGNMENT_LEFT + b.disabled = not aktiv + b.custom_minimum_size = Vector2(0, 34) + b.add_theme_font_size_override("font_size", 15) + b.add_theme_stylebox_override("normal", _sb(C_ROW, 6, farbe, 0, 4)) + b.add_theme_stylebox_override("hover", _sb(C_ROW_HOVER, 6, farbe, 0, 4)) + b.add_theme_stylebox_override("pressed", _sb(C_ROW_HOVER.lightened(0.1), 6, farbe, 0, 4)) + b.add_theme_stylebox_override("disabled", _sb(C_ROW.darkened(0.25), 6, farbe.darkened(0.45), 0, 4)) + (in_ if in_ != null else _wo()).add_child(b) + if aktiv: + b.pressed.connect(cb) + return b + + +## Leise Aktion: zurück, abbrechen. +func _knopf_leise(beschriftung: String, cb: Callable, in_: Container = null) -> Button: + var b := Button.new() + b.text = beschriftung + b.custom_minimum_size = Vector2(0, 30) + b.add_theme_font_size_override("font_size", 14) + b.add_theme_color_override("font_color", C_MUTED) + (in_ if in_ != null else _wo()).add_child(b) + b.pressed.connect(cb) + return b + + func _trenner() -> void: var s := HSeparator.new() - _content.add_child(s) + _wo().add_child(s) -func _zeigen(kind: String) -> void: +## Zeigt den gebauten Inhalt im passenden Wirt. `zurueck` ist der Weg, den Esc +## nimmt -- nur setzen, wenn das Panel aus einem anderen heraus geöffnet wurde. +func _zeigen(kind: String, zurueck: Callable = Callable()) -> void: _kind = kind - offen = true - _dim.visible = true - _panel.visible = true - _panel.reset_size() - panel_opened.emit() + _ziel = null + var wirt := _wirt_fuer(kind) + if _box.get_parent() != wirt: + if _box.get_parent() != null: + _box.get_parent().remove_child(_box) + wirt.add_child(_box) + _drawer.visible = wirt == _drawer + _karte_host.visible = wirt == _karte_host + _modal.visible = wirt == _modal + _dim.visible = wirt == _modal + if zurueck.is_valid(): + _verlauf.append(zurueck) + if wirt == _modal: + _modal.reset_size() + if not offen: + offen = true + panel_opened.emit() + + +## Esc: eine Ebene zurück, sonst zu. +func _zurueck() -> void: + if _verlauf.is_empty(): + schliessen() + return + var cb: Callable = _verlauf.pop_back() + cb.call() func schliessen() -> void: @@ -357,9 +567,13 @@ func schliessen() -> void: return offen = false _kind = "" + _ziel = null + _verlauf.clear() _apply_human = null + _drawer.visible = false + _karte_host.visible = false + _modal.visible = false _dim.visible = false - _panel.visible = false panel_closed.emit() @@ -420,8 +634,8 @@ func _journal_techniken() -> void: if not Game.knows(id): continue var t := TechDB.by_id(id) - _knopf("%s · Stufe %d · %s" % [t["name"], Game.skill_of(id), - Ursachen.LISTE[t["ursache"]]["name"]], + _karte("%s · Stufe %d · %s" % [t["name"], Game.skill_of(id), + Ursachen.LISTE[t["ursache"]]["name"]], t["farbe"], func() -> void: technik_panel(id)) @@ -441,9 +655,7 @@ func technik_panel(tech_id: String) -> void: gegen.append(Probleme.name_von(str(tag))) if not gegen.is_empty(): _text("[color=#9fb6ff]Im Moment gegen:[/color] %s" % ", ".join(gegen), 15) - _trenner() - _knopf("Journal", func() -> void: journal_panel("techniken")) - _zeigen("technik") + _zeigen("technik", journal_panel.bind("techniken")) func _journal_missionen() -> void: @@ -455,11 +667,14 @@ func _journal_missionen() -> void: if not Game.angenommen(id): continue var stand := "%d %%" % int(Game.mission_progress(id)) + var farbe := C_GESEHEN if Game.completed.has(id): stand = "★ erfüllt" + farbe = C_SYSTEM elif Game.active_mission == id: stand = "aktiv · %d %%" % int(Game.mission_progress(id)) - _knopf("%s · %s" % [h["name"], stand], + farbe = C_AKZENT + _karte("%s · %s" % [h["name"], stand], farbe, func() -> void: systeme_panel(id)) @@ -493,9 +708,7 @@ func systeme_panel(human_id: String) -> void: if offene > 0: _trenner() _tagesablauf(human_id) - _trenner() - _knopf("Journal", func() -> void: journal_panel("missionen")) - _zeigen("systeme") + _zeigen("systeme", journal_panel.bind("missionen")) _scroll_wunsch = scroll_alt _scroll_frames = 4 @@ -543,8 +756,8 @@ func _stunden_zeile(human_id: String, nr: int, von: int, bis: int, text: String) 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) + _karte("%s %s" % [spanne, text], _ablauf_farbe(human_id, nr), + func() -> void: slot_panel(human_id, nr, systeme_panel.bind(human_id)), offen) ## Der offene Moment: hier zählt, welche Technik zu diesem Problem passt. @@ -585,7 +798,7 @@ func situation_panel(mensch: Node3D) -> void: ## Was soll in diesem Slot stehen? -func slot_panel(human_id: String, nr: int) -> void: +func slot_panel(human_id: String, nr: int, zurueck: Callable = Callable()) -> 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]) @@ -618,8 +831,7 @@ func slot_panel(human_id: String, nr: int) -> void: _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") + _zeigen("slot", zurueck) func _system_setzen(human_id: String, nr: int, tech_id: String) -> void: @@ -699,11 +911,15 @@ func brett_panel() -> void: continue gezeigt += 1 var marke := "" + var farbe := C_GESEHEN if Game.active_mission == id: marke = " · aktiv %d %%" % int(Game.mission_progress(id)) + farbe = C_AKZENT elif Game.angenommen(id): marke = " · %d %%" % int(Game.mission_progress(id)) - _knopf("%s · %s%s" % [h["name"], h["traum"], marke], + if bool(h.get("eigen", false)): + farbe = C_WARN + _karte("%s · %s%s" % [h["name"], h["traum"], marke], farbe, func() -> void: aushang_panel(id)) if gezeigt == 0: _text("[color=#ffcc66]Nichts passt — im Hörsaal einen Weg lernen.[/color]", 16) @@ -733,12 +949,11 @@ func aushang_panel(human_id: String) -> void: Game.accept_mission(human_id) brett_panel()) if bool(h.get("eigen", false)): - _knopf("Aushang abnehmen", func() -> void: + _knopf_leise("Aushang abnehmen", func() -> void: Aushaenge.loeschen(human_id) Game.welt_erzeugen() brett_panel()) - _knopf("Zurück", func() -> void: brett_panel()) - _zeigen("aushang") + _zeigen("aushang", brett_panel) # -------------------------------------------------------- Aushang schreiben @@ -756,35 +971,83 @@ func schreiben_panel(entwurf: Dictionary = {}) -> void: "traum": "", "geschichte": "", "woerter": []} var scroll_alt := _scroll.scroll_vertical if offen and _kind == "schreiben" else 0 _leeren("Aushang schreiben") + + var spalten := HBoxContainer.new() + spalten.add_theme_constant_override("separation", 22) + _content.add_child(spalten) + var links := VBoxContainer.new() + links.custom_minimum_size.x = 400 + links.add_theme_constant_override("separation", 8) + spalten.add_child(links) + var rechts := VBoxContainer.new() + rechts.size_flags_horizontal = Control.SIZE_EXPAND_FILL + rechts.add_theme_constant_override("separation", 6) + spalten.add_child(rechts) + + _ziel = links _zeile("Name", _eingabe("name")) _zeile("Alter", _zahl("alter", 14, 99)) _zeile("Beruf", _auswahl("beruf", HumanDB.BERUFE)) _zeile("Traum", _eingabe("traum")) - _trenner() - var w: Array = _entwurf["woerter"] - _text("[b]Wörter[/b] [color=#8e93a8]mindestens %d · %d gewählt[/color]" % - [Woerter.MINDESTENS, w.size()], 15) - for gruppe in Woerter.GRUPPEN: - _text("[color=#9fb6ff]%s[/color]" % Woerter.GRUPPEN[gruppe], 13) - _haken_raster(Woerter.in_gruppe(gruppe)) - _trenner() - _text("[b]Geschichte[/b] [color=#8e93a8]was im Weg steht[/color]", 15) + _text("[b]Geschichte[/b] [color=#8e93a8]was im Weg steht[/color]", 14) _textfeld("geschichte") _trenner() + _vorschau = _text("", 14) + _trenner() var reihe := HBoxContainer.new() reihe.add_theme_constant_override("separation", 8) - _content.add_child(reihe) + links.add_child(reihe) _reihenknopf(reihe, "Zufall", func() -> void: schreiben_panel(HumanDB.zufallsprofil())) _reihenknopf(reihe, "Speichern", func() -> void: _aushang_speichern()) - _reihenknopf(reihe, "Zurück", func() -> void: - _entwurf = {} - brett_panel()) - _zeigen("schreiben") + + _ziel = rechts + var w: Array = _entwurf["woerter"] + _woerter_kopf = _text("", 14) + for gruppe in Woerter.GRUPPEN: + _text("[color=#9fb6ff]%s[/color]" % Woerter.GRUPPEN[gruppe], 13) + _haken_raster(Woerter.in_gruppe(gruppe)) + _ziel = null + _vorschau_aktualisieren() + _zeigen("schreiben", brett_panel) _scroll_wunsch = scroll_alt _scroll_frames = 4 +var _vorschau: RichTextLabel +var _woerter_kopf: RichTextLabel + + +## Was aus den Wörtern wird -- live, während man ankreuzt. +func _vorschau_aktualisieren() -> void: + if _vorschau == null or not is_instance_valid(_vorschau): + return + var w: Array = _entwurf["woerter"] + _woerter_kopf.text = "[b]Wörter[/b] [color=#8e93a8]mindestens %d · %d gewählt[/color]" % \ + [Woerter.MINDESTENS, w.size()] + if w.is_empty(): + _vorschau.text = "[color=#8e93a8]Kreuze an, was auf sie oder ihn zutrifft — daraus entsteht der Mensch.[/color]" + return + var bild := Woerter.profil(w) + var urs: Array = [] + for u in bild["ursachen"]: + urs.append(str(Ursachen.LISTE[u]["name"])) + var prob: Array = [] + for pr in bild["probleme"]: + prob.append(Probleme.name_von(str(pr))) + var zuege: Array = [] + var namen := {"einrichten": "offen", "guete": "beständig", "abwehr": "stur", + "momente": "reizbar", "dauer": "lang", "tempo": "treibt", "sozial": "gesellig", + "sichtbar": "durchschaubar"} + for v in Woerter.VERHALTEN: + var wert := int(bild["verhalten"][v]) + if wert != 0: + zuege.append("%s %s" % [namen[v], "+" if wert > 0 else "−"]) + _vorschau.text = "[color=#ff9a8a]%s[/color]\n[color=#ffcc66]%s[/color] · %d Blocker im Tag\n[color=#8e93a8]%s[/color]" % \ + [" · ".join(urs), " · ".join(prob), (bild["blocker"] as Array).size(), + " ".join(zuege) if not zuege.is_empty() else "unauffällig"] + + func _aushang_speichern() -> void: var fehler := Aushaenge.pruefen(_entwurf) if not fehler.is_empty(): @@ -794,6 +1057,7 @@ func _aushang_speichern() -> void: Game.welt_erzeugen() Game.notified.emit("Aushang hängt: %s" % _entwurf["name"], Color(0.6, 1, 0.7)) _entwurf = {} + _verlauf.clear() aushang_panel(id) @@ -809,7 +1073,7 @@ func _zeile(titel: String, widget: Control) -> void: hb.add_child(l) widget.size_flags_horizontal = Control.SIZE_EXPAND_FILL hb.add_child(widget) - _content.add_child(hb) + _wo().add_child(hb) func _eingabe(feld: String) -> LineEdit: @@ -846,15 +1110,15 @@ func _textfeld(feld: String) -> TextEdit: t.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY t.add_theme_font_size_override("font_size", 15) t.text_changed.connect(func() -> void: _entwurf[feld] = t.text) - _content.add_child(t) + _wo().add_child(t) return t func _haken_raster(keys: Array) -> void: var g := GridContainer.new() - g.columns = 4 - g.add_theme_constant_override("h_separation", 6) - _content.add_child(g) + g.columns = 3 + g.add_theme_constant_override("h_separation", 4) + _wo().add_child(g) var gewaehlt: Array = _entwurf["woerter"] for key in keys: var c := CheckBox.new() @@ -866,7 +1130,8 @@ func _haken_raster(keys: Array) -> void: if an and not liste.has(key): liste.append(key) elif not an: - liste.erase(key)) + liste.erase(key) + _vorschau_aktualisieren()) g.add_child(c) @@ -942,5 +1207,5 @@ func _vor_ort_setzen(tech_id: String) -> void: func _unhandled_input(event: InputEvent) -> void: if offen and event.is_action_pressed("ui_cancel"): - schliessen() + _zurueck() get_viewport().set_input_as_handled() diff --git a/tests/verify.gd b/tests/verify.gd index 1a13856..7f777ca 100644 --- a/tests/verify.gd +++ b/tests/verify.gd @@ -226,6 +226,10 @@ func test_journal() -> void: "jasagen": "ja sagt", "scrollen": "scrollt", "serie": "serie", "wach": "wach", "kuehlschrank": "kühlschrank", "spielen": "spielt", "umschalten": "umschalt", "klagen": "klagt", "vergleichen": "vergleicht", + "mails": "postfach", "kaffee": "kaffee", "perfekt": "detail", "plan_neu": "plan", + "snooze": "schlummer", "doesen": "döst", "bestellen": "bestellt", + "nachrichten": "nachrichten", "ueberstunden": "länger", "absage": "absag", + "waelzen": "wälzt", } var luecken: Array = [] var zuviel: Array = []