This commit is contained in:
Team3
2026-09-15 23:49:37 +02:00
parent f1b0f15e30
commit 12cbb88692
5 changed files with 543 additions and 264 deletions

View File

@@ -1,29 +1,29 @@
78098b89c88a74ebe8c20117b8754620 78098b89c88a74ebe8c20117b8754620
::res://::1788260833 ::res://::1788260833
anforderungen.md::TextFile::-1::1788584851::0::1::::<><><>0<>0<><>:: 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 ::res://scenes/::1788012512
main.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://scripts/main.gd main.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://scripts/main.gd
::res://scripts/::1789505734 ::res://scripts/::1789506182
aushaenge.gd::GDScript::4842442143352950821::1789505730::0::1::::Aushaenge<>RefCounted<><>0<>0<><>:: aushaenge.gd::GDScript::4842442143352950821::1789506182::0::1::::Aushaenge<>RefCounted<><>0<>0<><>::
bewohner.gd::GDScript::2125278938812678811::1789505829::0::1::::Bewohner<>Node3D<><>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_db.gd::GDScript::4217518896355437099::1788260794::0::1::::DreamDB<>RefCounted<><>0<>0<><>::
dream_world.gd::GDScript::8279158278942867258::1788260794::0::1::::DreamWorld<>Node3D<><>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<><>:: earth_world.gd::GDScript::6086818627272428934::1788260794::0::1::::EarthWorld<>Node3D<><>0<>0<><>::
game.gd::GDScript::3600922541500868814::1789505829::0::1::::<>Node<><>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<><>:: main.gd::GDScript::1504009331368977934::1788598457::0::1::::<>Node3D<><>0<>0<><>::
netz.gd::GDScript::8422943321868648339::1788260794::0::1::::Netz<>RefCounted<><>0<>0<><>:: netz.gd::GDScript::8422943321868648339::1788260794::0::1::::Netz<>RefCounted<><>0<>0<><>::
orte.gd::GDScript::5536340784294498671::1788260833::0::1::::Orte<>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<><>:: player.gd::GDScript::8724108548430371398::1788459499::0::1::::Player<>CharacterBody3D<><>0<>0<><>::
prims.gd::GDScript::322632462553361584::1788088940::0::1::::Prims<>RefCounted<><>0<>0<><>:: prims.gd::GDScript::322632462553361584::1788088940::0::1::::Prims<>RefCounted<><>0<>0<><>::
probleme.gd::GDScript::4928319609961045127::1789505398::0::1::::Probleme<>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<><>:: technique_db.gd::GDScript::4543286561661639701::1789507527::0::1::::TechDB<>RefCounted<><>0<>0<><>::
ui.gd::GDScript::843471304695966286::1789506056::0::1::::UI<>CanvasLayer<><>0<>0<><>:: ui.gd::GDScript::843471304695966286::1789507751::0::1::::UI<>CanvasLayer<><>0<>0<><>::
ursachen.gd::GDScript::7371116734724270900::1789505398::0::1::::Ursachen<>RefCounted<><>0<>0<><>:: ursachen.gd::GDScript::7371116734724270900::1789505398::0::1::::Ursachen<>RefCounted<><>0<>0<><>::
woerter.gd::GDScript::1848212517770692820::1789505563::0::1::::Woerter<>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.gd::GDScript::5999651346126871724::1788260833::0::1::::<>Node<><>0<>0<><>::
shots.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://tests/shots.gd 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 verify.tscn::PackedScene::-1::1788012512::0::1::::<><><>0<>0<><>::res://tests/verify.gd

View File

