Files
dreamer-game/scripts/ui.gd
2026-08-30 19:56:47 +02:00

595 lines
21 KiB
GDScript

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 _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
var _on_travel: Callable
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)
var steuerung := Label.new()
steuerung.text = "WASD Bewegen · Maus Umsehen · E Interagieren · J Journal · Esc Maus frei"
steuerung.set_anchors_preset(Control.PRESET_TOP_RIGHT)
steuerung.position = Vector2(-640, 16)
steuerung.size = Vector2(620, 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)
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 = []
zeilen.append("[b]%s[/b]" % Game.time_string())
zeilen.append("Einsicht: [b]%d[/b] Techniken: [b]%d / %d[/b]" %
[Game.einsicht, Game.known.size(), TechDB.LIST.size()])
if Game.active_mission != "":
var h := HumanDB.by_id(Game.active_mission)
zeilen.append("Mission: [color=#ffdd77]%s[/color] — %d %% · %d Regeln" %
[h["name"], int(Game.mission_progress(Game.active_mission)),
Game.regel_anzahl(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)
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" %
[t["was"], t["wann"], t["warum"]])
# -------------------------------------------------------- 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]Einsicht: [b]%d[/b] · Mission: %s[/color]" %
[Game.einsicht, 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 wird der erste Weg gelehrt.", 16)
return
_text("Was du gelernt hast. Jede Anwendung draußen macht eine Technik von allein " +
"besser; vertieft wird im Hörsaal.", 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)
_text("[color=#9fb6ff]Hilft bei:[/color] %s [color=#9fb6ff]Momente:[/color] %s" %
[TechDB.tags_text(t["tags"]), TechDB.momente_text(t["momente"])], 14)
_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)
_text("[color=#9fb6ff]Themen:[/color] %s" % TechDB.tags_text(h["tags"]), 14)
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("Regelwerk — %d Regeln" % Game.regel_anzahl(id),
func() -> void: pipeline_panel(id))
_trenner()
# -------------------------------------------------------- Regelwerk einer Mission
## Wenn-Dann-Regeln: tut der Mensch etwas Bestimmtes, greift die zugeordnete
## Technik von allein. So automatisiert der Spieler den Fortschritt.
func pipeline_panel(human_id: String) -> void:
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "pipeline" else 0
var h := HumanDB.by_id(human_id)
_leeren("Regelwerk — %s" % h["name"])
_text("Jede Tätigkeit im Tag von %s kann eine Technik auslösen. " % h["name"] +
"Wechselt sie hinein, greift die Regel von allein — solange du auf der Erde bist.", 15)
_text("[color=#aaaaaa]Fortschritt: [b]%d %%[/b] · Regeln: [b]%d[/b][/color]" %
[int(Game.mission_progress(human_id)), Game.regel_anzahl(human_id)], 15)
_trenner()
for activity in HumanDB.aktivitaeten(human_id):
var tat := Orte.text(activity)
if not Game.erreichbar(activity):
_knopf("%s → unerreichbar" % tat, func() -> void: pass, false)
continue
var tech: String = Game.regel_fuer(human_id, activity)
var beschriftung := "%s → keine Regel" % tat
if tech != "":
var ev := Game.evaluate(tech, human_id, activity, false)
beschriftung = "%s → %s · %d %%" % [tat, TechDB.by_id(tech)["name"],
int(round(float(ev["chance"]) * 100.0))]
_knopf(beschriftung, func() -> void: regel_panel(human_id, activity))
_trenner()
_knopf("Zurück zum Journal", func() -> void: journal_panel("missionen"))
_zeigen("pipeline")
_scroll_wunsch = scroll_alt
_scroll_frames = 4
## Welche Technik soll bei dieser Tätigkeit greifen?
func regel_panel(human_id: String, activity: String) -> void:
var h := HumanDB.by_id(human_id)
_leeren("Regel für »%s«" % Orte.text(activity))
if Game.known.is_empty():
_text("[color=#ffcc66]Du kennst keine Technik. Im Hörsaal der Wege " +
"werden sie gelehrt.[/color]", 16)
_knopf("Zurück", func() -> void: pipeline_panel(human_id))
_zeigen("regel")
return
_text("Was soll greifen, wenn %s %s? Die Erfolgschance gilt für genau diesen Moment. " %
[h["name"], Orte.text(activity)] +
"Jede Technik hält nur einen Posten — sie hier einzusetzen nimmt sie anderswo weg.", 15)
_trenner()
for id in TechDB.ids():
if not Game.knows(id):
continue
var t := TechDB.by_id(id)
var ev := Game.evaluate(id, human_id, activity, false)
var marker: Array = []
if ev["tag"]:
marker.append("[color=#88ff99]passt zum Thema[/color]")
if ev["moment"]:
marker.append("[color=#88ff99]passender Moment[/color]")
if ev["blocked"]:
marker.append("[color=#ff8877]%s[/color]" % ev["grund"])
if marker.is_empty():
marker.append("[color=#aaaaaa]weder Thema noch Moment passen[/color]")
var gewaehlt := ""
var haengt := Game.regel_moment(human_id, id)
if haengt == activity:
gewaehlt = " [color=#ffdd77](hier eingestellt)[/color]"
elif haengt != "":
gewaehlt = " [color=#ffaa66](hängt an »%s«)[/color]" % Orte.text(haengt)
_text("[b]%s[/b] · Stufe %d%s\n[color=#cccccc]%s[/color]\n%s" %
[t["name"], Game.skill_of(id), gewaehlt, t["wann"], " · ".join(marker)], 16)
var beschriftung := "Blockiert" if ev["blocked"] else \
"Diese Regel — %d %% Erfolgschance" % int(round(float(ev["chance"]) * 100.0))
_knopf(beschriftung, func() -> void:
Game.regel_setzen(human_id, activity, id)
pipeline_panel(human_id), not ev["blocked"])
_trenner()
_knopf("Keine Regel für diese Tätigkeit", func() -> void:
Game.regel_setzen(human_id, activity, "")
pipeline_panel(human_id))
_zeigen("regel")
# -------------------------------------------------------- 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]Einsicht: [b]%d[/b] · Gelernt: [b]%d / %d[/b] von %d entdeckten[/color]" %
[Game.einsicht, Game.known.size(), TechDB.LIST.size(), Game.offene_techniken()], 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)
_text("[color=#9fb6ff]Hilft bei:[/color] %s [color=#9fb6ff]Momente:[/color] %s" %
[TechDB.tags_text(t["tags"]), TechDB.momente_text(t["momente"])], 14)
if Game.knows(id):
_knopf("Vertiefen (1 Einsicht)", func() -> void:
if Game.deepen(id):
lehre_panel(ort), Game.einsicht >= 1)
else:
_knopf("Lernen (1 Einsicht)", func() -> void:
if Game.learn(id):
lehre_panel(ort), Game.einsicht >= 1)
_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("Menschen, deren Traum feststeckt. Nimm einen an — das Tor zur Erde " +
"öffnet sich nur mit einer Mission.", 15)
_trenner()
for h in HumanDB.LIST:
var id: String = h["id"]
_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)
_text("[color=#9fb6ff]Themen:[/color] %s" % TechDB.tags_text(h["tags"]), 14)
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:
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()
_zeigen("brett")
_scroll_wunsch = scroll_alt
_scroll_frames = 4
# -------------------------------------------------------- Anwenden auf der Erde
func anwenden_panel(mensch: Node3D) -> void:
_apply_human = mensch
var h: Dictionary = mensch.data
var aktiv: String = mensch.activity
_leeren("%s — %s" % [h["name"], Orte.text(aktiv)])
_text("[b]Traum:[/b] %s" % h["traum"], 16)
if Game.active_mission == h["id"]:
_text("Fortschritt: [b]%d %%[/b]" % int(Game.mission_progress(h["id"])), 16)
elif Game.completed.has(h["id"]):
_text("[color=#88ff99]★ Traum erfüllt.[/color]", 16)
else:
_text("[color=#ffcc66]Keine aktive Mission für diesen Menschen. " +
"Fortschritt zählt nur mit angenommener Mission.[/color]", 15)
_trenner()
if Game.known.is_empty():
_text("[color=#ffcc66]Du kennst keine Technik.[/color]")
_zeigen("anwenden")
return
for id in TechDB.ids():
if not Game.knows(id):
continue
var t := TechDB.by_id(id)
var ev := Game.evaluate(id, h["id"], aktiv, mensch.resist_left > 0.0)
var marker: Array = []
if ev["tag"]:
marker.append("[color=#88ff99]passt zum Thema[/color]")
if ev["moment"]:
marker.append("[color=#88ff99]passender Moment[/color]")
if ev["blocked"]:
marker.append("[color=#ff8877]%s[/color]" % ev["grund"])
elif mensch.resist_left > 0.0:
marker.append("[color=#ff8877]macht gerade dicht[/color]")
if marker.is_empty():
marker.append("[color=#aaaaaa]weder Thema noch Moment passen[/color]")
_text("[b]%s[/b] · Stufe %d\n[color=#cccccc]%s[/color]\n%s" %
[t["name"], Game.skill_of(id), t["wann"], " · ".join(marker)], 16)
var beschriftung := "Blockiert" if ev["blocked"] else "Anwenden — %d %% Erfolgschance" % int(round(float(ev["chance"]) * 100.0))
_knopf(beschriftung, func() -> void: _anwenden(id), not ev["blocked"])
_trenner()
_zeigen("anwenden")
func _anwenden(id: String) -> void:
if _apply_human == null or not is_instance_valid(_apply_human):
schliessen()
return
var mensch := _apply_human
var h: Dictionary = mensch.data
var res := Game.apply_technique(id, h["id"], mensch.activity, mensch.resist_left > 0.0)
schliessen()
if not res["ok"]:
Game.notified.emit(res["grund"], Color(1, 0.6, 0.4))
return
var t := TechDB.by_id(id)
if res["success"]:
mensch.reagiere(true, "%s wirkt\n+%d %%" % [t["name"], int(round(float(res["gain"])))])
Game.notified.emit("%s erreicht %s. (+%d %%)" %
[t["name"], h["name"], int(round(float(res["gain"])))], Color(0.6, 1, 0.7))
if res["wiederholung"] >= 2:
Game.notified.emit("Immer dieselbe Technik wirkt schwächer. Probier eine andere.",
Color(0.85, 0.85, 0.6))
else:
mensch.reagiere(false, "%s prallt ab" % t["name"])
Game.notified.emit("%s dringt gerade nicht durch (%d %% Chance)." %
[t["name"], int(round(float(res["chance"]) * 100.0))], Color(1, 0.6, 0.5))
func _unhandled_input(event: InputEvent) -> void:
if offen and event.is_action_pressed("ui_cancel"):
schliessen()
get_viewport().set_input_as_handled()