@@ -14,12 +14,22 @@ Steuerung: **WASD** bewegen · **Maus** umsehen · **E** interagieren ·
**F** folgen · **R** notieren · **J** Journal · **1/2/3** Tempo · **F** folgen · **R** notieren · **J** Journal · **1/2/3** Tempo ·
**Esc** Maus freigeben. **Esc** Maus freigeben.
Das **HUD** zeigt vier Zeilen: Zeit, die laufende Mission, den Tagesstreifen — **Die Oberfläche** ist um den Tag gebaut. Unten über die ganze Breite liegt
24 Felder, eines je Stunde, die laufende hervorgehoben — und darunter, was die **Zeitleiste**: 24 Felder, eines je Stunde, die laufende steht heraus.
gerade läuft. Grau heißt unbekannt, gelb „hier stimmt etwas nicht, aber du hast Grau heißt unbekannt, gelb „hier stimmt etwas nicht, noch nicht benannt", rot
es noch nicht benannt", rot ein benannter Blocker, grün ein gebautes System. ein benannter Blocker, grün ein gebautes System. Links darüber die Ursachen,
Bricht ein Moment auf, verdrängt er die Stundenzeile: er ist gleich vorbei. rechts die Momente; bricht einer auf, pulsiert die laufende Stunde und rechts
Alles Weitere steht im Journal, nichts davon auf dem Schirm. 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** In der Traumwelt springt **Leertaste**; auf der Erde steigt sie und **Umschalt**
lässt sinken. lässt sinken.

View File

@@ -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.", "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.", "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.", "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", "ursache": "kraft",
"belohnung": "abschalten", "belohnung": "abschalten",
"ersatz": "lässt den Bildschirm aus", "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.", "was": "Sich mit jemandem zu einer festen Zeit verabreden, um gemeinsam oder nebeneinander zu arbeiten.",
"wann": "Wenn man allein immer wieder aussteigt.", "wann": "Wenn man allein immer wieder aussteigt.",
"warum": "Vor einem anderen fällt Aufhören schwerer als vor sich selbst.", "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", "ursache": "halt",
"belohnung": "verbundenheit", "belohnung": "verbundenheit",
"ersatz": "meldet sich zum Dranbleiben", "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.", "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.", "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.", "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", "ursache": "anfang",
"belohnung": "erfolg", "belohnung": "erfolg",
"ersatz": "nimmt die nächste Sprosse", "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.", "was": "Sorgen auf eine feste Viertelstunde am Tag vertagen. Was vorher kommt, wird notiert und wartet.",
"wann": "Wenn Sorgen den ganzen Tag dazwischenfunken.", "wann": "Wenn Sorgen den ganzen Tag dazwischenfunken.",
"warum": "Sorgen lassen sich nicht abstellen, aber verschieben. Verschoben verlieren sie an Gewicht.", "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", "ursache": "grund",
"belohnung": "einordnung", "belohnung": "einordnung",
"ersatz": "parkt die Sorge bis fünf", "ersatz": "parkt die Sorge bis fünf",

View File

@@ -1,27 +1,59 @@
extends CanvasLayer extends CanvasLayer
class_name UI 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_opened
signal panel_closed signal panel_closed
const BREITE := 900 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 offen := false
var _kind := "" var _kind := ""
var _root: Control
var _hud: RichTextLabel var _hud: RichTextLabel
var _hinweis: Label var _hinweis: Label
var _log: VBoxContainer var _log: VBoxContainer
var _ablauf: HBoxContainer # Zeitleiste
var _ablauf_jetzt: RichTextLabel var _ablauf: PanelContainer
var _ablauf_grund: ColorRect var _ablauf_titel: Label
var _ursachen: RichTextLabel
var _probleme: RichTextLabel
var _zellen: Array = [] var _zellen: Array = []
var _ticks: 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 _dim: ColorRect
var _panel: PanelContainer var _box: VBoxContainer
var _titel: Label var _titel: Label
var _content: VBoxContainer
var _scroll: ScrollContainer 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 _apply_human: Node3D
var _journal_seite := "techniken" var _journal_seite := "techniken"
var _scroll_wunsch := 0 var _scroll_wunsch := 0
@@ -30,13 +62,64 @@ var _scroll_frames := 0
func _ready() -> void: func _ready() -> void:
layer = 10 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_hud()
_baue_panel() _baue_leiste()
_baue_wirte()
Game.notified.connect(_notiz) Game.notified.connect(_notiz)
Game.state_changed.connect(_hud_aktualisieren) Game.state_changed.connect(_hud_aktualisieren)
_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: func _baue_hud() -> void:
_hud = RichTextLabel.new() _hud = RichTextLabel.new()
_hud.bbcode_enabled = true _hud.bbcode_enabled = true
@@ -44,14 +127,12 @@ func _baue_hud() -> void:
_hud.scroll_active = false _hud.scroll_active = false
_hud.set_anchors_preset(Control.PRESET_TOP_LEFT) _hud.set_anchors_preset(Control.PRESET_TOP_LEFT)
_hud.position = Vector2(18, 14) _hud.position = Vector2(18, 14)
_hud.custom_minimum_size = Vector2(420, 130) _hud.custom_minimum_size = Vector2(420, 50)
_hud.size = Vector2(420, 130) _hud.size = Vector2(420, 50)
_hud.mouse_filter = Control.MOUSE_FILTER_IGNORE _hud.mouse_filter = Control.MOUSE_FILTER_IGNORE
_hud.add_theme_font_size_override("normal_font_size", 17) _hud.add_theme_font_size_override("normal_font_size", 17)
_hud.add_theme_font_size_override("bold_font_size", 17) _hud.add_theme_font_size_override("bold_font_size", 17)
add_child(_hud) _root.add_child(_hud)
_baue_ablauf()
var steuerung := Label.new() var steuerung := Label.new()
steuerung.text = "J Journal · 1-3 Tempo · Esc" 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_font_size_override("font_size", 14)
steuerung.add_theme_color_override("font_color", Color(1, 1, 1, 0.4)) steuerung.add_theme_color_override("font_color", Color(1, 1, 1, 0.4))
steuerung.mouse_filter = Control.MOUSE_FILTER_IGNORE steuerung.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(steuerung) _root.add_child(steuerung)
_hinweis = Label.new() _hinweis = Label.new()
_hinweis.set_anchors_preset(Control.PRESET_CENTER_BOTTOM) _hinweis.set_anchors_preset(Control.PRESET_CENTER_BOTTOM)
_hinweis.position = Vector2(-300, -110) _hinweis.position = Vector2(-320, -(LEISTE_H + 44))
_hinweis.size = Vector2(600, 30) _hinweis.size = Vector2(640, 30)
_hinweis.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _hinweis.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_hinweis.add_theme_font_size_override("font_size", 19) _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_color", Color(1, 0.95, 0.75))
_hinweis.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9)) _hinweis.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9))
_hinweis.add_theme_constant_override("outline_size", 6) _hinweis.add_theme_constant_override("outline_size", 6)
_hinweis.mouse_filter = Control.MOUSE_FILTER_IGNORE _hinweis.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(_hinweis) _root.add_child(_hinweis)
_log = VBoxContainer.new() _log = VBoxContainer.new()
_log.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT) _log.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
_log.position = Vector2(-520, -230) _log.position = Vector2(-520, -(LEISTE_H + 110))
_log.size = Vector2(500, 200) _log.size = Vector2(500, 90)
_log.alignment = BoxContainer.ALIGNMENT_END _log.alignment = BoxContainer.ALIGNMENT_END
_log.mouse_filter = Control.MOUSE_FILTER_IGNORE _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: func _hud_aktualisieren() -> void:
var kopf := "[b]%s[/b]" % Game.time_string() var kopf := "[b]%s[/b]" % Game.time_string()
if Game.schlaeft_ziel(): if Game.schlaeft_ziel():
@@ -303,53 +210,356 @@ func _notiz(text: String, farbe: Color) -> void:
tw.tween_callback(l.queue_free) 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: func _leeren(titel: String) -> void:
_ziel = null
for c in _content.get_children(): for c in _content.get_children():
_content.remove_child(c) _content.remove_child(c)
c.queue_free() c.queue_free()
_titel.text = titel _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() var r := RichTextLabel.new()
r.bbcode_enabled = true r.bbcode_enabled = true
r.fit_content = true r.fit_content = true
r.scroll_active = false r.scroll_active = false
r.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART 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.text = bb
r.add_theme_font_size_override("normal_font_size", groesse) r.add_theme_font_size_override("normal_font_size", groesse)
r.add_theme_font_size_override("bold_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 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() var b := Button.new()
b.text = beschriftung b.text = beschriftung
b.disabled = not aktiv 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) 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: if aktiv:
b.pressed.connect(cb) b.pressed.connect(cb)
return b 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: func _trenner() -> void:
var s := HSeparator.new() 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 _kind = kind
offen = true _ziel = null
_dim.visible = true var wirt := _wirt_fuer(kind)
_panel.visible = true if _box.get_parent() != wirt:
_panel.reset_size() if _box.get_parent() != null:
panel_opened.emit() _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: func schliessen() -> void:
@@ -357,9 +567,13 @@ func schliessen() -> void:
return return
offen = false offen = false
_kind = "" _kind = ""
_ziel = null
_verlauf.clear()
_apply_human = null _apply_human = null
_drawer.visible = false
_karte_host.visible = false
_modal.visible = false
_dim.visible = false _dim.visible = false
_panel.visible = false
panel_closed.emit() panel_closed.emit()
@@ -420,8 +634,8 @@ func _journal_techniken() -> void:
if not Game.knows(id): if not Game.knows(id):
continue continue
var t := TechDB.by_id(id) var t := TechDB.by_id(id)
_knopf("%s · Stufe %d · %s" % [t["name"], Game.skill_of(id), _karte("%s · Stufe %d · %s" % [t["name"], Game.skill_of(id),
Ursachen.LISTE[t["ursache"]]["name"]], Ursachen.LISTE[t["ursache"]]["name"]], t["farbe"],
func() -> void: technik_panel(id)) func() -> void: technik_panel(id))
@@ -441,9 +655,7 @@ func technik_panel(tech_id: String) -> void:
gegen.append(Probleme.name_von(str(tag))) gegen.append(Probleme.name_von(str(tag)))
if not gegen.is_empty(): if not gegen.is_empty():
_text("[color=#9fb6ff]Im Moment gegen:[/color] %s" % ", ".join(gegen), 15) _text("[color=#9fb6ff]Im Moment gegen:[/color] %s" % ", ".join(gegen), 15)
_trenner() _zeigen("technik", journal_panel.bind("techniken"))
_knopf("Journal", func() -> void: journal_panel("techniken"))
_zeigen("technik")
func _journal_missionen() -> void: func _journal_missionen() -> void:
@@ -455,11 +667,14 @@ func _journal_missionen() -> void:
if not Game.angenommen(id): if not Game.angenommen(id):
continue continue
var stand := "%d %%" % int(Game.mission_progress(id)) var stand := "%d %%" % int(Game.mission_progress(id))
var farbe := C_GESEHEN
if Game.completed.has(id): if Game.completed.has(id):
stand = "★ erfüllt" stand = "★ erfüllt"
farbe = C_SYSTEM
elif Game.active_mission == id: elif Game.active_mission == id:
stand = "aktiv · %d %%" % int(Game.mission_progress(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)) func() -> void: systeme_panel(id))
@@ -493,9 +708,7 @@ func systeme_panel(human_id: String) -> void:
if offene > 0: _trenner() if offene > 0: _trenner()
_tagesablauf(human_id) _tagesablauf(human_id)
_trenner() _zeigen("systeme", journal_panel.bind("missionen"))
_knopf("Journal", func() -> void: journal_panel("missionen"))
_zeigen("systeme")
_scroll_wunsch = scroll_alt _scroll_wunsch = scroll_alt
_scroll_frames = 4 _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 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) \ var offen: bool = Game.gesehen_slot(human_id, nr) and Game.erkannt(human_id, nr) \
and Game.slot_grund(human_id, nr) != "schlaeft" and Game.slot_grund(human_id, nr) != "schlaeft"
_knopf("%s %s" % [spanne, text], _karte("%s %s" % [spanne, text], _ablauf_farbe(human_id, nr),
func() -> void: slot_panel(human_id, nr), offen) 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. ## 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? ## 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 h := HumanDB.by_id(human_id)
var grund := Game.slot_grund(human_id, nr) var grund := Game.slot_grund(human_id, nr)
var stunde := "%02d:00" % int(Game.slots(human_id)[nr][0]) 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), _knopf(beschriftung, func() -> void: _system_setzen(human_id, nr, id),
bool(pruef["geht"])) bool(pruef["geht"]))
_trenner() _trenner()
_knopf("Zurück", func() -> void: systeme_panel(human_id)) _zeigen("slot", zurueck)
_zeigen("slot")
func _system_setzen(human_id: String, nr: int, tech_id: String) -> void: func _system_setzen(human_id: String, nr: int, tech_id: String) -> void:
@@ -699,11 +911,15 @@ func brett_panel() -> void:
continue continue
gezeigt += 1 gezeigt += 1
var marke := "" var marke := ""
var farbe := C_GESEHEN
if Game.active_mission == id: if Game.active_mission == id:
marke = " · aktiv %d %%" % int(Game.mission_progress(id)) marke = " · aktiv %d %%" % int(Game.mission_progress(id))
farbe = C_AKZENT
elif Game.angenommen(id): elif Game.angenommen(id):
marke = " · %d %%" % int(Game.mission_progress(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)) func() -> void: aushang_panel(id))
if gezeigt == 0: if gezeigt == 0:
_text("[color=#ffcc66]Nichts passt — im Hörsaal einen Weg lernen.[/color]", 16) _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) Game.accept_mission(human_id)
brett_panel()) brett_panel())
if bool(h.get("eigen", false)): if bool(h.get("eigen", false)):
_knopf("Aushang abnehmen", func() -> void: _knopf_leise("Aushang abnehmen", func() -> void:
Aushaenge.loeschen(human_id) Aushaenge.loeschen(human_id)
Game.welt_erzeugen() Game.welt_erzeugen()
brett_panel()) brett_panel())
_knopf("Zurück", func() -> void: brett_panel()) _zeigen("aushang", brett_panel)
_zeigen("aushang")
# -------------------------------------------------------- Aushang schreiben # -------------------------------------------------------- Aushang schreiben
@@ -756,35 +971,83 @@ func schreiben_panel(entwurf: Dictionary = {}) -> void:
"traum": "", "geschichte": "", "woerter": []} "traum": "", "geschichte": "", "woerter": []}
var scroll_alt := _scroll.scroll_vertical if offen and _kind == "schreiben" else 0 var scroll_alt := _scroll.scroll_vertical if offen and _kind == "schreiben" else 0
_leeren("Aushang schreiben") _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("Name", _eingabe("name"))
_zeile("Alter", _zahl("alter", 14, 99)) _zeile("Alter", _zahl("alter", 14, 99))
_zeile("Beruf", _auswahl("beruf", HumanDB.BERUFE)) _zeile("Beruf", _auswahl("beruf", HumanDB.BERUFE))
_zeile("Traum", _eingabe("traum")) _zeile("Traum", _eingabe("traum"))
_trenner() _text("[b]Geschichte[/b] [color=#8e93a8]was im Weg steht[/color]", 14)
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)
_textfeld("geschichte") _textfeld("geschichte")
_trenner() _trenner()
_vorschau = _text("", 14)
_trenner()
var reihe := HBoxContainer.new() var reihe := HBoxContainer.new()
reihe.add_theme_constant_override("separation", 8) reihe.add_theme_constant_override("separation", 8)
_content.add_child(reihe) links.add_child(reihe)
_reihenknopf(reihe, "Zufall", func() -> void: _reihenknopf(reihe, "Zufall", func() -> void:
schreiben_panel(HumanDB.zufallsprofil())) schreiben_panel(HumanDB.zufallsprofil()))
_reihenknopf(reihe, "Speichern", func() -> void: _aushang_speichern()) _reihenknopf(reihe, "Speichern", func() -> void: _aushang_speichern())
_reihenknopf(reihe, "Zurück", func() -> void:
_entwurf = {} _ziel = rechts
brett_panel()) var w: Array = _entwurf["woerter"]
_zeigen("schreiben") _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_wunsch = scroll_alt
_scroll_frames = 4 _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: func _aushang_speichern() -> void:
var fehler := Aushaenge.pruefen(_entwurf) var fehler := Aushaenge.pruefen(_entwurf)
if not fehler.is_empty(): if not fehler.is_empty():
@@ -794,6 +1057,7 @@ func _aushang_speichern() -> void:
Game.welt_erzeugen() Game.welt_erzeugen()
Game.notified.emit("Aushang hängt: %s" % _entwurf["name"], Color(0.6, 1, 0.7)) Game.notified.emit("Aushang hängt: %s" % _entwurf["name"], Color(0.6, 1, 0.7))
_entwurf = {} _entwurf = {}
_verlauf.clear()
aushang_panel(id) aushang_panel(id)
@@ -809,7 +1073,7 @@ func _zeile(titel: String, widget: Control) -> void:
hb.add_child(l) hb.add_child(l)
widget.size_flags_horizontal = Control.SIZE_EXPAND_FILL widget.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hb.add_child(widget) hb.add_child(widget)
_content.add_child(hb) _wo().add_child(hb)
func _eingabe(feld: String) -> LineEdit: func _eingabe(feld: String) -> LineEdit:
@@ -846,15 +1110,15 @@ func _textfeld(feld: String) -> TextEdit:
t.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY t.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
t.add_theme_font_size_override("font_size", 15) t.add_theme_font_size_override("font_size", 15)
t.text_changed.connect(func() -> void: _entwurf[feld] = t.text) t.text_changed.connect(func() -> void: _entwurf[feld] = t.text)
_content.add_child(t) _wo().add_child(t)
return t return t
func _haken_raster(keys: Array) -> void: func _haken_raster(keys: Array) -> void:
var g := GridContainer.new() var g := GridContainer.new()
g.columns = 4 g.columns = 3
g.add_theme_constant_override("h_separation", 6) g.add_theme_constant_override("h_separation", 4)
_content.add_child(g) _wo().add_child(g)
var gewaehlt: Array = _entwurf["woerter"] var gewaehlt: Array = _entwurf["woerter"]
for key in keys: for key in keys:
var c := CheckBox.new() var c := CheckBox.new()
@@ -866,7 +1130,8 @@ func _haken_raster(keys: Array) -> void:
if an and not liste.has(key): if an and not liste.has(key):
liste.append(key) liste.append(key)
elif not an: elif not an:
liste.erase(key)) liste.erase(key)
_vorschau_aktualisieren())
g.add_child(c) g.add_child(c)
@@ -942,5 +1207,5 @@ func _vor_ort_setzen(tech_id: String) -> void:
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
if offen and event.is_action_pressed("ui_cancel"): if offen and event.is_action_pressed("ui_cancel"):
schliessen() _zurueck()
get_viewport().set_input_as_handled() get_viewport().set_input_as_handled()

View File

@@ -226,6 +226,10 @@ func test_journal() -> void:
"jasagen": "ja sagt", "scrollen": "scrollt", "serie": "serie", "jasagen": "ja sagt", "scrollen": "scrollt", "serie": "serie",
"wach": "wach", "kuehlschrank": "kühlschrank", "spielen": "spielt", "wach": "wach", "kuehlschrank": "kühlschrank", "spielen": "spielt",
"umschalten": "umschalt", "klagen": "klagt", "vergleichen": "vergleicht", "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 luecken: Array = []
var zuviel: Array = [] var zuviel: Array = []